Skip to content

Commit cf29214

Browse files
authored
Merge pull request #12955 from LillyJadeKatrin/retroachievements-gecko
Add Support for Gecko Codes to Achievements Whitelist
2 parents c10809a + 51435b6 commit cf29214

File tree

16 files changed

+287
-91
lines changed

16 files changed

+287
-91
lines changed

Data/Sys/ApprovedInis.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@
160160
"title": "Gladius",
161161
"3D0894616C9A7FA5ED91C1D2F461BF14DF47ECEC": "Fix freeze in opening cutscene"
162162
},
163+
"GMSE01": {
164+
"title": "Super Mario Sunshine",
165+
"BD718F961DBA5372B1D0257D454D535746C453A0": "Widescreen"
166+
},
163167
"GNHE5d": {
164168
"title": "NHL HITZ 2002",
165169
"89393A24E2336841AA4CD0AD3BE1C9A66B89E9EF": "Nop Hack"

Data/Sys/GameSettings/GMSE01.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,6 @@ C2363138 00000009
175175
7C631670 54A5F0BE
176176
7C630194 7C630214
177177
60000000 00000000
178+
179+
[Gecko_RetroAchievements_Verified]
180+
$Widescreen

Source/Core/Core/AchievementManager.cpp

Lines changed: 112 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424
#include "Common/ScopeGuard.h"
2525
#include "Common/Version.h"
2626
#include "Common/WorkQueueThread.h"
27+
#include "Core/ActionReplay.h"
2728
#include "Core/Config/AchievementSettings.h"
2829
#include "Core/Config/FreeLookSettings.h"
2930
#include "Core/Config/MainSettings.h"
3031
#include "Core/Core.h"
32+
#include "Core/GeckoCode.h"
3133
#include "Core/HW/Memmap.h"
3234
#include "Core/HW/VideoInterface.h"
3335
#include "Core/PatchEngine.h"
@@ -370,7 +372,6 @@ void AchievementManager::SetHardcoreMode()
370372
if (Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f)
371373
Config::SetBaseOrCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
372374
Config::SetBaseOrCurrent(Config::FREE_LOOK_ENABLED, false);
373-
Config::SetBaseOrCurrent(Config::MAIN_ENABLE_CHEATS, false);
374375
}
375376
}
376377

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

387-
void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
388-
const std::string& game_ini_id) const
388+
template <typename T>
389+
void AchievementManager::FilterApprovedIni(std::vector<T>& codes,
390+
const std::string& game_ini_id) const
389391
{
390-
if (patches.empty())
392+
if (codes.empty())
391393
{
392394
// There's nothing to verify, so let's save ourselves some work
393395
return;
@@ -398,46 +400,120 @@ void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>&
398400
if (!IsHardcoreModeActive())
399401
return;
400402

403+
// Approved codes list failed to hash
404+
if (!m_ini_root->is<picojson::value::object>())
405+
{
406+
codes.clear();
407+
return;
408+
}
409+
410+
for (auto& code : codes)
411+
{
412+
if (code.enabled && !CheckApprovedCode(code, game_ini_id))
413+
code.enabled = false;
414+
}
415+
}
416+
417+
template <typename T>
418+
bool AchievementManager::CheckApprovedCode(const T& code, const std::string& game_ini_id) const
419+
{
420+
if (!IsHardcoreModeActive())
421+
return true;
422+
423+
// Approved codes list failed to hash
424+
if (!m_ini_root->is<picojson::value::object>())
425+
return false;
426+
401427
const bool known_id = m_ini_root->contains(game_ini_id);
402428

403-
auto patch_itr = patches.begin();
404-
while (patch_itr != patches.end())
429+
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying code {}", code.name);
430+
431+
bool verified = false;
432+
433+
if (known_id)
405434
{
406-
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying patch {}", patch_itr->name);
435+
auto digest = GetCodeHash(code);
407436

408-
bool verified = false;
437+
verified = m_ini_root->get(game_ini_id).contains(Common::SHA1::DigestToString(digest));
438+
}
409439

410-
if (known_id)
411-
{
412-
auto context = Common::SHA1::CreateContext();
413-
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(patch_itr->entries.size())));
414-
for (const auto& entry : patch_itr->entries)
415-
{
416-
context->Update(Common::BitCastToArray<u8>(entry.type));
417-
context->Update(Common::BitCastToArray<u8>(entry.address));
418-
context->Update(Common::BitCastToArray<u8>(entry.value));
419-
context->Update(Common::BitCastToArray<u8>(entry.comparand));
420-
context->Update(Common::BitCastToArray<u8>(entry.conditional));
421-
}
422-
auto digest = context->Finish();
440+
if (!verified)
441+
{
442+
OSD::AddMessage(fmt::format("Failed to verify code {} from file {}.", code.name, game_ini_id),
443+
OSD::Duration::VERY_LONG, OSD::Color::RED);
444+
OSD::AddMessage("Disable hardcore mode to enable this code.", OSD::Duration::VERY_LONG,
445+
OSD::Color::RED);
446+
}
447+
return verified;
448+
}
423449

