A static recompilation of a GameCube game to native PC.
This project takes The Legend of Zelda: The Wind Waker (GameCube, 2002) and statically recompiles its PowerPC 750CXe (Gekko) machine code into native x86-64 C code that runs directly on Windows 11. No emulator. No compatibility layer. Just the Great Sea, running natively.
Unlike an emulator that interprets or dynamically translates instructions at runtime, static recompilation translates the entire game binary ahead of time into compilable C source code. Each PowerPC instruction becomes a C expression:
// Original GameCube PowerPC:
// addi r3, r4, 0x20
// lwz r5, 0x10(r6)
// bl some_function
// Recompiled native C:
ctx->r[3] = (int32_t)ctx->r[4] + 0x20;
ctx->r[5] = MEM_READ32(ctx->r[6] + 0x10);
some_function(ctx, mem);The generated C code compiles with any modern compiler (MSVC, Clang, GCC) and runs at full native speed with all compiler optimizations applied.
+-----------------+
| Wind Waker |
| DOL/REL |
| (PowerPC 750) |
+--------+--------+
|
+--------v--------+
| ww_recompiler |
| PPC Disasm -> |
| CFG Builder -> |
| C Code Gen |
+--------+--------+
|
+--------v--------+
| Recompiled .cpp |
| (x86-64 native) |
+--------+--------+
|
+--------------+--------------+
| | |
+--------v------+ +----v----+ +-------v------+
| GX Runtime | | Audio | | Input |
| (D3D11) | | (DSP | | (XInput / |
| TEV -> HLSL | | ADPCM) | | Keyboard) |
| Texture decode| | XAudio2 | | GC Pad map |
+---------------+ +---------+ +--------------+
| Component | Description | Status |
|---|---|---|
| DOL Parser | Parses GameCube executable format (7 text + 11 data sections) | Done |
| REL Parser | Parses relocatable modules (actors, scenes) | Done |
| PPC Disassembler | Decodes all Gekko instructions including Paired Singles | Done |
| CFG Builder | Identifies functions and basic blocks from binary | Done |
| PPC-to-C Emitter | Translates each PPC instruction to C code | Done |
| Runtime | CPU context (GPR/FPR/PS/CR/SPR), memory, function dispatch | Done |
| Function Table | Auto-generated registration of all 8,148 functions for indirect dispatch | Done |
| GX Graphics | GameCube GX API -> Direct3D 11 translation | In Progress |
| TEV Shader Gen | Generates HLSL shaders from TEV stage configurations | In Progress |
| Texture Decoder | Decodes GC texture formats (I4/I8/IA/RGB565/RGB5A3/CMPR) | Done |
| Audio | DSP ADPCM decoder, voice mixer | In Progress |
| Input | Keyboard/XInput -> GameCube pad mapping | Done |
| OS Functions | Replacement implementations for Dolphin OS calls (heap, DVD, VI, CARD, threads) | In Progress |
The disassembler and recompiler handle the full Gekko instruction set:
- Integer: ADD, SUB, MUL, DIV, CMP, AND, OR, XOR, shift, rotate
- Floating Point: Single and double precision arithmetic, compare, convert
- Paired Singles: The Gekko's SIMD extension — PS_ADD, PS_MUL, PS_MADD, PS_MERGE, quantized load/store, etc.
- Load/Store: All sizes (byte/half/word/float/double), indexed, update, multiple, byte-reversed
- Branch: Direct, conditional, to LR/CTR, with/without link
- System: SPR access (LR, CTR, XER, GQR), CR manipulation, cache ops, sync
- Game: A legally obtained copy of The Wind Waker (GZLE01 - US version)
- Windows 11 with a DirectX 11 compatible GPU
- CMake 3.20+
- Visual Studio 2022 or compatible C++20 compiler
# Configure
cmake -B build -G "Visual Studio 17 2022" -A x64
# Build the recompiler tool
cmake --build build --target ww_recompiler --config Release
# Recompile the game (you need the DOL extracted from your ISO)
./build/Release/ww_recompiler.exe main.dol --extra-funcs extra_funcs.txt --output recompiled/
# IMPORTANT: After recompiling, apply patches to recompiled source:
# See "Recompiled Source Patches" section below for the full list.
# At minimum: recomp_0018.cpp (display init), recomp_0063.cpp (bump alloc),
# recomp_0066.cpp (VI wait, assert), recomp_0006.cpp (GX sync),
# recomp_0000.cpp (exception handler), recomp_0067.cpp (camera GX),
# recomp_0075.cpp (DVD read), recomp_0074.cpp (async DVD),
# recomp_0080.cpp (bctr tail call), recomp_0001.cpp (heap),
# recomp_0064.cpp (JKR resource lookup)
# Build the game runtime + recompiled code
cmake --build build --target ww_launcher --config Release
# Run (place your GZLE01 ISO as ww.iso in the project directory)
./build/Release/ww_launcher.exe
# Or specify paths explicitly:
./build/Release/ww_launcher.exe main.dol --iso /path/to/game.iso| Input | Action |
|---|---|
| WASD | Move Link |
| Mouse / Arrow Keys | Camera |
| Space | A — Roll, Talk, Grab |
| Left Shift | B — Sword |
| E | X — Item Slot 1 |
| Q | Y — Item Slot 2 |
| R | Z — Target Lock |
| Tab | R — Shield |
| F | L Trigger |
| Enter | Start / Pause |
| ESC | Quit |
XInput gamepads are also supported with standard button mapping.
The GameCube executable (DOL) contains up to 7 code sections and 11 data sections, all big-endian. We parse these and build a flat memory image.
Every 4-byte word in code sections is decoded into a structured instruction. The Gekko is a PowerPC 750CXe with ~200 base instructions plus ~50 Paired Singles SIMD extensions.
We identify function boundaries by scanning for bl (branch-and-link)
instructions, then trace execution flow to find basic blocks. Each function
becomes a C function.
Each instruction becomes a C expression operating on a PPCContext struct
that mirrors the Gekko's register file. Memory accesses go through
big-endian read/write helpers.
GameCube OS calls (memory allocation, DVD access, timing) are intercepted and replaced with native implementations. GX graphics calls are translated to Direct3D 11. Audio is decoded from DSP ADPCM and played via XAudio2.
The recompiler can process the full Wind Waker DOL, generate C code for all discovered functions, and the runtime provides the hardware abstraction needed to run it. The game renders textured 3D scene geometry from actual disc data through a GX→D3D11 translation pipeline at 60fps.
| Metric | Value |
|---|---|
| DOL Size | 4.16 MB |
| Entry Point | 0x80003140 |
| Functions Discovered | 8,148 |
| Total Instructions | 348,958 |
| Instruction Coverage | 99.99% (1 unknown encoding) |
| Output Files | 41 translation units + registration table |
| Supported Instruction Classes | Integer, Float, Paired Singles, Load/Store, Branch, CR, System |
The recompiler achieves near-perfect instruction coverage across the full Gekko ISA, including all Paired Singles SIMD instructions and quantized load/store operations with GQR-based dequantization.
The game is rendering textured 3D geometry from actual game data at 60fps:
- All 41 translation units compile with MSVC (C++20)
- DOL loaded, all 100 static constructors complete
- Game framework (
fapGm_Create) initialized successfully - Main game loop running at ~60 FPS with full
fapGm_Executedispatch - J3D/BDL model parser — extracts geometry, textures, and materials from Nintendo's binary model format
- Indexed triangle rendering — SHP1 display lists parsed, triangle strips/fans converted, vertex indices resolved
- CMPR texture decoding — 17 textures decoded (DXT1 variant) and bound via GX pipeline
- Per-batch material binding — INF1 scene graph maps shapes to materials to textures
- Multi-model scene — island terrain (5,946 verts, 8 batches) + ocean water (1,194 verts, 8 batches)
- TEV shader pipeline — texture * vertex color modulation, compiled and cached per-state
- Full D3D11 draw path — vertex buffer upload, pixel shader, blending, depth test, present
- Orbiting camera — perspective projection with automatic rotation around the island
- JKR archive mount system — proper JKRMemArchive objects in emulated memory with working vtable, linked into sVolumeList
- Stage.arc Yaz0 decompressed (59KB → 299KB), Room44.arc loaded (714KB), stage.dzs parsed
- 16 hardware-dependent functions patched in recompiled source
Stage.arc (59KB Yaz0 → 299KB decompressed):
tex/cloudtx_01.bti 4,128 bytes Cloud texture
tex/cloudtx_02.bti 4,128 bytes Cloud texture
tex/cloudtx_03.bti 4,128 bytes Cloud texture
dzs/stage.dzs 4,608 bytes Stage data (13 chunks)
dat/event_list.dat 227,804 bytes Event scripts
bdl/vr_back_cloud.bdl 40,096 bytes Skybox cloud model
bdl/vr_kasumi_mae.bdl 3,808 bytes Skybox haze model
bdl/vr_sky.bdl 6,496 bytes Skybox model
bdl/vr_uso_umi.bdl 3,072 bytes Sea surface model
Room44.arc (714KB raw RARC):
dzr/room.dzr 10,880 bytes Room data/actors
dzb/room.dzb 121,504 bytes Room collision mesh
dat/m128.amp 35,456 bytes Lightmap
dat/m128.bti 21,568 bytes Lightmap texture
dat/s128.bti 7,264 bytes Shadow texture
btk/model1.btk 2,784 bytes Texture animation
bdl/model.bdl 399,872 bytes Main room model (390KB)
bdl/model1.bdl 111,296 bytes Secondary room model
bdl/model3.bdl 3,360 bytes Tertiary room model
stage.dzs Chunk Table (13 chunks):
STAG (1) Stage settings RTBL (50) Room table
EVNT (1) Event data MULT (2) Spawn multipliers
EnvR (50) Environment registers Colo (5) Color settings
Pale (33) Palette entries Virt (31) Virtual light settings
RPAT (4) Room paths RPPN (30) Path points
ACTR (2) Actor placements RCAM (1) Camera
RARO (1) RARC override
| System | Status |
|---|---|
| Static constructors (100/100) | Working |
| Game framework (fapGm) | Working — full layer dispatch every frame |
| DVD/ISO file access | Working — FST parsed, both archives loaded from disc |
| Yaz0 decompression | Working — 59KB → 299KB decompressed correctly |
| RARC archive parsing | Working — 18 files across 2 archives |
| J3D/BDL model parsing | Working — VTX1, SHP1, INF1, TEX1 sections extracted |
| Indexed geometry rendering | Working — display list parsing, strip/fan→triangle conversion |
| CMPR texture decoding | Working — 17 textures decoded and bound to GX pipeline |
| Per-batch materials | Working — INF1 scene graph maps shapes to textures |
| TEV shader generation | Working — HLSL shaders compiled and cached per-state |
| D3D11 draw pipeline | Working — vertex buffers, pixel shaders, blending, depth |
| Multi-model scene | Working — terrain (opaque) + water (translucent alpha blend) |
| DZS stage data parsing | Working — 13 chunks, full chunk table decoded |
| Memory allocation | Working — bump allocator replaces JKR heap |
| JKR archive mounting | Working — JKRMemArchive objects in emulated memory, sVolumeList linked |
| getGlbResource / findVolume | Working — searches mounted archives, returns real JKR objects |
| Time Base (TB) register | Working — host QueryPerformanceCounter scaled to 40.5MHz |
| VI (Video Interface) HLE | Working — VIInit, VIWaitForRetrace (60Hz), VIFlush, framebuffer |
| CARD (Memory Card) HLE | Working — host FS backed (memcard_a/, memcard_b/), full read/write |
| Init stubs (OSInit, DVDInit, etc.) | Working — boot-time SDK calls satisfied |
| PPC helpers (ppc_helpers.h) | Working — CNTLZW, ROTL32, MFTBL/U, PSQ load/store from gcrecomp |
| HW register HLE | Working — VI line counter, SI/DI status, rate-limited logging |
The game's multi-threaded framework (fopMsgM/fapGm) is now operational:
- 9 processes dispatching every frame — root layer (0x0007), scene (0x0015), room manager (0x0017), environment (0x0028), camera (0x00A9), 3 scene sub-processes (0x01B5/0x01BA/0x01BB), and timing controller (0x01BC)
- Both execute and draw phases run — fn_table[2] and fn_table[3] dispatched via 3-level callback chain through the per-frame layer iteration
- Frame gate bypassed — VRetrace check always returns 0 without interrupts; we call dispatch sub-functions directly, skipping the gate
- Execution queue populated — process nodes at +0x34 linked into 0x803BCD60, with 16 priority lists iterated by func_802450D0
- Dolphin reference state loaded — 1170 global values from game info/SDA globals plus 831 scalar template values across 8 process types captured during Dolphin gameplay
- Scene state = 0 (active), matching Dolphin runtime
Process execute methods run but don't yet modify game state because process objects lack internal pointer state (set during construction, which is stubbed due to JKR archive dependencies).
Two systems are end-to-end functional:
- Rendering: ISO → Yaz0 → RARC → BDL → J3D parse → GX → D3D11 → 60fps
- Framework dispatch: fapGm per-frame → layer iteration → per-process execute+draw
The main blocker is the process creation chain: the original create functions (e.g., func_80022CEC) depend on JKR archive mounting, which requires a background thread and properly initialized heap system. Process objects are constructed with correct headers and scalar state from Dolphin captures, but lack the internal pointer state that the real create functions would initialize.
- Implement JKR archive mounting — the main blocker for the entire process creation chain. Every process create function (actors, camera, environment) loads resources from JKR archives during construction. Without a working JKRMemArchive mount system, processes can be allocated but not properly initialized.
- Get past the title screen — requires enough of the framework (scene transitions, input dispatch, UI rendering) to handle the title/file-select flow
- Connect game camera — the camera process has 610 values from Dolphin including position/FOV; use these to replace the hardcoded orbiting camera
- Actor spawning — parse room.dzr actor placements and create actor processes
- MAT3 material parsing — full TEV stage configuration from J3D materials
This project builds on incredible work by the community:
- zeldaret/tww — Wind Waker decompilation project providing invaluable symbol maps and architectural understanding
- N64Recomp — Pioneered the static recompilation approach for N64 games
- Dolphin — The gold standard GameCube/Wii emulator, whose GX shader generation code informs our TEV implementation
- decomp-toolkit — GameCube binary analysis tools
- powerpc-rs — Fuzz-tested PPC750 disassembler reference
This project does not include any copyrighted game assets or code. You must provide your own legally obtained copy of The Legend of Zelda: The Wind Waker.
Built with the same static recompilation methodology as our other projects. The Great Sea has never been closer.
"The wind... it is blowing." — The King of Red Lions