A game-agnostic toolkit for Wii reverse engineering and native PC ports: disc images, executables, the Gekko CPU, a static recompiler from Gekko code to C++, and a runtime that replaces the Wii's hardware under the recompiled game. The GameCube, the Wii's older self (the same processor, GPU and DSP), is served by the same pieces and its own few: its discs, its disc drive, ARAM, the controllers on SI.
The same idea as ps2kit, for the Wii. Each Wii game has its own engine and formats, but a large part of every port is the same work: the same disc encryption, executable formats, CPU, SDK and GPU. wiikit collects that shared part. It grows inside the ports: each piece is written because a game needed it, then kept free of that game's knowledge. Game formats and game fixes live in the ports.
| Port | Game | What it asked of wiikit |
|---|---|---|
| pc-victorious | Victorious: Taking the Lead (2012) | everything so far: the disc, the symbolised ELF, the recompiler, the runtime from __start to a played, heard, 16:9 game |
| pc-dragonquestswords | Dragon Quest Swords: The Masked Queen and the Tower of Mirrors (2007, PAL) | a stripped executable (function discovery, names by signature), RVZ images, the PAL boot and EuRGB60, the locked cache's DMA, thousands of draws a frame, audio under load, the Remote's speaker, swings from mouse drags |
| pc-conduit2 | Conduit 2 (2011) | the 2010 SDK's KPAD Ex forms, IOS replies after the call (a NAND race), the GP FIFO and display lists in MEM2, RG8/GB8 EFB copies, the relative mouse, the Classic Controller and SDL gamepads (split-screen), the F12 GX trace |
| pc-arcrisefantasia | Arc Rise Fantasia (2009) | the 2007 SDK's KPAD and WPAD (the Classic read from WPAD's own samples, and from the copy of them only that KPAD keeps), thousands of tiny skinned strips merged into single draws, the disc's files sized from the FST, the renderer's profiler |
| pc-megamanxcm | Mega Man X: Command Mission (2004, GameCube, PAL) | the GameCube: its discs, its clocks and the IPL's globals, the disc drive at its registers, 16 MB of ARAM, the controllers on SI, the GameCube's AX micro-code under MusyX (samples in ARAM, per-millisecond updates), drive and DMA times as long as the console's |
Some pieces were first written for two earlier Wii studies (The Last Story and Final Fantasy Crystal Chronicles: The Crystal Bearers): the disc extractor, the GX texture decoder, TPL, U8 and DSP-ADPCM.
A port takes wiikit as a git submodule at wiikit/, so that
python -m wiikit.… works from the port's root and the recompiler finds
wiikit/runtime next to itself:
git submodule add https://github.com/vs-sr-dev/wiikit.git wiikit
git clone --recursive <port> # or: git submodule update --initEach port pins a wiikit commit and moves it forward deliberately.
python -m wiikit.disc GAME.wbfs --info # .iso, .wbfs, .rvz, .wia; Wii or GameCube
python -m wiikit.rvz GAME.rvz # its tables and compression
python -m wiikit.disc GAME.wbfs --extract build/extract
python -m wiikit.dol build/extract/sys/main.dol --info
python -m wiikit.ppc build/extract/sys/main.dol --at 80006124
python -m wiikit.cw 'process__19CSongMoveBlockActorFf'
python -m wiikit.u8 ARCHIVE.arc
python -m wiikit.tpl TEXTURE.tpl build/out
python -m wiikit.recomp GAME.elf --out build/recomp [--hooks game-hooks.txt]
python -m wiikit.recomp main.dol --out build/recomp --symbols names.tsv # stripped
python -m wiikit.profile build/recomp-build/wiiboot.exe run.err [build/symbols.tsv]Building recompiled code needs CMake, Ninja, a C++20 compiler (clang from
MSYS2 so far) and SDL3; running it needs OpenGL 4.5. The generated
project includes runtime/runtime.cmake; a port adds its own targets and
its own layer (RtGameLayer) with -DWIIKIT_EXTRA=file.cmake.
| Layer | Question it answers | Now | Next |
|---|---|---|---|
| 1. Recognise | What is on this disc? | disc --info: game id, Wii or GameCube, partitions, WBFS usage; rvz: RVZ/WIA tables |
a fingerprint: magics, SDK library dates, middleware found by symbol or string (Scaleform, Wwise, Bink, NW4R, Home Button) |
| 2. Extract | Turn standard formats into standard files | disc (ISO, WBFS, RVZ and WIA; AES, FST; GameCube discs), u8, tpl, gxtex, dsp |
palette formats C4/C8/C14X2 in Python (the runtime's C++ gxtex has them), BRSTM/BRSAR, THP, BNR |
| 3. Map code | What does the code do, where? | dol (DOL and ELF, one address map, symbols, --same-as, --libs), cw (CodeWarrior demangler), ppc (Gekko decoder with paired singles, disassembly, callers, lis/addi and SDA xrefs, instruction census) |
ppc --mix without symbols; sig: library functions named by signature in stripped executables (Dolphin's .dsy, symbolised ELFs) |
| 4. Translate | Turn Gekko code into C++ | recomp: units and entry points to a fixed point, switch tables, one C++ function per entry, dispatch table, CMake project; stripped executables: function discovery (recomp/discover.py), switch tables sized from the code, names and hooks from a symbols.tsv |
faithful single-precision rounding |
| 5. Runtime | Replace the hardware | ppc.h (the CPU model), core/mem (guest space, dispatch, hooks), os (guest threads on host threads, interrupts, time), hw (PI, VI, DSP micro-codes, AI, EXI, SI, Hollywood; for a GameCube game the disc drive at its registers, 16 MB of ARAM, the controllers on SI), gx (FIFO parsing, vertex and texture decoding, the record), gxtex, gxshader (TEV and XF to GLSL), video (SDL3 window, OpenGL 4.5 renderer), ios + disc (IOS HLE at the IPC registers), sysconf, boot, wpad (the Wii Remote on the mouse and keys; the Classic Controller on keys and SDL gamepads, up to four, in KPAD's status and WPAD's own samples; the connect and extension callbacks), ax (the AX micro-code, the Wii's and the GameCube's: its 5 ms frames, its per-millisecond updates, samples in ARAM) and audio (SDL3 output), a port's own layer (RtGameLayer), a sampling profiler, and wiiboot (the console from the disc: a GameCube game gets its 162 MHz bus and the IPL's globals, no IOS) |
memory cards on EXI, the GameCube's second DSP task (the memory card's unlock), the early (2006–07) AX micro-code, locked-cache DMA, synthetic Remote motion (swing, thrust, shake) from mouse gestures, fog and Z textures, Dolphin as the oracle |
How the recompiler, the runtime, the renderer and the audio work is written up, with Victorious as the case, in pc-victorious's 09-recompiler, 11-runtime, 12-renderer and 13-audio.
- Pure Python, no dependencies, for layers 1–4; the runtime (layer 5) is
C++20, and its one dependency is SDL3, for the window and the sound. The
one exception is speed, not function:
aesuses pycryptodome when it is installed (1.3 MB/s in pure Python, a whole disc in about 18 minutes, against seconds), and gives the same bytes either way. - Every claim is checked on a real disc before it goes in.
- Game knowledge stays out.
- Every change is checked on every port before it goes in: each still gets as far as it did (Victorious boots, plays and sounds; the others as far as they have come).
| Module | Checked by |
|---|---|
aes |
the FIPS-197 C.1 vector; equal to pycryptodome on a random cluster and on disc data (python -m wiikit.aes) |
disc |
Victorious (WBFS): re-extraction equal to the previous extractor for all 46 files. A PAL disc (ISO): 3 751 files extracted. A GameCube disc (RVZ): 4 246 files, 1.35 GB, in 16 s, the offsets where its FST puts them |
rvz |
a PAL disc as RVZ (zstd 19, 128 KiB chunks): every raw region, junk filler included, equal to DolphinTool's ISO; the whole DATA partition extracted from the RVZ equal to the ISO's, 3 759 of 3 759 files, in 57 s |
dol |
--same-as: all ten DOL sections equal in Victorious's ELF |
cw |
20 619 of 20 619 function names demangled, including templates, conversion operators and anonymous namespaces |
ppc |
Victorious: 1 658 815 instructions, none undecoded; equal to capstone on every non-paired-single instruction up to standard aliases; paired-single fields checked by prologue/epilogue symmetry in 1 393 functions. A stripped 2007 DOL: 744 768 words, 14 non-zero words undecoded (data in text) |
gxtex |
The Last Story: byte-identical to textures Dolphin dumped from the running game |
tpl, u8 |
the Home Button archives (105 files; the icon decodes correctly) |
dsp |
Crystal Bearers' audio, by ear and spectrogram |
recomp/discover |
Victorious's DOL against its ELF: 20 612 of 20 619 function starts found; 0 units cut through a function (no branch of any function crosses a unit start); 353 units of dead code after tail calls, harmless. A stripped 2007 PAL DOL: 8 738 units, 332 switch tables sized from the code (3 unresolved), no branch to an unknown target. Victorious's generated C++ unchanged, 201 of 201 files |
recomp + runtime |
Victorious: all 20 653 functions compile and link; the game's own sprintf, strtod, 64-bit division, sin/cos, qsort with game comparators, PSMTX* paired-single matrices and memcpy/memset, run natively, match the host in 15 of 15 tests |
runtime (hardware) |
Victorious boots from __start to its main loop: the SDK's own OSInit report, its anti-modchip device check, the Bink logos, Wwise on AX, frames of GX commands |
runtime (renderer) |
Victorious, by eye: the Wii Strap screen, the Bink logos (indirect textures), the Scaleform title and menus, the episodes in 3D, at 30 frames a second. Draws in a row merged into one call: a stripped 2009 game's battles, 23 000 draws a frame (skinned models in strips of 4 to 10 vertices) in about 1 000 calls, 20 frames a second to 27-30; the other ports' boots unchanged |
runtime (audio) |
Victorious, by ear: music, voices, effects, the Bink movies' sound, the rhythm games |
runtime (Remote) |
Victorious, by hand: its first episode played with the mouse, the pointer under the mouse, the rhythm game's presses, holds and shakes |
runtime (GameCube) |
a stripped 2004 GameCube game, from __start to its opening scenes: the SDK's own OSInit report ("Console Type : Retail 3", 24 MB), its disc read through the drive's registers, ARAM sized by ARInit's own check, its controller found, polled and played with scripted presses, its logos, title, menu and real-time scenes at 50 frames a second |
runtime (GameCube audio) |
the same game, by ear: its music streamed through ARAM, its effects and voices, its opening movie's sound; a movie that waits for its sound now plays |
runtime (Classic Controller) |
a stripped 2010 game that learns of controllers only from the connect callback, by hand: played with an Xbox One pad, and with the keys, the mouse and the pad at once. A stripped 2009 game that reads the Classic from WPAD's raw samples (the 2007 KPAD's copy of them, WPADGetLatestIndexInBuf): played with the same pad into its first battle |
ppc.textrenders standard simplified mnemonics (sub,clrrwi,mr.,crclr…). Tools that need the canonical operation usedecode()'sopand fields, never the text.- Constant tracking in
ppc.Trackeris linear through a function and ignores control flow. That is enough for CodeWarrior'slis/addipairs. ppc --mixneeds function symbols: on a stripped DOL it counts nothing.- Discovery misses assembly reached only by an address built in code (exception handlers) and tiny functions reached only by pointer: the runtime reports them as unknown targets, to be given as seeds.
wiikit grew inside pc-victorious from its first session to its seventh and
was split out with its history (git subtree split) when a second port
began. The commit messages of that period describe Victorious's sessions;
the changes in them are wiikit's.
MIT — see LICENSE. wiikit contains no game data and no Nintendo code; it reads and replaces, it does not include.