Skip to content

Instantly share code, notes, and snippets.

@gulopine
Created November 13, 2011 08:27
Show Gist options
  • Save gulopine/1361829 to your computer and use it in GitHub Desktop.
Save gulopine/1361829 to your computer and use it in GitHub Desktop.
BSA parser using Biwako
from biwako import byte, common
class Header(byte.Structure, endianness=byte.LittleEndian):
bsa = byte.FixedString(b'BSA\x00')
version = byte.FixedInteger(104, size=4)
offset = byte.FixedInteger(0x24, size=4)
flags1 = byte.Integer(size=4)
folder_count = byte.Integer(size=4)
file_count = byte.Integer(size=4)
folder_name_length = byte.Integer(size=4)
file_names_length = byte.Integer(size=4)
flags2 = byte.Integer(size=4)
class Folder(byte.Structure, endianness=byte.LittleEndian):
hash = byte.Integer(size=8)
file_count = byte.Integer(size=4)
offset = byte.Integer(size=4)
class File(byte.Structure, endianness=byte.LittleEndian):
hash = byte.Integer(size=8)
size = byte.Integer(size=4)
offset = byte.Integer(size=4)
class Path(byte.Structure, endianness=byte.LittleEndian):
path = byte.LengthIndexedString(size=1)
class BSA(byte.Structure, endianness=byte.LittleEndian):
header = common.SubStructure(Header)
folders = common.List(common.SubStructure(Folder), size=header.folder_count)
path_field = byte.LengthIndexedString(size=1)
filename_field = byte.String()
if __name__ == '__main__':
# Lots of hackery to work around flattened lists
# Hopefully Biwako will get better at handling these
bsa = BSA(open(sys.argv[1], 'rb'))
for folder in bsa.folders:
_, path = path_field.read_value(bsa)
files = []
for i in range(folder.file_count):
file = File(bsa)
file.offset # Force evaluation of the structure
for file in files:
_, filename = filename_field.read_value(bsa)
print("%s\\%s hash=%08X offset=%d length=%d" % \
(path, filename, file.hash, file.offset, file.size))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment