t2b
Filetype: Data
File Extension(s): .cfg.bin, .astarbin, .chartbin, .npcbin, .pathbin, .actbin, .bin
Signature: \x01t2b
Platform(s): Nintendo 3DS
Endianness: Little-endian
Used in: Yo-kai Watch 1, Yo-kai Watch 2, Yo-kai Watch Blasters, Yo-kai Watch 3, Yo-kai Watch Busters 2
Alignment: 4 Byte Alignment
String Encoding(s): cp932, UTF-8
The t2b format is a structured binary configuration format used by 3DS Level-5 games for storing named entries containing typed values, configuring naerly all data within the games e.g. Items, Yo-kai, Quests, Maps etc. It most likely stands for text 2 binary as it’s the compiled version of a plaintext format which will be covered in a different page. After the 3DS era of Level-5’s engine, t2b files began to be superseeded by RDBN files of which we only know the compiled format of.
Format Layout
T2B File
├─ Entry Section
│ ├─ Entry Header
│ ├─ Entry Records
│ └─ Entry String Table
│
├─ CRC Section
│ ├─ CRC Header
│ ├─ CRC Records
│ └─ CRC String Table
│
├─ Footer (0x10 bytes from file end)
│
└─ Hi - idk I'm bored ok
Note that all offsets are relative to the start of their section. Meaning that entry string offsets are relative to the entry string table base and CRC string offsets are relative to the CRC string table base.
Footer
The footer is always located at the absolute offset fileSize - 0x10.
Structure
| Field | Type | Description |
|---|---|---|
| magic | uint32 | Must be 0x62327401. |
| unk1 | Int16 | - |
| encoding | Int16 | String encoding. See the section below for details. |
| unk2 | Int16 | - |
Encoding Values
| Value | Encoding |
|---|---|
0x0000 | CP932 (Shift-JIS) |
0x0001 | UTF-8 |
0x0100 | UTF-8 |
0x0101 | UTF-8 |
Note that all other encoding values are Invalid and should be treated as such.
Entry Section
This section starts at absolute offset 0x00.
Entry Header
All the fields below are unsigned 32-bit integers:
| Field | Description |
|---|---|
| entryCount | Number of entries. |
| stringDataOffset | Offset to the entry string table. |
| stringDataLength | Size of entry string table. |
| stringDataCount | Number of strings. |
Entry Record
| Field | Type | Size | Description |
|---|---|---|---|
| crc32 | uint32 | 4 bytes | CRC32 hash of the entry name. |
| entryCount | uint8 | 1 byte | Number of values in this entry. |
| entryTypes | packed ValueType array | variable | Types are packed sequentially. The size (in bytes) can be calculated via the following formula: ceil(entryCount / 4). |
| padding | N/A | variable | Stream is forward-aligned to a 4-byte boundary after reading the type array. |
| entryValues | Value[] | variable | Value array stored sequentially. The size (in bytes) can be calculated via the following formula: entryCount * ValueLength. |
Entry Types
In one byte there are 4 types packed within it as it utilises 2 bits per entry. This means that it can be extracted via the following formula (typeChunk >> (h * 2)) & 0x3.
ValueType
| Value | Meaning |
|---|---|
| 0 | String |
| 1 | Integer |
| 2 | Float |
| 3 | Invalid / unused |
After the type array the stream is aligned (to 4 bytes) with forward null padding. Note that here:
- String is an offset into the Entry String Table (negative = null string).
- Strings in the Entry String Table are encoded with CP932/UTF-8 depending on the encoding byte in the footer.
- Integer is a signed 32-bit/64-bit integer.
- Float is an IEEE-754 single/double precision float.
Entry Values
Whether values are 32-bit or 64-bit depends on the file. It is unknown where - or if - the format explicitly states value width. Parsers must detect whether values are 32-bit or 64-bit by attempting parsing and verifying section bounds. This has no effect on strings but affects the Integer and Float types as mentioned above.
Entry String Table
This table is located at stringDataOffset (which is defiend by the entry header). It is a continuous non-compressed blob of null-terminated strings. Encoding is defined in the footer of the t2b. Entries reference strings using offsets into this table.
CRC Section
This section is located after the entry section and its string table.
CRC Header
| Field | Type | Description |
|---|---|---|
| size | Uint32 | Size of the CRC section in bytes. |
| count | Uint32 | Number of CRC entries. |
| stringOffset | Uint32 | Offset to the CRC string table. |
| stringSize | Uint32 | Size of the string table (in bytes). |
Checksum Entry
The two fields defined below are both unsigned 32-bit integers:
| Field | Description |
|---|---|
| crc32 | CRC-32B (ISO-HLDC) hash of the entry name. |
| stringOffset | Offset to the name string. |
These entries map the CRC-32 hash to the Entry Name.
CRC String Table
This table is a continuous non-compressed blob of null-terminated strings representing the entry names. Encoding is defined in the footer of the t2b. Offsets are relative to the first string offset meaning they can be calculated as such: actualOffset = entry.stringOffset - firstEntry.stringOffset. Note that CRC string offsets are relative to the CRC string table start, NOT the file start.
Name Hashing
Entry names are hashed using CRC-32B (ISO-HLDC). But different t2bs have two different variants of it. They can either use the standard CRC-32 or CRC-32 JAM (Bitwise not of the standard CRC-32). It isn’t known if it is explicitly referenced in the file’s header or someplace else therefore it is currently advised to detect which variant is being used by hashing the first string and comparing it with the stored CRC-32.
Finally, note that after parsing, the file should resolve to a structure similar to what’s shown below:
T2B
├─ Entries[]
│ ├─ Name
│ └─ Values[]
│ ├─ Type
│ └─ Value
├─ Encoding
├─ ValueLength
└─ HashType
Tree Structure
Although entries are stored as a flat list in the binary format, hierarchical structure is encoded using naming conventions. This child/parent (tree) structure is shown in the original plaintext source format and can be reconstructed by parsing entry names. A subtree is typically defined by a begin marker entry followed by child entries and terminated by a corresponding end marker.
Hierarchy Markers
Opening Markers
Entries that signal the start of a subtree typically end with one of the following begin markers:
_BEGIN
_BEG
_BGN
_COUNT
These entries mark the start of a tree node, and all following entries up to the matching end marker are considered children of this node. Additionally, entries that terminate with a standalone underscore (_) may also function as opening markers in some cases.
Note: Some opening marker entries may contain a
ChildCountparameter. When present, this value is usually (but not always!) located as the last parameter in the entry’s value list. Due to several inconsistencies it is not recommended to rely on this functionality and to instead leave it up to the user.
Child Markers
Entries inside a subtree often carry info markers to indicate their type or role:
_INFO
_DATA
_VAL
_ITEM
These are usually the actual payload or metadata for the tree node.
Closing Marker
A subtree is terminated by a _END entry, which usually contains no parameters. The _END entry closes the most recent unclosed begin marker of the same prefix.
Special PTREE Case
Level-5 frequently uses a separate convention for property tree roots: with PTREE, PTVAL, PTVALS and _PTREE - where PTREE is the _BEG equivalent, PTVAL and PTVALS the _INFO equivalent and _PTREE the _END equivalent. This behaves like the normal _BEGIN / _END pattern but uses the custom naming scheme mentioned above.
To reconstruct the tree:
- Iterate entries linearly.
- If the name ends with a begin marker, start a new node.
- Consume all following entries until the matching end marker.
- Treat consumed entries as children of the current node.
Note: Recursively apply the above steps as needed for nested subtrees.