From 0e978ba2e3f5fd67387c00a484cc53bd10252b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 8 Jul 2019 23:03:27 +0200 Subject: [PATCH] Pre-check the contents of ZIP files before allowing install. --- Core/Loaders.h | 3 +- Core/Util/GameManager.cpp | 117 ++++++++++++++++++++++++++------------ Core/Util/GameManager.h | 18 +++++- UI/InstallZipScreen.cpp | 47 +++++++++------ UI/InstallZipScreen.h | 14 ++--- 5 files changed, 135 insertions(+), 64 deletions(-) diff --git a/Core/Loaders.h b/Core/Loaders.h index 527fa52f5b..690376c73b 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -54,9 +54,8 @@ enum class IdentifiedFileType { UNKNOWN, }; - -class FileLoader { // NB: It is a REQUIREMENT that implementations of this class are entirely thread safe! +class FileLoader { public: enum class Flags { NONE, diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 1e1d30622f..38ae3aeec8 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -58,7 +58,7 @@ bool GameManager::IsGameInstalled(std::string name) { return File::Exists(pspGame + name); } -bool GameManager::DownloadAndInstall(std::string storeZipUrl) { +bool GameManager::DownloadAndInstall(std::string storeFileUrl) { if (curDownload_.get() != 0) { ERROR_LOG(HLE, "Can only process one download at a time"); return false; @@ -69,11 +69,11 @@ bool GameManager::DownloadAndInstall(std::string storeZipUrl) { } std::string filename = GetTempFilename(); - curDownload_ = g_DownloadManager.StartDownload(storeZipUrl, filename); + curDownload_ = g_DownloadManager.StartDownload(storeFileUrl, filename); return true; } -bool GameManager::CancelDownload() { +bool GameManager::CancelDownload() { if (!curDownload_) return false; @@ -108,18 +108,18 @@ bool GameManager::Uninstall(std::string name) { void GameManager::Update() { if (curDownload_.get() && curDownload_->Done()) { INFO_LOG(HLE, "Download completed! Status = %i", curDownload_->ResultCode()); - std::string zipName = curDownload_->outfile(); + std::string fileName = curDownload_->outfile(); if (curDownload_->ResultCode() == 200) { - if (!File::Exists(zipName)) { - ERROR_LOG(HLE, "Downloaded file %s does not exist :(", zipName.c_str()); + if (!File::Exists(fileName)) { + ERROR_LOG(HLE, "Downloaded file %s does not exist :(", fileName.c_str()); curDownload_.reset(); return; } // Game downloaded to temporary file - install it! - InstallGameOnThread(zipName, true); + InstallGameOnThread(fileName, true); } else { ERROR_LOG(HLE, "Expected HTTP status code 200, got status code %i. Install cancelled, deleting partial file.", curDownload_->ResultCode()); - File::Delete(zipName.c_str()); + File::Delete(fileName.c_str()); } curDownload_.reset(); } @@ -138,33 +138,22 @@ void countSlashes(std::string fileName, int *slashLocation, int *slashCount) { } } -bool GameManager::InstallGame(std::string zipfile, bool deleteAfter) { - if (installInProgress_) { - ERROR_LOG(HLE, "Cannot have two installs in progress at the same time"); - return false; - } - - if (!File::Exists(zipfile)) { - ERROR_LOG(HLE, "ZIP file %s doesn't exist", zipfile.c_str()); - return false; - } - - I18NCategory *sy = GetI18NCategory("System"); - installInProgress_ = true; - - std::string pspGame = GetSysDirectory(DIRECTORY_GAME); - INFO_LOG(HLE, "Installing %s into %s", zipfile.c_str(), pspGame.c_str()); - int error; +ZipFileContents DetectZipFileContents(std::string fileName, ZipFileInfo *info) { + int error = 0; #ifdef _WIN32 - struct zip *z = zip_open(ConvertUTF8ToWString(zipfile).c_str(), 0, &error); + struct zip *z = zip_open(ConvertUTF8ToWString(fileName).c_str(), 0, &error); #else struct zip *z = zip_open(zipfile.c_str(), 0, &error); #endif if (!z) { - ERROR_LOG(HLE, "Failed to open ZIP file %s, error code=%i", zipfile.c_str(), error); - return false; + return ZipFileContents::UNKNOWN; } + ZipFileContents retVal = DetectZipFileContents(z, info); + zip_close(z); + return retVal; +} +ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info) { int numFiles = zip_get_num_files(z); // Verify that this is a PSP zip file with the correct layout. We also try @@ -201,24 +190,67 @@ bool GameManager::InstallGame(std::string zipfile, bool deleteAfter) { } } + info->stripChars = stripChars; + info->numFiles = numFiles; + info->isoFileIndex = isoFileIndex; // If a ZIP is detected as both, let's let the memstick game interpretation prevail. - if (isPSPMemstickGame && isZippedISO) { - isZippedISO = false; + if (isPSPMemstickGame) { + return ZipFileContents::PSP_GAME_DIR; + } else if (isZippedISO) { + return ZipFileContents::ISO_FILE; + } else { + return ZipFileContents::UNKNOWN; + } +} + +bool GameManager::InstallGame(std::string fileName, bool deleteAfter) { + if (installInProgress_) { + ERROR_LOG(HLE, "Cannot have two installs in progress at the same time"); + return false; } - if (isPSPMemstickGame) { + if (!File::Exists(fileName)) { + ERROR_LOG(HLE, "Game file %s doesn't exist", fileName.c_str()); + return false; + } + + bool isRawISO = false; // TODO: Make it possible to pass in this information. + if (isRawISO) { + return InstallRawISO(fileName, fileName); + } + + I18NCategory *sy = GetI18NCategory("System"); + installInProgress_ = true; + + std::string pspGame = GetSysDirectory(DIRECTORY_GAME); + INFO_LOG(HLE, "Installing '%s' into '%s'", fileName.c_str(), pspGame.c_str()); + int error = 0; +#ifdef _WIN32 + struct zip *z = zip_open(ConvertUTF8ToWString(fileName).c_str(), 0, &error); +#else + struct zip *z = zip_open(zipfile.c_str(), 0, &error); +#endif + if (!z) { + ERROR_LOG(HLE, "Failed to open ZIP file %s, error code=%i", fileName.c_str(), error); + return false; + } + + ZipFileInfo info; + ZipFileContents contents = DetectZipFileContents(z, &info); + switch (contents) { + case ZipFileContents::PSP_GAME_DIR: // InstallMemstickGame contains code to close z. - return InstallMemstickGame(z, zipfile, pspGame, numFiles, stripChars, deleteAfter); - } else if (isZippedISO) { - return InstallZippedISO(z, isoFileIndex, zipfile, deleteAfter); - } else { + return InstallMemstickGame(z, fileName, pspGame, info.numFiles, info.stripChars, deleteAfter); + case ZipFileContents::ISO_FILE: + return InstallZippedISO(z, info.isoFileIndex, fileName, deleteAfter); + default: ERROR_LOG(HLE, "File not a PSP game, no EBOOT.PBP found."); installProgress_ = 0.0f; installInProgress_ = false; installError_ = sy->T("Not a PSP game"); InstallDone(); if (deleteAfter) - File::Delete(zipfile); + File::Delete(fileName); return false; } } @@ -228,8 +260,9 @@ bool GameManager::ExtractFile(struct zip *z, int file_index, std::string outFile zip_stat_index(z, file_index, 0, &zstat); size_t size = zstat.size; + // Don't spam the log. if (file_index < 10) { - INFO_LOG(HLE, "Writing %d bytes to %s", (int)size, outFilename.c_str()); + INFO_LOG(HLE, "Writing %d bytes to '%s'", (int)size, outFilename.c_str()); } zip_file *zf = zip_fopen_index(z, file_index, 0); @@ -390,6 +423,16 @@ bool GameManager::InstallGameOnThread(std::string zipFile, bool deleteAfter) { return true; } +bool GameManager::InstallRawISO(std::string file, std::string originalName) { + std::string destPath = g_Config.currentDirectory + "/" + originalName; + File::Copy(file, destPath); + installProgress_ = 1.0f; + installInProgress_ = false; + installError_ = ""; + InstallDone(); + return true; +} + void GameManager::InstallDone() { if (installThread_.get() != 0) { installThread_.reset(); diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index 8e5a745de4..b654fb8655 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -69,9 +69,10 @@ public: bool InstallGameOnThread(std::string zipFile, bool deleteAfter); private: - bool InstallGame(std::string zipfile, bool deleteAfter = false); + bool InstallGame(std::string zipfile, bool deleteAfter); bool InstallMemstickGame(struct zip *z, std::string zipFile, std::string pspGame, int numFiles, int stripChars, bool deleteAfter); bool InstallZippedISO(struct zip *z, int isoFileIndex, std::string zipfile, bool deleteAfter); + bool InstallRawISO(std::string zipFile, std::string originalName); void InstallDone(); bool ExtractFile(struct zip *z, int file_index, std::string outFilename, size_t *bytesCopied, size_t allBytes); @@ -84,3 +85,18 @@ private: }; extern GameManager g_GameManager; + +enum class ZipFileContents { + UNKNOWN, + PSP_GAME_DIR, + ISO_FILE, +}; + +struct ZipFileInfo { + int numFiles; + int stripChars; // for PSP game + int isoFileIndex; // for ISO +}; + +ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info); +ZipFileContents DetectZipFileContents(std::string fileName, ZipFileInfo *info); diff --git a/UI/InstallZipScreen.cpp b/UI/InstallZipScreen.cpp index 943d0c9908..a5ab9d9634 100644 --- a/UI/InstallZipScreen.cpp +++ b/UI/InstallZipScreen.cpp @@ -42,24 +42,34 @@ void InstallZipScreen::CreateViews() { root_ = new LinearLayout(ORIENT_HORIZONTAL); ViewGroup *leftColumn = new AnchorLayout(new LinearLayoutParams(1.0f)); - root_->Add(leftColumn); - - std::string shortFilename = GetFilenameFromPath(zipPath_); - leftColumn->Add(new TextView(iz->T("Install game from ZIP file?"), ALIGN_LEFT, false, new AnchorLayoutParams(10, 10, NONE, NONE))); - leftColumn->Add(new TextView(shortFilename, ALIGN_LEFT, false, new AnchorLayoutParams(10, 60, NONE, NONE))); - - doneView_ = leftColumn->Add(new TextView("", new AnchorLayoutParams(10, 120, NONE, NONE))); - progressBar_ = leftColumn->Add(new ProgressBar(new AnchorLayoutParams(10, 200, 200, NONE))); - ViewGroup *rightColumnItems = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins)); + root_->Add(leftColumn); root_->Add(rightColumnItems); - installChoice_ = rightColumnItems->Add(new Choice(iz->T("Install"))); - installChoice_->OnClick.Handle(this, &InstallZipScreen::OnInstall); - backChoice_ = rightColumnItems->Add(new Choice(di->T("Back"))); - backChoice_->OnClick.Handle(this, &UIScreen::OnOK); // OK so that EmuScreen will handle it right + std::string shortFilename = GetFilenameFromPath(zipPath_); + + // TODO: Do in the background? + ZipFileInfo zipInfo; + ZipFileContents contents = DetectZipFileContents(zipPath_, &zipInfo); - rightColumnItems->Add(new CheckBox(&deleteZipFile_, iz->T("Delete ZIP file"))); + if (contents == ZipFileContents::ISO_FILE || contents == ZipFileContents::PSP_GAME_DIR) { + std::string question = iz->T("Install game from ZIP file?"); + leftColumn->Add(new TextView(question, ALIGN_LEFT, false, new AnchorLayoutParams(10, 10, NONE, NONE))); + leftColumn->Add(new TextView(shortFilename, ALIGN_LEFT, false, new AnchorLayoutParams(10, 60, NONE, NONE))); + + doneView_ = leftColumn->Add(new TextView("", new AnchorLayoutParams(10, 120, NONE, NONE))); + progressBar_ = leftColumn->Add(new ProgressBar(new AnchorLayoutParams(10, 200, 200, NONE))); + + installChoice_ = rightColumnItems->Add(new Choice(iz->T("Install"))); + installChoice_->OnClick.Handle(this, &InstallZipScreen::OnInstall); + backChoice_ = rightColumnItems->Add(new Choice(di->T("Back"))); + backChoice_->OnClick.Handle(this, &UIScreen::OnOK); // OK so that EmuScreen will handle it right + rightColumnItems->Add(new CheckBox(&deleteZipFile_, iz->T("Delete ZIP file"))); + } else { + leftColumn->Add(new TextView(iz->T("Zip file does not contain PSP software"), ALIGN_LEFT, false, new AnchorLayoutParams(10, 10, NONE, NONE))); + backChoice_ = rightColumnItems->Add(new Choice(di->T("Back"))); + backChoice_->OnClick.Handle(this, &UIScreen::OnOK); // OK so that EmuScreen will handle it right + } } bool InstallZipScreen::key(const KeyInput &key) { @@ -87,13 +97,16 @@ void InstallZipScreen::update() { progressBar_->SetProgress(g_GameManager.GetCurrentInstallProgressPercentage()); backChoice_->SetEnabled(false); } else { - progressBar_->SetVisibility(V_GONE); + if (progressBar_) + progressBar_->SetVisibility(V_GONE); backChoice_->SetEnabled(true); std::string err = g_GameManager.GetInstallError(); if (!err.empty()) { - doneView_->SetText(iz->T(err.c_str())); + if (doneView_) + doneView_->SetText(iz->T(err.c_str())); } else if (installStarted_) { - doneView_->SetText(iz->T("Installed!")); + if (doneView_) + doneView_->SetText(iz->T("Installed!")); MainScreen::showHomebrewTab = true; } } diff --git a/UI/InstallZipScreen.h b/UI/InstallZipScreen.h index b84d91e21f..d189041a1e 100644 --- a/UI/InstallZipScreen.h +++ b/UI/InstallZipScreen.h @@ -26,7 +26,7 @@ class InstallZipScreen : public UIDialogScreenWithBackground { public: - InstallZipScreen(std::string zipPath) : installChoice_(0), doneView_(0), zipPath_(zipPath), installStarted_(false), deleteZipFile_(false) {} + InstallZipScreen(std::string zipPath) : zipPath_(zipPath) {} virtual void update() override; virtual bool key(const KeyInput &key) override; @@ -36,12 +36,12 @@ protected: private: UI::EventReturn OnInstall(UI::EventParams ¶ms); - UI::Choice *installChoice_; - UI::Choice *backChoice_; - UI::ProgressBar *progressBar_; - UI::TextView *doneView_; + UI::Choice *installChoice_ = nullptr; + UI::Choice *backChoice_ = nullptr; + UI::ProgressBar *progressBar_ = nullptr; + UI::TextView *doneView_ = nullptr; std::string zipPath_; - bool installStarted_; - bool deleteZipFile_; + bool installStarted_ = false; + bool deleteZipFile_ = false; };