GBX

From TM Wiki
Jump to: navigation, search

Contents

[edit] General information

TrackMania gamebox files (*.gbx) are generic container files that can contain everything from configuration to textures to track definitions. They consist of a header section and a data section.

In old versions of TrackMania they used to be text files - nowadays they are binary files. Integers are stored in little endian order. The data section is often compressed (using LZO).

[edit] Engines, classes, chunks

A .gbx file more specifically stores the serialization of one or more class instances. There is one main instance, and optionally a number of auxiliary instances.

The serializable classes are organized into 16 engines. Each class is also subdivided into chunks. A class is then not serialized in one go, but rather as a series of chunks. This allows Nadeo to easily extend classes in new TrackMania versions: instead of having to define a new class they can simply add more chunks to an existing one, and have older versions ignore these new chunk types.

The data in a gbx file follows the pattern <chunk ID> <chunk data>. A chunk ID is a 32-bit number that identifies the engine, the class, and the chunk in that class. If you for example see the bytes 07 30 04 03 in the file, that would correspond to the integer 0x03043007, and be interpreted as follows:

engine class chunk
03     043   007

All engines and classes are named; in this case, engine 3 is the Game engine, and class 043 in that engine is CGameCtnChallenge. Chunks do not have names.

Apart from chunk ID's there are also class ID's, which are just like chunk ID's except the chunk index part is ignored (and 0). For a complete overview of engines and classes, see Class ID's.

[edit] Header

The header contains things like compression information, the class ID of the main class instance, and a few chunks of the main class that serve as meta information (e.g. the thumbnail of a challenge).

[edit] Data

[edit] Reading the data

The data section contains further chunks of the main class instance, and may also contain auxiliary class instances. Reading the data section is started by creating an in-memory instance of the class corresponding to the main class ID (instances are called nodes internally), and calling ReadNode on it:

ReadNode()
{
    while(true)
    {
        chunkID = ReadUInt32();
        if(chunkID == 0xFACADE01)
            return;

        chunkFlags = GetChunkFlags(chunkID);
        if(chunkFlags & 0x10)
        {
            skip = ReadUInt32();
            chunkDataSize = ReadUInt32();
        }

        ReadChunk(chunkID);
    }
}

GetChunkFlags() doesn't read anything from the file; it provides loading flags for the specified chunk ID. The only important flag is whether or not the chunk is "skippable". If it is, the chunk ID is followed by an int32 0x50494B53 ("SKIP", shows up as "PIKS" in the file due to little endian ordering) and an int32 specifying the size of the chunk data. This allows older versions of TrackMania that don't know how to parse this chunk ID to skip over the chunk data and go to the next chunk. If the chunk is not skippable, the chunk data follows immediately after the chunk ID.

A dummy chunk ID of 0xFACADE01 signifies the end of the chunk list for the current class.

Chunk data is not self-describing; the program itself has to know how to read each one. In fact, if your program doesn't know a specific chunk ID and the chunk is not skippable, you can't even tell how long the chunk is.

We will first describe a number of "primitives" that are used when reading and writing chunks. Then we will describe the contents of a number of common chunks.

[edit] Primitives

Note: the lookback string state is reset after each header chunk. The string list is cleared completely, and the next lookback string will again trigger the version number.

[edit] Class descriptions

[edit] CGameCtnChallenge (03 043 000)

03043002

byte version
int32 0
int32 bronzeTime (ms)
int32 silverTime (ms)
int32 goldTime (ms)
int32 authorTime (ms)
if version >= 4:
   int32 price (coppers)
   if version >= 6:
      int32 multilap (0/1)
      int32 trackType (0: Race, 1: Platform, 2: Puzzle, 3: Crazy, 4: Shortcut, 5: Stunts, 6: Script)
      if version >= 9:
         int32 0
         if version >= 10:
            int32 authorScore
            if version >= 11:
               int32 createdWithEditorSimple
                  if version >= 12:
                     int32 0
                     if version >= 13:
                        int32 nbCheckpoints
                        int32 nbLaps

03043003

byte version
meta (trackUID, environment, author)
string trackName
byte kind (0: (internal)EndMarker, 1: (old)Campaign, 2: (old)Puzzle, 3: (old)Retro, 4: (old)TimeAttack,
           5: (old)Rounds, 6: InProgress, 7: Campaign, 8: Multi, 9: Solo, 10: Site, 11: SoloNadeo, 12: MultiNadeo)
