From CloudModding TWW Wiki

BMD and BDL are the two formats that store model data in Nintendo games for the GameCube and the Wii, as part of Nintendo's J3DGraph library, which is part of their larger in-house JSYSTEM toolkit. It first appeared in Luigi's Mansion, where it was used only for effects and jewels. BMD was then used as the only model format in Super Mario Sunshine, and The Wind Waker introduced the BDL format. The most recent game to use this model format is Super Mario Galaxy 2. BMD is an abbreviation for Binary MoDel, and BDL is an abbreviation for Binary Display List.

BMD and BDL are almost entirely the same. BDL is an extension of BMD with an additional section for material FIFO instructions called MDL3. MDL3 is likely used for making rendering more efficient.

File Conventions

This file follows JSYSTEM binary conventions, including the file's header and chunk layouts. It's also common practice to lay out an array of structures, instead, in structure-of-arrays style.

Model Info (INF1)

This has miscellaneous information about how to read the rest of the model, the most important being the hierarchy, which describes how the shapes, materials, and joints are combined. It's laid out as a flattened hierarchy of joints, materials, and geometry data. It is used to determine the joint hierarchy, as well as the order in which to render the different parts of the geometry. It consists of a header followed by the hierarchy data.

Header

The header contains the size of the chunk, the total number of matrix groups, and the total number of vertexes in the model.

Offset Type Name Description
0x00
char[4] Chunk Type INF1
0x04
int Size
0x08
short Misc Flags
0x0A
short Padding 0xFFFF
0x0C
int Matrix Group Count The total number of matrix groups in the file.
0x10
int Vertex Count The total number of vertices in the VTX1 section.
0x14
int Hierarchy Data Offset Offset to the hierarchy data.

Hierarchy Data

The hierarchy is made up of nodes that give the node type and an index for the associated data, if necessary. A node looks like this:

Offset Type Name Description
0x00
short NodeType See the Hierarchy Node Types below.
0x02
short Data Optional data value, used by the Joint, Material or Shape node types.

Hierarchy Node Types

There are 6 node types - 3 for manipulating the hierarchy (Finish, New Node, and End Node) and 3 for actual model data (Joint, Material, Shape).

Value Name Meaning
0x00 Finish Ends the hierarchy
0x01 New Node Creates a child of the previous node
0x02 End Node Closes the current child node and returns to the parent node
0x10 Joint Adds a joint to the hierarchy
0x11 Material Adds a material to the hierarchy
0x12 Shape Adds geometry data to the hierarchy

Vertex Data (VTX1)

This chunk defines vertexes and the attributes associated with them. A vertex always includes position data, but it may also include UVs for texturing, normals for lighting, or vertex colors. All of this data is stored here.

The chunk has a header, which is followed by the definitions of each vertex attribute that is defined within the chunk. After these definitions are banks holding the data for each attribute.

Header

Offset Type Name Description
0x00
char[4] Chunk Type VTX1
0x04
int Size
0x08
int Vertex Format Offset Offset to the VertexFormat section.
0x0C
int Position Data Array Offset Offset to the data array for the Position attribute.
0x10
int Normal Data Array Offset Offset to the data array for the Normal attribute.
0x14
int NBT Data Array Offset Offset to the data array for the NBT attribute.
0x18
int[2] Color Data Array Offset Offsets to the data array for the Color attributes.
0x20
int[8] Texcoord Data Array Offset Offsets to the data array for the Texcoord attributes.

If an attribute has no data, the data array offset for that attribute will be 0.

Vertex Attributes

Each vertex attribute gives its type, a component count, and a data type. It may also include a decimal point value to use for compressed data types.

Offset Type Name Description
0x00
GXAttr Attribute Type The vertex attribute being defined
0x04
GXCompCnt Component Count
0x08
GXCompType Component Type
0x0C
byte Component Shift Shift for fixed-point attribute types
0x0D
byte[3] Padding 0xFFFFFF

This area of the chunk is padded to the nearest 32 bytes with the padding string. For example, if there are only 3 attributes in the list, there are 16 bytes of the padding string after the last attribute, filling the space where a 4th attribute would go.

Attribute Types

GXAttr is an enum as follows:

