mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 13:27:45 -07:00
Compare commits
11 Commits
83e9190d74
...
5dfa2d6dcb
Author | SHA1 | Date | |
---|---|---|---|
|
5dfa2d6dcb | ||
|
2c92e5b5b3 | ||
|
036a6cbbb3 | ||
|
f0489c552a | ||
|
d0c1ba2e33 | ||
|
df484d695d | ||
|
b3c5dc5fb9 | ||
|
dc49d8cd6b | ||
|
74d129ee12 | ||
|
c9e0cbec18 | ||
|
fe96bf4108 |
@ -160,6 +160,10 @@
|
|||||||
"title": "Gladius",
|
"title": "Gladius",
|
||||||
"3D0894616C9A7FA5ED91C1D2F461BF14DF47ECEC": "Fix freeze in opening cutscene"
|
"3D0894616C9A7FA5ED91C1D2F461BF14DF47ECEC": "Fix freeze in opening cutscene"
|
||||||
},
|
},
|
||||||
|
"GMSE01": {
|
||||||
|
"title": "Super Mario Sunshine",
|
||||||
|
"BD718F961DBA5372B1D0257D454D535746C453A0": "Widescreen"
|
||||||
|
},
|
||||||
"GNHE5d": {
|
"GNHE5d": {
|
||||||
"title": "NHL HITZ 2002",
|
"title": "NHL HITZ 2002",
|
||||||
"89393A24E2336841AA4CD0AD3BE1C9A66B89E9EF": "Nop Hack"
|
"89393A24E2336841AA4CD0AD3BE1C9A66B89E9EF": "Nop Hack"
|
||||||
|
@ -175,3 +175,6 @@ C2363138 00000009
|
|||||||
7C631670 54A5F0BE
|
7C631670 54A5F0BE
|
||||||
7C630194 7C630214
|
7C630194 7C630214
|
||||||
60000000 00000000
|
60000000 00000000
|
||||||
|
|
||||||
|
[Gecko_RetroAchievements_Verified]
|
||||||
|
$Widescreen
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "SDL2",
|
|
||||||
"buildsystem": "autotools",
|
|
||||||
"config-opts": ["--disable-static"],
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"type": "dir",
|
|
||||||
"path": "../../Externals/SDL/SDL"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cleanup": [ "/bin/sdl2-config",
|
|
||||||
"/include",
|
|
||||||
"/lib/libSDL2.la",
|
|
||||||
"/lib/libSDL2main.a",
|
|
||||||
"/lib/libSDL2main.la",
|
|
||||||
"/lib/libSDL2_test.a",
|
|
||||||
"/lib/libSDL2_test.la",
|
|
||||||
"/lib/cmake",
|
|
||||||
"/share/aclocal",
|
|
||||||
"/lib/pkgconfig"]
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
app-id: org.DolphinEmu.dolphin-emu
|
app-id: org.DolphinEmu.dolphin-emu
|
||||||
runtime: org.kde.Platform
|
runtime: org.kde.Platform
|
||||||
runtime-version: '6.7'
|
runtime-version: '6.8'
|
||||||
sdk: org.kde.Sdk
|
sdk: org.kde.Sdk
|
||||||
command: dolphin-emu-wrapper
|
command: dolphin-emu-wrapper
|
||||||
rename-desktop-file: dolphin-emu.desktop
|
rename-desktop-file: dolphin-emu.desktop
|
||||||
@ -47,9 +47,6 @@ modules:
|
|||||||
url: https://github.com/Unrud/xdg-screensaver-shim/archive/0.0.2.tar.gz
|
url: https://github.com/Unrud/xdg-screensaver-shim/archive/0.0.2.tar.gz
|
||||||
sha256: 0ed2a69fe6ee6cbffd2fe16f85116db737f17fb1e79bfb812d893cf15c728399
|
sha256: 0ed2a69fe6ee6cbffd2fe16f85116db737f17fb1e79bfb812d893cf15c728399
|
||||||
|
|
||||||
# build the vendored SDL2 from Externals until the runtime gets 2.30.6
|
|
||||||
- SDL2/SDL2.json
|
|
||||||
|
|
||||||
- name: dolphin-emu
|
- name: dolphin-emu
|
||||||
buildsystem: cmake-ninja
|
buildsystem: cmake-ninja
|
||||||
config-opts:
|
config-opts:
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "Common/Assert.h"
|
#include "Common/Assert.h"
|
||||||
#include "Common/BitUtils.h"
|
#include "Common/BitUtils.h"
|
||||||
#include "Common/CommonPaths.h"
|
#include "Common/CommonPaths.h"
|
||||||
|
#include "Common/Config/Config.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
#include "Common/IOFile.h"
|
#include "Common/IOFile.h"
|
||||||
#include "Common/Image.h"
|
#include "Common/Image.h"
|
||||||
@ -23,10 +24,12 @@
|
|||||||
#include "Common/ScopeGuard.h"
|
#include "Common/ScopeGuard.h"
|
||||||
#include "Common/Version.h"
|
#include "Common/Version.h"
|
||||||
#include "Common/WorkQueueThread.h"
|
#include "Common/WorkQueueThread.h"
|
||||||
|
#include "Core/ActionReplay.h"
|
||||||
#include "Core/Config/AchievementSettings.h"
|
#include "Core/Config/AchievementSettings.h"
|
||||||
#include "Core/Config/FreeLookSettings.h"
|
#include "Core/Config/FreeLookSettings.h"
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
|
#include "Core/GeckoCode.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/HW/VideoInterface.h"
|
#include "Core/HW/VideoInterface.h"
|
||||||
#include "Core/PatchEngine.h"
|
#include "Core/PatchEngine.h"
|
||||||
@ -64,6 +67,7 @@ void AchievementManager::Init()
|
|||||||
[](const char* message, const rc_client_t* client) {
|
[](const char* message, const rc_client_t* client) {
|
||||||
INFO_LOG_FMT(ACHIEVEMENTS, "{}", message);
|
INFO_LOG_FMT(ACHIEVEMENTS, "{}", message);
|
||||||
});
|
});
|
||||||
|
Config::AddConfigChangedCallback([this] { SetHardcoreMode(); });
|
||||||
SetHardcoreMode();
|
SetHardcoreMode();
|
||||||
m_queue.Reset("AchievementManagerQueue", [](const std::function<void()>& func) { func(); });
|
m_queue.Reset("AchievementManagerQueue", [](const std::function<void()>& func) { func(); });
|
||||||
m_image_queue.Reset("AchievementManagerImageQueue",
|
m_image_queue.Reset("AchievementManagerImageQueue",
|
||||||
@ -368,7 +372,6 @@ void AchievementManager::SetHardcoreMode()
|
|||||||
if (Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f)
|
if (Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f)
|
||||||
Config::SetBaseOrCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
|
Config::SetBaseOrCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
|
||||||
Config::SetBaseOrCurrent(Config::FREE_LOOK_ENABLED, false);
|
Config::SetBaseOrCurrent(Config::FREE_LOOK_ENABLED, false);
|
||||||
Config::SetBaseOrCurrent(Config::MAIN_ENABLE_CHEATS, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,10 +385,11 @@ bool AchievementManager::IsHardcoreModeActive() const
|
|||||||
return rc_client_is_processing_required(m_client);
|
return rc_client_is_processing_required(m_client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
|
template <typename T>
|
||||||
const std::string& game_ini_id) const
|
void AchievementManager::FilterApprovedIni(std::vector<T>& codes,
|
||||||
|
const std::string& game_ini_id) const
|
||||||
{
|
{
|
||||||
if (patches.empty())
|
if (codes.empty())
|
||||||
{
|
{
|
||||||
// There's nothing to verify, so let's save ourselves some work
|
// There's nothing to verify, so let's save ourselves some work
|
||||||
return;
|
return;
|
||||||
@ -396,46 +400,120 @@ void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>&
|
|||||||
if (!IsHardcoreModeActive())
|
if (!IsHardcoreModeActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Approved codes list failed to hash
|
||||||
|
if (!m_ini_root->is<picojson::value::object>())
|
||||||
|
{
|
||||||
|
codes.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& code : codes)
|
||||||
|
{
|
||||||
|
if (code.enabled && !CheckApprovedCode(code, game_ini_id))
|
||||||
|
code.enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool AchievementManager::CheckApprovedCode(const T& code, const std::string& game_ini_id) const
|
||||||
|
{
|
||||||
|
if (!IsHardcoreModeActive())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Approved codes list failed to hash
|
||||||
|
if (!m_ini_root->is<picojson::value::object>())
|
||||||
|
return false;
|
||||||
|
|
||||||
const bool known_id = m_ini_root->contains(game_ini_id);
|
const bool known_id = m_ini_root->contains(game_ini_id);
|
||||||
|
|
||||||
auto patch_itr = patches.begin();
|
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying code {}", code.name);
|
||||||
while (patch_itr != patches.end())
|
|
||||||
|
bool verified = false;
|
||||||
|
|
||||||
|
if (known_id)
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying patch {}", patch_itr->name);
|
auto digest = GetCodeHash(code);
|
||||||
|
|
||||||
bool verified = false;
|
verified = m_ini_root->get(game_ini_id).contains(Common::SHA1::DigestToString(digest));
|
||||||
|
|
||||||
if (known_id)
|
|
||||||
{
|
|
||||||
auto context = Common::SHA1::CreateContext();
|
|
||||||
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(patch_itr->entries.size())));
|
|
||||||
for (const auto& entry : patch_itr->entries)
|
|
||||||
{
|
|
||||||
context->Update(Common::BitCastToArray<u8>(entry.type));
|
|
||||||
context->Update(Common::BitCastToArray<u8>(entry.address));
|
|
||||||
context->Update(Common::BitCastToArray<u8>(entry.value));
|
|
||||||
context->Update(Common::BitCastToArray<u8>(entry.comparand));
|
|
||||||
context->Update(Common::BitCastToArray<u8>(entry.conditional));
|
|
||||||
}
|
|
||||||
auto digest = context->Finish();
|
|
||||||
|
|
||||||
verified = m_ini_root->get(game_ini_id).contains(Common::SHA1::DigestToString(digest));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!verified)
|
|
||||||
{
|
|
||||||
patch_itr = patches.erase(patch_itr);
|
|
||||||
OSD::AddMessage(
|
|
||||||
fmt::format("Failed to verify patch {} from file {}.", patch_itr->name, game_ini_id),
|
|
||||||
OSD::Duration::VERY_LONG, OSD::Color::RED);
|
|
||||||
OSD::AddMessage("Disable hardcore mode to enable this patch.", OSD::Duration::VERY_LONG,
|
|
||||||
OSD::Color::RED);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
patch_itr++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!verified)
|
||||||
|
{
|
||||||
|
OSD::AddMessage(fmt::format("Failed to verify code {} from file {}.", code.name, game_ini_id),
|
||||||
|
OSD::Duration::VERY_LONG, OSD::Color::RED);
|
||||||
|
OSD::AddMessage("Disable hardcore mode to enable this code.", OSD::Duration::VERY_LONG,
|
||||||
|
OSD::Color::RED);
|
||||||
|
}
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::SHA1::Digest AchievementManager::GetCodeHash(const PatchEngine::Patch& patch) const
|
||||||
|
{
|
||||||
|
auto context = Common::SHA1::CreateContext();
|
||||||
|
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(patch.entries.size())));
|
||||||
|
for (const auto& entry : patch.entries)
|
||||||
|
{
|
||||||
|
context->Update(Common::BitCastToArray<u8>(entry.type));
|
||||||
|
context->Update(Common::BitCastToArray<u8>(entry.address));
|
||||||
|
context->Update(Common::BitCastToArray<u8>(entry.value));
|
||||||
|
context->Update(Common::BitCastToArray<u8>(entry.comparand));
|
||||||
|
context->Update(Common::BitCastToArray<u8>(entry.conditional));
|
||||||
|
}
|
||||||
|
return context->Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::SHA1::Digest AchievementManager::GetCodeHash(const Gecko::GeckoCode& code) const
|
||||||
|
{
|
||||||
|
auto context = Common::SHA1::CreateContext();
|
||||||
|
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(code.codes.size())));
|
||||||
|
for (const auto& entry : code.codes)
|
||||||
|
{
|
||||||
|
context->Update(Common::BitCastToArray<u8>(entry.address));
|
||||||
|
context->Update(Common::BitCastToArray<u8>(entry.data));
|
||||||
|
}
|
||||||
|
return context->Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::SHA1::Digest AchievementManager::GetCodeHash(const ActionReplay::ARCode& code) const
|
||||||
|
{
|
||||||
|
auto context = Common::SHA1::CreateContext();
|
||||||
|
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(code.ops.size())));
|
||||||
|
for (const auto& entry : code.ops)
|
||||||
|
{
|
||||||
|
context->Update(Common::BitCastToArray<u8>(entry.cmd_addr));
|
||||||
|
context->Update(Common::BitCastToArray<u8>(entry.value));
|
||||||
|
}
|
||||||
|
return context->Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
|
||||||
|
const std::string& game_ini_id) const
|
||||||
|
{
|
||||||
|
FilterApprovedIni(patches, game_ini_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AchievementManager::FilterApprovedGeckoCodes(std::vector<Gecko::GeckoCode>& codes,
|
||||||
|
const std::string& game_ini_id) const
|
||||||
|
{
|
||||||
|
FilterApprovedIni(codes, game_ini_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AchievementManager::FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes,
|
||||||
|
const std::string& game_ini_id) const
|
||||||
|
{
|
||||||
|
FilterApprovedIni(codes, game_ini_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AchievementManager::CheckApprovedGeckoCode(const Gecko::GeckoCode& code,
|
||||||
|
const std::string& game_ini_id) const
|
||||||
|
{
|
||||||
|
return CheckApprovedCode(code, game_ini_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AchievementManager::CheckApprovedARCode(const ActionReplay::ARCode& code,
|
||||||
|
const std::string& game_ini_id) const
|
||||||
|
{
|
||||||
|
return CheckApprovedCode(code, game_ini_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementManager::SetSpectatorMode()
|
void AchievementManager::SetSpectatorMode()
|
||||||
|
@ -45,6 +45,16 @@ namespace PatchEngine
|
|||||||
struct Patch;
|
struct Patch;
|
||||||
} // namespace PatchEngine
|
} // namespace PatchEngine
|
||||||
|
|
||||||
|
namespace Gecko
|
||||||
|
{
|
||||||
|
class GeckoCode;
|
||||||
|
} // namespace Gecko
|
||||||
|
|
||||||
|
namespace ActionReplay
|
||||||
|
{
|
||||||
|
struct ARCode;
|
||||||
|
} // namespace ActionReplay
|
||||||
|
|
||||||
class AchievementManager
|
class AchievementManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -70,8 +80,8 @@ public:
|
|||||||
static constexpr std::string_view BLUE = "#0B71C1";
|
static constexpr std::string_view BLUE = "#0B71C1";
|
||||||
static constexpr std::string_view APPROVED_LIST_FILENAME = "ApprovedInis.json";
|
static constexpr std::string_view APPROVED_LIST_FILENAME = "ApprovedInis.json";
|
||||||
static const inline Common::SHA1::Digest APPROVED_LIST_HASH = {
|
static const inline Common::SHA1::Digest APPROVED_LIST_HASH = {
|
||||||
0xCC, 0xB4, 0x05, 0x2D, 0x2B, 0xEE, 0xF4, 0x06, 0x4A, 0xC9,
|
0xA4, 0x98, 0x59, 0x23, 0x10, 0x56, 0x45, 0x30, 0xA9, 0xC5,
|
||||||
0x57, 0x5D, 0xA9, 0xE9, 0xDE, 0xB7, 0x98, 0xF8, 0x1A, 0x6D};
|
0x68, 0x5A, 0xB6, 0x47, 0x67, 0xF8, 0xF0, 0x7D, 0x1D, 0x14};
|
||||||
|
|
||||||
struct LeaderboardEntry
|
struct LeaderboardEntry
|
||||||
{
|
{
|
||||||
@ -125,8 +135,16 @@ public:
|
|||||||
void SetHardcoreMode();
|
void SetHardcoreMode();
|
||||||
bool IsHardcoreModeActive() const;
|
bool IsHardcoreModeActive() const;
|
||||||
void SetGameIniId(const std::string& game_ini_id) { m_game_ini_id = game_ini_id; }
|
void SetGameIniId(const std::string& game_ini_id) { m_game_ini_id = game_ini_id; }
|
||||||
|
|
||||||
void FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
|
void FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
|
||||||
const std::string& game_ini_id) const;
|
const std::string& game_ini_id) const;
|
||||||
|
void FilterApprovedGeckoCodes(std::vector<Gecko::GeckoCode>& codes,
|
||||||
|
const std::string& game_ini_id) const;
|
||||||
|
void FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes,
|
||||||
|
const std::string& game_ini_id) const;
|
||||||
|
bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code, const std::string& game_ini_id) const;
|
||||||
|
bool CheckApprovedARCode(const ActionReplay::ARCode& code, const std::string& game_ini_id) const;
|
||||||
|
|
||||||
void SetSpectatorMode();
|
void SetSpectatorMode();
|
||||||
std::string_view GetPlayerDisplayName() const;
|
std::string_view GetPlayerDisplayName() const;
|
||||||
u32 GetPlayerScore() const;
|
u32 GetPlayerScore() const;
|
||||||
@ -181,6 +199,14 @@ private:
|
|||||||
void* userdata);
|
void* userdata);
|
||||||
void DisplayWelcomeMessage();
|
void DisplayWelcomeMessage();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void FilterApprovedIni(std::vector<T>& codes, const std::string& game_ini_id) const;
|
||||||
|
template <typename T>
|
||||||
|
bool CheckApprovedCode(const T& code, const std::string& game_ini_id) const;
|
||||||
|
Common::SHA1::Digest GetCodeHash(const PatchEngine::Patch& patch) const;
|
||||||
|
Common::SHA1::Digest GetCodeHash(const Gecko::GeckoCode& code) const;
|
||||||
|
Common::SHA1::Digest GetCodeHash(const ActionReplay::ARCode& code) const;
|
||||||
|
|
||||||
static void LeaderboardEntriesCallback(int result, const char* error_message,
|
static void LeaderboardEntriesCallback(int result, const char* error_message,
|
||||||
rc_client_leaderboard_entry_list_t* list,
|
rc_client_leaderboard_entry_list_t* list,
|
||||||
rc_client_t* client, void* userdata);
|
rc_client_t* client, void* userdata);
|
||||||
@ -265,6 +291,18 @@ public:
|
|||||||
|
|
||||||
constexpr bool IsHardcoreModeActive() { return false; }
|
constexpr bool IsHardcoreModeActive() { return false; }
|
||||||
|
|
||||||
|
constexpr bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code,
|
||||||
|
const std::string& game_ini_id)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr bool CheckApprovedARCode(const ActionReplay::ARCode& code,
|
||||||
|
const std::string& game_ini_id)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
constexpr void LoadGame(const std::string&, const DiscIO::Volume*) {}
|
constexpr void LoadGame(const std::string&, const DiscIO::Volume*) {}
|
||||||
|
|
||||||
constexpr void SetBackgroundExecutionAllowed(bool allowed) {}
|
constexpr void SetBackgroundExecutionAllowed(bool allowed) {}
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
|
|
||||||
#include "Core/ARDecrypt.h"
|
#include "Core/ARDecrypt.h"
|
||||||
|
#include "Core/AchievementManager.h"
|
||||||
#include "Core/CheatCodes.h"
|
#include "Core/CheatCodes.h"
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
#include "Core/PowerPC/MMU.h"
|
#include "Core/PowerPC/MMU.h"
|
||||||
@ -112,7 +113,7 @@ struct ARAddr
|
|||||||
|
|
||||||
// ----------------------
|
// ----------------------
|
||||||
// AR Remote Functions
|
// AR Remote Functions
|
||||||
void ApplyCodes(std::span<const ARCode> codes)
|
void ApplyCodes(std::span<const ARCode> codes, const std::string& game_id)
|
||||||
{
|
{
|
||||||
if (!Config::AreCheatsEnabled())
|
if (!Config::AreCheatsEnabled())
|
||||||
return;
|
return;
|
||||||
@ -121,7 +122,10 @@ void ApplyCodes(std::span<const ARCode> codes)
|
|||||||
s_disable_logging = false;
|
s_disable_logging = false;
|
||||||
s_active_codes.clear();
|
s_active_codes.clear();
|
||||||
std::copy_if(codes.begin(), codes.end(), std::back_inserter(s_active_codes),
|
std::copy_if(codes.begin(), codes.end(), std::back_inserter(s_active_codes),
|
||||||
[](const ARCode& code) { return code.enabled; });
|
[&game_id](const ARCode& code) {
|
||||||
|
return code.enabled &&
|
||||||
|
AchievementManager::GetInstance().CheckApprovedARCode(code, game_id);
|
||||||
|
});
|
||||||
s_active_codes.shrink_to_fit();
|
s_active_codes.shrink_to_fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,9 +173,10 @@ void AddCode(ARCode code)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadAndApplyCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini)
|
void LoadAndApplyCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini,
|
||||||
|
const std::string& game_id)
|
||||||
{
|
{
|
||||||
ApplyCodes(LoadCodes(global_ini, local_ini));
|
ApplyCodes(LoadCodes(global_ini, local_ini), game_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses the Action Replay section of a game ini file.
|
// Parses the Action Replay section of a game ini file.
|
||||||
|
@ -45,12 +45,13 @@ struct ARCode
|
|||||||
|
|
||||||
void RunAllActive(const Core::CPUThreadGuard& cpu_guard);
|
void RunAllActive(const Core::CPUThreadGuard& cpu_guard);
|
||||||
|
|
||||||
void ApplyCodes(std::span<const ARCode> codes);
|
void ApplyCodes(std::span<const ARCode> codes, const std::string& game_id);
|
||||||
void SetSyncedCodesAsActive();
|
void SetSyncedCodesAsActive();
|
||||||
void UpdateSyncedCodes(std::span<const ARCode> codes);
|
void UpdateSyncedCodes(std::span<const ARCode> codes);
|
||||||
std::vector<ARCode> ApplyAndReturnCodes(std::span<const ARCode> codes);
|
std::vector<ARCode> ApplyAndReturnCodes(std::span<const ARCode> codes);
|
||||||
void AddCode(ARCode new_code);
|
void AddCode(ARCode new_code);
|
||||||
void LoadAndApplyCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini);
|
void LoadAndApplyCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini,
|
||||||
|
const std::string& game_id);
|
||||||
|
|
||||||
std::vector<ARCode> LoadCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini);
|
std::vector<ARCode> LoadCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini);
|
||||||
void SaveCodes(Common::IniFile* local_ini, std::span<const ARCode> codes);
|
void SaveCodes(Common::IniFile* local_ini, std::span<const ARCode> codes);
|
||||||
|
@ -751,8 +751,7 @@ bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot)
|
|||||||
|
|
||||||
bool AreCheatsEnabled()
|
bool AreCheatsEnabled()
|
||||||
{
|
{
|
||||||
return Config::Get(::Config::MAIN_ENABLE_CHEATS) &&
|
return Config::Get(::Config::MAIN_ENABLE_CHEATS);
|
||||||
!AchievementManager::GetInstance().IsHardcoreModeActive();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsDebuggingEnabled()
|
bool IsDebuggingEnabled()
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "Common/Config/Config.h"
|
#include "Common/Config/Config.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
|
|
||||||
|
#include "Core/Config/AchievementSettings.h"
|
||||||
#include "Core/Config/GraphicsSettings.h"
|
#include "Core/Config/GraphicsSettings.h"
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
#include "Core/Config/SYSCONFSettings.h"
|
#include "Core/Config/SYSCONFSettings.h"
|
||||||
@ -33,6 +34,9 @@ public:
|
|||||||
layer->Set(Config::MAIN_CPU_THREAD, m_settings.cpu_thread);
|
layer->Set(Config::MAIN_CPU_THREAD, m_settings.cpu_thread);
|
||||||
layer->Set(Config::MAIN_CPU_CORE, m_settings.cpu_core);
|
layer->Set(Config::MAIN_CPU_CORE, m_settings.cpu_core);
|
||||||
layer->Set(Config::MAIN_ENABLE_CHEATS, m_settings.enable_cheats);
|
layer->Set(Config::MAIN_ENABLE_CHEATS, m_settings.enable_cheats);
|
||||||
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||||
|
layer->Set(Config::RA_HARDCORE_ENABLED, m_settings.enable_hardcore);
|
||||||
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
layer->Set(Config::MAIN_GC_LANGUAGE, m_settings.selected_language);
|
layer->Set(Config::MAIN_GC_LANGUAGE, m_settings.selected_language);
|
||||||
layer->Set(Config::MAIN_OVERRIDE_REGION_SETTINGS, m_settings.override_region_settings);
|
layer->Set(Config::MAIN_OVERRIDE_REGION_SETTINGS, m_settings.override_region_settings);
|
||||||
layer->Set(Config::MAIN_DSP_HLE, m_settings.dsp_hle);
|
layer->Set(Config::MAIN_DSP_HLE, m_settings.dsp_hle);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "Common/Config/Config.h"
|
#include "Common/Config/Config.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
|
|
||||||
|
#include "Core/AchievementManager.h"
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/Host.h"
|
#include "Core/Host.h"
|
||||||
@ -49,7 +50,7 @@ static std::vector<GeckoCode> s_active_codes;
|
|||||||
static std::vector<GeckoCode> s_synced_codes;
|
static std::vector<GeckoCode> s_synced_codes;
|
||||||
static std::mutex s_active_codes_lock;
|
static std::mutex s_active_codes_lock;
|
||||||
|
|
||||||
void SetActiveCodes(std::span<const GeckoCode> gcodes)
|
void SetActiveCodes(std::span<const GeckoCode> gcodes, const std::string& game_id)
|
||||||
{
|
{
|
||||||
std::lock_guard lk(s_active_codes_lock);
|
std::lock_guard lk(s_active_codes_lock);
|
||||||
|
|
||||||
@ -57,8 +58,12 @@ void SetActiveCodes(std::span<const GeckoCode> gcodes)
|
|||||||
if (Config::AreCheatsEnabled())
|
if (Config::AreCheatsEnabled())
|
||||||
{
|
{
|
||||||
s_active_codes.reserve(gcodes.size());
|
s_active_codes.reserve(gcodes.size());
|
||||||
|
|
||||||
std::copy_if(gcodes.begin(), gcodes.end(), std::back_inserter(s_active_codes),
|
std::copy_if(gcodes.begin(), gcodes.end(), std::back_inserter(s_active_codes),
|
||||||
[](const GeckoCode& code) { return code.enabled; });
|
[&game_id](const GeckoCode& code) {
|
||||||
|
return code.enabled &&
|
||||||
|
AchievementManager::GetInstance().CheckApprovedGeckoCode(code, game_id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
s_active_codes.shrink_to_fit();
|
s_active_codes.shrink_to_fit();
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ constexpr u32 HLE_TRAMPOLINE_ADDRESS = INSTALLER_END_ADDRESS - 4;
|
|||||||
// preserve the emulation performance.
|
// preserve the emulation performance.
|
||||||
constexpr u32 MAGIC_GAMEID = 0xD01F1BAD;
|
constexpr u32 MAGIC_GAMEID = 0xD01F1BAD;
|
||||||
|
|
||||||
void SetActiveCodes(std::span<const GeckoCode> gcodes);
|
void SetActiveCodes(std::span<const GeckoCode> gcodes, const std::string& game_id);
|
||||||
void SetSyncedCodesAsActive();
|
void SetSyncedCodesAsActive();
|
||||||
void UpdateSyncedCodes(std::span<const GeckoCode> gcodes);
|
void UpdateSyncedCodes(std::span<const GeckoCode> gcodes);
|
||||||
std::vector<GeckoCode> SetAndReturnActiveCodes(std::span<const GeckoCode> gcodes);
|
std::vector<GeckoCode> SetAndReturnActiveCodes(std::span<const GeckoCode> gcodes);
|
||||||
|
@ -848,6 +848,7 @@ void NetPlayClient::OnStartGame(sf::Packet& packet)
|
|||||||
packet >> m_net_settings.cpu_thread;
|
packet >> m_net_settings.cpu_thread;
|
||||||
packet >> m_net_settings.cpu_core;
|
packet >> m_net_settings.cpu_core;
|
||||||
packet >> m_net_settings.enable_cheats;
|
packet >> m_net_settings.enable_cheats;
|
||||||
|
packet >> m_net_settings.enable_hardcore;
|
||||||
packet >> m_net_settings.selected_language;
|
packet >> m_net_settings.selected_language;
|
||||||
packet >> m_net_settings.override_region_settings;
|
packet >> m_net_settings.override_region_settings;
|
||||||
packet >> m_net_settings.dsp_enable_jit;
|
packet >> m_net_settings.dsp_enable_jit;
|
||||||
|
@ -35,6 +35,7 @@ struct NetSettings
|
|||||||
bool cpu_thread = false;
|
bool cpu_thread = false;
|
||||||
PowerPC::CPUCore cpu_core{};
|
PowerPC::CPUCore cpu_core{};
|
||||||
bool enable_cheats = false;
|
bool enable_cheats = false;
|
||||||
|
bool enable_hardcore = false;
|
||||||
int selected_language = 0;
|
int selected_language = 0;
|
||||||
bool override_region_settings = false;
|
bool override_region_settings = false;
|
||||||
bool dsp_hle = false;
|
bool dsp_hle = false;
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "Common/UPnP.h"
|
#include "Common/UPnP.h"
|
||||||
#include "Common/Version.h"
|
#include "Common/Version.h"
|
||||||
|
|
||||||
|
#include "Core/AchievementManager.h"
|
||||||
#include "Core/ActionReplay.h"
|
#include "Core/ActionReplay.h"
|
||||||
#include "Core/Boot/Boot.h"
|
#include "Core/Boot/Boot.h"
|
||||||
#include "Core/Config/GraphicsSettings.h"
|
#include "Core/Config/GraphicsSettings.h"
|
||||||
@ -1358,6 +1359,7 @@ bool NetPlayServer::SetupNetSettings()
|
|||||||
settings.cpu_thread = Config::Get(Config::MAIN_CPU_THREAD);
|
settings.cpu_thread = Config::Get(Config::MAIN_CPU_THREAD);
|
||||||
settings.cpu_core = Config::Get(Config::MAIN_CPU_CORE);
|
settings.cpu_core = Config::Get(Config::MAIN_CPU_CORE);
|
||||||
settings.enable_cheats = Config::AreCheatsEnabled();
|
settings.enable_cheats = Config::AreCheatsEnabled();
|
||||||
|
settings.enable_hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||||
settings.selected_language = Config::Get(Config::MAIN_GC_LANGUAGE);
|
settings.selected_language = Config::Get(Config::MAIN_GC_LANGUAGE);
|
||||||
settings.override_region_settings = Config::Get(Config::MAIN_OVERRIDE_REGION_SETTINGS);
|
settings.override_region_settings = Config::Get(Config::MAIN_OVERRIDE_REGION_SETTINGS);
|
||||||
settings.dsp_hle = Config::Get(Config::MAIN_DSP_HLE);
|
settings.dsp_hle = Config::Get(Config::MAIN_DSP_HLE);
|
||||||
@ -1586,6 +1588,7 @@ bool NetPlayServer::StartGame()
|
|||||||
spac << m_settings.cpu_thread;
|
spac << m_settings.cpu_thread;
|
||||||
spac << m_settings.cpu_core;
|
spac << m_settings.cpu_core;
|
||||||
spac << m_settings.enable_cheats;
|
spac << m_settings.enable_cheats;
|
||||||
|
spac << m_settings.enable_hardcore;
|
||||||
spac << m_settings.selected_language;
|
spac << m_settings.selected_language;
|
||||||
spac << m_settings.override_region_settings;
|
spac << m_settings.override_region_settings;
|
||||||
spac << m_settings.dsp_enable_jit;
|
spac << m_settings.dsp_enable_jit;
|
||||||
@ -2067,13 +2070,18 @@ bool NetPlayServer::SyncCodes()
|
|||||||
}
|
}
|
||||||
// Sync Gecko Codes
|
// Sync Gecko Codes
|
||||||
{
|
{
|
||||||
|
std::vector<Gecko::GeckoCode> codes = Gecko::LoadCodes(globalIni, localIni);
|
||||||
|
|
||||||
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||||
|
AchievementManager::GetInstance().FilterApprovedGeckoCodes(codes, game_id);
|
||||||
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
|
|
||||||
// Create a Gecko Code Vector with just the active codes
|
// Create a Gecko Code Vector with just the active codes
|
||||||
std::vector<Gecko::GeckoCode> s_active_codes =
|
std::vector<Gecko::GeckoCode> active_codes = Gecko::SetAndReturnActiveCodes(codes);
|
||||||
Gecko::SetAndReturnActiveCodes(Gecko::LoadCodes(globalIni, localIni));
|
|
||||||
|
|
||||||
// Determine Codelist Size
|
// Determine Codelist Size
|
||||||
u16 codelines = 0;
|
u16 codelines = 0;
|
||||||
for (const Gecko::GeckoCode& active_code : s_active_codes)
|
for (const Gecko::GeckoCode& active_code : active_codes)
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(NETPLAY, "Indexing {}", active_code.name);
|
INFO_LOG_FMT(NETPLAY, "Indexing {}", active_code.name);
|
||||||
for (const Gecko::GeckoCode::Code& code : active_code.codes)
|
for (const Gecko::GeckoCode::Code& code : active_code.codes)
|
||||||
@ -2101,7 +2109,7 @@ bool NetPlayServer::SyncCodes()
|
|||||||
pac << MessageID::SyncCodes;
|
pac << MessageID::SyncCodes;
|
||||||
pac << SyncCodeID::GeckoData;
|
pac << SyncCodeID::GeckoData;
|
||||||
// Iterate through the active code vector and send each codeline
|
// Iterate through the active code vector and send each codeline
|
||||||
for (const Gecko::GeckoCode& active_code : s_active_codes)
|
for (const Gecko::GeckoCode& active_code : active_codes)
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(NETPLAY, "Sending {}", active_code.name);
|
INFO_LOG_FMT(NETPLAY, "Sending {}", active_code.name);
|
||||||
for (const Gecko::GeckoCode::Code& code : active_code.codes)
|
for (const Gecko::GeckoCode::Code& code : active_code.codes)
|
||||||
@ -2117,13 +2125,16 @@ bool NetPlayServer::SyncCodes()
|
|||||||
|
|
||||||
// Sync AR Codes
|
// Sync AR Codes
|
||||||
{
|
{
|
||||||
|
std::vector<ActionReplay::ARCode> codes = ActionReplay::LoadCodes(globalIni, localIni);
|
||||||
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||||
|
AchievementManager::GetInstance().FilterApprovedARCodes(codes, game_id);
|
||||||
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
// Create an AR Code Vector with just the active codes
|
// Create an AR Code Vector with just the active codes
|
||||||
std::vector<ActionReplay::ARCode> s_active_codes =
|
std::vector<ActionReplay::ARCode> active_codes = ActionReplay::ApplyAndReturnCodes(codes);
|
||||||
ActionReplay::ApplyAndReturnCodes(ActionReplay::LoadCodes(globalIni, localIni));
|
|
||||||
|
|
||||||
// Determine Codelist Size
|
// Determine Codelist Size
|
||||||
u16 codelines = 0;
|
u16 codelines = 0;
|
||||||
for (const ActionReplay::ARCode& active_code : s_active_codes)
|
for (const ActionReplay::ARCode& active_code : active_codes)
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(NETPLAY, "Indexing {}", active_code.name);
|
INFO_LOG_FMT(NETPLAY, "Indexing {}", active_code.name);
|
||||||
for (const ActionReplay::AREntry& op : active_code.ops)
|
for (const ActionReplay::AREntry& op : active_code.ops)
|
||||||
@ -2151,7 +2162,7 @@ bool NetPlayServer::SyncCodes()
|
|||||||
pac << MessageID::SyncCodes;
|
pac << MessageID::SyncCodes;
|
||||||
pac << SyncCodeID::ARData;
|
pac << SyncCodeID::ARData;
|
||||||
// Iterate through the active code vector and send each codeline
|
// Iterate through the active code vector and send each codeline
|
||||||
for (const ActionReplay::ARCode& active_code : s_active_codes)
|
for (const ActionReplay::ARCode& active_code : active_codes)
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(NETPLAY, "Sending {}", active_code.name);
|
INFO_LOG_FMT(NETPLAY, "Sending {}", active_code.name);
|
||||||
for (const ActionReplay::AREntry& op : active_code.ops)
|
for (const ActionReplay::AREntry& op : active_code.ops)
|
||||||
|
@ -197,8 +197,8 @@ void LoadPatches()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Gecko::SetActiveCodes(Gecko::LoadCodes(globalIni, localIni));
|
Gecko::SetActiveCodes(Gecko::LoadCodes(globalIni, localIni), sconfig.GetGameID());
|
||||||
ActionReplay::LoadAndApplyCodes(globalIni, localIni);
|
ActionReplay::LoadAndApplyCodes(globalIni, localIni, sconfig.GetGameID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,9 +245,6 @@ static void ApplyPatches(const Core::CPUThreadGuard& guard, const std::vector<Pa
|
|||||||
static void ApplyMemoryPatches(const Core::CPUThreadGuard& guard,
|
static void ApplyMemoryPatches(const Core::CPUThreadGuard& guard,
|
||||||
std::span<const std::size_t> memory_patch_indices)
|
std::span<const std::size_t> memory_patch_indices)
|
||||||
{
|
{
|
||||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::lock_guard lock(s_on_frame_memory_mutex);
|
std::lock_guard lock(s_on_frame_memory_mutex);
|
||||||
for (std::size_t index : memory_patch_indices)
|
for (std::size_t index : memory_patch_indices)
|
||||||
{
|
{
|
||||||
@ -335,7 +332,7 @@ bool ApplyFramePatches(Core::System& system)
|
|||||||
void Shutdown()
|
void Shutdown()
|
||||||
{
|
{
|
||||||
s_on_frame.clear();
|
s_on_frame.clear();
|
||||||
ActionReplay::ApplyCodes({});
|
ActionReplay::ApplyCodes({}, "");
|
||||||
Gecko::Shutdown();
|
Gecko::Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,7 +329,6 @@ void AchievementSettingsWidget::ToggleProgress()
|
|||||||
|
|
||||||
void AchievementSettingsWidget::UpdateHardcoreMode()
|
void AchievementSettingsWidget::UpdateHardcoreMode()
|
||||||
{
|
{
|
||||||
AchievementManager::GetInstance().SetHardcoreMode();
|
|
||||||
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
||||||
{
|
{
|
||||||
Settings::Instance().SetDebugModeEnabled(false);
|
Settings::Instance().SetDebugModeEnabled(false);
|
||||||
|
@ -115,7 +115,7 @@ void ARCodeWidget::OnItemChanged(QListWidgetItem* item)
|
|||||||
m_ar_codes[m_code_list->row(item)].enabled = (item->checkState() == Qt::Checked);
|
m_ar_codes[m_code_list->row(item)].enabled = (item->checkState() == Qt::Checked);
|
||||||
|
|
||||||
if (!m_restart_required)
|
if (!m_restart_required)
|
||||||
ActionReplay::ApplyCodes(m_ar_codes);
|
ActionReplay::ApplyCodes(m_ar_codes, m_game_id);
|
||||||
|
|
||||||
UpdateList();
|
UpdateList();
|
||||||
SaveCodes();
|
SaveCodes();
|
||||||
|
@ -202,7 +202,7 @@ void GeckoCodeWidget::OnItemChanged(QListWidgetItem* item)
|
|||||||
m_gecko_codes[index].enabled = (item->checkState() == Qt::Checked);
|
m_gecko_codes[index].enabled = (item->checkState() == Qt::Checked);
|
||||||
|
|
||||||
if (!m_restart_required)
|
if (!m_restart_required)
|
||||||
Gecko::SetActiveCodes(m_gecko_codes);
|
Gecko::SetActiveCodes(m_gecko_codes, m_game_id);
|
||||||
|
|
||||||
SaveCodes();
|
SaveCodes();
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ void HardcoreWarningWidget::CreateWidgets()
|
|||||||
auto* icon = new QLabel;
|
auto* icon = new QLabel;
|
||||||
icon->setPixmap(warning_icon);
|
icon->setPixmap(warning_icon);
|
||||||
|
|
||||||
m_text = new QLabel(tr("This feature is disabled in hardcore mode."));
|
m_text = new QLabel(tr("Only approved codes will be applied in hardcore mode."));
|
||||||
m_settings_button = new QPushButton(tr("Achievement Settings"));
|
m_settings_button = new QPushButton(tr("Achievement Settings"));
|
||||||
|
|
||||||
auto* layout = new QHBoxLayout;
|
auto* layout = new QHBoxLayout;
|
||||||
|
@ -88,10 +88,9 @@ void GeneralPane::CreateLayout()
|
|||||||
void GeneralPane::OnEmulationStateChanged(Core::State state)
|
void GeneralPane::OnEmulationStateChanged(Core::State state)
|
||||||
{
|
{
|
||||||
const bool running = state != Core::State::Uninitialized;
|
const bool running = state != Core::State::Uninitialized;
|
||||||
const bool hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
|
|
||||||
|
|
||||||
m_checkbox_dualcore->setEnabled(!running);
|
m_checkbox_dualcore->setEnabled(!running);
|
||||||
m_checkbox_cheats->setEnabled(!running && !hardcore);
|
m_checkbox_cheats->setEnabled(!running);
|
||||||
m_checkbox_override_region_settings->setEnabled(!running);
|
m_checkbox_override_region_settings->setEnabled(!running);
|
||||||
#ifdef USE_DISCORD_PRESENCE
|
#ifdef USE_DISCORD_PRESENCE
|
||||||
m_checkbox_discord_presence->setEnabled(!running);
|
m_checkbox_discord_presence->setEnabled(!running);
|
||||||
|
@ -18,7 +18,10 @@
|
|||||||
#include "Common/IOFile.h"
|
#include "Common/IOFile.h"
|
||||||
#include "Common/IniFile.h"
|
#include "Common/IniFile.h"
|
||||||
#include "Common/JsonUtil.h"
|
#include "Common/JsonUtil.h"
|
||||||
|
#include "Core/ActionReplay.h"
|
||||||
#include "Core/CheatCodes.h"
|
#include "Core/CheatCodes.h"
|
||||||
|
#include "Core/GeckoCode.h"
|
||||||
|
#include "Core/GeckoCodeConfig.h"
|
||||||
#include "Core/PatchEngine.h"
|
#include "Core/PatchEngine.h"
|
||||||
|
|
||||||
struct GameHashes
|
struct GameHashes
|
||||||
@ -27,6 +30,11 @@ struct GameHashes
|
|||||||
std::map<std::string /*hash*/, std::string /*patch name*/> hashes;
|
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)
|
TEST(PatchAllowlist, VerifyHashes)
|
||||||
{
|
{
|
||||||
// Load allowlist
|
// Load allowlist
|
||||||
@ -42,9 +50,9 @@ TEST(PatchAllowlist, VerifyHashes)
|
|||||||
const auto& list_filepath = fmt::format("{}{}{}", sys_directory, DIR_SEP, APPROVED_LIST_FILENAME);
|
const auto& list_filepath = fmt::format("{}{}{}", sys_directory, DIR_SEP, APPROVED_LIST_FILENAME);
|
||||||
ASSERT_TRUE(JsonFromFile(list_filepath, &json_tree, &error))
|
ASSERT_TRUE(JsonFromFile(list_filepath, &json_tree, &error))
|
||||||
<< "Failed to open file at " << list_filepath;
|
<< "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>());
|
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>())
|
for (const auto& entry : json_tree.get<picojson::object>())
|
||||||
{
|
{
|
||||||
ASSERT_TRUE(entry.second.is<picojson::object>());
|
ASSERT_TRUE(entry.second.is<picojson::object>());
|
||||||
@ -69,12 +77,23 @@ TEST(PatchAllowlist, VerifyHashes)
|
|||||||
std::string game_id = file.virtualName.substr(0, file.virtualName.find_first_of('.'));
|
std::string game_id = file.virtualName.substr(0, file.virtualName.find_first_of('.'));
|
||||||
std::vector<PatchEngine::Patch> patches;
|
std::vector<PatchEngine::Patch> patches;
|
||||||
PatchEngine::LoadPatchSection("OnFrame", &patches, ini_file, Common::IniFile());
|
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
|
// Filter patches for RetroAchievements approved
|
||||||
ReadEnabledOrDisabled<PatchEngine::Patch>(ini_file, "OnFrame", false, &patches);
|
ReadEnabledOrDisabled<PatchEngine::Patch>(ini_file, "OnFrame", false, &patches);
|
||||||
ReadEnabledOrDisabled<PatchEngine::Patch>(ini_file, "Patches_RetroAchievements_Verified", true,
|
ReadEnabledOrDisabled<PatchEngine::Patch>(ini_file, "Patches_RetroAchievements_Verified", true,
|
||||||
&patches);
|
&patches);
|
||||||
|
ReadEnabledOrDisabled<Gecko::GeckoCode>(ini_file, "Gecko", false, &geckos);
|
||||||
|
ReadEnabledOrDisabled<Gecko::GeckoCode>(ini_file, "Gecko_Enabled", false, &geckos);
|
||||||
|
ReadEnabledOrDisabled<Gecko::GeckoCode>(ini_file, "Gecko_RetroAchievements_Verified", true,
|
||||||
|
&geckos);
|
||||||
|
ReadEnabledOrDisabled<ActionReplay::ARCode>(ini_file, "ActionReplay", false, &action_replays);
|
||||||
|
ReadEnabledOrDisabled<ActionReplay::ARCode>(ini_file, "AR_RetroAchievements_Verified", true,
|
||||||
|
&action_replays);
|
||||||
// Get game section from allow list
|
// Get game section from allow list
|
||||||
auto game_itr = allow_list.find(game_id);
|
auto game_itr = allow_list.find(game_id);
|
||||||
|
bool itr_end = (game_itr == allow_list.end());
|
||||||
// Iterate over approved patches
|
// Iterate over approved patches
|
||||||
for (const auto& patch : patches)
|
for (const auto& patch : patches)
|
||||||
{
|
{
|
||||||
@ -92,38 +111,51 @@ TEST(PatchAllowlist, VerifyHashes)
|
|||||||
context->Update(Common::BitCastToArray<u8>(entry.conditional));
|
context->Update(Common::BitCastToArray<u8>(entry.conditional));
|
||||||
}
|
}
|
||||||
auto digest = context->Finish();
|
auto digest = context->Finish();
|
||||||
std::string hash = Common::SHA1::DigestToString(digest);
|
CheckHash(game_id, itr_end ? nullptr : &game_itr->second,
|
||||||
// Check patch in list
|
Common::SHA1::DigestToString(digest), patch.name);
|
||||||
if (game_itr == allow_list.end())
|
}
|
||||||
{
|
// Iterate over approved geckos
|
||||||
// Report: no patches in game found in list
|
for (const auto& code : geckos)
|
||||||
ADD_FAILURE() << "Approved hash missing from list." << std::endl
|
{
|
||||||
<< "Game ID: " << game_id << std::endl
|
if (!code.enabled)
|
||||||
<< "Patch: \"" << hash << "\" : \"" << patch.name << "\"";
|
|
||||||
continue;
|
continue;
|
||||||
}
|
// Hash patch
|
||||||
auto hash_itr = game_itr->second.hashes.find(hash);
|
auto context = Common::SHA1::CreateContext();
|
||||||
if (hash_itr == game_itr->second.hashes.end())
|
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(code.codes.size())));
|
||||||
|
for (const auto& entry : code.codes)
|
||||||
{
|
{
|
||||||
// Report: patch not found in list
|
context->Update(Common::BitCastToArray<u8>(entry.address));
|
||||||
ADD_FAILURE() << "Approved hash missing from list." << std::endl
|
context->Update(Common::BitCastToArray<u8>(entry.data));
|
||||||
<< "Game ID: " << game_id << ":" << game_itr->second.game_title << std::endl
|
|
||||||
<< "Patch: \"" << hash << "\" : \"" << patch.name << "\"";
|
|
||||||
}
|
}
|
||||||
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
|
context->Update(Common::BitCastToArray<u8>(entry.cmd_addr));
|
||||||
game_itr->second.hashes.erase(hash_itr);
|
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
|
// Report missing patches in map
|
||||||
if (game_itr == allow_list.end())
|
if (itr_end)
|
||||||
continue;
|
continue;
|
||||||
for (auto& remaining_hashes : game_itr->second.hashes)
|
for (auto& remaining_hashes : game_itr->second.hashes)
|
||||||
{
|
{
|
||||||
ADD_FAILURE() << "Hash in list not approved in ini." << std::endl
|
ADD_FAILURE() << "Hash in list not approved in ini." << std::endl
|
||||||
<< "Game ID: " << game_id << ":" << game_itr->second.game_title << 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
|
// Remove section from map
|
||||||
allow_list.erase(game_itr);
|
allow_list.erase(game_itr);
|
||||||
@ -136,3 +168,30 @@ TEST(PatchAllowlist, VerifyHashes)
|
|||||||
<< remaining_games.second.game_title;
|
<< 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user