Skip to content

Native games: ports of Super Mario 64, Star Fox 64, Ocarina of Time, Mario Kart 64, Majora's Mask and GoldenEye 007 - #16

Open
KurdtNervanna wants to merge 7 commits into
PaddleStroke:mainfrom
KurdtNervanna:native-game-ports
Open

KurdtNervanna wants to merge 7 commits into
PaddleStroke:mainfrom
KurdtNervanna:native-game-ports

Conversation

@KurdtNervanna

@KurdtNervanna KurdtNervanna commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

The next feature after #14 and #15: ports, build scripts for six N64 PC ports that run as native games on the RetroStone2.

Depends on #14 and #15. This branch sits on top of #15's branch, so until those are merged the diff also shows their three commits. The changes for review are the last four commits: 47954d7 (Super Mario 64, Star Fox 64, Ocarina of Time), 5a24d4d (Mario Kart 64), c461218 (Majora's Mask) and 4b5932e (GoldenEye 007).

No game is in this PR. ports/ holds only build scripts, launch scripts and patches against the ports' public source. Each game is built on a PC from the user's own ROM, and the result goes onto their own SD card. The READMEs say to never share it.

What it adds

Folder Game Port Status on a RetroStone2 Pro
ports/sm64 Super Mario 64 sm64-port Full speed
ports/starship Star Fox 64 Starship + libultraship Runs; some slowdown in busy scenes
ports/soh Ocarina of Time Ship of Harkinian 9.3.1 + libultraship Runs: menus, 3D, sound, saves, pause screen; a little slowdown in busy places
ports/spaghettikart Mario Kart 64 SpaghettiKart + libultraship Runs: races, sound, saves; mostly 30 fps, some slowdown in busy places
ports/2ship Majora's Mask 2Ship2Harkinian + libultraship Runs: 3D, music and sound, at the game's own 20 fps in most places
ports/goldeneye GoldenEye 007 Project Midas (fast3d) Runs: intro, menus, levels, sound; 30 fps in light areas, 10-15 in busy views

Each folder has a build-*.sh that clones the port at a fixed commit, applies the patches, makes the game data from the ROM on the PC and cross-builds the game with the RetroStoneOS toolchain (~/rsos/output). It writes roms/native/<Game>.sh plus the game's folder. ports/README.md walks through it step by step and says which other N64 ports can work on a Mali-400.

sm64: a KMS/DRM + GLES2 target for sm64-port. The built-in controls are read from /dev/input and follow the N64 emulator's remaps and core options, so Mario plays the same natively and emulated.

libultraship on OpenGL ES 2.0 (Starship, Ship of Harkinian, SpaghettiKart and 2Ship2Harkinian): libultraship needs GL 3.0 or GLES 3.0; the Mali-400 has GLES 2.0 only. The patches add a USE_GLES2 mode:

  • GLSL ES 1.00 shaders;
  • unsized texture formats;
  • packed depth+stencil (or 16-bit depth);
  • no MSAA and no vertex array objects;
  • framebuffer copies through glCopyTexSubImage2D.

The libultraship games use rsos-gamepad (#15) for the controls.

Ship of Harkinian also needed fixes for 32-bit ARM and Mesa's lima, found on the console:

  • Textures in the heap were dropped as "unresolved N64 segment addresses". On 32-bit, the heap is below 0x10000000.
  • A texture slot the 2-cycle combiner doesn't use had no size.
  • Primitive-depth draws wrote gl_FragDepth, which GLES2 lacks.
  • mixer.c used AArch64-only NEON intrinsics.
  • A fortify abort in audio_load.c.

There's also speed work measured with a CPU profile on the console:

  • vertices as client-side arrays instead of a new buffer object per draw;
  • asset lookups kept once resolved;
  • mincore instead of dladdr.

SpaghettiKart uses an older libultraship (60c9bb7), so it carries its own copy of Ship of Harkinian's patch, plus:

  • a backport of libultraship 766a6bad: SIGTERM called exit() inside the signal handler and deadlocked, so every quit from the menu hung;
  • fewer draw calls: loading the texture that is already bound no longer ends the draw call. Measured on the console, races were CPU-bound at about 0.1 ms per draw call;
  • no screen readback to the CPU every race frame (glReadPixels made the CPU wait for the GPU). The Luigi Raceway and Wario Stadium jumbotrons can't be filled on GLES2, so they are black and uploaded once;
  • -O3 and unsafe math (NEON for float math) instead of Buildroot's -O2;
  • first start: linear texture filtering, and controls for the built-in buttons;
  • an upstream bug: Toad's Turnpike's finish line was placed before xOrientation was set, so on the first race after starting the game a pole stood in the road.

2Ship2Harkinian (libultraship 7cb1022) takes Ship of Harkinian's patch as it is, plus the SpaghettiKart libultraship changes and SIGBUS/ARM registers in crash reports. Its own code needed three fixes for 32-bit ARM, all upstream bugs that stay silent on a 64-bit PC:

  • AudioPlayback_ProcessNotes skips notes whose layer pointer is below 0x7FFFFFFF, a check from the N64, where RAM starts at 0x80000000. Every 32-bit heap pointer fails it, so no playing note got its volume, pitch or start, and instruments were cut off or missing. The check is now 64-bit only (2Ship already leaves it out on the Wii U);
  • the mixer's 0xC80-byte DMEM buffer overflowed into the audio globals (found with AddressSanitizer on the console: crashes and a system freeze in the opening cutscene). It has a guard area now, and the clear and move lengths are clamped to it;
  • the sample importers never set sampleEnd, so the synthesizer read leftover memory for where a sample ends.

There's also audio catch-up when the game is below 20 fps, the GLES2 shaders in 2Ship's own copy, ARMv7 NEON in mixer.c, and -O3 with unsafe math (2Ship forces -O2 on Linux). 2Ship has no tools-only build, so its extractor (ZAPD, built with libultraship) needs the PC libraries the README lists.

GoldenEye 007 uses Project Midas 1.0.2b, whose PC builds are deliberately 32-bit (GoldenEye keeps N64 pointers in 32-bit fields), the same ILP32 little-endian model as the console's ARMv7. Its renderer is the Perfect Dark port's fast3d, which needs GL 3.0. The patch adds a GLES2 mode to that renderer:

  • GLSL ES 1.00, texture sizes as uniforms, no VAOs or framebuffer effects;
  • client-side vertex arrays;
  • a window that's visible from the start, for SDL's KMSDRM.

Two fixes were for the Mali-400's 16-bit fragment shaders:

  • The far-plane discard compared two large, nearly equal values (clip z and w) that rounded to the wrong side. That blanked the intro, the menus and many effects. The vertex shader now passes w - z.
  • The noise constant 143758.5453 is infinity in 16 bits.

Other changes:

  • an ARM build (link at 0x10000000, which Midas's display-list code needs);
  • crash reports with the ARM registers;
  • the render thread on, with -O3 and NEON for the renderer.

Two upstream bugs are fixed in the patch:

  • stanCheckLinkedSpecialTile declared the tile-walk callback's float arguments as s32. On the hard-float ARM ABI that shifted its pointer argument and crashed the game.
  • The room_cache default was never applied at start-up.

The room cache itself stays off: its vertex shader exceeds the Mali-400's 512-instruction vertex limit. Frame rate is bound by the lima driver's cost per GL draw, a few hundred draws a frame.

OS configs: the two RetroStone2 configs get the libraries the libultraship ports link against: Ogg/Vorbis, Opus/OpusFile, libzip, tinyxml2, spdlog, nlohmann-json and SDL2_net.

Docs: docs/native-games.md points to ports/.

Tests

  • check-defconfigs and check-modes pass. shellcheck isn't installed here.
  • build-soh.sh was run end to end from a fresh (shallow) clone with a .v64 ROM. Its oot.o2r and soh.o2r have the same contents as the ones played on the console, and soh.elf is ARM.
  • build-spaghettikart.sh was run end to end from a fresh clone with a .z64 ROM. Its mk64.o2r and spaghetti.o2r have the same contents as the ones played on the console, and Spaghettify is ARM.
  • build-2ship.sh was run end to end from a fresh clone with a .z64 ROM. 2s2h.elf matches the one played on the console apart from build paths and dates, and 2ship.o2r has the same contents. mm.o2r differs only in fields ZAPD leaves uninitialized and the game doesn't read (two extractions with the same build differ the same way).
  • build-goldeneye.sh was run end to end from a fresh clone with a .v64 ROM. ProjectMidas matches the one played on the console apart from the build path and date (12 bytes), and the ROM copy is the same .z64 image.
  • build-starship.sh and build-sm64.sh were run from scratch when they were written.

On hardware

All six games play on a RetroStone2 Pro with a fork build of 0.1.5 that has #14, #15 and these libraries, except Opus, OpusFile and SDL2_net. The OoT and Majora's Mask folders bring their own copies of the ones they use.

  • Super Mario 64 and Star Fox 64 were built with their scripts.
  • Ocarina of Time, Mario Kart 64 and Majora's Mask were built with the same steps by hand. Their scripts came afterwards and make the same archives.
  • GoldenEye 007 was built by hand the same way (the intro, the menus and the Dam played). Its script makes the same program.

🤖 Generated with Claude Code

KurdtNervanna and others added 4 commits October 28, 2026 01:34
…ator

The system "native" (Native games, roms/native): one launch script per
game (.sh), run by /bin/sh in its own folder and its own process group
(host_launch_native), with the display, sound and pads handed over as for
a libretro game. HOME on /data (the root is read-only), SDL_VIDEODRIVER
kmsdrm, SDL_AUDIODRIVER alsa, RSOS_GAME_DIR, RSOS_LANG; output to
game.log. Select+Start held 1 s quits it (SIGTERM to the group, SIGKILL
after HANG_KILL_MS); sleep stops the group and turns the backlight off;
a power-off sends SIGTERM; the idle power-off waits while it runs (no
save state); an error exit is shown.

SDL2 (KMS/DRM, GLES2, ALSA) in the RetroStone2 configs.
docs/native-games.md; check-frontend 2a-2c (the launch and environment,
an error exit, the menu stopping: the group goes too).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
check-transfer: sysmap.c lists every system of systems.c. Its alias is
"nativegames", not "ports": RetroPie's port scripts start RetroPie's own
emulators.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The RetroStone2's buttons, shoulder buttons and optional analog stick are
three input devices, and many SDL games read one game controller only (SDL
does not take the stick for a joystick at all). rsos-gamepad merges them into
one standard uinput gamepad, "RetroStoneOS Gamepad": positional buttons, the
D-pad as hat 0, the stick as the left stick (centred while not fitted). A
game's launch script starts it in the game's process group and gives SDL its
mapping (--print-mapping). The kernel gets CONFIG_INPUT_UINPUT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Build scripts and code changes for three N64 PC ports, each built on a PC
from the user's own ROM into a roms/native folder for the SD card. No game
or game data is in the repository.

- ports/sm64: sm64-port with a KMS/DRM GLES2 target and the built-in
  controls read from /dev/input with the N64 emulator's button settings.
- ports/starship: Starship (Star Fox 64) with an OpenGL ES 2.0 mode for
  libultraship (USE_GLES2: GLSL ES 1.00, GLES2 framebuffers, no MSAA).
- ports/soh: Ship of Harkinian 9.3.1 (Ocarina of Time) with the same GLES2
  mode for its libultraship, fixes for 32-bit ARM and Mesa's lima (textures
  in the heap were dropped, unsized texture slots, primitive depth without
  gl_FragDepth) and speed work measured on the console.
- The RetroStone2 configs get the libraries of the libultraship ports:
  Ogg/Vorbis, Opus/OpusFile, libzip, tinyxml2, spdlog, nlohmann-json,
  SDL2_net.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
KurdtNervanna added a commit to KurdtNervanna/RetroStoneOS that referenced this pull request Oct 29, 2026
…es in the configs

The port changes from the upstream ports PR (PaddleStroke#16):

- ports/soh/build-soh.sh: Ship of Harkinian 9.3.1 from the user's own ROM
  (.z64, .v64 or .n64) in one command, fetching only the pinned commit.
  Run from scratch: the same oot.o2r and soh.o2r contents as by hand.
- ports/soh/README.md: build, controls, what the patches change.
- The ports' text says RetroStoneOS instead of this fork and its build
  numbers; ports/README.md lists Ocarina of Time and Star Fox 64 as
  working; docs/native-games.md points to ports/.
- The RetroStone2 configs add Opus, OpusFile and SDL2_net, so an OS build
  has everything Ship of Harkinian links against.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ports/spaghettikart builds HarbourMasters' SpaghettiKart for the console from
the user's own ROM, with the same GLES2 libultraship mode as Ship of
Harkinian, carried over to SpaghettiKart's older libultraship (60c9bb7).
Nothing from the game is in the repository.

Also, measured on the console:
- libultraship: loading the texture already bound no longer ends the draw
  call (each costs ~0.1 ms of CPU on the Mali-400)
- SIGTERM no longer calls exit() in the signal handler (backport of
  libultraship 766a6bad): every quit from the menu used to hang
- no screen readback to the CPU every race frame; the jumbotrons, which GLES2
  cannot fill, are black and uploaded once
- -O3 and unsafe math (NEON for float math) instead of Buildroot's -O2
- first start: linear texture filtering; controls for the built-in buttons
- Toad's Turnpike: the finish line was placed before xOrientation was set,
  so on the first race a pole stood in the road (upstream bug)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@KurdtNervanna KurdtNervanna changed the title Native games: ports of Super Mario 64, Star Fox 64 and Ocarina of Time Native games: ports of Super Mario 64, Star Fox 64, Ocarina of Time and Mario Kart 64 Oct 29, 2026
ports/2ship/build-2ship.sh builds 2Ship2Harkinian 8a24047 (libultraship
7cb1022) from the user's own ROM: ZAPD on the PC for mm.o2r and 2ship.o2r, a
static libpng and the game for the console.

libultraship-gles2.patch is Ship of Harkinian's GLES2 patch plus the SIGTERM
quit backport (766a6bad), no flush for an already-bound texture, linear
filtering on first start and SIGBUS/ARM registers in crash reports.

2ship.patch fixes 2Ship on 32-bit ARM:
- notes were skipped by an N64 pointer check that every 32-bit heap pointer fails
- the mixer's DMEM buffer overflowed (found with ASan)
- samples' sampleEnd was never set
- audio catches up when the game is slow
- GLES2 shaders, ARMv7 NEON, -O3/unsafe math for GLES2 release builds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@KurdtNervanna KurdtNervanna changed the title Native games: ports of Super Mario 64, Star Fox 64, Ocarina of Time and Mario Kart 64 Native games: ports of Super Mario 64, Star Fox 64, Ocarina of Time, Mario Kart 64 and Majora's Mask Oct 30, 2026
build-goldeneye.sh builds Project Midas 1.0.2b (9062f9f) from the user's own
US ROM: Midas's asset extraction on the PC, the game for the console, and a
copy of the ROM in goldeneye/Data/ (the game reads it at every start).

goldeneye.patch:
- OpenGL ES 2.0 mode for the fast3d renderer (GE_GLES2): GLES2 loader,
  GLSL ES 1.00, texture sizes as uniforms, no VAOs or framebuffer effects,
  client-side vertex arrays, a window visible from the start (KMSDRM)
- Mali-400 16-bit fragment precision: the far clip compares w - z from the
  vertex shader (the intro, menus and effects were discarded), noise hash
- 32-bit ARM: ARM flags, link at 0x10000000, -fpermissive for GCC 14,
  crash reports with registers and a stack scan
- upstream bug: stanCheckLinkedSpecialTile took the tile-walk callback's
  float arguments as s32, which crashed on the hard-float ARM ABI
- render thread on, -O3/NEON for the renderer, room_cache applied at start
- a RetroStone2 controller layout on the N64 control styles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@KurdtNervanna KurdtNervanna changed the title Native games: ports of Super Mario 64, Star Fox 64, Ocarina of Time, Mario Kart 64 and Majora's Mask Native games: ports of Super Mario 64, Star Fox 64, Ocarina of Time, Mario Kart 64, Majora's Mask and GoldenEye 007 Oct 30, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant