PORTCLUB
Cabecera de Wario Land 4 (Recomp)
Port nativo

Wario Land 4 (Recomp)

Port nativo de Wario Land 4 (Recomp) para PC.

Wario Land 4 (Recomp)

Port nativo basado en lildavid/warioland4.

README del repositorio

WarioLand4Recomp

A static recompilation of Wario Land 4 (Game Boy Advance, US/EU) for Windows x64.

The Game Boy Advance CPU is an ARM7TDMI. This project translates the cartridge ROM into C++ at build time and runs the result as an ordinary native x64 program, so the game plays at native speed with no emulator in the loop. The GBA BIOS is recompiled the same way and executed as code — it is never stubbed and never HLE'd on the correctness path.

This repository contains no game code, no ROM and no BIOS. The ROM is a build input you supply locally; see baserom.md for the exact bytes required.


Experimental Preview

Experimental Preview
Full-game compatibility has not been exhaustively tested.

This is the project's own wording, quoted verbatim from the release-phase handoff notes (§6). Only the behaviour listed under Status and in docs/COMPATIBILITY.md has been verified by a bounded, repeatable run. Everything else — later levels, bosses, the endgame, edge cases, long play sessions — is untested, and no completion percentage is claimed anywhere in this repository.


Status — what is verified

verified means a bounded command exists and its evidence is named in docs/COMPATIBILITY.md. It never means "the whole game works".

verifiedevidence
ROM / BIOS identity gateyes — enforced before every route, build and bootbaserom.md, docs/ROM_IDENTITY.json, tools/validation/check-identity.ps1
Boot (recompiled BIOS entry path)yes — 120-frame routeroute boot, logs/routes/boot-120-last.png
BIOS logo, intro cutsceneyes — 120 / 720-frame routesroutes bios-logo, intro-cutscene
Attract demo, title, file selectyesroutes attract (3,600), title (5,400), title-input (6,000)
New game → difficulty menu → level entryyes — 8,600-frame routeroute new-game, logs/routes/game-8600-last.png
Host input reaches Wario inside a level and moves himyes — the only pinned route that shows thisroute host-walk-13500 (13500 frames, tests\input\gameplay-right.keyinput.txt)
A / B / DOWN verbs (jump, punch, dash, crouch)yes — each proved by an A/B frame comparison against a no-input controlroutes verb-jump-12090, verb-attack-13475, verb-dash-13500, verb-crouch-13500; re-run by tools/validation/verb-ab.ps1
Taking damageyes — gHeartMeter.current 4 → 3 at vblank 12,561, corroborated by HUD pixelsroute heartloss, logs/routes/heart-hud-compare.png
Water reaction (the reaction axis is live)yes — gWarioData.reaction = 1 at vblank 10,063, cleared at 10,095routes reaction-water, reaction-water-end; ten value-gated probes in reaction-enum
SRAM save → exit → restart → loadpartly — the round trip passes as state visibility, not as a byte-for-byte match against a known-good saveroutes save-write (12,000), save-load (6,000)
Determinism (same build, two processes)yes — identical counters and byte-identical frames, headless vs windowedroutes win-9000, win-19000, left-12139
Dispatch coveragereported honestly as NOT_STATIC — 7–13 dynamic IWRAM PCs remain, see Coverage honestyevery run's coverage JSON, recomp_coverage_AWAE.json
Automated smoke routeyes — 7 casestools/validation/smoke.ps1
Audio (music + SFX)no — the mixer output was measured on the attract route only; nothing was verified through a speakersee docs/VALIDATION.md §7
Player-driven room or level transitionno — no host-input route has changed the room; the transitions seen so far are the game's own attract demoroutes explore-1, explore-2, door-1, switch-hunt, switch-hunt2; positive control demo-room
Transformation (变身) under player controlno — none of the ten reaction values was reached by a host routeroute reaction-enum
Completing a level, Boss, endgameno — never run—

The full verified / measured-only / unverified matrix, with one evidence pointer per row and the commands to reproduce them, is docs/COMPATIBILITY.md.


Requirements

You need the original cartridge image and the GBARecomp generator that performs the translation. The generator is not vendored here; it is obtained from its public upstream repository at a pinned commit and then built locally. Setup is one script, and the pinned identity, the submodule revisions and this project's framework patches are all recorded in docs/FRAMEWORK_PIN.json. See Build.

itemvalue
base ROMWario Land 4 (USA, Europe).gba, placed in the project root (the only *.gba there)
ROM size8,388,608 bytes (0x00800000, 0xFF-padded)
ROM MD55fe47355a33e3fabec2a1607af88a404
ROM SHA-1b9fe05a8080e124b67bce6a623234ee3b518a2c1
ROM SHA-256d16c7bf6e62bb84049fff1b387108fbd1e6e2cd38ca994ab5310dd9cbf9ba414
GBA BIOSbios/gba_bios.bin, 16,384 bytes, SHA-1 300c20df6731a33952ded8c436f7f186d25d3492
save typeSRAM, 32,768 bytes (SRAM_V112, verified from the cartridge)

