Tes3Mod:BSA File Format

The UESPWiki – Your source for The Elder Scrolls since 1995
Jump to: navigation, search

Overview[edit]

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[edit]

Because files in a BSA do not store timestamps, they cannot be used as a "replacer"; Wrye Mash handles this excellently with its "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[edit]

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[edit]

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[edit]

Name Type/Size Info
fileSize ulong Size of the file
fileOffset ulong Offset of the file in the data section

Archive directory/name offsets[edit]

Name Type/Size Info
filenameOffset ulong Relative offset of the filename in the records section.

Filename records[edit]

Name Type/Size Info
filenameRecord zstring Lowercase ASCII, null-terminated. Offset given by corresponding filenameOffset.

Hash table[edit]

Name Type/Size Info
filenameHash hash Hashes of the filenames.

File data[edit]

Name Type/Size Info
Raw data blob Uncompressed and unseparated. The offset of each file is given by the corresponding fileOffset.

Sorting[edit]

All records except File Data are sorted by hash; file data appears to be sorted by the alphabetical order of file names.

Hash calculation[edit]

C++[edit]

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;

C#[edit]

Provided by RobinHood70.

public ulong GetHash(string name)
{
    name = name.ToLowerInvariant();
    var midPoint = name.Length >> 1;
    var low = new byte[4];
    var i = 0;
    while (i < midPoint)
    {
        low[i & 3] ^= (byte)name[i];
        i++;
    }

    var high = (uint)0;
    while (i < name.Length)
    {
        var temp = (uint)name[i] << (((i - midPoint) & 3) << 3);
        var bits = (int)(temp & 0x1F);
        high ^= temp;
        high = high << (32 - bits) | high >> bits;
        i++;
    }

    return BitConverter.ToUInt32(low, 0) | (ulong)high << 32;
}