AchievementManager: APPROVED_LIST_HASH quality of life improvements

APPROVED_LIST_HASH is moved to a separate file, making tests compilation faster after changing it.
The error message prints the hash in a way that it can be directly copy-pasted (though it still needs clang-format).
This commit is contained in:
Martino Fontana 2026-01-23 10:05:19 +01:00
parent 981b7df420
commit a108fb849f
8 changed files with 39 additions and 29 deletions

View File

@ -7,6 +7,7 @@
#include <array>
#include <memory>
#include <fmt/ranges.h>
#include <mbedtls/sha1.h>
#include "Common/Assert.h"
@ -390,17 +391,11 @@ Digest CalculateDigest(const u8* msg, size_t len)
std::string DigestToString(const Digest& digest)
{
static constexpr std::array<char, 16> lookup = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
std::string hash;
hash.reserve(digest.size() * 2);
for (size_t i = 0; i < digest.size(); ++i)
{
const u8 upper = static_cast<u8>((digest[i] >> 4) & 0xf);
const u8 lower = static_cast<u8>(digest[i] & 0xf);
hash.push_back(lookup[upper]);
hash.push_back(lookup[lower]);
}
return hash;
return fmt::format("{:02X}", fmt::join(digest, ""));
}
std::string DigestToSource(const Digest& digest)
{
return fmt::format("{{0x{:02X}}}", fmt::join(digest, ", 0x"));
}
} // namespace Common::SHA1

View File

@ -58,4 +58,5 @@ inline Digest CalculateDigest(const std::array<T, Size>& msg)
}
std::string DigestToString(const Digest& digest);
std::string DigestToSource(const Digest& digest);
} // namespace Common::SHA1

View File

@ -0,0 +1,13 @@
// Copyright 2026 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Common/Crypto/SHA1.h"
static constexpr std::string_view ACHIEVEMENT_APPROVED_LIST_FILENAME = "ApprovedInis.json";
// After building tests, find the new hash with:
// ./Binaries/Tests/tests --gtest_filter=PatchAllowlist.VerifyHashes
static const inline Common::SHA1::Digest ACHIEVEMENT_APPROVED_LIST_HASH = {
0xEA, 0x2F, 0x74, 0xA1, 0x6C, 0xF3, 0xB5, 0xD4, 0x8A, 0xAF,
0x03, 0x30, 0x58, 0x2A, 0xE0, 0xF7, 0x0A, 0x88, 0x86, 0xB3};

View File

@ -21,6 +21,7 @@
#include "Common/StringUtil.h"
#include "Common/Version.h"
#include "Common/WorkQueueThread.h"
#include "Core/AchievementApprovedHash.h"
#include "Core/ActionReplay.h"
#include "Core/Config/AchievementSettings.h"
#include "Core/Config/FreeLookSettings.h"
@ -100,23 +101,24 @@ picojson::value AchievementManager::LoadApprovedList()
{
picojson::value temp;
std::string error;
if (!JsonFromFile(fmt::format("{}{}{}", File::GetSysDirectory(), DIR_SEP, APPROVED_LIST_FILENAME),
if (!JsonFromFile(fmt::format("{}{}{}", File::GetSysDirectory(), DIR_SEP,
ACHIEVEMENT_APPROVED_LIST_FILENAME),
&temp, &error))
{
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to load approved game settings list {}",
APPROVED_LIST_FILENAME);
ACHIEVEMENT_APPROVED_LIST_FILENAME);
WARN_LOG_FMT(ACHIEVEMENTS, "Error: {}", error);
return {};
}
auto context = Common::SHA1::CreateContext();
context->Update(temp.serialize());
auto digest = context->Finish();
if (digest != APPROVED_LIST_HASH)
if (digest != ACHIEVEMENT_APPROVED_LIST_HASH)
{
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to verify approved game settings list {}",
APPROVED_LIST_FILENAME);
ACHIEVEMENT_APPROVED_LIST_FILENAME);
WARN_LOG_FMT(ACHIEVEMENTS, "Expected hash {}, found hash {}",
Common::SHA1::DigestToString(APPROVED_LIST_HASH),
Common::SHA1::DigestToString(ACHIEVEMENT_APPROVED_LIST_HASH),
Common::SHA1::DigestToString(digest));
return {};
}

