diff --git a/Source/Core/UICommon/GameFile.cpp b/Source/Core/UICommon/GameFile.cpp index 3417051a54..c575d4018b 100644 --- a/Source/Core/UICommon/GameFile.cpp +++ b/Source/Core/UICommon/GameFile.cpp @@ -22,6 +22,7 @@ #include #include +#include "Common/BitUtils.h" #include "Common/ChunkFile.h" #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" @@ -624,15 +625,75 @@ std::string GameFile::GetNetPlayName(const Core::TitleDatabase& title_database) return name + " (" + ss.str() + ")"; } +static std::array GetHash(u32 value) +{ + auto data = Common::BitCastToArray(value); + std::array hash; + mbedtls_sha1_ret(reinterpret_cast(data.data()), data.size(), hash.data()); + return hash; +} + +static std::array GetHash(std::string_view str) +{ + std::array hash; + mbedtls_sha1_ret(reinterpret_cast(str.data()), str.size(), hash.data()); + return hash; +} + +static std::optional> GetFileHash(const std::string& path) +{ + std::string buffer; + if (!File::ReadFileToString(path, buffer)) + return std::nullopt; + return GetHash(buffer); +} + +static std::optional> MixHash(const std::optional>& lhs, + const std::optional>& rhs) +{ + if (!lhs && !rhs) + return std::nullopt; + if (!lhs || !rhs) + return !rhs ? lhs : rhs; + std::array result; + for (size_t i = 0; i < result.size(); ++i) + result[i] = (*lhs)[i] ^ (*rhs)[(i + 1) % result.size()]; + return result; +} + std::array GameFile::GetSyncHash() const { - std::array hash{}; + std::optional> hash; if (m_platform == DiscIO::Platform::ELFOrDOL) { - std::string buffer; - if (File::ReadFileToString(m_file_path, buffer)) - mbedtls_sha1_ret(reinterpret_cast(buffer.data()), buffer.size(), hash.data()); + hash = GetFileHash(m_file_path); + } + else if (m_blob_type == DiscIO::BlobType::MOD_DESCRIPTOR) + { + auto descriptor = DiscIO::ParseGameModDescriptorFile(m_file_path); + if (descriptor) + { + GameFile proxy(descriptor->base_file); + if (proxy.IsValid()) + hash = proxy.GetSyncHash(); + + // add patches to hash if they're enabled + if (descriptor->riivolution) + { + for (const auto& patch : descriptor->riivolution->patches) + { + hash = MixHash(hash, GetFileHash(patch.xml)); + for (const auto& option : patch.options) + { + hash = MixHash(hash, GetHash(option.section_name)); + hash = MixHash(hash, GetHash(option.option_id)); + hash = MixHash(hash, GetHash(option.option_name)); + hash = MixHash(hash, GetHash(option.choice)); + } + } + } + } } else { @@ -640,7 +701,7 @@ std::array GameFile::GetSyncHash() const hash = volume->GetSyncHash(); } - return hash; + return hash.value_or(std::array{}); } NetPlay::SyncIdentifier GameFile::GetSyncIdentifier() const