Author Topic: [FFVII] Snowboard Progress  (Read 8123 times)


  • *
  • Posts: 362
  • Maktub
    • View Profile
    • Behemoth Productions
[FFVII] Snowboard Progress
« on: 2019-06-17 17:31:07 »
Snowboard Minigame modding scene progress by Maki.

Cheers Qhimm community.

Please refer to GitHub Thread if you want to cooperate.
« Last Edit: 2022-08-18 19:53:27 by Kuraudo. »


  • *
  • Posts: 362
  • Maktub
    • View Profile
    • Behemoth Productions
Re: [FFVII] Snowboard?
« Reply #1 on: 2019-06-18 06:35:13 »
Spoiler: show
Doing a little research:

Kaldarasha was in touch with MaKiPL, some updates here.

Any progress?

« Last Edit: 2019-07-01 20:20:17 by Kuraudo. »


  • 0xBAADF00D
  • *
  • Posts: 623
    • View Profile
Re: [FFVII] Snowboard?
« Reply #2 on: 2019-07-01 16:35:29 »

Code: [Select]
# Final Fantasy VII Snowboard TMD (Playstation TMD) to OBJ by Maki ([email protected])
# Made @ 01.07.2019
# version 0.1

#Based on structure by

import sys
import struct

filename = "for_ev.tmd" #change that for other
outfilename = "for_ev.obj" #change that also

