From 849000a958b83d18ca1f030f4f2fe97418ee7de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 24 Dec 2023 21:54:44 +0100 Subject: [PATCH 1/4] Texture replacer: Break out two functions from LoadIniValues --- GPU/Common/TextureReplacer.cpp | 107 ++++++++++++++++++--------------- GPU/Common/TextureReplacer.h | 3 + 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index a9f84e5e2d6d..678b3672e57a 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -198,6 +198,60 @@ bool TextureReplacer::LoadIni() { return true; } +void TextureReplacer::ScanForHashNamedFiles(VFSBackend *dir, std::map> &filenameMap) { + // Scan the root of the texture folder/zip and preinitialize the hash map. + std::vector filesInRoot; + dir->GetFileListing("", &filesInRoot, nullptr); + for (auto file : filesInRoot) { + if (file.isDirectory) + continue; + if (file.name.empty() || file.name[0] == '.') + continue; + Path path(file.name); + std::string ext = path.GetFileExtension(); + + std::string hash = file.name.substr(0, file.name.size() - ext.size()); + if (!((hash.size() >= 26 && hash.size() <= 27 && hash[24] == '_') || hash.size() == 24)) { + continue; + } + // OK, it's hash-like enough to try to parse it into the map. + if (equalsNoCase(ext, ".ktx2") || equalsNoCase(ext, ".png") || equalsNoCase(ext, ".dds") || equalsNoCase(ext, ".zim")) { + ReplacementCacheKey key(0, 0); + int level = 0; // sscanf might fail to pluck the level, but that's ok, we default to 0. sscanf doesn't write to non-matched outputs. + if (sscanf(hash.c_str(), "%16llx%8x_%d", &key.cachekey, &key.hash, &level) >= 1) { + // INFO_LOG(G3D, "hash-like file in root, adding: %s", file.name.c_str()); + filenameMap[key][level] = file.name; + } + } + } +} + +void TextureReplacer::ComputeAliasMap(const std::map> &filenameMap) { + for (auto &pair : filenameMap) { + std::string alias; + int mipIndex = 0; + for (auto &level : pair.second) { + if (level.first == mipIndex) { + alias += level.second + "|"; + mipIndex++; + } else { + WARN_LOG(G3D, "Non-sequential mip index %d, breaking. filenames=%s", level.first, level.second.c_str()); + break; + } + } + if (alias == "|") { + alias = ""; // marker for no replacement + } + // Replace any '\' with '/', to be safe and consistent. Since these are from the ini file, we do this on all platforms. + for (auto &c : alias) { + if (c == '\\') { + c = '/'; + } + } + aliases_[pair.first] = alias; + } +} + bool TextureReplacer::LoadIniValues(IniFile &ini, VFSBackend *dir, bool isOverride) { auto options = ini.GetOrCreateSection("options"); std::string hash; @@ -236,36 +290,13 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, VFSBackend *dir, bool isOverri int badFileNameCount = 0; std::map> filenameMap; - std::string badFilenames; - // Scan the root of the texture folder/zip and preinitialize the hash map. - std::vector filesInRoot; if (dir) { - dir->GetFileListing("", &filesInRoot, nullptr); - for (auto file : filesInRoot) { - if (file.isDirectory) - continue; - if (file.name.empty() || file.name[0] == '.') - continue; - Path path(file.name); - std::string ext = path.GetFileExtension(); - - std::string hash = file.name.substr(0, file.name.size() - ext.size()); - if (!((hash.size() >= 26 && hash.size() <= 27 && hash[24] == '_') || hash.size() == 24)) { - continue; - } - // OK, it's hash-like enough to try to parse it into the map. - if (equalsNoCase(ext, ".ktx2") || equalsNoCase(ext, ".png") || equalsNoCase(ext, ".dds") || equalsNoCase(ext, ".zim")) { - ReplacementCacheKey key(0, 0); - int level = 0; // sscanf might fail to pluck the level, but that's ok, we default to 0. sscanf doesn't write to non-matched outputs. - if (sscanf(hash.c_str(), "%16llx%8x_%d", &key.cachekey, &key.hash, &level) >= 1) { - // INFO_LOG(G3D, "hash-like file in root, adding: %s", file.name.c_str()); - filenameMap[key][level] = file.name; - } - } - } + ScanForHashNamedFiles(dir, filenameMap); } + std::string badFilenames; + if (ini.HasSection("hashes")) { auto hashes = ini.GetOrCreateSection("hashes")->ToMap(); // Format: hashname = filename.png @@ -307,29 +338,7 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, VFSBackend *dir, bool isOverri } // Now, translate the filenameMap to the final aliasMap. - for (auto &pair : filenameMap) { - std::string alias; - int mipIndex = 0; - for (auto &level : pair.second) { - if (level.first == mipIndex) { - alias += level.second + "|"; - mipIndex++; - } else { - WARN_LOG(G3D, "Non-sequential mip index %d, breaking. filenames=%s", level.first, level.second.c_str()); - break; - } - } - if (alias == "|") { - alias = ""; // marker for no replacement - } - // Replace any '\' with '/', to be safe and consistent. Since these are from the ini file, we do this on all platforms. - for (auto &c : alias) { - if (c == '\\') { - c = '/'; - } - } - aliases_[pair.first] = alias; - } + ComputeAliasMap(filenameMap); if (badFileNameCount > 0) { auto err = GetI18NCategory(I18NCat::ERRORS); diff --git a/GPU/Common/TextureReplacer.h b/GPU/Common/TextureReplacer.h index a0f76a3bf977..c1a0362af3e1 100644 --- a/GPU/Common/TextureReplacer.h +++ b/GPU/Common/TextureReplacer.h @@ -135,6 +135,9 @@ class TextureReplacer { float LookupReduceHashRange(int w, int h); std::string LookupHashFile(u64 cachekey, u32 hash, bool *foundAlias, bool *ignored); + void ScanForHashNamedFiles(VFSBackend *dir, std::map> &filenameMap); + void ComputeAliasMap(const std::map> &filenameMap); + bool enabled_ = false; bool allowVideo_ = false; bool ignoreAddress_ = false; From 126d70cf9d39844ecb124b37e9ec27695bf319f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 24 Dec 2023 22:00:37 +0100 Subject: [PATCH 2/4] Improve handling of texture packs without .ini files (not recommended, but exists) --- GPU/Common/TextureReplacer.cpp | 10 ++++++++++ GPU/Common/TextureReplacer.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index 678b3672e57a..7f2f7f601723 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -177,6 +177,16 @@ bool TextureReplacer::LoadIni() { return false; } else { WARN_LOG(G3D, "Texture pack lacking ini file: %s", basePath_.c_str()); + // Do what we can do anyway: Scan for textures and build the map. + std::map> filenameMap; + ScanForHashNamedFiles(dir, filenameMap); + ComputeAliasMap(filenameMap); + // Set some defaults. + allowVideo_ = false; + ignoreAddress_ = false; + reduceHash_ = false; + ignoreMipmap_ = false; + // TODO: others? } } diff --git a/GPU/Common/TextureReplacer.h b/GPU/Common/TextureReplacer.h index c1a0362af3e1..7cc192319fcb 100644 --- a/GPU/Common/TextureReplacer.h +++ b/GPU/Common/TextureReplacer.h @@ -18,9 +18,11 @@ #pragma once #include "ppsspp_config.h" + #include #include #include +#include #include #include "Common/CommonFuncs.h" From c2963c7941e46f4529d385dc1682bc5d25c0348e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 24 Dec 2023 22:18:54 +0100 Subject: [PATCH 3/4] Fix issue where "Texture replacement pack activated" wasn't displayed without an ini --- GPU/Common/TextureReplacer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index 7f2f7f601723..49549d243c0d 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -190,6 +190,9 @@ bool TextureReplacer::LoadIni() { } } + auto gr = GetI18NCategory(I18NCat::GRAPHICS); + g_OSD.Show(OSDType::MESSAGE_SUCCESS, gr->T("Texture replacement pack activated"), 2.0f); + vfs_ = dir; // If we have stuff loaded from before, need to update the vfs pointers to avoid @@ -380,9 +383,6 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, VFSBackend *dir, bool isOverri } } - auto gr = GetI18NCategory(I18NCat::GRAPHICS); - - g_OSD.Show(OSDType::MESSAGE_SUCCESS, gr->T("Texture replacement pack activated"), 2.0f); return true; } From faef4aae2c793dbea482f03bb08ec636d5081e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 24 Dec 2023 22:26:55 +0100 Subject: [PATCH 4/4] Add a comment on how to fix the actual bug. --- GPU/Common/TextureReplacer.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index 49549d243c0d..a17789b54235 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -181,12 +181,6 @@ bool TextureReplacer::LoadIni() { std::map> filenameMap; ScanForHashNamedFiles(dir, filenameMap); ComputeAliasMap(filenameMap); - // Set some defaults. - allowVideo_ = false; - ignoreAddress_ = false; - reduceHash_ = false; - ignoreMipmap_ = false; - // TODO: others? } } @@ -599,6 +593,7 @@ ReplacedTexture *TextureReplacer::FindReplacement(u64 cachekey, u32 hash, int w, } desc.logId = desc.filenames[0]; desc.hashfiles = desc.filenames[0]; // The generated filename of the top level is used as the key in the data cache. + // TODO: here `hashfiles` is set to an empty string, breaking stuff below. } else { desc.logId = hashfiles; SplitString(hashfiles, '|', desc.filenames);