Name both versions in the firmware overwrite warning

"Firmware 6.20 is installed. It will be erased and replaced with 6.60." is the
thing worth double-checking before wiping a firmware - installing off whatever
disc is to hand makes going backwards easy to do by accident.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Henrik Rydgård
2026-09-10 15:52:53 -06:00
co-authored by Claude Opus 5
parent 86fcfdc60a
commit e4d940604e
4 changed files with 39 additions and 10 deletions
+17 -5
View File
@@ -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<File::FileInfo> files;
return File::GetFilesInDir(dir, &files) && !files.empty();
}
static int CountFilesInDir(const Path &dir) {
std::vector<File::FileInfo> 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;
}
+7 -3
View File
@@ -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.
+13 -2
View File
@@ -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));
+2
View File
@@ -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<InstallState> state_;
bool reportedDone_ = false;