Sign in to follow this  
timothyjlaird

trouble parsing quake3 md3

Recommended Posts

My goal is to figure out how to parse MD3 quake model files so that I can write an import/export tool for DeleD. So I found a model here...

 

http://ioquake3.org/files/models2/q3mdl-imp.zip

 

And the format definition here...

 

http://en.wikipedia.org/wiki/MD3_%28file_format%29

 

I decompressed everything and tried to parse part of the header of the 'head.MD3' file with this python 2 code...

import binascii
import struct

filename = 'head.MD3'
bytes = ''
with open(filename,'rb') as fileptr:
    bytes = fileptr.read()
bytes = binascii.hexlify(bytes)
print bytes
print 'IDENT', bytes[0:8].decode("hex"), 'VERSION', bytes[9:17]

Both IDENT and VERSION are supposed to be 32 bits (so says wikipedia). The IDENT comes in good but VERSION is junk...

 

IDENT IDP3 VERSION f0000000

 

f0 is 15 which is consistent with the header info but what about the trailing zeroes?

 

The rest of the hex dump is attached...it does not seem to be consistent with the format defenition. I don't know if I'm not parsing this correctly, if the wikipedia info is wrong or the model is junk. Full output (hex dump of the file) is attached in a txt. Can anyone offer a starting point? Any thoughts?

Share this post


Link to post
Share on other sites

You're actually looking at it the wrong way. Both IDENT and VERSION are displaying wrong. This is an endian-ness issue. According to the md3 spec, IDENT is assigned the unsigned little-endian number 860898377 (0x33504449). If you were on a little-endian computer it would have displayed 3PDI when treated as a 32-bit number, because that's actually what the MD3 specifies it as on a little-endian machine. It would only display as IDP3 on a little endian machine if they read it one byte at a time and you converted it one byte at a time.

 

Since you're on a big-endian computer you need to convert all little-endian numbers to big-endian first. When you convert 0x49445033 to little-endian it will display (correctly) as 3PDI. Or you could treat the IDENT as 4 'octets' and read it 1 byte at a time.

Share this post


Link to post
Share on other sites


It would only display as IDP3 on a little endian machine if they read it one byte at a time and you converted it one byte at a time.

 

That seems to be what he's doing, and its unlikely he's on a big-endian machine. If OP wants to continue reading in single bytes, they'll have to be re-ordered; the other solution is to not read in single bytes, and instead read the format using the precise primitive types.

Share this post


Link to post
Share on other sites

Thanks for the suggestions. Figured it out by programmatically walking through the bytes looking for sane values for NUM_FRAMES, NUM_TAGS and NUM_SURFACES. Crude but effective. Code sample in case anyone else needs help figuring this out later on...

import binascii
import struct

filename = 'head.MD3'
bytes = ''
with open(filename,'rb') as fileptr:
    bytes = fileptr.read()
bytes = binascii.hexlify(bytes)

def test2(bytes, start):
    for x in range(0,512):
        hexstart1 = start+x
        hexstart2 = start+x+8
        hexstart3 = start+x+8+8
        hexstart4 = start+x+8+8+8
        hexstart5 = start+x+8+8+8+8
        hexstart6 = start+x+8+8+8+8+8
        hexstart7 = start+x+8+8+8+8+8+8
        hexstart8 = start+x+8+8+8+8+8+8+8
        NUM_FRAMES = struct.unpack('<I', bytes[hexstart1:hexstart1+8].decode("hex"))[0]
        NUM_TAGS = struct.unpack('<I', bytes[hexstart2:hexstart2+8].decode("hex"))[0]
        NUM_SURFACES = struct.unpack('<I', bytes[hexstart3:hexstart3+8].decode("hex"))[0]
        NUM_SKINS = struct.unpack('<I', bytes[hexstart4:hexstart4+8].decode("hex"))[0]
        OFS_FRAMES = struct.unpack('<I', bytes[hexstart5:hexstart5+8].decode("hex"))[0]
        OFS_TAGS = struct.unpack('<I', bytes[hexstart6:hexstart6+8].decode("hex"))[0]
        OFS_SURFACES = struct.unpack('<I', bytes[hexstart7:hexstart7+8].decode("hex"))[0]
        OFS_EOF = struct.unpack('<I', bytes[hexstart8:hexstart8+8].decode("hex"))[0]
        if NUM_FRAMES < 1024 and NUM_FRAMES > 0 and NUM_TAGS < 16 and NUM_TAGS > 0 and NUM_SURFACES < 32 and NUM_SURFACES > 0:
            print hexstart1
            print NUM_FRAMES, NUM_TAGS, NUM_SURFACES, NUM_SKINS, OFS_FRAMES, OFS_TAGS, OFS_SURFACES, OFS_EOF
            print '~~~~~~~~'

test2(bytes,0)
print len(bytes)/2

Output...

 

152
1 1 1 0 108 164 276 3284
~~~~~~~~
3284

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this