// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>1// SPDX-License-Identifier: CC-BY-NC-ND-4.023#pragma once45#include "common/small_string.h"6#include "common/types.h"78#include <array>9#include <functional>10#include <mutex>11#include <optional>12#include <span>13#include <string>14#include <utility>15#include <vector>1617class Error;18class StateWrapper;19class CDImage;2021struct Settings;2223namespace Achievements {2425enum class LoginRequestReason26{27UserInitiated,28TokenInvalid,29};3031inline constexpr size_t GAME_HASH_LENGTH = 16;32using GameHash = std::array<u8, GAME_HASH_LENGTH>;3334struct HashDatabaseEntry35{36GameHash hash;37u32 game_id;38u32 num_achievements;39};4041class ProgressDatabase42{43public:44struct Entry45{46u32 game_id;47u16 num_achievements_unlocked;48u16 num_hc_achievements_unlocked;49};5051ProgressDatabase();52~ProgressDatabase();5354bool Load(Error* error);5556const Entry* LookupGame(u32 game_id) const;5758private:59std::vector<Entry> m_entries;60};6162/// Acquires the achievements lock. Must be held when accessing any achievement state from another thread.63std::unique_lock<std::recursive_mutex> GetLock();6465/// Returns the achievements game hash for a given disc.66std::optional<GameHash> GetGameHash(CDImage* image);67std::optional<GameHash> GetGameHash(const std::string_view executable_name, std::span<const u8> executable_data);6869/// Returns the number of achievements for a given hash.70const HashDatabaseEntry* LookupGameHash(const GameHash& hash);7172/// Initializes the RetroAchievments client.73bool Initialize();7475/// Updates achievements settings.76void UpdateSettings(const Settings& old_config);7778/// Shuts down the RetroAchievements client.79void Shutdown();8081/// Call to refresh the all-progress database.82bool RefreshAllProgressDatabase(Error* error);8384/// Called when the system is start. Engages hardcore mode if enabled.85void OnSystemStarting(CDImage* image, bool disable_hardcore_mode);8687/// Called when the system is shutting down. If this returns false, the shutdown should be aborted.88void OnSystemDestroyed();8990/// Called when the system is being reset. Resets the internal state of all achievement tracking.91void OnSystemReset();9293/// Called when the system changes game.94void GameChanged(CDImage* image);9596/// Called once a frame at vsync time on the CPU thread.97void FrameUpdate();9899/// Called when the system is paused, because FrameUpdate() won't be getting called.100void IdleUpdate();101102/// Returns true if idle updates are necessary (e.g. outstanding requests).103bool NeedsIdleUpdate();104105/// Saves/loads state.106bool DoState(StateWrapper& sw);107108/// Attempts to log in to RetroAchievements using the specified credentials.109/// If the login is successful, the token returned by the server will be saved.110bool Login(const char* username, const char* password, Error* error);111112/// Logs out of RetroAchievements, clearing any credentials.113void Logout();114115/// Forces hardcore mode off until next reset.116void DisableHardcoreMode(bool show_message, bool display_game_summary);117118/// Prompts the user to disable hardcore mode, if they agree, returns true.119bool ConfirmHardcoreModeDisable(const char* trigger);120void ConfirmHardcoreModeDisableAsync(const char* trigger, std::function<void(bool)> callback);121122/// Returns true if hardcore mode is active, and functionality should be restricted.123bool IsHardcoreModeActive();124125/// RAIntegration only exists for Windows, so no point checking it on other platforms.126bool IsUsingRAIntegration();127bool IsRAIntegrationAvailable();128129/// Returns true if the achievement system is active. Achievements can be active without a valid client.130bool IsActive();131132/// Returns true if RetroAchievements game data has been loaded.133bool HasActiveGame();134135/// Returns the RetroAchievements ID for the current game.136u32 GetGameID();137138/// Returns true if the current game has any achievements or leaderboards.139bool HasAchievementsOrLeaderboards();140141/// Returns true if the current game has any leaderboards.142bool HasAchievements();143144/// Returns true if the current game has any leaderboards.145bool HasLeaderboards();146147/// Returns true if the game supports rich presence.148bool HasRichPresence();149150/// Returns the current rich presence string.151/// Should be called with the lock held.152const std::string& GetRichPresenceString();153154/// Returns the URL for the current icon of the game155const std::string& GetGameIconURL();156157/// Returns the path for the current icon of the game158const std::string& GetGameIconPath();159160/// Returns the RetroAchievements title for the current game.161/// Should be called with the lock held.162const std::string& GetGameTitle();163164/// Returns the path for the game that is current hashed/running.165const std::string& GetGamePath();166167/// Returns true if the user has been successfully logged in.168bool IsLoggedIn();169170/// Returns true if the user has been successfully logged in, or the request is in progress.171bool IsLoggedInOrLoggingIn();172173/// Returns the logged-in user name.174const char* GetLoggedInUserName();175176/// Returns the path to the user's profile avatar.177/// Should be called with the lock held.178std::string GetLoggedInUserBadgePath();179180/// Returns a summary of the user's points.181/// Should be called with the lock held.182SmallString GetLoggedInUserPointsSummary();183184/// Returns 0 if pausing is allowed, otherwise the number of frames until pausing is allowed.185u32 GetPauseThrottleFrames();186187/// Clears all cached state used to render the UI.188void ClearUIState();189190/// Draws ImGui overlays when not paused.191void DrawGameOverlays();192193/// Draws ImGui overlays when paused.194void DrawPauseMenuOverlays(float start_pos_y);195196/// Updates the stored most-recent and closest-to-completion achievements.197/// Call before calling DrawPauseMenuOverlays() for the first time.198void UpdateRecentUnlockAndAlmostThere();199200#ifndef __ANDROID__201202/// Queries the achievement list, and if no achievements are available, returns false.203bool PrepareAchievementsWindow();204205/// Renders the achievement list.206void DrawAchievementsWindow();207208/// Queries the leaderboard list, and if no leaderboards are available, returns false.209bool PrepareLeaderboardsWindow();210211/// Renders the leaderboard list.212void DrawLeaderboardsWindow();213214#endif // __ANDROID__215216} // namespace Achievements217218/// Functions implemented in the frontend.219namespace Host {220221/// Called if the big picture UI requests achievements login, or token login fails.222void OnAchievementsLoginRequested(Achievements::LoginRequestReason reason);223224/// Called when achievements login completes.225void OnAchievementsLoginSuccess(const char* display_name, u32 points, u32 sc_points, u32 unread_messages);226227/// Called whenever game details or rich presence information is updated.228/// Implementers can assume the lock is held when this is called.229void OnAchievementsRefreshed();230231/// Called when achievements login completes or they are disabled.232void OnAchievementsActiveChanged(bool active);233234/// Called whenever hardcore mode is toggled.235void OnAchievementsHardcoreModeChanged(bool enabled);236237/// Called whenever all progress is manually refreshed and completed.238void OnAchievementsAllProgressRefreshed();239240#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION241242/// Called when the RAIntegration menu changes.243void OnRAIntegrationMenuChanged();244245#endif246247} // namespace Host248249250