5

This is a korean MMORPG released back in 2001. The game and the company no longer exists. I've been analyzing the file formats as a personal project for awhile now.

I have already decoded the textures and script files. I only have 2 more files left to decode: .ani and .obj. With the extensions, I would guess that the .ani contains the animation and .obj contains the 3d models. As the title of my question would suggest. I'm currently working on the .obj file.

What I Know

.obj files are actually archives that contains one or more models. But exclusively 3D models (biped) because the textures are found on another archive with the extension .t16 or .tex.

I'll take the 22nd model inside the file def.obj as an example. I have also taken it out of the archive and saved it as a file called `def_022.obj.

From here on, I'll call the 22nd model Armor of Eagle. The next image is the texture for the Armor of Eagle found on def.t16:

enter image description here

The Armor of Eagle in-game:

enter image description here

A def_inf.txt file has this related line:

filename    LODstep polycnt
armor_eagle 0       313     // eagle = index 22

In general, the structure of .obj is:

06 00 00 00 03 00     // don't know what
3C 00                 // total number of models in this list
01 00 9E 00 00 00 9E 00 00 00 78 00 00 00 78 00 00 00 // the first model on the list with 0x9E polygons and 0x78 verticies
01 00 64 00 00 00 64 00 00 00 61 00 00 00 61 00 00 00 // the 2nd model
....... // continues until you reach the end of the headers
....... // then the bodies start, actual 3d data

This is how the headers looks:

enter image description here

  • Cyan:
    • 39 01 00 00 = 313 polygon count
    • 18 01 00 00 = 280 vertex count
    • AD 00 6B 00 = unknown

About def_022.obj

The face indices start at 0x4C [00 00 01 00...]
and by the looks of it ends at 0x7A2 [...A2 00 A3 00]

I was using the tool called hex2obj 0.24c but didn't have any luck in guessing the starting points of the vertices and uv list.

enter image description here

  • Yellow: seems to me that it is the chunk marker. (because all files start with it)
  • Light Blue: is a uint16 seq no., +1 for every model inside the def.obj. And it also corresponds to the index of its texture (on the texture archive).
  • Dark Green: is the face indices.

The Question

  • How do I go forward?
  • How do I find the address where the UV starts? (books? articles? resources?)
  • Lastly, how do I know where the vertices start?

This is all considering that I just analyze the binary and not debug the client itself to see how the client is taking the data.

UPDATE

I noticed a few new things on the file I'm analyzing def_022.obj. A bytearray gets repeated a lot of times. Although, I'm still not sure of the significance YET. 00 00 80 3F is also the bytes that each model start with, which I thought was the chunk marker:

enter image description here

  • Yellow: is the repeated bytes. You'll also notice that it is usually preceded by the byte 0x3F or 0x3E and followed by 0x0C and 0x0D and then 3 bytes of 0x00. This pattern gets repeated 185 times.
  • Cyan: is the area containing the face indices.
  • Upon further research I found out that 3f80 0000 = 1 or 0000 803f = 1 for little endian. But still don't know the significance, I'm going to dig deeper.

Based on the notes Mr @RadLexus has kindly provided. I was also able to plot the vertices and imported into blender:

enter image description here

Added the normals and UV:

v -13.531700 37.445000 2.338600
vn -0.687800 0.725000 -0.036200
vt 0.583800 0.086300

Then I tried adding the faces:

f 1 2 3
f 1 3 4
f 5 6 7
f 5 7 8
f 9 10 11
...

enter image description here

But still can't figure out how to apply the textures.

UPDATE

So I tried changing my .obj file to include a few more info about the face values like (just duplicated the value as x/x/x):

f 1/1/1 2/2/2 3/3/3
f 1/1/1 3/3/3 4/4/4
f 5/5/5 6/6/6 7/7/7

I finally got this:

enter image description here

Although it's still wrong, I guess I'm a bit closer.

FINAL UPDATE

Have figured it out. Although, I still don't know what the unknown1 and unknown2 are but it looks good enough:

enter image description here


Note: I know almost nothing about 3D file formats. Other than the basic components of a 3D object.

Download Related Files Here

  • 1
    Can you add a link to original files to download and inspect? – usr2564301 Aug 21 '16 at 15:06
  • 1
    @RadLexus thank you for your interest. I've added updated the question to include a link to the related files. I've also included related scripts I wrote for extracting data. – majidarif Aug 21 '16 at 15:14
  • 1
    Just FYI: you are on the right track with those 'repeated sequences'. Look up how a 4-byte float is stored. Recognizing these is a huge and important step in reversing a 3D format. – usr2564301 Aug 21 '16 at 19:54
  • @RadLexus I'm digging more into it. But I'm still not sure what the pattern is about. Although, a quick search showed that 3f80 0000 = 1 or 0000 803f in little endian (which is what the file uses). – majidarif Aug 22 '16 at 11:08
