Difference between revisions of "GBX"
BigBang1112 (talk | contribs) |
BigBang1112 (talk | contribs) (→Reading the body: Added Encapsulation) |
||
(32 intermediate revisions by 4 users not shown) | |||
Line 172: | Line 172: | ||
''Note'': In case of a text format .gbx file (marked by a 'T' in the header) all numbers and strings are stored as one line of ASCII text in each case. Every line ends with a carriage return-linefeed ({{c|<CR><LF>}}) combination (0x0D and 0x0A). A single {{c|<LF>}} is part of a string. | ''Note'': In case of a text format .gbx file (marked by a 'T' in the header) all numbers and strings are stored as one line of ASCII text in each case. Every line ends with a carriage return-linefeed ({{c|<CR><LF>}}) combination (0x0D and 0x0A). A single {{c|<LF>}} is part of a string. | ||
+ | |||
+ | ===Encapsulation=== | ||
+ | Encapsulation is a special kind of chunk behavior added in ManiaPlanet that handles lookbackstrings and noderefs differently. | ||
+ | |||
+ | * A new temporary lookbackstring list is created just for this chunk - the counting of strings starts from 1 again. However, the previous state of lookback strings is not reset. | ||
+ | * An index of the noderef is not included and the node is not added to the node list. That list is not reset either. | ||
+ | |||
+ | It looks like this: | ||
+ | |||
+ | CSystemArchiveEncapsuledChunk::CSystemArchiveEncapsuledChunk(); | ||
+ | // ... | ||
+ | CSystemArchiveEncapsuledChunk::~CSystemArchiveEncapsuledChunk(); | ||
+ | |||
+ | In this range of code, you need to set up your binary reader to handle the two rules above. | ||
+ | |||
+ | Here is the list of chunks that use encapsulation: | ||
+ | |||
+ | * CGameCtnChallenge '''03043040''', '''03043041''', '''03043043''', '''03043044''', '''0304304E''', '''0304304F''' (version < 2), '''03043054''', '''03043058''' (version >= 1) | ||
+ | * CGameCtnMacroBlockInfo '''0310D00B''', '''0310D011''' | ||
+ | * CGamePlayerProfileChunk_GameScores '''03146000''', '''03146002''', '''03146004''' | ||
+ | * CGamePlayerProfileChunk_ScriptPersistentTraits '''03170000''' | ||
== Class descriptions == | == Class descriptions == | ||
Line 381: | Line 402: | ||
uint32 version | uint32 version | ||
if version >= 5: | if version >= 5: | ||
− | + | uint32 frames // If version < 5 then frames = 1 | |
if version >= 2: | if version >= 2: | ||
− | + | for each frame: | |
− | + | uint32 size | |
− | + | byte image[size] // Image is JPEG/JFIF or WEBP/RIFF file format | |
− | + | if version >= 3: | |
− | + | uint32 size | |
− | + | byte image[size] | |
− | + | if version >= 6: | |
− | + | uint32 size | |
− | + | byte image[size] | |
− | + | if size != 0: | |
− | + | uint32 uncompressedSize | |
− | + | uint32 compressedSize | |
− | + | byte compressedData[compressedSize] // ZLIB compressed lightmap cache node | |
'''03043040''' (skippable) ''"items"'' | '''03043040''' (skippable) ''"items"'' | ||
− | ''Note:'' This chunk | + | ''Note:'' This chunk has its own lookback string state (is encapsulated with CSystemArchiveEncapsuledChunk). |
+ | |||
+ | ''Note:'' Version 5+ can have data that is based on the count of certain types of items in the item array. If you want to modify items in TM2020, you can come across issues when modifying the item array and it becomes rather difficult. This can be resolved by downgrading the version to 4. This downgrade should only affect item snapping - which is "useful" only when deleting items in the map editor. | ||
+ | |||
+ | ''Note:'' The following information may not be 100% accurate. | ||
+ | |||
+ | * '''connectedItemPairs''': Array of AnchoredObjects index pairs where the first item (of pair) is the item that will delete the second item (of pair). Example: (87, 110) - item #87 will delete item #110, when #87 gets deleted. | ||
+ | * '''snappedBlocks''': ''Values'' are ''indexes'' of the Blocks array (chunk 0x01F). -1 = snapped to an item and you should use the index of snappedItems. | ||
+ | * '''snappedItems''': ''Values'' are ''indexes'' of the AnchoredObjects array (this chunk). -1 = snapped to a block and you should use the index of snappedBlocks. | ||
+ | * '''snapItemGroups''': Separates item "deletion groups" via a simple number. Usual values are between 0 and 5. You can index these with itemSnapIndexes values. | ||
+ | * '''itemSnapIndexes''': Uses the values above to connect them with the item list. ''Values'' are ''indexes'' of '''snappedBlocks'''/'''snappedItems''' arrays. Any index of this array corresponds to the same AnchoredObjects index, '''except -1''', which means the item is not snapped. | ||
uint32 version | uint32 version | ||
− | + | uint32 | |
− | + | uint32 size | |
− | + | uint32 (10) | |
− | + | uint32 numItems | |
− | + | for each item: | |
− | + | node item (CGameCtnAnchoredObject, direct node) | |
− | + | if version >= 1 && version != 5: | |
− | + | uint32 connectedItemPairCount | |
+ | uint2 connectedItemPairs[connectedItemPairCount] // 2 uint32 per element | ||
+ | if version >= 5: // TM2020 | ||
+ | uint32 snappedBlockCount | ||
+ | uint32 snappedBlocks[snappedBlockCount] | ||
+ | if version < 7: | ||
+ | uint32 snapItemGroupCount | ||
+ | uint32 snapItemGroups[snapItemGroupCount] | ||
+ | if version >= 6: | ||
+ | uint32 snappedItemCount | ||
+ | uint32 snappedItems[snappedItemCount] | ||
+ | if version >= 7: | ||
+ | uint32 snapItemGroupCount | ||
+ | uint32 snapItemGroups[snapItemGroupCount] | ||
+ | if version != 6: | ||
+ | uint32 size | ||
+ | uint32[size] // unknown atm | ||
+ | uint32 itemSnapIndexCount // should be the same as the item count itself | ||
+ | uint32 itemSnapIndexes[itemSnapIndexCount] | ||
'''03043044''' (skippable) | '''03043044''' (skippable) | ||
Line 417: | Line 466: | ||
uint32 version | uint32 version | ||
if version >= 2: | if version >= 2: | ||
− | + | uint32 count (number of metadata records) | |
− | + | for each count: | |
− | + | string varName | |
− | + | uint32 varType | |
− | + | switch varType: | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
case EType_Boolean: | case EType_Boolean: | ||
− | + | bool | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
case EType_Integer: | case EType_Integer: | ||
− | + | int32 | |
case EType_Real: | case EType_Real: | ||
− | + | float | |
case EType_Text: | case EType_Text: | ||
− | + | string | |
case EType_Int2: | case EType_Int2: | ||
− | + | int32 | |
− | + | int32 | |
case EType_Int3: | case EType_Int3: | ||
− | + | int32 | |
− | + | int32 | |
− | + | int32 | |
case EType_Vec2: | case EType_Vec2: | ||
− | + | float | |
− | + | float | |
case EType_Vec3: | case EType_Vec3: | ||
− | + | float | |
− | + | float | |
− | + | float | |
case EType_Array: | case EType_Array: | ||
− | + | uint32 typeKey | |
+ | uint32 typeValue | ||
+ | uint32 arrayElements | ||
+ | for each arrayElements | ||
+ | switch typeKey: | ||
+ | case EType_Boolean: | ||
+ | bool | ||
+ | case EType_Integer: | ||
+ | int32 | ||
+ | case EType_Real: | ||
+ | float | ||
+ | case EType_Text: | ||
+ | string | ||
+ | switch typeValue: | ||
+ | case EType_Boolean: | ||
+ | bool | ||
+ | case EType_Integer: | ||
+ | int32 | ||
+ | case EType_Real: | ||
+ | float | ||
+ | case EType_Text: | ||
+ | string | ||
+ | case EType_Int2: | ||
+ | int32 | ||
+ | int32 | ||
+ | case EType_Int3: | ||
+ | int32 | ||
+ | int32 | ||
+ | int32 | ||
+ | case EType_Vec2: | ||
+ | float | ||
+ | float | ||
+ | case EType_Vec3: | ||
+ | float | ||
+ | float | ||
+ | float | ||
+ | case EType_Array: | ||
+ | recursively read multidimensional arrays | ||
The variable type is to be interpreted as follows: | The variable type is to be interpreted as follows: | ||
enum eScriptType | enum eScriptType | ||
{ | { | ||
− | + | EType_Void = 0, | |
− | + | EType_Boolean, | |
− | + | EType_Integer, | |
− | + | EType_Real, | |
− | + | EType_Class, | |
− | + | EType_Text, | |
− | + | EType_Enum, | |
− | + | EType_Array, | |
− | + | EType_ParamArray, | |
− | + | EType_Vec2, | |
− | + | EType_Vec3, | |
− | + | EType_Int3, | |
− | + | EType_Iso4, | |
− | + | EType_Ident, | |
− | + | EType_Int2, | |
− | + | EType_Struct | |
}; | }; | ||
Maniaplanet 4.1 has changed the way script types and values are written. The code now does the following: | Maniaplanet 4.1 has changed the way script types and values are written. The code now does the following: | ||
Line 519: | Line 568: | ||
'''03043054''' (skippable) | '''03043054''' (skippable) | ||
+ | |||
+ | ''Note:'' This chunk has its own lookback string state (is encapsulated with CSystemArchiveEncapsuledChunk). | ||
+ | |||
uint32 version (1) | uint32 version (1) | ||
uint32 | uint32 | ||
uint32 chunkSize | uint32 chunkSize | ||
uint32 itemCount (number of embedded items) | uint32 itemCount (number of embedded items) | ||
+ | meta items[itemCount] | ||
uint32 zipSize (embedded items ZIP file size) | uint32 zipSize (embedded items ZIP file size) | ||
byte zipFile[zipSize] | byte zipFile[zipSize] | ||
+ | uint32 textureCount | ||
+ | string textures[textureCount] | ||
'''21080001''' ''"VskDesc"'' | '''21080001''' ''"VskDesc"'' | ||
Line 697: | Line 752: | ||
fileref packDesc | fileref packDesc | ||
fileref parentPackDesc | fileref parentPackDesc | ||
+ | |||
+ | '''03059003''' ''"TM2020"'' | ||
+ | uint32 version | ||
+ | fileref secondaryPackDesc | ||
===CGameWaypointSpecialProperty (03 13B 000) === | ===CGameWaypointSpecialProperty (03 13B 000) === | ||
Line 722: | Line 781: | ||
vec3 absolutePositionInMap | vec3 absolutePositionInMap | ||
CGameWaypointSpecialProperty waypointSpecialProperty (-1 if not a waypoint, otherwise direct node) | CGameWaypointSpecialProperty waypointSpecialProperty (-1 if not a waypoint, otherwise direct node) | ||
+ | if version < 5: | ||
+ | uint32 | ||
if version >= 4: | if version >= 4: | ||
− | + | uint16 flags | |
− | + | if version >= 5: | |
− | + | vec3 pivotPosition | |
− | + | if version >= 6: | |
− | + | float scale | |
− | + | if version >= 7: | |
− | + | if (flags & 4) == 4: | |
− | + | fileRef packDesc | |
+ | if version >= 8: // TM 2020 | ||
+ | vec3 | ||
+ | vec3 | ||
===CGameCtnReplayRecord (03 093 000)=== | ===CGameCtnReplayRecord (03 093 000)=== | ||
'''03093000''' ''"Version"'' | '''03093000''' ''"Version"'' | ||
uint32 version | uint32 version | ||
− | if version >= | + | if version >= 3: |
meta (trackUID, environment, author) | meta (trackUID, environment, author) | ||
uint32 time (ms) | uint32 time (ms) | ||
Line 852: | Line 916: | ||
'''0309200B''' (skippable) | '''0309200B''' (skippable) | ||
− | uint32 | + | uint32 numCheckpoints |
− | + | for each checkpoint: | |
+ | uint32 time | ||
+ | uint32 stuntsScore (usually 0 in TM2) | ||
'''0309200C''' | '''0309200C''' | ||
Line 891: | Line 957: | ||
'''03092019''' | '''03092019''' | ||
− | uint32 eventsDuration | + | uint32 eventsDuration (if 0 then stop reading this chunk) |
uint32 ignored | uint32 ignored | ||
uint32 numControlNames | uint32 numControlNames | ||
Line 929: | Line 995: | ||
uint32 IsInternal : 1; | uint32 IsInternal : 1; | ||
uint32 IsAdvanced : 1; | uint32 IsAdvanced : 1; | ||
− | uint32 IconDesc : 5; // 0 = Unknown, 1 = NoIcon, 2 = | + | uint32 IconDesc : 5; // 0 = Unknown, 1 = NoIcon, 2 = 64x64, 3 = 128x128 |
uint32 __unused__ : 24; | uint32 __unused__ : 24; | ||
}; | }; | ||
Line 941: | Line 1,007: | ||
uint16 iconWidth | uint16 iconWidth | ||
uint16 iconHeight | uint16 iconHeight | ||
− | byte iconData[4*iconWidth*iconHeight] | + | if (int16)iconWidth >= 0 && (int16)iconHeight >= 0: |
+ | byte iconData[4*iconWidth*iconHeight] // one RGBA uint32 per pixel | ||
+ | else: | ||
+ | uint16 version | ||
+ | if version >= 1: | ||
+ | uint32 size | ||
+ | byte iconData[size] // WebP image | ||
'''0301A006''' (header) | '''0301A006''' (header) | ||
Line 1,137: | Line 1,209: | ||
string onlineLogin | string onlineLogin | ||
string onlineSupportKey | string onlineSupportKey | ||
+ | |||
+ | ===CGameCtnMediaClipGroup (03 07A 000)=== | ||
+ | '''0307A003''' | ||
+ | uint32 ignored (0xA) | ||
+ | uint32 numClips | ||
+ | for each clip: | ||
+ | noderef clip | ||
+ | uint32 numClips | ||
+ | for each clip: | ||
+ | vec3 referenceFramePosition (NaN if none) | ||
+ | uint32 referenceFrameRotation | ||
+ | uint32 triggerCondition (0: None, 1: Time < arg, 2: Time > arg, 3: Already triggered, 4: Speed < arg, 5: Speed > arg, 6: Not already triggered) | ||
+ | float triggerArgument | ||
+ | uint32 numTriggers | ||
+ | for each trigger: | ||
+ | vec3 position | ||
+ | |||
+ | ===CGameCtnMediaClip (03 079 000)=== | ||
+ | '''03079002''' | ||
+ | uint32 | ||
+ | uint32 numTracks | ||
+ | for each track: | ||
+ | noderef mediaTrack | ||
+ | string clipName | ||
+ | uint32 | ||
+ | |||
+ | '''03079003''' | ||
+ | uint32 | ||
+ | uint32 numTracks | ||
+ | for each track: | ||
+ | noderef mediaTrack | ||
+ | string clipName | ||
+ | |||
+ | '''03079004''' (all fields are ignored) | ||
+ | uint32 | ||
+ | |||
+ | '''03079005''' | ||
+ | uint32 ignored (0xA) | ||
+ | uint32 numTracks | ||
+ | for each track: | ||
+ | noderef mediaTrack | ||
+ | string clipName | ||
+ | |||
+ | '''03079007''' | ||
+ | uint32 localPlayerClipEntIndex | ||
+ | |||
+ | '''0307900A''' | ||
+ | bool stopWhenLeave | ||
+ | |||
+ | '''0307900D''' | ||
+ | uint32 | ||
+ | uint32 version | ||
+ | uint32 numTracks | ||
+ | for each track: | ||
+ | noderef mediaTrack | ||
+ | string clipName | ||
+ | bool stopWhenLeave | ||
+ | bool | ||
+ | bool stopWhenRespawn | ||
+ | string | ||
+ | float | ||
+ | uint32 localPlayerClipEntIndex | ||
+ | |||
+ | ===CGameCtnMediaTrack (03 078 000)=== | ||
+ | '''03078001''' | ||
+ | string trackName | ||
+ | uint32 ignored (0xA) | ||
+ | uint32 numTracks | ||
+ | for each track: | ||
+ | noderef mediaBlock | ||
+ | uint32 unknown | ||
+ | |||
+ | '''03078004''' | ||
+ | bool keepPlaying | ||
+ | uint32 ignored (0) | ||
+ | |||
+ | ===CControlEffectSimi (07 010 000)=== | ||
+ | '''07010003''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | vec2 position | ||
+ | float rotation (in rad) | ||
+ | float scaleX | ||
+ | float scaleY | ||
+ | float opacity | ||
+ | float depth | ||
+ | bool centered | ||
+ | |||
+ | '''07010004''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | vec2 position | ||
+ | float rotation (in rad) | ||
+ | float scaleX | ||
+ | float scaleY | ||
+ | float opacity | ||
+ | float depth | ||
+ | float | ||
+ | float isContinousEffect | ||
+ | float | ||
+ | float | ||
+ | bool centered | ||
+ | uint32 colorBlendMode | ||
+ | bool isContinousEffect | ||
+ | |||
+ | '''07010005''' | ||
+ | ReadChunk(0x07010004) | ||
+ | bool isInterpolated | ||
+ | |||
+ | ===CGameCtnMediaBlock=== | ||
+ | |||
+ | ====CGameCtnMediaBlockCameraPath (03 0A1 000)==== | ||
+ | '''030A1002''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | vec3 cameraPosition | ||
+ | float pitch (rad) | ||
+ | float yaw (rad) | ||
+ | float roll (rad) | ||
+ | float FOV | ||
+ | bool anchorRot | ||
+ | uint32 indexTargetPlayer (maxint: None, 0: Local player) | ||
+ | bool anchorVis | ||
+ | uint32 indexAnchorPlayer (maxint: None, 0: Local player) | ||
+ | vec3 targetPosition | ||
+ | float weight | ||
+ | float | ||
+ | float | ||
+ | float | ||
+ | float | ||
+ | |||
+ | ====CGameCtnMediaBlockCameraCustom (03 0A2 000)==== | ||
+ | '''030A2005''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | uint32 interpolation (0: None, 1: Hermit, 2: Linear, 3: FixedTangent) | ||
+ | uint64 unused (0) | ||
+ | vec3 cameraPosition | ||
+ | float pitch (rad) | ||
+ | float yaw (rad) | ||
+ | float roll (rad) | ||
+ | float FOV | ||
+ | bool anchorRot | ||
+ | uint32 indexTargetPlayer (maxint: None, 0: Local player) | ||
+ | bool anchorVis | ||
+ | uint32 indexAnchorPlayer (maxint: None, 0: Local player) | ||
+ | vec3 targetPosition | ||
+ | float leftTangentX | ||
+ | float leftTangentY | ||
+ | float leftTangentZ | ||
+ | float rightTangentX | ||
+ | float rightTangentY | ||
+ | float rightTangentZ | ||
+ | |||
+ | ====CGameCtnMediaBlockCameraEffectShake (03 0A4 000)==== | ||
+ | '''030A4000''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | float intensity | ||
+ | float speed | ||
+ | |||
+ | ====CGameCtnMediaBlockImage (03 0A5 000)==== | ||
+ | '''030A5000''' | ||
+ | noderef CControlEffectSimi | ||
+ | fileref image | ||
+ | |||
+ | ====CGameCtnMediaBlockMusicEffect (03 0A6 000)==== | ||
+ | '''030A6001''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | float musicVolume | ||
+ | float soundVolume | ||
+ | |||
+ | ====CGameCtnMediaBlockSound (03 0A7 000)==== | ||
+ | '''030A7001''' | ||
+ | fileref sound | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | float volume | ||
+ | float pan | ||
+ | |||
+ | '''030A7002''' | ||
+ | uint32 playCount | ||
+ | bool isLooping | ||
+ | |||
+ | '''030A7003''' | ||
+ | uint32 version | ||
+ | uint32 playCount | ||
+ | bool isLooping | ||
+ | bool isMusic | ||
+ | if version >= 1: | ||
+ | bool stopWithClip | ||
+ | if version >= 2: | ||
+ | bool audioToSpeech | ||
+ | int audioToSpeechTarget | ||
+ | |||
+ | '''030A7004''' | ||
+ | fileref sound | ||
+ | uint32 ignored (1) | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | float volume | ||
+ | float pan (unused) | ||
+ | vec3 soundTransmitorPosition | ||
+ | |||
+ | ====CGameCtnMediaBlockText (03 0A8 000)==== | ||
+ | '''030A8001''' | ||
+ | string text | ||
+ | noderef CControlEffectSimi | ||
+ | |||
+ | '''030A8002''' | ||
+ | color textColor (with in-game color selector format) | ||
+ | |||
+ | ====CGameCtnMediaBlockTrails (03 0A9 000)==== | ||
+ | '''030A9000''' | ||
+ | float timeClipStart | ||
+ | float timeClipEnd | ||
+ | |||
+ | ====CGameCtnMediaBlockTransitionFade (03 0AB 000)==== | ||
+ | '''030AB000''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | float opacity | ||
+ | color transitionColor (with in-game color selector format) | ||
+ | float ignored (1.0) | ||
+ | |||
+ | ====CGameCtnMediaBlockFxColors (03 080 000)==== | ||
+ | '''03080003''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | float intensity | ||
+ | float blendZ (far) | ||
+ | float distance (near) | ||
+ | float distance (far) | ||
+ | additionnalParameters (near) | ||
+ | additionnalParameters (far) | ||
+ | |||
+ | additionnalParameters format: | ||
+ | float inverse | ||
+ | float hue | ||
+ | float saturation | ||
+ | float brightness | ||
+ | float contrast | ||
+ | color | ||
+ | float ignored (1.0) | ||
+ | float ignored (1.0) | ||
+ | float ignored (1.0) | ||
+ | float ignored (0.0) | ||
+ | |||
+ | ====CGameCtnMediaBlockFxBlurDepth (03 081 000)==== | ||
+ | '''03081001''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | float lensSize | ||
+ | bool forceFocus | ||
+ | float focusZ | ||
+ | |||
+ | ====CGameCtnMediaBlockFxBlurMotion (03 082 000)==== | ||
+ | '''03082000''' | ||
+ | float timeClipStart | ||
+ | float timeClipEnd | ||
+ | |||
+ | ====CGameCtnMediaBlockFxBloom (03 083 000)==== | ||
+ | '''03083001''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | float intensity | ||
+ | float sensitivity | ||
+ | |||
+ | ====CGameControlCameraFree (03 084 000)==== | ||
+ | '''03084003''' | ||
+ | float timeClipStart | ||
+ | float timeClipEnd | ||
+ | lookbackstring cameraView | ||
+ | uint32 indexTargetPlayer (0: Local player) | ||
+ | |||
+ | ====CGameCtnMediaBlockTime (03 085 000)==== | ||
+ | '''03085000''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | float timeValue | ||
+ | float tangent | ||
+ | |||
+ | ====CGameCtnMediaBlock3dStereo (03 024 000)==== | ||
+ | '''03024000''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | float separation | ||
+ | float screenDist | ||
+ | |||
+ | ====CGameCtnMediaBlockTriangles (03 029 000)==== | ||
+ | '''03029001''' | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | uint32 numKeys | ||
+ | uint32 numPoints | ||
+ | for each key: | ||
+ | for each point: | ||
+ | vec3 pointPosition | ||
+ | uint32 numPoints | ||
+ | for each point: | ||
+ | color pointColor (with in-game color selector format) | ||
+ | float opacity | ||
+ | uint32 numTriangles | ||
+ | for each triangle: (index of the 3 vertices forming the triangle) | ||
+ | uint32 vertex1 | ||
+ | uint32 vertex2 | ||
+ | uint32 vertex3 | ||
+ | uint32 ignored (1) | ||
+ | uint32 ignored (0) | ||
+ | uint32 ignored (0) | ||
+ | float ignored (1.0) | ||
+ | uint32 ignored (0) | ||
+ | uint64 ignored (0) | ||
+ | |||
+ | ====CGameCtnMediaBlockGhost (03 0E5 000)==== | ||
+ | '''030E5001''' | ||
+ | float timeClipStart | ||
+ | float timeClipEnd | ||
+ | noderef ghostModel (CGameCtnGhost) | ||
+ | float startOffset | ||
+ | |||
+ | '''030E5002''' | ||
+ | uint32 version | ||
+ | if version < 3: | ||
+ | float timeClipStart | ||
+ | float timeClipEnd | ||
+ | if version >= 3: | ||
+ | uint32 numKeys | ||
+ | for each key: | ||
+ | float timeStamp | ||
+ | float | ||
+ | noderef ghostModel (CGameCtnGhost) | ||
+ | float startOffset | ||
+ | bool noDamage | ||
+ | bool forceLight | ||
+ | bool forceHue | ||
===CMwNod (01 001 000)=== | ===CMwNod (01 001 000)=== | ||
Line 1,153: | Line 1,577: | ||
* [http://www.xaseco.org/tools.php Extract GBX data] - a PHP script to format and print data from all file types supported by the GBX Data Fetcher module. | * [http://www.xaseco.org/tools.php Extract GBX data] - a PHP script to format and print data from all file types supported by the GBX Data Fetcher module. | ||
* [http://www.xaseco.org/tools.php Tally GBX versions] - a PHP script to tally some version data from all .Challenge|Map.Gbx files (includes sample challenges in all known versions). | * [http://www.xaseco.org/tools.php Tally GBX versions] - a PHP script to tally some version data from all .Challenge|Map.Gbx files (includes sample challenges in all known versions). | ||
+ | * [https://github.com/BigBang1112/gbx-net GBX.NET] - a C#/.NET parser for .Gbx files. | ||
+ | * [https://github.com/donadigo/pygbx pygbx] - a Python library to parse .Gbx files. | ||
+ | * [https://github.com/ThaumicTom/gbx.js gbx.js] - a slim, fast and easy to set up Gamebox (GBX) parser written in vanilla JavaScript. | ||
+ | * [https://github.com/stefan-baumann/ManiaPlanetSharp ManiaPlanetSharp] - a .NET library, written in C#, which provides easy-to-use, object-oriented ways to access data and services related to ManiaPlanet. | ||
* [http://micolous.id.au/pages/disasm.html Trackmania Disassembler] - includes a library allowing you to write your own applications that can read the format. | * [http://micolous.id.au/pages/disasm.html Trackmania Disassembler] - includes a library allowing you to write your own applications that can read the format. | ||
* {{ArchiveOrg|http://tossha.ru/trackstudio/|TrackStudio|https://web.archive.org/web/20160319194133/http://tossha.ru/trackstudio/}} - a Windows TrackMania Forever track editor with 3D interface. | * {{ArchiveOrg|http://tossha.ru/trackstudio/|TrackStudio|https://web.archive.org/web/20160319194133/http://tossha.ru/trackstudio/}} - a Windows TrackMania Forever track editor with 3D interface. |
Latest revision as of 14:45, 4 November 2022
TrackMania .gbx files (GameBox) are generic container files that can contain everything from configuration to textures to track definitions. They consist of a header, a reference table and the body.
In old versions of TrackMania they used to be text files – nowadays they are binary files. Integers are stored in little-endian order. The file body is often compressed (using LZO).
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 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 IDs there are also class IDs, which are just like chunk IDs except the chunk index part is ignored (and 0). For a complete overview of engines and classes, see Class IDs.
Header
The header contains things like compression information and the class ID of the main class instance. The current version of the header also provides a few chunks of the main class that serve as meta information (e.g. the thumbnail of a challenge).
- byte[3] magic: "GBX"
- uint16 version: currently 6
- if version >= 3:
- byte format: 'B' or 'T': Binary or Text (always B for version 6 gbx files)
- byte compression: 'U' or 'C': Uncompressed or Compressed reference table (unused, always U)
- byte compression: 'U' or 'C': Uncompressed or Compressed body (typically C for binary files)
- if version >= 4:
- byte unknown: 'R' or 'E': unknown purpose (typically R for binary files)
- uint32 classID (class ID of main class instance)
- if version >= 6:
- uint32 userDataSize
- byte[userDataSize]:
- uint32 numHeaderChunks
- HeaderEntry[numHeaderChunks]
- uint32 chunkID
- uint32 chunkSize (may have bit 31 set. This indicates a "heavy" header chunk which is skipped while scanning gbx files on game startup)
- concatenated data of header chunks
- uint32 numNodes: the total number of class instances related to this gbx file. This includes the main instance, any local auxiliary instances, and any referenced external nodes/files. An internal list will be allocated with this number of entries; the main instance is at index 0.
Reference table
- uint32 numExternalNodes: the number of external nodes and files that this .gbx references. These come from other files located in the .gbx's .pak file. The references to these will be placed in the same list as the local nodes (see above). Both raw files (e.g. textures) and .gbx main instances can be referenced.
- if numExternalNodes > 0:
- uint32 ancestorLevel: how many folder levels to go up in the .pak folder hierarchy to reach the base folder from which files will be referenced.
- uint32 numSubFolders
- Folder folders[numSubFolders]
- string name
- uint32 numSubFolders
- Folder folders[numSubFolders]
- ExternalNode externals[numExternalNodes]
- uint32 flags
- if (flags & 4) == 0:
- string fileName
- else:
- uint32 resourceIndex
- uint32 nodeIndex (the index in the local node list where this external reference will be stored)
- if version >= 5: (header version)
- bool useFile (0 or 1, whether to use the file itself or the node that was loaded from the file)
- if (flags & 4) == 0:
- uint32 folderIndex: the depth-first index of the folder which the file is in. 0 means the base folder itself.
Body
- if body is compressed:
- uint32 uncompressedSize
- uint32 compressedSize
- byte data[compressedSize] (compressed with regular LZO)
- else:
- byte data[]
Reading the body
The file body contains further chunks of the main class instance, and may also contain auxiliary class instances. Reading the body 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()[1] { while (true) { chunkID = ReadUInt32();[2] if (chunkID == 0xFACADE01) // no more chunks { OnNodLoaded();[3] return; } chunkFlags = GetChunkInfo(chunkID);[4] if (chunkFlags == 0xFACADE01 || (chunkFlags & 0x11) == 0x10) { skip = ReadUInt32(); if (skip != 0x534B4950) // "SKIP" { OnNodLoaded(); return; } chunkDataSize = ReadUInt32(); SkipData(chunkDataSize);[5] // skip unknown or obsolete chunk } if (chunkFlags != 0xFACADE01 && (chunkFlags & 0x11) != 0x10) { if (chunkFlags & 0x10) // skippable { skip = ReadUInt32(); // unused chunkDataSize = ReadUInt32(); // unused } ReadChunk(chunkID);[6] // read the chunk } } }
The code 0xFACADE01 is used here for two different purposes. As a dummy chunk ID (read from the file), it signifies the end of the chunk list for the current class. As a flag (returned by the function GetChunkInfo), it signifies that the passed chunk ID is unknown. GetChunkInfo() doesn't read anything from the file; it provides loading flags for the specified chunk ID. The flag 0x01 indicates that the chunk must be read. If this flag is not set, the chunk can be skipped (if possible). The second important flag is 0x10 and indicates whether the chunk is "skippable" or not. If it is, the chunk ID is followed by an uint32 0x50494B53 ("SKIP", shows up as "PIKS" in the file due to little-endian ordering) and an uint32 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.
The function OnNodLoaded() is used by some classes to prepare the read data (e.g., to make them compatible with newer versions of the engine).
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.
Primitives
- bool: 32-bit little-endian integer that can be 0 or 1.
- byte, uint16, int32, uint32, uint64, uint128, float: regular little-endian encoding.
- vec2:
- float x
- float y
- vec3:
- float x
- float y
- float z
- color:
- float r
- float g
- float b
- string:[7]
- lookbackstring:[8] a form of compression which allows to avoid repeating the same string multiple times. Every time a new string is encountered, it is added to a string list, and from then on this list entry is referenced instead of repeating the string another time.
- if this is the first lookback string encountered:
- uint32 version (currently 3)
- uint32 index: bit 31 and 30 define the string type. If both bits are 0, the index is a number. The actual index is represented by the bits 0-29. If this value is 0, a new string follows (and will be added to the string list). If it is greater than one, use the string at stringlist [index - 1]. If no data is provided (unassigned), the bits 30 and 31 indicate how this state is stored. If bit 31 is set, the string "Unassigned" is used, but if bit 30 is set, the value -1 is stored instead.
- If the bits 0 through 29 are 0 and bit 30 or 31 is 1:
- string newString. Append to the string list.
- if this is the first lookback string encountered:
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. If index represents a number (bits 30 and 31 not set), it describes the position inside a global string table. In most cases it concerns the ID of a collection.
Note: Virtual Skipper 2 uses version 2 of the lookback strings. In this version, the string is always stored, the index always contains the position within the global name table, and the field with the version is also always present.
- meta:[9] contains meta information like the track environment, time of day, and author.
- lookbackstring id
- lookbackstring collection
- lookbackstring author
- fileref:[10] path to an external file, e.g. a skin.
- byte version (currently 3)
- if version >= 3:
- byte[32] (checksum)
- string filePath (skin: if version < 2 relative to Skins folder else relative to user folder)
- if filePath.length > 0 && version >= 1:
- string locatorUrl
- noderef:[11] a reference to an auxiliary class instance.
- uint32 index. if this is -1, the node reference is empty (null).
- if the index is >= 0 and the node at the index has not been read yet:
- uint32 classID: instantiate a new node for this class ID and store it in the node list at the specified index.
- ReadNode()
Note: In case of a text format .gbx file (marked by a 'T' in the header) all numbers and strings are stored as one line of ASCII text in each case. Every line ends with a carriage return-linefeed (<CR><LF>) combination (0x0D and 0x0A). A single <LF> is part of a string.
Encapsulation
Encapsulation is a special kind of chunk behavior added in ManiaPlanet that handles lookbackstrings and noderefs differently.
- A new temporary lookbackstring list is created just for this chunk - the counting of strings starts from 1 again. However, the previous state of lookback strings is not reset.
- An index of the noderef is not included and the node is not added to the node list. That list is not reset either.
It looks like this:
CSystemArchiveEncapsuledChunk::CSystemArchiveEncapsuledChunk(); // ... CSystemArchiveEncapsuledChunk::~CSystemArchiveEncapsuledChunk();
In this range of code, you need to set up your binary reader to handle the two rules above.
Here is the list of chunks that use encapsulation:
- CGameCtnChallenge 03043040, 03043041, 03043043, 03043044, 0304304E, 0304304F (version < 2), 03043054, 03043058 (version >= 1)
- CGameCtnMacroBlockInfo 0310D00B, 0310D011
- CGamePlayerProfileChunk_GameScores 03146000, 03146002, 03146004
- CGamePlayerProfileChunk_ScriptPersistentTraits 03170000
Class descriptions
CGameCtnChallenge (03 043 000)
03043002 "TmDesc"
byte version if version < 3: meta (trackUID, environment, mapAuthor) string trackName bool 0 if version >= 1: uint32 bronzeTime (ms) uint32 silverTime (ms) uint32 goldTime (ms) uint32 authorTime (ms) if version == 2: byte if version >= 4: uint32 cost (Copper price; since version 12: Display cost) if version >= 5: bool multilap if version == 6: bool if version >= 7: uint32 trackType (0: Race, 1: Platform, 2: Puzzle, 3: Crazy, 4: Shortcut, 5: Stunts, 6: Script) if version >= 9: uint32 0 if version >= 10: uint32 authorScore if version >= 11: uint32 editorMode (bit 0: advanced/simple editor, bit 1: has ghost blocks) if version >= 12: bool 0 if version >= 13: uint32 nbCheckpoints uint32 nbLaps
03043003 "Common"
byte version meta (trackUID, environment, mapAuthor) 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: bool locked (used by Virtual Skipper to lock the map parameters) string password (weak xor encryption, no longer used in newer track files; see 03043029) if version >= 2: meta decoration (timeOfDay, environment, envirAuthor) (decoration envir can be different than collection envir) if version >= 3: vec2 mapOrigin if version >= 4: vec2 mapTarget if version >= 5: uint128 if version >= 6: string mapType string mapStyle if version <= 8: bool if version >= 8: uint64 lightmapCacheUID if version >= 9: byte lightmapVersion if version >= 11: lookbackstring titleUID
03043004 "Version"
uint32 version
03043005 "Community"
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 "Thumbnail"
uint32 version if version != 0: uint32 thumbSize "<Thumbnail.jpg>" byte thumb[thumbSize] "</Thumbnail.jpg>" "<Comments>" string comments "</Comments>"
03043008 "Author"
uint32 version uint32 authorVersion string authorLogin string authorNick string authorZone string authorExtraInfo
0304300D
meta (vehicle, collection, author)
03043011
noderef collectorList noderef challengeParameters uint32 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)
03043012
string
03043013
ReadChunk(0x0304301F)
03043014 (skippable)
uint32 string password (old style password with weak xor encryption. This chunk is no longer used in newer track files; see 03043029)
03043017 (skippable)
uint32 numCheckpoints Checkpoint[numCheckpoints]
Checkpoint:
uint32 uint32 uint32
03043018 (skippable)
bool uint32 numLaps
03043019 (skippable)
fileref modPackDesc
0304301C (skippable)
uint32 playMode (0: Race, 1: Platform, 2: Puzzle, 3: Crazy, 4: Shortcut, 5: Stunts)
0304301F
meta (trackUID, environment, mapAuthor) string trackName meta decoration (timeOfDay, environment, envirAuthor) uint32 sizeX uint32 sizeY uint32 sizeZ bool needUnlock if chunkId != 03043013: uint32 version uint32 numBlocks for each block: lookbackstring blockName byte rotation (0/1/2/3) byte x byte y byte z if version == 0: uint16 flags if version > 0: uint32 flags if (flags == 0xFFFFFFFF) continue (read the next block) if (flags & 0x8000) != 0: custom block lookbackstring author noderef skin if (flags & 0x100000) noderef blockparameters
Note: blocks with flags 0xFFFFFFFF should be skipped, they aren't counted in the numBlocks.
Note: It is possible that additional blocks with flags 0xFFFFFFFF (Unassigned) follow after all other blocks.
03043021
noderef clipIntro noderef clipGroupInGame noderef clipGroupEndRace
03043022
uint32
03043024
fileref customMusicPackDesc
03043025
vec2 mapCoordOrigin vec2 mapCoordTarget
03043026
noderef clipGlobal
03043027
bool archiveGmCamVal if archiveGmCamVal: byte GmMat3 (vec3 x 3) vec3 float float float
03043028
ReadChunk(0x03043027) string comments
03043029 (skippable)
uint128 passwordHash (salted MD5) uint32 CRC32("0x" + hex(passwordHash) + "???" + trackUID)
0304302A
bool
0304303D (skippable)
bool unknown uint32 version if version >= 5: uint32 frames // If version < 5 then frames = 1 if version >= 2: for each frame: uint32 size byte image[size] // Image is JPEG/JFIF or WEBP/RIFF file format if version >= 3: uint32 size byte image[size] if version >= 6: uint32 size byte image[size] if size != 0: uint32 uncompressedSize uint32 compressedSize byte compressedData[compressedSize] // ZLIB compressed lightmap cache node
03043040 (skippable) "items"
Note: This chunk has its own lookback string state (is encapsulated with CSystemArchiveEncapsuledChunk).
Note: Version 5+ can have data that is based on the count of certain types of items in the item array. If you want to modify items in TM2020, you can come across issues when modifying the item array and it becomes rather difficult. This can be resolved by downgrading the version to 4. This downgrade should only affect item snapping - which is "useful" only when deleting items in the map editor.
Note: The following information may not be 100% accurate.
- connectedItemPairs: Array of AnchoredObjects index pairs where the first item (of pair) is the item that will delete the second item (of pair). Example: (87, 110) - item #87 will delete item #110, when #87 gets deleted.
- snappedBlocks: Values are indexes of the Blocks array (chunk 0x01F). -1 = snapped to an item and you should use the index of snappedItems.
- snappedItems: Values are indexes of the AnchoredObjects array (this chunk). -1 = snapped to a block and you should use the index of snappedBlocks.
- snapItemGroups: Separates item "deletion groups" via a simple number. Usual values are between 0 and 5. You can index these with itemSnapIndexes values.
- itemSnapIndexes: Uses the values above to connect them with the item list. Values are indexes of snappedBlocks/snappedItems arrays. Any index of this array corresponds to the same AnchoredObjects index, except -1, which means the item is not snapped.
uint32 version uint32 uint32 size uint32 (10) uint32 numItems for each item: node item (CGameCtnAnchoredObject, direct node) if version >= 1 && version != 5: uint32 connectedItemPairCount uint2 connectedItemPairs[connectedItemPairCount] // 2 uint32 per element if version >= 5: // TM2020 uint32 snappedBlockCount uint32 snappedBlocks[snappedBlockCount] if version < 7: uint32 snapItemGroupCount uint32 snapItemGroups[snapItemGroupCount] if version >= 6: uint32 snappedItemCount uint32 snappedItems[snappedItemCount] if version >= 7: uint32 snapItemGroupCount uint32 snapItemGroups[snapItemGroupCount] if version != 6: uint32 size uint32[size] // unknown atm uint32 itemSnapIndexCount // should be the same as the item count itself uint32 itemSnapIndexes[itemSnapIndexCount]
03043044 (skippable)
uint32 unknown (0) uint32 size uint32 classID uint32 version if version >= 2: uint32 count (number of metadata records) for each count: string varName uint32 varType switch varType: case EType_Boolean: bool case EType_Integer: int32 case EType_Real: float case EType_Text: string case EType_Int2: int32 int32 case EType_Int3: int32 int32 int32 case EType_Vec2: float float case EType_Vec3: float float float case EType_Array: uint32 typeKey uint32 typeValue uint32 arrayElements for each arrayElements switch typeKey: case EType_Boolean: bool case EType_Integer: int32 case EType_Real: float case EType_Text: string switch typeValue: case EType_Boolean: bool case EType_Integer: int32 case EType_Real: float case EType_Text: string case EType_Int2: int32 int32 case EType_Int3: int32 int32 int32 case EType_Vec2: float float case EType_Vec3: float float float case EType_Array: recursively read multidimensional arrays
The variable type is to be interpreted as follows:
enum eScriptType { EType_Void = 0, EType_Boolean, EType_Integer, EType_Real, EType_Class, EType_Text, EType_Enum, EType_Array, EType_ParamArray, EType_Vec2, EType_Vec3, EType_Int3, EType_Iso4, EType_Ident, EType_Int2, EType_Struct };
Maniaplanet 4.1 has changed the way script types and values are written. The code now does the following:
- First write the list of all types (all types appear only once).
- For each value, write the index of the type in the list, and then write the value.
Writing the types and values has not changed except for the addition of structures which are a plain list of members.
Also note that the indices are written in the following form:
- If the index is < 128, it is written directly into one byte (because this covers 99% of the cases).
- Otherwise, it is written in the first 7 bits of the first byte and in the following 2 bytes, a total of 23 bits (7+8+8). The 8th bit of the first byte is set to 1.
Note that this scheme is also used to write string lengths.
03043054 (skippable)
Note: This chunk has its own lookback string state (is encapsulated with CSystemArchiveEncapsuledChunk).
uint32 version (1) uint32 uint32 chunkSize uint32 itemCount (number of embedded items) meta items[itemCount] uint32 zipSize (embedded items ZIP file size) byte zipFile[zipSize] uint32 textureCount string textures[textureCount]
21080001 "VskDesc"
byte version if version < 1: meta (trackUID, environment, mapAuthor) string trackName bool unknown uin32 unknown if version < 1: byte unknown byte unknown if version < 9: byte boatName (0: Acc, 1 : Multi, 2 : Melges, 3 : OffShore) if version >= 9: lookbackstring boat (separated by line feed: Boat name, website, boat ID) if version >= 12: lookbackstring boatAuthor byte raceMode (0: Fleet Race, 1 : Match Race, 2 : Team Race) byte unknown byte windDirection (0: North, 1: NE, 2: East, 3: SE, 4: South, 5: SW, 6: West, 7: NW) byte windStrength (Force: windStrength + 3) byte weather (0: Sunny, 1: Cloudy, 2: Rainy, 3: Stormy) byte unknown byte startDelay (0: Immediate, 1: 1 Min, 2: 3 Min, 3: 5 Min, 4: 8 Min) uint32 startTime if version >= 2: uint32 timeLimit bool noPenalty bool inflPenalty bool finishFirst if version >= 3: byte nbAIs if version >= 4: float courseLength if version == 4: byte unknown uint32 windShiftDuration if version >= 5: int32 windShiftAngle byte unknown if version == 6 || version == 7: bool unknown string unknown if version >= 7: bool exactWind (Exact wind: !exactWind) if version >= 10: uint32 spawnPoints if version >= 11: byte aILevel (0: Easy, 1: Intermediate, 2: Expert, 3: Pro) if version >= 13: bool smallShifts if version >= 14: bool noRules bool startSailUp
CGameCtnCollectorList (03 01B 000)
0301B000
uint32 archiveCount SCollectorStock archive[archiveCount] meta (blockName, collection, author) uint32 numPieces
CGameCtnChallengeParameters (03 05B 000)
0305B000 (all fields are ignored)
uint32 uint32 uint32 uint32 uint32 uint32 uint32 uint32
0305B001
string tip string tip string tip string tip
0305B002 (all fields are ignored)
uint32 uint32 uint32 float float float uint32 uint32 uint32 uint32 uint32 uint32 uint32 uint32 uint32 uint32
0305B003 (all fields are ignored)
uint32 float uint32 uint32 uint32 uint32
0305B004
uint32 bronzeTime (ms) uint32 silverTime (ms) uint32 goldTime (ms) uint32 authorTime (ms) uint32 ignored
0305B005 (all fields are ignored)
uint32 uint32 uint32
0305B006
uint32 count uint32 items[count] (ignored)
0305B007
uint32 (ignored)
0305B008
uint32 timeLimit (ms) uint32 authorScore (ms)
0305B00A (skippable)
uint32 (0?) uint32 bronzeTime (ms) uint32 silverTime (ms) uint32 goldTime (ms) uint32 authorTime (ms) uint32 timeLimit (ms) uint32 authorScore (ms)
0305B00D
noderef raceValidateGhost (CGameCtnGhost) (usually -1)
0305B00E (skippable)
string mapType string mapStyle uint32
CGameCtnBlockSkin (03 059 000)
03059000
string text string ignored
03059001
string text fileref packDesc
03059002
string text fileref packDesc fileref parentPackDesc
03059003 "TM2020"
uint32 version fileref secondaryPackDesc
CGameWaypointSpecialProperty (03 13B 000)
Mapped to the new GameData engine (2E) as class CGameWaypointSpecialProperty (2E009000).
0313B000
uint32 version if version == 1: uint32 spawn uint32 order if version == 2: string tag uint32 order
CGameCtnAnchoredObject (03 101 000)
03101002
uint32 version meta (itemFile, collection, author) vec3 pitchYawRoll byte blockUnitX byte blockUnitY byte blockUnitZ lookbackstring anchorTreeId vec3 absolutePositionInMap CGameWaypointSpecialProperty waypointSpecialProperty (-1 if not a waypoint, otherwise direct node) if version < 5: uint32 if version >= 4: uint16 flags if version >= 5: vec3 pivotPosition if version >= 6: float scale if version >= 7: if (flags & 4) == 4: fileRef packDesc if version >= 8: // TM 2020 vec3 vec3
CGameCtnReplayRecord (03 093 000)
03093000 "Version"
uint32 version if version >= 3: meta (trackUID, environment, author) uint32 time (ms) string nickName if version >= 6: string driverLogin if version >= 8: byte 1 lookbackstring titleUID
03093001 "Community"
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 "Author" (header)
uint32 version uint32 authorVersion string authorLogin string authorNick string authorZone string authorExtraInfo
03093002 (body)
uint32 size byte GBX[size] (the track the replay was recorded on)
03093007 (skippable)
uint32
03093014
uint32 ignored (0xA) uint32 numGhosts noderef ghosts[numGhosts] uint32 ignored uint32 num uint64[numExtras]
03093015
noderef
CGameGhost (03 03F 005)
0303F005
uint32 uncompressedSize uint32 compressedSize byte compressedData[compressedSize]: (compressed with zlib deflate) uint32 classID bool bSkipList2 uint32 uint32 samplePeriod uint32 uint32 size byte sampleData[size] (samples of position, rotation, speed... of the car during the race) uint32 numSamples if numSamples > 0: uint32 firstSampleOffset if numSamples > 1: uint32 sizePerSample if sizePerSample == -1: uint32 sampleSizes[numSamples - 1] if bSkipList2 == 0: uint32 num int32 sampleTimes[num]
0303F006
uint32 IsReplaying ReadChunk(0x0303F005)
A sample record looks as follows:
vec3 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.
- The real part of the quaternion is calculated as cos(angle) which corresponds to a rotation of 2*angle around the rotation axis.
- The imaginary part of the quaternion (the rotation axis) is calculated as the vector (sin(angle)*cos(axisPitch)*cos(axisHeading), sin(angle)*cos(axisPitch)*sin(axisHeading), sin(angle)*sin(axisPitch)).
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)).
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.
03092000 (skippable)
uint32 meta playerModel vec3 uint32 numSkinRef fileref skinRef[numSkinRef] uint32 string ghostNickname string ghostAvatarFile uint32
03092005 (skippable)
uint32 raceTime
03092008 (skippable)
uint32 numRespawns
03092009 (skippable)
color lightTrailColor
0309200A (skippable)
uint32 stuntsScore
0309200B (skippable)
uint32 numCheckpoints for each checkpoint: uint32 time uint32 stuntsScore (usually 0 in TM2)
0309200C
uint32 ignored
0309200E
lookbackstring uid
0309200F
string ghostLogin
03092010
lookbackstring
03092012
uint32 ignored uint128
03092013 (skippable)
uint32 uint32
03092014 (skippable)
uint32
03092015
lookbackstring playerMobilId
03092017 (skippable)
uint32 num fileref skinPackDescs[num] string ghostNickname string ghostAvatarName
03092018
meta
03092019
uint32 eventsDuration (if 0 then stop reading this chunk) uint32 ignored uint32 numControlNames lookbackstring controlNames[numControlNames] uint32 numControlEntries uint32 ControlEntry[numControlEntries] uint32 time (ms + 100000) byte controlNameIndex uint32 onoff (1/0) string gameVersion uint32 exeChecksum uint32 osKind uint32 cpuKind string raceSettingsXML uint32
CGameCtnCollector (03 01A 000)
Base class for CGameCtnBlockInfo, CGameCtnMacroBlockInfo, CGameCtnObjectInfo and CGameCtnDecoration.
Mapped to the new GameData engine (2E) as class CGameCtnCollector (2E001000).
0301A003 "Desc" (header)
meta (name, collection, author) lookbackstring version string pageName (slash-separated folder path where the block appears in the editor) if version == 5: lookbackstring if version >= 4: lookbackstring if version >= 3: struct SCollectorDescFlags { uint32 __unused2__ : 1; uint32 IsInternal : 1; uint32 IsAdvanced : 1; uint32 IconDesc : 5; // 0 = Unknown, 1 = NoIcon, 2 = 64x64, 3 = 128x128 uint32 __unused__ : 24; }; uint16 catalogPosition (order of the blocks within pageName) if version >= 7: string name if version >= 8: byte prodState (0: Aborted, 1: GameBox, 2: DevBuild, 3: Release)
0301A004 "Icon" (header)
uint16 iconWidth uint16 iconHeight if (int16)iconWidth >= 0 && (int16)iconHeight >= 0: byte iconData[4*iconWidth*iconHeight] // one RGBA uint32 per pixel else: uint16 version if version >= 1: uint32 size byte iconData[size] // WebP image
0301A006 (header)
uint64 filetime // lightmap cache timestamp
0301A007
bool isInternal uint32 uint32 catalogPosition uint32 uint32 uint32
0301A008
byte string skinFile
0301A009
string pagePath (same as in chunk 0301A003) bool hasIconFid if hasIconFid: noderef iconFid lookbackstring
0301A00B
meta
301A00C
string name
301A00D
string description
0301A00E
bool iconUseAutoRender uint32 iconQuarterRotationY
0301A00F
string defaultSkinName
CGameCtnObjectInfo (03 01C 000)
Mapped to the new GameData engine (2E) as class CGameItemModel (2E002000).
0301C000 (header)
uint32 itemType (0: Undefined, 1: Ornament (formerly: StaticObject), 2: PickUp (formerly: DynaObject), 3: Character, 4: Vehicle, 5: Spot, 6: Cannon, 7: Group, 8: Decal, 9: Turret, 10: Wagon, 11: Block, 12: EntitySpawner
0301C001 (header)
uint32
0301C006
uint32 defaultCamIndex (-1 = none, 0 and above = camera index; used for cars)
0301C008
uint32 numNadeoSkinFids noderef fids[numNadeoSkinFids] (file references)
0301C009
uint32 version = 10 uint32 numCameras noderef cameras[numCameras] (references to CGameControlCamera; used for cars)
0301C00A
noderef decoratorSolid (CPlugDecoratorSolid)
0301C00B
noderef stemMaterial (CPlugMaterial) noderef stemBumpMaterial (CPlugMaterial)
0301C00C
noderef raceInterfaceFid
0301C010
noderef bannerProfileFid (file reference to e.g. BannerProfileCanyon.Texture.gbx)
0301C012
vec3 groundPoint float painterGroundMargin float orbitalCenterHeightFromGround float orbitalRadiusBase float orbitalPreviewAngle
301C013
noderef audioEnvironmentInCar (CPlugAudioEnvironment; used for cars)
301C014
noderef baseAttributes (CGameAttributes; unused?)
301C015
uint32 objectInfoType (0: Undefined, 1: Ornament, 2: PickUp, 3: Character, 4: Vehicle)
0301C016
noderef defaultSkinFid
0301C017
uint32 version = 0 bool isFreelyAnchorable
0301C018
int version = 0 bool isUsable
0301C019
int version noderef phyModel // CPlugSurface noderef visModel // CPlugSurface if version >= 1: noderef visModelStatic // CPlugSolid2Model
CGameCtnDecoration (03 038 000)
03038000 "MoodRemaping"
byte version string dirName ReadChunk(0x03031000)
03038001
byte version uint32 unknown
CGameCtnCollection (03 033 000)
03033001 "Desc"
byte version lookbackstring collection bool needUnlock if version >= 1: string iconEnv string iconCollection if version >= 2: int32 sortIndex if version >= 3: lookbackstring defaultZone if version >= 4: meta (vehicle, collection, author) if version >= 5: string mapFid vec2 vec2 if version == 5: vec2 mapCoordElem if version == 6 || version == 7: vec2 mapCoordElem vec2 mapCoordIcon if version >= 7: string loadscreen if version >= 8: vec2 mapCoordElem vec2 mapCoordIcon vec2 mapCoordDesc string longDesc if version >= 9: string displayName if version >= 10: bool isEditable
03033002 "CollectorFolders"
byte version string folderBlockInfo string folderItem string folderDecoration if version == 1 || version == 2: string folder if version >= 2: string folderCardEventInfo if version >= 3: string folderMacroBlockInfo if version >= 4: string folderMacroDecals
03033003 "MenuIconsFolders"
byte version string folderMenusIcons
CGameSkin (03 031 000)
Moved to engine Plug (09) as class CPlugGameSkin (090F4000).
03031000 "Skin"
byte version string dirName if version >= 1: string textureName string sceneId byte number for each number: uint32 classId string name string file if version >= 2: bool needMipMap if version >= 4: string dirNameAlt if version >= 5: bool useDefaultSkin
CGamePlayerProfile (03 08C 000)
0308C000 "NetPlayerProfile"
string onlineLogin string onlineSupportKey
CGameCtnMediaClipGroup (03 07A 000)
0307A003
uint32 ignored (0xA) uint32 numClips for each clip: noderef clip uint32 numClips for each clip: vec3 referenceFramePosition (NaN if none) uint32 referenceFrameRotation uint32 triggerCondition (0: None, 1: Time < arg, 2: Time > arg, 3: Already triggered, 4: Speed < arg, 5: Speed > arg, 6: Not already triggered) float triggerArgument uint32 numTriggers for each trigger: vec3 position
CGameCtnMediaClip (03 079 000)
03079002
uint32 uint32 numTracks for each track: noderef mediaTrack string clipName uint32
03079003
uint32 uint32 numTracks for each track: noderef mediaTrack string clipName
03079004 (all fields are ignored)
uint32
03079005
uint32 ignored (0xA) uint32 numTracks for each track: noderef mediaTrack string clipName
03079007
uint32 localPlayerClipEntIndex
0307900A
bool stopWhenLeave
0307900D
uint32 uint32 version uint32 numTracks for each track: noderef mediaTrack string clipName bool stopWhenLeave bool bool stopWhenRespawn string float uint32 localPlayerClipEntIndex
CGameCtnMediaTrack (03 078 000)
03078001
string trackName uint32 ignored (0xA) uint32 numTracks for each track: noderef mediaBlock uint32 unknown
03078004
bool keepPlaying uint32 ignored (0)
CControlEffectSimi (07 010 000)
07010003
uint32 numKeys for each key: float timeStamp vec2 position float rotation (in rad) float scaleX float scaleY float opacity float depth bool centered
07010004
uint32 numKeys for each key: float timeStamp vec2 position float rotation (in rad) float scaleX float scaleY float opacity float depth float float isContinousEffect float float bool centered uint32 colorBlendMode bool isContinousEffect
07010005
ReadChunk(0x07010004) bool isInterpolated
CGameCtnMediaBlock
CGameCtnMediaBlockCameraPath (03 0A1 000)
030A1002
uint32 numKeys for each key: float timeStamp vec3 cameraPosition float pitch (rad) float yaw (rad) float roll (rad) float FOV bool anchorRot uint32 indexTargetPlayer (maxint: None, 0: Local player) bool anchorVis uint32 indexAnchorPlayer (maxint: None, 0: Local player) vec3 targetPosition float weight float float float float
CGameCtnMediaBlockCameraCustom (03 0A2 000)
030A2005
uint32 numKeys for each key: float timeStamp uint32 interpolation (0: None, 1: Hermit, 2: Linear, 3: FixedTangent) uint64 unused (0) vec3 cameraPosition float pitch (rad) float yaw (rad) float roll (rad) float FOV bool anchorRot uint32 indexTargetPlayer (maxint: None, 0: Local player) bool anchorVis uint32 indexAnchorPlayer (maxint: None, 0: Local player) vec3 targetPosition float leftTangentX float leftTangentY float leftTangentZ float rightTangentX float rightTangentY float rightTangentZ
CGameCtnMediaBlockCameraEffectShake (03 0A4 000)
030A4000
uint32 numKeys for each key: float timeStamp float intensity float speed
CGameCtnMediaBlockImage (03 0A5 000)
030A5000
noderef CControlEffectSimi fileref image
CGameCtnMediaBlockMusicEffect (03 0A6 000)
030A6001
uint32 numKeys for each key: float timeStamp float musicVolume float soundVolume
CGameCtnMediaBlockSound (03 0A7 000)
030A7001
fileref sound uint32 numKeys for each key: float timeStamp float volume float pan
030A7002
uint32 playCount bool isLooping
030A7003
uint32 version uint32 playCount bool isLooping bool isMusic if version >= 1: bool stopWithClip if version >= 2: bool audioToSpeech int audioToSpeechTarget
030A7004
fileref sound uint32 ignored (1) uint32 numKeys for each key: float timeStamp float volume float pan (unused) vec3 soundTransmitorPosition
CGameCtnMediaBlockText (03 0A8 000)
030A8001
string text noderef CControlEffectSimi
030A8002
color textColor (with in-game color selector format)
CGameCtnMediaBlockTrails (03 0A9 000)
030A9000
float timeClipStart float timeClipEnd
CGameCtnMediaBlockTransitionFade (03 0AB 000)
030AB000
uint32 numKeys for each key: float timeStamp float opacity color transitionColor (with in-game color selector format) float ignored (1.0)
CGameCtnMediaBlockFxColors (03 080 000)
03080003
uint32 numKeys for each key: float timeStamp float intensity float blendZ (far) float distance (near) float distance (far) additionnalParameters (near) additionnalParameters (far)
additionnalParameters format: float inverse float hue float saturation float brightness float contrast color float ignored (1.0) float ignored (1.0) float ignored (1.0) float ignored (0.0)
CGameCtnMediaBlockFxBlurDepth (03 081 000)
03081001
uint32 numKeys for each key: float timeStamp float lensSize bool forceFocus float focusZ
CGameCtnMediaBlockFxBlurMotion (03 082 000)
03082000
float timeClipStart float timeClipEnd
CGameCtnMediaBlockFxBloom (03 083 000)
03083001
uint32 numKeys for each key: float timeStamp float intensity float sensitivity
CGameControlCameraFree (03 084 000)
03084003
float timeClipStart float timeClipEnd lookbackstring cameraView uint32 indexTargetPlayer (0: Local player)
CGameCtnMediaBlockTime (03 085 000)
03085000
uint32 numKeys for each key: float timeStamp float timeValue float tangent
CGameCtnMediaBlock3dStereo (03 024 000)
03024000
uint32 numKeys for each key: float timeStamp float separation float screenDist
CGameCtnMediaBlockTriangles (03 029 000)
03029001
uint32 numKeys for each key: float timeStamp uint32 numKeys uint32 numPoints for each key: for each point: vec3 pointPosition uint32 numPoints for each point: color pointColor (with in-game color selector format) float opacity uint32 numTriangles for each triangle: (index of the 3 vertices forming the triangle) uint32 vertex1 uint32 vertex2 uint32 vertex3 uint32 ignored (1) uint32 ignored (0) uint32 ignored (0) float ignored (1.0) uint32 ignored (0) uint64 ignored (0)
CGameCtnMediaBlockGhost (03 0E5 000)
030E5001
float timeClipStart float timeClipEnd noderef ghostModel (CGameCtnGhost) float startOffset
030E5002
uint32 version if version < 3: float timeClipStart float timeClipEnd if version >= 3: uint32 numKeys for each key: float timeStamp float noderef ghostModel (CGameCtnGhost) float startOffset bool noDamage bool forceLight bool forceHue
CMwNod (01 001 000)
01001000 "FolderDep"
uint32 number for each number: string dirName
References to the actual functions
- ↑ CMwNod::Archive
- ↑ CClassicArchive::ReadNat32
- ↑ CMwNod::OnNodLoaded
- ↑ CMwNod::GetChunkInfo
- ↑ CClassicArchive::SkipData
- ↑ CMwNod::Chunk
- ↑ CClassicArchive::DoString
- ↑ CMwId::Archive
- ↑ SGameCtnIdentifier::Archive
- ↑ CSystemPackManager::ArchivePackDesc
- ↑ CSystemArchiveNod::DoNodPtr
Applications and Libraries that can inspect/modify the file format
- GbxDump - a Windows tool to dump and analyze the header of all kinds of .Gbx files.
- GBX Data Fetcher - a PHP module with classes to extract useful data from .Challenge|Map.Gbx files (including the thumbnail image), .Replay.Gbx files and .Pack.Gbx|.pak files, as well as parse their XML blocks.
- Extract GBX data - a PHP script to format and print data from all file types supported by the GBX Data Fetcher module.
- Tally GBX versions - a PHP script to tally some version data from all .Challenge|Map.Gbx files (includes sample challenges in all known versions).
- GBX.NET - a C#/.NET parser for .Gbx files.
- pygbx - a Python library to parse .Gbx files.
- gbx.js - a slim, fast and easy to set up Gamebox (GBX) parser written in vanilla JavaScript.
- ManiaPlanetSharp - a .NET library, written in C#, which provides easy-to-use, object-oriented ways to access data and services related to ManiaPlanet.
- Trackmania Disassembler - includes a library allowing you to write your own applications that can read the format.
- TrackStudio (@archive.org) - a Windows TrackMania Forever track editor with 3D interface.
- Blockmix tools (@archive.org) - Challenge editing tools (Recompressor, ChallengeEdit, GBX-Master, CELightRotate, TmfBlockMixEdition).
- TMPakTool (@archive.org) - an open source tool which can open and edit .pak files in an Explorer-like interface. Comes with a C# library which you can use in your own applications to work with .pak files. Download & Source code
- TMUnlimiter (@archive.org) - Patches the in-game track editor to remove the block placement restrictions. Source code
- GBXedit - Command-line based blockmix editor for TM² Stadium maps.
- MapEdit - GUI-based blockmix editor for all ManiaPlanet maps.
- ReplayToChallenge download - a Windows tool to extract a Challenge from a .Replay.Gbx file (includes source code).
- GbxDecompress download - a Windows tool that decompresses or compresses the file body of a given .Gbx file (includes source code).
- EmbeddedItems download - a Windows tool that indicates all embedded items of a given .Map.Gbx file (includes source code).
- GbxLightMap download - a Windows tool to extract the lightmaps from a given .Map.Gbx file (includes source code).
- GbxMetadata download - a Windows tool that indicates the persistent attributes of a given .Map.Gbx file (includes source code).
- File:Krzychor-campaign-maker.zip - a Windows tool that allows to create custom campaigns for TMNF/TMUF. Sources not included.