DKC3Recomp

DKC3Recomp brings Donkey Kong Country 3: Dixie Kong's Double Trouble! to PC.

README

DKC3Recomp

A native recompilation of Donkey Kong Country 3: Dixie Kong's Double Trouble! (SNES, USA, En/Fr) built the way DKC2Recomp was built: the game's code is statically recompiled to C by snesrecomp from a bank configuration derived from a public disassembly, the shared snesrecomp runtime executes anything the analysis cannot prove through its 65816 interpreter, and project-owned hosts present the game natively on macOS and Windows.

You must supply your own DKC3 ROM. The supported image is the headerless North American (En,Fr) release, 4 MiB, SHA-256 2277a2d8dddb01fe5cb0ae9a0fa225d42b3a11adccaeafa18e3c339b3794a32b. No ROM data, generated code, or extracted assets are stored in this repository.

Status

Bring-up. See docs/BRINGUP.md for the dated record of what runs and what has been verified, and docs/WIDESCREEN_GUIDE.md for how the widened presentation works and how its defects are diagnosed and fixed. Levels present wide at 16:10, 16:9, and the selectable 21:9 ultrawide mode with DKC2Recomp's terrain reconstruction driven by DKC3's own level map, verified on Lakeside Limbo. The 21:9 option uses the nearest symmetric width that preserves the shared runtime's safe sprite coordinate range: 446x224, or approximately 20.91:9. Screens the map does not cover center the native frame between black margins. The placed-object spatial scan and final activation checks, the sprite renderer culls, and the static banana arcs are widened to the presented view by scripts/apply_dkc3_widescreen_overrides.py, verified on Lakeside Limbo. The scan includes adjacent 256-pixel cells so 21:9 objects, including objects in quick saves made by older builds, do not wait for a cell boundary to activate. Murky Mill's HDMA-windowed BG3 light cones are evaluated across the physical widescreen span instead of being clipped and repeated at the native edges, verified at the reported quick-save state in both 16:10 and 16:9. Floodlit Fish's underwater BG3 color-math composition now receives its subscreen tint across transparent side-margin pixels without clipping the reconstructed BG1 terrain at the native edges, verified at the reported quick-save state in 16:10, 16:9, and 21:9 with an unchanged 4:3 frame. KAOS's body layer, enabled partway down the frame by HDMA, now uses its complete object tilemap in the wide margins. This fixes the right-edge body cutoff and the repeated fragment at the left edge in the reported boss save. The streamed waterfall layer now decodes its authored columns into the wide margins, checked against the native tilemap every frame. Waterfalls keep their world positions after scrolling instead of disappearing at one edge and repeating at the other, verified at the reported save in all wide aspects. Bleak's snowball arena now preserves Mode 2's correct background/sprite order, so the snowman appears in front of the distant snowbank and behind foreground cover. Its bounded background maps fill 16:10, 16:9, and 21:9 using their hardware wrap, with the native center and gameplay unchanged by widening. Pothole Panic's cave now fills the wide view from its authored level map. Its shape-1 layout uses 32 metatile rows per column, twice the height of the previously supported horizontal layout.

A second layer that streams a strip of the level map, Riverside Race's reflection under the water line, is served in the margins from the terrain store at the row offset its rows prove every frame, while its static underwater backdrop wraps as a plane judged by row-level write tracking; and the first visible row of the margins now decodes like the rest instead of being blanked on tile boundaries. See docs/BRINGUP.md for the evidence.

Native macOS release

The v0.0.6 release fixes the Windows menu bar staying on screen in fullscreen, restores the requested window size under the menu bar, keeps the game DPI-aware when the launcher is skipped, and makes the Windows CMake configure work again; it carries the unchanged v0.0.4 Mac archive below. The Mac source, menus, and display-link pacing are retained; the Mac binary has not been rebuilt for these Windows-focused releases.

Download DKC3Recomp-v0.0.4-macOS-arm64.zip from Releases, extract it, and open DKC3Recomp.app. Select your own legally obtained North American (En,Fr) ROM in the launcher; the ROM stays at its original path and is never copied into the application bundle.

The published v0.0.4 bundle includes the Floodlit Fish tint fix, the river's second-layer reflection and backdrop in the margins, and the fix for the strip that flashed at the top of the margins; v0.0.3 carried 21:9 and the adjacent-cell placement activation fix.

This release is for Apple silicon running macOS 26 or newer. The app is ad-hoc signed rather than notarized, so if Gatekeeper blocks the first launch, Control-click the app in Finder, choose Open, and confirm once. The project is still in bring-up: the tested boot, launcher, save-state, and Lakeside Limbo paths work, but full-game compatibility is not yet claimed.