Type Name Meaning
0x00 Position Matrix  ?
0x01 Tex Matrix 0  ?
0x02 Tex Matrix 1  ?
0x03 Tex Matrix 2  ?
0x04 Tex Matrix 3  ?
0x05 Tex Matrix 4  ?
0x06 Tex Matrix 5  ?
0x07 Tex Matrix 6  ?
0x08 Tex Matrix 7  ?
0x09 Position Position of the vertex in space
0x0A Normal Normal for the vertex used for lighting calculations
0x0B Color 0 Vertex color for color slot 0
0x0C Color 1 Vertex color for color slot 1
0x0D Tex 0 UV coordinates for texture 0
0x0E Tex 1 UV coordinates for texture 1
0x0F Tex 2 UV coordinates for texture 2
0x10 Tex 3 UV coordinates for texture 3
0x11 Tex 4 UV coordinates for texture 4
0x12 Tex 5 UV coordinates for texture 5
0x13 Tex 6 UV coordinates for texture 6
0x14 Tex 7 UV coordinates for texture 7
0x19 Normal/Binormal/Tangent  ?
0xFF Null Attribute Terminate the attribute list. There are no more attributes.

Data Types

GXCompType is as follows. There are two versions of the list of data types. The first version is for primitive data types such as bytes and shorts, while the other version is for how vertex colors are stored. Thus, the attributes Color 0 and Color 1 use a value from the Colors data type table, while everything else uses the Primitives table.

Primitives:

Type Name
0x00 Unsigned Byte
0x01 Signed Byte
0x02 Unsigned Short (int16)
0x03 Signed Short (int16)
0x04 Float (single)

Colors:

Type Name
0x00 RGB565
0x01 RGB8
0x02 RGBX8
0x03 RGBA4
0x04 RGBA6
0x05 RGBA8

Data Arrays

The last portion of this chunk is made up of the data array holding the data for the vertex attributes. Each bank is located at the corresponding offset in the chunk's header, and is stored in the data type specified in the vertex attribute definition. Each data array is aligned to the nearest 8 bytes with the standard padding string.

Skinning Envelopes (EVP1)

This chunk defines the envelope matrices for weighted skinning, allowing multiple bones to move one set of geometry data. See the DRW1 and SHP1 matrix group section to determine how these matrices are eventually used.

Header

Offset Type Name Description
0x00
char[4] Chunk Type EVP1
0x04
int Size
0x08
short Envelope Matrix Count The number of envelope matrices in this section
0x0A
short Padding 0xFFFF
0x0C
int Joint Count Table Offset
0x10
int Index Table Offset
0x14
int Weight Table Offset
0x18
int Inverse Bind Pose Table Offset

Each envelope matrix is made up of multiple joints which contribute to the matrix, in different weights. For an example, an envelope matrix for a typical human skeleton might want to define an "elbow" section which is influenced equally by the two different parts of the arm. To do this, it would specify two joints, specifying each of the arm joints in the index table, with 0.5 and 0.5 as weights.

Joint Count Table

This gives the number of joints that influence a particular envelope matrix.

Offset Type Name Description
0x00
byte Joint Count

Index and Weight tables

The joint indices and joint weights are used to scale each joint that affects the envelope matrix. The weights of each joints should add up to a sum of 1; otherwise, the vertexes do not move with the bones correctly.

These arrays are paired -- that is, both the joint index and joint weight arrays should be the same size.

Joint Index Table

Offset Type Name Description
0x00
byte Joint Index Index of the joint, as defined by the JNT1 section

Joint Weight Table

Offset Type Name Description
0x00
float Weight Weight of this joint in the envelope matrix.

Inverse Bind Pose Matrices

Vertices that are destined to be enveloped are not stored in joint-local space, as they do not have a singular joint parent to be local to. Instead, they are stored in the model's global space, which is typically a "bind pose" like an A pose or a T pose. To convert each enveloped vertex from the model's global space to the relative space for each joint so it can be skinned, then an inverse-bind-pose matrix must be first applied. This is equivalent to the inverse model matrix of each joint, but it is stored precomputed in the model file for efficiency purposes. Each joint referenced for the envelope should have a proper inverse bind pose matrix in this array, stored at its joint index.

Offset Type Name Description
0x00
MTX34 Inverse Bind Pose Matrix

Draw Matrix Array (DRW1)

This chunk determines how the global draw matrix array list is built. It is made up of a header, followed by the matrix type array and the data array.

Header

Offset Type Name Description
0x00
char[4] Chunk Type EVP1
0x04
int Size
0x08
short Element Count
0x0A
short Padding 0xFFFF
0x0C
int Matrix Type Array Offset
0x10
int Data Array Offset

Partial Weight Array

This is an array of matrix type values. The possible matrix types are "joint" (0x00), and "envelope" (0x01).

// 1 byte long
/*0x00*/ DRWMatrixType MatrixType;

Data Array

The data array is made up of a single short. If the respective matrix type is joint, then it contains a joint index (see the JNT1 section). If it is envelope, then it contains an envelope matrix index (see the EVP1 section).

// 2 bytes long
/*0x00*/ short Data;

Joint Data (JNT1)

This chunk defines joint data, including position, scale, rotation, and names. It has a header followed by the joint data and a string table.

Header

Offset Type Name Description
0x00
char[4] Chunk Type JNT1
0x04
int Size
0x08
short Joint Count
0x0A
short Padding 0xFFFF
0x0C
int Joint Transform Table Offset
0x10
int Remap Table Offset
0x14
int Name Table Offset

There are JointCount joints. Each joint is defined as a name in the name table, and an index in the remap table. The config table determines the joint's settings and initial configuration. It is possible, if unlikely, for two joints with different names to have the same configuration. In this case, JointConfigData will not have JointCount entries. These should be treated as two separate joints that share a single configuration.

Joint Transformation Data

This includes scaling, rotation, and translation data for the joint, as well as a bounding sphere radius and bounding box information.

Offset Type Name Description
0x00
MatrixTypeFlags (short) Matrix Type Flags Affects Joint Matrix Calculation
0x02
bool Do Not Inherit Parent Scale
0x03
byte Padding 0xFF
0x04
float Scale X
0x08
float Scale Y
0x0C
float Scale Z
0x10
short Rotation X
0x12
short Rotation Y
0x14
short Rotation Z
0x16
short Padding 0xFFFF
0x18
float Translation X
0x1C
float Translation Y
0x20
float Translation Z
0x24
float Bounding Sphere Radius
0x28
float Bounding Box Min X
0x2C
float Bounding Box Min Y
0x30
float Bounding Box Min Z
0x34
float Bounding Box Max X
0x38
float Bounding Box Max Y
0x3C
float Bounding Box Max Z

Rotation is stored in signed units that range from -0x7FFF to 0x7FFF, corresponding to -180 degrees and 180 degrees, respectively.

Shape Data (SHP1)

SHP1 stores the geometry data for the model. The data is split into "shapes", with each shape having multiple "matrix groups." Each shape can have different vertex attributes active, which translates into the actual geometry data differing in vertex structure between shapes. However, since the GameCube can only support 10 matrices per draw call, which might not be enough for some complex skinned meshes, each shape can be split up into different matrix group, each of which can use a separate draw matrix table. The matrices eventually referenced by this table are used together with the PNMTXIDX vertex attribute.

The chunk begins with a header. This contains counts and offsets to each section of the data. After this is the shape data, defining shapes with a bounding sphere radius, a bounding box, and an offset to the attributes that the shape uses. An index table, the attribute table, the matrix table, the geometry data, the matrix data, and the draw primitive data follow.

Header

// 0x2C/44 bytes long
/*0x00*/ int char[4] Magic; // 'SHP1'
/*0x04*/ int ChunkSize; // Total bytes of chunk
/*0x08*/ short SectionCount; // How many shapes there are
/*0x0A*/ short Padding; // 0xFFFF/-1
/*0x0C*/ int ShapeDataOffset; // Offset to Shape's  (Relative to SHP1Header start)
/*0x10*/ int RemapTableOffset;
/*0x14*/ int NameTableOffset; // Usually zero
/*0x18*/ int AttribTableOffset; // Offset to AttribTable
/*0x1C*/ int MatrixTableOffset; // Offset to MatrixTable
/*0x20*/ int PrimitiveDataOffset; // Offset to the actual primitive data
/*0x24*/ int MatrixDataOffset; // Offset to MatrixData
/*0x28*/ int MatrixGroupTableOffset;

Remap Table

Much like joints and materials, a remap table is provided to allow the shape at index N to be pulled from a different shape entry in the file. This seems to be identity most of the time. Similarly, the offset at 0x14 would normally point to a name table, but this has not been observed in the wild yet.

