From 3a5c5a51c5f9aa386b2503539acb8a6223c3c7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Oct 2026 19:47:51 +0200 Subject: [PATCH 1/3] Allow --mount in the app, so --vsh can boot with a disc inserted --mount ISO used to be headless-only, and EmuScreen always cleared CoreParameter::mountIso. Pass it from the command line into the first EmuScreen, which keeps it across resets and drops it when another game is booted. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/CmdLine.cpp | 2 +- Core/CmdLine.h | 2 +- Core/PSPLoaders.cpp | 2 +- UI/Background.h | 1 + UI/EmuScreen.cpp | 7 ++++--- UI/EmuScreen.h | 4 +++- UI/MiscScreens.cpp | 4 ++-- UI/NativeApp.cpp | 4 +++- 8 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Core/CmdLine.cpp b/Core/CmdLine.cpp index 02c1248873..ad1ae1a746 100644 --- a/Core/CmdLine.cpp +++ b/Core/CmdLine.cpp @@ -226,7 +226,7 @@ static const CommandLineParam g_autoParams[] = { {POFF(timeoutWall), CmdParamType::Double, "timeout", '\0', "Alias for --timeout-wall", CmdLineMode::Headless}, {POFF(frameStats), CmdParamType::Bool, "frame-stats", '\0', "Print flips per emulated second at the end of the run, counted from --state's load", CmdLineMode::Headless}, {POFF(maxScreenshotError), CmdParamType::Double, "max-mse", '\0', "Maximum allowed MSE error for screenshot comparison", CmdLineMode::Headless}, - {POFF(mountIso), CmdParamType::String, "mount", 'm', "Mount ISO/CSO on umd1:", CmdLineMode::Headless}, + {POFF(mountIso), CmdParamType::String, "mount", 'm', "Mount ISO/CSO on umd1: when booting an ELF/PBP or --vsh"}, {POFF(unpackUpdater), CmdParamType::String, "unpack-updater", '\0', "Unpack the firmware in an updater EBOOT.PBP into DIR and exit", CmdLineMode::Headless}, {POFF(unpackUpdaterModel), CmdParamType::String, "unpack-updater-model", '\0', "PSP model to unpack for (01g..12g, default any)", CmdLineMode::Headless}, {POFF(unpackUpdaterFilter), CmdParamType::String, "unpack-updater-filter", '\0', "Only unpack entries under this path, e.g. flash0:/font/", CmdLineMode::Headless}, diff --git a/Core/CmdLine.h b/Core/CmdLine.h index 2982435ad7..e5dfa88fa7 100644 --- a/Core/CmdLine.h +++ b/Core/CmdLine.h @@ -215,7 +215,7 @@ struct CommandLineOptions { // Headless: preserve the alpha channel when saving PNG screenshots. std::optional screenshotSaveKeepAlpha; - // Headless: mount an ISO/CSO on umd1:. + // Mount an ISO/CSO on umd1: when booting an ELF/PBP (or the VSH). std::optional mountIso; // Headless: also log through OutputDebugString (Windows). std::optional odsLog; diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index f2287e90dd..01572b51cb 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -529,7 +529,7 @@ static Path NormalizePath(const Path &path) { } bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string_view discId, bool loadGameConfigs, std::string *error_string) { - // This is really just for headless, might need tweaking later. + // Set by --mount, which also lets the VSH boot with a disc in the drive. if (PSP_CoreParameter().mountIsoLoader != nullptr) { std::shared_ptr bd(ConstructBlockDevice(PSP_CoreParameter().mountIsoLoader, error_string)); if (bd) { diff --git a/UI/Background.h b/UI/Background.h index fd64e9dff9..c6a78723c5 100644 --- a/UI/Background.h +++ b/UI/Background.h @@ -6,6 +6,7 @@ class UIContext; extern Path boot_filename; +extern Path boot_mountIso; void UIBackgroundInit(UIContext &dc); void UIBackgroundShutdown(); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index c7b21be1df..e401c974f1 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -161,8 +161,8 @@ void EmuScreen::SetPSPAnalog(int iInternalScreenRotation, int stick, float x, fl __CtrlSetAnalogXY(stick, x, y); } -EmuScreen::EmuScreen(const Path &filename) - : gamePath_(filename) { +EmuScreen::EmuScreen(const Path &filename, const Path &mountIso) + : gamePath_(filename), mountIso_(mountIso) { saveStateSlot_ = SaveState::GetCurrentSlot(); g_controlMapper.AddListener(this); @@ -321,7 +321,7 @@ void EmuScreen::ProcessGameBoot(const Path &filename) { coreParam.graphicsContext = PSP_CoreParameter().graphicsContext; coreParam.enableSound = g_Config.bEnableSound; coreParam.fileToStart = filename; - coreParam.mountIso.clear(); + coreParam.mountIso = mountIso_; coreParam.mountRoot = g_Config.mountRoot; coreParam.startBreak = !g_Config.bAutoRun; coreParam.headLess = false; @@ -619,6 +619,7 @@ void EmuScreen::sendMessage(UIMessage message, const char *value) { bootPending_ = true; bootIsReset_ = false; gamePath_ = newGamePath; + mountIso_.clear(); } } else if (message == UIMessage::CONFIG_LOADED) { // In case we need to position touch controls differently. diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 790143f27a..87905d024f 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -40,7 +40,8 @@ struct ImGuiContext; class EmuScreen : public UIScreen, protected ControlListener { public: - EmuScreen(const Path &filename); + // mountIso, if set, is mounted on umd1: when filename is an ELF/PBP (like the VSH). + EmuScreen(const Path &filename, const Path &mountIso = Path()); ~EmuScreen(); const char *tag() const override { return "Emu"; } @@ -101,6 +102,7 @@ private: bool bootPending_ = true; bool bootIsReset_ = false; Path gamePath_; + Path mountIso_; bool quit_ = false; std::string errorMessage_; diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index f1db4d1054..dede720f6d 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -367,7 +367,7 @@ void LogoScreen::Next() { switch (afterLogoScreen_) { case AfterLogoScreen::TO_GAME_SETTINGS: if (!gamePath.empty()) { - screenManager()->switchScreen(new EmuScreen(gamePath)); + screenManager()->switchScreen(new EmuScreen(gamePath, boot_mountIso)); } else { screenManager()->switchScreen(new MainScreen()); } @@ -379,7 +379,7 @@ void LogoScreen::Next() { case AfterLogoScreen::DEFAULT: default: if (boot_filename.size()) { - screenManager()->switchScreen(new EmuScreen(gamePath)); + screenManager()->switchScreen(new EmuScreen(gamePath, boot_mountIso)); } else { screenManager()->switchScreen(new MainScreen()); } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 69190d2bd6..ae16f3c8c3 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -411,6 +411,7 @@ std::thread *graphicsLoadThread; // globals Path boot_filename; +Path boot_mountIso; // This is called before NativeInit so we do a little bit of initialization here. void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, bool *landscape, std::string *version) { @@ -701,6 +702,7 @@ void NativeInit(int argc, const char *argv[], const CommandLineOptions &cmdLineO if (boot_filename.empty() && cmdLineOptions.bootVSH.value_or(false)) { boot_filename = g_Config.nandRootDirectory / "flash0/vsh/module/vshmain.prx"; } + boot_mountIso = cmdLineOptions.mountIso.has_value() ? Path(cmdLineOptions.mountIso.value()) : Path(); if (cmdLineOptions.appendConfig.has_value()) { g_Config.SetAppendedConfigIni(Path(cmdLineOptions.appendConfig.value())); @@ -833,7 +835,7 @@ void NativeInit(int argc, const char *argv[], const CommandLineOptions &cmdLineO } } else if (skipLogo && !boot_filename.empty()) { INFO_LOG(Log::System, "Launching EmuScreen with boot filename '%s'", boot_filename.c_str()); - g_screenManager->switchScreen(new EmuScreen(boot_filename)); + g_screenManager->switchScreen(new EmuScreen(boot_filename, boot_mountIso)); } else { g_screenManager->switchScreen(new LogoScreen(AfterLogoScreen::DEFAULT)); } From 2d116a5b76aca5787807faec85a21f696cb49a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Oct 2026 19:52:38 +0200 Subject: [PATCH 2/3] sceReg: Let a key read a bool setting, use it for VSH UMD autoboot KeyValue gets an optional pointer to a g_Config bool, which the key reads as 0/1. The setting wins over guest writes: since category_version is kept low, the VSH upgrades the registry at every boot and rewrites every key with its default. /CONFIG/SYSTEM/umd_autoboot reads the new SystemParam VshUmdAutoboot (default true, as before). Turned off, --vsh --mount stays in the XMB with the disc in the Game column instead of starting it. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/Config.cpp | 1 + Core/Config.h | 2 ++ Core/HLE/sceReg.cpp | 12 +++++++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index eaee0d4ec4..53a4862374 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1121,6 +1121,7 @@ static const ConfigSetting systemParamSettings[] = { ConfigSetting("BypassOSKWithKeyboard", SETTING(g_Config, bBypassOSKWithKeyboard), false, CfgFlag::PER_GAME), ConfigSetting("WlanPowerSave", SETTING(g_Config, bWlanPowerSave), (bool) PSP_SYSTEMPARAM_WLAN_POWERSAVE_OFF, CfgFlag::PER_GAME), ConfigSetting("EncryptSave", SETTING(g_Config, bEncryptSave), true, CfgFlag::PER_GAME | CfgFlag::REPORT), + ConfigSetting("VshUmdAutoboot", SETTING(g_Config, bVshUmdAutoboot), true, CfgFlag::DEFAULT), ConfigSetting("MemStickSize", SETTING(g_Config, iMemStickSizeGB), 16, CfgFlag::DEFAULT), ConfigSetting("FuseID", SETTING(g_Config, sFuseID), "", CfgFlag::DEFAULT), }; diff --git a/Core/Config.h b/Core/Config.h index d746b7227b..3b4bab60e1 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -594,6 +594,8 @@ public: int iButtonPreference; int iLockParentalLevel; bool bEncryptSave; + // /CONFIG/SYSTEM/umd_autoboot: whether the VSH starts an inserted disc by itself. + bool bVshUmdAutoboot; // Networking bool bEnableAdhocServer; diff --git a/Core/HLE/sceReg.cpp b/Core/HLE/sceReg.cpp index a8156576dd..5d1b27c7f0 100644 --- a/Core/HLE/sceReg.cpp +++ b/Core/HLE/sceReg.cpp @@ -8,6 +8,7 @@ #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeMap.h" #include "Common/StringUtils.h" +#include "Core/Config.h" #define SYSTEM_REGISTRY "/system" #define REG_KEYNAME_SIZE 27 @@ -58,6 +59,9 @@ struct KeyValue { const char *strValue; // also can be used for bin. Note: can't be std::string/string_view, will cut off the length. int intValue; const KeyValue *dirContents; // intValue is the count. + // If set, an INT key reads this setting (as 0 or 1), even after the guest writes it: the VSH + // rewrites every key with its default at each boot, since category_version is below its schema. + const bool *boolSetting = nullptr; }; // Guest-written registry values (sceRegCreateKey / sceRegSetKeyValue), keyed by category path @@ -764,7 +768,7 @@ static const KeyValue tree_CONFIG_SYSTEM_CHARACTER_SET[] = { static const KeyValue tree_CONFIG_SYSTEM[] = { { "owner_name", ValueType::STR, "P939", 5 }, { "backlight_brightness", ValueType::INT, "", (int)0x3 }, // decimal: 3 - { "umd_autoboot", ValueType::INT, "", (int)0x1 }, // decimal: 1 + { "umd_autoboot", ValueType::INT, "", (int)0x1, nullptr, &g_Config.bVshUmdAutoboot }, // decimal: 1 { "usb_charge", ValueType::INT, "", (int)0x1 }, // decimal: 1 { "umd_cache", ValueType::INT, "", (int)0x1 }, // decimal: 1 { "usb_auto_connect", ValueType::INT, "", (int)0x1 }, // decimal: 1 @@ -1090,6 +1094,12 @@ static const MergedCategory &GetMergedCategory(const std::string &path) { } } + for (auto &kv : merged.keyvals) { + if (kv.boolSetting) { + kv.intValue = *kv.boolSetting ? 1 : 0; + } + } + return merged; } From 5a8e865532c1c648cf98e8cc4ec51cca51a4d760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Oct 2026 19:57:01 +0200 Subject: [PATCH 3/3] Config: Default VshUmdAutoboot to off Booting the VSH with a disc now stays in the XMB, with the disc in the Game column, rather than starting the game right away. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/Config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 53a4862374..ab4fb941b4 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1121,7 +1121,7 @@ static const ConfigSetting systemParamSettings[] = { ConfigSetting("BypassOSKWithKeyboard", SETTING(g_Config, bBypassOSKWithKeyboard), false, CfgFlag::PER_GAME), ConfigSetting("WlanPowerSave", SETTING(g_Config, bWlanPowerSave), (bool) PSP_SYSTEMPARAM_WLAN_POWERSAVE_OFF, CfgFlag::PER_GAME), ConfigSetting("EncryptSave", SETTING(g_Config, bEncryptSave), true, CfgFlag::PER_GAME | CfgFlag::REPORT), - ConfigSetting("VshUmdAutoboot", SETTING(g_Config, bVshUmdAutoboot), true, CfgFlag::DEFAULT), + ConfigSetting("VshUmdAutoboot", SETTING(g_Config, bVshUmdAutoboot), false, CfgFlag::DEFAULT), ConfigSetting("MemStickSize", SETTING(g_Config, iMemStickSizeGB), 16, CfgFlag::DEFAULT), ConfigSetting("FuseID", SETTING(g_Config, sFuseID), "", CfgFlag::DEFAULT), };