As a personal project I've been trying to reverse engineer the art assets for the old Dynamix game Earthsiege 2 (this game has long been abandonware and was recently released for free by Hi-Rez, the current copyright holder). It was child's play to decode the images/textures, but I've been having trouble with the binary 3D model format.
As some background, the 3D models are saved as DTS files. DTS is a proprietary binary format (little-Endian), short for "Dynamix Three-Space". I wasn't able to find any resources on reversing ES2-era DTS files.
For this post I'll focus on the Apocalypse.
The Apocalypse model is stored in Apoca.dts
. The file starts out like this (in hex, with annotations):
| File size | ? |ChunkMarker|Chunk Length
02|7C 7F 01 00|4B 1F 3D 7F|03 00 1E 00|FC 5F 00
00|FF FF 00 00 0E 08 BF FF CC FF 23 04 01 00 15
00 14 00 70 46 00 00 FF FF 00 00 B7 06 BF FF CC
FF 23 04 1B 00 14 00 14 00 4A 01 00 00 01 00 0C
00 AB 01 FB FF 22 00 7B 05 18 00 0F 00 10 00 06 <-Faces
00 02 00 03 00 04 00 05 00 02 00 05 00 07 00 08
00 0A 00 0B 00 04 00 03 00 03 00 02 00 08 00 0A
00 0B 00 07 00 05 00 04 00 0A 00 08 00 07 00 0B
00 00 00 1D 00 FF 07 00 00 00 00 00 00 24 FF 72 <-Some vertices here
01 82 05 DC 00 72 01 82 05 DC 00 C0 FE 8C 05 24 <-
FF C0 FE 8C 05 00 F8 00 00 00 00 24 FF E8 FE 00 <-
05 24 FF 04 01 60 04 00 08 00 00 00 00 DC 00 04 <-
01 60 04 DC 00 E8 FE 00 05 00 00 7A 07 2A FD 00 <-
00 4F F8 CE FD 00 00 BB FD 55 F8 02 00 00 04 02
00 00 04 00 00 00 14 00 00 00 14 00 00 00 00 FF
FF FF FF FF FF FF FF FF FF FF FF 1B 00 00 00 FF
FF FF FF FF FF FF FF FF FF FF FF 19 00 00 00 FF
FF FF FF FF FF FF FF FF FF FF FF 03 00 14 00 0A
00 00 00 00 00 02 00 04 00 00 00 00 00 03 00 14
For formatted/color-coded analysis, see http://postimg.org/image/8e56re90n/
To see the first 3 chunks of the file in their entirety, see http://pastebin.com/RTFkdiBd
Current knowledge
Each DTS file is broken into chunks. The 10th - 13th bytes are a start-of-chunk marker; I think this is 03 00 1E 00
in each file. The next four bytes are the size of the chunk, always followed by FF 00. A new chunk will begin immediately after the previous ends. I don't know how the chunks divvy up data right now, but it does appear that multiple chunks contain vertices. This may be related to the fact that the model is noticeably divided into discrete parts, rather than a single mesh.
Each vertex is a set of 6 bytes, consisting of 3 signed shorts for the X, Y, and Z coordinates of that vertex. The first vertex in this file is 24 FF 62 01 82 05, which has coordinates -220, 354, 1401 when converted to decimal. The sample I've provided contains the following vertices:
24 FF 72 01 82 05
DC 00 72 01 82 05
DC 00 C0 FE 8C 05
24 FF C0 FE 8C 05
24 FF E8 FE 00 05
24 FF 04 01 60 04
DC 00 04 01 60 04
DC 00 E8 FE 00 05
These vertices define the crotch. Interestingly, the crotch is actually located above the head in 3D space as defined in the file, so it must get translated somewhere. I have tested and verified that the above bytes contain the crotch by editing them in RAM while the game is running, which distorts the model immediately when I click back into the game window.
Notice that between some of these vertices are two sets of six bytes that do not appear to be vertices (they don't correspond to any point on the model and have no effect when altered in RAM). I don't know what the deal with these is:
00 F8 00 00 00 00
00 08 00 00 00 00
The rest of the model is defined in pieces throughout the file. Vertices are clustered into small groups, which I think define one shape at a time. I can find vertices for everything but the weapons and legs. The legs are animated, so they might be defined differently, or located in a different file. The weapons are defined in a separate file.
Preceding the vertices are some shorts with small values, e.g. 06 00 02 00. These have something to do with the faces; my guess is that they refer to vertices by index to define a face. I have verified that these affect the faces by editing them in RAM while the game is running, but haven't fully decoded them yet.
There is always 6 bytes of 0s (00 00 00 00 00 00
) between the faces and the vertices. There is always the marker 04 00 00 00 14 00 00 00 14 00 00 00
shortly after the vertices end. Using this knowledge, I'm able to parse vertices from a file by looking between those two markers; however, this is imprecise and I end up with a bunch of junk vertices forming a partial spherical shell around the model.
Here is a rendering of a point cloud of vertices I am able to read out of the Apocalypse DTS file; I have filtered out some of the junk vertices here, but there are still some present around the edges and in the middle. Take note that the hips and crotch are located above the torso in the file.
What's next?
I'm not hoping to decode the entire DTS file from start to finish - it's much too long and complex - but I'd like to at least be able to read the vertices, and hopefully faces, out of the files.
The biggest struggle I've been having at the moment is trying to figure out how to know exactly where a set of faces/vertices start and end. My main question would be how to precisely determine where a group of faces/vertices start and end, as they are not in the exact same place in every file. Any other information you can spot that I've missed would be awesome, but that's my main objective.