Packaging note (current): the emucore and its runtime data are no longer committed here. They are built in the WinNative-Emu fork and shipped in the Retro-Consoles bundle the app downloads at runtime. The build/patch details below describe how that fork produces the binary and remain accurate for the fork.
Decision (2026-07-22): replace the dolphin-libretro GLES path for GameCube/Wii with an embedded standalone Dolphin core, mirroring the ARMSX2/PS2 pattern. WinNative drives everything — Dolphin’s own menus/UI are stripped; every setting, driver and toggle is fed from WinNative’s surfaces (Shortcut Settings, Settings > Retro, in-game drawer).
Why: dolphin-libretro can only render GLES via libretrodroid, and the system Adreno GLES driver SIGSEGVs on new silicon (see DOLPHIN_VULKAN.md). Standalone Dolphin brings its own Vulkan backend + native adrenotools/Turnip loading, RetroAchievements, netplay, and save infrastructure.
dolphin/build-emucore.sh — clones dolphin-emu/dolphin at pinned SHA
b008142f72a099a9bcb971776209504f085ae0be (+ submodules), applies patches/,
CMake+Ninja (NDK toolchain, arm64-v8a, android-26, flexible page sizes ON,
analytics/autoupdate/tests OFF, RetroAchievements ON) and builds:
libmain.so — Dolphin core + JNIlibhook_impl.so, libmain_hook.so, libgsl_alloc_hook.so,
libfile_redirect_hook.so — libadrenotools (custom Vulkan driver loading)build.gradle — library module org.dolphinemu.dolphinemu; native build is opt-in
via -Pdolphin.buildNative=true until the pipeline is proven; otherwise packages
prebuilt/emucore/.:gc process via
org.dolphinemu.dolphinemu.wn.DolphinEmulationActivity (WinNative-owned; no
upstream fragments/menus).Status 2026-07-22: Phases 1–5 done and verified on-device (CPH2749): TP GC boots on Vulkan crash-free, input proven via chained input-only transitions, settings bridge verified both directions (stretch applied / auto restored), and Turnip verified on Adreno 840 — WN-Turnip-1.06 staged via wn.gc.driver → GpuDrivers/Extracted, libvulkan_freedreno.so confirmed mapped in-process, clean rendering. GPU Driver pickers live on both GC/Wii settings surfaces.
Phases 6–7 (2026-07-22, verified on-device): DolphinGameOverlay attaches the shared
WinNative RetroInputView (GC/Wii themes) + drawer menu via the DolphinHost hook
(Ps2GameOverlay pattern; PluviaApp installs in every process, :gc gets light init).
Verified: overlay renders over the game, drawer opens, save state wrote GZ2E01.s01,
Exit → StopEmulation → clean finish. Routing: GC/Wii launches go to embedded Dolphin
in both launch paths behind wn.gc.embedded (default true; off = libretro fallback).
Settings parity pass (2026-07-22, compile-verified; device was disconnected):
retro_def_var_<sys>_<key> / retro_def_*) → Shortcut extra (retro_var_* /
retro_*) → launch (resolvedCoreVariables + wn_touch/wn_audio/wn_hud) → in-game
changes apply live AND persist back to the shortcut. GPU driver = wn.gc.driver
(global for GC+Wii, PS2 pattern).wn.gc.embedded is off.Start/teardown lifecycle (2026-07-22, compile-verified): the Dolphin core cannot
reliably boot twice in one process, so :gc follows the ARMSX2 model — fresh process
per session. teardown() (idempotent; Exit button, back press, Run() returning, and
onDestroy-when-finishing all funnel into it) does StopEmulation → join the Run thread
(≤15s, so memcards/saves flush) → delete .running_rom marker → finish() → kill the
:gc process. onPause/onResume skip pausing while stopping (no pause-during-shutdown
races). Launch side: the game activity now JOINS the Library task (NEW_TASK only from
non-activity contexts) so exiting lands back on the Library; prepareLaunch kills a
lingering :gc before starting UNLESS the .running_rom marker matches the requested
ROM (tapping Play on the running game resumes instead of restarting).
Drawer parity + shared-source pass (2026-07-22, compile-verified):
RetroDrawerTabs.build
(Menu / Display / HUD / Sound / Controls; netplay correctly absent).RetroControlsMenu — single source used by BOTH
RetroActivity (all libretro consoles, incl. the PSX-inversion gate) and the
Dolphin overlay: touch toggle, adaptive sticks (now on GC/Wii too — wn_adaptive
rides the launch vars), haptics, edit/reset layout, and the four button-color
pickers + reset.RetroHudSupport.buildHudEntries + FrameRating with new
shared container-JSON persistence (loadContainerHudSettings/save…); master
toggle also drives Dolphin’s native OSD (ShowFPS/VPS/Speed). FPS/graph elements
default off for Dolphin (no per-frame Java callback; native OSD covers it).Settings surfaces + persistence pass 2 (compile-verified):
Open items: overlay polish (FrameRating-style HUD, touch-IR for Wii ir_mode, controller-connected hiding, playtime/cloud hooks), RetroAchievements for the embedded path, defaults tuning (dual core ON), retiring the libretro dolphin core after parity sign-off. On-device retest checklist when phone reconnects: drawer tabs render + live-apply (aspect change visible), audio mute, HUD OSD, VBI toggle, touch-controls hide, edit-layout back handling, Play-button routing end-to-end.
libmain.so; vendor the minimal Java classes
Dolphin’s IDCache/JNI_OnLoad requires (NativeLibrary, NativeConfig,
GpuDriverHelper, model classes); patch IDCache to drop upstream-UI-only classes.NativeLibrary.SurfaceChanged,
Run(rom), pause/resume, input forwarding, clean shutdown.User/Config/Dolphin.ini + GFX.ini from WinNative
prefs pre-boot (renderer=Vulkan, IR scale, aspect, dual core, JIT, skip BIOS,
widescreen, sensor bar, IR mode, cheats); live updates via NativeConfig where
safe. Same keys/surfaces as the current GC/Wii settings (RetroCoreOptions ids map
1:1 onto Dolphin ini keys).CustomDriver Turnip packs through Dolphin’s
GpuDriverHelper; per-console wn.gc.driver pref; default system Vulkan.DolphinGameOverlay (Ps2GameOverlay analogue) — RetroInputView touch
overlay, WinNative drawer (save states via NativeLibrary.SaveState/LoadState,
live settings, HUD, cheats), playtime stats, exit cloud backup.RetroShortcuts.launch sends gc/wii to the embedded activity (toggle
first, libretro fallback until parity); settings surfaces switch to the Dolphin
sections; decide native RetroAchievements vs RetroAchievementsManager.Authoritative JNI-required Java surface (every FindClass under Source/Android/jni/),
30 classes — all model/util, no upstream UI:
NativeLibraryARCheat, GeckoCheat, PatchCheat, GraphicsMod, GraphicsModGroupcontrolleremu/{Control, ControlGroup, ControlGroupContainer,
ControlReference, EmulatedController, NumericSetting}, CoreDevice(+$Control),
InputDetectorNetplaySession, model/Playerriivolution/model/RiivolutionPatches, skylanders/model/SkylanderPairmodel/GameFile, model/GameFileCacheAnalytics, AudioUtils, BooleanSupplier, CompressCallback,
ContentHandler, NetworkHelper, PermissionsHandler, WiiUpdateCallbackutils/GpuDriverHelper (static-native Kotlin side of the adrenotools bridge;
native impl jni/GpuDriver.cpp calls adrenotools_* directly — Phase 5 hook point)Naive transitive closure is 92 files because a few bridge classes import the UI web.
Patch these cut points (in patches/), then the closure collapses to ~50 UI-free files:
NativeLibrary.kt — replace EmulationActivity/AlertMessage references with a
WnHost callback interface our activity registers (alerts, exit, orientation,
title changes). This IS the B2 design: UI callbacks go to WinNative.utils/Analytics.kt — strip the FragmentActivity dialog method (analytics is
compiled out with -DENABLE_ANALYTICS=OFF).NetplaySession.kt — sever imports of netplay UI (NetplaySetupActivity,
viewmodels) if present; keep the session/Player model.Settings.kt/NativeConfig chain — sever SettingsActivity/MenuTag imports.DolphinApplication (not JNI-required); our module provides directory init
via DirectoryInitialization directly.ui/ adapters fall out once EmulationActivity is cut.Vendoring mechanism: gradle Copy task from emucore-src/.../java (post-patch) into
build/generated/dolphinJava, added as a srcDir — no duplicated sources in-repo.
Reviewed Medard22/Dolphin-MMJR2-VBI, nachoverdon/Dolphin-MMJR2, Mato00KAN/Dolphin-MMJR2 for patches worth adopting:
bVISkip
(VideoConfig.h:307), adaptive gate CoreTiming::UpdateVISkip/GetVISkip (only skips
when the core runs behind real time; auto-off under determinism/netplay), interrupt
suppression in VideoInterface.cpp:986. Config: GFX.ini [Hacks] VISkip; vendored
BooleanSetting.GFX_HACK_VI_SKIP already exposes it to our bridge. Nothing to port.
Phase 4: expose as a WinNative “VBI Skip (auto frameskip)” toggle, default ON
(adaptive — no cost at full speed; this is the MMJR2 “runs better” effect).libdolphin_libretro_android.so) stays in place until Phase 7
parity, then is removed along with its jniLibs entry.Cloud-save bucket fix (2026-07-22): the real reason Save History was blank for
ALL retro consoles — auto-upload used cloudGameId (“retro_gc_