Fixed Multi Config Code Approvals

Refactors the AR/Gecko/Patch code approval process to verify from every possible game ini, not just the base game ID. This fixes codes on specific revisions or codes general to any region.
This commit is contained in:
LillyJadeKatrin
2025-03-14 23:53:54 -04:00
parent fd2766f5df
commit 0615ade725
10 changed files with 61 additions and 58 deletions

View File

@ -28,6 +28,7 @@
#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/ConfigLoaders/GameConfigLoader.h"
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/GeckoCode.h" #include "Core/GeckoCode.h"
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
@ -386,8 +387,8 @@ bool AchievementManager::IsHardcoreModeActive() const
} }
template <typename T> template <typename T>
void AchievementManager::FilterApprovedIni(std::vector<T>& codes, void AchievementManager::FilterApprovedIni(std::vector<T>& codes, const std::string& game_id,
const std::string& game_ini_id) const u16 revision) const
{ {
if (codes.empty()) if (codes.empty())
{ {
@ -409,13 +410,14 @@ void AchievementManager::FilterApprovedIni(std::vector<T>& codes,
for (auto& code : codes) for (auto& code : codes)
{ {
if (code.enabled && !CheckApprovedCode(code, game_ini_id)) if (code.enabled && !CheckApprovedCode(code, game_id, revision))
code.enabled = false; code.enabled = false;
} }
} }
template <typename T> template <typename T>
bool AchievementManager::CheckApprovedCode(const T& code, const std::string& game_ini_id) const bool AchievementManager::CheckApprovedCode(const T& code, const std::string& game_id,
u16 revision) const
{ {
if (!IsHardcoreModeActive()) if (!IsHardcoreModeActive())
return true; return true;
@ -424,22 +426,22 @@ bool AchievementManager::CheckApprovedCode(const T& code, const std::string& gam
if (!m_ini_root->is<picojson::value::object>()) if (!m_ini_root->is<picojson::value::object>())
return false; return false;
const bool known_id = m_ini_root->contains(game_ini_id);
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying code {}", code.name); INFO_LOG_FMT(ACHIEVEMENTS, "Verifying code {}", code.name);
bool verified = false; bool verified = false;
if (known_id) auto hash = Common::SHA1::DigestToString(GetCodeHash(code));
{
auto digest = GetCodeHash(code);
verified = m_ini_root->get(game_ini_id).contains(Common::SHA1::DigestToString(digest)); for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(game_id, revision))
{
auto config = filename.substr(0, filename.length() - 4);
if (m_ini_root->contains(config) && m_ini_root->get(config).contains(hash))
verified = true;
} }
if (!verified) if (!verified)
{ {
OSD::AddMessage(fmt::format("Failed to verify code {} from file {}.", code.name, game_ini_id), OSD::AddMessage(fmt::format("Failed to verify code {} for game ID {}.", code.name, game_id),
OSD::Duration::VERY_LONG, OSD::Color::RED); OSD::Duration::VERY_LONG, OSD::Color::RED);
OSD::AddMessage("Disable hardcore mode to enable this code.", OSD::Duration::VERY_LONG, OSD::AddMessage("Disable hardcore mode to enable this code.", OSD::Duration::VERY_LONG,
OSD::Color::RED); OSD::Color::RED);
@ -487,33 +489,33 @@ Common::SHA1::Digest AchievementManager::GetCodeHash(const ActionReplay::ARCode&
} }
void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches, void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
const std::string& game_ini_id) const const std::string& game_id, u16 revision) const
{ {
FilterApprovedIni(patches, game_ini_id); FilterApprovedIni(patches, game_id, revision);
} }
void AchievementManager::FilterApprovedGeckoCodes(std::vector<Gecko::GeckoCode>& codes, void AchievementManager::FilterApprovedGeckoCodes(std::vector<Gecko::GeckoCode>& codes,
const std::string& game_ini_id) const const std::string& game_id, u16 revision) const
{ {
FilterApprovedIni(codes, game_ini_id); FilterApprovedIni(codes, game_id, revision);
} }
void AchievementManager::FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes, void AchievementManager::FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes,
const std::string& game_ini_id) const const std::string& game_id, u16 revision) const
{ {
FilterApprovedIni(codes, game_ini_id); FilterApprovedIni(codes, game_id, revision);
} }
bool AchievementManager::CheckApprovedGeckoCode(const Gecko::GeckoCode& code, bool AchievementManager::CheckApprovedGeckoCode(const Gecko::GeckoCode& code,
const std::string& game_ini_id) const const std::string& game_id, u16 revision) const
{ {
return CheckApprovedCode(code, game_ini_id); return CheckApprovedCode(code, game_id, revision);
} }
bool AchievementManager::CheckApprovedARCode(const ActionReplay::ARCode& code, bool AchievementManager::CheckApprovedARCode(const ActionReplay::ARCode& code,
const std::string& game_ini_id) const const std::string& game_id, u16 revision) const
{ {
return CheckApprovedCode(code, game_ini_id); return CheckApprovedCode(code, game_id, revision);
} }
void AchievementManager::SetSpectatorMode() void AchievementManager::SetSpectatorMode()

View File

@ -133,16 +133,17 @@ public:
std::recursive_mutex& GetLock(); std::recursive_mutex& GetLock();
bool IsHardcoreModeActive() const; 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, void FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches, const std::string& game_id,
const std::string& game_ini_id) const; u16 revision) const;
void FilterApprovedGeckoCodes(std::vector<Gecko::GeckoCode>& codes, void FilterApprovedGeckoCodes(std::vector<Gecko::GeckoCode>& codes, const std::string& game_id,
const std::string& game_ini_id) const; u16 revision) const;
void FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes, void FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes, const std::string& game_id,
const std::string& game_ini_id) const; u16 revision) const;
bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code, const std::string& game_ini_id) const; bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code, const std::string& game_id,
bool CheckApprovedARCode(const ActionReplay::ARCode& code, const std::string& game_ini_id) const; u16 revision) const;
bool CheckApprovedARCode(const ActionReplay::ARCode& code, const std::string& game_id,
u16 revision) const;
void SetSpectatorMode(); void SetSpectatorMode();
std::string_view GetPlayerDisplayName() const; std::string_view GetPlayerDisplayName() const;
@ -201,9 +202,9 @@ private:
void SetHardcoreMode(); void SetHardcoreMode();
template <typename T> template <typename T>
void FilterApprovedIni(std::vector<T>& codes, const std::string& game_ini_id) const; void FilterApprovedIni(std::vector<T>& codes, const std::string& game_id, u16 revision) const;
template <typename T> template <typename T>
bool CheckApprovedCode(const T& code, const std::string& game_ini_id) const; bool CheckApprovedCode(const T& code, const std::string& game_id, u16 revision) const;
Common::SHA1::Digest GetCodeHash(const PatchEngine::Patch& patch) const; Common::SHA1::Digest GetCodeHash(const PatchEngine::Patch& patch) const;
Common::SHA1::Digest GetCodeHash(const Gecko::GeckoCode& code) const; Common::SHA1::Digest GetCodeHash(const Gecko::GeckoCode& code) const;
Common::SHA1::Digest GetCodeHash(const ActionReplay::ARCode& code) const; Common::SHA1::Digest GetCodeHash(const ActionReplay::ARCode& code) const;
@ -259,7 +260,6 @@ private:
std::chrono::steady_clock::time_point m_last_progress_message = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point m_last_progress_message = std::chrono::steady_clock::now();
Common::Lazy<picojson::value> m_ini_root{LoadApprovedList}; Common::Lazy<picojson::value> m_ini_root{LoadApprovedList};
std::string m_game_ini_id;
std::unordered_map<AchievementId, LeaderboardStatus> m_leaderboard_map; std::unordered_map<AchievementId, LeaderboardStatus> m_leaderboard_map;
bool m_challenges_updated = false; bool m_challenges_updated = false;
@ -302,14 +302,12 @@ public:
constexpr bool IsHardcoreModeActive() { return false; } constexpr bool IsHardcoreModeActive() { return false; }
constexpr bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code, constexpr bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code, const std::string& game_id)
const std::string& game_ini_id)
{ {
return true; return true;
}; };
constexpr bool CheckApprovedARCode(const ActionReplay::ARCode& code, constexpr bool CheckApprovedARCode(const ActionReplay::ARCode& code, const std::string& game_id)
const std::string& game_ini_id)
{ {
return true; return true;
}; };