The Japan release (AWAJ) is a different image and is not supported. Any hash mismatch aborts the run — the project will not boot, generate or build against a ROM it cannot identify.

Tooling (discovered, not hardcoded to a single location):

  • CMake 3.20+ and Ninja (or the MSVC generator)
  • MinGW-w64 GCC on PATH (GCC 16.1.0 was used here); override with MINGW_ROOT or pwsh tools/regeneration/build-host.ps1 -Toolchain <mingw-root>
  • SDL2 2.32+ development libraries, i.e. a prefix containing x86_64-w64-mingw32/include/SDL2/SDL.h; discovered from .deps/sdl2, from a sibling _sdl2/SDL2-*/ directory next to this project, or from an MSYS2 prefix. Override with build-host.ps1 -Sdl2Root <sdk>
  • Python 3.10+ for the validation tooling (standard library only)
  • PowerShell 7 (pwsh) for every script in tools/

Build

The framework source is fetched from upstream, not vendored here. A fresh clone has no reference/gbarecomp; step 1 clones mstan/gbarecomp at the commit pinned in docs/FRAMEWORK_PIN.json, checks out the three submodule revisions that commit requires, and applies this project's framework patches from patches/gbarecomp/. Those patches are small and necessary — they add the codegen tail macros the ARM core emits, the keypad-IRQ path, the SRAM_F_V save signature and a 64 KiB flash-size fix. docs/FRAMEWORK_PIN.json records every patch by SHA-256.

Only the ROM and the BIOS are yours to supply; nothing else in the build is machine-specific.

# 1. clone the framework at its pinned upstream commit, then apply the patches
pwsh tools/regeneration/setup-framework.ps1

# 2. build gba_recompile. regen.ps1 needs it, so this step is not optional
pwsh tools/regeneration/build-framework.ps1

# 3. translate ROM + BIOS into C++ under generated/
pwsh tools/regeneration/regen.ps1

# 4. build the host executable
pwsh tools/regeneration/build-host.ps1

# 5. run the smoke suite
pwsh tools/validation/smoke.ps1

Step 1 needs network access the first time; it refuses to run in place of an existing reference/gbarecomp unless you pass -Force, and it re-verifies every patched file against the SHA-1s recorded in docs/FRAMEWORK_PIN.json. -VerifyOnly re-checks an existing checkout without touching it, and -Build folds steps 1 and 2 into one command.

The executable lands at build/host/WarioLand4Recomp.exe, together with the DLLs it needs (SDL2.dll and the MinGW runtime libgcc_s_seh-1.dll, libstdc++-6.dll, libwinpthread-1.dll).

regen.ps1 clears generated/cart and generated/bios before generating, so a plain run always produces a plain build. Add -SymbolOverlay to import decompilation names and data extents as symbols; that path is behaviour-preserving and is proved by an A/B comparison in docs/VALIDATION.md.

build-host.ps1 -Fresh configures from scratch; -Toolchain and -Sdl2Root override the discovered locations.

Running it

Player Mode (Native GUI Launcher)

Double-clicking WarioLand4Recomp.exe launches the modern native recomp-ui graphical launcher.

  • Game page: Select your legal GBA ROM and BIOS dump with automatic SHA-1 and size validation. Supports "Skip Launcher" and "Reset Configuration".
  • Input page: Customize keyboard bindings and SDL2 GameController gamepad mappings with full hotkey support.
  • Video & Audio page: Adjust integer scaling, windowed/fullscreen modes, and volume settings.
  • Zero Compiler Dependency: Dynamic IWRAM code generated on the stack (0x03007D0C..0x03007D90) is transparently handled via runtime interpreter fallback hooks (0x03007D00..0x03007E00), so players do not need GCC, MSYS2, CMake, Ninja, or Python installed.

Command-Line & Automation

# Direct launch bypassing launcher GUI:
build\host\WarioLand4Recomp.exe --rom "Wario Land 4 (USA, Europe).gba" --bios bios\gba_bios.bin

# Reopen launcher if previously set to skip:
build\host\WarioLand4Recomp.exe --launcher

# Enable developer mode (self-heal diagnostics, miss logging):
build\host\WarioLand4Recomp.exe --dev --rom "Wario Land 4 (USA, Europe).gba" --bios bios\gba_bios.bin

Controls

The default bindings match the rest of the recomp ecosystem, so they are the same before any configuration file exists:

