The TRB files are containers with various sections used to define the model (textures, shaders, skeleton, geometry etc). A binary template for this file format can be found here.
Sections
Section Header
The following header is for all the different sections.
Offset
Size
Type
Description
0x0
0x4
UInt32
FourCC. Always 0x53454442 (SEDB, "Section Data Binary")
0x4
0x4
UInt32
Resource FourCC, gives the section type
0x8
0x4
UInt32
Version
0xC
0x2
UInt16 (BE)
Resource type 2, seems to define a subtype
0xE
0x2
UInt16
Header size, gives the section data offset
0x10
0x4
UInt32
Section size, header included
0x14
0x1C
UInt32[7]
Reserved, always null
Section Types
SEDBRES
This section is used to define and encapsulate all the other ones. It contains information such as the sections count, offsets and other info. To avoid confusion, sections contained in this one will be called "subsections".
This section is used to define the necessary semantics in case of data spread into several TRBs. The attribute definition is similar to the ones in the STMS chunk. See m253/583 on ps3/win32 for examples.
Name table, with the joint names, type names, subsection names etc
SKL Subsection Header
Offset
Size
Type
Description
0x0
0x4
UInt32
Subsection name index (index in the name table, gives the purpose of the subsection)
0x4
0x4
UInt32
Subsection start offset (relative to the end of the subsection headers)
0x8
0x4
UInt32
Subsection end offset (relative to the end of the subsection headers)
0xC
0x4
UInt32
Unknown (always 1?)
SKL Subsection
The subsections seen so far are the following:
Hierarchy data subsection
This subsection has the necessary information to construct the skeleton (parenting, position, rotation etc). The number of joint entries is equal to the subsection size divided by 0xB0 (size of the joint structure). The joint structure is defined the following way:
Offset
Size
Type
Description
0x0
0x4
UInt32
Name index (index in the name table)
0x4
0x4
UInt32
Type index (index in the name table)
0x8
0x4
UInt32
Unknown index (index in the name table, probably reserved)
0xC
0x4
Int32
Always -1, probably padding
0x10
0xC
Float32[3]
Joint location, in local space (i.e. relative to the parent joint)
0x1C
0x10
Float32[4]
Joint rotation, in local space (i.e. relative to the parent joint). As a quaternion.
0x2C
0xC
Float32[3]
Joint scale, in local space (i.e. relative to the parent joint). Always (1,1,1) it seems.
0x38
0x4
Int32
Parent index (index in the joint hierarchy)
0x3C
0x4
Int32
Child 1 index (index in the joint hierarchy)
0x40
0x4
Int32
Child 2 index (index in the joint hierarchy)
0x44
0x4
UInt32
Joint hash or joint index (For FF XIII)
0x48
0x4
UInt32
Bitfield (assumption) of unknown purpose. Seen values: 0x2 (very common), 0x12 (XIII), 0x22 (LR garbs)
0x4C
0x4
UInt32
Group ID, referenced by animation files.
0x50
0x60
UInt32[24]
Reserved, always null.
N.B. The child indices do not seem to be used within the game other than determining the load order of the bones. Bones that are not
listed in a “child” index are not created but the specific order of the child index does not seem to matter. i.e. if each bone utilizes a single child index pointing to the next bone (0 -> 1, 1 -> 2, etc.) for the whole list, then it all still loads and works fine. The only restriction is that a bone listed as a “parent” MUST be created before the parent index is used. A parent or child index of -1 is not used.
For the group ID, the purpose seems to vary between the games: In XIII the joint groups seemed to be fairly well defined as
0 = Main
1 = Accessory/Clothing/Hair
2 = Body
3 = Head
Whereas in XIII-2/LR it seems to split the skeleton into "limbs".
KineDriver subsection
Research needed.
SEDBelb
This section is used to define the "sockets", which are used as location to attach weapons, accessories etc.
Next are some structs (socket count of them) used to define the names, bone parenting and location info of the sockets.
Offset
Size
Type
Description
0x0
0x4
UInt32
Parent bone name offset (relative to the beginning of the SEDBelb section)
0x4
0x4
UInt32
Socket name offset (relative to the beginning of the SEDBelb section)
0x8
0xC
Float32[3]
Socket location, in local space (i.e. relative to the parent bone)
0x14
0xC
Float32[3]
Reserved, always null. May be padding.
Next some euler angles (socket count of them), used to define the socket rotation.
Offset
Size
Type
Description
0x0
0x4
Float32
X axis rotation (in radians)
0x4
0x4
Float32
Y axis rotation (in radians)
0x8
0x4
Float32
Z axis rotation (in radians)
Finally, a section where all the null-terminated strings referenced by the previous offsets are packed. Socket names and their bone parent names can be found here.
SEDBPHB
Chunks
Chunks are structures used to subdivide the above sections in several blocks of information. There are two chunk types : chunks and chunk containers. The latter are used to "pack" several chunks, which will be referred to as their "children".
Chunks
Chunk Header
The following header is for all the different chunks.
Offset
Size
Type
Description
0x0
0x4
UInt32
FourCC, used to define the chunk
0x4
0x4
UInt32
Version
0x8
0x4
UInt32
Chunk Size (arbitrary, depending on the chunk it may be the size, size - 0xC, size - 0x4 etc)
0xC
0x4
UInt32
Next chunk offset (a 0 value means there's none)
FILE
This chunk contains the actual DX9 shader code and name. As of now the best way to disassemble/decompile the bytecode is to use the following PR build.
Fixed size string, duplicate of the one in the NAME chunk above with a 16 char limit.
0x20
0x24
UInt32
Unknown
0x24
0x28
UInt32
MESH count
0x28
0x2C
UInt32
Unknown (bitfield, byte field ? Different values, possibly be four byte fields with the 3rd and 4th bytes always being 0. 1st and 2nd can have different values. (more so in the environment trb))
N.B.: For the bone indices, these are not the absolute indices as defined in the SEDBSKL section. Instead, a bone map is made using the ENVD chunks and the indices refer to the bones defined in the latter.
Data type
Value
Data type
0x0
UInt16
0x1
Float32
0x2
Float16
0x3
UByte
0x4
Int16
0x6
Byte
Semantic
Value
Semantic
0x0
Position
0x2
Normal
0x3
Color
0x4
Binormal
0x8
UV1
0x9
UV2
0xA
UV3
0xB
UV4
0xD
Tangent
0xE
Bone weight
0xF
Bone index
0xFF
Index
N.B: Color : while this can be used to color shade the model somewhat, it's used more for shadowing areas of the model independently. For example, Hope's wrists as they come out of his gloves are darkened by using this. 4 byte values, in RGBx. 255 being full brightness/color and lower values darkening the specific color. The game always keeps the last value at 255., which is most likely the alpha channel.
This chunk defines which vertices are affected by a given bone (referenced by its name) and by how much (weight). It is redundant since this info is already given by the STMS buffer, however the game uses the ENVD order as a bone map.
For example if the first ENVD of a MESH chunk container references the bone "larm", then a 0 bone index in the STMS buffer will refer to "larm", whereas the 0 index in the skeleton would normally refer to the bone "trans". Therefore, when combined together, the ENVD chunks form a bone map, defining a skeleton subset that deforms the submesh.
This chunk contains some bounding box info. AABB chunks are used for identifying viewable meshes in-game while
COMP chunks use some bounding box like information used to determine the scale/bias, which is then used to
dequantize the submeshes' position data.