diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 2889d9b9d7..f1957c8c2e 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -35,7 +35,7 @@ static const std::string NEW_TEXTURE_DIR = "new/"; static const int VERSION = 1; static const int MAX_MIP_LEVELS = 64; -TextureReplacer::TextureReplacer() : enabled_(false), allowVideo_(false), hash_(ReplacedTextureHash::QUICK) { +TextureReplacer::TextureReplacer() : enabled_(false), allowVideo_(false), ignoreAddress_(false), hash_(ReplacedTextureHash::QUICK) { none_.alphaStatus_ = ReplacedTextureAlpha::UNKNOWN; } @@ -85,12 +85,27 @@ bool TextureReplacer::LoadIni() { // 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()); 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."); + } + + 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) { @@ -174,20 +189,27 @@ u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureForm } const u8 *checkp = Memory::GetPointer(addr); + float reduceHashSize = 1.0; + if (reduceHash_) + reduceHashSize = 0.5; if (bufw <= w) { // We can assume the data is contiguous. These are the total used pixels. const u32 totalPixels = bufw * h + (w - bufw); - const u32 sizeInRAM = (textureBitsPerPixel[fmt] * totalPixels) / 8; + const u32 sizeInRAM = (textureBitsPerPixel[fmt] * totalPixels) / 8 * reduceHashSize; switch (hash_) { case ReplacedTextureHash::QUICK: return StableQuickTexHash(checkp, sizeInRAM); + case ReplacedTextureHash::XXH32: + return DoReliableHash32(checkp, sizeInRAM, 0xBACD7814); + case ReplacedTextureHash::XXH64: + return DoReliableHash64(checkp, sizeInRAM, 0xBACD7814); default: return 0; } } else { // We have gaps. Let's hash each row and sum. - const u32 bytesPerLine = (textureBitsPerPixel[fmt] * w) / 8; + const u32 bytesPerLine = (textureBitsPerPixel[fmt] * w) / 8 * reduceHashSize; const u32 stride = (textureBitsPerPixel[fmt] * bufw) / 8; u32 result = 0; @@ -200,6 +222,22 @@ u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureForm } break; + case ReplacedTextureHash::XXH32: + for (int y = 0; y < h; ++y) { + u32 rowHash = DoReliableHash32(checkp, bytesPerLine, 0xBACD7814); + result = (result * 11) ^ rowHash; + checkp += stride; + } + break; + + case ReplacedTextureHash::XXH64: + for (int y = 0; y < h; ++y) { + u32 rowHash = DoReliableHash64(checkp, bytesPerLine, 0xBACD7814); + result = (result * 11) ^ rowHash; + checkp += stride; + } + break; + default: break; } @@ -232,6 +270,10 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey, int newH = h; LookupHashRange(cachekey >> 32, newW, newH); + if (ignoreAddress_) { + cachekey = cachekey & 0xFFFFFFFFULL; + } + for (int i = 0; i < MAX_MIP_LEVELS; ++i) { const std::string hashfile = LookupHashFile(cachekey, hash, i); const std::string filename = basePath_ + hashfile; @@ -304,8 +346,12 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl if (replacedInfo.isVideo && !allowVideo_) { return; } + u64 cachekey = replacedInfo.cachekey; + if (ignoreAddress_) { + cachekey = cachekey & 0xFFFFFFFFULL; + } - std::string hashfile = LookupHashFile(replacedInfo.cachekey, replacedInfo.hash, level); + std::string hashfile = LookupHashFile(cachekey, replacedInfo.hash, level); const std::string filename = basePath_ + hashfile; const std::string saveFilename = basePath_ + NEW_TEXTURE_DIR + hashfile; @@ -315,7 +361,7 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl return; } - ReplacementCacheKey replacementKey(replacedInfo.cachekey, replacedInfo.hash); + ReplacementCacheKey replacementKey(cachekey, replacedInfo.hash); auto it = savedCache_.find(replacementKey); if (it != savedCache_.end() && File::Exists(saveFilename)) { // We've already saved this texture. Let's only save if it's bigger (e.g. scaled now.) @@ -419,7 +465,7 @@ std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, int level) { key.hash = 0; alias = aliases_.find(key); - if (alias == aliases_.end()) { + if (!ignoreAddress_ && alias == aliases_.end()) { // No data hash. key.cachekey = cachekey; key.hash = 0; @@ -433,7 +479,7 @@ std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, int level) { alias = aliases_.find(key); } - if (alias == aliases_.end()) { + if (!ignoreAddress_ && alias == aliases_.end()) { // Address, but not clut hash (in case of garbage clut data.) key.cachekey = cachekey & ~0xFFFFFFFFULL; key.hash = hash; diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index bbf84def86..4def1506fa 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -50,6 +50,8 @@ enum class ReplacedTextureAlpha { enum class ReplacedTextureHash { // TODO: Maybe only support crc32c for now? QUICK, + XXH32, + XXH64, }; struct ReplacedTextureLevel { @@ -196,6 +198,8 @@ protected: SimpleBuf saveBuf; bool enabled_; bool allowVideo_; + bool ignoreAddress_; + bool reduceHash_; std::string gameID_; std::string basePath_; ReplacedTextureHash hash_;