BFRES (File Format)
BFRES (Binary caFe RESources) is an archive format used on Wii U and in Mario Kart 8 to store graphics data. Typically, a BFRES file will contain information for one model including textures and animations, but this is not necessarily the case. BFRES seems to be a direct upgrade of the BRRES format used on Wii. Due to their vast size, large BFRES files are often compressed using YAZ0 compression and will then have the file extension .szs.
There are different versions of the format and therefor the format varies in different games. This documentation mostly focus on the v3.4.0.X format. The basic architecture of a BFRES file is any number of subfiles each in one of 12 groups governing format and purpose. Each subfile is something like a model or a texture. Unless otherwise noted, all offsets in BFRES files are relative to themselves, not the beginning of the file which is more common in other formats.
Every BFRES file begins with an 0x6C byte FRES header.
|0x00||4||Char||File magic. Always FRES in ASCII.|
|0x04||4||UInt32||Version number of the BFRES file format, stored as four bytes. The content of the sub files depend on the version. Version numbers used in Mario Kart 8 are v184.108.40.206 (1426), v220.127.116.11 (4), v18.104.22.168 (1), v22.214.171.124 (8) and v126.96.36.199 (1).|
|0x08||2||UInt16||Byte Order Mark (BOM): 0xFEFF for big endian and 0xFFFE for little endian. Not handled on consoles, so the file data must be stored in the endianness of the target console (e.g. big endian for the Wii U).|
|0x0A||2||UInt16||Header length which technically only spans the bytes until File alignment, thus always 0x0010.|
|0x0C||4||UInt32||File length including all headers, in bytes.|
|0x10||4||UInt32||File alignment a power of 2. Most specific data alignment required in the file. The graphics card often requires data to be aligned to specific power of 2 boundaries. If the file is loaded into memory aligned to this value (a multiply of this value), then all addresses will be correct for the graphics card.|
|0x14||4||Int32||File name offset (without file extension).|
|0x18||4||Int32||String table length. Length in bytes of the string table.|
|0x1C||4||Int32||String table offset.|
|0x20||4 × 12||Int32||File offsets. Offsets to index groups for each of the 12 subfile types. 0 indicates that a particular subfile type is not present.|
|0x50||2 × 12||UInt16||File counts. Number of files in the index groups for each of the 12 subfile types. 0 for subfile types which are not present. This excludes the root entries, and is the same as each of the subfile counts in the index groups.|
|0x68||4||UInt32||User pointer, can be set at runtime, 0 in files.|
|0x6C||End of FRES header|
The format and purpose of subfiles is determined entirely by the index group they're in of the 12 main index groups references by the FRES header. Note that these subfiles are not necessarily continuous. Typically, all the headers for all subfiles occur at the start of the BFRES, followed by the string table, and then finally all the data referenced by the offsets in each of the subfile headers. This is probably a caching optimization, as the CPU generally only deals with the headers of each format, and the graphical data can be sent to the GPU in one go. Grouping together the headers makes them all more likely to fit in the CPU's cache, speeding up file access.
|5||FSHU||Texture SRT animation.|
|6||FTXP||Texture pattern animation.|
|7||FVIS||Bone visibility animation.|
|8||FVIS||Material visibility animation.|
The BFRES Index Group is a data structure that occurs very often within BFRES files and subfiles. It is a radix tree in which a series of named data pointers are stored which are used to represent subfiles or other structures. It is primarily used like a dictionary to quickly look up offsets to data by name.
It has a 0x08 byte header, as follows.
|0x00||4||UInt32||Length of group in bytes.|
|0x04||4||Int32||Number of entries (tree nodes) in the group, excluding the root entry (same as possibly available file counts in headers).|
|0x08||End of index group header|
This is then followed by a number of entries equal to the number in the header, plus a root entry. The root entry is never an actual data entry, but instead a reference point; it is always the first entry in the index group and is not included in the entry count.
Each entry is a 0x10 byte structure as follows.
|0x00||4||UInt32||Search value. Refers to a single bit in the filename being searched for. It's laid out like so: XXXXXYYY, where X is the index of the last valid byte (From the left) of the name string, and Y is the position of that bit within that byte.|
|0x04||2||UInt16||Left index. If the bit referred to by the search value is 0, jump to the index group entry with this index. (Index 0 is the dummy node at the beginning)|
|0x06||2||UInt16||Right index. If the bit referred to by the search value is 1, jump to the index group entry with this index. (Index 0 is the dummy node at the beginning).|
|0x08||4||Int32||Name pointer. Offset to the name of this entry.|
|0x0C||4||Int32||Data pointer. Offset to the data of this entry.|
|0x10||End of entry|
The node indices seem to be 0 when there is no/NULL node there.
To find a specific filename in the index group using the tree search algorithm, use the following pseudo-Python:
def searchIndexGroup(groupPtr, data, name): # groupPtr is an int containing the offset to the index group # data is the data for the entire BFRES # name is the name we're searching for (bytes, not str) # Returns the offset of the start of the file data, or -1 for failure firstEntry = groupPtr + 8 searchval = parse_u32(data, firstEntry + 0) leftIdx = parse_u16(data, firstEntry + 4) entries = groupPtr + 8 entry = entries + leftIdx * 16 nextsearchval = parse_u32(data, entry + 0) while searchval > nextsearchval: # Get the direction charpos = searchval >> 3 bitpos = searchval & 0b111 if charpos >= len(name): direction = 0 else: direction = (name[charpos] >> bitpos) & 1 # Go to the left index or right index based on the value of that bit searchval = nextsearchval if direction == 0: leftIdx = parse_u16(data, entry + 4) entry = entries + leftIdx * 16 else: rightIdx = parse_u16(data, entry + 6) entry = entries + rightIdx * 16 nextsearchval = parse_u32(data, entry + 0) # Ensure that we reached the correct file namePtr = entry + 8 + parse_u32(data, entry + 8) if not namePtr: return -1 namelen = parse_u32(data, namePtr - 4) if namelen != len(name): return -1 if parse_null_terminated_string(data, namePtr) != name: return -1 return entry
Arrays storing arbitrary numbers or strings under a textual key can be attached to many BFRES sections. An offset in a header of an applicable section points to an index group with each entry referencing a key, element count and type of such array, together with an array count stored in the same header. If no user data was attached, the offset and element count fields are both 0.
The data can be retrieved by a game at runtime for any purpose. For Mario Kart 8 files, user data is rare and usually only seen in materials, where it specifies 1-element Single arrays stored under gsys_include_bake and gsys_bake_texel_per_meter keys, with their exact use unknown.
The format of a user data array pointed to by the mentioned index group entries is as follows:
|0x00||4||Int32||Name offset. Points to the key of this user data with which it can be accessed by games.|
|0x04||2||UInt16||N = number of elements stored in the array.|
|0x06||1||Byte|| Type of the elements in the array.
|0x08||End of user data array header|
Numerical user data follows directly after this header as an array of N elements.
Textual data is stored as an array of offsets which point to strings following directly afterwards - they are not located in the file-wide string table. The strings are encoded in UTF-8 with the character set given above, and 0-terminated (there is no 4-byte length prefix as with strings stored in the string table). New lines should be represented with CRLF characters.
|0x00||4 × N||Int32[N]||N offsets to the strings. The strings are null-terminated and come directly after this array. They are not stored in the string table.|
The BFRES String Table is a table that occurs near the end of the BFRES file. There is no header information for the table. References to the table are stored as offsets to strings directly. The strings are stored in ASCII order (so Z comes before a). The length of each string is stored in the 4 bytes before that string and the strings are also null terminated. Each length is 4 byte aligned. The pointer in the FRES header points to the length of the first string.
Embedded files are found in the 11:th index group and can be any data. In Mario Kart 8 only shader archive data has been seen with one exception. PackunMusic.bfres is a special case because it contains shader source code for a shader. The shader file is called Turbo_UBER.bfsha (Cafe Turbo is the codename of Mario Kart 8).
The data offset in an index group points to the following structure:
|0x00||4||Int32||Offset to the file data.|
|0x04||4||UInt32||Length of the file data in bytes.|
|0x08||End of Embedded File header|
These structures are always placed directly after the main header. The file data is always placed at the end of the file.
The following tools can handle BFRES files:
- BFRES Extractor, by LordNed (can extract all BFRES sections)
- BFRES Tool, by AboodXD (can inject and extract textures)
- ModelThingy, by Ploaj (can view FSKA animations)
- NintenTools.Bfres (.NET library creating, loading, and saving BFRES files; scripts visualizing file layout)
- Wexos's Toolbox, by Wexos (can view, save and create files)