View File

@ -113,7 +113,7 @@ struct ARAddr
// ---------------------- // ----------------------
// AR Remote Functions // AR Remote Functions
void ApplyCodes(std::span<const ARCode> codes, const std::string& game_id) void ApplyCodes(std::span<const ARCode> codes, const std::string& game_id, u16 revision)
{ {
if (!Config::AreCheatsEnabled()) if (!Config::AreCheatsEnabled())
return; return;
@ -122,9 +122,9 @@ void ApplyCodes(std::span<const ARCode> codes, const std::string& game_id)
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),
[&game_id](const ARCode& code) { [&game_id, &revision](const ARCode& code) {
return code.enabled && return code.enabled && AchievementManager::GetInstance().CheckApprovedARCode(
AchievementManager::GetInstance().CheckApprovedARCode(code, game_id); code, game_id, revision);
}); });
s_active_codes.shrink_to_fit(); s_active_codes.shrink_to_fit();
} }
@ -174,9 +174,9 @@ 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) const std::string& game_id, u16 revision)
{ {
ApplyCodes(LoadCodes(global_ini, local_ini), game_id); ApplyCodes(LoadCodes(global_ini, local_ini), game_id, revision);
} }
// Parses the Action Replay section of a game ini file. // Parses the Action Replay section of a game ini file.