2

Some preliminary notes only – may evolve into a complete answer.


My approach was the following. Clearly, the numbers at the end are floating point numbers. Also, they are not all floating point; the sequence 0C 00 00 00 a few bytes in is not a reasonable floating point number. Counting off to the next "unreasonable" value (which happened to be 0C 00 00 00 again, but there are other values as well) told me the stride of this data was 28 bytes. (Which is 40 in decimal, but for documentation I strongly prefer hex.) If you have a capable hex viewer of which you can adjust the view width, you can see that this is correct if you set its view width to 40 characters.

Counting back from the end in 40-byte steps gave me the most likely starting point of this sequence.

The digits right before it are clearly at least 2-byte (unsigned short), and sort of increase from the start to the end. As this is a 3D model, I had a strong hunch they form triangle data, so at least they should contain a, b, and c vertex indices, and possibly additional triangle attributes. This turned out, by trial and error, to be not the case. (The "trial and error" consisted of dumping them into a display program. As that worked straight away, further numerical investigations were unnecessary.)

Since this information is (1) enough to display an object, and (2) covers all of the data in the file apart from the few bytes at the start – which can be anything – I went on writing a full display program.


The first 004A bytes are unknown. They contain some floating point numbers (00 00 80 3F is 1.0, as a little endian 4 byte floating point number), and they could be anything. (A scale is likely.)

The next 2 bytes form the number 0016, which is in decimals 22. This could be the internal 'object number'.

Then, starting at 004C, 113h (313, in decimal) triangle definitions follow. A single triangle definition consists of 3 unsigned short indices a,b,c, which point to 3 coordinates. The largest index in this list is 0117, a significant number!

Right after this, 0118 3D points follow. Each 3D point has the following structure:

float x
float y
float z
float unknown (usually 1.0?)
unsigned int unknown
float normal_x
float normal_y
float normal_z
float u   \
float v   / correct; see bottom update

Affirming that the 3 normal_* variables indeed form a normal can be done by adding their squares together; they should hover around a value of 1.0. That is correct for the first hundred or so of these coordinates, up to 3 decimals of accuracy:

1.000004
1.000080
0.999935
1.000076
0.999936
(and so on)

Adding up the sizes of all coordinate elements leads to a total size of 28 bytes (40, in decimal), and:

  1. the remaining part of the file is a multiple of this;
  2. dividing the size of the remaining part by 28 results in 118 (280, in decimal).

The significance of the 'highest index' in the triangle list is therefore proven :) They are indeed coordinate indices.

Where do these numbers 139h and 118h come from? They appear in the header part of the entire file!

Here is the proof that the first 3 elements are indeed x, y, and z. I plotted x and y only, with y inverted (negative towards the bottom of the screen). You can clearly recognize the 'body' part of your in-game image.

a wireframe body


Not all coordinates are quite the same. The unknown_1 float value is 1.000 for the first 173 coordinates, then jumps to other values for the remainders. Similarly, the integer unknown_2 hovers between values from 4 to 43 for these, and then jumps to a much higher value. This needs some further investigation.


The u,v values can be mapped onto the source image directly. They are expressed in a floating point range from 0..1 so you need to multiply them by the source image's width and height. Here is an image of that:

texture coordinates

The unused parts of the image are presumably used by other 3D models.

| improve this answer | |
  • I'm starting to understand the format. Although, I'm confused about UV and mapping the texture. And also how to guess what part is the face indices if the "scrambled alphabet" isn't recognizable. – majidarif Aug 23 '16 at 17:00
  • @majidarif: My idea about u,v was correct, see the update. I'm not sure what you mean with "scrambled alphabet" though. Are you referring to the original hex dump? – usr2564301 Aug 23 '16 at 22:43
  • Thanks, I'll try the update about the u,v now. About the scrambled alphabet, I read somewhere that the easiest way to find the face indices is by finding a segment of scrambled alphabets as shown here. But I'm just thinking what if that's not the case? Is there a more concrete definition of the face indices? Like, maybe, values that are between 1-255 only? – majidarif Aug 23 '16 at 22:50
  • When you say u,v, when using .obj is it the vp? I can't make it work on my end. This is how I did it: "vt %f %f" % (u*width, v*height), I'm not sure if this is what you meant by multiplying by the source image dimensions. – majidarif Aug 23 '16 at 23:31
  • @majidarif: multiplying is only necessary when your library does not already do it for you. Reading a few topics on Blender seems to suggest it's not necessary, but of course you could ask on there and refer to this question. – usr2564301 Aug 23 '16 at 23:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.