mirror of
https://github.com/dolphin-emu/dolphin
synced 2024-06-28 22:46:42 +00:00
Compare commits
2 Commits
f64416ae93
...
c6c80cbeba
Author | SHA1 | Date | |
---|---|---|---|
|
c6c80cbeba | ||
|
6ae420b7da |
13
Data/Sys/ApprovedInis.txt
Normal file
13
Data/Sys/ApprovedInis.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
# The purpose of this file is to provide a list of hashes for approved ini files for RetroAchievements use.
|
||||
# Files approved in this list may be used; if any hash is incorrect then it has been modified by the
|
||||
# player and risks being used for cheating.
|
||||
# When editing any ini file in Sys/GameSettings, check if that file is here. If it is, and the changes you
|
||||
# have made do not modify gameplay in a cheating fashion, please recalculate the SHA1 of that file and
|
||||
# update it in this list. Then, recalculate the SHA1 of *this* file and update it in AchievementManager.h.
|
||||
|
||||
GCCE01 START
|
||||
# Fix buffer overrun bug (crash at Goblin Wall)
|
||||
6C107FEC15C76201233CA2645EB5FAB4FF9751CE
|
||||
# Fix GBA connections
|
||||
483BDB94615C690045C3759795AF13CE76552286
|
||||
GCCE01 END
|
|
@ -1,5 +1,8 @@
|
|||
# GCCE01 - FINAL FANTASY Crystal Chronicles
|
||||
|
||||
# This patch is approved for RetroAchievements use. Please see
|
||||
# Sys/ApprovedInis.txt for more information.
|
||||
|
||||
[OnFrame]
|
||||
# Fix incorrect bounds check before an EFB to RAM copy that causes buffer overruns.
|
||||
# With this patch enabled, it is safe to set EFBToTextureEnable = True.
|
||||
|
|
|
@ -14,16 +14,21 @@
|
|||
#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"
|
||||
#include "Common/Image.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/Version.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "Core/ActionReplay.h"
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/GeckoCode.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/PatchEngine.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
#include "Core/System.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
|
@ -262,6 +267,130 @@ bool AchievementManager::IsHardcoreModeActive() const
|
|||
return rc_client_is_processing_required(m_client);
|
||||
}
|
||||
|
||||
bool AchievementManager::VerifyPatchHash(const Common::SHA1::Digest& digest) const
|
||||
{
|
||||
File::IOFile list_file;
|
||||
if (!list_file.Open(
|
||||
fmt::format("{}{}{}", File::GetSysDirectory(), DIR_SEP, APPROVED_LIST_FILENAME), "rb"))
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Failed to open approved patch hash list. Disabling all patches.");
|
||||
return false;
|
||||
}
|
||||
std::vector<u8> buffer(list_file.GetSize());
|
||||
if (!list_file.ReadBytes(buffer.data(), list_file.GetSize()))
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Failed to read approved patch hash list. Disabling all patches.");
|
||||
return false;
|
||||
}
|
||||
list_file.Close();
|
||||
|
||||
auto context = Common::SHA1::CreateContext();
|
||||
context->Update(buffer);
|
||||
auto list_hash = context->Finish();
|
||||
|
||||
if (list_hash != APPROVED_LIST_HASH)
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Approved list hash does not match expected hash");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hash;
|
||||
for (size_t ix = 0; ix < digest.size(); ix++)
|
||||
{
|
||||
u8 upper = digest[ix] / 16;
|
||||
hash.append(1, upper + ((upper > 9) ? 'A' - 10 : '0'));
|
||||
u8 lower = digest[ix] % 16;
|
||||
hash.append(1, lower + ((lower > 9) ? 'A' - 10 : '0'));
|
||||
}
|
||||
|
||||
auto section_begin_itr =
|
||||
std::search(buffer.begin(), buffer.end(), m_game_ini_id.begin(), m_game_ini_id.end());
|
||||
auto section_end_itr =
|
||||
std::search(section_begin_itr + 1, buffer.end(), m_game_ini_id.begin(), m_game_ini_id.end());
|
||||
auto hash_itr = std::search(section_begin_itr, section_end_itr, hash.begin(), hash.end());
|
||||
|
||||
if (hash_itr == section_end_itr)
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Ini hash {} from file {} not found in approved list", hash,
|
||||
m_game_ini_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <size_t length>
|
||||
void UpdateFromArray(Common::SHA1::Context& context, const std::array<u8, length>& data)
|
||||
{
|
||||
context.Update(data.data(), data.size());
|
||||
}
|
||||
|
||||
bool AchievementManager::IsPatchApproved(const PatchEngine::Patch& patch) const
|
||||
{
|
||||
if (!IsHardcoreModeActive())
|
||||
return true;
|
||||
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying patch {}", patch.name);
|
||||
auto context = Common::SHA1::CreateContext();
|
||||
UpdateFromArray(*context, Common::BitCastToArray<u8>(static_cast<u64>(patch.entries.size())));
|
||||
for (const auto& entry : patch.entries)
|
||||
{
|
||||
UpdateFromArray(*context, Common::BitCastToArray<u8>(entry.type));
|
||||
UpdateFromArray(*context, Common::BitCastToArray<u8>(entry.address));
|
||||
UpdateFromArray(*context, Common::BitCastToArray<u8>(entry.value));
|
||||
UpdateFromArray(*context, Common::BitCastToArray<u8>(entry.comparand));
|
||||
UpdateFromArray(*context, Common::BitCastToArray<u8>(entry.conditional));
|
||||
}
|
||||
auto digest = context->Finish();
|
||||
|
||||
bool verified = VerifyPatchHash(digest);
|
||||
if (!verified)
|
||||
{
|
||||
OSD::AddMessage(
|
||||
fmt::format("Failed to verify patch {} from file {}.", patch.name, m_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);
|
||||
}
|
||||
return verified;
|
||||
}
|
||||
|
||||
bool AchievementManager::IsGeckoCodeApproved(const Gecko::GeckoCode& code) const
|
||||
{
|
||||
if (!IsHardcoreModeActive())
|
||||
return true;
|
||||
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying gecko code {}", code.name);
|
||||
auto context = Common::SHA1::CreateContext();
|
||||
UpdateFromArray(*context, Common::BitCastToArray<u8>(static_cast<u64>(code.codes.size())));
|
||||
for (const auto& entry : code.codes)
|
||||
{
|
||||
UpdateFromArray(*context, Common::BitCastToArray<u8>(entry.address));
|
||||
UpdateFromArray(*context, Common::BitCastToArray<u8>(entry.data));
|
||||
}
|
||||
auto digest = context->Finish();
|
||||
|
||||
return VerifyPatchHash(digest);
|
||||
}
|
||||
|
||||
bool AchievementManager::IsARCodeApproved(const ActionReplay::ARCode& code) const
|
||||
{
|
||||
if (!IsHardcoreModeActive())
|
||||
return true;
|
||||
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying AR code {}", code.name);
|
||||
auto context = Common::SHA1::CreateContext();
|
||||
UpdateFromArray(*context, Common::BitCastToArray<u8>(static_cast<u64>(code.ops.size())));
|
||||
for (const auto& entry : code.ops)
|
||||
{
|
||||
UpdateFromArray(*context, Common::BitCastToArray<u8>(entry.cmd_addr));
|
||||
UpdateFromArray(*context, Common::BitCastToArray<u8>(entry.value));
|
||||
}
|
||||
auto digest = context->Finish();
|
||||
|
||||
return VerifyPatchHash(digest);
|
||||
}
|
||||
|
||||
void AchievementManager::SetSpectatorMode()
|
||||
{
|
||||
rc_client_set_spectator_mode_enabled(m_client, Config::Get(Config::RA_SPECTATOR_ENABLED));
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
#include "Common/Event.h"
|
||||
#include "Common/HttpRequest.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "Core/ActionReplay.h"
|
||||
#include "Core/GeckoCode.h"
|
||||
#include "Core/PatchEngine.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
#include "VideoCommon/Assets/CustomTextureData.h"
|
||||
|
||||
|
@ -60,6 +63,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.txt";
|
||||
static const inline Common::SHA1::Digest APPROVED_LIST_HASH = {
|
||||
0xA1, 0x1D, 0xDE, 0x49, 0x87, 0xE1, 0x86, 0xD0, 0x59, 0x40,
|
||||
0xFB, 0xF5, 0x98, 0x39, 0x54, 0xD6, 0x2A, 0xF1, 0x33, 0xD6};
|
||||
|
||||
struct LeaderboardEntry
|
||||
{
|
||||
|
@ -105,6 +112,10 @@ 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; }
|
||||
bool IsPatchApproved(const PatchEngine::Patch& patch) const;
|
||||
bool IsGeckoCodeApproved(const Gecko::GeckoCode& gecko_code) const;
|
||||
bool IsARCodeApproved(const ActionReplay::ARCode& ar_code) const;
|
||||
void SetSpectatorMode();
|
||||
std::string_view GetPlayerDisplayName() const;
|
||||
u32 GetPlayerScore() const;
|
||||
|
@ -161,6 +172,8 @@ private:
|
|||
rc_client_leaderboard_entry_list_t* list,
|
||||
rc_client_t* client, void* userdata);
|
||||
|
||||
bool VerifyPatchHash(const Common::SHA1::Digest& digest) const;
|
||||
|
||||
static void HandleAchievementTriggeredEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardStartedEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardFailedEvent(const rc_client_event_t* client_event);
|
||||
|
@ -206,6 +219,8 @@ 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();
|
||||
|
||||
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;
|
||||
|
@ -237,6 +252,10 @@ public:
|
|||
|
||||
constexpr bool IsHardcoreModeActive() { return false; }
|
||||
|
||||
bool IsPatchApproved(const PatchEngine::Patch& patch) const { return true; }
|
||||
bool IsGeckoCodeApproved(const Gecko::GeckoCode& gecko_code) const { return true; }
|
||||
bool IsARCodeApproved(const ActionReplay::ARCode& ar_code) const { return true; }
|
||||
|
||||
constexpr void LoadGame(const std::string&, const DiscIO::Volume*) {}
|
||||
|
||||
constexpr void DoFrame() {}
|
||||
|
|
|
@ -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"
|
||||
|
@ -199,13 +200,15 @@ std::vector<ARCode> LoadCodes(const Common::IniFile& global_ini, const Common::I
|
|||
{
|
||||
if (!current_code.ops.empty())
|
||||
{
|
||||
codes.push_back(current_code);
|
||||
if (AchievementManager::GetInstance().IsARCodeApproved(current_code))
|
||||
codes.push_back(current_code);
|
||||
current_code.ops.clear();
|
||||
}
|
||||
if (!encrypted_lines.empty())
|
||||
{
|
||||
DecryptARCode(encrypted_lines, ¤t_code.ops);
|
||||
codes.push_back(current_code);
|
||||
if (AchievementManager::GetInstance().IsARCodeApproved(current_code))
|
||||
codes.push_back(current_code);
|
||||
current_code.ops.clear();
|
||||
encrypted_lines.clear();
|
||||
}
|
||||
|
@ -227,7 +230,8 @@ std::vector<ARCode> LoadCodes(const Common::IniFile& global_ini, const Common::I
|
|||
}
|
||||
|
||||
// Handle the last code correctly.
|
||||
if (!current_code.ops.empty())
|
||||
if (!current_code.ops.empty() &&
|
||||
AchievementManager::GetInstance().IsARCodeApproved(current_code))
|
||||
{
|
||||
codes.push_back(current_code);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "Common/IniFile.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/CheatCodes.h"
|
||||
|
||||
namespace Gecko
|
||||
|
@ -155,7 +156,7 @@ std::vector<GeckoCode> LoadCodes(const Common::IniFile& globalIni, const Common:
|
|||
ss.seekg(1);
|
||||
[[fallthrough]];
|
||||
case '$':
|
||||
if (!gcode.name.empty())
|
||||
if (!gcode.name.empty() && AchievementManager::GetInstance().IsGeckoCodeApproved(gcode))
|
||||
gcodes.push_back(gcode);
|
||||
gcode = GeckoCode();
|
||||
gcode.enabled = (1 == ss.tellg()); // silly
|
||||
|
@ -189,7 +190,7 @@ std::vector<GeckoCode> LoadCodes(const Common::IniFile& globalIni, const Common:
|
|||
}
|
||||
|
||||
// add the last code
|
||||
if (!gcode.name.empty())
|
||||
if (!gcode.name.empty() && AchievementManager::GetInstance().IsGeckoCodeApproved(gcode))
|
||||
{
|
||||
gcodes.push_back(gcode);
|
||||
}
|
||||
|
|
|
@ -118,7 +118,8 @@ void LoadPatchSection(const std::string& section, std::vector<Patch>* patches,
|
|||
if (line[0] == '$')
|
||||
{
|
||||
// Take care of the previous code
|
||||
if (!currentPatch.name.empty())
|
||||
if (!currentPatch.name.empty() &&
|
||||
AchievementManager::GetInstance().IsPatchApproved(currentPatch))
|
||||
{
|
||||
patches->push_back(currentPatch);
|
||||
}
|
||||
|
@ -135,7 +136,8 @@ void LoadPatchSection(const std::string& section, std::vector<Patch>* patches,
|
|||
}
|
||||
}
|
||||
|
||||
if (!currentPatch.name.empty() && !currentPatch.entries.empty())
|
||||
if (!currentPatch.name.empty() && !currentPatch.entries.empty() &&
|
||||
AchievementManager::GetInstance().IsPatchApproved(currentPatch))
|
||||
{
|
||||
patches->push_back(currentPatch);
|
||||
}
|
||||
|
@ -214,6 +216,11 @@ void LoadPatches()
|
|||
Common::IniFile globalIni = sconfig.LoadDefaultGameIni();
|
||||
Common::IniFile localIni = sconfig.LoadLocalGameIni();
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
AchievementManager::GetInstance().SetGameIniId(sconfig.GetGameID());
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
LoadPatchSection("OnFrame", &s_on_frame, globalIni, localIni);
|
||||
|
||||
// Check if I'm syncing Codes
|
||||
|
@ -233,9 +240,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)
|
||||
|
@ -277,9 +281,6 @@ static void ApplyPatches(const Core::CPUThreadGuard& guard, const std::vector<Pa
|
|||
static void ApplyMemoryPatches(const Core::CPUThreadGuard& guard,
|
||||
std::span<const std::size_t> memory_patch_indices)
|
||||
{
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
std::lock_guard lock(s_on_frame_memory_mutex);
|
||||
for (std::size_t index : memory_patch_indices)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue
Block a user