mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-12-16 04:09:39 +00:00
Refactors the PatchAllowlistTest to streamline the experience for developers. Instead of a textual description of what needs to change in ApprovedInis.json for RetroAchievements compatibility, the test will now generate a replacement file and instruct the coder where to copy it in their local branch, and what to update APPROVED_LIST_HASH to. The result should be easier and more instructive for developers to make changes, while still maintaining that allowed codes cannot be added or modified without recompiling Dolphin. As ApprovedInis.json no longer needs to be user-readable for this process, it no longer contains titles or pretty formatting and as such is updated in this commit, hash included.
374 lines
13 KiB
C++
374 lines
13 KiB
C++
// Copyright 2023 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <ctime>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <set>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <thread>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include <rcheevos/include/rc_api_runtime.h>
|
|
#include <rcheevos/include/rc_api_user.h>
|
|
#include <rcheevos/include/rc_client.h>
|
|
#include <rcheevos/include/rc_hash.h>
|
|
#include <rcheevos/include/rc_runtime.h>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/Config/Config.h"
|
|
#include "Common/Event.h"
|
|
#include "Common/Functional.h"
|
|
#include "Common/HttpRequest.h"
|
|
#include "Common/JsonUtil.h"
|
|
#include "Common/Lazy.h"
|
|
#include "Common/WorkQueueThread.h"
|
|
#include "DiscIO/Volume.h"
|
|
#include "VideoCommon/Assets/CustomTextureData.h"
|
|
|
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
|
#include <rcheevos/include/rc_client_raintegration.h>
|
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
|
|
|
namespace Core
|
|
{
|
|
class CPUThreadGuard;
|
|
class System;
|
|
} // namespace Core
|
|
|
|
namespace PatchEngine
|
|
{
|
|
struct Patch;
|
|
} // namespace PatchEngine
|
|
|
|
namespace Gecko
|
|
{
|
|
class GeckoCode;
|
|
} // namespace Gecko
|
|
|
|
namespace ActionReplay
|
|
{
|
|
struct ARCode;
|
|
} // namespace ActionReplay
|
|
|
|
class AchievementManager
|
|
{
|
|
public:
|
|
using BadgeNameFunction = std::function<std::string(const AchievementManager&)>;
|
|
|
|
static constexpr size_t HASH_SIZE = 33;
|
|
using Hash = std::array<char, HASH_SIZE>;
|
|
using AchievementId = u32;
|
|
static constexpr size_t FORMAT_SIZE = 24;
|
|
using FormattedValue = std::array<char, FORMAT_SIZE>;
|
|
using LeaderboardRank = u32;
|
|
static constexpr size_t RP_SIZE = 256;
|
|
using RichPresence = std::array<char, RP_SIZE>;
|
|
using Badge = VideoCommon::CustomTextureData::ArraySlice::Level;
|
|
static constexpr size_t MAX_DISPLAYED_LBOARDS = 4;
|
|
// This is hardcoded to 24MiB because rcheevos currently hardcodes it to 24MiB.
|
|
static constexpr u32 MEM1_SIZE = 0x01800000;
|
|
static constexpr u32 MEM2_START = 0x10000000;
|
|
|
|
static constexpr std::string_view DEFAULT_PLAYER_BADGE_FILENAME = "achievements_player.png";
|
|
static constexpr std::string_view DEFAULT_GAME_BADGE_FILENAME = "achievements_game.png";
|
|
static constexpr std::string_view DEFAULT_LOCKED_BADGE_FILENAME = "achievements_locked.png";
|
|
static constexpr std::string_view DEFAULT_UNLOCKED_BADGE_FILENAME = "achievements_unlocked.png";
|
|
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 = {
|
|
0x6D, 0x91, 0xF5, 0xC1, 0xE2, 0x4C, 0xC3, 0x39, 0xF5, 0x7F,
|
|
0xEC, 0xA9, 0x8C, 0xA9, 0xBD, 0x61, 0x28, 0x54, 0x11, 0x62};
|
|
|
|
struct LeaderboardEntry
|
|
{
|
|
std::string username;
|
|
FormattedValue score;
|
|
LeaderboardRank rank;
|
|
};
|
|
|
|
struct LeaderboardStatus
|
|
{
|
|
std::string name;
|
|
std::string description;
|
|
u32 player_index = 0;
|
|
std::unordered_map<u32, LeaderboardEntry> entries;
|
|
};
|
|
|
|
struct UpdatedItems
|
|
{
|
|
bool all = false;
|
|
bool player_icon = false;
|
|
bool game_icon = false;
|
|
bool all_achievements = false;
|
|
std::set<AchievementId> achievements{};
|
|
bool all_leaderboards = false;
|
|
std::set<AchievementId> leaderboards{};
|
|
bool rich_presence = false;
|
|
int failed_login_code = 0;
|
|
};
|
|
using UpdateCallback = std::function<void(const UpdatedItems&)>;
|
|
|
|
using AsyncCallback = Common::MoveOnlyFunction<void()>;
|
|
using AsyncCallbackHandler = Common::MoveOnlyFunction<void(AsyncCallback)>;
|
|
|
|
static AchievementManager& GetInstance();
|
|
|
|
void Init(void* hwnd, AsyncCallbackHandler async_callback_handler);
|
|
void SetUpdateCallback(UpdateCallback callback);
|
|
void Login(const std::string& password);
|
|
bool HasAPIToken() const;
|
|
void LoadGame(const DiscIO::Volume* volume);
|
|
bool IsGameLoaded() const;
|
|
void SetBackgroundExecutionAllowed(bool allowed);
|
|
|
|
static std::string CalculateHash(const std::string& file_path);
|
|
|
|
void FetchPlayerBadge();
|
|
void FetchGameBadges();
|
|
|
|
void DoFrame();
|
|
|
|
bool CanPause();
|
|
void DoIdle();
|
|
|
|
std::recursive_mutex& GetLock();
|
|
bool IsHardcoreModeActive() const;
|
|
|
|
void FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches, const std::string& game_id,
|
|
u16 revision) const;
|
|
void FilterApprovedGeckoCodes(std::vector<Gecko::GeckoCode>& codes, const std::string& game_id,
|
|
u16 revision) const;
|
|
void FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes, const std::string& game_id,
|
|
u16 revision) const;
|
|
bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code, const std::string& game_id,
|
|
u16 revision) const;
|
|
bool CheckApprovedARCode(const ActionReplay::ARCode& code, const std::string& game_id,
|
|
u16 revision) const;
|
|
|
|
void SetSpectatorMode();
|
|
std::string_view GetPlayerDisplayName() const;
|
|
u32 GetPlayerScore() const;
|
|
const Badge& GetPlayerBadge() const;
|
|
std::string_view GetGameDisplayName() const;
|
|
rc_client_t* GetClient();
|
|
rc_api_fetch_game_data_response_t* GetGameData();
|
|
const Badge& GetGameBadge() const;
|
|
const Badge& GetAchievementBadge(AchievementId id, bool locked) const;
|
|
const LeaderboardStatus* GetLeaderboardInfo(AchievementId leaderboard_id);
|
|
RichPresence GetRichPresence() const;
|
|
bool AreChallengesUpdated() const;
|
|
void ResetChallengesUpdated();
|
|
const std::unordered_set<AchievementId>& GetActiveChallenges() const;
|
|
std::vector<std::string> GetActiveLeaderboards() const;
|
|
|
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
|
const rc_client_raintegration_menu_t* GetDevelopmentMenu();
|
|
u32 ActivateDevMenuItem(u32 menu_item_id);
|
|
void SetDevMenuUpdateCallback(std::function<void(void)> callback)
|
|
{
|
|
m_dev_menu_callback = callback;
|
|
}
|
|
bool CheckForModifications() { return rc_client_raintegration_has_modifications(m_client); }
|
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
|
|
|
void DoState(PointerWrap& p);
|
|
|
|
void CloseGame();
|
|
void Logout();
|
|
void Shutdown();
|
|
|
|
private:
|
|
AchievementManager() = default;
|
|
|
|
struct FilereaderState
|
|
{
|
|
int64_t position = 0;
|
|
std::unique_ptr<DiscIO::Volume> volume;
|
|
};
|
|
|
|
static picojson::value LoadApprovedList();
|
|
|
|
static void* FilereaderOpen(const char* path_utf8);
|
|
static void FilereaderSeek(void* file_handle, int64_t offset, int origin);
|
|
static int64_t FilereaderTell(void* file_handle);
|
|
static size_t FilereaderRead(void* file_handle, void* buffer, size_t requested_bytes);
|
|
static void FilereaderClose(void* file_handle);
|
|
|
|
static u32 FindConsoleID(const DiscIO::Platform& platform);
|
|
|
|
void LoadDefaultBadges();
|
|
static void LoginCallback(int result, const char* error_message, rc_client_t* client,
|
|
void* userdata);
|
|
|
|
void FetchBoardInfo(AchievementId leaderboard_id);
|
|
|
|
std::unique_ptr<DiscIO::Volume>& GetLoadingVolume() { return m_loading_volume; }
|
|
|
|
static void LoadGameCallback(int result, const char* error_message, rc_client_t* client,
|
|
void* userdata);
|
|
static void ChangeMediaCallback(int result, const char* error_message, rc_client_t* client,
|
|
void* userdata);
|
|
void DisplayWelcomeMessage();
|
|
|
|
void SetHardcoreMode();
|
|
|
|
template <typename T>
|
|
void FilterApprovedIni(std::vector<T>& codes, const std::string& game_id, u16 revision) const;
|
|
template <typename T>
|
|
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 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);
|
|
|
|
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);
|
|
static void HandleLeaderboardSubmittedEvent(const rc_client_event_t* client_event);
|
|
static void HandleLeaderboardTrackerUpdateEvent(const rc_client_event_t* client_event);
|
|
static void HandleLeaderboardTrackerShowEvent(const rc_client_event_t* client_event);
|
|
static void HandleLeaderboardTrackerHideEvent(const rc_client_event_t* client_event);
|
|
static void HandleAchievementChallengeIndicatorShowEvent(const rc_client_event_t* client_event);
|
|
static void HandleAchievementChallengeIndicatorHideEvent(const rc_client_event_t* client_event);
|
|
static void HandleAchievementProgressIndicatorShowEvent(const rc_client_event_t* client_event);
|
|
static void HandleGameCompletedEvent(const rc_client_event_t* client_event, rc_client_t* client);
|
|
static void HandleResetEvent(const rc_client_event_t* client_event);
|
|
static void HandleServerErrorEvent(const rc_client_event_t* client_event);
|
|
|
|
static void Request(const rc_api_request_t* request, rc_client_server_callback_t callback,
|
|
void* callback_data, rc_client_t* client);
|
|
static u32 MemoryVerifier(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client);
|
|
static u32 MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client);
|
|
void FetchBadge(Badge* badge, u32 badge_type, const BadgeNameFunction function,
|
|
const UpdatedItems callback_data);
|
|
static void EventHandler(const rc_client_event_t* event, rc_client_t* client);
|
|
|
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
|
static void LoadIntegrationCallback(int result, const char* error_message, rc_client_t* client,
|
|
void* userdata);
|
|
static void RAIntegrationEventHandler(const rc_client_raintegration_event_t* event,
|
|
rc_client_t* client);
|
|
static void MemoryPoker(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client);
|
|
static void GameTitleEstimateHandler(char* buffer, u32 buffer_size, rc_client_t* client);
|
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
|
|
|
rc_runtime_t m_runtime{};
|
|
rc_client_t* m_client{};
|
|
std::atomic<Core::System*> m_system{};
|
|
bool m_is_runtime_initialized = false;
|
|
UpdateCallback m_update_callback = [](const UpdatedItems&) {};
|
|
std::unique_ptr<DiscIO::Volume> m_loading_volume;
|
|
Config::ConfigChangedCallbackID m_config_changed_callback_id;
|
|
Badge m_default_player_badge;
|
|
Badge m_default_game_badge;
|
|
Badge m_default_unlocked_badge;
|
|
Badge m_default_locked_badge;
|
|
std::atomic_bool m_background_execution_allowed = true;
|
|
Badge m_player_badge;
|
|
Hash m_game_hash{};
|
|
u32 m_game_id = 0;
|
|
rc_api_fetch_game_data_response_t m_game_data{};
|
|
bool m_is_game_loaded = false;
|
|
Badge m_game_badge;
|
|
bool m_display_welcome_message = false;
|
|
std::unordered_map<AchievementId, Badge> m_unlocked_badges;
|
|
std::unordered_map<AchievementId, Badge> m_locked_badges;
|
|
RichPresence m_rich_presence;
|
|
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();
|
|
|
|
Common::Lazy<picojson::value> m_ini_root{LoadApprovedList};
|
|
|
|
std::unordered_map<AchievementId, LeaderboardStatus> m_leaderboard_map;
|
|
bool m_challenges_updated = false;
|
|
std::unordered_set<AchievementId> m_active_challenges;
|
|
std::vector<rc_client_leaderboard_tracker_t> m_active_leaderboards;
|
|
|
|
bool m_dll_found = false;
|
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
|
std::function<void(void)> m_dev_menu_callback;
|
|
std::vector<u8> m_cloned_memory;
|
|
std::recursive_mutex m_memory_lock;
|
|
std::string m_title_estimate;
|
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
|
|
|
Common::AsyncWorkThread m_queue;
|
|
Common::AsyncWorkThread m_image_queue;
|
|
mutable std::recursive_mutex m_lock;
|
|
std::recursive_mutex m_filereader_lock;
|
|
|
|
AsyncCallbackHandler m_async_callback_handler;
|
|
}; // class AchievementManager
|
|
|
|
#else // USE_RETRO_ACHIEVEMENTS
|
|
|
|
#include <string>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
|
|
namespace ActionReplay
|
|
{
|
|
struct ARCode;
|
|
}
|
|
|
|
namespace DiscIO
|
|
{
|
|
class Volume;
|
|
}
|
|
|
|
namespace Gecko
|
|
{
|
|
class GeckoCode;
|
|
}
|
|
|
|
class AchievementManager
|
|
{
|
|
public:
|
|
static AchievementManager& GetInstance()
|
|
{
|
|
static AchievementManager s_instance;
|
|
return s_instance;
|
|
}
|
|
|
|
constexpr bool IsHardcoreModeActive() { return false; }
|
|
|
|
constexpr bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code, const std::string& game_id,
|
|
u16 revision)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
constexpr bool CheckApprovedARCode(const ActionReplay::ARCode& code, const std::string& game_id,
|
|
u16 revision)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
constexpr void LoadGame(const DiscIO::Volume*) {}
|
|
|
|
constexpr void SetBackgroundExecutionAllowed(bool allowed) {}
|
|
|
|
constexpr void DoFrame() {}
|
|
|
|
constexpr void CloseGame() {}
|
|
};
|
|
|
|
#endif // USE_RETRO_ACHIEVEMENTS
|