Trackmania .gbx

Skeletons, animations, shaders, texturing, converting, fixing and anything else related to read game models
AMG
Posts: 71
Joined: Sun Aug 10, 2014 1:13 pm

Trackmania .gbx

Post by AMG »

Textures are in .dds so can be opened easily
3D models are in .gbx files instead, can something be done for them? :)
Last edited by AMG on Sat Jan 30, 2016 3:06 pm, edited 1 time in total.
barti
Posts: 34
Joined: Sun Nov 09, 2014 2:40 pm

Re: Trackmania .gbx

Post by barti »

I tried to make a MAXScript for this format a while ago. There's two most common model types for cars (you can see which one it is by opening the model in a hex editor and looking at the first few bytes - BUCR (Binary - Compressed) and TUUE (Text - Uncompressed). BUCR models use LZO compression. My script works on a few TUUE files, and since it was complete enough to extract the model I wanted I didn't bother with it anymore. Maybe it'll be useful for some models:

Code: Select all

fGBX = getOpenFileName caption:"Choose Model File:" \
types:"TrackMania Gamebox Container (*.gbx)|*.gbx|"

fn readByteAsChar binstr = -- not necessary, but looks nicer in my opinion
(
   return bit.intAsChar(readbyte binstr)
)

fn readTextLine binstr = -- reads a string until a newline character is found
(
   str = ""
   while c != 13 do
   (
      c = readbyte binstr #unsigned
      if c != 13 do str += bit.intAsChar(c)
   )
   readbyte binstr
   return str
)

fn readTextBool binstr = -- as above but returns boolean
(
   return readTextLine binstr as booleanClass
)

fn readTextInt binstr = -- as above but returns integer
(
   return readTextLine binstr as integer
)

fn readTextFloat binstr = -- as above but returns float
(
   return readTextLine binstr as float
)

fn readStr binstr strLen = --reads a fixed-size string
(
   str = ""
   for w=1 to strLen do(str += (bit.intasChar(readByte binstr #unsigned)))
   return str
)

if fGBX != undefined then (

   f = fopen fGBX "rb"
   clearlistener()
   
   fArr=#()
   vArr=#()
   nArr=#()
   tArr=#()
   
   propertyCount = 7
   firstMesh = true
   
   modelPath = getFilenamePath fGBX
   diffusePath = modelPath+"diffuse.dds"
   detailsPath = modelPath+"details.dds"

   diffMaterial = standard showInViewport:true Name:"diffuse"
   detMaterial  = standard showInViewport:true Name:"details"

   diffMaterial.diffuseMap = Bitmaptexture fileName:diffusePath name:"diffuse"   
   detMaterial.diffuseMap  = Bitmaptexture fileName:detailsPath name:"details"

   header = readStr f 3
   if header != "GBX" do return messageBox "This is not a TrackMania GBX Container."

   version = readshort f
   if version != 6 do messageBox "GBX file isn't version 6. There might be problems."

   fileFormat = readByteAsChar f -- Binary or Text
   refTable   = readByteAsChar f -- Compressed or Uncompressed
   fileBody   = readByteAsChar f -- Compressed or Uncompressed
   unknown    = readByteAsChar f -- R or E (unknown purpose)
   classID    = readTextLine f -- in car models this should be 09005000
   userData   = readTextInt f -- length of user data
   numRefs    = readTextInt f -- number of chunks ? (unverified)
   numExtRefs = readTextInt f -- number of external chunks ? (usually 0 - not used)
   numMeshes  = 0
      
   for i=1 to 4 do -- totally the wrong way to do it
   (
      chunkID = readTextLine f
      case chunkID of
      (
         default:
         (
            return messageBox "Unknown ID " + mainID + "."
         )
         "9005000":
         (
            readTextInt f
         )
         "9005007":
         (
            readTextBool f
         )
         "900500d":
         (
            readTextBool f
            readTextBool f
            readTextInt f
         )
         "904f000":
         (
            -- do nothing
         )
         "904f006":
         (
            readTextInt f
            numMeshes = readTextInt f
            readTextInt f
         )
         "904f006":
         (
            readTextInt f
            numMeshes = readTextInt f
            readTextInt f
         )
         "904f00d":
         (
            if firstMesh do
            (
               readTextInt f
               firstMesh = false
            )
         )
         "40000000":
         (
            objectName = readTextLine f
            
         )
         
      )
   )
   
   -- estimated beginning of submeshes
   for i = 1 to numMeshes do
   (
      for x=1 to propertyCount do readTextLine f
         
      objectName = readTextLine f
      
      for x=1 to 23 do readTextLine f
      
      count = readTextInt f
      
      for x=1 to 2 do readTextLine f
      
      free tArr
      for x = 1 to count do
      (
         tu=readfloat f
         tv=readfloat f
         append tArr[tu,tv,0]
      )
      
      for x=1 to 10 do readTextLine f
      
      free vArr
      free nArr
      for x = 1 to count do
      (
         vx=readfloat f
         vy=readfloat f
         vz=readfloat f
         nx=readfloat f
         ny=readfloat f
         nz=readfloat f
         unk1=readfloat f
         unk2=readfloat f
         unk3=readfloat f
         unk4=readfloat f
         append vArr[vx,-vz,vy]
         append nArr[nx,-nz,ny]
      )
      

      for x=1 to 5 do readTextLine f
      
      unk=readlong f
      count=readTextInt f
      
      free fArr
      for x = 1 to count/3 do
      (
         fa=1+readshort f #unsigned
         fb=1+readshort f #unsigned
         fc=1+readshort f #unsigned
         append fArr[fa,fb,fc]
      )
      
      for x=1 to 10 do readTextLine f
      fseek f 8 #seek_cur
      for x=1 to 4 do readTextLine f
      fseek f 88 #seek_cur
      for x=1 to 4 do readTextLine f
      readlong f
      for x=1 to 6 do readTextLine f
      readlong f
      tmatrix = matrix3 1
      
      tmatrix.row1 = [readTextFloat f, readTextFloat f, readTextFloat f]
      tmatrix.row2 = [readTextFloat f, readTextFloat f, readTextFloat f]
      tmatrix.row3 = [readTextFloat f, readTextFloat f, readTextFloat f]
      tmatrix.row4 = [readTextFloat f, readTextFloat f, readTextFloat f]
      for x=1 to 2 do readTextLine f
         

      max modify mode
      cui.expertModeOn()
      
      with redraw off
      (
         amesh = mesh vertices:vArr faces:fArr name:objectName
         
         meshop.setMapSupport amesh 1 true
         setMesh amesh tverts:tarr
         
         for face = 1 to amesh.numfaces do setFaceSmoothGroup amesh face 1
         select amesh
         addmodifier amesh (Edit_Normals ()) ui:off
         amesh.Edit_Normals.MakeExplicit selection:#{1..nArr.count}
         EN_convertVS = amesh.Edit_Normals.ConvertVertexSelection
         EN_setNormal = amesh.Edit_Normals.SetNormal
         normID = #{}
         --apply normals
         for v = 1 to nArr.count do
         (
            free normID
            EN_convertVS #{v} &normID
            for id in normID do EN_setNormal id nArr[v]
         )
         collapseStack amesh
         max select none
         
         amesh.transform = tMatrix
         --rotate amesh (eulerangles 90 0 0)
         
         matType = objectName[1]
         
         case matType of
         (
            default:
            (
            --return messageBox "Uknown material type."
            )
            "S":
            (
               amesh.material = diffMaterial
            )
            "s":
            (
               amesh.material = diffMaterial
            )
            "d":
            (
               amesh.material = detMaterial
            )
            "D":
            (
               amesh.material = detMaterial
            )
            "g":
            (
               amesh.material = detMaterial
            )
         )
      )
      propertyCount = 6
   )
   gc()
   fclose f
   cui.expertModeOff()
)
else
(
   clearlistener()
)
Argonaut
Posts: 46
Joined: Sat Sep 27, 2014 10:24 pm

Re: Trackmania .gbx

Post by Argonaut »

barti wrote:I tried to make a MAXScript for this format a while ago. There's two most common model types for cars (you can see which one it is by opening the model in a hex editor and looking at the first few bytes - BUCR (Binary - Compressed) and TUUE (Text - Uncompressed). BUCR models use LZO compression. My script works on a few TUUE files, and since it was complete enough to extract the model I wanted I didn't bother with it anymore. Maybe it'll be useful for some models:

Code: Select all

 -code snipped for short reply-


So say if I have a BUCR model downloaded, which LZO decompression tool/scanner would I use? (probably simple question plus importing a BUCR model in the MAXscript throws errors). After investigation of Trackmania mods, this has me interested aswell..
barti
Posts: 34
Joined: Sun Nov 09, 2014 2:40 pm

Re: Trackmania .gbx

Post by barti »

There's no LZO scanner that I'm aware of. One way of going about it would be writing a QuickBMS script. But a better solution would be to decompress the LZO file within MAXScript - there's some info on the internet about that, but I haven't looked into it.

Also even if you extract a Binary - Compressed file, it's still not a Text file. My script only supports Text files so far, and even then only some of them will actually import - this script still needs a lot of work.

Also which version of Trackmania are you trying to import?
AMG
Posts: 71
Joined: Sun Aug 10, 2014 1:13 pm

Re: Trackmania .gbx

Post by AMG »

If I'm not wrong, the latest Trackmania games use the same format for these models, because in the readme I can't see any specification for this mod, if it is related to Nations, Sunrise, etc.
Of course, please correct me if my guess is wrong!
barti
Posts: 34
Joined: Sun Nov 09, 2014 2:40 pm

Re: Trackmania .gbx

Post by barti »

Automotive Gaming wrote:If I'm not wrong, the latest Trackmania games use the same format for these models, because in the readme I can't see any specification for this mod, if it is related to Nations, Sunrise, etc.
Of course, please correct me if my guess is wrong!


You're right, all TrackMania games (Nations, Sunrise, etc.) use similar model formats (with a few different versions). But I was wondering if TrackMania 2 uses this format as well.
From what I saw, cars for TrackMania 2 are packed in "NadeoPak" format, uses some kind of encryption.