Building on Windows

The Windows x64 SDL build has passed all 23 project tests, a 4,801-frame headless run, and a packaged launcher/visible-game smoke check. Full-game completion and hardware/controller coverage are not claimed.

Requirements: Visual Studio 2022 with Desktop development with C++, CMake, and Python 3. Initialize the Git submodules before building. In PowerShell:

python scripts/generate_snesrecomp.py --analysis-backend python --rom 'C:\private\dkc3.sfc'
cmake -S . -B build-windows -G 'Visual Studio 17 2022' -A x64 `
  -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DSNESRECOMP_SDL_BACKEND=SDL2 `
  -DDKC3_ROM='C:\private\dkc3.sfc'
cmake --build build-windows --config Release --parallel 4
ctest --test-dir build-windows -C Release --output-on-failure

Run build-windows/Release/DKC3RecompSDL.exe. This is the shared SDL2/OpenGL host with 4:3, 16:10, 16:9, and 21:9 presentation, reconstruction upscaling, overlay, and audio rate control. Keep SDL2.dll and the assets directory beside the executable when moving it. Select your own ROM in the launcher. The separate DKC3Recomp.exe in the build directory is the legacy Win32 host; portable packages use the SDL executable renamed to DKC3Recomp.exe.

The game window now has native Game and View dropdown menus matching the Mac's game commands: Pause / Settings, Quick Save, Quick Load, fullscreen, nearest/bilinear scaling, and all four aspect ratios. Windows also exposes reconstruction scaling, all five dither/edge-reconstruction levels, level-edge policies, and screen models in View submenus. Selections apply live and are remembered beside the executable. Game > Pause / Settings > Settings opens the same detailed sliders and dropdowns as the Mac overlay, including reconstruction strength, softness, shading, audio, and volume; Controls and Assist Tools are adjacent tabs. These menus appear after launching the game, not on the pre-boot ROM picker. Ordinary Windows minimize/close commands replace the macOS-specific Hide and application-management commands. The Windows title bar, menu bar, and nested dropdowns use a dark theme. The product title is simply DKC3Recomp, without a pre-release label.

CMake fetches the pinned SDL 2.30.9 source when no SDL2 package is installed. The Python analysis backend avoids a Rust toolchain requirement. If cmake is not on PATH, use its full path from Visual Studio's bundled CMake tools. Private Windows smoke tests cover all four aspect modes with reconstruction, overlay, rewind, fast-forward, and quick-state save/load. ROM-free tests remain available with -DDKC3_BUILD_SNESRECOMP=OFF.

Building on macOS

Requirements: CMake, Ninja, SDL2 (brew install cmake ninja sdl2), Python 3, and Rust's cargo for the native analyzer (Python falls back when it is absent).

git clone --recurse-submodules https://github.com/elliotttate/DKC3Recomp.git
cd DKC3Recomp
python3 scripts/generate_snesrecomp.py --rom /private/path/dkc3.sfc
./build_macos.sh /private/path/dkc3.sfc

generate_snesrecomp.py verifies the ROM's hash, refreshes recomp/funcs.h, and emits the private recompiled units under generated/ (ignored by Git). build_macos.sh builds build/macos/DKC3Recomp.app and the headless runner, bundles SDL2, embeds the project icon, and ad-hoc signs the app. Open the app and select the ROM in its launcher.

Release builds enable interprocedural optimization when CMake's compiler and linker check succeeds, allowing optimization across the generated game code and runtime. Set -DDKC3_ENABLE_IPO=OFF when configuring to disable it; unsupported toolchains retain ordinary Release optimization. The project's runtime adaptations live as literal hunks under cmake/runtime-patches/; scripts/apply_dkc3_runtime_patches.py applies them to build-directory copies of the pinned snesrecomp sources at configure time and fails closed when an anchor moves (-DDKC3_RUNTIME_PATCH_DIR points a scratch build at another hunk directory, for example with tools/diagnostics/ added). They keep the scalar PPU's widescreen merge and composite off per-pixel branch chains, give the shared bus a direct path for plain cartridge ROM reads, stop the interpreter prefetching poll bytes it never consults, let the interpreter hand a compiled routine it reaches by jump to the compiled code whenever it owns the return frame that routine will pop (SNESRECOMP_LLE_JUMP_BOUNCE=0 restores the call-only behavior), re-interpret a compiled jump-table dispatch whose static table misses the live index instead of skipping the handler, and compile out audio reference diagnostics that a zero-valued trace define had left running on every sample. Tier-2 coverage journals are now opt in (SNESRECOMP_TIER2_CAPTURE=1), as is the stack-balance auditor (SNESRECOMP_STACKBAL_AUDIT=1). Measurements and validation limits are recorded in docs/BRINGUP.md.

