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 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-11-11 12:14:46 +01:00
1 parent ec36fad065
commit 2fae0dadba
3 files changed
+21 -28

No files matched your search

+7
View File
@@ -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);
+11 -28
View File
@@ -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<Path> FindGameSaves(const Path &saveDir, std::string_view gameID) {
std::vector<Path> saves;
std::vector<File::FileInfo> 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<ShareZipEntry> &entries) {
if (!ShareFilesAsZip(zipName, entries)) {
auto sa = GetI18NCategory(I18NCat::SAVEDATA);
@@ -87,6 +74,17 @@ static void ExportAsZip(std::string_view zipName, const std::vector<ShareZipEntr
}
}
void ExportGameSaves(GameInfo *info) {
std::vector<ShareZipEntry> 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<Path> 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<ShareZipEntry> 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);
+3
View File
@@ -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);