View File

@ -45,13 +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, const std::string& game_id); void ApplyCodes(std::span<const ARCode> codes, const std::string& game_id, u16 revision);
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); const std::string& game_id, u16 revision);
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);

View File

@ -50,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, const std::string& game_id) void SetActiveCodes(std::span<const GeckoCode> gcodes, const std::string& game_id, u16 revision)
{ {
std::lock_guard lk(s_active_codes_lock); std::lock_guard lk(s_active_codes_lock);
@ -60,9 +60,9 @@ void SetActiveCodes(std::span<const GeckoCode> gcodes, const std::string& game_i
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),
[&game_id](const GeckoCode& code) { [&game_id, &revision](const GeckoCode& code) {
return code.enabled && return code.enabled && AchievementManager::GetInstance().CheckApprovedGeckoCode(
AchievementManager::GetInstance().CheckApprovedGeckoCode(code, game_id); code, game_id, revision);
}); });
} }
s_active_codes.shrink_to_fit(); s_active_codes.shrink_to_fit();

View File

@ -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, const std::string& game_id); void SetActiveCodes(std::span<const GeckoCode> gcodes, const std::string& game_id, u16 revision);
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);

View File

@ -2075,7 +2075,7 @@ bool NetPlayServer::SyncCodes()
std::vector<Gecko::GeckoCode> codes = Gecko::LoadCodes(globalIni, localIni); std::vector<Gecko::GeckoCode> codes = Gecko::LoadCodes(globalIni, localIni);
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance().FilterApprovedGeckoCodes(codes, game_id); AchievementManager::GetInstance().FilterApprovedGeckoCodes(codes, game_id, revision);
#endif // USE_RETRO_ACHIEVEMENTS #endif // USE_RETRO_ACHIEVEMENTS
// Create a Gecko Code Vector with just the active codes // Create a Gecko Code Vector with just the active codes
@ -2129,7 +2129,7 @@ bool NetPlayServer::SyncCodes()
{ {
std::vector<ActionReplay::ARCode> codes = ActionReplay::LoadCodes(globalIni, localIni); std::vector<ActionReplay::ARCode> codes = ActionReplay::LoadCodes(globalIni, localIni);
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance().FilterApprovedARCodes(codes, game_id); AchievementManager::GetInstance().FilterApprovedARCodes(codes, game_id, revision);
#endif // USE_RETRO_ACHIEVEMENTS #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> active_codes = ActionReplay::ApplyAndReturnCodes(codes); std::vector<ActionReplay::ARCode> active_codes = ActionReplay::ApplyAndReturnCodes(codes);

View File

@ -183,7 +183,8 @@ void LoadPatches()
LoadPatchSection("OnFrame", &s_on_frame, globalIni, localIni); LoadPatchSection("OnFrame", &s_on_frame, globalIni, localIni);
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance().FilterApprovedPatches(s_on_frame, sconfig.GetGameID()); AchievementManager::GetInstance().FilterApprovedPatches(s_on_frame, sconfig.GetGameID(),
sconfig.GetRevision());
#endif // USE_RETRO_ACHIEVEMENTS #endif // USE_RETRO_ACHIEVEMENTS
// Check if I'm syncing Codes // Check if I'm syncing Codes
@ -194,8 +195,10 @@ void LoadPatches()
} }
else else
{ {
Gecko::SetActiveCodes(Gecko::LoadCodes(globalIni, localIni), sconfig.GetGameID()); Gecko::SetActiveCodes(Gecko::LoadCodes(globalIni, localIni), sconfig.GetGameID(),
ActionReplay::LoadAndApplyCodes(globalIni, localIni, sconfig.GetGameID()); sconfig.GetRevision());
ActionReplay::LoadAndApplyCodes(globalIni, localIni, sconfig.GetGameID(),
sconfig.GetRevision());
} }
} }
@ -340,7 +343,7 @@ bool ApplyFramePatches(Core::System& system)
void Shutdown() void Shutdown()
{ {
s_on_frame.clear(); s_on_frame.clear();
ActionReplay::ApplyCodes({}, ""); ActionReplay::ApplyCodes({}, "", 0);
Gecko::Shutdown(); Gecko::Shutdown();
} }

View File

@ -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, m_game_id); ActionReplay::ApplyCodes(m_ar_codes, m_game_id, m_game_revision);
UpdateList(); UpdateList();
SaveCodes(); SaveCodes();

View File

@ -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, m_game_id); Gecko::SetActiveCodes(m_gecko_codes, m_game_id, m_game_revision);
SaveCodes(); SaveCodes();
} }