BFRES (File Format)
BFRES 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.
This article describes the BFRES file format seen in Mario Kart 8. It is unknown if the file format differs between games like the BRRES format does. 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||"FRES" File identifier, ASCII string.|
|0x04||1||Unknown has value 0x03 in Mario Kart 8 files.[FRES 1]|
|0x05||1||Unknown has value 0x00, 0x03 or 0x04 in Mario Kart 8 files.|
|0x06||1||Unknown has value 0x00 in Mario Kart 8 files.|
|0x07||1||Unknown has value 0x01, 0x02 or 0x04 in Mario Kart 8 files.|
|0x08||2||Byte order mark (BOM): 0xFE,0xFF for big endian and 0xFF,0xFE for little endian. Seems to indicate endian-ness of embedded files.|
|0x0a||2||Unknown Probably a version number[FRES 2], always has value 0x0010 in Mario Kart 8 files.|
|0x0c||4||File length including all headers, in bytes.|
|0x10||4||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[FRES 3], then all addresses will be correct for the graphics card. Always 0x00002000 for tracks?|
|0x14||4||File name offset (without file extension).|
|0x18||4||String table Length. Length in bytes of the String Table.|
|0x1c||4||String table offset.|
|0x20||4 × 12||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||File counts. Number of files in the index groups for each of the 12 subfile types. 0 for subfile types which are not present.|
|0x68||4||Unknown always 0 in Mario Kart 8 files.|
|0x6c||End of FRES header|
- Almost certainly 4 separate bytes as have not yet read BOM.
- This is an assumption because of the parallel with BRRES.
- That is to say, the address of the data is a multiple of this value.
The BFRES Index Group is a data structure that occurs very often within BFRES files and subfiles. It is a binary search tree in which a series of named data pointers are stored which are used to represent subfiles. It has a 0x08 byte header, as follows.
|0x00||4||Length of group in bytes.|
|0x04||4||Number in group (excluding the root entry).|
|0x08||End of index group header|
This is then followed by a number of entries equal to the number in the header. The first entry is never an actual data entry, but instead a reference point. Each entry is a 0x10 byte structure as follows.
|0x00||4||Search value. Refers to a single bit in the filename being searched for. (Right-shift by 3 to get the last byte index of the current name entry, and perform an & with 7 to get the bit index within that byte.)|
|0x04||2||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||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||Name pointer. Offset to the name of this entry.|
|0x0c||4||Data pointer. Offset to the data of this entry.|
|0x10||End of entry|
To find a specific filename in the index group using the binary 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
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.
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 all the data. This is probably a caching optimisation, as the CPU generally only deals with the headers of each format, and the GPU deals with the data. Grouping together the headers makes them all more likely to fit in the CPU's cache, speeding up file access.
|2||FSKA skeleton animations.|
|3||FSHU data. Unknown why 3 indices all use the same format.|
|6||FTXP texture pattern animations.|
|7||FVIS bone visibility. Unknown why 2 indices both use the same format. (Index 7 is unused in MK8, but can be found in some files in Super Mario Maker; for example, in /content/Pack/StaticSkin.pack/Model/MW_Enemy_koopa.szs/Output.bfres)|
|9||FSHA shader data.|
|10||FSCN scene data.|
|11||Embedded file. The data offset points to a offset & length pair which describe the embedded file. The embedded files always seem to be placed at the end of the BFRES fille. Shader source code has been seen in embedded files, though this is not typically the case.|
The following tool can operate on BFRES files: