- C++ 63.5%
- Python 22.8%
- C 6.2%
- CMake 4.6%
- Shell 2.3%
- Other 0.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Source tree of local 169b16cab7831fe1b5599d50ee87c89aa4554dc5; upstream chasem-dev/sms-pc-port base 831cdd97277ae3ac6696e8e81fda1dd732ff2f91. Native code ac40c4b; no game data included. |
||
| .github/workflows | ||
| assets | ||
| cmake | ||
| decomp@967436b8c6 | ||
| decomp-patches | ||
| docs | ||
| mods | ||
| platform | ||
| rom | ||
| src | ||
| tools | ||
| .gitattributes | ||
| .gitignore | ||
| .gitmodules | ||
| bindings.txt | ||
| build.cmd | ||
| BUILD.md | ||
| build.sh | ||
| clean.cmd | ||
| clean.sh | ||
| CMakeLists.txt | ||
| LICENSE | ||
| README.md | ||
| run-hd-cutscenes.sh | ||
| run.cmd | ||
| run.sh | ||
| settings.txt | ||
| THIRD_PARTY_NOTICES.md | ||
sms-port
Archipelago Windows WIP
This fork contains an experimental native Windows x64 Archipelago integration. Setup, the supported seed profile, the client and the portable test package live in sunshine-archipelago. The bridge and save contract are documented in docs/AP_NATIVE_SPIKE.md. The complete normal campaign through Bowser has not been played through on this build. Rocket input/equip, partner delivery and normal save/reload have separate recorded checks; the native mechanism tests do not establish full campaign acceptance.
The published source snapshot retains native code revision ac40c4b and
decompilation revision 967436b8c623834d38a57d34923775f45eca8861.
The tested Windows executable SHA-256 is
6c4b58dce2e9774694ed9d7680f48ab9f1e7210007c47eb6ecf56adbb9217fb4.
It reads your own GMSE01 Rev 0 disc image; no game image or extracted game data is included.
To build this WIP from source, use the Windows prerequisites in BUILD.md, clone this fork recursively, and run the following in MSYS2 MINGW64:
git clone --recursive --branch wip-2026-10-14 https://git.peliarch.ca/alaric/sunshine-pc-port.git
cd sunshine-pc-port
cmake -S . -B build/windows-64 -G Ninja -DSMS_ARCH=64 -DSMS_BUNDLE_DISC= -DSMS_GX_BUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build/windows-64 --target sms sms_ap_bridge_test
./build/windows-64/sms_ap_bridge_test.exe
Keep the pinned decomp/ revision when reproducing this build.
For distribution builds, use bare CMake with an empty SMS_BUNDLE_DISC:
SMS_STANDALONE=0 skips the bundled copy, but the upstream build script can still
embed a game-derived Explorer icon when it discovers an image in rom/.
The portable WIP executable was configured with SMS_BUNDLE_DISC= and contains
only a manifest resource, without that icon or an embedded disc.
The port is MIT licensed; the decompilation has its own CC0 license. THIRD_PARTY_NOTICES.md preserves notices for code compiled into the port. The Windows runtime also needs SDL2, libgcc, libstdc++ and winpthreads. The portable package includes those DLLs and their notices; a source build uses the versions installed by MSYS2. Windows supplies the system graphics and audio DLLs.
A native PC port of Super Mario Sunshine (GameCube, North America, GMSE01), built from the matching decompilation.
No game data is included. You build the program from source, and it reads the models, textures, levels, music and movies from your own disc image at run time.
For guided setup, updates and a Play button, use our dedicated SMS Launcher for Windows, macOS and Linux. The launcher sets up this port on your computer using your own supported disc image; downloads are on its releases page.
Decompilation progress
This card shows the GMSE01 decompilation snapshot recorded by the source revision pinned in this port. Fuzzy similarity measures approximate code similarity; the other two tracks show byte-perfect code and code linked from matching source.
Supported systems
| System | Word size | Status | Output |
|---|---|---|---|
| Linux (x86) | 32-bit (default) | plays | build/linux-32/sms |
| Linux (x86-64) | 64-bit (SMS_ARCH=64) |
plays; still being tested stage by stage (docs/64-BIT.md) | build/linux-64/sms |
| macOS (Intel, or Apple Silicon under Rosetta 2) | 64-bit | plays | build/macos-64/sms |
| Windows (MSYS2 MINGW64) | 64-bit | boots under Wine; native runtime checks | build/windows-64/sms.exe |
The game code keeps pointers in 4-byte fields, so it was written for a 32-bit machine. The 64-bit builds keep every address the game sees below 4 GiB; see docs/64-BIT.md.
Quick start
-
Get the source, including the decompilation submodule:
git clone --recursive https://github.com/chasem-dev/sms-pc-port.git cd sms-pc-port -
Install the prerequisites for your system: Linux, macOS, Windows.
-
Put your disc image in
rom/: one.iso,.gcmor Dolphin.cisoof GMSE01 Rev 0. -
Build and play:
./build.sh ./run.shOn Windows, run these in the MSYS2 MINGW64 shell, or run
.\build.cmdand.\run.cmdfrom PowerShell.
The first build compiles about 600 game files and takes a while; later builds only rebuild what changed.
Because the image is in rom/, ./build.sh also makes a standalone copy with the game's files inside (sms-standalone, or SMS.app on macOS) that runs without the image.
See BUILD.md.
You can also keep the image elsewhere and pass it: ./run.sh "/path/to/Super Mario Sunshine (US).iso".
Build and run
The same two scripts work on every system:
| Command | What it does |
|---|---|
./build.sh [IMAGE] |
builds build/<os>-<arch>/sms; with an image (argument, SMS_DISC_IMAGE, or the one in rom/) also the standalone copy |
./run.sh [IMAGE] [--headless] |
runs that build: with the image you pass, else the standalone copy, else the image in rom/ |
./clean.sh [--all] [--dry-run] |
deletes the build output (every build/<os>-<arch>/); never deletes your disc image, and keeps the downloaded SDL2; --all deletes all of build/ |
SMS_ARCH=64 ./build.sh |
chooses the word size (Linux: 32 default or 64; macOS: 64 only; Windows: 64 default (32 legacy with MINGW32)) |
JOBS=2 ./build.sh |
limits parallel compiler jobs (default: all cores) |
SMS_STANDALONE=0 ./build.sh |
skips the standalone copy even when an image is found |
./build.sh --help, ./run.sh --help and ./clean.sh --help print the details.
When both a 32-bit and a 64-bit build exist, ./run.sh runs the 32-bit one unless SMS_ARCH=64 is set.
Options
Options can be kept in settings.txt (resolution = 2, texture_packs = on, ...), or set as environment variables before the command, for example SMS_SKIP_MOVIES=1 ./run.sh; an environment variable wins over the file:
| Option | Effect |
|---|---|
SMS_SKIP_MOVIES=1 |
skip cutscenes; keep portal animations playing |
SMS_LANGUAGE=fr |
play in French, German (de), Spanish (es) or Italian (it) instead of English (en, the default), with that language pack installed |
SMS_HEAT_HAZE=0 |
turn off the heat-wave shimmer that distorts the picture in sunny areas |
SMS_BUTTON_PROMPTS=style |
the buttons shown in the game's text and on the FLUDD gauge: gamecube (the game's own, the default), xbox, playstation, steamdeck, keyboard, or auto (keyboard and mouse or the controller in use) |
SMS_BUTTON_PROMPT_PAD=style |
with auto, how every controller's prompts look: xbox, playstation, steamdeck or gamecube (default: match the controller) |
SMS_BUTTON_PROMPT_DIR=dir |
the prompt images, <style>.png (eight 64x64 icons in a row: A B X Y Z L R C-stick); SMS Launcher draws them from your bindings |
SMS_HDR=1 |
HDR output on Windows while Windows HDR is on: the SDR picture is expanded to HDR and presented through a Direct3D 11 swap chain (scRGB). --display-info prints what Windows reports for each display (HDR, peak and full-frame nits, SDR content brightness, calibration profile) as JSON |
SMS_HDR_PAPER_WHITE=nits |
how bright ordinary whites, menus and text are in HDR; auto (default) uses Windows' SDR content brightness |
SMS_HDR_PEAK=nits |
the brightest an HDR highlight reaches; auto (default) uses the display's peak as Windows reports it, which follows a Windows HDR Calibration profile |
SMS_HDR_CONTRAST=n, SMS_HDR_SATURATION=n |
HDR contrast (50 to 150) and saturation (0 to 200), percent; 100 leaves them as they are |
SMS_HDR_HIGHLIGHTS=n |
0 to 100 (default 40): how far the brightest parts climb toward the peak, in stops; 0 keeps SDR white at the paper white, 100 puts it at the peak |
SMS_HDR_COPY=1 |
hand each frame to Direct3D through system memory instead of a texture shared with OpenGL (about 1.3 ms more per frame at 4K). The game does this by itself when the graphics driver will not share a texture; this forces it, for a driver that shares one but shows it wrong |
SMS_AUDIO=0 |
no sound |
SMS_VOLUME=n |
master volume, 0 to 100 (default 100) |
SMS_SAVE_DIR=dir |
memory card folder |
SMS_SAVE_STATE_DIR=dir |
save state folder (default: savestates beside the memory card folder) |
SMS_BINDINGS=file |
key bindings file (default bindings.txt in this folder) |
SMS_DISC_IMAGE=file |
disc image to use when none is passed |
--headless (after the image) or SMS_HEADLESS=1 |
no window, for testing (Linux only) |
SMS_OVERLAY=1 |
open the debug overlay at start |
SMS_GX_SCALE=n |
render at n times the GameCube's resolution |
SMS_ANISO=n |
anisotropic filtering, 2 to 16 (up to what the GPU supports), for sharper textures at glancing angles; textures whose mipmap levels are an effect (such as the sea's sparkles) keep the GameCube's level of detail |
SMS_FULLSCREEN=1 |
borderless fullscreen at the desktop's resolution (on Windows, an undecorated normal window one row taller than the monitor, without an SDL fullscreen transition, so the driver does not promote it to a fullscreen flip that blanks the monitor); exclusive switches the display to SMS_FULLSCREEN_MODE=WxH@Hz (else the desktop's mode; on a monitor with Windows HDR on it stays borderless, since a mode switch can leave HDR's colours wrong). F11 or Alt+Enter toggles fullscreen while playing |
SMS_DISPLAY=n |
the monitor to open on (0 is the primary one; by default, the one under the mouse) |
SMS_VSYNC=1 |
wait for the display's refresh; adaptive shows a late frame at once instead of waiting a whole refresh |
SMS_MSAA=n |
multisample anti-aliasing with 2, 4 or 8 samples (up to what the GPU supports) |
SMS_GX_GL_THREAD=0 |
keep OpenGL on the game's own thread instead of a thread of its own (for troubleshooting a graphics driver) |
SMS_FXAA=1 |
FXAA on the final picture, smoothing edges MSAA leaves (alpha-tested foliage, fences) |
SMS_SHARPEN=n |
contrast-adaptive sharpening of the final picture, 0 to 100 |
SMS_GAMMA=x |
brightness curve: 1.0 is unchanged, above 1 is brighter (0.3 to 3) |
SMS_ASPECT=stretch |
fill the window instead of keeping the picture's shape; integer keeps whole multiples of 640x528 |
SMS_PRESENT_FILTER=sharp |
scale the picture with crisp pixels (nearest for none at all; default bilinear, which averages when the internal resolution is above the window's; fsr upscales a lower internal resolution with AMD FSR 1, sharpened as much as SMS_SHARPEN asks; nis with NVIDIA Image Scaling, sharpened from NVIDIA's default up as SMS_SHARPEN asks) |
SMS_FSR_MODE=quality |
with fsr or nis, render at a share of the picture's size on screen and upscale the rest: native (100%), quality (67%), balanced (59%), performance (50%) or ultraperformance (33%); it sets the internal resolution in place of SMS_GX_SCALE |
SMS_WIDESCREEN=16:9 |
widescreen (also 21:9, 16:10): a wider view, with the HUD and menus kept 4:3 in the middle |
SMS_FRAME_RATE=30, 60, or 120 |
gameplay frame rate: 30 (GameCube native), 60 (port default), or 120 (optional); logos, menus and movies stay at 30 |
SMS_WIDESCREEN_HUD=edges |
with widescreen, move the gameplay HUD's counters to the left edge and the water gauge to the right one |
Optional mods, such as HD texture packs, go in mods/; python3 tools/mods/get.py textures downloads and installs the UHD texture pack there.
Saves go to a memory card in slot A, kept as files in ~/.local/share/sms-port/card-a on Linux and macOS ($XDG_DATA_HOME/sms-port/card-a if that is set) and in %APPDATA%\sms-port\card-a on Windows.
Every other switch (debugging, tracing, graphics) is listed in docs/DEVELOPMENT.md.
Frame rate
Gameplay defaults to 60 frames a second; choose 30 for the GameCube's native rate or 120 for smoother motion in settings.txt (frame_rate = 120) or with SMS_FRAME_RATE=120 ./run.sh.
The launcher offers these three choices under Settings → Gameplay → Frame rate.
Game speed stays the same, and logos, menus and movies stay at 30 fps.
120 fps is available on any display, but needs sufficient CPU/GPU performance; a display running at 120 Hz or faster shows its full benefit.
With display vsync enabled (vsync = on / SMS_VSYNC=1), a slower refresh rate can limit presentation, so leave vsync off to avoid that limit.
Like on the GameCube, missed retraces can reduce the achieved frame rate in steps.
The debug overlay (backtick) shows where each frame's time goes:
game: the game's own code (and the GX commands it writes).GX: the renderer, split intovertices(loading GX vertices),batches(issuing draws),textures,copies(EFB copies) andpeeks.waiting for the GPUis the part of it spent blocked on the GPU, or on the thread that hands the frame to the graphics driver.presentandswap: drawing the frame to the window andSDL_GL_SwapWindow.idle: the game waiting for the next retrace, which is spare time.
If the overlay's GL line says llvmpipe, softpipe or Software, the game is rendering on the CPU and cannot hold 30 fps.
On Linux this is usually the 32-bit build without the GPU driver's 32-bit libraries; build 64-bit (SMS_ARCH=64 ./build.sh) or see BUILD.md.
Controls
Controller 1 reads the keyboard and any game controller SDL recognises (A/B/X/Y, Start, right shoulder = Z, triggers = L/R, sticks, d-pad). Keyboard defaults:
GameCube adapters work too. On Windows, an adapter in Wii U mode (Nintendo WUP-028, Mayflash and other copies; USB 057e:0337) is read directly through WinUSB, so it needs its WinUSB driver (the one Dolphin installs); SMS_GC_ADAPTER=VVVV:PPPP adds another USB id and SMS_NO_GC_ADAPTER=1 turns it off. In PC/HID mode the adapter is a normal joystick for SDL. The first plugged port is controller 1.
| GameCube | Keys |
|---|---|
| Control stick | arrow keys or WASD (hold Left Ctrl for half tilt) |
| C-stick | I / J / K / L |
| A | Space or X |
| B | Shift or C |
| X / Y | V / F |
| Z | Z |
| L / R (full press) | Q / E |
| Start | Enter |
| D-pad | 1 2 3 4 (up, down, left, right) or keypad 8 2 4 6 |
| Debug overlay (frame rate, stats, keys) | ` (backtick) |
| Game and movie speed x1 / x2 / x4 / x10 (overlay open) | F7 |
| Warp menu (debug overlay open) | F8, or a controller's Back/Select button |
| Save a save state | F5, or L1 + R1 (LB + RB) held together on a controller |
| Save states list (in the pause menu) | the same: F5, or L1 + R1 |
| Load the newest save state | F6 |
| Quit | Esc |
The warp menu lists every stage on the disc (Eclipse's too) and, under each, its episodes and secret courses with their names and scene files; picking one warps there straight away, as a pipe or the pause menu's Exit Area would, without the Shines that would unlock it.
It warps while a stage is playing (not during a cutscene, dialogue or the pause menu); the game keeps running behind it but reads no input.
Arrow keys or the d-pad (or the left stick) move, Enter or A opens a stage and warps, Esc, Backspace or B goes back; LB/RB and Page Up/Down skip a page, and the mouse works too.
F8 and Back/Select open it only while the debug overlay (`) is open; otherwise they are ordinary keys and buttons, so PAD_BACK can still be bound to a game control (it then opens the menu instead while the overlay is open).
Save states
A save state is the game at one moment: in a level, F5 or L1 + R1 (LB + RB) saves it. The pause menu shows those buttons at its top right with LOAD SAVE STATE: press them there to open the list of states, newest first, with a picture of each; A loads one, Y deletes it, B goes back. F6 loads the newest without the menu. The buttons show as your keyboard or controller shows them, from the launcher's button prompt pictures (their names without the launcher).
Loading a state saved by the same build of the game puts everything back as it was, Mario, enemies, the camera, the music, in place and at once. A state from another build (an update, someone else's game, another word size) cannot be put back that way; it takes you to its level and episode and puts Mario where he was, with his health, FLUDD's water and nozzle, and the progress the state had (Shines, coins, lives). The list marks those states "position".
States are files (*.smsstate) in a savestates folder beside the memory card folder (~/.local/share/sms-port/savestates by default; SMS_SAVE_STATE_DIR or save_state_dir sets another); share one by sending the file. Loading a state takes you to its progress too, so saving to the memory card afterwards saves that. Saving a state does not touch the memory card.
How states work, and their limits: docs/SAVE-STATES.md.
To change them, edit bindings.txt (CONTROL = KEY KEY ..., one control per line; a line replaces that control's defaults), or point SMS_BINDINGS at another file.
Controller buttons can be remapped the same way, by name: PAD_A, PAD_B, PAD_X, PAD_Y, PAD_LB, PAD_RB, PAD_LT, PAD_RT, PAD_START, PAD_BACK, PAD_DPUP … (the full list is in bindings.txt). A line's keys and buttons each replace only their own kind, so A = PAD_Y moves A to the controller's Y button and keeps Space and X; Z = PAD_RT puts Z on the right trigger. L and R stay analog when bound to a trigger.
Two keys or two buttons joined with + must be held together: the default SAVE_STATE = F5 PAD_LB+PAD_RB saves with F5, or with L1 and R1 (LB and RB) held at once. While one of such a pair is held, the game does not see the other, so holding L1 and then pressing R1 does not also press Z.
L_SOFT and R_SOFT are a light L or R press: the trigger goes part of the way, without the click, as when a GameCube trigger is pressed gently (for example, spraying FLUDD on the move with R). They have no default keys; bind them like any other control (R_SOFT = LALT PAD_RB). SMS_SOFT_TRIGGER=n (soft_trigger) sets how far, 5 to 95 percent (default 40).
SMS_CAMERA_INVERT_X=1 and SMS_CAMERA_INVERT_Y=1 invert the C-stick's camera control left/right and up/down, for keys and controllers alike (recorded .dtm movies are played as recorded).
The launcher sets them from Settings → Gameplay → Invert camera X / Y (X on, Y off by default), effective on the next game launch.
SMS_FREE_CAMERA=1 keeps the camera where you point it: it no longer swings back behind Mario by itself as he runs (L still recentres it). SMS_CAMERA_SPEED=n scales how fast the C-stick turns the camera, in percent (100 is the game's own; 10 to 400). SMS_MOUSE_CAMERA=1 turns the camera with the mouse: the window captures the mouse while it has focus, F10 releases it and a click takes it back; SMS_MOUSE_SENSITIVITY=n sets its speed in percent. The invert settings apply to the mouse too.
The game ignores the keyboard and controllers while another window has focus, so a controller used in another app (or in the launcher) does not also move Mario. Anything held is released when the window loses focus; a button still held when you come back counts once you press it again. SMS_BACKGROUND_INPUT=1 (background_input) keeps input on in the background.
Controllers rumble where the game rumbles the GameCube controller: the one you last pressed or moved does, through SDL (Xbox, PlayStation, Switch and other controllers with motors), and on Windows a controller on a GameCube adapter read through WinUSB does too, when the adapter's second (power) cable is plugged in. The game's own Rumble option turns it on and off as on the console; SMS_RUMBLE=0 (rumble = off) turns it off for the port, and SMS_RUMBLE_STRENGTH=n (rumble_strength) sets its strength in percent (default 100; SDL controllers only). Rumble stops while another window has focus.
On the file-select screen, walk Mario left under a block for about half a second and press A to jump into it.
Repository layout
build.sh, run.sh build and run, on every system
clean.sh delete build output
*.cmd the same three from PowerShell or Command Prompt (Windows)
bindings.txt keyboard bindings
rom/ your disc image (ignored by git)
build/<os>-<arch>/ build output (ignored by git)
decomp/ the decompilation (git submodule: sms-english)
decomp-patches/ PC-only changes applied to copies of decomp sources at configure time
platform/ host replacements for the GameCube SDK: graphics, disc, audio, input, OS
src/ entry point and compatibility headers
tools/ build helpers and developer tools
docs/ developer documentation and reference screenshots
Documentation
- BUILD.md: prerequisites for each system, the standalone build and macOS app, manual CMake builds, troubleshooting.
- docs/DEVELOPMENT.md: where a fix goes (decomp, patch or platform), how the build works, the platform layer, the decomp patches, every environment variable, developer tools, performance.
- docs/64-BIT.md: how the 64-bit build works and what is left.
platform/*/README.md: each platform module in detail.
The standalone build contains the whole game, so keep it to yourself: sharing it is sharing the game.
Languages
The North American disc is in English; the European release adds French, German, Spanish and Italian.
A language pack holds one of those languages' text and menus, taken from the European disc, for this port to play with your North American one: dialogue, cutscene subtitles, signs, menus, the HUD, the title screen and the memory card screens.
python3 tools/mods/get.py language fr installs the French pack in mods/languages/fr (de, es, it for the others), and language = fr in settings.txt (or SMS_LANGUAGE=fr) plays in it; SMS Launcher does both from its Language setting.
Saves are the same in every language. The packs are made with the scripts in sms-hd-texture-extras; mods/README.md describes their layout.
HD cutscenes
The optional HD pack enhances all 21 original movies at 3× resolution and preserves their timing and original audio. The updated launcher installs the movie patches from your own disc when HD textures are enabled. See HD cutscenes for installation, conversion tools and playback checks.