424-
verified = m_ini_root->get(game_ini_id).contains(Common::SHA1::DigestToString(digest));
425-
}
450+
Common::SHA1::Digest AchievementManager::GetCodeHash(const PatchEngine::Patch& patch) const
451+
{
452+
auto context = Common::SHA1::CreateContext();
453+
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(patch.entries.size())));
454+
for (const auto& entry : patch.entries)
455+
{
456+
context->Update(Common::BitCastToArray<u8>(entry.type));
457+
context->Update(Common::BitCastToArray<u8>(entry.address));
458+
context->Update(Common::BitCastToArray<u8>(entry.value));
459+
context->Update(Common::BitCastToArray<u8>(entry.comparand));
460+
context->Update(Common::BitCastToArray<u8>(entry.conditional));
461+
}
462+
return context->Finish();
463+
}
426464

427-
if (!verified)
428-
{
429-
patch_itr = patches.erase(patch_itr);
430-
OSD::AddMessage(
431-
fmt::format("Failed to verify patch {} from file {}.", patch_itr->name, game_ini_id),
432-
OSD::Duration::VERY_LONG, OSD::Color::RED);
433-
OSD::AddMessage("Disable hardcore mode to enable this patch.", OSD::Duration::VERY_LONG,
434-
OSD::Color::RED);
435-
}
436-
else
437-
{
438-
patch_itr++;
439-
}
465+
Common::SHA1::Digest AchievementManager::GetCodeHash(const Gecko::GeckoCode& code) const
466+
{
467+
auto context = Common::SHA1::CreateContext();
468+
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(code.codes.size())));
469+
for (const auto& entry : code.codes)
470+
{
471+
context->Update(Common::BitCastToArray<u8>(entry.address));
472+
context->Update(Common::BitCastToArray<u8>(entry.data));
440473
}
474+
return context->Finish();
475+
}
476+
477+
Common::SHA1::Digest AchievementManager::GetCodeHash(const ActionReplay::ARCode& code) const
478+
{
479+
auto context = Common::SHA1::CreateContext();
480+
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(code.ops.size())));
481+
for (const auto& entry : code.ops)
482+
{
483+
context->Update(Common::BitCastToArray<u8>(entry.cmd_addr));
484+
context->Update(Common::BitCastToArray<u8>(entry.value));
485+
}
486+
return context->Finish();
487+
}
488+
489+
void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
490+
const std::string& game_ini_id) const
491+
{
492+
FilterApprovedIni(patches, game_ini_id);
493+
}
494+
495+
void AchievementManager::FilterApprovedGeckoCodes(std::vector<Gecko::GeckoCode>& codes,
496+
const std::string& game_ini_id) const
497+
{
498+
FilterApprovedIni(codes, game_ini_id);
499+
}
500+
501+
void AchievementManager::FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes,
502+
const std::string& game_ini_id) const
503+
{
504+
FilterApprovedIni(codes, game_ini_id);
505+
}
506+
507+
bool AchievementManager::CheckApprovedGeckoCode(const Gecko::GeckoCode& code,
508+
const std::string& game_ini_id) const
509+
{
510+
return CheckApprovedCode(code, game_ini_id);
511+
}
512+
513+
bool AchievementManager::CheckApprovedARCode(const ActionReplay::ARCode& code,
514+
const std::string& game_ini_id) const
515+
{
516+
return CheckApprovedCode(code, game_ini_id);
441517
}
442518

443519
void AchievementManager::SetSpectatorMode()

Source/Core/Core/AchievementManager.h

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ namespace PatchEngine
4545
struct Patch;
4646
} // namespace PatchEngine
4747

