Reverse Engineering WipEout (PSX)
In 1995 one of my all time favorite video games was released: the original WipEout for PlayStation. The brand new PlayStation produced 3D graphics previously unseen on living room TVs and WipEout exploited its capabilities like no other game at the time. It was one of the pioneering titles of the fifth generation console era.
WipEout's art style was distinctively different from other games too. With the help of the UK based design studio The Designers Republic the game achieved a mature look that was in stark contrast to the comic style found in most other games.
I remember poking around on the CD of the PC Version of WipEout back in the day, looking for ways to modify the game. I was thrilled to find .pcx
images of all textures and tried to change one of the in-game billboard graphics to show my name. I wasn't able to get it working.
Now, almost 20 years later, I thought I'd give it another shot.
WipEout Model Viewer – A WebGL Experiment
Extracting 3D Models
I didn't have much experience with reverse engineering any data formats, but given the game's age, I expected the 3D format to be quite simple and straight forward. What surprised me was the many different nuances in which scene models were stored. The track itself was stored separately with a different format, spanning multiple files, yet again.
Knowing the PlayStation didn't have a Floating Point Unit (FPU) I assumed vertex data had to be stored as Integers. Using JavaScript and the three.js 3D Library I quickly whipped up a page that would load one the games's .PRM
files that I thought would contain the 3D objects. I loaded all data in the file as coordinates of 3 Integers (x, y, z) and rendered them as a point cloud. This allowed me to quickly shift the stride (the spacing between distinct coordinates in the file) and look for patterns.
What I found was indeed chunks of raw vertex data, along with an object header specifying the object's internal name and number of vertices and polygons in that object. Each polygon is preceded by a polygon header and a chunk of index pointers into the object's vertices. These polygon headers is where I probably spent most of my time. I identified 11 different polygon types used by the game – triangles, quads, sprites, textured or flat, having vertex colors or face colors etc. Two types are still ignored because I couldn't figure out what they were.
The track itself is stored in several distinct files. The most interesting of which are TRACK.TRV
, containing raw vertices, and TRACK.TRF
containing the track faces as quads of 4 index pointers into the vertices. Pretty straight forward.
Reading textures
Most textures in early PSX games were stored in the TIM image format, containing data straight in the PSX' native frame buffer format. A TIM file stores pixels either directly as 16 BPP colors, or as 8 BPP or 4 BPP indices into a color palette. And indeed, you can find a number of TIM files on the original WipEout CD: the start splash screen, background images, loading images etc. However, the images I was most interested in – the textures used for 3D models - were missing.
Some googling revealed that textures are stored in the compressed SCENE.CMP
files. These files contain a very simple header that specifies the number of TIM images in the file, along with the uncompressed sizes of each image. The data itself is compressed using LZ77. I even found some C code in a reverse engineering wiki, that would uncompress these files. With this code quickly ported to JavaScript and poking around in the polygon data to find the texture index and coordinates, I was finally be able to draw textured models.
The textures for the track however were an entirely different beast. Along with the vertex and face data (TRACK.TRV
and TRACK.TRF
) each track comes with an additional TRACK.TRS
, containing Track Sections of some sort, and a TRACK.VEW
, containing visibility lists (I think). I spent a lot of time trying to find the texture index for each track face in any of these files.
In fact, the unhelpfully named TRACK.CMP
did not contain the track textures. Instead, track textures were stored in a file called LIBRARY.CMP
, which seemed to be compiled from a set of source images.
As I painstakingly found out, the tracks in WipEout had a Level of Detail (LOD) system, subdividing each track face in up to 4x4 quads when they're near the camera. This was probably done to lessen the impact of the PSX' missing perspective correction when drawing textures. These subdivided faces also used higher resolution textures.
Now, here's the kicker the LIBRARY.CMP
file contains about 300 images, but only 19 distinct textures - each in 3 different LOD levels: 4x4, 2x2 and 1x1 tiles, each tile being 32x32 pixels in size. And, as if that wasn't complicated enough, these 4x4 and 2x2 tiles are not stored in the right order. Instead, yet another file, LIBRARY.TTF
, stores indices into the LIBRARY.CMP
to compose these 4x4 and 2x2 versions.
Knowing that the texture index has to be between 0 and 18, I found it stored as a single byte along with each track face. I also found that another single byte for each track face specifies a few flags for that face. The most important one for drawing: whether to draw with flipped X texture coordinates.
Wrapping up
Finally, I had a complete, textured, vertex-lit scene and track. Using three.js drawing everything was a no-brainer, as it happily lets you create models of all kinds of different polygon data. I also wanted to have a simple fly-through animation for each track. three.js was tremendously helpful to create a smooth camera spline along the track. The optional orbit controls for three.js are top notch, too. They just work without any modifications on mobile and touch devices.
With all the work that went into this project, handling the WebGL drawing with three.js turned out to be one of the simplest parts.
The finished WipEout Model Viewer loads all the original data files and does all the binary file reading, unpacking and scene creation in JavaScript.
All in all, I wrote about 800 lines of JavaScript to load and draw 3D scenes for a 20 year old game. I wonder how big the original WipEout sorurce is. It's quite sad that probably nobody will ever see it again, considering that it now belongs to Sony.
Full source on github: github.com/phoboslab/wipeout