How agent swarms decompile games byte-for-byte
Seven months teaching fleets of coding agents to reconstruct a shipped game binary byte-for-byte — and building the oracles that keep them honest.
For the past seven months I have been pointing swarms of Claude Code agents at a 16-year-old Xbox 360 binary and teaching them to decompile it byte-for-byte. The goal sounds like game preservation, and it is — but the interesting part turned out not to be the games. It was the machinery: how you get dozens of AI agents to grind a brutally exacting task around the clock, what happens when they start gaming their own metrics, and what it takes to build oracles that let a machine tell itself the truth.
The strictest standard there is
Matching decompilation is the console-preservation community’s gold standard. You reconstruct C++ source that, when recompiled with the original compiler at the original flags, produces machine code byte-identical to the shipped retail game. Not “close enough.” Not “behaves the same.” Byte-identical. A byte-matched source tree is a provably faithful reimplementation — original, clean-room code that recreates a piece of software history without distributing a single copyrighted byte.
My targets were three games built on Harmonix’s in-house “Milo” engine: Rock Band 3 on Xbox 360, Rock Band 3 on Wii, and Dance Central 3. The real project, though, was a question: how far can AI coding agents push a clean-room decompilation before a human has to step back in?
The honest answer, as of mid-2026: remarkably far. In one repo, agents byte-matched 18,689 functions in roughly eight weeks across 1,472 commits, with one day peaking at over a thousand new matches. The Dance Central 3 effort reached 92.44% of its 32,252 authorable functions at a normalized match score — and grew a from-scratch WebGPU port of the engine that boots and plays. The Wii build sits at 81.9% fuzzy-matched across 11.4 MB of code. And then, at a certain point, the swarm hands you back a strategy decision instead of more matches. More on that at the end.
Why this is genuinely hard
The Xbox 360 version of Rock Band 3 shipped with zero debug symbols. No PDB, no map file, no names. Every one of tens of thousands of functions starts life as an anonymous address. The binary was produced by Microsoft’s MSVC compiler for PowerPC, which folds identical functions together and lets the linker reorder code freely — so even “ground truth” is ambiguous. I also measured against the whole binary, including the SDK, C runtime, and middleware nobody will ever match, which makes my honest whole-binary number (25.1%) deliberately less flattering than the 73.1% for the game code the effort actually targets. No denominator games.
You cannot brute-force names out of a symbol-less binary. What makes it tractable is what I came to call the Rosetta Stone strategy. I had two other decompilations of adjacent code. Dance Central 3 runs the same engine, compiled by the same toolchain at the same flags, and its debug symbols survived — so it became the engine-and-toolchain oracle. The Wii build of Rock Band 3 retains assert strings and function names that the Xbox retail build stripped — so it became the game-code oracle. The stripped Xbox binary sits at the intersection: same engine as one sibling, same game as the other, symbols from neither. Triangulating between two parallel texts is the only reason the third is readable at all.
The agent factory
A single agent can match a function. Getting dozens to grind concurrently without corrupting each other’s work is an infrastructure problem, and the infrastructure is as much the story as the matches.
The core is an orchestrator MCP server sitting over a SQLite match database. Agents don’t eyeball raw assembly and guess; they call typed tools — run a structured diff, analyze a function, look up a struct offset, record a patch result. The whole thesis is structured tools, not vibes. Around it grew about two dozen reusable agent skills packaging the recurring reverse-engineering moves: reconnaissance on an unmatched function, assembly comparison, stack-layout analysis, cross-referencing the sibling decompilations.
Isolation came from a copy-on-write worktree pool: each agent reflinks the binary and a warm build cache into a private worktree, with hard rules against destructive git operations in the shared tree. Speed came from a content-addressed object cache keyed on compiler identity and dependency hashes, which cut a cold full rebuild from about five minutes to 3.5 seconds. When your feedback loop is “compile and compare,” that two-orders-of-magnitude speedup is the difference between an agent iterating and an agent stalling.
Underneath sits a whole forked toolchain — the tools that split a retail executable into linkable objects, a compatibility layer that runs the real 25-year-old MSVC linker on Linux by faking an undocumented Microsoft COM interface, and a diffing arbiter extended to emit machine-readable diagnoses agents can act on. The deepest cut: the Xbox 360’s VMX128 SIMD extension is an instruction set that even mainstream disassembly frameworks don’t decode — and the standard tooling silently mis-decoded it — so I wrote full semantics for all 77 opcodes by hand.
When the metric lies to you
Byte-matching is a great metric right up until you realize it can lie, and each way it lies forced a piece of real engineering.
A function can score 100% and still be wrong. The fuzzy score forgives relocation-target differences, so a function can call the wrong symbol and still look perfect. These bugs are invisible to any static check. The fix was to stop trusting the score and make the decompiled code actually run — hence the WebGPU engine port, which re-links the decompiled game code into a native runtime that even runs in a browser tab. Running the game caught what the metric couldn’t: a single sweep found 65 wrong-callee bugs masked by relocations, plus subtler things like a comparison function reading a string pointer as raw characters. To exercise the Kinect gesture code without a Kinect, I fed YOLO pose estimation into the game’s real move-scoring pipeline. A companion tracing harness captures per-function execution under an emulator and replays it against the decompiled code — and, memorably, audited itself to discover that 96.7% of its bug-hunting pool was comparing byte-identical code to itself, then used that reframing to immediately find two real, independently confirmed bugs.
The compiler is the referee, so I cracked it open. Every candidate, every permutation, every score ends in a compile-and-compare against the original MSVC — which makes the compiler the bottleneck of every loop. MSVC’s cl.exe is secretly two programs: a front end that writes an intermediate language to disk, and a back end that turns that IL into an object file. That IL is a real, drivable interface, if you can capture it — the compiler deletes its temp files on exit, so extracting the bundle took an undocumented flag combination that aborts the back end after the front end writes, plus a syscall fault injector that turns file deletion into a no-op for the reference capture. Crude, effective. With the IL in hand, I built a clean-room Rust port of the back end that never decompiles the original: it reverse-engineers the object format by byte-diffing alone and treats the real DLL as the sole judge, returning “not implemented” everywhere it can’t yet prove itself. On its MVP function class it is byte-exact and roughly 200–290 times faster per object — at 32 threads, about 897,000 objects per second versus about 3,100 for the real thing. That’s the speedup that could let a search score millions of candidates.
Your fitness function is an adversary. The search engine that automates the last brutal stretch of matching — the register-allocation and instruction-scheduling gap where the code is right but the bytes differ — carries 140 behavior-preserving source rewrites and tries them against the real compiler. Early on, under a naive “accept if the score goes up” rule, it shipped two genuinely wrong rewrites its own metric rewarded: one turned a multiply-by-half into an add-half (equal only at a single input) for +3.25 points, and another swapped the arguments of a non-commutative call for a fraction of a point of rounding noise. Both are documented like incident reports. The response — byte-exact recompilation as the only terminal judge, every other signal demoted to an advisory gradient — generalizes to any synthesis system driven by an imperfect reward. Reward hacking isn’t a hypothetical in agent systems. I have the commits.
What surprised me
Non-reasoning models beat reasoning models at this. On a small pinball-firmware side project, reasoning models kept burning their entire token budget thinking and returning nothing. Cheaper non-reasoning models just wrote the code. When a hard external verifier exists, “think harder” can be a liability — the verifier does the thinking.
Every proxy metric eventually gets satisfied by something you didn’t mean. The reward-hacked rewrites and the relocation-masked bugs were the same lesson in different clothing. The durable fix is always a ground-truth oracle below the proxy: the real compiler, the running engine, the original DLL.
The best day came from diagnosis, not production. My single best day — over a thousand new matches — came not from more agents but from one insight: the retail linker scatters functions freely across the binary, so my splitter was attributing them to the wrong source files. A pool of functions that looked “unmatchable” wasn’t missing source; it was a bookkeeping illusion.
Agents grind well and stop badly. The most valuable artifacts they produced weren’t matches — they were state documents that classified dead ends with evidence and escalated a genuine resourcing decision back to me: the cheap veins are exhausted, and the long tail needs either serious distributed compute or a human. Management science, applied to a solo project.
What generalized, and what’s unfinished
The pattern — agents plus structured tooling plus a ruthless external verifier — traveled well beyond one engine. It powered a responsible-disclosure firmware-triage methodology for network appliances, where the same rigor was used as often to disprove scary-looking findings as to confirm real ones. It drove render-parity work on an obfuscated game client, a shipped multiplayer mod, and the sandbox and orchestration infrastructure needed to run agent fleets safely in the first place — hundreds of thousands of lines of which were written by the agents themselves.
I want to be precise about what’s proven versus promising. The Rust compiler port is byte-exact only on its MVP class; most PowerPC code generation still returns “not implemented.” The ML side of the search program has produced honest negative results as often as wins — one capability sweep put a frontier model’s whole-function byte-exact rate at 12%, and a favored search strategy was retired for producing zero results. The performance numbers are from my hardware, not independently reproduced. And the headline decompilation is paused at a pivot: the cheap matching is done, and the deep grind is a resourcing question I haven’t fully answered.
That’s the honest shape of it. The agents did not replace the hard thinking; they made the hard thinking cheap to act on at scale — which turned “one person can maybe decompile a game” into “one person can run a decompilation lab.” The frontier was never the byte count. It was building the oracles that let a machine tell itself the truth.