48+
namespace Gecko
49+
{
50+
class GeckoCode;
51+
} // namespace Gecko
52+
53+
namespace ActionReplay
54+
{
55+
struct ARCode;
56+
} // namespace ActionReplay
57+
4858
class AchievementManager
4959
{
5060
public:
@@ -70,8 +80,8 @@ class AchievementManager
7080
static constexpr std::string_view BLUE = "#0B71C1";
7181
static constexpr std::string_view APPROVED_LIST_FILENAME = "ApprovedInis.json";
7282
static const inline Common::SHA1::Digest APPROVED_LIST_HASH = {
73-
0xCC, 0xB4, 0x05, 0x2D, 0x2B, 0xEE, 0xF4, 0x06, 0x4A, 0xC9,
74-
0x57, 0x5D, 0xA9, 0xE9, 0xDE, 0xB7, 0x98, 0xF8, 0x1A, 0x6D};
83+
0xA4, 0x98, 0x59, 0x23, 0x10, 0x56, 0x45, 0x30, 0xA9, 0xC5,
84+
0x68, 0x5A, 0xB6, 0x47, 0x67, 0xF8, 0xF0, 0x7D, 0x1D, 0x14};
7585

7686
struct LeaderboardEntry
7787
{
@@ -125,8 +135,16 @@ class AchievementManager
125135
void SetHardcoreMode();
126136
bool IsHardcoreModeActive() const;
127137
void SetGameIniId(const std::string& game_ini_id) { m_game_ini_id = game_ini_id; }
138+
128139
void FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
129140
const std::string& game_ini_id) const;
141+
void FilterApprovedGeckoCodes(std::vector<Gecko::GeckoCode>& codes,
142+
const std::string& game_ini_id) const;
143+
void FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes,
144+
const std::string& game_ini_id) const;
145+
bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code, const std::string& game_ini_id) const;
146+
bool CheckApprovedARCode(const ActionReplay::ARCode& code, const std::string& game_ini_id) const;
147+
130148
void SetSpectatorMode();
131149
std::string_view GetPlayerDisplayName() const;
132150
u32 GetPlayerScore() const;
@@ -181,6 +199,14 @@ class AchievementManager
181199
void* userdata);
182200
void DisplayWelcomeMessage();
183201

202+
template <typename T>
203+
void FilterApprovedIni(std::vector<T>& codes, const std::string& game_ini_id) const;
204+
template <typename T>
205+
bool CheckApprovedCode(const T& code, const std::string& game_ini_id) const;
206+
Common::SHA1::Digest GetCodeHash(const PatchEngine::Patch& patch) const;
207+
Common::SHA1::Digest GetCodeHash(const Gecko::GeckoCode& code) const;
208+
Common::SHA1::Digest GetCodeHash(const ActionReplay::ARCode& code) const;
209+
184210
static void LeaderboardEntriesCallback(int result, const char* error_message,
185211
rc_client_leaderboard_entry_list_t* list,
186212
rc_client_t* client, void* userdata);
@@ -265,6 +291,18 @@ class AchievementManager
265291

266292
constexpr bool IsHardcoreModeActive() { return false; }
267293

294+
constexpr bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code,
295+
const std::string& game_ini_id)
296+
{
297+
return true;
298+
};
299+
300+
constexpr bool CheckApprovedARCode(const ActionReplay::ARCode& code,
301+
const std::string& game_ini_id)
302+
{
303+
return true;
304+
};
305+
268306
constexpr void LoadGame(const std::string&, const DiscIO::Volume*) {}
269307

270308
constexpr void SetBackgroundExecutionAllowed(bool allowed) {}

Source/Core/Core/ActionReplay.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "Common/MsgHandler.h"
4040

4141
#include "Core/ARDecrypt.h"
42+
#include "Core/AchievementManager.h"
4243
#include "Core/CheatCodes.h"
4344
#include "Core/Config/MainSettings.h"
4445
#include "Core/PowerPC/MMU.h"
@@ -112,7 +113,7 @@ struct ARAddr
112113

