The making of Don Matrelli’s Legacy, a mod for Grand Prix Circuit (part I)
Annali da Samarcanda
Alberto Marnetto’s Notebook
Don Matrelli’s Legacy:
Part I ·<br>Part II
Part III ·-->Part III ·<br>Part III ·-->Part IV
Contents
(Top)
Gleefully falling into the rabbit hole
A missing piece
On the tracks of the tracks
I’m gonna go build my own theme park
How to build a car
End of Part I
The making of Don Matrelli’s Legacy, a mod for Grand Prix Circuit
Part I: the reverse engineering
Some weeks ago I released an expansion for the only Formula 1 game I ever liked: Grand Prix Circuit (GPC), a 38-year-old work by Distinctive Software1. This project was by far my most ambitious to date: it took more than two months and required learning the basics of many disciplines, but I’m very satisfied of how it turned out.
This post series will tell how the mod, Don Matrelli’s Legacy , came to be.
Gleefully falling into the rabbit hole
I had not played GPC for decades and the inspiration came by chance. I was toying around with the Stressed resource editor for Stunts, looking at a partially-built new vehicle. Looking at the cockpit sprites, I remembered reading somewhere that for such bitmaps Stunts had kept the same image format as its predecessors, Test Drive and GPC. It was unlikely that the cockpits of the cars in those games could be used as base of the creation, but that day I felt more inclined to do experiments rather than pixel art.
The 2D asset files of Stunts are stored in files with extension PVS and PES (Packed VGA/EGA shapes), both of which Stressed is able to open. The older GPC offers instead PES and PCS files: promising. I tried to open a PES file from GPC and got an error of invalid compression magic. It seemed I had a small riddle to solve.
I set up a workspace for the reverse engineering work and, with the limited free tokens still available, I launched Qwen over GPC’s main executable, called GPEGA.EXE. The datafile compression algorithm, a simple RLE scheme, quickly fell under the blows of the LLM: soon I could compile a freshly-baked piece of slop that could unpack the PES files on the fly. Yet another adventure made trivial by modern technology, but the story was just at the beginning.
I opened some GPC datafiles: no error message any more and a list of sprite names read out from the file header. However, when I tried to open the individual bitmaps, instead of the familiar shapes of cars and circuits I got mysterious blobs of pixels. And yet, there seemed to be a method in that folly, and I got a first illumination when analyzing the sprite named map_ in one of the track files:
As distorted as it was, the image was clearly resembling GPC’s minimap of the Monza circuit:
Qwen was not able to figure out how to process the image above to get the one below: finally an interesting challenge for the human-in-the-loop. It did not take much to realize that Stressed was reading the image as if it were in 256 colors, 1 byte per pixel. EGA has 16 colors, so naïvely one can think it uses only half byte? But no, the track bitmap in Stressed was horizontally compressed by a factor 8, so the format had to be one bit per pixel. I adjusted the code accordingly and obtained a correct, albeit black-and-white, depiction of the Monza track.
Promising, but where are the color info stored? Finding out took a lot of time, especially since I continued to interact with the LLM much past the point it stopped producing good insights. I finally stumbled into the solution by plotting the data past the declared bitmap size. The patterns started to make sense, and after some experiments, an image from the Monaco circuit allowed me to solve the puzzle.
The goal. Ignore the cloud: it's a mobile sprite, added by a separate algorithm.
The decoding attempt that let me see the light. Some stray bits at the start offset the image, but the buildings are recognizable.
If one is familiar with color models, it’s easy to recognize that the four stacked black-and-white versions of the Monte-Carlo scenery are like the colored plates of an old printing process: as it turns out, the first of them determines whether the “blue” bit of the corresponding pixel is active, the others control the green, red and brightness bit respectively. One could have guessed so immediately if one knows that the EGA card stores its video buffer in planar format, but it was a fun investigation.
After overcoming this obstacle, the rest of the PES format was easily figured out. The only remaining riddle was the meaning of a uint8[4] array associated with each sprite: the decoder was emitting the correct images when the array was {1, 2, 4, 8}, which was usually the case, but failed when the values were different. It turned out that:
each array element map corresponds to one of the “color plates”, and determines which of the four EGA color planes (brightness, red, green and blue) such plate controls. For example, if the first element is 5 (0101 in binary)...