Overview
BSA files are the archive format used by Morrowind, its expansions, and modders. It is a relatively simple container format compared to the Oblivion BSA format. Several tools have been written to open and create them.
The information presented here has been pulled from ghostwheel's site and Timeslip's BSA code for NifSkope.
Limitations
Because files in a BSA do not store timestamps, they cannot be used as a "replacer"; Wrye Mash handles this excellently with it's "Bain for Mash" installer features.
Sound, Music, Fonts and Splash screens are not recognized inside a BSA.
Files in a BSA must have unique filename hashes.
The format
A Morrowind BSA is broken into 6 sections:
Name | Size (bytes) | Info |
---|---|---|
Header | 12 | Stores a magic number, lookup pointer, and the total number of files |
File size/offset | 8 * number of files | |
Name offsets | 4 * number of files | |
Names | hashOffset - (12 * number of files) | |
Hashes | 8 * number of files | Used as the sort key for all records except raw file data |
Raw data | (blob) | Sorted by filename? |
Header
Name | Type/Size | Info |
---|---|---|
version | byte[4] | 0x00000100 |
hashOffset | ulong | Offset of the hash table in the file, minus the header size (12). Add 8 * fileCount to find the offset of the data section. |
fileCount | ulong | Number of files in the BSA, and number of records for every other section. |
File sizes/offsets
Name | Type/Size | Info |
---|---|---|
fileSize | ulong | Size of the file |
fileOffset | ulong | Offset of the file in the data section |
Archive directory/name offsets
Name | Type/Size | Info |
---|---|---|
filenameOffset | ulong | Relative offset of the filename in the records section. |
Filename records
Name | Type/Size | Info |
---|---|---|
filenameRecord | zstring | Lowercase ASCII, null-terminated. Offset given by corresponding filenameOffset. |
Hash table
Name | Type/Size | Info |
---|---|---|
filenameHash | hash | Hashes of the filenames. |
File data
Name | Type/Size | Info |
---|---|---|
Raw data | blob | Uncompressed and unseparated. The offset of each file is given by the corresponding fileOffset. |
Sorting
All records except File Data are sorted by hash; file data appears to be sorted by the alphabetical order of file names.
Hash calculation
From the source of bsapack:
unsigned l = (len>>1); unsigned sum, off, temp, i, n; for(sum = off = i = 0; i < l; i++) { sum ^= (((unsigned)(name[i]))<<(off&0x1F)); off += 8; } hash.value1 = sum; for(sum = off = 0; i < len; i++) { temp = (((unsigned)(name[i]))<<(off&0x1F)); sum ^= temp; n = temp & 0x1F; sum = (sum << (32-n)) | (sum >> n); // binary "rotate right" off += 8; } hash.value2 = sum;