diff --git a/Core/System.cpp b/Core/System.cpp index 73606e9c08..fb7e378e69 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -878,6 +878,18 @@ bool PSP_InitStart(const CoreParameter &coreParam) { } } + if (filename.GetFilename() == "vshmain.prx") { + // A firmware installed for the other model makes the XMB look for files it doesn't have. + InstalledFirmwareInfo firmware; + ReadInstalledFirmwareInfo(g_Config.nandRootDirectory, &firmware, false); + if (!InstalledFirmwareFitsModel(firmware, g_Config.iPSPModel)) { + auto sy = GetI18NCategory(I18NCat::SYSTEM); + g_OSD.Show(OSDType::MESSAGE_WARNING, ApplySafeSubstitutions( + sy->T("FirmwareOtherModel", "Firmware %1 was installed for a different PSP model, so the XMB may not work. Install it again for this model."), + firmware.version), 10.0f, "firmware_model"); + } + } + if (System_GetPropertyBool(SYSPROP_ENOUGH_RAM_FOR_FULL_ISO)) { if (g_Config.bCacheFullIsoInRam) { switch (fileType) { diff --git a/Core/Util/PSARUnpack.cpp b/Core/Util/PSARUnpack.cpp index c5b5e44ac2..44da1af5c7 100644 --- a/Core/Util/PSARUnpack.cpp +++ b/Core/Util/PSARUnpack.cpp @@ -1323,6 +1323,31 @@ void ReadInstalledFirmwareInfo(const Path &nandRoot, InstalledFirmwareInfo *info if (File::ReadTextFileToString(flash0 / "vsh/etc/version.txt", &versionTxt)) { ParseVersionTxt(versionTxt, info); } + + // 3.x names them nandipl_psp1000.bin and nandipl_psp2000.ipl (psp02h in 4.x), 5.x on nandipl_01g.bin + // and so on. Older firmwares have one psp_ipl.bin or psp_nandipl.bin for every model. + std::vector ipls; + File::GetFilesInDir(nandRoot / "ipl", &ipls); + bool anyModelTagged = false; + for (const File::FileInfo &ipl : ipls) { + const std::string name = ipl.name; + const bool for1000 = name.find("psp1000") != std::string::npos || name.find("_01g") != std::string::npos; + const bool for2000 = name.find("psp2000") != std::string::npos || name.find("psp02h") != std::string::npos || name.find("_02g") != std::string::npos; + info->forPSP1000 |= for1000; + info->forPSP2000 |= for2000; + anyModelTagged |= for1000 || for2000 || name.find("g.") != std::string::npos; + } + if (!anyModelTagged) { + info->forPSP1000 = true; + info->forPSP2000 = true; + } +} + +bool InstalledFirmwareFitsModel(const InstalledFirmwareInfo &info, int pspModel) { + if (!info.hasVsh) { + return true; + } + return pspModel == PSP_MODEL_FAT ? info.forPSP1000 : info.forPSP2000; } bool EraseInstalledFirmware(const Path &nandRoot, std::string *error) { diff --git a/Core/Util/PSARUnpack.h b/Core/Util/PSARUnpack.h index 0d07e2c9de..8535a48794 100644 --- a/Core/Util/PSARUnpack.h +++ b/Core/Util/PSARUnpack.h @@ -163,6 +163,11 @@ struct InstalledFirmwareInfo { int kernelModuleCount = 0; // Files in flash0:/kd, which is what --disable-hle wants. int fileCount = 0; u64 totalSize = 0; + // Which of the models we emulate the install has files for, from the names of its ipl/ files + // ("nandipl_01g.bin", "nandipl_psp2000.ipl"). An install for every model has both, and so does + // a firmware from before the PSP-2000, whose one ipl serves every model. + bool forPSP1000 = false; + bool forPSP2000 = false; }; // Walks the NAND directory (the one holding flash0/flash1). Pass countFiles = false when @@ -170,6 +175,10 @@ struct InstalledFirmwareInfo { // and a full firmware is a few hundred files, which isn't free on a phone's storage. void ReadInstalledFirmwareInfo(const Path &nandRoot, InstalledFirmwareInfo *info, bool countFiles = true); +// False if a full firmware is installed and has no files for pspModel (PSP_MODEL_*). Its XMB then +// looks for the other model's files, which aren't there. +bool InstalledFirmwareFitsModel(const InstalledFirmwareInfo &info, int pspModel); + // Wipes what's in the NAND directory: flash0, flash1 and ipl. Two firmwares can't be merged - // files a newer one dropped would linger and still get loaded - so an install starts from empty. // Deliberately leaves an install's staging directory alone: InstallFirmware() calls this with a diff --git a/UI/Settings/SystemPage.cpp b/UI/Settings/SystemPage.cpp index dad32cb3ec..99e469cf9b 100644 --- a/UI/Settings/SystemPage.cpp +++ b/UI/Settings/SystemPage.cpp @@ -26,6 +26,7 @@ #include "Common/Data/Text/I18n.h" #include "Common/File/FileUtil.h" #include "Common/File/Path.h" +#include "Common/StringUtils.h" #include "Common/GPU/thin3d.h" #include "Common/Log.h" #include "Common/Render/ManagedTexture.h" @@ -44,6 +45,7 @@ #include "Core/Reporting.h" #include "Core/System.h" #include "Core/Util/PathUtil.h" +#include "Core/Util/PSARUnpack.h" #include "UI/Background.h" #include "UI/GameSettingsScreen.h" #include "UI/MemStickScreen.h" @@ -173,6 +175,31 @@ static void OnJitAffectingSetting() { System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT); } +// A firmware only has the files of the model it was installed for, and its XMB looks for the files of +// the model we report. So a change to a model the installed firmware doesn't fit gets a warning. +static void OnPSPModelChanged(SettingsPageHost *host, int previous) { + if (g_Config.iPSPModel == previous) { + return; + } + InstalledFirmwareInfo firmware; + ReadInstalledFirmwareInfo(GetSysDirectory(DIRECTORY_NAND), &firmware, false); + if (InstalledFirmwareFitsModel(firmware, g_Config.iPSPModel)) { + return; + } + auto di = GetI18NCategory(I18NCat::DIALOG); + auto sy = GetI18NCategory(I18NCat::SYSTEM); + const std::string message = ApplySafeSubstitutions( + sy->T("FirmwareModelChange", "Firmware %1 is installed for a different PSP model. With this one, the XMB may not work until you install the firmware again."), + firmware.version); + host->PageScreenManager()->push(new UI::MessagePopupScreen(sy->T("PSP Model"), message, di->T("OK"), di->T("Cancel"), [host, previous](bool yes) { + if (!yes) { + g_Config.iPSPModel = previous; + } + // Either way, so the next change compares against the model now set. + host->RecreatePageViews(); + })); +} + // Using SYSTEM translations static void BuildPSPSettingsPage(SettingsPage *page) { SettingsPageHost *host = page->Host(); @@ -181,8 +208,11 @@ static void BuildPSPSettingsPage(SettingsPage *page) { // The ordering here is simply mapping directly to PSP_SYSTEMPARAM_LANGUAGE_*. page->Setting(&g_Config.iLanguage); + const int modelShown = g_Config.iPSPModel; page->Setting(&g_Config.iPSPModel).EnabledFunc([] { return !PSP_IsInited(); + }).OnChange([host, modelShown](UI::EventParams &) { + OnPSPModelChanged(host, modelShown); }); page->Custom("Nickname", [host](UI::ViewGroup *parent) { return AddNicknameChoice(host, parent); diff --git a/assets/lang/en_US.ini b/assets/lang/en_US.ini index 15fd5e9fb3..377cb41fc6 100644 --- a/assets/lang/en_US.ini +++ b/assets/lang/en_US.ini @@ -1631,7 +1631,9 @@ Files = Files Final processed image = Final processed image Firmware installation failed = Firmware installation failed Firmware version = Firmware version +FirmwareModelChange = Firmware %1 is installed for a different PSP model. With this one, the XMB may not work until you install the firmware again. FirmwareNeedsNoGame = Stop the game to change the installed firmware +FirmwareOtherModel = Firmware %1 was installed for a different PSP model, so the XMB may not work. Install it again for this model. FirmwareSources = PPSSPP has its own replacements for the PSP's system files, so a firmware is optional. Installing one gets you the real fonts, and lets you launch the XMB. Most game discs carry a firmware updater - open a game's info screen to install from it. Fonts = Fonts Grid = Grid