mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #18610 from hrydgard/texture-replacer-no-ini-fix
Texture replacer: Fix for texture directories missing an ini file
This commit is contained in:
commit
a9a670fb98
2 changed files with 71 additions and 52 deletions
|
@ -177,9 +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<ReplacementCacheKey, std::map<int, std::string>> filenameMap;
|
||||
ScanForHashNamedFiles(dir, filenameMap);
|
||||
ComputeAliasMap(filenameMap);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -198,6 +205,60 @@ bool TextureReplacer::LoadIni() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void TextureReplacer::ScanForHashNamedFiles(VFSBackend *dir, std::map<ReplacementCacheKey, std::map<int, std::string>> &filenameMap) {
|
||||
// Scan the root of the texture folder/zip and preinitialize the hash map.
|
||||
std::vector<File::FileInfo> 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<ReplacementCacheKey, std::map<int, std::string>> &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 +297,13 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, VFSBackend *dir, bool isOverri
|
|||
int badFileNameCount = 0;
|
||||
|
||||
std::map<ReplacementCacheKey, std::map<int, std::string>> filenameMap;
|
||||
std::string badFilenames;
|
||||
|
||||
// Scan the root of the texture folder/zip and preinitialize the hash map.
|
||||
std::vector<File::FileInfo> 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 +345,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);
|
||||
|
@ -361,9 +377,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;
|
||||
}
|
||||
|
||||
|
@ -580,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);
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonFuncs.h"
|
||||
|
@ -135,6 +137,9 @@ protected:
|
|||
float LookupReduceHashRange(int w, int h);
|
||||
std::string LookupHashFile(u64 cachekey, u32 hash, bool *foundAlias, bool *ignored);
|
||||
|
||||
void ScanForHashNamedFiles(VFSBackend *dir, std::map<ReplacementCacheKey, std::map<int, std::string>> &filenameMap);
|
||||
void ComputeAliasMap(const std::map<ReplacementCacheKey, std::map<int, std::string>> &filenameMap);
|
||||
|
||||
bool enabled_ = false;
|
||||
bool allowVideo_ = false;
|
||||
bool ignoreAddress_ = false;
|
||||
|
|
Loading…
Add table
Reference in a new issue