summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorAdmiral H. Curtiss <pikachu025@gmail.com>2024-07-07 23:48:09 +0200
committerGitHub <noreply@github.com>2024-07-07 23:48:09 +0200
commitb10d62cf66ac770d598b0d208338c43b73a67caa (patch)
treeff37b6cf24660e2cca4ebf2e70d2c0f9e7bbeac4 /Source/Core
parent02e1b9414909db1644562b3669967feff203460f (diff)
parentae87bf9af5d6a09a380949b47f2d7c484cea7d01 (diff)
Merge pull request #12913 from LillyJadeKatrin/retroachievements-allowlist-test
RetroAchievements - Patch Allowlist Unit Test
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Common/FileUtil.cpp13
-rw-r--r--Source/Core/Core/AchievementManager.cpp72
-rw-r--r--Source/Core/Core/AchievementManager.h20
-rw-r--r--Source/Core/Core/PatchEngine.cpp10
4 files changed, 111 insertions, 4 deletions
diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp
index 5582018b30..545bfaba44 100644
--- a/Source/Core/Common/FileUtil.cpp
+++ b/Source/Core/Common/FileUtil.cpp
@@ -60,6 +60,10 @@
#include "jni/AndroidCommon/AndroidCommon.h"
#endif
+#if defined(__FreeBSD__)
+#include <sys/sysctl.h>
+#endif
+
namespace fs = std::filesystem;
namespace File
@@ -738,6 +742,15 @@ std::string GetExePath()
return PathToString(exe_path_absolute);
#elif defined(__APPLE__)
return GetBundleDirectory();
+#elif defined(__FreeBSD__)
+ int name[4]{CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+ size_t length = 0;
+ if (sysctl(name, 4, nullptr, &length, nullptr, 0) != 0 || length == 0)
+ return {};
+ std::string dolphin_exe_path(length, '\0');
+ if (sysctl(name, 4, dolphin_exe_path.data(), &length, nullptr, 0) != 0)
+ return {};
+ return dolphin_exe_path;
#else
char dolphin_exe_path[PATH_MAX];
ssize_t len = ::readlink("/proc/self/exe", dolphin_exe_path, sizeof(dolphin_exe_path));
diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp
index 544ec36afc..788a394be4 100644
--- a/Source/Core/Core/AchievementManager.cpp
+++ b/Source/Core/Core/AchievementManager.cpp
@@ -14,6 +14,7 @@
#include <rcheevos/include/rc_hash.h>
#include "Common/Assert.h"
+#include "Common/BitUtils.h"
#include "Common/CommonPaths.h"
#include "Common/FileUtil.h"
#include "Common/IOFile.h"
@@ -26,6 +27,7 @@
#include "Core/Core.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/VideoInterface.h"
+#include "Core/PatchEngine.h"
#include "Core/PowerPC/MMU.h"
#include "Core/System.h"
#include "DiscIO/Blob.h"
@@ -70,6 +72,34 @@ void AchievementManager::Init()
}
}
+void AchievementManager::LoadApprovedList()
+{
+ picojson::value temp;
+ std::string error;
+ if (!JsonFromFile(fmt::format("{}{}{}", File::GetSysDirectory(), DIR_SEP, APPROVED_LIST_FILENAME),
+ &temp, &error))
+ {
+ WARN_LOG_FMT(ACHIEVEMENTS, "Failed to load approved game settings list {}",
+ APPROVED_LIST_FILENAME);
+ WARN_LOG_FMT(ACHIEVEMENTS, "Error: {}", error);
+ return;
+ }
+ auto context = Common::SHA1::CreateContext();
+ context->Update(temp.serialize());
+ auto digest = context->Finish();
+ if (digest != APPROVED_LIST_HASH)
+ {
+ WARN_LOG_FMT(ACHIEVEMENTS, "Failed to verify approved game settings list {}",
+ APPROVED_LIST_FILENAME);
+ WARN_LOG_FMT(ACHIEVEMENTS, "Expected hash {}, found hash {}",
+ Common::SHA1::DigestToString(APPROVED_LIST_HASH),
+ Common::SHA1::DigestToString(digest));
+ return;
+ }
+ std::lock_guard lg{m_lock};
+ m_ini_root = std::move(temp);
+}
+
void AchievementManager::SetUpdateCallback(UpdateCallback callback)
{
m_update_callback = std::move(callback);
@@ -322,6 +352,48 @@ 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
+{
+ if (!IsHardcoreModeActive())
+ return;
+
+ if (!m_ini_root.contains(game_ini_id))
+ patches.clear();
+ auto patch_itr = patches.begin();
+ while (patch_itr != patches.end())
+ {
+ INFO_LOG_FMT(ACHIEVEMENTS, "Verifying patch {}", patch_itr->name);
+
+ 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();
+
+ bool 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++;
+ }
+ }
+}
+
void AchievementManager::SetSpectatorMode()
{
rc_client_set_spectator_mode_enabled(m_client, Config::Get(Config::RA_SPECTATOR_ENABLED));
diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h
index 3996003040..efddd003a9 100644
--- a/Source/Core/Core/AchievementManager.h
+++ b/Source/Core/Core/AchievementManager.h
@@ -27,6 +27,7 @@
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/HttpRequest.h"
+#include "Common/JsonUtil.h"
#include "Common/WorkQueueThread.h"
#include "DiscIO/Volume.h"
#include "VideoCommon/Assets/CustomTextureData.h"
@@ -37,6 +38,11 @@ class CPUThreadGuard;
class System;
} // namespace Core
+namespace PatchEngine
+{
+struct Patch;
+} // namespace PatchEngine
+
class AchievementManager
{
public:
@@ -60,6 +66,10 @@ public:
static constexpr std::string_view GRAY = "transparent";
static constexpr std::string_view GOLD = "#FFD700";
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 = {
+ 0x01, 0x1E, 0x2E, 0x74, 0xDD, 0x07, 0x79, 0xDA, 0x0E, 0x5D,
+ 0xF8, 0x51, 0x09, 0xC7, 0x9B, 0x46, 0x22, 0x95, 0x50, 0xE9};
struct LeaderboardEntry
{
@@ -109,6 +119,9 @@ public:
std::recursive_mutex& GetLock();
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 SetSpectatorMode();
std::string_view GetPlayerDisplayName() const;
u32 GetPlayerScore() const;
@@ -132,7 +145,7 @@ public:
void Shutdown();
private:
- AchievementManager() = default;
+ AchievementManager() { LoadApprovedList(); };
struct FilereaderState
{
@@ -140,6 +153,8 @@ private:
std::unique_ptr<DiscIO::Volume> volume;
};
+ void LoadApprovedList();
+
static void* FilereaderOpenByFilepath(const char* path_utf8);
static void* FilereaderOpenByVolume(const char* path_utf8);
static void FilereaderSeek(void* file_handle, int64_t offset, int origin);
@@ -211,6 +226,9 @@ private:
std::chrono::steady_clock::time_point m_last_rp_time = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point m_last_progress_message = std::chrono::steady_clock::now();
+ picojson::value m_ini_root;
+ std::string m_game_ini_id;
+
std::unordered_map<AchievementId, LeaderboardStatus> m_leaderboard_map;
bool m_challenges_updated = false;
std::unordered_set<AchievementId> m_active_challenges;
diff --git a/Source/Core/Core/PatchEngine.cpp b/Source/Core/Core/PatchEngine.cpp
index f020c754eb..0728637e47 100644
--- a/Source/Core/Core/PatchEngine.cpp
+++ b/Source/Core/Core/PatchEngine.cpp
@@ -182,6 +182,13 @@ void LoadPatches()
LoadPatchSection("OnFrame", &s_on_frame, globalIni, localIni);
+#ifdef USE_RETRO_ACHIEVEMENTS
+ {
+ std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
+ AchievementManager::GetInstance().FilterApprovedPatches(s_on_frame, sconfig.GetGameID());
+ }
+#endif // USE_RETRO_ACHIEVEMENTS
+
// Check if I'm syncing Codes
if (Config::Get(Config::SESSION_CODE_SYNC_OVERRIDE))
{
@@ -197,9 +204,6 @@ void LoadPatches()
static void ApplyPatches(const Core::CPUThreadGuard& guard, const std::vector<Patch>& patches)
{
- if (AchievementManager::GetInstance().IsHardcoreModeActive())
- return;
-
for (const Patch& patch : patches)
{
if (patch.enabled)