Vibe-exploring Stunt Island
(without paying a Rappen)
“It would be really cool” – I thought as a kid – “if we could just copy-paste the assets between games. I’d love to import Stunt Island’s resources to use them as scenery and vehicles in a different 3D game”.
It took 30 years, but here we are:
Both asset extractor and flight simulator are available in my Stunt Island page. What follows are some notes about their making.
Let the unpaid intern do the job
As the title says, I used LLMs to perform most of the work behind these divertissements. I wanted to gain experience in vibe coding, so I tried to keep my hands off the editor and delegate all the technical work – both the reverse engineering of the asset format and the writing of auxiliary tools and of the mini flight simulator – to the coding agents. Despite following the tech news closely, I was still surprised to see the tasks performed with good speed and precision: really a change of paradigm that people in my profession will have to deal with.
The project seemed almost too easy. To add some challenge, I decided to limit myself to free tools. Sure, paying the monthly subscription of one of the popular coding agents would have costed much less than the time I spent browsing to explore the free tiers of the various companies, but like many humans I feel the irrational attractive of the free deals. The timing was right, as I managed to get the most out of many deals before the providers turned the screws on rate limits.
- Ollama’s free cloud models are a nice boon, but the weekly allowance is only sufficient for ~15 minute of vibe coding.
- There was some kind of special deal from Microsoft that allowed access to top-of-the-line models for users of the free tiers. I enjoyed a very productive 3-hour session with Copilot, before being told I had exhausted my monthly limits.
- I had exactly one useful 1-hour session with Gemini. Any further attempt fell into a spiral of “model not reachable due to high load” and “you have reached the token limit for this model: downgrade?”.
- I tried some free cloud models from Openrouter (
nemotron-3-super-120b-a12b,minimax-m2.5) and found them worse than useless.
I was about to give up and open the wallet, but one of my last attempts struck gold. Alibaba’s Qwen CLI offered an absurdly good deal with 1000 requests per day and no token limit. Given the generosity of the offer I was skeptical about the effectiveness of the model, but I had to change my mind. It took a while to find the right incantation to start the CLI without it exhausting the memory,
ulimit -v 100000000 && NODE_OPTIONS="--max-old-space-size=8192" qwen
but after that, the CLI’s undescribed “coder_agent” (I guess Qwen 3.5 with size auto-selection) proved to be a very powerful and reliable model. I had to prompt it periodically and save it from some reasoning deathloops, but it solved a lot of problems on its own and wrote the whole code of the game, with me just applying some finishing touch and refactoring the most convoluted points.
Stunt Island’s assets
Stunt Island datafiles have been object of various reverse engineering attempts. Several resources are available, including utilities to create and extract assets with source code partially published (see the dedicated page on Stunt Island Central).
The 3D models are stored in files with .RES extension, each containing a collection of models in a format called SOD. Extracting the models from the RES is trivial, decoding the SOD is anything but. The SOD format, in fact, is not a declarative language like the typical 3D file formats. It’s more a powered-up version of Logo, creating the 3D model with a sequence of imperative commands which also include IF, GOSUB and RETURN blocks. It’s easy to see why no proper parser yet exists: the language is only partially documented, it’s very easy to desync during the parsing given the binary format, and the statefulness of the procedure means that misunderstanding a keyword may ruin the intepretation of all subsequent directives.
Given this situation I was unsure on what to expect when I pointed the LLM at the .RES files and at the existing source code of the various parsers, asking it to build its own decoder. Sure enough, the first attempts only produced garbage. After a while, however, in one of the garbled pieces of output I was able to make out one of the wings of the Red Baron’s triplane. Engouraged, I pushed forth and soon the bits and bytes started to crystallize into flying machines.
I was quite happy to see the LLM make progress by just saying “this does not look right, please retry”, but I would have also liked to add my own brain into the effort. Unfortunately, this was not to be. The documentation about the SOD language is too fragmentary, and parsing the datafiles by hand and seeing what the decoder was doing wrong would have taken a lot of time. One thing I would have liked to figure out is the exact semantic of the IFVIS keyword: I have the feeling that it really behaves like an if-branch, rendering certain directives if the model part is in full sight and others, more simplified, if it is occluded or far. I tried to make the decoder skip one or the other branch of this directive, but both choices led to the planes losing parts. On the other hand, rendering both branches results in polygons overlapping, with Z-fighting and parts in wrong color.
I pointed the LLM to a disassembly of the game: according to a session in GitHub Copilot, when the game loads a SOD asset it generates at runtime parts of the machine code necessary to visualize it. If true, this approach resembles what Wolfenstein 3D does, and makes the system much harder to reverse engineer. If I had decided to dedicate some weeks to the matter I am sure I could have made some breakthrough, and maybe even written a game-identical parse algorithm. But my free time is limited and I decided to assign my priorities elsewehere.
After all, the decoder I had in hand was already able to export in acceptable fashion my two favourite “flyables” of the game, the duck and the Fokker triplane. Since I was in the middle of my LLM frenzy, I asked Qwen to build another converter, this time from the exported models to the format used by the racing game Stunts. This task was much easier, since Stunts’s resources are stored in a very conventional format, a declarative list of primitives with just a couple of minor twists. The language model took one of those features, the wheels, very seriously:
But after fixing that, and letting Qwen add a nearest-neighbour algorithm to convert the vertex colors, the result were perfectly acceptable models.
I enjoyed rolling around on my triplane for a while, then I realized I would never be able to distribute the model legally, since it comes from Disney’s IP. I liked the idea so much that I am going to publish a Fokker plane for Stunts based on free assets, but this is another story.
Back to Stunt Island, I tried the decoder on other assets of the game. The results seem similar but slightly different from those produced by the other available export tool, SODParse. In general it seems that the vide-coded decoder is much better on planes but slightly worse on everything else, probably because of its aggressive treatment of the IFVIS directive. In any case, I have published the source code so that others can pick up the challenge in future.
Remaking Free Flight
Now I had a wealth of assets floating around and still some desire to try out vide-coding. I went for the most immediate choice, making a program to navigate Stunt Island’s environment in 3D. A similar software, called SI Free Flight, had already been made by an anonymous programmer in year 2000 and I was not planning to add fancy features, so I went on to just building a remake, whose added value was the usage of modern technology (e.g. unlimited window size) and open source license allowing anyone to make improvements. I thought of choosing a new title, but I could not find one I liked, so I went with SI Free Flight – Take 21.
The choice of framework, probably the only contribution of the least capable models, fell on Raylib, which in hindsight I am satisfied of. A little C-ish, but very immediate and low-boilerplate.
I know nothing about 3D programming, so I just “fully gave in to the vibes”, let Qwen do the writing and just evaluate the results. Sensing that the availability of a powerful free model was a temporary luxury, I tried to work fast and complete the project as early as possible. Indeed this seems to be a relevant change of this new way of developing: when I write the code myself I will time ensuring it looks decent, meticulously fixing style inconsistencies and typos. Here, even in the later stages when I finally did some cleanup and refactoring, I was happy to mix east const and west, #pragma once and include guards, tabs and spaces. I probably even left some indentation error. I did not choose to lower my acceptance bar on purpose, but it really seems the availability of a high-speed code producer makes you wish to go forward soon and not “waste time on the details”.
Be that as it may, the experience was not devoid of learning moments. In particular I learned some 3D concepts like batch rendering and GPU instantiation. I did not think any optimization technique would be necessary to deal with the assets of a 1992 software, but I had to reconsider and, once more, render homage to the game’s authors Adrian Stephens and Ronald Fortier: the island packs more than 140,000 objects, and a naïve attempt to draw them one GPU call at a time managed to get the frame rate down to 3-4 fps. Even ignoring the sophisticated optimizations of the original game, I still had to put in some basic expedients like frustum and distance culling, as well as loading only one copy of the mesh for assets sharing the same model. I would have liked to make DrawMeshInstanced() work (rendering all copies of a given asset type in one shot) but all attempts failed without a recognizable cause for the error. In any case, just the basic culling, which removes objects outside of the viewing cone and those who would occupy less than one pixel on screen, was enough to achieve 40+ FPS under any circumstance.
Since a 3D engine is a standard task, easily built by a LLM, and the asset decoder was available, the main other difficulty in making a mini flight simulator was to find the list of objects of which the island is made. Apparently there are two datafiles contaning that: one contains the basic primitives making up the landscape (plains, high mountains) and defining reference points for the default assets; the other is a scenegraph determining the coordinates of the assets themselves. The scenegraph is not a simple list but a tree structure that also seems to operate a binary space partitioning, determining which objects can be seen from a given point of view. The tree has various levels of branches, while its roots are the assets proper, varying in size from plants to a low mountain.
Such assets, by the way, are contained in the game’s SCENERY<n>.RES files and not available in the game’s set designer. I wonder why: having more props to build up the scenes would have been useful. Also, interestingly, the scenegraph did not specify the Eulerian angles (roll, pitch, yaw) of any object. As it turn out, it’s not necessary because all the scenery is already oriented in the intended way: for things that have to appear at different orientations, like the road segments, the scenery files contain multiple copies at all the necessary angles.
It took some time to place the objects correctly, especially since Qwen was dead set on calling “Y” the vertical coordinate while older games (and myself) tend to call it “Z”. Since the model was doing the coding, it soon realized it was better to let it have its way and just adjusting the user-visible messages. After fixing various signs and mirroring errors, the island started to resemble the one shown by the DOS game.
Comparing old and new graphics confirmed my ideas about how not always “more” means “better”. The limited 320x200 resolution of the original game somehow mitigated the problems of the low number of polygons: in those huge pixels2 your brain could imagine all kinds of made-up details. In the world of modern GPUs and 4K monitors, instead, it’s harder not to be thrown off by the Euclidean perfection of the hills and the polyhedral smoothness of all surfaces.
On the other hand, SI Free Flight allows to explore the landscape in a quick and safe fashion, without risking to crash one’s scout aircraft like in Stunt Island’s own free roaming mode: a couple of hours with the simulator got me more familiar with the island than many dozens of game missions and set design sessions did, and allowed me to get a much better grasp of the geography and see less-known corners. If someone is still intending to create machinima with the game, Free Flight could be a good planning tool to find the right filming locations. Also, being able to look at thousands of objects at a time without strict LOS limits makes you admire the effort that went in building this fictional place.
The rest of the project was fairly easy sailing: I tweaked the controls and, to make something fancy, added a TIE Fighter-style targeting computer. I tried to fix as many rendering glitches I could, but some issues remained that could not be easily solved. The base altitude of the objects, for example, is not in the scenegraph and I was unable to reverse-engineer how the game computes it. Since most of the objects have it at the sea level this is not a fatal flaw, but has unpleasant consequences such as the bridge roads collapsed into the water and the castle on the central islet buried under earth.
But one needs to leave some challenges to future generations, after all: my last LLM request was to create a nice icon for the game, then I packed the project and started the rituals for its publication.
Closing thoughts
Just a couple of days after my last vibe coding session, the Qwen free tier was retired: a good moment to draw conclusions. Unfortunately, my feelings about this experience are not as positive as after previous adventures. In 2024, to produce my 8x detail patch I put in a dozen of hours of effort and ended up changing three bytes. Here the time invested was much more and resulted in a bigger project, but I still feel I have accomplished less. What is missing is the “blood and tears”, the feeling of struggling against hard challenges and the joy of overcoming them with grit and luck. The remake of Free Flight was akin to a river cruise: takes time, there can be stormy days, but in general you stay in a comfortable cabin and eat tasty meals at predictable hours.
I don’t know how the situation will evolve. Currently there are still challenges above the LLM’s skills, but it’s unclear how long they will last. The availability of language models is a boon if the goal is to create mods and reconstruct how old programs work (I would have never returned to this game without this kind of assist) but a curse if one is looking for the thrill of solving ancient riddles with their own skill. Sure, one can deliberately abstain from using AI and tackle the challenge unassisted, but the thought is always present that a novice could reach the same result by just putting some money into a high-performance model. Is hobby reverse engineering turning into a pay-to-win game?
I wanted to write a full essay about this topic, but I soon found a new adventure to dive in, and realized that creating new things is more fun than complaining about things one cannot change. See you soon! In the meanwhile, you can head to my Stunt Island page to download the tools.