Reconstructing a 1990s DOS Game from Binary
I’ve been spending my evenings reverse-engineering a DOS game from the mid-90s. Not playing it. Reconstructing it. Taking a compiled executable and working backward to understand how it worked, then rewriting it from scratch.
The game itself isn’t important for this story. It was a multiplayer thing, text-based, the kind of game you played over modems when that was still novel. I spent a lot of hours on it as a kid. The original source code is long gone, if it ever existed outside the developer’s hard drive. What remains is a 400KB executable compiled with Delphi 4 and a set of binary data files.
Reading Dead Code
The main tool is Ghidra, the NSA’s reverse engineering framework that they open-sourced a few years back. You load the executable, tell it the architecture (32-bit x86), and it starts disassembling. What you get is a mess of assembly language, function calls to addresses instead of names, and a lot of patience required.
The first task is finding the data structures. This game stores everything in fixed-size binary records written directly to disk. No headers, no metadata, no format versioning. Just raw structs, one after another. If you don’t know the exact size of each record type, you can’t read the files.
I found the sizes by looking for file I/O calls in the disassembly, then tracing back to see what buffer sizes were being allocated. Cross-referencing with the actual file sizes on disk and dividing by the number of expected entries confirmed it. One record type had a padding byte I didn’t expect. That took two days to find.
Matching Behavior
Knowing the structures isn’t enough. You need to understand the logic. How does the game calculate prices? How does combat resolve? What algorithm generates the game world?
My approach: run the original in DOSBox, poke at it, record the outputs. Then find the corresponding code in Ghidra, understand what it’s doing, reimplement it, and compare. When the outputs match exactly, I know I’ve got it right.
The economic simulation was the most interesting puzzle. There’s a negotiation system with rules I never noticed as a player. Thresholds that change how the game responds to your offers. Price curves that depend on supply levels in non-obvious ways. Someone designed this carefully, and the elegance only becomes visible when you’re reading the actual math.
Combat was trickier because it involves randomness. The game uses a probability system where outcomes depend on ratios stored in the data files. But one entity type had a hidden modifier that wasn’t stored with the rest of its stats. It was a flag bit in a completely different field. I only found it because my implementation gave different results than the original, and I kept digging until I understood why.
Why This Matters (To Me)
Reverse engineering is tedious. You’re reading assembly for functions that do boring things like “copy a string” or “increment a counter.” You’re wrong constantly. You misread a jump condition, miss a side effect, assume a variable is signed when it’s unsigned. Progress is slow.
But every solved puzzle is satisfying in a way that’s hard to explain. You’re not just learning what the code does. You’re learning what the original developer was thinking. Their constraints. Their shortcuts. Their clever tricks. It’s a conversation across decades, conducted entirely in machine code.
I’m not releasing this project. The IP is still active, still licensed, still someone’s livelihood. This is purely personal, a thing I’m doing because twelve-year-old me would have thought it was magic, and because forty-year-old me finally has the skills to pull it off.
Some projects are just for you.