Shape Data

A shape is a series of draw calls that use the same vertex attributes. A shape also has a bounding box and a bounding sphere.

0x00
byte Matrix Type See below
0x01
byte Padding 0xFF
0x02
short Matrix Group Count How many matrix groups to this shape
0x04
short Attributes Offset Relative to SHP1Header.AttribsTableOffset
0x06
short First Matrix Data Index Index of the first MatrixData for this shape
0x08
short First Matrix Group Index Index to first matrix group ('Matrix Group Count' consecutive Indexes)
0x0A
short Padding 0xFFFF
0x0C
float Bounding Sphere Radius
0x10
float Bounding Box Min X
0x14
float Bounding Box Min Y
0x18
float Bounding Box Min Z
0x1C
float Bounding Box Max X
0x20
float Bounding Box Max Y
0x24
float Bounding Box Max Z

The Attributes Offset variable is the offset to the first vertex attribute to enable for the shape. After this first attribute is enabled, the next is enabled, and so on, until an attribute with the type 0xFF (null attribute) is encountered.

Matrix Type

This enum determines how the position and normal matrices are calculated for the shape.

Type Name
0x00 Single Matrix
0x01 Billboard
0x02 Y Billboard
0x03 Multi Matrix (Skinned Model)

Vertex Attributes

This is a list of the vertex attributes that are enabled for each shape. They are stored similarly to the attributes in the VTX1 chunk, but they consist of only the attribute type and the data type. This data type refers to the type of the index into the VTX1 data banks, and not the type of the data itself. Because of this, the data type for these attributes is typically a signed short (0x03).

// 8 bytes long
/*0x00*/ int AttributeType;
/*0x04*/ int DataType;

For information on what values AttributeType and DataType can be, see Attribyte Types and Data Types in the VTX1 section.

Matrix Table

This section stores indexes of draw matrices, as defined by DRW1. There are 10 of them, and they map to the 10 hardware matrix slots available through GX.

// 20 bytes long
/*0x00*/ short DrawMatrixIndex[10];

This index can also sometimes be 0xFFFF. If this happens, then the index for this slot should be taken from the previous matrix group. An index of 0xFFFF should not appear on the first matrix group.

Primitive Display List Data

This is the actual geometry data, formatted as a GX display list, which is a number of draw call commands. Each draw call command has a primitive type, a vertex count, and then the vertex data.

// Header is 3 bytes, vertex data varies in length
/*0x00*/ byte PrimitiveType;
/*0x01*/ short VertexCount;

Each vertex consists of an index corresponding to the active vertex attributes. For example, if a shape has the position, color 1, and tex 1 attributes active, one vertex would have an index into the position data bank, the color 1 data bank, and the tex 1 data bank.

Primitives are padded to the nearest 8 bytes with zero-value bytes.

Primitive Types

Below are the primitive types supported by the GameCube's GPU. Note that although some of them exist, they may not be used in practice. It appears that BMD and BDL most often use Triangle Strips, and rarely Triangle Fans.

Type Name
0x80 Quads
0x90 Triangles
0x98 Triangle Strips
0xA0 Triangle Fans
0xA8 Lines
0xB0 Line Strips
0xB8 Points

Matrix Data

// 8 bytes long
/*0x00*/ short UseMtxIndex;
/*0x02*/ short UseMtxCount; // `Count` many consecutive indexes into MatrixTable
/*0x04*/ int FirstUseMtxIndex;

Each matrix group can reference a sub-section of the MatrixTable through this MatrixData chunk. This determines the matrix table for this matrix group. The matrix table references indexes into the draw matrix array (see the DRW1 chunk). The PNMTXIDX vertex attribute determines which matrix out of this table is applied for which vertex.

If the DisplayFlags is 0x03 (Multi), then UseMtxCount and FirstUseMtxIndex determine a variable-length matrix array and UseMtxIndex is unused. Otherwise, the matrix to use for the shape is defined by UseMtxIndex, and UseMtxCount and FirstUseMtxIndex are unused. However, it has been found by observation that official BMD/BDL files will set up a matrix table of size 1 with a single entry, equal to UseMtxIndex.

Primitive Location Data

This gives the size and location of every matrix group in the model.