On macOS a visible game window presents through a Metal layer driven by CAMetalDisplayLink on its own thread (runner/macos_metal_presenter.m): the emulation thread hands each frame to a mailbox and never enters the window system, which removes the WindowServer round trip inside the legacy OpenGL swap from the frame loop. The display link's callbacks also supply the pacing ticks, so frames stay locked to the refresh as before. The OpenGL path remains for the settings overlay, for hidden test windows and their drawable captures, and when DKC3_METAL_PRESENTER=0 is set.

The bank configurations carry exit-width contracts (exit_mx_at) and function splits that let the recompiler compile routines whose exits it cannot derive; they are tables in tools/ingest_dkc3_disasm.py (EXIT_MX_AT, FUNC_SPLITS) with the disassembly structure that justifies each, so a re-ingest reproduces them.

The SDL host gives the active player's game controller a short haptic pulse when a descending jump both defeats an enemy and rebounds upward. The trigger observes the enemy's actual defeated-sprite transition, so ordinary jumps, damage, swimming, and enemies defeated by unrelated causes do not activate it. The rumble request runs off the frame-critical thread. The pause menu's Settings tab shows the detected controller and rumble capability, provides a test pulse, enables or disables the feedback, and remembers that choice; setting DKC3_HAPTICS=0 when launching the app also disables it.

On macOS, optional MSU-1 replacement music can be enabled from Music > Choose MSU-1 Music Pack…. Choose PCM Folder for an extracted pack or .msu1 Archive for an archive, select it, then restart the app. The host accepts track-N.pcm, dkc3_msu-N.pcm, and dkc3_msu1-N.pcm naming, mixes the 44.1 kHz music with the game's original sound effects, and preserves the selected pack for later launches. Music > Disable Replacement Music restores the stock soundtrack after a restart. For development runs, DKC3_MSU1_PACK=/path/to/pack selects a pack, DKC3_MSU1_DISABLE=1 suppresses a saved selection, and DKC3_MSU1_GAIN=0.0..4.0 adjusts replacement-music gain. Quick loads and rewind keep the current music choice: older saves cannot bring the original soundtrack back underneath a replacement pack. Disabling replacement music also restores native sequencing when loading an MSU-era save.

For macOS audio isolation checks, the headless runner accepts an explicit DKC3_MSU1_PACK too (it never reads the app's saved pack preference). Combine it with DKC3_MSU1_GAIN=0, DKC3_SAVESTATE_INPUT, and DKC3_AUDIO_PCM to capture only the remaining stock sound effects; omit the gain override to capture the full replacement mix.

Headless validation

build-headless/dkc3_snesrecomp_headless /private/path/dkc3.sfc 600

runs the game for 600 frames with no window and prints frame, WRAM, VRAM, CGRAM, and OAM hashes with video and audio activity counts. The switches in runner/headless_main.c write frames as PPM (DKC3_FRAME_PPM, DKC3_FRAME_PPM_PREFIX with START/END/STEP), raw audio (DKC3_AUDIO_PCM), and memory dumps, restore an SRAM image or a quick save (DKC3_SRAM_INPUT, DKC3_SAVESTATE_INPUT), and replay scripted input (SNESRECOMP_INPUT_PLAY). DKC3_PPU_LEGACY=1 selects the independent scalar renderer for pixel-oracle comparisons. CMake applies the checked Mode 2 priority adaptation, with the other runtime hunks, to build copies of the pinned sources; the submodule stays unchanged.

Regenerating the bank configuration

The cfg files under recomp/ were derived from the H4v0c21 DKC3 disassembly by tools/ingest_dkc3_disasm.py, which needs a checkout of that project with its assembled dkc3.sym beside the bank sources:

python3 tools/ingest_dkc3_disasm.py --disasm /path/to/DKC3-Disassembly --output recomp

The output holds only names, addresses, bounded ranges, data regions, and finite dispatch contracts. The disassembly itself is GPL-3 and is not redistributed here; see THIRD_PARTY_NOTICES.md.

Tests

cmake -S . -B build-headless -G Ninja -DDKC3_ROM=/private/path/dkc3.sfc
cmake --build build-headless
ctest --test-dir build-headless --output-on-failure

The unit tests cover the host modules carried over from DKC2Recomp, the ingester, and the pacing-log tool. With DKC3_ROM set, the suite also boots the game headlessly and, on macOS, runs the app hidden.

Lineage

The host code, build scripts, and working rules come from DKC2Recomp; the game adapter and the ingester are new. Widescreen, save tools, and the diagnostics that DKC2Recomp accumulated are not carried over until DKC3 has its own evidence for them.