GBA buttonkey
AX
BZ
LC
RV
StartEnter
SelectRight Shift
D-padarrow keys
FullscreenAlt+Enter
PauseShift+P
TurboTab
Performance displayF

They are rebindable: the runtime reads keybinds.ini ([player1], scancode names) for the button map and config.ini ([KeyMap]) for the system hotkeys. Both files are optional and live next to the runtime's launcher configuration.

Saves

The runtime uses SRAM. Unless --save-path is given, the save file is the ROM path with its extension replaced by .sav — for the standard setup, Wario Land 4 (USA, Europe).sav next to the ROM. The save type itself is never taken from a filename: it is detected from the cartridge image (SRAM_V112 at ROM offset 0x283EF8) and the runtime reports save=SRAM signature=SRAM_V at boot.

Windowed vs headless

Every route above is headless by default. A windowed run reads the real keyboard, and in this game any host button held during the attract demo re-phases it — so a windowed run must be driven with an explicit input replay (recorded as G10 in docs/KNOWN_ISSUES.md). run-route.ps1 is headless unless -Window is passed; -Window plus -FrameDumpStart/-FrameDumpCount is how mid-run filmstrips are captured.


How it works

Wario Land 4 ROM  ──gba_recompile──▶  generated/cart/*.cpp   (8 shards, one dispatch table)
GBA BIOS         ──gba_recompile──▶  generated/bios/*
                                        │
                                        ▼
   src/main.cpp ──▶ gbarecomp runtime ──▶ WarioLand4Recomp.exe
  • Static recompilation, not emulation. Each ROM function becomes a native function. A per-instruction resume table (generated/cart/dispatch_table.cpp) keeps hand-written C, BL jumps and interrupt returns connected.
  • BIOS is sacred. SWIs, IRQs and DMA run through recompiled BIOS bytes through the same dispatch table. The boot intro is a presentation choice, never a fast-forward.
  • Honest self-healing. A dispatch miss is never silent. The interpreter may bridge it for the rest of the session, but it is logged loudly, recompiled on the fly, and written to recomp_master_misses_AWAE.toml.frag for review. Misses are never auto-merged into game.toml; a human reviews them, merges the real ones, regenerates and re-runs.
  • Coverage honesty is load-bearing. The runtime prints what was interpreted and what was native. This project is currently NOT_STATIC; that number is reported honestly in every run rather than rounded up.

Coverage honesty

The remaining dispatch misses are 7–13 dynamic IWRAM addresses in 0x03007D0C..0x03007D90 — code the cartridge generates onto its own stack at runtime. With a cold cache the strict gate aborts on exactly that stub (pc=0x03007D1C, runtime_arm: STRICT_STATIC dispatch miss …), not on undiscovered cartridge code: the 62 cartridge-ROM misses were reviewed and merged. No static declaration can cover the dynamic ones, which is a framework limitation (F6), not a configuration error. Declaring that address range explicitly was tried and reverted: it made every long route hang.


Validation

scriptwhat it does
tools/validation/smoke.ps1the release gate: boot, attract, title input, controller A/B, save round trip
tools/validation/check-identity.ps1ROM / BIOS / decomp / framework / toolchain identity audit
tools/validation/run-route.ps1runs one bounded route, verifies the ROM hash first, writes coverage JSON + miss fragment into logs/routes/
tools/release/publication-audit.ps1the publication boundary: no ROM, no BIOS, no generated code, no personal paths, no secrets
pwsh tools/validation/run-route.ps1 -Tag boot-120 -Frames 120
pwsh tools/validation/run-route.ps1 -Tag new-game -Frames 12000 -InputReplay tests/input/new-game.keyinput.txt
pwsh tools/validation/smoke.ps1 -Quick      # skips the three 13,500-frame routes
pwsh tools/release/publication-audit.ps1 -Json

tools/validation/smoke.ps1 runs a fixed set of seven cases and checks three things per case: the process exited 0 and wrote a coverage JSON, every dispatch miss is inside the documented dynamic set, and the final frame is byte-identical to the recorded one. The recorded expectations live in tests/routes/smoke-expectations.json; after a change that legitimately alters a frame, re-record with tools/validation/smoke.ps1 -Record and review the diff instead of hand-editing the file.

The route catalog — frame budget, window mode, extra arguments and the observation each route must produce — is tests/routes/routes.csv; docs/VALIDATION.md §4 describes them and §5 carries the numbered rules learned from failures. Input traces in tests/input/ are frame-indexed KEYINPUT registers in active-low form, so a route's inputs are exact and reproducible. Always confirm a replay was actually loaded: Select-String -Path logs\routes\<tag>.log -Pattern 'input_replay=' must read input_replay=ENABLED path="…" events=N; DISABLED means the trace was dropped and the run proves nothing.

Windowed runs can also be observed from outside the process (-Window -Frames N -TcpObserve <port>), which is how the coverage questions in docs/KNOWN_ISSUES.md were answered.


Repository layout

pathpublishablecontents
src/yesthe game-specific host layer — main.cpp, 18 lines
game.tomlyesper-game recompiler configuration: identity gate, code copies, jump table, extra functions
symbols/yesIWRAM symbol map and decomp-derived symbol metadata
tools/regeneration/yesbuild and regeneration scripts, including setup-framework.ps1 (framework acquisition)
tools/validation/yesroutes, probes, smoke suite
tools/release/yesthe publication audit
patches/gbarecomp/yesthis project's framework patches, applied by setup-framework.ps1 and SHA-256-pinned
tests/yesthe route table, expectations and input traces
docs/yesidentity record, framework pin, progress, known issues, validation rules, compatibility matrix
reference/gbarecomp/noframework checkout created by setup-framework.ps1 (upstream commit + local patches)
reference/tomlplusplus/toml.hppyessingle vendored MIT header, the only tracked file under reference/
third_party/nooptional local clone of the decompilation, used as text — not needed for the shortest build, and its .git and tools/agbcc submodule are not kept
generated/noROM-derived C++ — regenerate, never commit, never hand-edit
build/, logs/, recomp_cache/nobuild trees, run logs, self-heal cache
roms/, saves/, bios/nothe ROM, the save and the BIOS dump

generated/ is an artifact, not source. Problems are fixed in the framework, game.toml or the symbol metadata and then regenerated; a hand-edited generated/recompiled_*.cpp is a defect, not a fix.


Known limitations

The short list; every entry is quoted with its evidence in docs/KNOWN_ISSUES.md.

idsummary
F1no non-interactive asset picker; without --rom/--bios the runtime opens a modal dialog and blocks
F2the self-heal compiler is hardcoded to C:/msys64/mingw64/bin/g++.exe; set GBARECOMP_HEAL_CXX
F6dynamic RAM code cannot be declared, so coverage can never read FULLY_STATIC
G10in a windowed run the live SDL keyboard can silently re-phase the attract demo — always drive a windowed route with an explicit input replay
G11262 named decompilation functions are unplaced because placing them needs a decompilation build (arm-none-eabi-objdump/agbcc), which this environment does not have
G14the player-driven route is confined to room 0, and no host-input route has produced a room change (the game's own demo does traverse room 0 — see G27)
G20rooms are linked by a 12-byte exit-record table, and the byte that selects the link index is ordinary writable IWRAM
G21every Thumb literal-load offset in this ROM is decoded two different ways — a high-priority open framework question
G23the attract demo injects its own button stream into the input buffer by DMA3 from inside the room loader
G24变身 is gWarioData.reaction at 0x03001898, and no host-input route has reached any transforming value
G25a fresh save has exactly one world-map node, and the ROM's own graph agrees
G29RESOLVED — the framework is no longer a local-only snapshot. It is cloned from mstan/gbarecomp at a pinned public commit and patched by tools/regeneration/setup-framework.ps1; see docs/KNOWN_ISSUES.md and docs/FRAMEWORK_PIN.json
retractionroute traverse-13500 demonstrates the attract demo's traversal, not host input (round 34, 2026-10-08). It stays pinned because its frames are stable, but it proves nothing about input — host-walk-13500 is the case that does.

Licence

This project's own hand-written material is under the PolyForm Noncommercial License 1.0.0 — see LICENSE.

It is a recompilation project and is not a ROM distribution: the ROM, the BIOS and all ROM-derived generated code stay local and are not committed, published or redistributed here. You must supply your own legally obtained cartridge image.

Third-party components (GBARecomp, toml++, SDL2, the MinGW runtime, and the decompilation used as reference) keep their own licences — see THIRD_PARTY_NOTICES.md.

Documentation

documentcontents
baserom.mdthe required ROM/BIOS bytes and every identity gate
docs/COMPATIBILITY.mdwhat is verified, what is measured only, and what is untested — with an evidence pointer per row
docs/ROM_IDENTITY.jsonthe raw identity record
docs/FRAMEWORK_PIN.jsonthe pinned framework revision and dependency hashes
docs/KNOWN_ISSUES.mdframework (F) and game (G) issues with evidence
docs/VALIDATION.mdthe validation doctrine, the numbered rules and the evidence index
THIRD_PARTY_NOTICES.mdevery third-party component, its licence and the notice you must ship
tests/routes/routes.csvevery registered route and what it must observe

Comentarios (0)

Inicia sesión para dejar un comentario.

Sé el primero en comentar sobre este juego.