Merge branch 'master' of https://github.com/dolphin-emu/dolphin into dolphin-emu-maste2r

This commit is contained in:
Nayla Hanegan 2024-12-24 20:47:03 -05:00
commit 75a9451d0b
218 changed files with 53095 additions and 44354 deletions

View file

@ -11,15 +11,15 @@ namespace
{
// The encrypted bytes corresponding to the following settings, in order:
// "key" = "val"
Common::SettingsHandler::Buffer BUFFER_A{0x91, 0x91, 0x90, 0xEE, 0xD1, 0x2F, 0xF0, 0x34, 0x79};
Common::SettingsBuffer BUFFER_A{0x91, 0x91, 0x90, 0xEE, 0xD1, 0x2F, 0xF0, 0x34, 0x79};
// The encrypted bytes corresponding to the following settings, in order:
// "key1" = "val1"
// "key2" = "val2"
// "foo" = "bar"
Common::SettingsHandler::Buffer BUFFER_B{
0x91, 0x91, 0x90, 0xE2, 0x9A, 0x38, 0xFD, 0x55, 0x42, 0xEA, 0xC4, 0xF6, 0x5E, 0xF, 0xDF, 0xE7,
0xC3, 0x0A, 0xBB, 0x9C, 0x50, 0xB1, 0x10, 0x82, 0xB4, 0x8A, 0x0D, 0xBE, 0xCD, 0x72, 0xF4};
Common::SettingsBuffer BUFFER_B{0x91, 0x91, 0x90, 0xE2, 0x9A, 0x38, 0xFD, 0x55, 0x42, 0xEA, 0xC4,
0xF6, 0x5E, 0xF, 0xDF, 0xE7, 0xC3, 0x0A, 0xBB, 0x9C, 0x50, 0xB1,
0x10, 0x82, 0xB4, 0x8A, 0x0D, 0xBE, 0xCD, 0x72, 0xF4};
// The encrypted bytes corresponding to the following setting:
// "\xFA" = "a"
@ -27,7 +27,7 @@ Common::SettingsHandler::Buffer BUFFER_B{
// This setting triggers the edge case fixed in PR #8704: the key's first and only byte matches the
// first byte of the initial encryption key, resulting in a null encoded byte on the first attempt
// to encode the line.
Common::SettingsHandler::Buffer BUFFER_C{0xF0, 0x0E, 0xD4, 0xB2, 0xAA, 0x44};
Common::SettingsBuffer BUFFER_C{0xF0, 0x0E, 0xD4, 0xB2, 0xAA, 0x44};
// The encrypted bytes corresponding to the following setting:
// "\xFA\xE9" = "a"
@ -40,76 +40,69 @@ Common::SettingsHandler::Buffer BUFFER_C{0xF0, 0x0E, 0xD4, 0xB2, 0xAA, 0x44};
// 2. The key's second byte matches the first byte of the encryption key after two
// rotations, resulting in a null encoded byte on the second attempt to encode the line (with a
// single LF inserted before the line).
Common::SettingsHandler::Buffer BUFFER_D{0xF0, 0xFE, 0x13, 0x3A, 0x9A, 0x2F, 0x91, 0x33};
Common::SettingsBuffer BUFFER_D{0xF0, 0xFE, 0x13, 0x3A, 0x9A, 0x2F, 0x91, 0x33};
} // namespace
TEST(SettingsHandlerTest, EncryptSingleSetting)
TEST(SettingsWriterTest, EncryptSingleSetting)
{
Common::SettingsHandler handler;
handler.AddSetting("key", "val");
Common::SettingsHandler::Buffer buffer = handler.GetBytes();
Common::SettingsWriter writer;
writer.AddSetting("key", "val");
Common::SettingsBuffer buffer = writer.GetBytes();
EXPECT_TRUE(std::ranges::equal(buffer, BUFFER_A));
}
TEST(SettingsHandlerTest, DecryptSingleSetting)
TEST(SettingsReaderTest, DecryptSingleSetting)
{
Common::SettingsHandler handler(BUFFER_A);
EXPECT_EQ(handler.GetValue("key"), "val");
const Common::SettingsReader reader(BUFFER_A);
EXPECT_EQ(reader.GetValue("key"), "val");
}
TEST(SettingsHandlerTest, EncryptMultipleSettings)
TEST(SettingsWriterTest, EncryptMultipleSettings)
{
Common::SettingsHandler handler;
handler.AddSetting("key1", "val1");
handler.AddSetting("key2", "val2");
handler.AddSetting("foo", "bar");
Common::SettingsHandler::Buffer buffer = handler.GetBytes();
Common::SettingsWriter writer;
writer.AddSetting("key1", "val1");
writer.AddSetting("key2", "val2");
writer.AddSetting("foo", "bar");
Common::SettingsBuffer buffer = writer.GetBytes();
EXPECT_TRUE(std::ranges::equal(buffer, BUFFER_B));
}
TEST(SettingsHandlerTest, DecryptMultipleSettings)
TEST(SettingsReaderTest, DecryptMultipleSettings)
{
Common::SettingsHandler handler(BUFFER_B);
EXPECT_EQ(handler.GetValue("key1"), "val1");
EXPECT_EQ(handler.GetValue("key2"), "val2");
EXPECT_EQ(handler.GetValue("foo"), "bar");
const Common::SettingsReader reader(BUFFER_B);
EXPECT_EQ(reader.GetValue("key1"), "val1");
EXPECT_EQ(reader.GetValue("key2"), "val2");
EXPECT_EQ(reader.GetValue("foo"), "bar");
}
TEST(SettingsHandlerTest, GetValueOnSameInstance)
TEST(SettingsWriterTest, EncryptAddsLFOnNullChar)
{
Common::SettingsHandler handler;
handler.AddSetting("key", "val");
EXPECT_EQ(handler.GetValue("key"), "");
}
TEST(SettingsHandlerTest, EncryptAddsLFOnNullChar)
{
Common::SettingsHandler handler;
handler.AddSetting("\xFA", "a");
Common::SettingsHandler::Buffer buffer = handler.GetBytes();
Common::SettingsWriter writer;
writer.AddSetting("\xFA", "a");
Common::SettingsBuffer buffer = writer.GetBytes();
EXPECT_TRUE(std::ranges::equal(buffer, BUFFER_C));
}
TEST(SettingsHandlerTest, EncryptAddsLFOnNullCharTwice)
TEST(SettingsWriterTest, EncryptAddsLFOnNullCharTwice)
{
Common::SettingsHandler handler;
handler.AddSetting("\xFA\xE9", "a");
Common::SettingsHandler::Buffer buffer = handler.GetBytes();
Common::SettingsWriter writer;
writer.AddSetting("\xFA\xE9", "a");
Common::SettingsBuffer buffer = writer.GetBytes();
EXPECT_TRUE(std::ranges::equal(buffer, BUFFER_D));
}
TEST(SettingsHandlerTest, DecryptSingleAddedLF)
TEST(SettingsReaderTest, DecryptSingleAddedLF)
{
Common::SettingsHandler handler(BUFFER_C);
EXPECT_EQ(handler.GetValue("\xFA"), "a");
const Common::SettingsReader reader(BUFFER_C);
EXPECT_EQ(reader.GetValue("\xFA"), "a");
}
TEST(SettingsHandlerTest, DecryptTwoAddedLFs)
TEST(SettingsReaderTest, DecryptTwoAddedLFs)
{
Common::SettingsHandler handler(BUFFER_D);
EXPECT_EQ(handler.GetValue("\xFA\xE9"), "a");
const Common::SettingsReader reader(BUFFER_D);
EXPECT_EQ(reader.GetValue("\xFA\xE9"), "a");
}

View file

@ -18,7 +18,10 @@
#include "Common/IOFile.h"
#include "Common/IniFile.h"
#include "Common/JsonUtil.h"
#include "Core/ActionReplay.h"
#include "Core/CheatCodes.h"
#include "Core/GeckoCode.h"
#include "Core/GeckoCodeConfig.h"
#include "Core/PatchEngine.h"
struct GameHashes
@ -27,6 +30,11 @@ struct GameHashes
std::map<std::string /*hash*/, std::string /*patch name*/> hashes;
};
using AllowList = std::map<std::string /*ID*/, GameHashes>;
void CheckHash(const std::string& game_id, GameHashes* game_hashes, const std::string& hash,
const std::string& patch_name);
TEST(PatchAllowlist, VerifyHashes)
{
// Load allowlist
@ -42,9 +50,9 @@ TEST(PatchAllowlist, VerifyHashes)
const auto& list_filepath = fmt::format("{}{}{}", sys_directory, DIR_SEP, APPROVED_LIST_FILENAME);
ASSERT_TRUE(JsonFromFile(list_filepath, &json_tree, &error))
<< "Failed to open file at " << list_filepath;
// Parse allowlist - Map<game id, Map<hash, name>
// Parse allowlist - Map<game id, Map<hash, name>>
ASSERT_TRUE(json_tree.is<picojson::object>());
std::map<std::string /*ID*/, GameHashes> allow_list;
AllowList allow_list;
for (const auto& entry : json_tree.get<picojson::object>())
{
ASSERT_TRUE(entry.second.is<picojson::object>());
@ -69,12 +77,25 @@ TEST(PatchAllowlist, VerifyHashes)
std::string game_id = file.virtualName.substr(0, file.virtualName.find_first_of('.'));
std::vector<PatchEngine::Patch> patches;
PatchEngine::LoadPatchSection("OnFrame", &patches, ini_file, Common::IniFile());
std::vector<Gecko::GeckoCode> geckos = Gecko::LoadCodes(Common::IniFile(), ini_file);
std::vector<ActionReplay::ARCode> action_replays =
ActionReplay::LoadCodes(Common::IniFile(), ini_file);
// Filter patches for RetroAchievements approved
ReadEnabledOrDisabled<PatchEngine::Patch>(ini_file, "OnFrame", false, &patches);
for (auto& patch : patches)
patch.enabled = false;
ReadEnabledOrDisabled<PatchEngine::Patch>(ini_file, "Patches_RetroAchievements_Verified", true,
&patches);
for (auto& code : geckos)
code.enabled = false;
ReadEnabledOrDisabled<Gecko::GeckoCode>(ini_file, "Gecko_RetroAchievements_Verified", true,
&geckos);
for (auto& code : action_replays)
code.enabled = false;
ReadEnabledOrDisabled<ActionReplay::ARCode>(ini_file, "AR_RetroAchievements_Verified", true,
&action_replays);
// Get game section from allow list
auto game_itr = allow_list.find(game_id);
bool itr_end = (game_itr == allow_list.end());
// Iterate over approved patches
for (const auto& patch : patches)
{
@ -92,38 +113,51 @@ TEST(PatchAllowlist, VerifyHashes)
context->Update(Common::BitCastToArray<u8>(entry.conditional));
}
auto digest = context->Finish();
std::string hash = Common::SHA1::DigestToString(digest);
// Check patch in list
if (game_itr == allow_list.end())
{
// Report: no patches in game found in list
ADD_FAILURE() << "Approved hash missing from list." << std::endl
<< "Game ID: " << game_id << std::endl
<< "Patch: \"" << hash << "\" : \"" << patch.name << "\"";
CheckHash(game_id, itr_end ? nullptr : &game_itr->second,
Common::SHA1::DigestToString(digest), patch.name);
}
// Iterate over approved geckos
for (const auto& code : geckos)
{
if (!code.enabled)
continue;
}
auto hash_itr = game_itr->second.hashes.find(hash);
if (hash_itr == game_itr->second.hashes.end())
// Hash patch
auto context = Common::SHA1::CreateContext();
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(code.codes.size())));
for (const auto& entry : code.codes)
{
// Report: patch not found in list
ADD_FAILURE() << "Approved hash missing from list." << std::endl
<< "Game ID: " << game_id << ":" << game_itr->second.game_title << std::endl
<< "Patch: \"" << hash << "\" : \"" << patch.name << "\"";
context->Update(Common::BitCastToArray<u8>(entry.address));
context->Update(Common::BitCastToArray<u8>(entry.data));
}
else
auto digest = context->Finish();
CheckHash(game_id, itr_end ? nullptr : &game_itr->second,
Common::SHA1::DigestToString(digest), code.name);
}
// Iterate over approved AR codes
for (const auto& code : action_replays)
{
if (!code.enabled)
continue;
// Hash patch
auto context = Common::SHA1::CreateContext();
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(code.ops.size())));
for (const auto& entry : code.ops)
{
// Remove patch from map if found
game_itr->second.hashes.erase(hash_itr);
context->Update(Common::BitCastToArray<u8>(entry.cmd_addr));
context->Update(Common::BitCastToArray<u8>(entry.value));
}
auto digest = context->Finish();
CheckHash(game_id, itr_end ? nullptr : &game_itr->second,
Common::SHA1::DigestToString(digest), code.name);
}
// Report missing patches in map
if (game_itr == allow_list.end())
if (itr_end)
continue;
for (auto& remaining_hashes : game_itr->second.hashes)
{
ADD_FAILURE() << "Hash in list not approved in ini." << std::endl
<< "Game ID: " << game_id << ":" << game_itr->second.game_title << std::endl
<< "Patch: " << remaining_hashes.second << ":" << remaining_hashes.first;
<< "Code: " << remaining_hashes.second << ":" << remaining_hashes.first;
}
// Remove section from map
allow_list.erase(game_itr);
@ -136,3 +170,30 @@ TEST(PatchAllowlist, VerifyHashes)
<< remaining_games.second.game_title;
}
}
void CheckHash(const std::string& game_id, GameHashes* game_hashes, const std::string& hash,
const std::string& patch_name)
{
// Check patch in list
if (game_hashes == nullptr)
{
// Report: no patches in game found in list
ADD_FAILURE() << "Approved hash missing from list." << std::endl
<< "Game ID: " << game_id << std::endl
<< "Code: \"" << hash << "\": \"" << patch_name << "\"";
return;
}
auto hash_itr = game_hashes->hashes.find(hash);
if (hash_itr == game_hashes->hashes.end())
{
// Report: patch not found in list
ADD_FAILURE() << "Approved hash missing from list." << std::endl
<< "Game ID: " << game_id << ":" << game_hashes->game_title << std::endl
<< "Code: \"" << hash << "\": \"" << patch_name << "\"";
}
else
{
// Remove patch from map if found
game_hashes->hashes.erase(hash_itr);
}
}

View file

@ -16,6 +16,9 @@ std::vector<std::string> Host_GetPreferredLocales()
void Host_PPCSymbolsChanged()
{
}
void Host_PPCBreakpointsChanged()
{
}
void Host_RefreshDSPDebuggerWindow()
{
}
@ -41,7 +44,7 @@ bool Host_UpdateDiscordPresenceRaw(const std::string& details, const std::string
void Host_UpdateDisasmDialog()
{
}
void Host_JitCacheCleared()
void Host_JitCacheInvalidation()
{
}
void Host_JitProfileDataWiped()