Skip to content

Commit

Permalink
Merge pull request #12955 from LillyJadeKatrin/retroachievements-gecko
Browse files Browse the repository at this point in the history
Add Support for Gecko Codes to Achievements Whitelist
  • Loading branch information
JMC47 authored Dec 2, 2024
2 parents c10809a + 51435b6 commit cf29214
Show file tree
Hide file tree
Showing 16 changed files with 287 additions and 91 deletions.
4 changes: 4 additions & 0 deletions Data/Sys/ApprovedInis.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@
"title": "Gladius",
"3D0894616C9A7FA5ED91C1D2F461BF14DF47ECEC": "Fix freeze in opening cutscene"
},
"GMSE01": {
"title": "Super Mario Sunshine",
"BD718F961DBA5372B1D0257D454D535746C453A0": "Widescreen"
},
"GNHE5d": {
"title": "NHL HITZ 2002",
"89393A24E2336841AA4CD0AD3BE1C9A66B89E9EF": "Nop Hack"
Expand Down
3 changes: 3 additions & 0 deletions Data/Sys/GameSettings/GMSE01.ini
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,6 @@ C2363138 00000009
7C631670 54A5F0BE
7C630194 7C630214
60000000 00000000

[Gecko_RetroAchievements_Verified]
$Widescreen
148 changes: 112 additions & 36 deletions Source/Core/Core/AchievementManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
#include "Common/ScopeGuard.h"
#include "Common/Version.h"
#include "Common/WorkQueueThread.h"
#include "Core/ActionReplay.h"
#include "Core/Config/AchievementSettings.h"
#include "Core/Config/FreeLookSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/GeckoCode.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/VideoInterface.h"
#include "Core/PatchEngine.h"
Expand Down Expand Up @@ -370,7 +372,6 @@ void AchievementManager::SetHardcoreMode()
if (Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f)
Config::SetBaseOrCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
Config::SetBaseOrCurrent(Config::FREE_LOOK_ENABLED, false);
Config::SetBaseOrCurrent(Config::MAIN_ENABLE_CHEATS, false);
}
}

Expand All @@ -384,10 +385,11 @@ bool AchievementManager::IsHardcoreModeActive() const
return rc_client_is_processing_required(m_client);
}

void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
const std::string& game_ini_id) const
template <typename T>
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
return;
Expand All @@ -398,46 +400,120 @@ void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>&
if (!IsHardcoreModeActive())
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);

auto patch_itr = patches.begin();
while (patch_itr != patches.end())
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying code {}", code.name);

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();
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;
}

verified = m_ini_root->get(game_ini_id).contains(Common::SHA1::DigestToString(digest));
}
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();
}

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++;
}
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()
Expand Down
42 changes: 40 additions & 2 deletions Source/Core/Core/AchievementManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ namespace PatchEngine
struct Patch;
} // namespace PatchEngine

namespace Gecko
{
class GeckoCode;
} // namespace Gecko

namespace ActionReplay
{
struct ARCode;
} // namespace ActionReplay

class AchievementManager
{
public:
Expand All @@ -70,8 +80,8 @@ class AchievementManager
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 = {
0xCC, 0xB4, 0x05, 0x2D, 0x2B, 0xEE, 0xF4, 0x06, 0x4A, 0xC9,
0x57, 0x5D, 0xA9, 0xE9, 0xDE, 0xB7, 0x98, 0xF8, 0x1A, 0x6D};
0xA4, 0x98, 0x59, 0x23, 0x10, 0x56, 0x45, 0x30, 0xA9, 0xC5,
0x68, 0x5A, 0xB6, 0x47, 0x67, 0xF8, 0xF0, 0x7D, 0x1D, 0x14};

struct LeaderboardEntry
{
Expand Down Expand Up @@ -125,8 +135,16 @@ class AchievementManager
void SetHardcoreMode();
bool IsHardcoreModeActive() const;
void SetGameIniId(const std::string& game_ini_id) { m_game_ini_id = game_ini_id; }

void FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
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();
std::string_view GetPlayerDisplayName() const;
u32 GetPlayerScore() const;
Expand Down Expand Up @@ -181,6 +199,14 @@ class AchievementManager
void* userdata);
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,
rc_client_leaderboard_entry_list_t* list,
rc_client_t* client, void* userdata);
Expand Down Expand Up @@ -265,6 +291,18 @@ class AchievementManager

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 SetBackgroundExecutionAllowed(bool allowed) {}
Expand Down
13 changes: 9 additions & 4 deletions Source/Core/Core/ActionReplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "Common/MsgHandler.h"

#include "Core/ARDecrypt.h"
#include "Core/AchievementManager.h"
#include "Core/CheatCodes.h"
#include "Core/Config/MainSettings.h"
#include "Core/PowerPC/MMU.h"
Expand Down Expand Up @@ -112,7 +113,7 @@ struct ARAddr

// ----------------------
// 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())
return;
Expand All @@ -121,7 +122,10 @@ void ApplyCodes(std::span<const ARCode> codes)
s_disable_logging = false;
s_active_codes.clear();
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();
}

Expand Down Expand Up @@ -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.
Expand Down
5 changes: 3 additions & 2 deletions Source/Core/Core/ActionReplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ struct ARCode

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 UpdateSyncedCodes(std::span<const ARCode> codes);
std::vector<ARCode> ApplyAndReturnCodes(std::span<const ARCode> codes);
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);
void SaveCodes(Common::IniFile* local_ini, std::span<const ARCode> codes);
Expand Down
3 changes: 1 addition & 2 deletions Source/Core/Core/Config/MainSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -751,8 +751,7 @@ bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot)

bool AreCheatsEnabled()
{
return Config::Get(::Config::MAIN_ENABLE_CHEATS) &&
!AchievementManager::GetInstance().IsHardcoreModeActive();
return Config::Get(::Config::MAIN_ENABLE_CHEATS);
}

bool IsDebuggingEnabled()
Expand Down
9 changes: 7 additions & 2 deletions Source/Core/Core/GeckoCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "Common/Config/Config.h"
#include "Common/FileUtil.h"

#include "Core/AchievementManager.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/Host.h"
Expand Down Expand Up @@ -49,16 +50,20 @@ static std::vector<GeckoCode> s_active_codes;
static std::vector<GeckoCode> s_synced_codes;
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);

s_active_codes.clear();
if (Config::AreCheatsEnabled())
{
s_active_codes.reserve(gcodes.size());

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();

Expand Down
2 changes: 1 addition & 1 deletion Source/Core/Core/GeckoCode.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ constexpr u32 HLE_TRAMPOLINE_ADDRESS = INSTALLER_END_ADDRESS - 4;
// preserve the emulation performance.
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 UpdateSyncedCodes(std::span<const GeckoCode> gcodes);
std::vector<GeckoCode> SetAndReturnActiveCodes(std::span<const GeckoCode> gcodes);
Expand Down
Loading

0 comments on commit cf29214

Please sign in to comment.