Struct.error: argument for 's' must be a bytes object

Basically, I don’t know anything about Python. I’m just trying to make this old script run but I’m getting the following error when executing it:

Wave Bank/Music_0
Wave Bank/Music_1
Wave Bank/Music_2
Wave Bank/Music_3
Wave Bank/Music_4
Wave Bank/Music_5
Traceback (most recent call last):
File “/home/maquinote/.local/share/Steam/steamapps/common/Terraria/Content/makeXwb.py”, line 95, in
main(argv[1], argv[2:])
File “/home/maquinote/.local/share/Steam/steamapps/common/Terraria/Content/makeXwb.py”, line 16, in main
bankData = struct.pack(“II64sIIIIII”,
struct.error: argument for ‘s’ must be a bytes object

Any help appreciated. Here’s the code:

import struct
import os
import glob

def main(outFileName, files, name="Wave Bank"):
   
   with open(outFileName, "wb") as outFile:
      outFile.write("WBND".encode())
      outFile.write(struct.pack("II",
         46, #major version
         44 #minor version
      ))
      wmas = sorted(glob.glob('Wave Bank/Music_*'), key=lambda x:int(x.split('_')[1]))
      for wma in wmas:
         print (wma)
      bankData = struct.pack("II64sIIIIII", 
         0, #flags 
         len(wmas),
         name,
         24, #entry meta data size
         64, #entry name size
         0, #alignment
         0, #format data for compact format
         0, #unkn?
         0, #unkn?
      )
      
      metaDatas = []
      playRegionCur = 0
      for wma in wmas:
         codec = 4
         chans = 2
         rate = 44100
         align = 99
         bits = 0
         
         format = (
            (codec & 0b11) |
            ((chans & 0b111) << 2) |
            ((rate & 0x3ffff) << (2+3)) |
            ((align & 0xff) << (2+3+18)) |
            ((bits & 1) << (2+3+18+8))
         )
         
         playDataSize = os.path.getsize(wma)
         
         metaData = struct.pack("IIIIII",
            0, #flags and duration...?
            format,
            playRegionCur, #play region offset
            playDataSize, #play region length
            0, #loop region offset
            0  #loop region length
         )
         metaDatas.append(metaData)
         playRegionCur += playDataSize
      
      metaDatas = ''.join(metaDatas)
      
      seekTable = ""
      unknRegion = ""
      
      headerFormat = "IIIIIIIIII"
      headerEndOffset = outFile.tell()+struct.calcsize(headerFormat)
      outFile.write(struct.pack(headerFormat,
         headerEndOffset, #bank data offset
         len(bankData), #bank data len
         
         headerEndOffset+len(bankData), #entry metadata offset
         len(metaDatas), #entry metadata len
         
         headerEndOffset+len(bankData)+len(metaDatas), #seek table offset
         len(seekTable), #seek table len
         
         headerEndOffset+len(bankData)+len(metaDatas)+len(seekTable), #unkn region offset
         len(unknRegion),
         
         headerEndOffset+len(bankData)+len(metaDatas)+len(seekTable)+len(unknRegion), #play region offset
         playRegionCur
      ))
      
      assert outFile.tell() == headerEndOffset
      
      outFile.write(bankData)
      outFile.write(metaDatas)
      outFile.write(seekTable)
      outFile.write(unknRegion)
      
      for wma in wmas:
         outFile.write(open(wma, "rb").read())


if __name__ == "__main__":
   from sys import argv
   main(argv[1], argv[2:])

As it says. The name that you pack into that struct can’t be a string, because strings are an abstraction for Unicode text with some particular encoding. They aren’t raw data and don’t have an unambiguous mapping to raw data. You have to either supply a bytes object (such as a bytes literal with a preceding b: b'Wave Bank', or else explain how to encode the string in raw data by using its .encode method and supplying an encoding. (Actually, you already know this, because you’re already doing it in the code: outFile.write("WBND".encode()).) You have to know which encoding you want to use, which will depend on doing research depending on the data format you’re trying to implement. (Without an argument, you get the default utf-8 encoding, which may or may not be correct for your purposes. But assuming that WBND is supposed to be a FourCC identifier, it makes more sense to use a bytes literal there anyway: outFile.write(b'WBND').)

As an aside, currently your command line parsing doesn’t give you a way to override the default name. Is that intentional?

1 Like