if version >= 1:
   int32
   string password (weak xor encryption, no longer used in newer track files; see 03043029)
   if version >= 2:
      meta background (timeOfDay, environment, author)
      if version >= 3:
         vec2D mapOrigin
         if version >= 4:
            vec2D mapTarget
            if version >= 5:
               int128
               if version >= 9:
                  int32 0
                  string mapStyle
                  int64
                  byte lightmap

03043004

int32 version

03043005

string xml

The XML block contains everything the 003 header chunk does, except for the password. Unlike the 003 chunk, however, it also contains the list of dependencies for the track (images, mods, music, etc.), as well as the version number of the software that created the track, the actual number of laps, and an optional Mod name.

03043007

int32 haveThumbnail
if haveThumbnail != 0:
    int32 thumbSize
    "<Thumbnail.jpg>"
    byte thumb[thumbSize]
    "</Thumbnail.jpg>"
    "<Comments>"
    string comments
    "</Comments>"

03043008

int32 version
int32 authorVersion
string authorLogin
string authorNick
string authorZone
string authorExtraInfo

0304300D

meta empty

03043011

noderef collectorList
noderef challengeParameters
int32 kind (0: (internal)EndMarker, 1: (old)Campaign, 2: (old)Puzzle, 3: (old)Retro, 4: (old)TimeAttack,
            5: (old)Rounds, 6: InProgress, 7: Campaign, 8: Multi, 9: Solo, 10: Site, 11: SoloNadeo, 12: MultiNadeo)

03043014 (skippable)

int32
string password (old style password with weak xor encryption. this chunk is no longer used in newer challenge files, see 03043029)

03043017 (skippable)

int32 numCheckpoints
Checkpoint[numCheckpoints]

Checkpoint:

int32
int32
int32

03043018 (skippable)

int32
int32 numLaps

03043019 (skippable)

fileref modPackDesc

0304301C (skippable)

int32 playMode (0: Race, 1: Platform, 2: Puzzle, 3: Crazy, 4: Shortcut, 5: Stunts)

0304301F

meta (trackUID, environment, author)
string trackName
meta (timeOfDay, environment, author)
int32 sizeX
int32 sizeY
int32 sizeZ
int32 needUnlock
int32 flagsAre32Bit

int32 numBlocks
for each block:
    lookbackstring blockName
    byte rotation (0/1/2/3)
    byte x
    byte y
    byte z
    int16/int32 flags
    if (flags & 0x8000) != 0: custom block
        lookbackstring author
        noderef skin

03043021

noderef clipIntro
noderef clipGroupInGame
noderef clipGroupEndRace

03043022

int32

03043024

fileref customMusicPackDesc

03043025

vec2D mapCoordOrigin
vec2D mapCoordTarget

03043026

noderef clipGlobal

03043027

int32 provided
if provided != 0:
    byte
    vec3D x 3
    vec3D
    float
    float
    float

03043028

ReadChunk(0x03043027)
string comments

03043029 (skippable)

int128 passwordHash (salted MD5)
int32 CRC32("0x" + hex(passwordHash) + "???" + trackUID)

0304302A

int32

[edit] CGameCtnCollectorList (03 01B 000)

0301B000

int32 num
Item items[num]
    meta
    int32

[edit] CGameCtnChallengeParameters (03 05B 000)

0305B000 (all fields are ignored)

int32
int32
int32
int32

int32
int32
int32

int32

0305B001

string tip
string tip
string tip
string tip

0305B002 (all fields are ignored)

int32
int32
int32
float
float
float
int32
int32
int32
int32
int32
int32
int32
int32
int32
int32

0305B003 (all fields are ignored)

int32
float

int32
int32
int32

int32

0305B004

int32 bronzeTime (ms)
int32 silverTime (ms)
int32 goldTime (ms)
int32 authorTime (ms)
int32 ignored

0305B005

int32 x 3 (ignored)

0305B006

int32 num
int32 items[count] (ignored)

0305B008

int32 timeLimit (ms)
int32 authorScore (ms)

0305B00A (skippable)

int32 (0?)
int32 bronzeTime (ms)
int32 silverTime (ms)
int32 goldTime (ms)
int32 authorTime (ms)
int32 timeLimit (ms)
int32 authorScore (ms)

