Toby Fox f555de201f feature: iOS Support (#116)
* build: add an iOS arm64 target

Selects iOS before macOS, since APPLE is true for both and the macOS
branch would otherwise claim an iOS configure and pick a memory backend
that cannot compile there.

Third-party libraries are pinned static on Apple targets. aurora picks
shared ones whenever BUILD_SHARED_LIBS is merely undefined, and its
extern/ tree unsets the cache entry while working around xxhash, so
passing it on the command line only survives one configure. On iOS that
was fatal rather than untidy: libpng linked as a dylib with an @rpath
into the build tree, which dyld cannot resolve inside a bundle.

User state lives in the container's Documents rather than a name-scoped
Application Support directory, so Config.toml sits beside the game data
a relative dvd_root resolves against, and both are reachable over file
sharing.

(cherry picked from commit ad7231fca3d55c5d7d1807c5accb6874d702111f)
(cherry picked from commit 76cc9594e02d5a858c0848b4b4e4e5a585151cc0)
(cherry picked from commit 80a6b53abd699942178fde2f16010d54dba9952b)

* ios: resolve the flat guest base at runtime

No fixed base works on every device. Probing an iPhone 17 Pro and an
iPad Pro M5 with the same 24-candidate sweep gave disjoint sets of free
4 GiB windows - 448 GiB only, against 12, 16, 20, 24, 32 and 48 GiB -
and the extended-virtual-addressing entitlement changed neither map. So
iOS takes whatever the kernel offers and publishes it, while every other
target keeps its compile-time constant.

The backend is the macOS one adapted twice over: <mach/mach_vm.h> is not
in the iOS SDK, so the vm_* calls are used instead, and the backing store
is an anonymous mapping rather than a file in /tmp, which the sandbox
denies.

g_requiresCheckedAccess is set from the real page size. Apple Silicon's
16 KiB host pages are coarser than the 4 KiB guest page, so page
protection alone cannot be relied on.

(cherry picked from commit 0677b83ed45e8b59d779d37cad172c737daf08eb)
(cherry picked from commit 9a8a80ffd4c3f87652e37208fc88afd0a94e6cf8)
(cherry picked from commit 2b7707e6226ee20a6c0564c0f9bbcdd06b511061)

* ios: pick a display explicitly and survive a second scene

A bare SDL_WINDOWPOS_CENTERED does not say which display it means. With
an external screen attached that left the window sized for one display
and the surface for another, which played audio over a black screen.
The primary display is now named outright and its bounds used.

SDL calls SDL_main from scene:willConnectToSession:, once per connecting
scene, and iOS creates a second scene when a display is attached. That
re-entered main while the first call was still inside the game loop: the
data sections reloaded, no static constructors ran the second time, and
aurora then failed to create a window. Declaring
UIApplicationSupportsMultipleScenes=false does not prevent the extra
scene, so the guard belongs here.

(cherry picked from commit d8b984cfbb8521f06776ae765131452c4380e473)
(cherry picked from commit d20895a98739c11f97de1222834859e2a55e3516)
(cherry picked from commit 492f57ef5122305b0ab63258a5a4efe960f5abc9)

* ios: add on-screen touch controls

A touch device has no pad and no F10, so without these it boots to a
screen nothing can drive.

The overlay polls SDL's finger list rather than using ImGui widgets,
whose SDL backend collapses touch to one emulated mouse - steering,
accelerating and drifting all happen at once. Input merges into
PAD__Read_HLE only when port 0 has no real pad, so a controller always
wins, and the overlay hides itself while one is attached.

Positions are held as a distance from a screen edge in units of screen
height. A fraction of width lands somewhere different on a 1.45:1 iPad
and a 2.17:1 iPhone. The d-pad is one cross with the direction taken
from the dominant axis, and the stick keeps the finger that grabbed it
until that finger lifts, so sliding past the gate does not drop steering
mid-corner.

Buttons use Zacksly's GameCube icons, CC BY 3.0, with attribution beside
them in the bundle. The dark backing is each glyph's own silhouette
tinted black, so the d-pad and the triggers are not given a disc they do
not have. They are decoded up front: doing it lazily made the frame the
controls appear on pay for every decode at once.

Tapping the FPS readout opens the settings bar, which is otherwise
unreachable - and the readout's tap target is cleared before the early
return when FPS display is off, or a stale rectangle keeps swallowing
taps meant for the fallback menu button.

PADIsInputBlocked comes back to aurora; it went with the WUP-028 revert
and the overlay needs it to avoid driving the guest while the bar is up.

(cherry picked from commit 79d3de3c0b9c883ff2ec34a5964498a475e91240)
(cherry picked from commit cf7a8edb3405d4f7d7fd189d19cdb2f746b190f9)
(cherry picked from commit 360261208c95616ec81d8f8c3d69efc3c776c54b)

* ios: build an unsigned ipa

CMake bundles iOS targets with its own default Info.plist, whose
CFBundleIdentifier is empty and which carries none of the iOS keys, so
what came out of the build could not be installed. It now gets a real
plist and is packaged as an .ipa.

Unsigned on purpose: AltStore, SideStore and LiveContainer sign on the
device with the user's own Apple ID, so anything applied here would only
be replaced. WiiCompiled.entitlements is a template for signing by hand,
which those tools ignore.

Packaging runs after the asset copies rather than before, or the archive
gets the binary and nothing else. The touch directory is cleared before
it is copied, since copy_directory merges and a removed icon would
otherwise be shipped.

(cherry picked from commit 5ce43949dbf27abff6782bc41e3be0ede3ac135b)
(cherry picked from commit 633e3e0aa2f7ea116741f27ec46e9c743be9cf9a)
(cherry picked from commit a4ae6f2e8bdf11596a89dca9607aba92713184be)

* docs: document the iOS build

Covers building the .ipa, sideloading it, the one entitlement the app
needs, and where the game data goes.

The configure line is the one that works: without CMAKE_SYSTEM_PROCESSOR
the Dawn package URL comes out as dawn-ios-.tar.gz and the build fails
on a 404.

(cherry picked from commit 08eefad3fd1cb5e8f79a144ac119bc65852e33e6)
(cherry picked from commit b7cd38e07eefc8af4c1c60ca411c009eec4e212a)
(cherry picked from commit 5a1650ef194f9da4ae8a68f1a9779b16ee118ca8)

* ios: stub out Discord presence

It connects over a Unix socket to a local Discord client, which iOS does
not have, so it can only ever fail and back off. Stubbed rather than left
retrying.

(cherry picked from commit 69aa5e5c297abe65188d391748b8fb11c57337d2)

* build: keep the macOS host tests off iOS and give the audit target libpng

The macOS test executables were gated on MKW_PLATFORM_MACOS, which iOS also sets, so a full iOS build tried to compile guest_flat_memory_macos.cpp against an SDK with no <mach/mach_vm.h>. The products-off audit compile picked up the touch overlay sources without the PNG include path.

* ios: tidy the touch decode and pad merge after review

The row-pointer buffer now lives in PngReader so a libpng longjmp cannot skip its destructor. PAD__Read_HLE no longer uses the port 0 error code to decide whether touch applies; keyboard bindings report PAD_ERR_NONE with no controller attached, and TouchPad::Read already checks for a physical pad.

* docs: tag the iOS build fence as sh

* ios: pin the CPU baseline instead of tuning for the build host

-mcpu=native tuned the phone binary for whichever Mac compiled it, and upstream clang rejects the flag outright when cross-compiling. iOS 17 implies the A12 and later.

* build: cross-compile the iOS products from Linux

* use online sdk instead of mac sdk to build on linux

* build: ios platform identifier

* build: build the ios ipa on windows too

* fix: strap cover ends too early on slower devices

* ci: compile the ios runtime on a linux runner

* docs: note the entitlement needed on devices with less than 4gb ram
2026-09-05 10:12:53 +02:00
2026-09-05 10:12:53 +02:00
2026-09-05 10:12:53 +02:00
2026-09-05 09:55:09 +02:00
2026-08-23 17:10:50 +02:00
2026-09-05 10:12:53 +02:00
2026-08-23 17:10:50 +02:00
2026-08-23 17:12:04 +02:00
2026-08-23 17:10:50 +02:00
2026-09-05 10:12:53 +02:00
2026-09-01 17:01:13 +02:00

WiiCompiled

A native PC port of Mario Kart Wii, made with static recompilation.

There's no emulator in the loop, no interpreter, no JIT, no PowerPC anywhere at runtime.

Important

There is no Nintendo code, no assets and no game data anywhere in this project or its releases. You need your own legally dumped copy of the PAL version of the game. Setup only ships the toolchain, the translation runs on your machine against your disc image, and nothing ever gets uploaded.

What is a github, I just want to play


What it does

Unlocked framerate with interpolation. The original game is hard-locked to 60 fps. The runtime can generate interpolated frames in between, so on a 120/144 Hz monitor things genuinely look smoother.

Warning

Interpolation is experimental right now and will show artifacts in specific scenarios.

Any aspect ratio you want. Drag the window bigger, wider, whatever, the camera adjusts live.

Native rendering via aurora. The graphics layer is built on aurora. Aurora is a source-level GameCube & Wii compatibility layer.

High internal resolution. Play at several times the console's resolution.

Music ducking. Start playing something else, Spotify, a YouTube video, and the game automatically mutes its own music until the other audio stops. Optional, if you'd rather it didn't. All audio that shows in your display media controls on your windows pc fall under this.

An in-game settings bar. Press F10 while the game window has focus:

  • Internal resolution
  • FPS counter
  • Controller assignment for all four ports
  • Full per-controller button mapping
  • Volume, instant mute, and the music ducking toggle

Everything you change is saved to Config.toml on the spot and restored next launch.

Real controller support. Controllers are fed to the game as a GameCube controller. Mappings are positional (south, east, west, north) rather than Xbox-labelled, so the same config makes sense on Xbox, PlayStation, Nintendo and generic SDL pads alike, and extra inputs like paddles, touchpads and share buttons show up when the hardware reports them. The official Wii U / Switch GameCube adapter (WUP-028) works too; as with Dolphin, on Windows the adapter must be switched to the WinUSB driver once (Zadig).

Real Wii Remotes over Bluetooth. Pair a Wii Remote with Windows (Settings > Bluetooth > Add device, press 1+2 or SYNC, leave the PIN empty) and the game reads it as an actual Wii Remote through KPAD: Wii Remote icons and prompts, Wii Wheel tilt steering, wheelies and tricks all come from the game's own motion code. Nunchuk and Classic Controller are real Wii extensions too: the game gets the Nunchuk's stick, C/Z and accelerometer, and the Classic Controller through KPADGetUnifiedWpadStatus with its own layout and icons, so its buttons do what the game says they do and no mapping is involved. Plug an extension in or pull it out mid-game and the game switches control scheme like on the console (the runtime patches SDL's Wii driver, which otherwise loses the remote for good on an extension change). Only the Wii U Pro Controller, which has no Wii-era equivalent, is fed to the game as a GameCube pad with Nintendo's layout. If a remote drops out or was switched on after launch, the runtime keeps rescanning Bluetooth until it comes back (F10 > Controller settings > Wii Remotes). SDL's read of the remote's factory accelerometer calibration often times out over Bluetooth (console.log then says "Using fallback accelerometer calibration") and it falls back to a nominal zero point, so the same menu has a one-button calibration (remote flat, buttons up) that removes the small tilt offset some remotes show.

Known limitations of the Wii Remote path:

  • No IR pointer yet: menus are navigated with the D-pad and A (the game treats the remote as pointing away from the screen).
  • Battery level is not reported to the game and the remote's speaker is not implemented.
  • Only the Wii Remote's own accelerometer is calibrated; the Nunchuk's uses SDL's fixed zero point.
  • The Classic Controller's L/R triggers reach the game as digital (full pull on click): SDL does not expose their analog travel.
  • Turn the Wii Remote support off in that menu if you use a Mayflash DolphinBar, which already presents the remote as a regular gamepad.

Requirements

  • Windows 10 or 11, 64-bit
  • GPU: GTX 1650 / RX 6400 / Arc A310 or higher
  • CPU: Intel Core i5-8400 / AMD Ryzen 5 2600 (4c/6c, ~3.5GHz+) or higher
  • About 20 GB of free disk space during installation (Final game size ~5 GB)
  • macOS 14 (Sonoma) or later on Apple Silicon
  • On macOS, Apple Xcode Command Line Tools (Setup opens Apple's installer when they are missing)
  • A clean, unmodified PAL RMCP01 disc image of Mario Kart Wii, dumped by you. ISO, GCM, GCZ, CISO, WBFS, WIA and RVZ are accepted.

Note

GPU/CPU minimums are set by driver support and D3D12/Vulkan feature requirements, not by the game's actual demands.

Only the clean PAL revision will work. Anything else (other regions, patched executables) is rejected outright.

Note

Nobody here will tell you where to get the game. Dumping your own disc is on you, and links to game files won't be provided or tolerated.

Installing

For an easy experience, use Wheel Wizard. Pick your clean PAL RMCP01 image under Settings, turn on WiiCompiled (beta), and hit install from the Home page. Wheel Wizard downloads the setup tool from this repo and walks you through install, updates and launching. The backend itself is deliberately command-line only, Wheel Wizard is a wrapper around it.

iOS

Requires macOS with Xcode. Build the WiiCompiled target for iOS arm64; the build writes WiiCompiled-unsigned.ipa next to the app bundle.

cmake -S runtime -B build-ios -G Ninja -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_SYSTEM_PROCESSOR=arm64 \
    -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_OSX_SYSROOT=iphoneos \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=17.0 \
    -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH \
    -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \
    -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \
    -DCMAKE_DISABLE_FIND_PACKAGE_absl=TRUE \
    -DAURORA_DAWN_PROVIDER=package
cmake --build build-ios --target WiiCompiled

iOS from Linux

The same .ipa builds on a Linux host with upstream clang and lld; no Theos, xtool or Xcode. Tested on Debian 13 with clang-19 lld-19 llvm-19 cmake ninja-build git. The only Apple piece is the iPhoneOS SDK, and xybp888/iOS-SDKs carries current ones:

git clone --depth 1 --filter=blob:none --sparse https://github.com/xybp888/iOS-SDKs.git /opt/iOS-SDKs
git -C /opt/iOS-SDKs sparse-checkout set --no-cone iPhoneOS26.5.sdk

Pass that directory as IOS_SDK (or copy iPhoneOS.sdk out of a Mac's Xcode to /opt/iPhoneOS.sdk, the default). The translated shard manifest under generated/ records absolute paths from the machine that ran the translator, so either translate on the Linux host or symlink that path to your checkout.

cmake -S runtime -B build-ios -G Ninja -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_TOOLCHAIN_FILE=cmake/ios-linux-toolchain.cmake -DIOS_SDK=/opt/iOS-SDKs/iPhoneOS26.5.sdk \
    -DCMAKE_DISABLE_FIND_PACKAGE_absl=TRUE -DAURORA_DAWN_PROVIDER=package
cmake --build build-ios --target WiiCompiled

iOS from Windows

The same toolchain file works on Windows with llvm-mingw, which is what the Windows build already uses. Three things differ from a Linux host:

  • llvm-mingw does not ship ld64.lld.exe or llvm-install-name-tool.exe; LLVM tools dispatch on their file name, so copy ld.lld.exe to ld64.lld.exe and llvm-objcopy.exe to llvm-install-name-tool.exe in its bin directory.
  • Git for Windows checks the SDK repo's symlinks out as small text files unless core.symlinks is on (Developer Mode). Either enable that before cloning, or replace each placeholder with a copy of its target; libSystem.tbd is one of them and the link fails without it.
  • Pass the toolchain file as an absolute path, and MKW_IOS_LLVM_BIN as the llvm-mingw bin directory with forward slashes.
cmake -S runtime -B build-ios -G Ninja -DCMAKE_BUILD_TYPE=Release `
    -DCMAKE_TOOLCHAIN_FILE=C:/src/Wiicompiled/runtime/cmake/ios-linux-toolchain.cmake `
    -DIOS_SDK=C:/iOS-SDKs/iPhoneOS26.5.sdk -DMKW_IOS_LLVM_BIN=C:/llvm-mingw/bin `
    -DCMAKE_DISABLE_FIND_PACKAGE_absl=TRUE -DAURORA_DAWN_PROVIDER=package
cmake --build build-ios --target WiiCompiled

Install it with AltStore or SideStore, which sign with your own Apple ID. The app needs the com.apple.developer.kernel.increased-memory-limit entitlement or it exits at startup; GetMoreRam grants it with a free Apple ID. GetMoreRam grants com.apple.developer.kernel.extended-virtual-addressing at the same time, which matters on devices with less than 4 GB of RAM: the runtime reserves a 4 GiB guest address space at startup and that is right on the limit iOS allows without it. It makes no difference on the 8 GB devices this was developed on.

runtime/cmake/ios/WiiCompiled.entitlements is a template for signing by hand, with placeholders to replace; the sideloaders build their own and ignore it. It carries both entitlements, so the provisioning profile you sign with needs both capabilities enabled.

Copy Config.toml and an extracted DATA directory into the app's Documents folder, using Files on the device or the Finder with it connected. Keep dvd_root relative:

[paths]
dvd_root = "DATA"

Touch controls appear when no gamepad is attached; tap the FPS readout for settings.

Caution

Only take builds from this repository's Releases page. If someone's sharing an installer through Discord or some random download site, don't touch it!!

WiiCompiled, Wheel Wizard, Retro rewind and other related projects are developed independently and each has its own contribution rules and all have their own rules. What applies here does not automatically apply there, and vice versa. Check each project's own CONTRIBUTING and README files.

Retro Rewind

Retro Rewind, ZPL's Mario Kart Wii mod distribution, can be built as its own static profile: instead of applying Code.pul as runtime patches, the Kamek/Pulsar code is statically translated together with the base game into a separate native executable.

Wheel Wizard drives this too.

Building from source

Owning the game is still required even if you compile everything yourself.

You'll need: .NET 8 SDK, CMake, Ninja, and LLVM/Clang (the shipped build uses LLVM-MinGW targeting x86-64-v3).

Build the translator:

dotnet build translator/Translator.sln -c Release

The default test suite needs no binaries and no host C++ compiler, so you can hack on the translator without any game data around.

For everything beyond that, feeding in your own main.dol/StaticR.rel, running the translation, generating the manifest and build graph, and compiling. see translator/README.md.

FAQ

Is this an emulator? No. Everything is compiled to native code before you ever press play. At runtime there's nothing emulating a Wii CPU or GPU.

Do you provide the game? No. Don't ask. Nothing in this repo or any release contains Nintendo code or assets.

Why does setup take so long? Because we don't ship the translated binary, most other recomp projects do, but we don't want to risk it right now, setup has to run a static recompiler over the whole game and then throw a C++ compiler at the result. It's a one-time cost on your machine.

Which game version works? Clean PAL RMCP01. Other regions and modified executables are rejected. Translating them against the wrong manifest would give you a subtly broken game that's miserable to debug for us.

Can I recompile other GameCube/Wii games with it? The translator itself handles DOLs and RELs generically, see projects/examples/generic-dol.yml. The catch is that a playable port also needs a runtime: audio, input, GX, everything the game touches.

The game crashed / stopped with an error. Errors are deliberately loud instead of quietly swallowed. Send a report along with the run log from %LOCALAPPDATA%\WiiCompiled\Logs.

Will you fix original bugs? Not in the base game, behavior identical to real hardware is the goal. Only report things where this port differs from the original game. As for Retro Rewind, some base-game behavior is patched, so if it differs from the base game, that's normal. If Retro Rewind behavior differs between Dolphin/Wii and WiiCompiled, open an issue on GitHub.

How accurate are the physics? 100% - this is proven by in-game ghosts. Since ghosts are replay files based on inputs rather than tracked positions, matching ghosts prove the physics match across Dolphin/Wii/WiiCompiled.

Is it done? Not fully. The game is in a state where everything should be playable and the physics do match 100% with the original game, but compatibility, rendering, networking and performance are all actively being worked on. If you do find an issue, we strongly encourage you to open one on GitHub so we can take a look at it.

AI usage

AI coding tools were used during development of this project. All translated output is verified against real hardware behavior and most importantly, physics accuracy is proven synced across Wii, Dolphin, and WiiCompiled (see FAQ).

Credits

  • aurora - the GX rendering/windowing backend this project's whole graphics layer sits on. MIT licensed.
  • Dawn - Google's WebGPU implementation, powering aurora's Direct3D, Vulkan and OpenGL backends.
  • Dolphin Emulator - an invaluable reference for Wii hardware behavior during development, plus the source of the free DSP coefficient ROM and the unmodified default WiiConnect24 bootstrap tree bundled with the runtime.
  • Retro Rewind by ZPL and team - the mod distribution this project supports.
  • Wheel Wizard - the mod manager this project integrates with as a launch backend.
  • Everyone in the static recompilation community.

Bundled third-party components and their licenses live in THIRD-PARTY-NOTICES.md.

License

WiiCompiled is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License, version 3 as published by the Free Software Foundation.

WiiCompiled is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

Any mkwii distribution making use of WiiCompiled must be licensed under GPL v3.0.

Not affiliated with, endorsed by, or associated with Nintendo. Mario Kart Wii is a trademark of Nintendo. No Nintendo intellectual property is contained in, distributed with, or obtainable through this project.

S
Description
No description provided
Readme GPL-3.0 7.2 MiB
Languages
C++ 50%
C# 40.5%
C 4.1%
PowerShell 1.7%
Shell 1.6%
Other 2.1%