def ReadInt():
    return struct.unpack("<I",[0]

def GetVertices(pVert, nVert):
    vertices = [], 0)
    for n in range(0, nVert):
        x = struct.unpack('<h',[0]
        y = struct.unpack('<h',[0]
        z = struct.unpack('<h',[0]
        w = struct.unpack('<h',[0]
        vertices += [[x,y,z]]
    return vertices

def GetPolygons(pPrim, nPrim):
    polygons = [], 0)

    for n in range(0, nPrim):
        modeVariable = struct.unpack('<I',[0]
        if(modeVariable != 0x31010506):
            print('unknown mode at: ' + str(fd.tell()))
            return [],1) # unknown, UV?
        A = struct.unpack('<H',[0]
        B = struct.unpack('<H',[0]
        C = struct.unpack('<H',[0]
        polygons += [[A,B,C]],1)
    return polygons


fd = open(filename,"rb")
    print("cannot open file. Exiting")
if(ReadInt() != 0x41):
    print("tmd version is not 0x41")
    exit(),1) # we don't really care for flags here. It's always 0
objCount = ReadInt()

#Main arrays
pVerts = []
nVerts = []
pNorms = []
nNorms = []
pPrims = []
nPrims = []

#Step 1- Gather info about every single object to arrays
for i in range(0,objCount):
    pVerts += [ReadInt()]
    nVerts += [ReadInt()]
    pNorms += [ReadInt()]
    nNorms += [ReadInt()]
    pPrims += [ReadInt()]
    nPrims += [ReadInt()]

    print("===Object: {}===\nVertices: {}\tPointer: {}\nNormals: {}\tPointer: {}\nPrims: {}\tPointer: {}\nScale: {}".format(i, nVerts[i], pVerts[i], nNorms[i], pNorms[i], nPrims[i], pPrims[i], ReadInt()))
#Step 2- Process gathered data
for i in range(0,objCount):
    vertices = GetVertices(pVerts[i]+12, nVerts[i])
    fda = open('obj{}.obj'.format(i), 'wt')
    for n in vertices:
        fda.write("v {} {} {}\n".format(n[0], -n[1], n[2]))
    polygons = GetPolygons(pPrims[i]+12, nPrims[i])
    for n in polygons:
        fda.write("f {} {} {}\n".format(n[0]+1,n[1]+1,n[2]+1))

This for 0.1 version. Looks like for_gs.tmd and for_en.tmd have the same data (huh??)
I'll be digging for UVs and thankfully for reimport. Should be easy

Vertices (as in TMD):
Code: [Select]
short X;
short -Y;
short Z;
short W; //unused

Polygon type A:
Code: [Select]
0x06050131 //polygon type const. Probably that means triangle and the other unknown one is quad, but that's just assumption
char unk[12] //unkown, I'm missing three UVs, but what are other values?
ushort A;
ushort B;
ushort C;
ushort pad; //maybe a place for D?

Polygon type B:
Code: [Select]
0x80808000 //const?
ushort A; //untested
ushort B; //untested
ushort C; //untested
ushort PAD;
char unk[16]; //not sure
« Last Edit: 2019-07-01 16:44:46 by Maki »


  • *
  • Posts: 362
  • Maktub
    • View Profile
    • Behemoth Productions
Re: [FFVII] Snowboard Progress
« Reply #3 on: 2019-07-01 20:21:11 »
Just Brilliant. Are there Cid and Tifa's model too?


  • *
  • Posts: 362
  • Maktub
    • View Profile
    • Behemoth Productions
Re: [FFVII] Snowboard Progress
« Reply #4 on: 2020-07-03 06:16:55 »
So, just to update you all and for the sake of keeping track of any progress done and what has to be done:

That's a pre-release tool by Maki:

However, as per Kaldarasha we are missing the skeleton info.

Kaldarasha: "I need also the skeleton info, and a way to modify it. Best would be if the pieces are seen at the skeleton as on kimera. Even if there would be a limit for the tris I could make a stretched recolor of the default model. The final touch would be to edit the animations to adjust the models hight."
Maki: "There's no skeleton info in TMD file, every chunk/object like Clouds right arm is just a model without anything else, there's no animation data inside the .TMD files"
TrueOdin was suggesting: "Something I would do personally for this topic is to start the game in RenderDoc, get to the minigame and catch several frames.
Afterwards analyze the various draw call for each frame and try to find out how the engine is passing the information.
Once it's clear what the engine it's passing, the first step it's to find those models that are being passed and try to reassemble them"

EDIT 27/01/23 - Last updates, long story short from the discord:

Lazaro: "I tried RenderDoc with the Nino's imported model. It seems that are triangles that are drawn (they are in RenderDoc mesh feature), you can even saw and select them, but in the game they are not rendererd. So, the only thing I can think is happening is in the winding order."[...]
Maki: "Ok, then it's indeed backface culling happening on inverted normals - so winding order"[...]
Lazaro: "It must be, because AFAIK the TMD models has 0 normals. With the P Model I have the normals (I have to compute them for rendering), but when I convert to TMD, I don't have them. I will have to check the winding order in the part where I assign the vertices of both structs."[...]
TrueOdin: "and I think this is your issue "[...]
Lazaro: "Well, this is what I tried to say all this time. We have an issue of winding order for the vertices of the triangles."

EDIT 14/02/23 - Last updates, long story short from the discord:

Snowboard limit of 84 verticies is removed by Maki. Getting finally the first steps into modding Snowboard right.

EDIT 20/02/23 - Last updates, long story short from the discord:

Lazaro: "A bit more of testing. I added more parts of the model. But it is a bit difficult to make adjust it to the skeleton (we don't know yet where it is). At least the len of each part. And we don't know either which animation is using. Maybe the angles are embedded also. Apart of all this, the parts don't correspond with the field model for example. I had to join two of the parts of the arms. And the legs are another history. There is only 1 part of the leg for both legs. So, there is only 1 upper leg, 1 lower leg and 1 foot as objects, but then it uses the same model for each of the legs. Oh, and lastly, the accuracy/definition of the model. I'm not sure if we will be able to improve the detail, because the TMD uses shorts (when normally we would be using floats for the vertices x/y/z)."
Kaldarasha: "I have rebuild the skeleton based on the body parts. So we have a good idea how it does look. The problem is that we can't use float numbers, therefore we actually need big integer values. But this means we need a way to downscale these in the game engine, which I guess doesn't care if int or float is used."

So that's where we stand at with snowboard modding scene so far. GitHub, for who wants to collaborate.
« Last Edit: 2023-02-21 08:40:04 by Kuraudo. »


  • 0xBAADF00D
  • *
  • Posts: 623
    • View Profile
Re: [FFVII] Snowboard Progress
« Reply #5 on: 2023-03-14 10:58:41 »
long story short-> the vertices count was limited by the engine with fixed amount of vertices to parse. Thankfully the code in assembler is easy enough for quick NOP'ing the limit.
Code: [Select]
Code: [Select]
cmp dword ptr [ebp-0C],0x54Change it to NOPs

Quote from: L@Zar0
6 90s
Therefore 6 times 0x90 (NOP) works on opcode 0x007321D7