From 57e96b2d81b062b351e2b29a4edf9b86c4c9a208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 9 Sep 2026 12:07:28 -0600 Subject: [PATCH] Add a PSP Firmware screen under Settings > Tools Shows what's actually in PSP/NAND - nothing, a fonts-only partial install, or a full firmware with its version, build date and region - read from flash0:/vsh/etc/version.txt, which is present both in a PSAR-unpacked install and a NAND dumped off hardware. Also offers to install an updater, erase the NAND, and launch the XMB, the last one gated on FirmwareVersionSupportsVSH() since the module patches that get vshmain.prx running are tied to 6.61's offsets. Installing a firmware now erases flash0/flash1/ipl first - two firmwares can't be merged, a file the new one doesn't have would linger and still get loaded. Co-Authored-By: Claude Opus 5 --- Core/Util/PSARUnpack.cpp | 115 +++++++++++++++++++ Core/Util/PSARUnpack.h | 29 +++++ UI/CMakeLists.txt | 2 + UI/FirmwareScreen.cpp | 181 ++++++++++++++++++++++++++++++ UI/FirmwareScreen.h | 52 +++++++++ UI/GameSettingsScreen.cpp | 4 + UI/InstallUpdateScreen.cpp | 24 ++-- UI/InstallUpdateScreen.h | 5 +- UI/MainScreen.cpp | 2 +- UI/UI.vcxproj | 2 + UI/UI.vcxproj.filters | 6 + UWP/UI_UWP/UI_UWP.vcxproj | 2 + UWP/UI_UWP/UI_UWP.vcxproj.filters | 6 + android/jni/Android.mk | 1 + 14 files changed, 422 insertions(+), 9 deletions(-) create mode 100644 UI/FirmwareScreen.cpp create mode 100644 UI/FirmwareScreen.h diff --git a/Core/Util/PSARUnpack.cpp b/Core/Util/PSARUnpack.cpp index a0880bc6fa..3993210bbd 100644 --- a/Core/Util/PSARUnpack.cpp +++ b/Core/Util/PSARUnpack.cpp @@ -22,6 +22,7 @@ #include "zlib.h" +#include "Common/File/DirListing.h" #include "Common/File/FileUtil.h" #include "Common/File/Path.h" #include "Common/Log.h" @@ -1049,6 +1050,120 @@ static bool ReadUpdaterPSAR(const Path &filename, std::vector *psar, std::st return false; } +// Adds up everything under a directory. Returns false if the directory isn't there at all, which +// is how the caller tells "no firmware" from "an empty one". +static bool ScanDirRecursive(const Path &dir, int *fileCount, u64 *totalSize) { + std::vector files; + if (!File::GetFilesInDir(dir, &files)) { + return false; + } + for (const File::FileInfo &file : files) { + if (file.isDirectory) { + ScanDirRecursive(file.fullName, fileCount, totalSize); + } else { + (*fileCount)++; + *totalSize += file.size; + } + } + return true; +} + +static int CountFilesInDir(const Path &dir) { + std::vector files; + if (!File::GetFilesInDir(dir, &files)) { + return 0; + } + int count = 0; + for (const File::FileInfo &file : files) { + if (!file.isDirectory) { + count++; + } + } + return count; +} + +// flash0:/vsh/etc/version.txt, as the firmware itself writes it: +// release:6.60: +// build:5455,0,3,1,0:builder@vsh-build6 +// system:57716@release_660,0x06060010: +// vsh:p6616@release_660,v58533@release_660,20110727: +// target:1:WorldWide +// The date at the end of the vsh line is the only date in there, and the target line's last +// field is the region the firmware was built for. +static void ParseVersionTxt(std::string_view contents, InstalledFirmwareInfo *info) { + std::vector lines; + SplitString(contents, '\n', lines); + for (std::string_view line : lines) { + line = StripSpaces(line); + if (startsWith(line, "release:")) { + std::string_view rest = line.substr(strlen("release:")); + const size_t colon = rest.find(':'); + info->version = std::string(colon == std::string_view::npos ? rest : rest.substr(0, colon)); + } else if (startsWith(line, "vsh:")) { + // The build date is the last comma-separated field, e.g. "...,20110727:". + std::string_view rest = line.substr(strlen("vsh:")); + if (!rest.empty() && rest.back() == ':') { + rest = rest.substr(0, rest.size() - 1); + } + const size_t comma = rest.rfind(','); + if (comma != std::string_view::npos) { + const std::string_view date = rest.substr(comma + 1); + if (date.size() == 8 && std::all_of(date.begin(), date.end(), [](char c) { return c >= '0' && c <= '9'; })) { + info->buildDate = StringFromFormat("%.*s-%.*s-%.*s", + 4, date.data(), 2, date.data() + 4, 2, date.data() + 6); + } + } + } else if (startsWith(line, "target:")) { + const size_t colon = line.rfind(':'); + if (colon != std::string_view::npos && colon + 1 < line.size()) { + info->target = std::string(line.substr(colon + 1)); + } + } + } +} + +void ReadInstalledFirmwareInfo(const Path &nandRoot, InstalledFirmwareInfo *info) { + *info = InstalledFirmwareInfo{}; + + const Path flash0 = nandRoot / "flash0"; + for (const char *dir : { "flash0", "flash1", "ipl" }) { + ScanDirRecursive(nandRoot / dir, &info->fileCount, &info->totalSize); + } + info->anythingInstalled = info->fileCount > 0; + if (!info->anythingInstalled) { + return; + } + + info->fontCount = CountFilesInDir(flash0 / "font"); + info->kernelModuleCount = CountFilesInDir(flash0 / "kd"); + info->hasVsh = File::Exists(flash0 / "vsh/module/vshmain.prx"); + + std::string versionTxt; + if (File::ReadTextFileToString(flash0 / "vsh/etc/version.txt", &versionTxt)) { + ParseVersionTxt(versionTxt, info); + } +} + +bool EraseInstalledFirmware(const Path &nandRoot, std::string *error) { + bool success = true; + for (const char *dir : { "flash0", "flash1", "ipl" }) { + const Path path = nandRoot / dir; + if (File::Exists(path) && !File::DeleteDirRecursively(path)) { + ERROR_LOG(Log::Loader, "Failed to erase %s", path.c_str()); + if (error) { + *error = "Couldn't erase " + path.ToString(); + } + success = false; + } + } + return success; +} + +bool FirmwareVersionSupportsVSH(std::string_view version) { + // See the module patches in sceKernelModule.cpp - they're tied to 6.61's exact offsets. + return version == "6.61"; +} + std::string BundledUpdateInfo::Describe() const { if (!present) { return std::string(); diff --git a/Core/Util/PSARUnpack.h b/Core/Util/PSARUnpack.h index f3ef8a5d18..b200955ed3 100644 --- a/Core/Util/PSARUnpack.h +++ b/Core/Util/PSARUnpack.h @@ -141,6 +141,35 @@ bool ReadBundledUpdateInfo(IFileSystem *fs, std::string_view pathPrefix, Bundled // decryption needed, so it's cheap enough to check every disc with. Empty if there's no updater. std::string ReadUpdaterVersion(const Path &filename); +// What's actually in the NAND directory right now. That can be anything from a handful of fonts +// we pulled off a game disc to a full firmware unpacked from an updater, so this reports what's +// there rather than assuming one or the other. +struct InstalledFirmwareInfo { + bool anythingInstalled = false; // flash0/flash1 exist and hold at least one file. + // From flash0:/vsh/etc/version.txt, which only a full firmware install has. Empty if all + // that's there is a partial install like the fonts. + std::string version; // "6.60" + std::string buildDate; // "2011-07-27". Empty if the file doesn't spell one out. + std::string target; // "WorldWide" + bool hasVsh = false; // flash0:/vsh/module/vshmain.prx - what launching the XMB needs. + int fontCount = 0; // Files in flash0:/font, which is all sceFont wants. + int kernelModuleCount = 0; // Files in flash0:/kd, which is what --disable-hle wants. + int fileCount = 0; + u64 totalSize = 0; +}; + +// Walks the NAND directory (the one holding flash0/flash1). A full firmware is only a few +// hundred files, so this is cheap, but it does read the whole tree. +void ReadInstalledFirmwareInfo(const Path &nandRoot, InstalledFirmwareInfo *info); + +// 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. +bool EraseInstalledFirmware(const Path &nandRoot, std::string *error); + +// The firmware versions we can actually boot the VSH (XMB) on. Every other version loads, but +// the module patches it needs are version-specific, so it won't get anywhere. +bool FirmwareVersionSupportsVSH(std::string_view version); + // The same three, for the disc mounted as disc0: - i.e. the game that's running. These read // through the mounted filesystem instead of opening the image a second time, which also means // they work for the shapes that aren't an image at all, like a folder-based "disc". diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 3a43190dc3..d163777075 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -99,6 +99,8 @@ list(APPEND UISource UploadScreen.cpp CwCheatScreen.h CwCheatScreen.cpp + FirmwareScreen.h + FirmwareScreen.cpp InstallUpdateScreen.h InstallUpdateScreen.cpp InstallZipScreen.h diff --git a/UI/FirmwareScreen.cpp b/UI/FirmwareScreen.cpp new file mode 100644 index 0000000000..ab042c7376 --- /dev/null +++ b/UI/FirmwareScreen.cpp @@ -0,0 +1,181 @@ +// Copyright (c) 2026- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Common/Data/Text/I18n.h" +#include "Common/Data/Text/Parsers.h" +#include "Common/Log.h" +#include "Common/StringUtils.h" +#include "Common/System/Request.h" +#include "Common/System/System.h" +#include "Common/UI/Notice.h" +#include "Common/UI/PopupScreens.h" +#include "Common/UI/ScreenManager.h" +#include "Common/UI/View.h" +#include "Common/UI/ViewGroup.h" + +#include "Core/Config.h" +#include "Core/System.h" +#include "Core/Util/PathUtil.h" + +#include "UI/FirmwareScreen.h" +#include "UI/InstallUpdateScreen.h" +#include "UI/MiscViews.h" + +std::string_view FirmwareScreen::GetTitle() const { + auto sy = GetI18NCategory(I18NCat::SYSTEM); + return sy->T("PSP Firmware"); +} + +void FirmwareScreen::BeforeCreateViews() { + nandRoot_ = GetSysDirectory(DIRECTORY_NAND); + ReadInstalledFirmwareInfo(nandRoot_, &info_); +} + +void FirmwareScreen::CreateContentViews(UI::ViewGroup *parent) { + using namespace UI; + + auto sy = GetI18NCategory(I18NCat::SYSTEM); + auto iz = GetI18NCategory(I18NCat::INSTALLZIP); + auto di = GetI18NCategory(I18NCat::DIALOG); + auto st = GetI18NCategory(I18NCat::STORE); // Borrow "Size" from here, like GameScreen does. + + LinearLayout *content = parent->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + + if (!info_.anythingInstalled) { + content->Add(new NoticeView(NoticeLevel::INFO, sy->T("No firmware installed"), + sy->T("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."))); + content->Add(new TextWithImage(ImageID("I_FOLDER"), GetFriendlyPath(nandRoot_))); + return; + } + + if (!info_.version.empty()) { + std::string versionLine = std::string(sy->T("Firmware version")) + ": " + info_.version; + content->Add(new TextView(versionLine, ALIGN_LEFT, false))->SetBig(true); + if (!info_.buildDate.empty() || !info_.target.empty()) { + std::string details = info_.buildDate; + if (!info_.target.empty()) { + if (!details.empty()) { + details += " - "; + } + details += info_.target; + } + content->Add(new TextView(details, ALIGN_LEFT, false)); + } + } else { + // No flash0:/vsh/etc/version.txt, so this isn't a full firmware - most likely just the + // fonts, which is what installing from a running game's disc leaves behind. + content->Add(new NoticeView(NoticeLevel::INFO, sy->T("Partial firmware installed"), + sy->T("Some system files are present, but not a complete firmware"))); + } + + content->Add(new ItemHeader(sy->T("Contents"))); + content->Add(new InfoItem(sy->T("Fonts"), info_.fontCount)); + content->Add(new InfoItem(sy->T("Kernel modules"), info_.kernelModuleCount)); + content->Add(new InfoItem(sy->T("XMB (VSH)"), info_.hasVsh ? di->T("Yes") : di->T("No"))); + content->Add(new InfoItem(sy->T("Files"), info_.fileCount)); + content->Add(new InfoItem(st->T("Size"), NiceSizeFormat(info_.totalSize))); + + content->Add(new ItemHeader(iz->T("Install into folder"))); + content->Add(new TextView(GetFriendlyPath(nandRoot_), ALIGN_LEFT | FLAG_WRAP_TEXT, false)); +} + +void FirmwareScreen::CreateSettingsViews(UI::ViewGroup *parent) { + using namespace UI; + + auto sy = GetI18NCategory(I18NCat::SYSTEM); + auto iz = GetI18NCategory(I18NCat::INSTALLZIP); + + // Booting anything replaces the running game, and erasing the NAND out from under a game + // that has flash0 mounted is worse - so while a game is running, this screen is read-only. + const bool gameRunning = PSP_IsInited(); + + Choice *launch = parent->Add(new Choice(sy->T("Launch XMB"), ImageID("I_PLAY"))); + launch->OnClick.Add([this](UI::EventParams &) { + LaunchVSH(); + }); + launch->SetEnabled(!gameRunning && info_.hasVsh && FirmwareVersionSupportsVSH(info_.version)); + + if (info_.hasVsh && !FirmwareVersionSupportsVSH(info_.version)) { + parent->Add(new NoticeView(NoticeLevel::WARN, sy->T("XMB unsupported"), + sy->T("XMBUnsupportedVersion", "Only firmware 6.61 can boot the XMB so far"))); + } + + if (System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) { + Choice *install = parent->Add(new Choice(iz->T("Install PSP firmware update"), ImageID("I_FOLDER_UPLOAD"))); + install->OnClick.Add([this](UI::EventParams &) { + BrowseForUpdater(); + }); + install->SetEnabled(!gameRunning); + } + + if (info_.anythingInstalled) { + Choice *erase = parent->Add(new Choice(sy->T("Erase firmware"), ImageID("I_TRASHCAN"))); + erase->OnClick.Add([this](UI::EventParams &) { + AskToErase(); + }); + erase->SetEnabled(!gameRunning); + } + + if (System_GetPropertyBool(SYSPROP_HAS_OPEN_DIRECTORY)) { + parent->Add(new Choice(sy->T("Show firmware folder")))->OnClick.Add([this](UI::EventParams &) { + System_LaunchUrl(LaunchUrlType::LOCAL_FOLDER, nandRoot_.ToString()); + }); + } + + if (gameRunning) { + parent->Add(new NoticeView(NoticeLevel::INFO, + sy->T("FirmwareNeedsNoGame", "Stop the game to change the installed firmware"), "")); + } +} + +void FirmwareScreen::dialogFinished(const Screen *dialog, DialogResult result) { + // Coming back from the installer (or the erase confirmation), the NAND may well look + // different than it did when we scanned it. Rescanning is cheap, so just always redo it - + // BeforeCreateViews does the scan. + RecreateViews(); + UITwoPaneBaseDialogScreen::dialogFinished(dialog, result); +} + +void FirmwareScreen::LaunchVSH() { + System_PostUIMessage(UIMessage::REQUEST_GAME_BOOT, (nandRoot_ / "flash0/vsh/module/vshmain.prx").ToString()); +} + +void FirmwareScreen::BrowseForUpdater() { + auto iz = GetI18NCategory(I18NCat::INSTALLZIP); + System_BrowseForFile(GetRequesterToken(), iz->T("Install PSP firmware update"), BrowseFileType::BOOTABLE, + [this](std::string_view value, int) { + screenManager()->push(new InstallUpdateScreen(Path(value), "", false)); + }); +} + +void FirmwareScreen::AskToErase() { + auto sy = GetI18NCategory(I18NCat::SYSTEM); + auto di = GetI18NCategory(I18NCat::DIALOG); + + std::string question(sy->T("EraseFirmwareConfirm", "This deletes everything in the NAND folder, including the fonts.")); + screenManager()->push(new UI::MessagePopupScreen(sy->T("Erase firmware"), question, di->T("Delete"), di->T("Cancel"), + [this](bool erase) { + if (!erase) { + return; + } + std::string error; + if (!EraseInstalledFirmware(nandRoot_, &error)) { + ERROR_LOG(Log::Loader, "Failed to erase the firmware: %s", error.c_str()); + } + RecreateViews(); + })); +} diff --git a/UI/FirmwareScreen.h b/UI/FirmwareScreen.h new file mode 100644 index 0000000000..9f3e495207 --- /dev/null +++ b/UI/FirmwareScreen.h @@ -0,0 +1,52 @@ +// Copyright (c) 2026- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Common/File/Path.h" +#include "Common/UI/UIScreen.h" +#include "Common/UI/ViewGroup.h" + +#include "Core/Util/PSARUnpack.h" + +#include "UI/SimpleDialogScreen.h" + +// What's in PSP/NAND - which can be nothing at all, just the fonts we pulled off some game's +// disc, or a full firmware unpacked from an updater. Also the place to launch the XMB from, +// since that's what a full firmware buys you. +class FirmwareScreen : public UITwoPaneBaseDialogScreen { +public: + FirmwareScreen(const Path &gamePath) + : UITwoPaneBaseDialogScreen(gamePath, TwoPaneFlags::SettingsToTheRight | TwoPaneFlags::ContentsCanScroll) {} + + const char *tag() const override { return "Firmware"; } + +protected: + std::string_view GetTitle() const override; + void BeforeCreateViews() override; + void CreateSettingsViews(UI::ViewGroup *parent) override; + void CreateContentViews(UI::ViewGroup *parent) override; + void dialogFinished(const Screen *dialog, DialogResult result) override; + +private: + void LaunchVSH(); + void BrowseForUpdater(); + void AskToErase(); + + Path nandRoot_; + InstalledFirmwareInfo info_; +}; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 5d158d31ce..31116c339a 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -50,6 +50,7 @@ #include "UI/DevScreens.h" #include "UI/DeveloperToolsScreen.h" #include "UI/DisplayLayoutScreen.h" +#include "UI/FirmwareScreen.h" #include "UI/RemoteISOScreen.h" #include "UI/SavedataScreen.h" #include "UI/SystemInfoScreen.h" @@ -1138,6 +1139,9 @@ void GameSettingsScreen::CreateToolsSettings(UI::ViewGroup *tools) { tools->Add(new Choice(ri->T("Remote disc streaming")))->OnClick.Add([=](UI::EventParams &) { screenManager()->push(new RemoteISOScreen(gamePath_)); }); + tools->Add(new Choice(sy->T("PSP Firmware")))->OnClick.Add([=](UI::EventParams &) { + screenManager()->push(new FirmwareScreen(gamePath_)); + }); } void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) { diff --git a/UI/InstallUpdateScreen.cpp b/UI/InstallUpdateScreen.cpp index abe8a4de2b..6a0e98e453 100644 --- a/UI/InstallUpdateScreen.cpp +++ b/UI/InstallUpdateScreen.cpp @@ -36,8 +36,8 @@ #include "UI/MiscViews.h" #include "UI/EmuScreen.h" -InstallUpdateScreen::InstallUpdateScreen(const Path &path, std::string_view title) - : UISimpleBaseDialogScreen(Path(), SimpleDialogFlags::ContentsCanScroll), path_(path), title_(title) { +InstallUpdateScreen::InstallUpdateScreen(const Path &path, std::string_view title, bool allowRun) + : UISimpleBaseDialogScreen(Path(), SimpleDialogFlags::ContentsCanScroll), path_(path), title_(title), allowRun_(allowRun) { destination_ = GetSysDirectory(DIRECTORY_NAND); File::FileInfo fileInfo; @@ -80,7 +80,8 @@ void InstallUpdateScreen::CreateDialogViews(UI::ViewGroup *parent) { container->Add(new TextView(GetFriendlyPath(destination_)))->SetAlign(FLAG_WRAP_TEXT); if (overwrites_) { - container->Add(new NoticeView(NoticeLevel::WARN, di->T("Confirm Overwrite"), "")); + container->Add(new NoticeView(NoticeLevel::WARN, di->T("Confirm Overwrite"), + iz->T("The firmware already installed will be erased first"))); } container->Add(new Spacer(12.0f)); @@ -90,10 +91,12 @@ void InstallUpdateScreen::CreateDialogViews(UI::ViewGroup *parent) { StartInstall(); }); - Choice *runChoice = container->Add(new Choice(dev->T("Run"), ImageID("I_PLAY"))); - runChoice->OnClick.Add([this](UI::EventParams &e) { - screenManager()->switchScreen(new EmuScreen(path_)); - }); + if (allowRun_) { + Choice *runChoice = container->Add(new Choice(dev->T("Run"), ImageID("I_PLAY"))); + runChoice->OnClick.Add([this](UI::EventParams &e) { + screenManager()->switchScreen(new EmuScreen(path_)); + }); + } progressBar_ = container->Add(new ProgressBar()); progressBar_->SetVisibility(V_GONE); @@ -125,6 +128,13 @@ void InstallUpdateScreen::StartInstall() { options.progress = [state](float progress) { state->progress = progress; }; + // Two firmwares can't be merged - a file the new one doesn't have would linger and still + // get loaded - so start from an empty NAND. + if (!EraseInstalledFirmware(destination, &state->error)) { + state->success = false; + state->done = true; + return; + } state->success = UnpackUpdater(path, destination, options, &state->stats, &state->error); if (state->success && state->stats.written == 0) { // Nothing came out, so the archive had no file list for the model we asked for - diff --git a/UI/InstallUpdateScreen.h b/UI/InstallUpdateScreen.h index e4fe1c5b5a..7c7933fc6e 100644 --- a/UI/InstallUpdateScreen.h +++ b/UI/InstallUpdateScreen.h @@ -39,7 +39,9 @@ class InstallUpdateScreen : public UISimpleBaseDialogScreen { public: // title is the updater's SFO title, which already carries the version ("PSP Update ver 6.61"). - InstallUpdateScreen(const Path &path, std::string_view title); + // allowRun offers to boot the updater instead of unpacking it - which makes sense when the + // user picked the updater to launch it, but not when they came here to install a firmware. + InstallUpdateScreen(const Path &path, std::string_view title, bool allowRun); void CreateDialogViews(UI::ViewGroup *parent) override; void update() override; @@ -70,6 +72,7 @@ private: std::string title_; u64 fileSize_ = 0; bool overwrites_ = false; + bool allowRun_ = false; std::shared_ptr state_; bool reportedDone_ = false; diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 3419450e2b..13fafba12a 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -101,7 +101,7 @@ static void LaunchFile(ScreenManager *screenManager, Screen *currentScreen, cons std::string title = info->GetTitle(); // includes the version. // The unpacker wants the PBP itself, not the folder it happens to sit in. const Path pbpPath = info->fileType == IdentifiedFileType::PSP_PBP ? path : path / "EBOOT.PBP"; - screenManager->push(new InstallUpdateScreen(pbpPath, title)); + screenManager->push(new InstallUpdateScreen(pbpPath, title, true)); return; } break; diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index e74c8805fe..13d1600cd5 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -80,6 +80,7 @@ + @@ -139,6 +140,7 @@ + diff --git a/UI/UI.vcxproj.filters b/UI/UI.vcxproj.filters index c56a1b7fb3..1d4f450d53 100644 --- a/UI/UI.vcxproj.filters +++ b/UI/UI.vcxproj.filters @@ -43,6 +43,9 @@ Screens + + Screens + Screens @@ -200,6 +203,9 @@ Screens + + Screens + Screens diff --git a/UWP/UI_UWP/UI_UWP.vcxproj b/UWP/UI_UWP/UI_UWP.vcxproj index 3da7949f38..de59f9088d 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj +++ b/UWP/UI_UWP/UI_UWP.vcxproj @@ -115,6 +115,7 @@ + @@ -175,6 +176,7 @@ + diff --git a/UWP/UI_UWP/UI_UWP.vcxproj.filters b/UWP/UI_UWP/UI_UWP.vcxproj.filters index 4274cc244d..a2cc4904b0 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj.filters +++ b/UWP/UI_UWP/UI_UWP.vcxproj.filters @@ -76,6 +76,9 @@ Screens + + Screens + Screens @@ -225,6 +228,9 @@ Screens + + Screens + Screens diff --git a/android/jni/Android.mk b/android/jni/Android.mk index ef5b0c577a..d7c5912df0 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -989,6 +989,7 @@ LOCAL_SRC_FILES := \ $(SRC)/UI/Background.cpp \ $(SRC)/UI/CwCheatScreen.cpp \ $(SRC)/UI/InstallPkgScreen.cpp \ + $(SRC)/UI/FirmwareScreen.cpp \ $(SRC)/UI/InstallUpdateScreen.cpp \ $(SRC)/UI/InstallZipScreen.cpp \ $(SRC)/UI/JitCompareScreen.cpp \