From b4d9d038b479a022a1986aa3092ca560cbc4b6ee Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 14 Jul 2019 16:01:37 -0700 Subject: [PATCH 1/2] Replacement: Allow region-specific settings. This makes it easier to reuse one pack for multiple game ids. --- Core/Config.cpp | 2 +- Core/TextureReplacer.cpp | 139 ++++++++++++++++++++++++--------------- Core/TextureReplacer.h | 2 + 3 files changed, 88 insertions(+), 55 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index ff381e219e74..201c09cbff85 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -744,7 +744,7 @@ static ConfigSetting graphicsSettings[] = { ReportedConfigSetting("ReplaceTextures", &g_Config.bReplaceTextures, true, true, true), ReportedConfigSetting("SaveNewTextures", &g_Config.bSaveNewTextures, false, true, true), - ReportedConfigSetting("IgnoreTextureFilenames", &g_Config.bIgnoreTextureFilenames, true, true, false), + ConfigSetting("IgnoreTextureFilenames", &g_Config.bIgnoreTextureFilenames, false, true, true), ReportedConfigSetting("TexScalingLevel", &g_Config.iTexScalingLevel, 1, true, true), ReportedConfigSetting("TexScalingType", &g_Config.iTexScalingType, 0, true, true), diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 1be43ebba2b6..6087c87aebbc 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -77,82 +77,109 @@ bool TextureReplacer::LoadIni() { aliases_.clear(); hashranges_.clear(); + allowVideo_ = false; + ignoreAddress_ = false; + reduceHash_ = false; + if (File::Exists(basePath_ + INI_FILENAME)) { IniFile ini; ini.LoadFromVFS(basePath_ + INI_FILENAME); - auto options = ini.GetOrCreateSection("options"); - std::string hash; - options->Get("hash", &hash, ""); - // TODO: crc32c. - if (strcasecmp(hash.c_str(), "quick") == 0) { - hash_ = ReplacedTextureHash::QUICK; - } else if (strcasecmp(hash.c_str(), "xxh32") == 0) { - hash_ = ReplacedTextureHash::XXH32; - } else if (strcasecmp(hash.c_str(), "xxh64") == 0) { - hash_ = ReplacedTextureHash::XXH64; - } else { - ERROR_LOG(G3D, "Unsupported hash type: %s", hash.c_str()); + if (!LoadIniValues(ini)) { return false; } - options->Get("video", &allowVideo_, false); - options->Get("ignoreAddress", &ignoreAddress_, false); - options->Get("reduceHash", &reduceHash_, false); // Multiplies sizeInRAM/bytesPerLine in XXHASH by 0.5 - if (reduceHash_ && hash_ == ReplacedTextureHash::QUICK) { - reduceHash_ = false; - ERROR_LOG(G3D, "Texture Replacement: reduceHash option requires safer hash, use xxh32 or xxh64 instead."); - } + // Allow overriding settings per game id. + std::string overrideFilename; + if (ini.GetOrCreateSection("games")->Get(gameID_.c_str(), &overrideFilename, "")) { + if (!overrideFilename.empty() && overrideFilename != INI_FILENAME) { + INFO_LOG(G3D, "Loading extra texture ini: %s", overrideFilename.c_str()); + IniFile overrideIni; + overrideIni.LoadFromVFS(basePath_ + overrideFilename); - if (ignoreAddress_ && hash_ == ReplacedTextureHash::QUICK) { - ignoreAddress_ = false; - ERROR_LOG(G3D, "Texture Replacement: ignoreAddress option requires safer hash, use xxh32 or xxh64 instead."); + if (!LoadIniValues(overrideIni, true)) { + return false; + } + } } + } - int version = 0; - if (options->Get("version", &version, 0) && version > VERSION) { - ERROR_LOG(G3D, "Unsupported texture replacement version %d, trying anyway", version); - } + // The ini doesn't have to exist for it to be valid. + return true; +} + +bool TextureReplacer::LoadIniValues(IniFile &ini, bool isOverride) { + auto options = ini.GetOrCreateSection("options"); + std::string hash; + options->Get("hash", &hash, ""); + // TODO: crc32c. + if (strcasecmp(hash.c_str(), "quick") == 0) { + hash_ = ReplacedTextureHash::QUICK; + } else if (strcasecmp(hash.c_str(), "xxh32") == 0) { + hash_ = ReplacedTextureHash::XXH32; + } else if (strcasecmp(hash.c_str(), "xxh64") == 0) { + hash_ = ReplacedTextureHash::XXH64; + } else if (!isOverride || !hash.empty()) { + ERROR_LOG(G3D, "Unsupported hash type: %s", hash.c_str()); + return false; + } + + options->Get("video", &allowVideo_, allowVideo_); + options->Get("ignoreAddress", &ignoreAddress_, ignoreAddress_); + // Multiplies sizeInRAM/bytesPerLine in XXHASH by 0.5. + options->Get("reduceHash", &reduceHash_, reduceHash_); + if (reduceHash_ && hash_ == ReplacedTextureHash::QUICK) { + reduceHash_ = false; + ERROR_LOG(G3D, "Texture Replacement: reduceHash option requires safer hash, use xxh32 or xxh64 instead."); + } + + if (ignoreAddress_ && hash_ == ReplacedTextureHash::QUICK) { + ignoreAddress_ = false; + ERROR_LOG(G3D, "Texture Replacement: ignoreAddress option requires safer hash, use xxh32 or xxh64 instead."); + } + + int version = 0; + if (options->Get("version", &version, 0) && version > VERSION) { + ERROR_LOG(G3D, "Unsupported texture replacement version %d, trying anyway", version); + } - bool filenameWarning = false; - if (ini.HasSection("hashes")) { - auto hashes = ini.GetOrCreateSection("hashes")->ToMap(); - // Format: hashname = filename.png - bool checkFilenames = g_Config.bSaveNewTextures && g_Config.bIgnoreTextureFilenames; - for (const auto &item : hashes) { - ReplacementAliasKey key(0, 0, 0); - if (sscanf(item.first.c_str(), "%16llx%8x_%d", &key.cachekey, &key.hash, &key.level) >= 1) { - aliases_[key] = item.second; - if (checkFilenames) { + bool filenameWarning = false; + if (ini.HasSection("hashes")) { + auto hashes = ini.GetOrCreateSection("hashes")->ToMap(); + // Format: hashname = filename.png + bool checkFilenames = g_Config.bSaveNewTextures && !g_Config.bIgnoreTextureFilenames; + for (const auto &item : hashes) { + ReplacementAliasKey key(0, 0, 0); + if (sscanf(item.first.c_str(), "%16llx%8x_%d", &key.cachekey, &key.hash, &key.level) >= 1) { + aliases_[key] = item.second; + if (checkFilenames) { #if PPSSPP_PLATFORM(WINDOWS) - // Uppercase probably means the filenames don't match. - // Avoiding an actual check of the filenames to avoid performance impact. - filenameWarning = filenameWarning || item.second.find_first_of("\\ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos; + // Uppercase probably means the filenames don't match. + // Avoiding an actual check of the filenames to avoid performance impact. + filenameWarning = filenameWarning || item.second.find_first_of("\\ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos; #else - filenameWarning = filenameWarning || item.second.find_first_of("\\:<>|?*") != std::string::npos; + filenameWarning = filenameWarning || item.second.find_first_of("\\:<>|?*") != std::string::npos; #endif - } - } else { - ERROR_LOG(G3D, "Unsupported syntax under [hashes]: %s", item.first.c_str()); } + } else { + ERROR_LOG(G3D, "Unsupported syntax under [hashes]: %s", item.first.c_str()); } } + } - if (filenameWarning) { - I18NCategory *err = GetI18NCategory("Error"); - host->NotifyUserMessage(err->T("textures.ini filenames may not be cross-platform"), 6.0f); - } + if (filenameWarning) { + I18NCategory *err = GetI18NCategory("Error"); + host->NotifyUserMessage(err->T("textures.ini filenames may not be cross-platform"), 6.0f); + } - if (ini.HasSection("hashranges")) { - auto hashranges = ini.GetOrCreateSection("hashranges")->ToMap(); - // Format: addr,w,h = newW,newH - for (const auto &item : hashranges) { - ParseHashRange(item.first, item.second); - } + if (ini.HasSection("hashranges")) { + auto hashranges = ini.GetOrCreateSection("hashranges")->ToMap(); + // Format: addr,w,h = newW,newH + for (const auto &item : hashranges) { + ParseHashRange(item.first, item.second); } } - // The ini doesn't have to exist for it to be valid. return true; } @@ -660,6 +687,10 @@ bool TextureReplacer::GenerateIni(const std::string &gameID, std::string *genera fs << "[options]\n"; fs << "version = 1\n"; fs << "hash = quick\n"; + fs << "[games]\n"; + fs << "# Used to make it easier to install, and override settings for other regions.\n"; + fs << "# Files still have to be copied to each TEXTURES folder."; + fs << gameID << " = textures.ini\n"; fs << "\n"; fs << "# Use / for folders not \\, avoid special characters, and stick to lowercase.\n"; fs << "# See wiki for more info.\n"; diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index 35049b390cb0..35e3c22a1a55 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -25,6 +25,7 @@ #include "Common/MemoryUtil.h" #include "GPU/ge_constants.h" +class IniFile; class TextureCacheCommon; class TextureReplacer; @@ -190,6 +191,7 @@ class TextureReplacer { protected: bool LoadIni(); + bool LoadIniValues(IniFile &ini, bool isOverride = false); void ParseHashRange(const std::string &key, const std::string &value); bool LookupHashRange(u32 addr, int &w, int &h); std::string LookupHashFile(u64 cachekey, u32 hash, int level); From 8aed2129524d0f461cef03b72d99a6cf2d5ece20 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 14 Jul 2019 17:43:51 -0700 Subject: [PATCH 2/2] UI: Allow installing texture packs from zips. Requires the textures.ini to have a [games] section. --- Core/Util/GameManager.cpp | 203 ++++++++++++++++++++++++++++++++++---- Core/Util/GameManager.h | 12 ++- UI/InstallZipScreen.cpp | 23 ++++- UI/InstallZipScreen.h | 1 + 4 files changed, 214 insertions(+), 25 deletions(-) diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 909f798a73d0..a8ea9d9adb36 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -34,7 +34,11 @@ #include "Common/FileUtil.h" #include "Common/StringUtils.h" #include "Core/Config.h" +#include "Core/Loaders.h" +#include "Core/ELF/ParamSFO.h" +#include "Core/ELF/PBPReader.h" #include "Core/System.h" +#include "Core/FileSystems/ISOFileSystem.h" #include "Core/Util/GameManager.h" #include "i18n/i18n.h" @@ -171,8 +175,11 @@ ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info) { // directory of the Games tab (where else?). bool isPSPMemstickGame = false; bool isZippedISO = false; + bool isTexturePack = false; int stripChars = 0; int isoFileIndex = -1; + int stripCharsTexturePack = -1; + int textureIniIndex = -1; for (int i = 0; i < numFiles; i++) { const char *fn = zip_get_name(z, i, 0); @@ -198,17 +205,33 @@ ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info) { isZippedISO = true; isoFileIndex = i; } + } else if (zippedName.find("textures.ini") != std::string::npos) { + int slashCount = 0; + int slashLocation = -1; + countSlashes(zippedName, &slashLocation, &slashCount); + if (stripCharsTexturePack == -1 || slashLocation < stripCharsTexturePack + 1) { + stripCharsTexturePack = slashLocation + 1; + isTexturePack = true; + textureIniIndex = i; + } } } info->stripChars = stripChars; info->numFiles = numFiles; info->isoFileIndex = isoFileIndex; + info->textureIniIndex = textureIniIndex; + info->ignoreMetaFiles = false; + // If a ZIP is detected as both, let's let the memstick game interpretation prevail. if (isPSPMemstickGame) { return ZipFileContents::PSP_GAME_DIR; } else if (isZippedISO) { return ZipFileContents::ISO_FILE; + } else if (isTexturePack) { + info->stripChars = stripCharsTexturePack; + info->ignoreMetaFiles = true; + return ZipFileContents::TEXTURE_PACK; } else { return ZipFileContents::UNKNOWN; } @@ -236,7 +259,7 @@ bool GameManager::InstallGame(const std::string &url, const std::string &fileNam installInProgress_ = true; std::string pspGame = GetSysDirectory(DIRECTORY_GAME); - INFO_LOG(HLE, "Installing '%s' into '%s'", fileName.c_str(), pspGame.c_str()); + std::string dest = pspGame; int error = 0; #ifdef _WIN32 struct zip *z = zip_open(ConvertUTF8ToWString(fileName).c_str(), 0, &error); @@ -252,22 +275,148 @@ bool GameManager::InstallGame(const std::string &url, const std::string &fileNam ZipFileContents contents = DetectZipFileContents(z, &info); switch (contents) { case ZipFileContents::PSP_GAME_DIR: + INFO_LOG(HLE, "Installing '%s' into '%s'", fileName.c_str(), pspGame.c_str()); // InstallMemstickGame contains code to close z. - return InstallMemstickGame(z, fileName, pspGame, info.numFiles, info.stripChars, deleteAfter); + return InstallMemstickGame(z, fileName, pspGame, info, false, deleteAfter); case ZipFileContents::ISO_FILE: + INFO_LOG(HLE, "Installing '%s' into its containing directory", fileName.c_str()); + // InstallZippedISO contains code to close z. return InstallZippedISO(z, info.isoFileIndex, fileName, deleteAfter); + case ZipFileContents::TEXTURE_PACK: + // InstallMemstickGame contains code to close z, and works for textures too. + if (DetectTexturePackDest(z, info.textureIniIndex, &dest)) { + INFO_LOG(HLE, "Installing '%s' into '%s'", fileName.c_str(), dest.c_str()); + File::CreateFullPath(dest); + File::CreateEmptyFile(dest + "/.nomedia"); + return InstallMemstickGame(z, fileName, dest, info, true, deleteAfter); + } + return false; 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(); + SetInstallError(sy->T("Not a PSP game")); if (deleteAfter) File::Delete(fileName); return false; } } +bool GameManager::DetectTexturePackDest(struct zip *z, int iniIndex, std::string *dest) { + I18NCategory *iz = GetI18NCategory("InstallZip"); + + struct zip_stat zstat; + zip_stat_index(z, iniIndex, 0, &zstat); + + if (zstat.size >= 32 * 1024 * 1024) { + SetInstallError(iz->T("Texture pack doesn't support install")); + return false; + } + + std::string buffer; + buffer.resize(zstat.size); + zip_file *zf = zip_fopen_index(z, iniIndex, 0); + if (zip_fread(zf, &buffer[0], buffer.size()) != (ssize_t)zstat.size) { + SetInstallError(iz->T("Zip archive corrupt")); + return false; + } + + IniFile ini; + std::stringstream sstream(buffer); + ini.Load(sstream); + + auto games = ini.GetOrCreateSection("games")->ToMap(); + if (games.empty()) { + SetInstallError(iz->T("Texture pack doesn't support install")); + return false; + } + + std::string gameID = games.begin()->first; + if (games.size() > 1) { + // Check for any supported game on their recent list and use that instead. + for (const std::string &path : g_Config.recentIsos) { + std::string recentID = GetGameID(path); + if (games.find(recentID) != games.end()) { + gameID = recentID; + break; + } + } + } + + std::string pspTextures = GetSysDirectory(DIRECTORY_TEXTURES); + *dest = pspTextures + gameID + "/"; + return true; +} + +void GameManager::SetInstallError(const std::string &err) { + installProgress_ = 0.0f; + installInProgress_ = false; + installError_ = err; + InstallDone(); +} + +std::string GameManager::GetGameID(const std::string &path) const { + auto loader = ConstructFileLoader(path); + std::string id; + + switch (Identify_File(loader)) { + case IdentifiedFileType::PSP_PBP_DIRECTORY: + delete loader; + loader = ConstructFileLoader(ResolvePBPFile(path)); + id = GetPBPGameID(loader); + break; + + case IdentifiedFileType::PSP_PBP: + id = GetPBPGameID(loader); + break; + + case IdentifiedFileType::PSP_ISO: + case IdentifiedFileType::PSP_ISO_NP: + id = GetISOGameID(loader); + break; + + default: + id.clear(); + break; + } + + delete loader; + return id; +} + +std::string GameManager::GetPBPGameID(FileLoader *loader) const { + PBPReader pbp(loader); + std::vector sfoData; + if (pbp.GetSubFile(PBP_PARAM_SFO, &sfoData)) { + ParamSFOData sfo; + sfo.ReadSFO(sfoData); + return sfo.GetValueString("DISC_ID"); + } + return ""; +} + +std::string GameManager::GetISOGameID(FileLoader *loader) const { + SequentialHandleAllocator handles; + BlockDevice *bd = constructBlockDevice(loader); + if (!bd) { + return ""; + } + ISOFileSystem umd(&handles, bd); + + PSPFileInfo info = umd.GetFileInfo("/PSP_GAME/PARAM.SFO"); + int handle = 0; + if (info.exists) { + handle = umd.OpenFile("/PSP_GAME/PARAM.SFO", FILEACCESS_READ); + } + + std::string sfoData; + sfoData.resize(info.size); + umd.ReadFile(handle, (u8 *)&sfoData[0], info.size); + umd.CloseFile(handle); + + ParamSFOData sfo; + sfo.ReadSFO((const u8 *)sfoData.data(), sfoData.size()); + return sfo.GetValueString("DISC_ID"); +} + bool GameManager::ExtractFile(struct zip *z, int file_index, std::string outFilename, size_t *bytesCopied, size_t allBytes) { struct zip_stat zstat; zip_stat_index(z, file_index, 0, &zstat); @@ -319,18 +468,33 @@ bool GameManager::ExtractFile(struct zip *z, int file_index, std::string outFile } } -bool GameManager::InstallMemstickGame(struct zip *z, std::string zipfile, std::string pspGame, int numFiles, int stripChars, bool deleteAfter) { +bool GameManager::InstallMemstickGame(struct zip *z, const std::string &zipfile, const std::string &dest, const ZipFileInfo &info, bool allowRoot, bool deleteAfter) { size_t allBytes = 0; size_t bytesCopied = 0; I18NCategory *sy = GetI18NCategory("System"); + auto fileAllowed = [&](const char *fn) { + if (!allowRoot && strchr(fn, '/') == 0) + return false; + + const char *basefn = strrchr(fn, '/'); + basefn = basefn ? basefn + 1 : fn; + + if (info.ignoreMetaFiles) { + if (basefn[0] == '.' || !strcmp(basefn, "Thumbs.db") || !strcmp(basefn, "desktop.ini")) + return false; + } + + return true; + }; + // Create all the directories first in one pass std::set createdDirs; - for (int i = 0; i < numFiles; i++) { + for (int i = 0; i < info.numFiles; i++) { const char *fn = zip_get_name(z, i, 0); std::string zippedName = fn; - std::string outFilename = pspGame + zippedName.substr(stripChars); + std::string outFilename = dest + zippedName.substr(info.stripChars); bool isDir = *outFilename.rbegin() == '/'; if (!isDir && outFilename.find("/") != std::string::npos) { outFilename = outFilename.substr(0, outFilename.rfind('/')); @@ -339,7 +503,7 @@ bool GameManager::InstallMemstickGame(struct zip *z, std::string zipfile, std::s File::CreateFullPath(outFilename.c_str()); createdDirs.insert(outFilename); } - if (!isDir && strchr(fn, '/') != 0) { + if (!isDir && fileAllowed(fn)) { struct zip_stat zstat; if (zip_stat_index(z, i, 0, &zstat) >= 0) { allBytes += zstat.size; @@ -349,13 +513,13 @@ bool GameManager::InstallMemstickGame(struct zip *z, std::string zipfile, std::s // Now, loop through again in a second pass, writing files. std::vector createdFiles; - for (int i = 0; i < numFiles; i++) { + for (int i = 0; i < info.numFiles; i++) { const char *fn = zip_get_name(z, i, 0); // Note that we do NOT write files that are not in a directory, to avoid random // README files etc. - if (strchr(fn, '/') != 0) { - fn += stripChars; - std::string outFilename = pspGame + fn; + if (fileAllowed(fn)) { + fn += info.stripChars; + std::string outFilename = dest + fn; bool isDir = *outFilename.rbegin() == '/'; if (isDir) continue; @@ -367,10 +531,10 @@ bool GameManager::InstallMemstickGame(struct zip *z, std::string zipfile, std::s } } } - INFO_LOG(HLE, "Extracted %i files (%i bytes / %i).", numFiles, (int)bytesCopied, (int)allBytes); + INFO_LOG(HLE, "Extracted %i files (%i bytes / %i).", info.numFiles, (int)bytesCopied, (int)allBytes); zip_close(z); - z = 0; + z = nullptr; installProgress_ = 1.0f; installInProgress_ = false; installError_ = ""; @@ -381,11 +545,8 @@ bool GameManager::InstallMemstickGame(struct zip *z, std::string zipfile, std::s return true; bail: - zip_close(z); // We end up here if disk is full or couldn't write to storage for some other reason. - installProgress_ = 0.0f; - installInProgress_ = false; - installError_ = sy->T("Storage full"); + zip_close(z); // We don't delete the original in this case. Try to delete the files we created so far. for (size_t i = 0; i < createdFiles.size(); i++) { File::Delete(createdFiles[i].c_str()); @@ -393,7 +554,7 @@ bool GameManager::InstallMemstickGame(struct zip *z, std::string zipfile, std::s for (auto iter = createdDirs.begin(); iter != createdDirs.end(); ++iter) { File::DeleteDir(iter->c_str()); } - InstallDone(); + SetInstallError(sy->T("Storage full")); return false; } diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index 48062f76d7b1..898d24ad4dfc 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -32,6 +32,8 @@ enum class GameManagerState { }; struct zip; +class FileLoader; +struct ZipFileInfo; class GameManager { public: @@ -70,13 +72,18 @@ class GameManager { private: bool InstallGame(const std::string &url, const std::string &tempFileName, bool deleteAfter); - bool InstallMemstickGame(struct zip *z, std::string zipFile, std::string pspGame, int numFiles, int stripChars, bool deleteAfter); + bool InstallMemstickGame(struct zip *z, const std::string &zipFile, const std::string &pspGame, const ZipFileInfo &info, bool allowRoot, bool deleteAfter); bool InstallZippedISO(struct zip *z, int isoFileIndex, std::string zipfile, bool deleteAfter); bool InstallRawISO(const std::string &zipFile, const std::string &originalName, bool deleteAfter); void InstallDone(); bool ExtractFile(struct zip *z, int file_index, std::string outFilename, size_t *bytesCopied, size_t allBytes); + bool DetectTexturePackDest(struct zip *z, int iniIndex, std::string *dest); + void SetInstallError(const std::string &err); std::string GetTempFilename() const; + std::string GetGameID(const std::string &path) const; + std::string GetPBPGameID(FileLoader *loader) const; + std::string GetISOGameID(FileLoader *loader) const; std::shared_ptr curDownload_; std::shared_ptr installThread_; bool installInProgress_; @@ -90,12 +97,15 @@ enum class ZipFileContents { UNKNOWN, PSP_GAME_DIR, ISO_FILE, + TEXTURE_PACK, }; struct ZipFileInfo { int numFiles; int stripChars; // for PSP game int isoFileIndex; // for ISO + int textureIniIndex; // for textures + bool ignoreMetaFiles; }; ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info); diff --git a/UI/InstallZipScreen.cpp b/UI/InstallZipScreen.cpp index ea7d206d8a70..79908e8ac614 100644 --- a/UI/InstallZipScreen.cpp +++ b/UI/InstallZipScreen.cpp @@ -63,13 +63,30 @@ void InstallZipScreen::CreateViews() { 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"))); + + returnToHomebrew_ = true; + } else if (contents == ZipFileContents::TEXTURE_PACK) { + std::string question = iz->T("Install textures 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"))); + rightColumnItems->Add(new CheckBox(&deleteZipFile_, iz->T("Delete ZIP file"))); + + returnToHomebrew_ = false; } 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 } + + // OK so that EmuScreen will handle it right. + backChoice_->OnClick.Handle(this, &UIScreen::OnOK); } bool InstallZipScreen::key(const KeyInput &key) { @@ -107,7 +124,7 @@ void InstallZipScreen::update() { } else if (installStarted_) { if (doneView_) doneView_->SetText(iz->T("Installed!")); - MainScreen::showHomebrewTab = true; + MainScreen::showHomebrewTab = returnToHomebrew_; } } UIScreen::update(); diff --git a/UI/InstallZipScreen.h b/UI/InstallZipScreen.h index d189041a1eb3..e8015fdfa53b 100644 --- a/UI/InstallZipScreen.h +++ b/UI/InstallZipScreen.h @@ -41,6 +41,7 @@ class InstallZipScreen : public UIDialogScreenWithBackground { UI::ProgressBar *progressBar_ = nullptr; UI::TextView *doneView_ = nullptr; std::string zipPath_; + bool returnToHomebrew_ = true; bool installStarted_ = false; bool deleteZipFile_ = false; };