Monday, 4 April 2011

Investigating X-Wing (OPT and XWI file formats)

The other day I was reminiscing about the old PC games I used to play back in the 90s when I remembered the Lucasarts classic X-Wing. I remembered that in the late 90s I used to make levels for it using an editor. A bit of searching found me two different editors that I remember fiddling with, one was the DOS based "X-Wing Mission Builder" and another was the Windows based X-Ed.

I found downloads for both (the X-Ed website still exists!) and both ran fine on my Ubuntu PC (XMB using DosBox, X-Ed through Wine). Little did I know that these downloads were going to start a chain reaction that would drive me to spend hours of my weekend staring at a hex editor. :)

When I was playing with X-Ed, I noticed that the map format itself seemed quite simple. You build maps by placing "flight groups", each flight group has a ship type (e.g. X-Wing), a start position, a ship count, and a number of waves. There are other flags for orders, team etc. but basically building a level for the game involved placing these flight groups and setting objectives (e.g. destroy an entire flight group). So, (I thought), if building a level is this simple, then surely the level format is simple. If I could understand the level format, perhaps I could load levels into a graphics engine like Ogre. How hard could it be?

It took some searching, but I eventually found that someone had already described the format in detail. I noticed though that some values were missing, by comparing X-Ed's UI to the level spec I could see which options weren't in the specification I just had to figure out which checkbox in the UI, affected which unknown element in the spec. So, armed with GHex, I made changes to test map while watching what it did to the file. When I was done I'd deciphered the 8 unknown values from the spec. I emailed the website owner and he's updated the page.

Fuelled by this success, I knocked together a little C++ app to load the map into Ogre and place the Ogre head mesh everywhere a ship would be. It worked, my next goal was to replace those Ogre heads with free 3D models of spaceships (e.g. not Star Wars related), perhaps I could turn this into an actual game! [1]

Only, staring at the large, overlapping Ogre heads, something occurred to me. What scale are the ships in the original X-Wing? If I could load them then I could find out... in fact, if I could load them then I might be able to code a modern, replacement engine for X-Wing!

This is where my curiosity probably went a step too far. I thought if the XWI level file format was so easy to find and load, then perhaps the OPT model format was also simple. Oh, how wrong I was.

Long story, short: I found a specification for the OPT format used in the later games in the series, it was good but some parts were a little ambiguous, or could have been explained in detail. I spent hours and hours staring at the hex code of the X-Wing model, I managed to understand the structure, load in the vertices, dump out the textures, everything was going fine until...

Assertion `indexes[0] + file_->get_index_offset() < vertex_count' failed.

For some reason, I'm finding indexes to vertices that are greater than the number of vertices in the mesh! This is the last stumbling block and I'm stumped.

So, if anyone is reading this, and they were part of the OPT hacking community back in the day please get in touch!

[1] Obviously not using the original maps or models, as they are copyright of Lucasarts etc. but using X-Ed to build maps.

1 comment:

  1. I had the same problem. It turns out the problem was that I'd assumed from the doc that the vertex references for all polys were all in a row. Actually, the information is grouped by face.

    IE it's not:
    poly0-vert0 poly0-vert1 poly0-vert2 poly0-vert3 poly1-vert0
    ...
    poly0-edge0 poly0-edge1 poly0-edge2 poly0-edge3 poly1-edge0

    It's:
    poly0-vert0 poly0-vert1 poly0-vert2 poly0-vert3
    poly0-edge0 poly0-edge1 poly0-edge2 poly0-edge3
    ...
    poly1-vert0 poly1-vert1 poly1-vert2 poly1-vert3
    poly1-edge0 poly1-edge1 poly1-edge2 poly1-edge3

    ReplyDelete