From 2fae0dadba5ae8f342880cd7d4319376c1c66f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 11 Nov 2026 11:53:49 +0100 Subject: [PATCH] Game screen: Export all of a game's saves from its menu Exporting a single save stays in the savedata manager's popup. Exporting a whole game's saves is rarer, so it moves to the game's menu. Co-Authored-By: Claude Opus 5.5 --- UI/GameScreen.cpp | 7 +++++++ UI/SavedataScreen.cpp | 39 +++++++++++---------------------------- UI/SavedataScreen.h | 3 +++ 3 files changed, 21 insertions(+), 28 deletions(-) diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 224a5d8c78..2874e755ca 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -61,6 +61,7 @@ #include "UI/MainScreen.h" #include "UI/BackgroundAudio.h" #include "UI/SavedataScreen.h" +#include "UI/ShareFiles.h" #include "UI/MiscViews.h" // This is in an objective-C file. @@ -722,6 +723,12 @@ void GameScreen::CreateContextMenu(UI::ViewGroup *parent) { screenManager()->push(new SavedataScreen(gamePath_)); }); } + if (info_->saveDataSize && CanShareFiles()) { + auto sa = GetI18NCategory(I18NCat::SAVEDATA); + parent->Add(new Choice(sa->T("Export all saves for this game"), ImageID("I_SHARE")))->OnClick.Add([this](UI::EventParams &e) { + ExportGameSaves(info_.get()); + }); + } if (info_->saveDataSize) { Choice *btnDeleteSaveData = new Choice(ga->T("Delete Save Data"), ImageID("I_TRASHCAN")); parent->Add(btnDeleteSaveData)->OnClick.Handle(this, &GameScreen::OnDeleteSaveData); diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index d043a10bb0..54e39d0165 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -67,19 +67,6 @@ static void AddFolderToZip(const Path &folder, const std::string &nameInZip, std } } -// The save folders of a game next to a save of it, without installed data. -static std::vector FindGameSaves(const Path &saveDir, std::string_view gameID) { - std::vector saves; - std::vector dirs; - File::GetFilesInDir(saveDir, &dirs); - for (const File::FileInfo &dir : dirs) { - if (dir.isDirectory && startsWith(dir.name, gameID) && File::Exists(dir.fullName / "PARAM.SFO") && !IsInstalledGameData(dir.fullName)) { - saves.push_back(dir.fullName); - } - } - return saves; -} - static void ExportAsZip(std::string_view zipName, const std::vector &entries) { if (!ShareFilesAsZip(zipName, entries)) { auto sa = GetI18NCategory(I18NCat::SAVEDATA); @@ -87,6 +74,17 @@ static void ExportAsZip(std::string_view zipName, const std::vector entries; + for (const Path &dir : info->GetSaveDataDirectories()) { + // Installed data is large and not a save, so it's left out. + if (File::Exists(dir / "PARAM.SFO") && !IsInstalledGameData(dir)) { + AddFolderToZip(dir, dir.GetFilename(), &entries); + } + } + ExportAsZip(info->GetTitle() + " (" + info->id + ")", entries); +} + SavedataView::SavedataView(UIContext &dc, const Path &savePath, IdentifiedFileType type, std::string_view title, std::string_view savedataTitle, std::string_view savedataDetail, std::string_view fileSize, std::string_view mtime, bool showIcon, UI::LayoutParams *layoutParams) : LinearLayout(ORIENT_VERTICAL, layoutParams) { @@ -220,21 +218,6 @@ void SavedataPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { } } - if (canExport && !isState && ginfo->Ready(GameInfoFlags::PARAM_SFO) && ginfo->id.size() >= 5) { - // The game ID starts the folder names (the same rule as GetSaveDataDirectories()). - std::vector gameSaves = FindGameSaves(savePath_.NavigateUp(), ginfo->id); - if (gameSaves.size() > 1) { - std::string zipName = ginfo->GetTitle() + " (" + ginfo->id + ")"; - content->Add(new Choice(sa->T("Export all saves for this game"), ImageID("I_SHARE"), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)))->OnClick.Add([gameSaves, zipName](UI::EventParams &e) { - std::vector entries; - for (const Path &dir : gameSaves) { - AddFolderToZip(dir, dir.GetFilename(), &entries); - } - ExportAsZip(zipName, entries); - }); - } - } - LinearLayout *buttonRow = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); buttonRow->SetSpacing(12); diff --git a/UI/SavedataScreen.h b/UI/SavedataScreen.h index aeef0ee4d1..f706c3114f 100644 --- a/UI/SavedataScreen.h +++ b/UI/SavedataScreen.h @@ -185,3 +185,6 @@ private: UI::TextView *mTime_ = nullptr; UI::TextView *fileSize_ = nullptr; }; + +// Zips a game's saves (without installed data) and shares the zip. +void ExportGameSaves(GameInfo *info);