0305B00D

int32 (-1?)

0305B00E (skippable)

int32
int32
int32

[edit] CGameCtnBlockSkin (03 059 000)

03059000

string text
string ignored

03059001

string text
fileref packDesc

03059002

string text
fileref packDesc
fileref parentPackDesc

[edit] CGameCtnReplayRecord (03 093 000)

03093000

int32 version
meta (trackUID, environment, author)
int32 time (ms)
string nickName
if version > 5:
    string driverLogin

03093001

string xml

The XML block contains the UID and replay ("best") time like in the header. It also contains the version number of the software that created the replay; optionally the respawns count (can be -1 or larger), the Stunts score, and a validable flag; and in recent versions occasionally two checkpoints fields.

03093002 (header)

int32 version
int32 authorVersion
string authorLogin
string authorNick
string authorZone
string authorExtraInfo

03093002 (data)

int32 size
byte GBX[size], the track the replay was recorded on

03093007 (skippable)

int32

03093014

int32 ignored (0xA)
int32 numGhosts
noderef ghosts[numGhosts]
int32 ignored
int32 num
int64[numExtras]

03093015

noderef

[edit] CGameGhost (03 03F 005)

0303F005

int32 uncompressedSize
int32 compressedSize
byte compressedData[compressedSize]: (compressed with zlib deflate)
    int32 classID
    int32 bSkipList2
    int32
    int32 samplePeriod
    int32

    int32 size
    byte sampleData[size] (samples of position, rotation, speed... of the car during the race)

    int32 numSamples
    if numSamples > 0:
        int32 firstSampleOffset
        if numSamples > 1:
            int32 sizePerSample
            if sizePerSample == -1:
                int32 sampleSizes[numSamples - 1]

    if bSkipList2 == 0:
         int32 num
         int32 data[num]

A sample record looks as follows:

vec3D position
uint16 angle      (0..0xFFFF -> 0..pi)
int16 axisHeading (-0x8000..0x7FFF -> -pi..pi)
int16 axisPitch   (-0x8000..0x7FFF -> -pi/2..pi/2)
int16 speed       (-> exp(speed/1000); 0x8000 means 0)
int8 velocityHeading (-0x80..0x7F -> -pi..pi)
int8 velocityPitch   (-0x80..0x7F -> -pi/2..pi/2)
... (more unknown data)

The rotation of the car is calculated as a quaternion.

You can convert this quaternion to a transform matrix.

The velocity vector (direction and speed of movement) is calculated in a similar way: (speed*cos(velocityPitch)*cos(velocityHeading), speed*cos(velocityPitch)*sin(velocityHeading), speed*sin(velocityPitch)).

[edit] CGameCtnGhost (03 092 000)

CGameCtnGhost is a subclass of CGameGhost. If you encounter an unknown chunk ID while reading a CGameCtnGhost instance, delegate it to CGameGhost.

03092005 (skippable)

int32 raceTime

03092008 (skippable)

int32 numRespawns

03092009 (skippable)

color lightTrailColor

0309200A (skippable)

int32 stuntsScore

0309200B (skippable)

int32 num
int64[num]

0309200C

int32 ignored

0309200E

lookbackstring uid

0309200F

string ghostLogin

03092010

lookbackstring 

03092012

int32 ignored
int128

03092013 (skippable)

int32
int32

03092014 (skippable)

int32 

03092015

lookbackstring playerMobilId

03092017 (skippable)

int32 num
fileref skinPackDescs[num]
string ghostNickname
string ghostAvatarName

03092018

meta

03092019

int32 eventsDuration
int32 ignored
int32 numControlNames
lookbackstring controlNames[numControlNames]

int32 numControlEntries
int32
ControlEntry[numControlEntries]
    int32 time (ms + 100000)
    byte controlNameIndex
    int32 onoff (1/0)

string gameVersion
int32 exeChecksum
int32 osKind
int32 cpuKind
string raceSettingsXML
int32

[edit] Historical information

While the above description of *.gbx files is far more complete and precise than before, it is also entirely different from – and more abstract than – the original description, which formed the basis of the tools below. That historical description can still be viewed at this revision link.

[edit] Applications and Libraries that can inspect the file format

To do ... translate...

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox
In other languages