113114
// ----------------------
114115
// AR Remote Functions
115-
void ApplyCodes(std::span<const ARCode> codes)
116+
void ApplyCodes(std::span<const ARCode> codes, const std::string& game_id)
116117
{
117118
if (!Config::AreCheatsEnabled())
118119
return;
@@ -121,7 +122,10 @@ void ApplyCodes(std::span<const ARCode> codes)
121122
s_disable_logging = false;
122123
s_active_codes.clear();
123124
std::copy_if(codes.begin(), codes.end(), std::back_inserter(s_active_codes),
124-
[](const ARCode& code) { return code.enabled; });
125+
[&game_id](const ARCode& code) {
126+
return code.enabled &&
127+
AchievementManager::GetInstance().CheckApprovedARCode(code, game_id);
128+
});
125129
s_active_codes.shrink_to_fit();
126130
}
127131

@@ -169,9 +173,10 @@ void AddCode(ARCode code)
169173
}
170174
}
171175

172-
void LoadAndApplyCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini)
176+
void LoadAndApplyCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini,
177+
const std::string& game_id)
173178
{
174-
ApplyCodes(LoadCodes(global_ini, local_ini));
179+
ApplyCodes(LoadCodes(global_ini, local_ini), game_id);
175180
}
176181

177182
// Parses the Action Replay section of a game ini file.

Source/Core/Core/ActionReplay.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,13 @@ struct ARCode
4545

4646
void RunAllActive(const Core::CPUThreadGuard& cpu_guard);
4747

48-
void ApplyCodes(std::span<const ARCode> codes);
48+
void ApplyCodes(std::span<const ARCode> codes, const std::string& game_id);
4949
void SetSyncedCodesAsActive();
5050
void UpdateSyncedCodes(std::span<const ARCode> codes);
5151
std::vector<ARCode> ApplyAndReturnCodes(std::span<const ARCode> codes);
5252
void AddCode(ARCode new_code);
53-
void LoadAndApplyCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini);
53+
void LoadAndApplyCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini,
54+
const std::string& game_id);
5455

5556
std::vector<ARCode> LoadCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini);
5657
void SaveCodes(Common::IniFile* local_ini, std::span<const ARCode> codes);

Source/Core/Core/Config/MainSettings.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -751,8 +751,7 @@ bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot)
751751

752752
bool AreCheatsEnabled()
753753
{
754-
return Config::Get(::Config::MAIN_ENABLE_CHEATS) &&
755-
!AchievementManager::GetInstance().IsHardcoreModeActive();
754+
return Config::Get(::Config::MAIN_ENABLE_CHEATS);
756755
}
757756

758757
bool IsDebuggingEnabled()

Source/Core/Core/GeckoCode.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "Common/Config/Config.h"
1616
#include "Common/FileUtil.h"
1717

18+
#include "Core/AchievementManager.h"
1819
#include "Core/Config/MainSettings.h"
1920
#include "Core/Core.h"
2021
#include "Core/Host.h"
@@ -49,16 +50,20 @@ static std::vector<GeckoCode> s_active_codes;
4950
static std::vector<GeckoCode> s_synced_codes;
5051
static std::mutex s_active_codes_lock;
5152

52-
void SetActiveCodes(std::span<const GeckoCode> gcodes)
53+
void SetActiveCodes(std::span<const GeckoCode> gcodes, const std::string& game_id)
5354
{
5455
std::lock_guard lk(s_active_codes_lock);
5556

5657
s_active_codes.clear();
5758
if (Config::AreCheatsEnabled())
5859
{
5960
s_active_codes.reserve(gcodes.size());
61+
6062
std::copy_if(gcodes.begin(), gcodes.end(), std::back_inserter(s_active_codes),
61-
[](const GeckoCode& code) { return code.enabled; });
63+
[&game_id](const GeckoCode& code) {
64+
return code.enabled &&
65+
AchievementManager::GetInstance().CheckApprovedGeckoCode(code, game_id);
66+
});
6267
}
6368
s_active_codes.shrink_to_fit();
6469

Source/Core/Core/GeckoCode.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ constexpr u32 HLE_TRAMPOLINE_ADDRESS = INSTALLER_END_ADDRESS - 4;
6060
// preserve the emulation performance.
6161
constexpr u32 MAGIC_GAMEID = 0xD01F1BAD;
6262

63-
void SetActiveCodes(std::span<const GeckoCode> gcodes);
63+
void SetActiveCodes(std::span<const GeckoCode> gcodes, const std::string& game_id);
6464
void SetSyncedCodesAsActive();
6565
void UpdateSyncedCodes(std::span<const GeckoCode> gcodes);
6666
std::vector<GeckoCode> SetAndReturnActiveCodes(std::span<const GeckoCode> gcodes);

0 commit comments

Comments
 (0)