diff --git a/Core/Util/PSARUnpack.cpp b/Core/Util/PSARUnpack.cpp index 67e7391624..3d6ec49089 100644 --- a/Core/Util/PSARUnpack.cpp +++ b/Core/Util/PSARUnpack.cpp @@ -908,7 +908,7 @@ static const char *UPDATE_PSAR_SUFFIX = "PSP_GAME/SYSDIR/UPDATE/DATA.BIN"; static const char *UPDATE_SFO_SUFFIX = "PSP_GAME/SYSDIR/UPDATE/PARAM.SFO"; // A disc updater's PARAM.SFO titles itself "PSP(tm) Update ver 3.95"; we want the number. -static std::string VersionFromUpdaterTitle(std::string_view title) { +std::string VersionFromUpdaterTitle(std::string_view title) { const size_t space = title.rfind(' '); if (space != std::string_view::npos) { return std::string(title.substr(space + 1)); @@ -1068,6 +1068,13 @@ static bool ScanDirRecursive(const Path &dir, int *fileCount, u64 *totalSize) { return true; } +// Whether a directory holds anything at all, without walking into it. flash0's contents are always +// in subdirectories (font/, kd/, vsh/, ...), so one listing settles "is anything installed". +static bool DirHasEntries(const Path &dir) { + std::vector files; + return File::GetFilesInDir(dir, &files) && !files.empty(); +} + static int CountFilesInDir(const Path &dir) { std::vector files; if (!File::GetFilesInDir(dir, &files)) { @@ -1122,14 +1129,19 @@ static void ParseVersionTxt(std::string_view contents, InstalledFirmwareInfo *in } } -void ReadInstalledFirmwareInfo(const Path &nandRoot, InstalledFirmwareInfo *info) { +void ReadInstalledFirmwareInfo(const Path &nandRoot, InstalledFirmwareInfo *info, bool countFiles) { *info = InstalledFirmwareInfo{}; const Path flash0 = nandRoot / "flash0"; - for (const char *dir : { "flash0", "flash1", "ipl" }) { - ScanDirRecursive(nandRoot / dir, &info->fileCount, &info->totalSize); + if (countFiles) { + for (const char *dir : { "flash0", "flash1", "ipl" }) { + ScanDirRecursive(nandRoot / dir, &info->fileCount, &info->totalSize); + } + info->anythingInstalled = info->fileCount > 0; + } else { + // Cheap equivalent: an install always has files under flash0, so one listing settles it. + info->anythingInstalled = DirHasEntries(flash0); } - info->anythingInstalled = info->fileCount > 0; if (!info->anythingInstalled) { return; } diff --git a/Core/Util/PSARUnpack.h b/Core/Util/PSARUnpack.h index b200955ed3..6b524a543d 100644 --- a/Core/Util/PSARUnpack.h +++ b/Core/Util/PSARUnpack.h @@ -141,6 +141,9 @@ 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); +// Pulls the version out of an updater's SFO title: "PSP(tm) Update ver 3.95" -> "3.95". +std::string VersionFromUpdaterTitle(std::string_view title); + // 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. @@ -158,9 +161,10 @@ struct InstalledFirmwareInfo { 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); +// Walks the NAND directory (the one holding flash0/flash1). Pass countFiles = false when +// fileCount and totalSize aren't wanted: those are the only fields that need the whole tree read, +// 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); // 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. diff --git a/UI/InstallUpdateScreen.cpp b/UI/InstallUpdateScreen.cpp index 8cb94d2458..f8f7990af7 100644 --- a/UI/InstallUpdateScreen.cpp +++ b/UI/InstallUpdateScreen.cpp @@ -46,7 +46,8 @@ InstallUpdateScreen::InstallUpdateScreen(const Path &path, std::string_view titl fileSize_ = fileInfo.size; } // There's no practical way to merge two firmwares, so an install replaces whatever is there. - overwrites_ = File::Exists(destination_ / "flash0"); + ReadInstalledFirmwareInfo(destination_, &installed_, false); + overwrites_ = installed_.anythingInstalled; } std::string_view InstallUpdateScreen::GetTitle() const { @@ -81,8 +82,18 @@ void InstallUpdateScreen::CreateDialogViews(UI::ViewGroup *parent) { container->Add(new TextView(GetFriendlyPath(destination_)))->SetAlign(FLAG_WRAP_TEXT); if (overwrites_) { + // The title is whatever the updater's SFO says, so only trust the tail of it if it came + // out looking like a version number rather than the last word of some other sentence. + std::string newVersion = VersionFromUpdaterTitle(title_); + if (newVersion.find('.') == std::string::npos || newVersion[0] < '0' || newVersion[0] > '9') { + newVersion.clear(); + } + const std::string_view unknown = "N/A"; container->Add(new NoticeView(NoticeLevel::WARN, di->T("Confirm Overwrite"), - iz->T("The firmware already installed will be erased first"))); + ApplySafeSubstitutions( + iz->T("ReplaceFirmware", "Firmware %1 is installed. It will be erased and replaced with %2."), + installed_.version.empty() ? unknown : std::string_view(installed_.version), + newVersion.empty() ? unknown : std::string_view(newVersion)))); } container->Add(new Spacer(12.0f)); diff --git a/UI/InstallUpdateScreen.h b/UI/InstallUpdateScreen.h index 69b7eb1da1..83712d6111 100644 --- a/UI/InstallUpdateScreen.h +++ b/UI/InstallUpdateScreen.h @@ -77,6 +77,8 @@ private: u64 fileSize_ = 0; bool overwrites_ = false; bool allowRun_ = false; + // What's in the NAND right now, for overwrite warnings. + InstalledFirmwareInfo installed_; std::shared_ptr state_; bool reportedDone_ = false;