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

Kuraudo

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

Read more here.

Cheers Qhimm community.
« Last Edit: 2025-12-20 06:47:16 by Kuraudo »

Kuraudo

  • Nitpicker
  • *
  • Posts: 396
  • [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. »

makipl

  • 0xBAADF00D
  • *
  • Posts: 624
  • 0xCCCCCCCC
    • 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 http://wiki.xentax.com/index.php/Playstation_TMD


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", fd.read(4))[0]

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

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

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


     

fd = open(filename,"rb")
if(fd==None):
    print("cannot open file. Exiting")
    exit()
if(ReadInt() != 0x41):
    print("tmd version is not 0x41")
    exit()
fd.seek(4,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))
    fda.close()

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 »

Kuraudo

  • Nitpicker
  • *
  • Posts: 396
  • [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?

Kuraudo

  • Nitpicker
  • *
  • Posts: 396
  • [Maktub]
    • View Profile
    • Behemoth Productions
Re: [FFVII] Snowboard Progress
« Reply #4 on: 2020-07-03 06:16:55 »
FF7 Modding Scene | Snowboard

Just a quick update to keep everyone on track with the progress so far and what still needs to be done.
That's a pre-release tool by Maki: FF7 Snowboard Tool and Commit


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"


27/01/23 - 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 https://gamedev.stackexchange.com/questions/72754/cull-mode-back-faces "[...]
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."


14/02/23 - Maki's patch
Snowboard limit of 84 verticies is removed by Maki. Getting finally the first steps into modding Snowboard right.


20/02/23 - Updates
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."


14/03/23 - Pierre.jsn's vanilla Sephiroth
Pierre.jsn: "version of Sephiroth for snowboard, its not very beautiful but for fun"


15/04/23 - Skeleton
Kaldarasha: "@Pierre.jsn Might help you. I rebuild or I guessed the skeleton based on the size of the parts. The ready for export looks strange, but that's the orientation of the snowboard files."


17/04/23 - Snowboard ChaOS model in-game
Lazaro: "A pair of videos about this with ChaOS Cloud model (done by @Kaldarasha, @Pierre.jsn has sent to me it)"


08/05/23 - TrueOdin's patch available starting v1.15.0.77+ FFNx canary
TrueOdin: "In the screenshot above the model is using floating point vertex data, which means HIGH PRECISION models. You are no more limited to the core game integer rounding. Also the good news here is that not only we can swap the model, but technically we can redraw the entire minigame (also using high precision data)
Every 3D object you see is contained in two different files: for_ev.tmd ( used for the snowboard event after the village ) and for_gs.tmd ( used for the snowboard GS minigame )
they ARE interchangeable up to a certain degree, I think only some objects are missing from one file or the other
Finally for modders, you have to wait for the next ⁠kimera release as there's something to be changed there as well. Stay tuned for updates"


12/05/23 - Issue
Kaldarasha: "@TrueOdin we have a little problem, the new tmd mode seems to crash the game after the snowboard event. I dunno if it's also the case for the GS minigame, but the event does crash at the end."


15/05/23 - Vanilla OK
TrueOdin: "So I actually discovered something funny through testing
If I extract the vanilla models and I convert them to float using the latest Kimera the game does not crash afterwards
for neither the main event or the gold saucer one
so I wonder if your model files are corrupted somehow @Kaldarasha
I'd suggest maybe starting with the latest Kimera version ( 1.8z ) and bring your new model on top of the vanilla for_gs.tmd file
So, TL-DR;
1) Open the vanilla for_gs.tmd
2) Click on Convert to Float
3) Bring your model changes
4) Save as TMD
If this still happens then it could be because of the file size, and for this I fear I have to patch more around the game
but first please consider trying this"
Lazaro: "I tried this with latest KimeraCS, 1.8z. But I updated only one object, the chest.
This is the file with only the chest part updated. You can check this with KimeraCS. But then the issue happens.
FOR_GS.TMD/705.89 KB
If what you say is true (that it works without updating the polys/vertices -so, theorically it maintains the size of the arrays-, maybe the issue comes that way somehow.
TrueOdin: "What do you mean by the size of the arrays?"
Lazaro: "Until now (vanilla) we have an array of a struct of 8bytes (3 x ushort for x/y/z and 1x  ushort for pad). This is 4 x2 = 8 bytes per record of the array. Now we have a struct of 16bytes ( 4 x float). This is 4 * 4 = 16 bytes per record of the array. In some moment, ff7 engine allocates that memory, and I saw a sub (when exiting snowboard) that frees all that memory (I think). What I've been thinking is that ff7 memory allocation/freeing could be inconsistent. But, of course, I'm not sure. Apart, the crash to desktop happens in ffnx as IDA reported, not in ff7 engine. But I don't know either what it means.
Maybe ff7 engine is trying to free an array of ushorts and not an array of floats.
I will test in any case your discovery, to be sure we can confirm that.
But tomorrow."




So that's where we stand at with snowboard modding scene so far. Discord Qhimm Forums, for who wants to collaborate.
« Last Edit: 2025-12-20 06:50:18 by Kuraudo »

makipl

  • 0xBAADF00D
  • *
  • Posts: 624
  • 0xCCCCCCCC
    • 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.
Opcode:
Code: [Select]
0x007321D7
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