The re4 project decompiles Resident Evil 4 on the GameCube into C/C++ that recompiles to byte-identical binaries. The target is the G4BE08 debug build — the “Nov 25 2004” prototype, both discs — and the build reproduces main.dol plus all 114 REL overlays exactly, verified against a SHA-1 file on every build.
It is 1,083 objects, 15,641 functions, ~555k lines of C and no assembly files.
What it took:
- The original compilers, rebuilt: SN Systems ProDG 3.9.3 (GCC 2.95.3), Metrowerks CodeWarrior 2.4.7 for the CRI middleware, CodeWarrior GC/1.2.5n for the Nintendo SDK.
- Zero-code search first: prototypes, object size and ordering, rodata/vtable gaps, statement and declaration order, inline boundaries.
- Tagged levers only after that — 644
// COMPILER-DIFF:comments (dead tests, asm anchors, register pins), and not one of them emits an instruction, which a lint enforces. - An asm-removal pass that deleted roughly 200 hand-written asm sites in favour of real C, keeping assembly only where the target bytes contain instructions the compiler cannot emit from C at all: paired-single kernels, GQR setup, a few cache and SPR intrinsics.
The matching notes are the real artefact. Every observable difference is mapped to the compiler pass behind it — loop-hoisting thresholds, gcse pseudo numbering, local-alloc quantity ordering, reload register tables — and then to the source shape that reproduces the original bytes.
The engine guide is just as good. Rooms are archives of tagged blocks streamed by block.cpp over the data-control and DVD queues. Per-frame work is registered tasks with priorities. Enemy behaviour is encoded in four routine bytes. Difficulty is a continuous score, not a setting: GameAddPoint accumulates kills, damage, accuracy and deaths, clamps at 0–11000, and Game_level = point / 1000 drives enemy HP, drops and QTE windows. Group AI is throttled by shared room timers rather than per-enemy logic.
Two things outlive the project. Matching is compiler archaeology with a fallback ladder — explain the bytes structurally before you pin a register — and a debug build is a gift to everyone twenty years later, because the function names and original file paths only survived in those symbols.