Reverse Engineering Crazy Taxi, Part 2

In this second part, we move from archive files to geometry, exploring how Crazy Taxi stores its 3D models and using a 'Rosetta Cube' to crack the proprietary .shp format.
Mar 29, 2026
This is the second of a series of posts detailing how I put interactive, 3D versions of Crazy Taxi’s levels onto the web. If you haven’t read the first post, I’d highly recommend it, since this builds off many of the ideas developed there.
- Part 1: Introduction & archive reversing Part 2: Reversing shape files
In part 1, we covered some basic elements of reverse engineering in order to decode Crazy Taxi’s .all file format, which turned out to be an archive format used to bundle thousands of the game’s asset files. I also stressed a couple lessons I’ve learned while reverse engineering, which I’ll go ahead and recap here:
Take detailed notes as you’re reversing. You never know when that random offset you noticed 2 hours ago will come in handy again. As early as possible, test your theories with actual code. Writing a quick script that checks your assumption across hundreds of files will save you tons of time compared to manually scanning, and that code can serve as a testbed for further discovery and theorizing.
In this post, we’ll build on these lessons with a couple new techniques in order to puzzle out the .shp file format, which I suspected might have something to do with the game’s 3D models.
What’s in a name?
But why do I think .shp files have anything to do with models? Well for one, .shp looks a lot like the word “shape”, doesn’t it? Admittedly, this is fairly weak evidence by itself, but it was enough to give me my initial hunch and was luckily bolstered by a number of other observations:
- Prior to expanding the
.allarchives, the only.shpfile in the game files is calledcube0.shp, and cubes are famously a kind of shape. It also seemed roughly the size I’d expect for a cube’s geometry (i.e. small). - Besides
cube0.shp, all the other.shpfiles live in archive files with names likepolDC0.all,polDC1.all, etc., whose prefixpolkinda suggests the word “polygon”. - The 2,744
.shpfiles within those archives have names likeCZ_train_B.shp,course_4_129.shp,CZ_IMPALA.shp, which all seem like reasonable names for game models.
Running with this theory, and assuming cube0.shp indeed contained a cube’s geometry in some Crazy Taxi format, analyzing that file seemed like a great first step to cracking this nut.
Rosetta Cube
Cubes, as you might know, are pretty simple shapes. After the humble triangle, they’re typically the first thing you draw to the screen when writing a renderer or game engine. So the fact that the developers at Acclaim dropped what appears to be a cube written in their proprietary format into our lap makes it something like a Rosetta Stone, since although we don’t know what makes a .shp file tick, we certainly know what to expect from a dang cube.
To inform our investigation into the .shp file, let’s review some standard elements used to store 3D geometry using a well-known, plaintext file format: the Wavefront .obj file.
In .obj files, all data is stored as plaintext lines, prefixed by one or two letters to indicate the type of data it is. For our purposes, we’ll only focus on three of these:
- Vertex positions: shapes in 3D graphics are made up of points, called vertices, and the most important feature of a vertex is its position. Positions starts with a
vand are followed by their coordinates. - Vertex normals: a vertex normal starts with
vnand is also followed by coordinates, but instead of defining a point, these define a vector. Basically, they define the direction a surface is facing at some point. This is crucial for adding lighting to a scene. - Face elements: a face starts with
fand is followed by 3 groups of values which describe a triangle. These usually take the form of indices into the list of positions and normals defined above.
The main takeaway here is that a shape can be described as a series of triangles, with each vertex in a triangle having some number of attributes (e.g. positions and normal vectors) associated with it.
What’s in the box
At last, here’s cube0.shp. When I’m looking at a new binary file format in a hex editor, I’ll often sorta squint my eyes and scroll through it to try to get a feel for how “dense” the data looks at different points. This is useful for identifying the boundaries of different sections of data.
I’ll abbreviate the number formats as follows: u for unsigned integer, s for signed integer, or f for floating-point, followed by the bit length (e.g., s16, f32).
To my eyes, there are a couple visibly obvious sections in this file:
0x00 - 0x110: Sparsely populated with some probablyf32and maybeu32values. I’m guessing this is our file header.0x120 - 0x150: Compactly filled with bytes whose integer values are either really big or really small. Maybe they’re f16s?0x150 - 0x180: Starts with some ASCII text that kinda looks like two numbers, followed by some byte data that could bes8.0x180 - 0x1c0: Compact array of maybeu16s ors16s.0x1c0 - 0x240: Seems to be a mixture ofu32s,f32s,u8s, and possibly even some string data.0x240 - 0x320: Could beu16s ors16s.0x340 - 0x390(end): Seems to be fixed-length strings, two of which reference.texfiles.
Source: Hacker News












