Title-app mode: one game as its own local AppImage / dmg / portable exe - #14
Merged
Merged
Conversation
…_app 1) A port's framework stages a title payload (title.json schema 1, the core, the game package) and the hub finds it with --title, or beside itself in <exe_dir>/title/ (macOS also ../Resources/title/). It then opens that title's home page instead of the library. --run-core/--core without --title keeps Direct mode exactly as it was. - Flags (revision 4): --title, --core (alias of --run-core, overriding the title's), --runner (beats RETRO_CORE_RUNNER and the lookup), --hub, --check-title. Every path on the command line is made absolute against the launch directory before anything else, in Direct mode too. - The ROM: --rom, the remembered one, rom.file_names beside the app, then in <data dir>/roms/; otherwise the home page shows "Choose ROM..." (SDL3 SDL_ShowOpenFileDialog; the callback only stores the answer, the frame loop takes it) and Play stays disabled. rom.size / rom.sha256 are checked; a mismatch is refused with expected vs got. A good ROM is remembered. - State: <dir of the app>/<id>-data/, the app being $RETRO_HUB_APP (a --hub re-exec), $APPIMAGE, $RETCOMM_PORTABLE_EXE, the .app bundle, else the hub. Probed by writing; <user data>/<id>/ when not writable, logged. hub.paths point there, so config.json, sessions, saves, settings, the describe cache and rom.json land in it. The runtime updater is off in a title app. - mods.toml: the n64lle selection is read from and written to <data dir>/mods.toml (ModScanResult::selection), and the runner child gets RETRO_TITLE_STATE_DIR=<data dir> (PlayArgs::env -> LaunchSpec::env). - --check-title prints title, id, core, package, title_dir, runner, runner_version, rom (path | none | refused) and data_dir; exit 0 when title/core/package/runner resolve. It creates nothing. - --hub <p> re-executes that hub with the same arguments minus --hub, plus --title and --runner, and RETRO_HUB_APP; not when <p> is this hub, and never twice (RETRO_HUB_REEXEC). A hub from before title-app mode is given the title as a Direct-mode command line when it has direct_mode >= 2 and a ROM resolves, else refused (exit 2). Tests: retro-hub-title-test (title.json, anchors and the data dir incl. the read-only fallback, ROM checks and resolution order, mods.toml in the state dir, flat title paths). Checked by hand on Linux with the Retro-Runtime fake package core: --check-title with/without --rom, a wrong-sha ROM refused, --hub to a copy, to itself, under RETRO_HUB_REEXEC, and to the feat/direct-home hub. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mmon/ build-title-app.sh (AppImage on Linux; .app in a .dmg on macOS) and build-title-app.ps1 (Windows: one portable .exe) package a title payload with retro-hub in title-app mode and a runner beside it. The kit runs from a flat hub prefix, <hub dir>/packaging/title/, and needs nothing from this tree; the last stdout line is `app <absolute path>`. Gates, each failing the build: the payload allowlist (MANIFEST.txt exact -- it lists every file but itself and title.json -- no symlinks, no ROM/disc extension, no N64 ROM header magic, and the magic scan over the whole app); the artifact's title/ byte-identical to the payload; on Linux every library a payload ELF imports is in the AppImage, a system library, or shipped in the payload itself (the game package imports the generic core by soname, which the runner has already loaded); hub and runner --version read back out of the extracted AppImage / mounted dmg / unpacked exe; `retro-hub --check-title` from the artifact in an empty directory with a clean environment, data dir beside the AppImage / exe, never in the dmg. linuxdeploy is only pointed at retro-hub and the runner, never at the payload. Shared with the launcher's own packaging instead of copied (packaging/common/): appimage.sh (linuxdeploy fetch/run with RETRO_HUB_LINUXDEPLOY for an offline copy, AppImage extraction), AppRun.in (moved from linux/), macos.sh (Info.plist, icns, DMG, moved out of build-app.sh), Info.plist.in (moved, @name@/@BUNDLE_ID@), bundle_dylibs.sh (moved; macos/bundle_dylibs.sh forwards to it), portable.ps1 (signing and the stub + zip + RCM1 exe, out of package.ps1; the zip now always uses '/' entry names). The kit caches linuxdeploy in ${XDG_CACHE_HOME:-~/.cache}/retro-hub/tools. The portable stub recognises a title payload (title/title.json in the zip): it unpacks into <exe dir>/<id>-data/app/ (or %LOCALAPPDATA%\<id>\app\) when the payload's fingerprint changes, leaves RETCOMM_HOME unset, and starts the hub in the caller's working directory; arguments are now quoted as CommandLineToArgvW reads them back. CMake installs the kit to share/retcomm/packaging/{title,common}; the bare hub archive carries it at packaging/ (scripts/flat_hub_layout.sh, one file list shared with scripts/build-local.sh) and checks it runs from the archive. Linux: run end to end on a fake payload (Retro-Runtime's fake_pkg_core, a random-bytes ROM); the launcher's own AppImage and the bare hub archive were rebuilt through the refactored scripts. The stub was cross-built with mingw and run under wine on a fake title exe. macOS (build-title-app.sh --format dmg, build-app.sh) and Windows (build-title-app.ps1, package.ps1) are written and parse (bash -n, shellcheck, pwsh parser) but have not been run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For a port's framework (n64lle) to run and package a title app. Builds retro-hub and a retro-core-runner -- the submodule's, or one from --runtime <Retro-Runtime checkout> (its scripts/build-local.sh when present, else cmake) -- into out/local/<platform>/, laid out like the bare hub archive (scripts/flat_hub_layout.sh) plus the runner, the title-app kit and, on Windows, the portable stub. The runner is installed with an $ORIGIN RUNPATH and finds the SDL3 copied beside the hub. SDL3: found, else built from source into .cache/sdl3 (scripts/sdl3_local.sh, now shared with packaging/linux/build-local-appimage.sh); Homebrew on macOS; vcpkg at the CI baseline on Windows. Flags --debug --out --build --runtime --jobs --help (and -Debug -Out -Build -Runtime -Jobs -Help). It checks the prefix -- `title_app 1` and --package in retro-hub --version, the runner's game_package 1, the kit runs -- and prints RETRO_HUB=<absolute path> last. Outputs (out/, build-local*/, .cache/) are already gitignored. Run on Linux x86_64, with the submodule's runner and with --runtime ../Retro-Runtime-buildlocal. build-local.ps1 parses under pwsh 7.4 and has not been run on Windows; the macOS branch of build-local.sh has not been run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/RELEASES.md: the revision-4 flags in the Direct mode table (--core --title --runner --hub --check-title, paths made absolute), --runner first in the runner lookup, a Title-app mode section (finding the title, title.json schema 1, ROM resolution and refusal, the data dir beside the app and its fallback, RETRO_TITLE_STATE_DIR and mods.toml, --check-title's lines, --hub and older hubs), revision 4 in the revision notes, title_app in --version, and packaging/ in the bare hub archive. packaging/README.md: packaging/common/, and a Title apps section (usage, outputs per OS, the MANIFEST.txt rule, the three gates, tool caching, what is unrun). README.md: scripts/build-local.*. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pp mode A title app launched with --core <dev core> fell back to Direct mode and lost its bundled title. Direct mode is now only a hub with no title found. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… README.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…untime overlay) landed on main CMakeLists.txt: retro-hub gets both hub_title.cpp and retro_overlay. The Retro-Runtime pin is main's d5146fa. build-local + ctest 15/15; the Pokemon Stadium title app rebuilt on it (dev runner d5146fa), every kit gate passed, and it ran from a clean directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Title-app mode: retro-hub can run as one game's own app, and a packaging kit turns it into a local AppImage,
.dmgor single portable.exe. The contract is n64lledocs/TITLE-APP.md; its §9 records every deviation and every verification.Base:
main.feat/direct-home(#13) and the runtime overlay (#15) landed there first.157ff67mergesmainin;CMakeLists.txtconflicted, andretro-hubnow gets bothhub_title.cppandretro_overlay. After the merge, ctest passes 15/15, and the Pokemon Stadium app was rebuilt on it with every gate passing and relaunched.The hub:
title/title.jsonbeside the exe,Resources/title/on macOS, or--title.--core,--package: override the bundled title's.--runner: beatsRETRO_CORE_RUNNERand the lookup.--hub <p>: re-exec with that hub, passing along the bundled title and runner.--rom.--check-title: resolve everything and print it, with no window.--version:direct_mode 4,title_app 1.--rom, else the remembered path, else a file beside the app, else the home-page picker. It is checked againstrom.size/rom.sha256, a mismatch is refused, and the choice is remembered.mods.tomlgo to<id>-data/beside the AppImage, the portable exe or the.app. The directory is probed for writability; if it isn't writable, the user data dir is used. The runner child getsRETRO_TITLE_STATE_DIR.packaging/title/kit:.appand Windows scripts now sharepackaging/common/with it.Kit gates:
--versionare read back out of the artifact.--check-titleruns from a clean directory.title/must be byte-identical in the artifact.scripts/build-local.sh/.ps1:out/local/<platform>/, including the kit.--runtime <Retro-Runtime checkout>uses that checkout's runner.RETRO_HUB=<path>.Verified on linux-x86_64:
pokemonstadium-app; every gate passed.--core,--runnerand--hubeach took effect.Not run: macOS (the kit's dmg path,
build-app.shafter the refactor), Windows (build-title-app.ps1,build-local.ps1,package.ps1; only the portable stub ran, under wine), and the file picker used by a person.Retro-Runtime pin:
d5146fa, the pin onmain.🤖 Generated with Claude Code