// 4 bytes long
/*0x00*/ int Size; //Size in bytes of the primitive draw display list
/*0x04*/ int Offset; //Offset relative to `SHP1Header.PrimitiveDataOffset`

Note that the offset given by the location data is relative to the start of the primitive data - that is, the value of PrimitiveDataOffset in the SHP1's header.

Material Data (MAT3)

To-do

Header

Size: 0x84 bytes.

Offset Type Name Description
0x00
string Magic Should be "MAT3".
0x04
int Section Size
0x08
short Num Materials
0x0A
short Padding 1
0x0C
int Material Entry Data Offset
0x10
int Material Remap Table Offset
0x14
int String Table Offset
0x18
int Indirect Texturing Info Offset
0x1C
int Cull Mode Info Offset
0x20
int Material Colors Offset
0x24
int Num Color Channels Offset
0x28
int Color Channel Info Offset
0x2C
int Ambient Colors Offset
0x30
int Light Info Offset
0x34
int Num Tex Gens Offset
0x38
int Tex Gen Info Offset
0x3C
int Post Tex Gen Info Offset
0x40
int Tex Matrix Info Offset
0x44
int Post Tex Matrix Info Offset
0x48
int Texture Remap Table Offset
0x4C
int Tev Order Info Offset
0x50
int Tev Colors Offset
0x54
int Tev Konst Colors Offset
0x58
int Num Tev Stages Offset
0x5C
int Tev Stage Info Offset
0x60
int Tev Swap Mode Info Offset
0x64
int Tev Swap Mode Table Info Offset
0x68
int Fog Info Offset
0x6C
int Alpha Compare Info Offset
0x70
int Blend Mode Info Offset
0x74
int ZMode Info Offset
0x78
int ZCompare Info Offset
0x7C
int Dither Info Offset
0x80
int NBTScale Info Offset

Material Entry

Size: 0x14C bytes.

Offset Type Name Description
0x000
byte Pixel Engine Mode 1 = Opaque, 2 = Alpha Test, 4 = Translucent
0x001
byte Cull Mode Index
0x002
byte Num Color Channel Controls Index
0x003
byte Num Tex Gens Index
0x004
byte Num Tev Stages Index
0x005
byte ZCompLoc Index
0x006
byte ZMode Index
0x007
byte Dither Index
0x008
short[2] Material Color Indexes
0x00C
short[4] Color Channel Control Indexes
0x014
short[2] Ambient Color Indexes
0x018
short[8] Light Color Indexes
0x028
short[8] Tex Gen Info Indexes
0x038
short[8] Post Tex Gen Info Indexes
0x048
short[10] Tex Matrix Indexes
0x05C
short[20] Post Tex Matrix Indexes
0x084
short[8] Texture Indexes
0x094
short[4] Tev Konst Color Indexes
0x09C
byte[16] Tev Konst Color Sels
0x0AC
byte[16] Tev Konst Alpha Sels
0x0BC
short[16] Tev Order Info Indexes
0x0DC
short[4] Tev Color Indexes
0x0E4
short[16] Tev Stage Info Indexes
0x104
short[16] Tev Swap Modes Info Indexes
0x124
short[16] Tev Swap Mode Tables Indexes
0x144
short Fog Info Index
0x146
short Alpha Compare Index
0x148
short Blend Mode Index
0x14A
short NBT Scale Index

FIFO Data (MDL3)

MDL3 is only found in the BDL format, and contains FIFO instructions. While the exact purpose of this chunk isn't known, it is assumed that it's used to speed up rendering by offering pre-compiled material data to feed to the GameCube's GPU.

It begins with a header, which is followed by a listing of the offset and size of each packet of FIFO instructions. After this is the instruction data itself, and then a set of offsets that identify blocks of instructions within the packets. It ends with a string table that gives a name to each packet of instructions. This name typically matches the name of a material found in MAT3.

Header

// 0x24/36 bytes long
/*0x00*/ char[4] Magic; // 'MDL3'
/*0x04*/ int Size;
/*0x08*/ short EntryCount;
/*0x0A*/ short Padding; // 0xFFFF/-1
/*0x0C*/ int PacketOffset; // Packet Location Data
/*0x10*/ int SubPacketLocationOffset; // Sub-packet Location Offset
/*0x14*/ int MatrixIndexOffset; // Matrix Index Offset
/*0x18*/ int PixelEngineModesOffset;
/*0x1C*/ int IndexesOffset; 
/*0x20*/ int StringTableOffset; //See JNT1

