mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-05-29 00:21:34 +08:00
Support auto-installing plugins from ZIPs with a deeper directory structure
This commit is contained in:
@@ -30,6 +30,10 @@ public:
|
||||
void SetLevel(NoticeLevel level) {
|
||||
level_ = level;
|
||||
}
|
||||
void SetLevelAndText(NoticeLevel level, std::string_view text) {
|
||||
level_ = level;
|
||||
text_ = text;
|
||||
}
|
||||
void SetSquishy(bool squishy) {
|
||||
squishy_ = squishy;
|
||||
}
|
||||
|
||||
@@ -509,21 +509,22 @@ void DetectZipFileContents(zip_t *z, ZipFileInfo *info) {
|
||||
totalFileSize += stat.size;
|
||||
|
||||
std::string fileName(fn);
|
||||
std::string zippedName = fileName; // actually, lowercase-name
|
||||
std::transform(zippedName.begin(), zippedName.end(), zippedName.begin(),
|
||||
[](unsigned char c) { return asciitolower(c); }); // Not using std::tolower to avoid Turkish I->ı conversion.
|
||||
// Ignore macos metadata stuff
|
||||
if (startsWith(zippedName, "__macosx/")) {
|
||||
continue;
|
||||
}
|
||||
if (endsWith(zippedName, "/")) {
|
||||
if (endsWith(fileName, "/")) {
|
||||
// A directory. Not all zips bother including these.
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string lowerCaseName = fileName; // actually, lowercase-name
|
||||
std::transform(lowerCaseName.begin(), lowerCaseName.end(), lowerCaseName.begin(),
|
||||
[](unsigned char c) { return asciitolower(c); }); // Not using std::tolower to avoid Turkish I->ı conversion.
|
||||
// Ignore macos metadata stuff
|
||||
if (startsWith(lowerCaseName, "__macosx/")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int prevSlashLocation = -1;
|
||||
int slashCount = countSlashes(zippedName, &prevSlashLocation);
|
||||
if (zippedName.find("eboot.pbp") != std::string::npos) {
|
||||
int slashCount = countSlashes(lowerCaseName, &prevSlashLocation);
|
||||
if (lowerCaseName.find("eboot.pbp") != std::string::npos) {
|
||||
if (slashCount >= 1 && (!isPSPMemstickGame || prevSlashLocation < stripChars + 1)) {
|
||||
stripChars = prevSlashLocation + 1;
|
||||
isPSPMemstickGame = true;
|
||||
@@ -531,11 +532,11 @@ void DetectZipFileContents(zip_t *z, ZipFileInfo *info) {
|
||||
INFO_LOG(Log::HLE, "Wrong number of slashes (%i) in '%s'", slashCount, fn);
|
||||
}
|
||||
// TODO: Extract icon and param.sfo from the pbp to be able to display it on the install screen.
|
||||
} else if (endsWith(zippedName, ".iso") || endsWith(zippedName, ".cso") || endsWith(zippedName, ".chd")) {
|
||||
} else if (endsWith(lowerCaseName, ".iso") || endsWith(lowerCaseName, ".cso") || endsWith(lowerCaseName, ".chd")) {
|
||||
if (slashCount <= 1) {
|
||||
// We only do this if the ISO file is in the root or one level down.
|
||||
isZippedISO = true;
|
||||
INFO_LOG(Log::HLE, "ISO found in zip: %s", zippedName.c_str());
|
||||
INFO_LOG(Log::HLE, "ISO found in zip: %s", lowerCaseName.c_str());
|
||||
if (info->isoFileIndex != -1) {
|
||||
INFO_LOG(Log::HLE, "More than one ISO file found in zip. Ignoring additional ones.");
|
||||
} else {
|
||||
@@ -543,27 +544,27 @@ void DetectZipFileContents(zip_t *z, ZipFileInfo *info) {
|
||||
info->contentName = fn;
|
||||
}
|
||||
}
|
||||
} else if (zippedName.find("textures.ini") != std::string::npos) {
|
||||
int slashLocation = (int)zippedName.find_last_of('/');
|
||||
} else if (lowerCaseName.find("textures.ini") != std::string::npos) {
|
||||
int slashLocation = (int)lowerCaseName.find_last_of('/');
|
||||
if (stripCharsTexturePack == -1 || slashLocation < stripCharsTexturePack + 1) {
|
||||
stripCharsTexturePack = slashLocation + 1;
|
||||
isTexturePack = true;
|
||||
info->textureIniIndex = i;
|
||||
}
|
||||
} else if (endsWith(zippedName, ".ppdmp")) {
|
||||
} else if (endsWith(lowerCaseName, ".ppdmp")) {
|
||||
isFrameDump = true;
|
||||
info->isoFileIndex = i;
|
||||
info->contentName = fn;
|
||||
} else if (endsWith(zippedName, ".ppst")) {
|
||||
int slashLocation = (int)zippedName.find_last_of('/');
|
||||
} else if (endsWith(lowerCaseName, ".ppst")) {
|
||||
int slashLocation = (int)lowerCaseName.find_last_of('/');
|
||||
if (stripChars == 0 || slashLocation < stripChars + 1) {
|
||||
stripChars = slashLocation + 1;
|
||||
}
|
||||
isSaveStates = true;
|
||||
info->gameTitle = fn;
|
||||
} else if (endsWith(zippedName, "psp_game/sysdir/eboot.bin") || endsWith(zippedName, "psp_game/sysdir/boot.bin")) {
|
||||
} else if (endsWith(lowerCaseName, "psp_game/sysdir/eboot.bin") || endsWith(lowerCaseName, "psp_game/sysdir/boot.bin")) {
|
||||
isExtractedISO = true;
|
||||
} else if (endsWith(zippedName, "/param.sfo")) {
|
||||
} else if (endsWith(lowerCaseName, "/param.sfo")) {
|
||||
// Get the game name so we can display it.
|
||||
std::string paramSFOContents;
|
||||
if (ZipExtractFileToMemory(z, i, ¶mSFOContents)) {
|
||||
@@ -581,13 +582,18 @@ void DetectZipFileContents(zip_t *z, ZipFileInfo *info) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (endsWith(zippedName, "/icon0.png")) {
|
||||
} else if (endsWith(lowerCaseName, "/icon0.png")) {
|
||||
hasIcon0PNG = true;
|
||||
} else if (endsWith(zippedName, "/plugin.ini") && slashCount == 1) {
|
||||
} else if (endsWith(lowerCaseName, "/plugin.ini") && slashCount >= 1) {
|
||||
int slashLocation = (int)lowerCaseName.find_last_of('/');
|
||||
// Find previous slash to determine the root of the plugin, so we can display it properly.
|
||||
int prevSlashLocation = (int)lowerCaseName.find_last_of('/', slashLocation - 1);
|
||||
_dbg_assert_(prevSlashLocation != std::string::npos);
|
||||
stripChars = prevSlashLocation + 1;
|
||||
hasPluginIni = true;
|
||||
ZipExtractFileToMemory(z, i, &info->iniContents);
|
||||
info->contentName = fileName.substr(0, fileName.find_last_of('/'));
|
||||
} else if (endsWith(zippedName, ".prx") && slashCount == 1) {
|
||||
} else if (endsWith(lowerCaseName, ".prx")) {
|
||||
hasPRX = true;
|
||||
}
|
||||
if (slashCount == 0) {
|
||||
|
||||
@@ -175,7 +175,7 @@ void GameManager::Update() {
|
||||
}
|
||||
}
|
||||
|
||||
bool ZipCanExtractWithoutOverwrite(struct zip *z, const Path &destination, int maxOkFiles) {
|
||||
bool ZipCanExtractWithoutOverwrite(struct zip *z, const Path &destination, int stripChars, int maxOkFiles) {
|
||||
int numFiles = zip_get_num_files(z);
|
||||
if (numFiles > maxOkFiles && maxOkFiles >= 0) {
|
||||
// Ignore the check, just assume we can't.
|
||||
@@ -260,7 +260,7 @@ void GameManager::InstallZipContents(ZipFileTask task) {
|
||||
}
|
||||
|
||||
// Check for 7z. We don't support very many scenarios here yet, but we do support ISO install.
|
||||
if (task.zipFileInfo->archiveType == ArchiveType::SevenZ) {
|
||||
if (task.zipFileInfo && task.zipFileInfo->archiveType == ArchiveType::SevenZ) {
|
||||
Path destPath = task.destination;
|
||||
g_OSD.SetProgressBar("install", di->T("Installing..."), 0.0f, 0.0f, 0.0f, 0.1f);
|
||||
Path fn(task.zipFileInfo->isoFilename);
|
||||
@@ -662,7 +662,7 @@ bool GameManager::ExtractZipContents(struct zip *z, const Path &dest, const ZipF
|
||||
|
||||
bool isDir = zippedName.empty() || zippedName.back() == '/';
|
||||
if (!isDir && zippedName.find('/') != std::string::npos) {
|
||||
outFilename = dest / zippedName.substr(0, zippedName.rfind('/'));
|
||||
outFilename = dest / zippedName.substr(info.stripChars, zippedName.rfind('/') - info.stripChars);
|
||||
} else if (!isDir) {
|
||||
outFilename = dest;
|
||||
}
|
||||
|
||||
@@ -119,4 +119,4 @@ private:
|
||||
|
||||
extern GameManager g_GameManager;
|
||||
|
||||
bool ZipCanExtractWithoutOverwrite(struct zip *z, const Path &destination, int maxOkFiles);
|
||||
bool ZipCanExtractWithoutOverwrite(struct zip *z, const Path &destination, int stripChars, int maxOkFiles);
|
||||
|
||||
@@ -165,8 +165,6 @@ void InstallZipScreen::CreateContentViews(UI::ViewGroup *parent) {
|
||||
leftColumn->Add(new TextView(zipFileInfo_.contentName));
|
||||
}
|
||||
|
||||
doneView_ = leftColumn->Add(new TextView(""));
|
||||
|
||||
if (zipFileInfo_.contents == ZipFileContents::ISO_FILE) {
|
||||
const bool isInDownloads = File::IsProbablyInDownloadsFolder(zipPath_);
|
||||
Path parent;
|
||||
@@ -205,7 +203,7 @@ void InstallZipScreen::CreateContentViews(UI::ViewGroup *parent) {
|
||||
leftColumn->Add(new TextView(GetFriendlyPath(zipPath_)));
|
||||
Path pluginsDir = GetSysDirectory(DIRECTORY_PLUGINS);
|
||||
ZipContainer zipFile = ZipOpenPath(zipPath_);
|
||||
overwrite = !ZipCanExtractWithoutOverwrite(zipFile, pluginsDir, 50);
|
||||
overwrite = !ZipCanExtractWithoutOverwrite(zipFile, pluginsDir, zipFileInfo_.stripChars, 50);
|
||||
ZipClose(zipFile);
|
||||
std::stringstream sstream(zipFileInfo_.iniContents);
|
||||
IniFile ini;
|
||||
@@ -236,7 +234,7 @@ void InstallZipScreen::CreateContentViews(UI::ViewGroup *parent) {
|
||||
|
||||
Path savestateDir = GetSysDirectory(DIRECTORY_SAVESTATE);
|
||||
ZipContainer zipFile = ZipOpenPath(zipPath_);
|
||||
overwrite = !ZipCanExtractWithoutOverwrite(zipFile, savestateDir, 50);
|
||||
overwrite = !ZipCanExtractWithoutOverwrite(zipFile, savestateDir, zipFileInfo_.stripChars, 50);
|
||||
ZipClose(zipFile);
|
||||
|
||||
destFolders_.push_back(savestateDir);
|
||||
@@ -253,7 +251,7 @@ void InstallZipScreen::CreateContentViews(UI::ViewGroup *parent) {
|
||||
|
||||
Path savedataDir = GetSysDirectory(DIRECTORY_SAVEDATA);
|
||||
ZipContainer zipFile = ZipOpenPath(zipPath_);
|
||||
overwrite = !ZipCanExtractWithoutOverwrite(zipFile, savedataDir, 50);
|
||||
overwrite = !ZipCanExtractWithoutOverwrite(zipFile, savedataDir, zipFileInfo_.stripChars, 50);
|
||||
ZipClose(zipFile);
|
||||
|
||||
destFolders_.push_back(savedataDir);
|
||||
@@ -307,7 +305,8 @@ void InstallZipScreen::CreateContentViews(UI::ViewGroup *parent) {
|
||||
break;
|
||||
}
|
||||
|
||||
doneView_ = leftColumn->Add(new TextView(""));
|
||||
doneView_ = leftColumn->Add(new NoticeView(NoticeLevel::SUCCESS, "", ""));
|
||||
doneView_->SetVisibility(Visibility::V_GONE);
|
||||
|
||||
if (destFolders_.size() > 1) {
|
||||
leftColumn->Add(new TextView(iz->T("Install into folder")));
|
||||
@@ -374,11 +373,14 @@ void InstallZipScreen::update() {
|
||||
}
|
||||
std::string err = g_GameManager.GetInstallError();
|
||||
if (!err.empty()) {
|
||||
if (doneView_)
|
||||
doneView_->SetText(iz->T(err));
|
||||
if (doneView_) {
|
||||
doneView_->SetLevelAndText(NoticeLevel::ERROR, iz->T(err));
|
||||
doneView_->SetVisibility(Visibility::V_VISIBLE);
|
||||
}
|
||||
} else if (installStarted_) {
|
||||
if (doneView_) {
|
||||
doneView_->SetText(iz->T("Installed!"));
|
||||
doneView_->SetLevelAndText(NoticeLevel::SUCCESS, iz->T("Installed!"));
|
||||
doneView_->SetVisibility(Visibility::V_VISIBLE);
|
||||
}
|
||||
if (playChoice_) {
|
||||
// Encourage the user to back out and play directly from the main screen,
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "UI/BaseScreens.h"
|
||||
#include "UI/SimpleDialogScreen.h"
|
||||
#include "Common/UI/Notice.h"
|
||||
|
||||
class SavedataView;
|
||||
|
||||
@@ -49,7 +50,7 @@ private:
|
||||
UI::Choice *installChoice_ = nullptr;
|
||||
UI::Choice *playChoice_ = nullptr;
|
||||
UI::Choice *backChoice_ = nullptr;
|
||||
UI::TextView *doneView_ = nullptr;
|
||||
NoticeView *doneView_ = nullptr;
|
||||
SavedataView *existingSaveView_ = nullptr;
|
||||
Path savedataToOverwrite_;
|
||||
Path zipPath_;
|
||||
|
||||
Reference in New Issue
Block a user