mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 06:50:29 +02:00
Merge pull request #22521 from hrydgard/savedata-zip-root
Install savedata zips that contain a single save folder's files in the root
This commit is contained in:
5 files changed
+85
-3
No files matched your search
+15
-1
@@ -39,6 +39,7 @@
|
||||
#include "Core/ELF/PBPReader.h"
|
||||
#include "Core/ELF/ParamSFO.h"
|
||||
#include "Core/Util/GameManager.h"
|
||||
#include "Core/Util/PathUtil.h"
|
||||
|
||||
// TODO: This is just part of a VolDescriptor, see ISOFileSystem.cpp.
|
||||
struct PVD {
|
||||
@@ -517,7 +518,9 @@ void DetectZipFileContents(zip_t *z, ZipFileInfo *info) {
|
||||
int stripChars = 0;
|
||||
int stripCharsTexturePack = -1;
|
||||
int filesInRoot = 0;
|
||||
int filesInSubdirs = 0;
|
||||
int directoriesInRoot = 0;
|
||||
std::string rootSaveDir; // SAVEDATA_DIRECTORY of a PARAM.SFO in the root of the zip.
|
||||
bool hasParamSFO = false;
|
||||
bool isExtractedISO = false;
|
||||
bool hasIcon0PNG = false;
|
||||
@@ -590,7 +593,7 @@ void DetectZipFileContents(zip_t *z, ZipFileInfo *info) {
|
||||
info->gameTitle = fn;
|
||||
} else if (endsWith(lowerCaseName, "psp_game/sysdir/eboot.bin") || endsWith(lowerCaseName, "psp_game/sysdir/boot.bin")) {
|
||||
isExtractedISO = true;
|
||||
} else if (endsWith(lowerCaseName, "/param.sfo")) {
|
||||
} else if (lowerCaseName == "param.sfo" || endsWith(lowerCaseName, "/param.sfo")) {
|
||||
// Get the game name so we can display it.
|
||||
std::string paramSFOContents;
|
||||
if (ZipExtractFileToMemory(z, i, ¶mSFOContents)) {
|
||||
@@ -605,6 +608,9 @@ void DetectZipFileContents(zip_t *z, ZipFileInfo *info) {
|
||||
info->savedataDetails = sfo.GetValueString("SAVEDATA_DETAIL");
|
||||
info->savedataDir = sfo.GetValueString("SAVEDATA_DIRECTORY"); // should also be parsable from the path.
|
||||
hasParamSFO = true;
|
||||
if (slashCount == 0) {
|
||||
rootSaveDir = info->savedataDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -632,6 +638,8 @@ void DetectZipFileContents(zip_t *z, ZipFileInfo *info) {
|
||||
}
|
||||
if (slashCount == 0) {
|
||||
filesInRoot++;
|
||||
} else {
|
||||
filesInSubdirs++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,6 +660,12 @@ void DetectZipFileContents(zip_t *z, ZipFileInfo *info) {
|
||||
} else if (stripChars == 0 && filesInRoot == 0 && hasParamSFO && hasIcon0PNG && !isExtractedISO) {
|
||||
// As downloaded from GameFAQs, for example.
|
||||
info->contents = ZipFileContents::SAVE_DATA;
|
||||
} else if (filesInSubdirs == 0 && IsSafePathComponent(rootSaveDir) && !isExtractedISO && !isSaveStates && !isFrameDump) {
|
||||
// The contents of a single save folder, zipped without the folder itself (the zip is usually named
|
||||
// after it). The folder name is the SFO's SAVEDATA_DIRECTORY (game ID + save name, like ULUS10019000).
|
||||
info->contents = ZipFileContents::SAVE_DATA;
|
||||
info->saveDataInRoot = true;
|
||||
info->savedataDir = rootSaveDir;
|
||||
} else if (isFrameDump) {
|
||||
info->contents = ZipFileContents::FRAME_DUMP;
|
||||
} else if (isSaveStates) {
|
||||
|
||||
@@ -191,6 +191,7 @@ struct ZipFileInfo {
|
||||
int isoFileIndex = -1; // for ISO
|
||||
int textureIniIndex = -1; // for textures
|
||||
bool ignoreMetaFiles = false;
|
||||
bool saveDataInRoot = false; // for SAVE_DATA - the files of a single save are in the root, extract into savedataDir.
|
||||
std::string gameTitle; // from PARAM.SFO if available
|
||||
std::string savedataTitle;
|
||||
std::string savedataDetails;
|
||||
|
||||
@@ -414,7 +414,11 @@ void GameManager::InstallZipContents(ZipFileTask task) {
|
||||
case ZipFileContents::SAVE_DATA:
|
||||
{
|
||||
Path pspSaveData = GetSysDirectory(DIRECTORY_SAVEDATA);
|
||||
success = ExtractZipContents(z, pspSaveData, zipInfo, false);
|
||||
if (zipInfo.saveDataInRoot) {
|
||||
success = ExtractZipContents(z, pspSaveData / zipInfo.savedataDir, zipInfo, true);
|
||||
} else {
|
||||
success = ExtractZipContents(z, pspSaveData, zipInfo, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZipFileContents::SAVE_STATES:
|
||||
|
||||
@@ -249,7 +249,7 @@ void InstallZipScreen::CreateContentViews(UI::ViewGroup *parent) {
|
||||
|
||||
Path savedataDir = GetSysDirectory(DIRECTORY_SAVEDATA);
|
||||
ZipContainer zipFile = ZipOpenPath(zipPath_);
|
||||
overwrite = !ZipCanExtractWithoutOverwrite(zipFile, savedataDir, zipFileInfo_.stripChars, 50);
|
||||
overwrite = !ZipCanExtractWithoutOverwrite(zipFile, zipFileInfo_.saveDataInRoot ? savedataDir / zipFileInfo_.savedataDir : savedataDir, zipFileInfo_.stripChars, 50);
|
||||
ZipClose(zipFile);
|
||||
|
||||
destFolders_.push_back(savedataDir);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/File/Path.h"
|
||||
#include "Core/ELF/ParamSFO.h"
|
||||
#include "Core/Loaders.h"
|
||||
#include "Core/Util/GameManager.h"
|
||||
#include "Core/Util/PathUtil.h"
|
||||
@@ -123,6 +124,66 @@ static bool TestSaveStateZipExtraction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// A single save folder's contents zipped without the folder (named ULUS10019000.zip, say). The folder
|
||||
// name comes from SAVEDATA_DIRECTORY in the PARAM.SFO.
|
||||
static bool TestRootSaveDataZip() {
|
||||
Path tempRoot = File::GetCurDirectory() / "unittest_zip_savedata_test";
|
||||
File::DeleteDirRecursively(tempRoot);
|
||||
EXPECT_TRUE(File::CreateDir(tempRoot));
|
||||
|
||||
Path destDir = tempRoot / "SAVEDATA";
|
||||
EXPECT_TRUE(File::CreateDir(destDir));
|
||||
|
||||
std::string sfoContents;
|
||||
for (const char *saveDir : { "ULUS10019000", "../evil" }) {
|
||||
ParamSFOData sfo;
|
||||
sfo.SetValue("CATEGORY", "MS", 4);
|
||||
sfo.SetValue("TITLE", "CODED ARMS", 128);
|
||||
sfo.SetValue("SAVEDATA_DIRECTORY", saveDir, 64);
|
||||
u8 *sfoData = nullptr;
|
||||
size_t sfoSize = 0;
|
||||
sfo.WriteSFO(&sfoData, &sfoSize);
|
||||
sfoContents.assign((const char *)sfoData, sfoSize);
|
||||
delete[] sfoData;
|
||||
|
||||
Path zipPath = tempRoot / "save.zip";
|
||||
int errorp = 0;
|
||||
zip_t *z = zip_open(zipPath.c_str(), ZIP_CREATE | ZIP_TRUNCATE, &errorp);
|
||||
EXPECT_TRUE(z != nullptr);
|
||||
static const char *const contents = "data";
|
||||
zip_source_t *source = zip_source_buffer(z, sfoContents.data(), sfoContents.size(), 0);
|
||||
EXPECT_TRUE(zip_file_add(z, "PARAM.SFO", source, ZIP_FL_ENC_UTF_8) >= 0);
|
||||
for (const char *name : { "ICON0.PNG", "CODED.ARM" }) {
|
||||
source = zip_source_buffer(z, contents, strlen(contents), 0);
|
||||
EXPECT_TRUE(zip_file_add(z, name, source, ZIP_FL_ENC_UTF_8) >= 0);
|
||||
}
|
||||
EXPECT_TRUE(zip_close(z) == 0);
|
||||
|
||||
z = zip_open(zipPath.c_str(), 0, &errorp);
|
||||
EXPECT_TRUE(z != nullptr);
|
||||
ZipFileInfo info;
|
||||
DetectZipFileContents(z, &info);
|
||||
if (!strcmp(saveDir, "../evil")) {
|
||||
EXPECT_EQ_INT((int)info.contents, (int)ZipFileContents::UNKNOWN);
|
||||
zip_close(z);
|
||||
continue;
|
||||
}
|
||||
EXPECT_EQ_INT((int)info.contents, (int)ZipFileContents::SAVE_DATA);
|
||||
EXPECT_TRUE(info.saveDataInRoot);
|
||||
EXPECT_EQ_STR(info.savedataDir, std::string("ULUS10019000"));
|
||||
|
||||
GameManager manager;
|
||||
EXPECT_TRUE(manager.ExtractZipContents(z, destDir / info.savedataDir, info, true));
|
||||
zip_close(z);
|
||||
|
||||
EXPECT_TRUE(File::Exists(destDir / "ULUS10019000" / "PARAM.SFO"));
|
||||
EXPECT_TRUE(File::Exists(destDir / "ULUS10019000" / "CODED.ARM"));
|
||||
}
|
||||
|
||||
File::DeleteDirRecursively(tempRoot);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool TestIsSafePathComponent() {
|
||||
EXPECT_TRUE(IsSafePathComponent("ULUS10041"));
|
||||
EXPECT_TRUE(IsSafePathComponent("NPJH-50465"));
|
||||
@@ -151,5 +212,7 @@ bool TestZipSlip() {
|
||||
return false;
|
||||
if (!TestSaveStateZipExtraction())
|
||||
return false;
|
||||
if (!TestRootSaveDataZip())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
Reference in new issue
Block a user