The header is padded to the nearest 32 bytes with the standard padding string.

Packet Listing

This gives the size and offset of a packet of FIFO instructions. The offset is relative to the start of the packet's entry in the listing, rather than the start of the listing itself or the start of the MDL3 chunk.

// 4 bytes long
/*0x00*/ int Offset;
/*0x04*/ int Size;

The listing is padded to the nearest 32 bytes with the standard padding string.

Packet Data

This is a set of FIFO instructions that affect specific areas of the GameCube's FIFO system. Each packet is split into sub-packets based on the area that the instructions at that offset in the packet work on. The offsets to these sub-packets are stored in the Sub-Packet Location Data, defined below.

There are two kinds of FIFO instructions found in a packet: BP and XF. They can be differentiated by their first byte, which is an opcode as interpreted by the GameCube. This opcode is 0x61 for BP commands, and 0x10 for XF commands.

BP Command:

// 5 bytes long
/*0x00*/ byte OpcodeID; // 0x61 for BP commands
/*0x01*/ byte RegisterAddress; // The address of the register to modify
/*0x02*/ byte RegisterValue1;
/*0x03*/ byte RegisterValue2; // RegisterValue1/2/3 are really part of 1, 24-bit value
/*0x04*/ byte RegisterValue3;

The 3 RegisterValue variables are meant to be read together as a single 24-bit value, and this value is what is placed into the register specified by RegisterAddress. What effect this value has on the GPU is determined by the address of the register that is modified.

XF Command:

// Header is 5 bytes long; data is variable length
/*0x00*/ byte OpcodeID; // 0x10 for XF commands
/*0x01*/ short DataLengthMultiplierMinus1;
/*0x03*/ short RegisterAddress;
/*0x05*/ byte[(DataLengthMultiplierMinus1 + 1) * 4] Data;

For whatever reason, the length of the data that the command uses isn't stored; instead, it stores a multiplier that must be multiplied by 4 to get the actual data length. To make this even more odd, DataLengthMultiplierMinus1 must have 1 added to it before it can be used, as shown in the array definition for the Data variable.

It is currently unknown how the data stored in the command is used, but the effect that it has on the GameCube's GPU is based on the RegisterAddress that the command gives.

Sub-Packet Location Data

This needs to be investigated.

This gives the offsets within a packet of blocks of instructions that affect specific parts of the material that this packet belongs to. Exactly what these blocks do is not yet known, but we do currently have a general understanding of what they affect, as shown in the layout below.

// 0x10/16 bytes long
/*0x00*/ short ChannelColorOffset;
/*0x02*/ short ChannelControlOffset;
/*0x04*/ short TextureGenOffset;
/*0x06*/ short TextureOffset;
/*0x08*/ short TevOffset;
/*0x0A*/ short PixelOffset;
/*0x0C*/ int Padding;

Unknown Section 2 ("Matrix Index Offset")

This needs to be investigated.

It appears to consist of at least a float and an int. However, the float is typically very small, casting doubt on the idea that it is a valid float.

// 8 bytes long
/*0x00*/ float MaybeFloat;
/*0x04*/ int MaybeInt;

The "default" value appears to be:

3C F3 CF 00 00 F3 CF 3C

This value is found in the majority of files, sometimes alongside other values.

Pixel Engine Modes

This is an array of bytes, padded to the nearest 8 with the standard padding string.

The byte is the same as the Pixel Engine Mode of the the material that the packet is associated with.

Texture Data (TEX1)

This chunk contains the images used for textures in materials. It is made up of a header and embedded BTI images.

Header

// 0x14/20 bytes long
/*0x00*/ char[4] Magic; // 'TEX1'
/*0x04*/ int Size;
/*0x08*/ short TextureCount; // Number of textures
/*0x0A*/ short Padding; // Usually 0xFFFF/-1
/*0x0C*/ int TextureHeaderOffset; // TextureCount bti image headers are stored here. Relative to TEX1Header start.
/*0x10*/ int StringTableOffset; // Stores one filename for each texture. Relative to TEX1Header start.

Embedded BTI Images

These are the actual textures. Information about embedded images can be found in the article about the BTI format.