A parser for Bethesda's file formats
Project description
About
For example:
There are so many “unarchiver” tools for Bethesda’s archives (.bsa and .ba2), but no good programmatic way to read these filetypes. Using this package, understanding every little detail about an archive is simple and straight-forward (see BSA Usage and BA2 Usage).
For more advanced usage and information, check out the documentation.
The supported filetypes are parsers not writers.We do not currently support the writing of archives or plugins.
Installation
Because this is glorious Python, installing bethesda-structs should be super-duper simple.
Using PyPi
The fastest and quickest way to install this packages is by simply using pipenv (or if you’re oldschool pip).
$ pipenv install bethesda-structs
Using Git
You can install this package using Git by simply cloning the repo and building the package yourself!
$ git clone https://github.com/stephen-bunn/bethesda-structs.git
$ pipenv install --dev
$ pipenv run python setup.py install
Usage
ESP
This package aims to provide simple, programmatic access to the in-depth details of a plugin!
Because of how long it takes to build complete subrecord parers for a given plugin version, the only currently supported plugins are:
FNVPlugin - Fallout: New Vegas (partial)
F03Plugin - Fallout 3 (partial and experimental)
>>> from bethesda_structs.plugin.fnv import FNVPlugin >>> plugin = FNVPlugin.parse_file('/media/sf_VMShared/esp/fnv/NVWillow.esp') >>> print(plugin) FNVPlugin(filepath='/media/sf_VMShared/esp/fnv/NVWillow.esp') >>> >>> # print plugin header (is a record) ... >>> print(plugin.container.header) Container: type = u'TES4' (total 4) data_size = 163 flags = Container: master = True id = 0 revision = 0 version = 15 data = b'HEDR\x0c\x00\x1f\x85\xab?\x97\x12\x00\x00#\xad'... (truncated, total 163) subrecords = ListContainer: Container: type = u'HEDR' (total 4) data_size = 12 data = b'\x1f\x85\xab?\x97\x12\x00\x00#\xad\r\x00' (total 12) parsed = Container: value = Container: version = 1.340000033378601 num_records = 4759 next_object_id = 896291 description = u'Header' (total 6) Container: type = u'CNAM' (total 4) data_size = 9 data = b'llamaRCA\x00' (total 9) parsed = Container: value = u'llamaRCA' (total 8) description = u'Author' (total 6) Container: type = u'SNAM' (total 4) data_size = 16 data = b'NVWillow v.1.10\x00' (total 16) parsed = Container: value = u'NVWillow v.1.10' (total 15) description = u'Description' (total 11) Container: type = u'MAST' (total 4) data_size = 14 data = b'FalloutNV.esm\x00' (total 14) parsed = Container: value = u'FalloutNV.esm' (total 13) description = u'Master Plugin' (total 13) Container: type = u'DATA' (total 4) data_size = 8 data = b'\x00\x00\x00\x00\x00\x00\x00\x00' (total 8) parsed = Container: value = 0 description = u'File Size' (total 9) Container: type = u'ONAM' (total 4) data_size = 68 data = b'V\xe3\x0c\x00\xc3\xe3\x0c\x00\xc4\xe3\x0c\x00\xc5\xe3\x0c\x00'... (truncated, total 68) parsed = Container: value = ListContainer: 844630 844739 844740 844741 1372461 1372463 1383111 1385321 1387301 1387302 1387303 1387304 1387906 1457771 1479505 1520201 1544392 description = u'Overridden Records' (total 18) >>> >>> # iterate over KEYM records (only 1 in this plugin) ... >>> for record in plugin.iter_records('KEYM'): ... print(record) ... Container: type = u'KEYM' (total 4) data_size = 279 flags = Container: id = 17415634 revision = 0 version = 15 data = b'EDID\x17\x00WillowNova'... (truncated, total 279) subrecords = ListContainer: Container: type = u'EDID' (total 4) data_size = 23 data = b'WillowNovacBunga'... (truncated, total 23) parsed = Container: value = u'WillowNovacBungalowKey' (total 22) description = u'Editor ID' (total 9) Container: type = u'OBND' (total 4) data_size = 12 data = b'\xff\xff\xfc\xff\x00\x00\x01\x00\x04\x00\x00\x00' (total 12) parsed = Container: value = Container: X1 = -1 Y1 = -4 Z1 = 0 X2 = 1 Y2 = 4 Z2 = 0 description = u'Object Bounds' (total 13) Container: type = u'FULL' (total 4) data_size = 27 data = b'Dino Dee-lite Bu'... (truncated, total 27) parsed = Container: value = u'Dino Dee-lite Bungalow Key' (total 26) description = u'Name' (total 4) Container: type = u'MODL' (total 4) data_size = 23 data = b'Clutter\\Key01Dir'... (truncated, total 23) parsed = Container: value = u'Clutter\\Key01Dirty.NIF' (total 22) description = u'Model Filename' (total 14) Container: type = u'ICON' (total 4) data_size = 48 data = b'Interface\\Icons\\'... (truncated, total 48) parsed = Container: value = u'Interface\\Icons\\PipboyImages\\Ite'... (truncated, total 47) description = u'Large Icon Filename' (total 19) Container: type = u'MICO' (total 4) data_size = 66 data = b'Interface\\Icons\\'... (truncated, total 66) parsed = Container: value = u'Interface\\Icons\\PipboyImages_sma'... (truncated, total 65) description = u'Small Icon Filename' (total 19) Container: type = u'SCRI' (total 4) data_size = 4 data = b'T.\n\x01' (total 4) parsed = Container: value = FormID(form_id=17444436, forms=['SCPT']) description = u'Script' (total 6) Container: type = u'YNAM' (total 4) data_size = 4 data = b'\xbb\x10\x07\x00' (total 4) parsed = Container: value = FormID(form_id=463035, forms=['SOUN']) description = u'Sound - Pick Up' (total 15) Container: type = u'ZNAM' (total 4) data_size = 4 data = b'\xbc\x10\x07\x00' (total 4) parsed = Container: value = FormID(form_id=463036, forms=['SOUN']) description = u'Sound - Drop' (total 12) Container: type = u'DATA' (total 4) data_size = 8 data = b'\x00\x00\x00\x00\x00\x00\x00\x00' (total 8) parsed = Container: value = Container: value = 0 weight = 0.0 description = u'Data' (total 4)
BSA
Bethesda’s default archive structure.
>>> from bethesda_structs.archive.bsa import BSAArchive >>> archive = BSAArchive.parse_file('/media/sf_VMShared/bsa/Campfire.bsa') >>> print(archive) BSAArchive(filepath=PosixPath('/media/sf_VMShared/bsa/Campfire.bsa')) >>> >>> # print archive header ... >>> print(archive.container.header) Container: magic = b'BSA\x00' (total 4) version = 105 directory_offset = 36 archive_flags = Container: directories_named = True files_named = True directory_count = 4 file_count = 493 directory_names_length = 50 file_names_length = 14839 file_flags = Container: >>> >>> # print last directory block, containing 1 file record ... >>> print(archive.container.directory_blocks[-1]) Container: name = u'meshes\\mps\x00' (total 11) file_records = ListContainer: Container: hash = 16183754957220078963 size = 2384 offset = 25094933 >>> >>> # print archived filenames (only first 5, 488 more) ... >>> print(archive.container.file_names) ListContainer: _camp_objectplacementindicatorthread01.psc _camp_objectplacementindicatorthread02.psc _camp_objectplacementindicatorthread03.psc _camp_tentsitlayscript.psc campcampfire.psc ... >>> >>> # extract archive to directory ... >>> archive.extract('/home/USER/Downloads')
BA2
The two available archive subtypes are both supported.
General (GNRL)
Used to store generic files in a compressed/bundled file.
>>> from bethesda_structs.archive.btdx import BTDXArchive >>> archive = BTDXArchive.parse_file('/media/sf_VMShared/ba2/CheatTerminal - Main.ba2') >>> print(archive) BTDXArchive(filepath=PosixPath('/media/sf_VMShared/ba2/CheatTerminal - Main.ba2')) >>> >>> # print archive header ... >>> print(archive.container.header) Container: magic = b'BTDX' (total 4) version = 1 type = u'GNRL' (total 4) file_count = 982 names_offset = 3600179 >>> >>> # print first archive file entry ... >>> print(archive.container.files[0]) Container: hash = 153050373 ext = u'pex' (total 3) directory_hash = 1081231424 offset = 35376 packed_size = 0 unpacked_size = 887 >>> >>> # extract archive to directory ... >>> archive.extract('/home/USER/Downloads')
Direct Draw (DX10)
Used to store (specifically) Microsoft Direct Draw textures.
>>> from bethesda_structs.archive.btdx import BTDXArchive >>> archive = BTDXArchive.parse_file('/media/sf_VMShared/ba2/AK74m - Textures.ba2') >>> print(archive) BTDXArchive(filepath=PosixPath('/media/sf_VMShared/ba2/AK74m - Textures.ba2')) >>> >>> # print archive header ... >>> print(archive.container.header) Container: magic = b'BTDX' (total 4) version = 1 type = u'DX10' (total 4) file_count = 116 names_offset = 329069673 >>> >>> # print first archive file entry ... >>> print(archive.container.files[0]) Container: header = Container: hash = 362144756 ext = u'dds' (total 3) directory_hash = 1416395408 chunks_count = 4 chunk_header_size = 24 height = 2048 width = 2048 mips_count = 12 format = 99 chunks = ListContainer: Container: offset = 11136 packed_size = 2714729 unpacked_size = 4194304 start_mip = 0 end_mip = 0 Container: offset = 2725865 packed_size = 840614 unpacked_size = 1048576 start_mip = 1 end_mip = 1 Container: offset = 3566479 packed_size = 217598 unpacked_size = 262144 start_mip = 2 end_mip = 2 Container: offset = 3784077 packed_size = 71579 unpacked_size = 87408 start_mip = 3 end_mip = 11 >>> >>> # extract archive to directory ... >>> archive.extract('/home/USER/Downloads')
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file bethesda-structs-0.1.4.tar.gz
.
File metadata
- Download URL: bethesda-structs-0.1.4.tar.gz
- Upload date:
- Size: 44.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.1.0 requests-toolbelt/0.9.1 tqdm/4.34.0 CPython/3.6.8
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 024f994e35b034b3833b319335c58a3a8b18b6063d5364536daafd3aca5c6c48 |
|
MD5 | a1f01449ff62a94cbc4c0f82d6a0eec6 |
|
BLAKE2b-256 | acd82b6970d4568096743ec25f9fe73dd44f95b6ba29573606238cec6e4a28de |