Structure ASCI UNICODE Problem

Just starting out? Need help? Post your questions and find answers here.
Wolfram
Enthusiast
Enthusiast
Posts: 568
Joined: Thu May 30, 2013 4:39 pm

Structure ASCI UNICODE Problem

Post by Wolfram »

How can I use a ASCI Structure in a Unicode program?

For example I want to read a WAVE file with a unicode program and use a structure for the chunk headers like this.

Code: Select all

Structure headerStruc
  type.s{4} ;must be 4 byte but is 8 in unicode
  size.l
EndStructure
macOS Catalina 10.15.7
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Re: Structure ASCI UNICODE Problem

Post by nco2k »

Code: Select all

Structure headerStruc
  type.a[4]
  size.l
EndStructure

Define Test.headerStruc
PokeS(@Test\type, "ABCD", 4, #PB_Ascii | #PB_String_NoZero)
Debug PeekS(@Test\type, 4, #PB_Ascii)
c ya,
nco2k
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
Wolfram
Enthusiast
Enthusiast
Posts: 568
Joined: Thu May 30, 2013 4:39 pm

Re: Structure ASCI UNICODE Problem

Post by Wolfram »

Thanks nco2k, but I than I don't need a structure.
Thats not the the solution I'm hoping for.
macOS Catalina 10.15.7
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Re: Structure ASCI UNICODE Problem

Post by djes »

In nco2k's code, there's not only a structure, but a code to read/decode ASCII. Apart of this, a structure is a good way to handle file headers.
User avatar
helpy
Enthusiast
Enthusiast
Posts: 552
Joined: Sat Jun 28, 2003 12:01 am

Re: Structure ASCI UNICODE Problem

Post by helpy »

djes wrote:In nco2k's code, there's not only a structure, but a code to read/decode ASCII. Apart of this, a structure is a good way to handle file headers.
Starting with 5.50 strings in PB are stored in as UNICODE strings with UCS2 encoding (on windows, I did not found any information about the encoding on Linux or MacOS).

Starting with PB 5.50 I recommend that you never use PB strings to store strings with different encodings inside a PB string variable.

Here are some ideas how to handle the 4 byte ID, mentioned in your first post:

Code: Select all

Structure tID
	StructureUnion
		char.a[4]
		lID.l
	EndStructureUnion
EndStructure
Global id.tID, i

Structure tHeader
	chunkID.tID
	size.l
EndStructure

Macro CreateIDFromChars( a, b, c, d )
	d << 24 + c << 16 + b << 8 + a
EndMacro
#ID_Chunk_RIFF = CreateIDFromChars( 'R', 'I', 'F', 'F' )

id\lID = #ID_Chunk_RIFF
Debug "Single letters of ID:"
For i = 0 To 3
	Debug "  --> Letter " + Str(i+1) + ": " + Chr(id\char[i])
Next i
Debug "ID read as 4 byte ASCII string: " + PeekS(@id, 4, #PB_Ascii)
Debug "ID as hexadecimal long value: $" + Hex(id\lID)
Debug "ID as deczimal long value: " + Str(id\lID)
Debug "----"


Procedure CreateIDFromString(IdString.s)
	Protected LongID.l
	If Len(IdString) = 4
		PokeS(@LongID, IdString, 4, #PB_Ascii | #PB_String_NoZero)
	EndIf
	ProcedureReturn LongID
EndProcedure

id\lID = CreateIDFromString("WAVE")
Debug "Single letters of ID:"
For i = 0 To 3
	Debug "  --> Letter " + Str(i+1) + ": " + Chr(id\char[i])
Next i
Debug "ID read as 4 byte ASCII string: " + PeekS(@id, 4, #PB_Ascii)
Debug "ID as hexadecimal long value: $" + Hex(id\lID)
Debug "ID as deczimal long value: " + Str(id\lID)
Windows 10 / Windows 7
PB Last Final / Last Beta Testing
User avatar
helpy
Enthusiast
Enthusiast
Posts: 552
Joined: Sat Jun 28, 2003 12:01 am

Re: Structure ASCI UNICODE Problem

Post by helpy »

After a little more thinking ...
For these IDs (chunkID, formatID ...) defined in AIFF format I never would use a string or char array to store this ID. Just use a long inside the structure:

Code: Select all

Structure tHeader
	chunkID.l
	size.l
EndStructure
Global header.tHeader

Procedure CreateIDFromString(IdString.s)
	Protected LongID.l
	If Len(IdString) = 4
		PokeS(@LongID, IdString, 4, #PB_Ascii | #PB_String_NoZero)
	EndIf
	ProcedureReturn LongID
EndProcedure

Procedure.s GetIDstring(ID.l)
	ProcedureReturn PeekS(@ID, 4, #PB_Ascii)
EndProcedure

header\chunkID = CreateIDFromString("RIFF")
Debug "ID read as 4 byte ASCII string: '" + GetIDstring(header\chunkID) + "'"
Debug "ID as hexadecimal long value: $" + Hex(header\chunkID)
Debug "ID as deczimal long value: " + Str(header\chunkID)
Debug "----"

Global otherID.l

otherID = CreateIDFromString("WAVE")
Debug "ID read as 4 byte ASCII string: '" + GetIDstring(otherID) + "'"
Debug "ID as hexadecimal long value: $" + Hex(otherID)
Debug "ID as deczimal long value: " + Str(otherID)
Debug "----"

otherID = CreateIDFromString("fmt ")
Debug "ID read as 4 byte ASCII string: '" + GetIDstring(otherID) + "'"
Debug "ID as hexadecimal long value: $" + Hex(otherID)
Debug "ID as deczimal long value: " + Str(otherID)
Debug "----"

otherID = CreateIDFromString("data")
Debug "ID read as 4 byte ASCII string: '" + GetIDstring(otherID) + "'"
Debug "ID as hexadecimal long value: $" + Hex(otherID)
Debug "ID as deczimal long value: " + Str(otherID)
Debug "----"
Windows 10 / Windows 7
PB Last Final / Last Beta Testing
User avatar
Demivec
Addict
Addict
Posts: 4089
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Structure ASCI UNICODE Problem

Post by Demivec »

Here's a weird variant also using DataSection to store the strings:

Code: Select all

Structure tHeader
   chunkID.l
   size.l
EndStructure
Global header.tHeader

;compare a pointer to the start of a chunk with ID strings in DataSection, first index starts at 1
Procedure CheckIDstring(*header.tHeader, index = -1)
  Protected indexMax = ((?EndChunkIDstrings - ?ChunkIDstrings) / SizeOf(Long)) + 1
  If index >= 1 And index < indexMax 
    If CompareMemory(*header, ?ChunkIDStrings + SizeOf(Long) * (index - 1), SizeOf(Long))
      ProcedureReturn index
    EndIf
  Else
    For index = 1 To indexMax 
      If CompareMemory(*header, ?ChunkIDStrings + SizeOf(Long) * (index - 1), SizeOf(Long))
        ProcedureReturn index
      EndIf
    Next
  EndIf
  
  ProcedureReturn 0 ; no match
EndProcedure

;returns ChunkIDstring as Unicode string
Procedure.s displayChunkIDstring(index)
  ProcedureReturn PeekS(?ChunkIDStrings + SizeOf(Long) * (index - 1), SizeOf(Long), #PB_Ascii)
EndProcedure

;returns ChunkIDstring as a string of bytes for display
Procedure.s displayChunkIDstringAsBytes(*header.tHeader)
  Protected i, output.s
  For i = 0 To 3
    output + "$" + Hex(PeekA(*header + i), #PB_Byte) + " "
  Next
  
  ProcedureReturn RTrim(output, " ")
EndProcedure


;test data to identify chunk ID String
#testdataByteLength = 12
Define *buffer, i, result
*buffer = AllocateMemory(#testdataByteLength * 2) ;make it bigger than needed
Restore SampleDataFromFile
If *buffer
  For i = 0 To 2
    CopyMemory(?SampleDataFromFile + i * #testdataByteLength, *buffer, #testdataByteLength)
    result =  CheckIDstring(*buffer)
    If result > 1
      Debug "DataSample " + i + " has chunkIDstring " + result + ": '" + displayChunkIDstring(result)
    Else
      Debug "DataSample had an unknown chunkIDstring: " + displayChunkIDstringAsBytes(*buffer)
    EndIf
  Next
EndIf


DataSection
  ChunkIDstrings:
  Data.a 'R', 'I', 'F', 'F'
  Data.a 'W', 'A', 'V', 'E'
  Data.a 'f', 'm', 't', ' '
  Data.a 'd', 'a', 't', 'a'
  EndChunkIDstrings:
  
  SampleDataFromFile:
  Data.a $52, $49, $46, $46, $24, $08, $00, $00, $57, $41, $56, $45
  Data.a $66, $6d, $74, $20, $10, $00, $00, $00, $01, $00, $02, $00; the rest of this chunk $22, $56, $00, $00, $88, $58, $01, $00, $04, $00, $10, $00
  Data.a $64, $61, $74, $61, $00, $08, $00, $00, $00, $00, $00, $00; the rest of this chunk $24, $17, $1e, $f3, $3c, $13, $3c, $14, $16, $f9, $18, $f9, $34, $e7, $23, $a6, $3c, $f2, $24, $f2, $11, $ce, $1a, $0d
  Data.a $62, $65, $65, $66, $24, $08, $00, $00, $57, $41, $56, $45
EndDataSection
The example is made up to demonstrate it and isn't very effective on its own but I hope it gives some ideas.
Wolfram
Enthusiast
Enthusiast
Posts: 568
Joined: Thu May 30, 2013 4:39 pm

Re: Structure ASCI UNICODE Problem

Post by Wolfram »

Thanks for all you help.

I think the best way is to work with constants and read it as long.

Code: Select all

#WAVE = $45564157 ;"WAVE"
#RIFF = $46464952  ;"RIFF"

Structure headerStruc
  type.l
  size.l
EndStructure

*h.headerStruc

If *h\type = #WAVE
  ;do something...
  
ElseIf *h\type = #RIFF
  ;do something...
  
EndIf
To define the constants I wrote a small PB helper.

Code: Select all

Procedure.s asci2Long(*buffer)
  ;//*buffer is the address of a string like @"WAVE" or @"'lpcm'"
  Dim output.a(4)
  
  *byte.character
  *byte = *buffer
  
  *comment.string
  *comment = @*buffer
  
  If *byte\c = 39
    *byte = *buffer + SizeOf(character)
    i =3
    While i >=0
      output(i) = *byte\c
      
      i -1
      *byte +SizeOf(character)
    Wend  

    ProcedureReturn "$" +Hex(PeekL(@output() ) , #PB_Long )+"  ;" +*comment\s
    
    
  Else

    While i < 4
      output(i) = *byte\c
      
      i +1
      *byte +SizeOf(character)
    Wend

    
    ProcedureReturn "$" +Hex(PeekL(@output() ) , #PB_Long )+"  ;" +Chr(34) +*comment\s +Chr(34)
  EndIf

  
EndProcedure
macOS Catalina 10.15.7
Post Reply