When the user presses Enter (or the gamepad A button) on the main screen, the launcher does not immediately hand control to the game. It first fades the display to black, validates the game installation, ensures the base ROM is in place, assembles a complete CLI argument list from every active sub-screen, configures a sanitised environment, and only then spawns the game process. A background monitor thread watches the process and signals the launcher when it exits. The key source files areDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/retired64/sm64coopdx_launcher/llms.txt
Use this file to discover all available pages before exploring further.
src/game.rs (all logic functions) and src/main.rs (event loop integration and fade state machine).
Fade to black
When
AppState::Launching is entered, the main loop begins a full-screen fade-to-black animation. Music volume is immediately reduced to near-silence (sdl2::mixer::Music::set_volume(3) — approximately 2% of full volume).The fade lasts exactly FADE_DURATION_MS = 500 ms (defined in src/config.rs). Once the 500 ms fade completes, the launcher proceeds immediately to validation and process spawn.Pre-launch validation
validate_game_installation(game_dir, savepath) in src/game.rs runs a series of checks before any process is spawned:-
Language file check — verifies
{game_dir}/lang/English.iniexists. This is a hard error; if the file is missing, the game installation is considered incomplete and a red error message is rendered on screen. Launch is aborted. -
DynOS directory check — checks whether
{game_dir}/dynos/exists. This is a warning only (log::warn!); the launch continues even if the directory is absent, but DynOS packs will not load in the game. -
Savepath writability check — creates
~/.local/share/sm64coopdx/if it does not exist, then writes and deletes a.launcher_write_testfile to confirm the directory is writable. A filesystem permission error here is a hard failure.
Err string is displayed as red text on the launch screen. The main loop returns to the idle state after a short delay.ROM search and copy
The game binary requires a copy of the Super Mario 64 (US) ROM to be present in the savepath before it will start. This is the well-known MD5 of the Super Mario 64 (US v1.0) ROM.ROM copyOnce a valid ROM is found it is copied to Failure at this step produces a descriptive error message that includes the expected MD5 and the two recommended drop locations.
ensure_rom(savepath, game_dir, data_dir) handles this automatically.ROM search — find_rom(game_dir, data_dir)The function searches four locations in priority order:- The game binary’s directory (
game_dir) - The launcher data directory (
data_dir=~/.local/share/sm64coopdx/) - The
[game].rom_pathentry in~/.config/sm64coopdx/launcher.toml - Any
~/sm64coopdx_Linux-*/release directory
.z64 extension. A file is accepted only if its MD5 hash matches:~/.local/share/sm64coopdx/baserom.us.z64. If a valid copy already exists at the destination it is not re-copied.CLI argument assembly
build_all_game_args in src/game.rs concatenates three argument groups in order: mods → network → profile. The result is a single Vec<String> passed to spawn_game.build_mod_args)- If one or more mods are enabled:
--enable-mod <name>for each enabled mod name. - If no mods are enabled:
--disable-mods(single flag).
build_network_args)| Mode | Arguments |
|---|---|
Local | (none) |
Server | --server <host_port> |
Client | --client <join_ip> <join_port> |
CoopNet | --coopnet <password> |
--playername <name> and --playercount <n> when those values are non-empty/non-zero.Profile arguments (build_profile_args)Always emits --savepath <data_dir>. Conditionally emits:| Flag | Condition |
|---|---|
--configfile <path> | Only if sm64config.txt is non-empty |
--playername <name> | Only if playername is non-empty |
--skip-intro | If skip_intro = true |
--no-discord | If no_discord = true |
--fullscreen | If fullscreen = true |
--windowed | If windowed = true |
--skip-update-check | If skip_update_check = true |
--headless | If headless = true |
Environment setup
build_game_env() in src/game.rs constructs the game’s environment from scratch. It does not inherit the launcher’s environment. Instead it calls Command::env_clear() internally and then re-adds only a whitelisted set of variables:Process spawn
spawn_game(binary, args, log_dir) in src/game.rs performs the final spawn:- Verifies the binary exists and is a regular file.
- On Unix, ensures the binary has execute permission (
chmod 755if needed). - Sets the working directory to
binary.parent()— the game expects to find its assets relative to its own location. - Redirects stdout to
/dev/null. - Redirects stderr to
{log_dir}/game_stderr.log(wherelog_dirisdata_dir=~/.local/share/sm64coopdx/) for post-mortem diagnosis. - Applies the whitelisted environment from
build_game_env().
Child handle, which is immediately passed to spawn_monitor.Monitor thread
spawn_monitor(child: Child) spawns a background OS thread whose only job is to wait for the game process to exit:AGENTS.md). It only sets the GAME_EXITED atomic flag. The main event loop checks this flag each frame and, when it detects true, restores the music volume to MUSIC_VOLUME = 28 (about 22% of SDL2_mixer’s maximum).Example command line
A typical launch command line for a player namedMyName hosting a server on port 7777 with one mod enabled looks like this:
--enable-mod <name> is emitted for each mod and --disable-mods is not included. --disable-mods only appears as a single flag when the enabled-mods list is empty.
Game path resolution
Before the pipeline runs, the launcher must know where the game binary lives.resolve_game_path(cli_arg, profile_override) tries five sources in priority order:
Each level is validated — if the path does not point to an existing file the resolver falls through to the next level. The final path is always canonicalised (absolute, symlinks resolved) so that
current_dir(binary.parent()) in spawn_game cannot interfere with relative-path lookup in Command::new().