1

I'm trying to understand how this game builds the map and terrain. Specially if it uses a heightmap. So I can write an app that can view the map and move around (camera) freely.

First, I'm not entirely sure if the game actually uses height maps. But it was made in 2000/2001, so it makes sense if it did.

The following files are loaded each time a map is loaded. I'll use map 4 as an example from here on:

/map/map4.spt
/map/map4.inf
/map/map4.obj
/terrain/chunk4.dat
/terrain/addterrain4.ctr
/terrain/addterrain4.obj
/terrain/addterrain4.inf
/terrain/addterrain4.t16
/terrain/terrain4.obj
/terrain/terrain4.t16

*.T16

I know these are textures.

Here is terrain4.t16 unpacked (terrain):
enter image description here

And here is addterrain4.t16 unpacked (building):
enter image description here

*.SPT

I know this is an encrypted script. It contains things like ambient light color, sky color and other configurations for the map.

*.CTR

I'm not sure about this yet but I don't think this is the heightmap.

*.INF

I'm not sure about this yet but I don't think this is the heightmap. But it looks like positions for buildings/objects on the map.

*.OBJ

Although this sounds like a 3D object file, it isn't. So far I was able to parse this file with this script:

with open("map4.obj", "rb") as bin:
    bin.seek(0, 2)
    file_size = bin.tell()

    bin.seek(0)

    unk1 = struct.unpack("<h", bin.read(2))[0]

    width  = struct.unpack("<h", bin.read(2))[0]
    height = struct.unpack("<h", bin.read(2))[0]

    c_width = struct.unpack("<h", bin.read(2))[0]
    c_heigth = struct.unpack("<h", bin.read(2))[0]

    size1 = struct.unpack("<I", bin.read(4))[0]
    size2 = struct.unpack("<I", bin.read(4))[0]

    bin.seek(50)

    # (1, 76, 76, 304, 304, 5776, 92416)
    print(unk1, width, height, c_width, c_heigth, size1, size2)

    for x in range(size1):
        unk2 = struct.unpack("<BBBB", bin.read(4))

    for y in range(size2):
        unk3 = struct.unpack("<BB", bin.read(2))

The script reads the file till the end so I think I got the parsing right. But I still don't understand what those bytes (unk2, unk3) mean.

Here are the first few bytes: (1, 76, 76, 304, 304, 5776, 92416).

76x76 seems to me like an image widthxheight. This is the part where I start to suspect that this must be the heightmaps.

304x304 is the size of the map coordinates, if I go to the bottom right part of the map, the coords show me x304 y304. (0,0) (304,304)

5776 is 76*76.

92416 is 304*304.

I'm guessing the cellspacing is 4, because 304/76 = 4.

Hmmm... Maybe I should be reading terrain4.obj instead of map4.obj ???

Checking terrain4.obj (filesize: 2,194,888)

with open("terrain4.obj", "rb") as bin:
    bin.seek(0, 2)
    file_size = bin.tell()

    bin.seek(0)

    width  = struct.unpack("<I", bin.read(4))[0]
    height = struct.unpack("<I", bin.read(4))[0]

    print(width, height)

    for x in range(width*height):
        unk1 = struct.unpack("<fffHH", bin.read(16))
        print(unk1)

        for y in range(10):
            unk2 = struct.unpack("<ffffffBBBBff", bin.read(36))
            print(unk2)

        unk3 = struct.unpack("<HH", bin.read(4))
        print(unk3)

    print((bin.tell()-file_size) == 0)

results of prints (last few lines):

(64656, 900)
(9536.0, 0.0, -9664.0, 8, 60)
(9536.0, 367.3583679199219, -9664.0, 0.6106882691383362, 0.7202576398849487, -0.3290725350379944, 125, 125, 125, 255, 0.5, 0.5)
(9472.0, 439.9534912109375, -9664.0, 0.6727219820022583, 0.5930730104446411, -0.4423908591270447, 72, 72, 72, 255, 0.0, 0.5)
(9472.0, 529.98291015625, -9600.0, 0.5103294849395752, 0.5574429631233215, -0.6548444032669067, 91, 91, 91, 255, 0.0, 0.0)
(9536.0, 428.7572021484375, -9600.0, 0.5774530172348022, 0.5274922251701355, -0.6231372356414795, 83, 83, 83, 255, 0.5, 0.0)
(9600.0, 382.6859436035156, -9600.0, 0.32311421632766724, 0.6967470049858093, -0.6404222846031189, 110, 110, 110, 255, 1.0, 0.0)
(9600.0, 331.24566650390625, -9664.0, 0.4635116457939148, 0.8214490413665771, -0.3322325348854065, 149, 149, 149, 255, 1.0, 0.5)
(9600.0, 326.34356689453125, -9728.0, 0.5303630828857422, 0.847672700881958, -0.012873286381363869, 122, 122, 122, 255, 1.0, 1.0)
(9536.0, 370.5403747558594, -9728.0, 0.603411078453064, 0.7964465618133545, 0.039598409086465836, 107, 107, 107, 255, 0.5, 1.0)
(9472.0, 423.61572265625, -9728.0, 0.6945193409919739, 0.7165639996528625, -0.06464438885450363, 97, 97, 97, 255, 0.0, 1.0)
(9472.0, 439.9534912109375, -9664.0, 0.6727219820022583, 0.5930730104446411, -0.4423908591270447, 72, 72, 72, 255, 0.0, 0.5)
(64656, 900)
(9664.0, 0.0, -9664.0, 8, 60)
(9664.0, 323.531005859375, -9664.0, -0.16421209275722504, 0.9194830656051636, -0.3571907877922058, 202, 202, 202, 255, 0.5, 0.5)
(9600.0, 331.24566650390625, -9664.0, 0.11137175559997559, 0.9239282011985779, -0.3659959137439728, 199, 199, 199, 255, 0.0, 0.5)
(9600.0, 382.6859436035156, -9600.0, 0.32311421632766724, 0.6967470049858093, -0.6404222846031189, 110, 110, 110, 255, 0.0, 0.0)
(9664.0, 370.8617248535156, -9600.0, -0.030711062252521515, 0.8036384582519531, -0.5943247675895691, 202, 202, 202, 255, 0.5, 0.0)
(9728.0, 387.73626708984375, -9600.0, -0.3357184827327728, 0.8114649057388306, -0.478348970413208, 202, 202, 202, 255, 1.0, 0.0)
(9728.0, 359.2901306152344, -9664.0, -0.4858676493167877, 0.869583010673523, -0.0880785584449768, 202, 202, 202, 255, 1.0, 0.5)
(9728.0, 373.6744079589844, -9728.0, -0.6241185665130615, 0.7812370657920837, -0.012026631273329258, 202, 202, 202, 255, 1.0, 1.0)
(9664.0, 302.3572998046875, -9728.0, -0.23366673290729523, 0.9231089949607849, -0.3054006099700928, 202, 202, 202, 255, 0.5, 1.0)
(9600.0, 326.34356689453125, -9728.0, 0.23029862344264984, 0.9547197222709656, -0.18834225833415985, 177, 177, 177, 255, 0.0, 1.0)
(9600.0, 331.24566650390625, -9664.0, 0.11137175559997559, 0.9239282011985779, -0.3659959137439728, 199, 199, 199, 255, 0.0, 0.5)
(64656, 900)
True

*.DAT

First few bytes of 2,127,308 total:

00000000 19 00 00 00 00 01 00 00 23 00 00 00 00 00 80 42 ........#......B
00000016 00 00 FF 43 00 00 80 C2 00 00 00 00 FF FF 7F 3F ...C...........?
00000032 00 00 00 00 7A 7A 7A FF 00 00 00 3F 00 00 00 3F ....zzz....?...?
00000048 00 00 00 00 00 00 FF 43 00 00 80 C2 00 00 00 00 .......C........
00000064 FF FF 7F 3F 00 00 00 00 7A 7A 7A FF 00 00 00 00 ...?....zzz.....
00000080 00 00 00 3F 00 00 00 00 00 00 FF 43 00 00 00 80 ...?.......C....
00000096 00 00 00 00 FF FF 7F 3F 00 00 00 00 7A 7A 7A FF .......?....zzz.
00000112 00 00 00 00 00 00 00 00 00 00 80 42 00 00 FF 43 ...........B...C

I'm not sure about this one though.

My Questions:

  • Could the .obj file possibly be the heightmap, if not, what is it?
  • Any idea what the .CTR and .INF files are? (guesses?)

Additional Information:

The map 4 is called Lava Canyon. It features paths surrounded by canyons of lava.

Minimap Texture:

enter image description here

Related files: Download From Mediafire

  • okay, so they're actually not using heightmaps. Instead its a 3D object of the map. terrain4.obj are vertices. – majidarif Nov 7 '16 at 3:40

Your Answer

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

Browse other questions tagged or ask your own question.