runtime: skip-launcher toggle (mirrors snesrecomp)

Add a persistent skip-launcher mechanism so the runtime can boot straight
into the game without the GUI launcher window (removes the dismiss round-
trip for scripted/debug runs; the debug server then comes up immediately).

Mirrors snesrecomp's SkipLauncher:
- UserSettings.skip_launcher (config_loader.{h,cpp}): persisted under
  [launcher] skip_launcher in settings.toml; round-trips through a Launch
  (seeded before the window runs) so it is never silently dropped.
- main.cpp: --launcher forces the GUI back on even when skip_launcher is
  set; --no-launcher and the existing PSX_NO_LAUNCHER=1 env force it off.
  Precedence: --launcher wins; else skip if env / --no-launcher / setting.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthew Stanley
2026-06-15 09:34:03 -07:00
parent ce94e91ba2
commit 6d8ae54a1a
3 changed files with 37 additions and 3 deletions
+8
View File
@@ -517,6 +517,12 @@ UserSettings load_user_settings(const fs::path& path) {
s.spu_hq = toml::find<bool>(a, "spu_hq"); s.has_spu_hq = true;
});
}
if (doc.contains("launcher")) {
const toml::value& l = toml::find(doc, "launcher");
if (l.contains("skip_launcher")) try_get([&]{
s.skip_launcher = toml::find<bool>(l, "skip_launcher"); s.has_skip_launcher = true;
});
}
if (doc.contains("bios")) {
const toml::value& b = toml::find(doc, "bios");
if (b.contains("path")) try_get([&]{
@@ -611,6 +617,8 @@ bool save_user_settings(const fs::path& path, const UserSettings& s) {
f << "\n[audio]\n";
if (s.has_spu_hq)
f << "spu_hq = " << (s.spu_hq ? "true" : "false") << "\n";
if (s.has_skip_launcher)
f << "\n[launcher]\nskip_launcher = " << (s.skip_launcher ? "true" : "false") << "\n";
if (s.has_bios_path)
f << "\n[bios]\npath = \"" << fwd(s.bios_path) << "\"\n";
if (s.has_disc_path)
+4
View File
@@ -226,6 +226,10 @@ struct UserSettings {
bool has_texture_filter = false; int texture_filter = 0; // 0=nearest,1=bilinear
bool has_screen_kind = false; int screen_kind = 0; // 0..3 (ScreenKind)
bool has_auto_skip_fmv = false; bool auto_skip_fmv = false; // skip FMVs
// [launcher] — when true, boot straight into the game and skip the GUI
// launcher window (mirrors snesrecomp's SkipLauncher). Overridable per-run:
// `--launcher` forces the GUI back on; `PSX_NO_LAUNCHER=1` forces it off.
bool has_skip_launcher = false; bool skip_launcher = false;
// [audio]
bool has_spu_hq = false; bool spu_hq = false;
// [bios] / [disc] / [memcard]
+25 -3
View File
@@ -1341,11 +1341,18 @@ int main(int argc, char** argv) {
const char* game_config_path = nullptr;
const char* disc_override_path = nullptr;
bool bios_from_cli = false; /* CLI --bios/positional wins over settings.toml */
/* Launcher overrides (mirrors snesrecomp): --launcher forces the GUI back on
* even when [launcher] skip_launcher = true is set; --no-launcher (and the
* PSX_NO_LAUNCHER env) forces it off. --launcher wins if both are given. */
bool force_launcher = false;
bool force_no_launcher = false;
/* Parse args.
* --bios <path> override the compile-time BIOS path
* --game <toml> load a game config (single source of truth for
* disc / memcard / window title / debug port)
* --disc <path> override the game config disc path
* --launcher force the GUI launcher (overrides skip_launcher)
* --no-launcher skip the GUI launcher (boot straight in)
* <positional> deprecated alias for --bios
* No --memcard-dir / --game-root flags: those are config-driven. */
for (int i = 1; i < argc; i++) {
@@ -1356,6 +1363,10 @@ int main(int argc, char** argv) {
game_config_path = argv[++i];
} else if (std::strcmp(argv[i], "--disc") == 0 && i + 1 < argc) {
disc_override_path = argv[++i];
} else if (std::strcmp(argv[i], "--launcher") == 0) {
force_launcher = true;
} else if (std::strcmp(argv[i], "--no-launcher") == 0) {
force_no_launcher = true;
} else if (argv[i][0] != '-') {
bios_path = argv[i];
bios_from_cli = true;
@@ -1462,12 +1473,14 @@ int main(int argc, char** argv) {
* bundled game.toml. Any field present there overrides the config value;
* the command line (--bios/--disc) still wins over the file. Absent =>
* fall through to game.toml. The file is the launcher's persistence. */
bool skip_launcher_setting = false; /* [launcher] skip_launcher from settings.toml */
std::string settings_bios_storage; /* must outlive resolve_bios_for_runtime */
{
std::filesystem::path settings_path =
exe_dir_from_argv(argv[0]) / "settings.toml";
const PSXRecompV4::UserSettings us =
PSXRecompV4::load_user_settings(settings_path);
if (us.has_skip_launcher) skip_launcher_setting = us.skip_launcher;
if (us.has_renderer) g_video_renderer = us.renderer;
if (us.has_supersampling) g_video_scale = us.supersampling;
if (us.has_window_width) g_video_win_w = us.window_width;
@@ -1502,9 +1515,16 @@ int main(int argc, char** argv) {
* boots. Seeded with the effective settings (game.toml ∪ settings.toml);
* on LAUNCH the user's choices are persisted to settings.toml and applied.
* The launcher window/context is fully torn down before the emulator's own
* window is created, so the emulator boot path below is untouched. Skipped
* via PSX_NO_LAUNCHER=1 (e.g. CI / scripted runs). */
if (!std::getenv("PSX_NO_LAUNCHER")) {
* window is created, so the emulator boot path below is untouched.
*
* Skip the GUI (boot straight in) when ANY of: PSX_NO_LAUNCHER=1 env,
* --no-launcher, or the persisted [launcher] skip_launcher setting — unless
* --launcher forces it back on (mirrors snesrecomp's SkipLauncher / --launcher).
* This removes the dismiss-the-launcher round-trip for scripted/debug runs. */
const bool want_launcher =
force_launcher ||
(!std::getenv("PSX_NO_LAUNCHER") && !force_no_launcher && !skip_launcher_setting);
if (want_launcher) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) == 0) {
PSXRecompV4::UserSettings seed;
seed.renderer = g_video_renderer; seed.has_renderer = true;
@@ -1514,6 +1534,7 @@ int main(int argc, char** argv) {
seed.screen_kind = g_video_screen; seed.has_screen_kind = true;
seed.auto_skip_fmv = (g_auto_skip_fmv != 0); seed.has_auto_skip_fmv = true;
seed.spu_hq = g_audio_spu_hq; seed.has_spu_hq = true;
seed.skip_launcher = skip_launcher_setting; seed.has_skip_launcher = true;
if (bios_path && bios_path[0]) { seed.bios_path = bios_path; seed.has_bios_path = true; }
if (!resolved_disc.empty()) { seed.disc_path = resolved_disc; seed.has_disc_path = true; }
seed.memcard_dir = memcard_dir; seed.has_memcard_dir = true;
@@ -1576,6 +1597,7 @@ int main(int argc, char** argv) {
g_video_screen = seed.screen_kind;
g_auto_skip_fmv = seed.auto_skip_fmv ? 1 : 0;
g_audio_spu_hq = seed.spu_hq;
skip_launcher_setting = seed.skip_launcher;
if (seed.has_bios_path) {
settings_bios_storage = seed.bios_path.string();
bios_path = settings_bios_storage.c_str();