View File

@ -81,10 +81,6 @@ public:
static constexpr std::string_view GRAY = "transparent";
static constexpr std::string_view GOLD = "#FFD700";
static constexpr std::string_view BLUE = "#0B71C1";
static constexpr std::string_view APPROVED_LIST_FILENAME = "ApprovedInis.json";
static const inline Common::SHA1::Digest APPROVED_LIST_HASH = {
0xEA, 0x2F, 0x74, 0xA1, 0x6C, 0xF3, 0xB5, 0xD4, 0x8A, 0xAF,
0x03, 0x30, 0x58, 0x2A, 0xE0, 0xF7, 0x0A, 0x88, 0x86, 0xB3};
struct LeaderboardEntry
{

View File

@ -1,4 +1,5 @@
add_library(core
AchievementApprovedHash.h
AchievementManager.cpp
AchievementManager.h
ActionReplay.cpp

View File

@ -183,6 +183,7 @@
<ClInclude Include="Common\WindowsRegistry.h" />
<ClInclude Include="Common\WindowSystemInfo.h" />
<ClInclude Include="Common\WorkQueueThread.h" />
<ClInclude Include="Core\AchievementApprovedHash.h" />
<ClInclude Include="Core\AchievementManager.h" />
<ClInclude Include="Core\ActionReplay.h" />
<ClInclude Include="Core\ARDecrypt.h" />

View File

@ -18,7 +18,7 @@
#include "Common/FileUtil.h"
#include "Common/IniFile.h"
#include "Common/JsonUtil.h"
#include "Core/AchievementManager.h"
#include "Core/AchievementApprovedHash.h"
#include "Core/ActionReplay.h"
#include "Core/GeckoCode.h"
#include "Core/GeckoCodeConfig.h"
@ -128,23 +128,24 @@ TEST(PatchAllowlist, VerifyHashes)
auto context = Common::SHA1::CreateContext();
context->Update(new_allowlist_str);
auto digest = context->Finish();
if (digest != AchievementManager::APPROVED_LIST_HASH)
if (digest != ACHIEVEMENT_APPROVED_LIST_HASH)
{
ADD_FAILURE() << "Approved list hash does not match the one in AchievementMananger."
ADD_FAILURE() << "Approved list hash does not match the one in AchievementApprovedHash.h."
<< std::endl
<< "Please update APPROVED_LIST_HASH to the following:" << std::endl
<< Common::SHA1::DigestToString(digest);
<< Common::SHA1::DigestToSource(digest);
}
// Compare with old allowlist
static constexpr std::string_view APPROVED_LIST_FILENAME = "ApprovedInis.json";
std::string old_allowlist;
std::string error;
const auto& list_filepath = fmt::format("{}{}{}", sys_directory, DIR_SEP, APPROVED_LIST_FILENAME);
const auto& list_filepath =
fmt::format("{}{}{}", sys_directory, DIR_SEP, ACHIEVEMENT_APPROVED_LIST_FILENAME);
if (!File::ReadFileToString(list_filepath, old_allowlist) || old_allowlist != new_allowlist_str)
{
static constexpr std::string_view NEW_APPROVED_LIST_FILENAME = "New-ApprovedInis.json";
static constexpr std::string_view NEW_ACHIEVEMENT_APPROVED_LIST_FILENAME =
"New-ApprovedInis.json";
const auto& new_list_filepath =
fmt::format("{}{}{}", sys_directory, DIR_SEP, NEW_APPROVED_LIST_FILENAME);
fmt::format("{}{}{}", sys_directory, DIR_SEP, NEW_ACHIEVEMENT_APPROVED_LIST_FILENAME);
if (!JsonToFile(new_list_filepath, picojson::value(new_allowlist), false))
{
ADD_FAILURE() << "Failed to write new approved list to " << list_filepath;