mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-09-02 15:45:58 +00:00
Merge branch 'master' of https://github.com/dolphin-emu/dolphin into dolphin-emu-maste2r
This commit is contained in:
commit
75a9451d0b
218 changed files with 53095 additions and 44354 deletions
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue