Show and remove installed game updates from the game info screen

An installed update silently replaces what the game boots, so the info
pane now says when there is one - version, size and where it lives - and
the context menu offers to remove it again.

Removing takes the whole PSP/GAME/<DISC_ID> folder when the update is all
that's in it. When a digital game shares the folder, only PBOOT.PBP goes,
since deleting the folder would take the game with it and nothing records
what the install wrote. The confirmation names the exact path either way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018izZ1mGTWhz2RqeudqsDQR
This commit is contained in:
Henrik RydgårdandClaude Opus 5 committed 2026-09-08 11:15:15 -06:00
1 parent 3a70c6b149
commit f0dafdee10
5 files changed
+153 -2

No files matched your search

+62
View File
@@ -262,6 +262,15 @@ static bool FileTypeIsPlayable(IdentifiedFileType fileType) {
}
}
void GameScreen::RefreshInstalledUpdate() {
hasInstalledUpdate_ = false;
// Homebrew reuses real disc IDs often enough that we'd get false positives.
if (isHomebrew_ || !(knownFlags_ & GameInfoFlags::PARAM_SFO)) {
return;
}
hasInstalledUpdate_ = FindInstalledGameUpdate(info_->id, &installedUpdate_);
}
void GameScreen::CreateContentViews(UI::ViewGroup *parent) {
if (!info_) {
// Shouldn't happen
@@ -505,6 +514,24 @@ void GameScreen::CreateContentViews(UI::ViewGroup *parent) {
}
}
// An installed game update replaces the disc's executable, so it's worth saying so here -
// otherwise there's nothing in the UI to explain why a patched game is running.
RefreshInstalledUpdate();
if (hasInstalledUpdate_) {
infoLayout->Add(new ItemHeader(ga->T("Game update")));
std::string updateLine = installedUpdate_.title;
if (!installedUpdate_.appVer.empty()) {
const std::string version = ApplySafeSubstitutions(ga->T("Version %1"), installedUpdate_.appVer);
updateLine = updateLine.empty() ? version : updateLine + " - " + version;
}
if (updateLine.empty()) {
updateLine = ga->T("Installed");
}
updateLine += " - " + NiceSizeFormat(installedUpdate_.sizeOnDisk);
infoLayout->Add(new TextView(updateLine, ALIGN_LEFT, true))->SetBullet(true);
infoLayout->Add(new TextView(GetFriendlyPath(installedUpdate_.folder), ALIGN_LEFT | FLAG_WRAP_TEXT, true))->SetBullet(true);
}
// Show plugin info_, if any. Later might add checkboxes.
auto plugins = HLEPlugins::FindPlugins(info_->id, g_Config.sLanguageIni);
if (!plugins.empty()) {
@@ -625,6 +652,12 @@ void GameScreen::CreateContextMenu(UI::ViewGroup *parent) {
});
}
RefreshInstalledUpdate();
if (!inGame_ && hasInstalledUpdate_) {
Choice *btnDeleteUpdate = parent->Add(new Choice(ga->T("Delete Game Update"), ImageID("I_TRASHCAN")));
btnDeleteUpdate->OnClick.Handle(this, &GameScreen::OnDeleteGameUpdate);
}
// Don't want to be able to delete the game while it's running.
if (!inGame_) {
Choice *deleteChoice = parent->Add(new Choice(ga->T("Delete Game"), ImageID("I_WARNING")));
@@ -632,6 +665,35 @@ void GameScreen::CreateContextMenu(UI::ViewGroup *parent) {
}
}
void GameScreen::OnDeleteGameUpdate(UI::EventParams &e) {
if (!hasInstalledUpdate_) {
return;
}
auto di = GetI18NCategory(I18NCat::DIALOG);
auto ga = GetI18NCategory(I18NCat::GAME);
std::string prompt(ga->T("DeleteConfirmGameUpdate", "Do you really want to remove the installed update?\nThe game will go back to running the version on the disc."));
prompt += "\n\n";
// Say exactly what disappears - for a digital game the folder holds the game itself, so only
// the update's executable goes.
prompt += GetFriendlyPath(installedUpdate_.sharesFolderWithGame ? installedUpdate_.pbootPath : installedUpdate_.folder);
const bool trashAvailable = System_GetPropertyBool(SYSPROP_HAS_TRASH_BIN);
const InstalledGameUpdate update = installedUpdate_;
screenManager()->push(
new UI::MessagePopupScreen(ga->T("Delete Game Update"), prompt, trashAvailable ? di->T("Move to trash") : di->T("Delete"), di->T("Cancel"),
[this, update](bool yes) {
if (!yes) {
return;
}
if (!DeleteInstalledGameUpdate(update)) {
auto er = GetI18NCategory(I18NCat::ERRORS);
g_OSD.Show(OSDType::MESSAGE_ERROR, er->T("Failed to delete the game update"));
}
RecreateViews();
}));
}
void GameScreen::OnCreateConfig(UI::EventParams &e) {
if (!info_->Ready(GameInfoFlags::PARAM_SFO)) {
return;