// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team // SPDX-License-Identifier: GPL-3.0+ #pragma once #include "common/Pcsx2Types.h" #include "Config.h" #include #include #include #include #include #include class Error; class SaveStateBase; namespace Achievements { enum class LoginRequestReason { UserInitiated, TokenInvalid, }; /// Acquires the achievements lock. Must be held when accessing any achievement state from another thread. std::unique_lock GetLock(); /// Initializes the RetroAchievments client. bool Initialize(); /// Updates achievements settings. void UpdateSettings(const Pcsx2Config::AchievementsOptions& old_config); /// Resets the internal state of all achievement tracking. Call on system reset. void ResetClient(); /// Called when the system is being reset. If it returns false, the reset should be aborted. bool ConfirmSystemReset(); /// Called when the system is being shut down. If Shutdown() returns false, the shutdown should be aborted. bool Shutdown(bool allow_cancel); /// Called when the system is being paused and resumed. void OnVMPaused(bool paused); /// Called once a frame at vsync time on the CPU thread. void FrameUpdate(); /// Called when the system is paused, because FrameUpdate() won't be getting called. void IdleUpdate(); /// Saves/loads state. void LoadState(std::span data); void SaveState(SaveStateBase& writer); /// Attempts to log in to RetroAchievements using the specified credentials. /// If the login is successful, the token returned by the server will be saved. bool Login(const char* username, const char* password, Error* error); /// Logs out of RetroAchievements, clearing any credentials. void Logout(); /// Called when the system changes game, or is booting. void GameChanged(u32 disc_crc, u32 crc); /// Play achievement related sounds effects void PlayAchievementSound(bool is_specific_sound_enabled, const std::string& custom_sound_name, const std::string& default_sound_name); /// Re-enables hardcode mode if it is enabled in the settings. bool ResetHardcoreMode(bool is_booting); /// Forces hardcore mode off until next reset. void DisableHardcoreMode(); /// Returns the translated title to display for the message box asking the // user to disable hardcore mode. const char* GetHardcoreModeDisableTitle(); /// Returns the translated text to display in the message box asking the /// user to disable hardcore mode. std::string GetHardcoreModeDisableText(const char* reason); /// Returns true if hardcore mode is active, and functionality should be restricted. bool IsHardcoreModeActive(); /// RAIntegration only exists for Windows, so no point checking it on other platforms. bool IsUsingRAIntegration(); /// Returns true if the achievement system is active. Achievements can be active without a valid client. bool IsActive(); /// Returns true if RetroAchievements game data has been loaded. bool HasActiveGame(); /// Returns the RetroAchievements ID for the current game. u32 GetGameID(); /// Computes the RetroAchievements hash for a disc image WITHOUT booting it, so a frontend can /// identify a whole library against RA's game list. Returns an empty string if the image cannot /// be read or carries no PS2 boot ELF. /// /// Repoints the global CDVD at `image_path`, so it refuses to run while a VM is valid — doing it /// anyway would pull the disc out from under the running game. Call from a background thread with /// no VM active, as the game list scanner does. std::string GetGameHashForImage(const std::string& image_path); /// Returns true if the current game has any achievements or leaderboards. bool HasAchievementsOrLeaderboards(); /// Returns true if the current game has any achievements. bool HasAchievements(); /// Snapshot the current game's achievements as JSON for the in-game /// overlay's right-side panel. Walks rc_client buckets in display /// order (active challenge → recently unlocked → unlocked → almost /// there → locked → unofficial → unsupported). Empty array when no /// active game / not logged in. Format: /// { /// "active": bool, // game has any achievements /// "loggedIn": bool, // a user is logged in to RA /// "userName": "string", // display name when loggedIn /// "items": [ /// { "id": int, "title": "...", "description": "...", /// "points": int, "unlocked": bool, "bucket": int, /// "rarity": float, "measuredProgress": "..." } /// ] /// } /// Self-contained — no rcheevos headers needed by the caller. std::string GetAchievementsAsJSON(); /// Returns true if the current game has any leaderboards. bool HasLeaderboards(); /// Returns true if the game supports rich presence. bool HasRichPresence(); /// Returns the current rich presence string. /// Should be called with the lock held. const std::string& GetRichPresenceString(); /// Returns the current game icon url. /// Should be called with the lock held. const std::string& GetGameIconURL(); /// Returns the RetroAchievements title for the current game. /// Should be called with the lock held. const std::string& GetGameTitle(); /// Returns the logged-in user name. const char* GetLoggedInUserName(); /// Returns the path to the user's profile avatar. /// Should be called with the lock held. std::string GetLoggedInUserBadgePath(); /// Clears all cached state used to render the UI. void ClearUIState(); /// Draws ImGui overlays when not paused. void DrawGameOverlays(); /// Draws ImGui overlays when paused. void DrawPauseMenuOverlays(); /// Queries the achievement list, and if no achievements are available, returns false. bool PrepareAchievementsWindow(); /// Renders the achievement list. void DrawAchievementsWindow(); /// Queries the leaderboard list, and if no leaderboards are available, returns false. bool PrepareLeaderboardsWindow(); /// Renders the leaderboard list. void DrawLeaderboardsWindow(); #ifdef ENABLE_RAINTEGRATION /// Prevents the internal implementation from being used. Instead, RAIntegration will be /// called into when achievement-related events occur. void SwitchToRAIntegration(); namespace RAIntegration { void MainWindowChanged(void* new_handle); void GameChanged(); std::vector> GetMenuItems(); void ActivateMenuItem(int item); } // namespace RAIntegration #endif struct UserStats { std::string username; std::string display_name; std::string avatar_path; u32 points = 0; u32 softcore_points = 0; u32 unread_messages = 0; }; struct GameStats { std::string title; std::string rich_presence; std::string icon_path; std::string icon_url; u32 game_id = 0; u32 unlocked_achievements = 0; u32 total_achievements = 0; u32 unlocked_points = 0; u32 total_points = 0; bool has_achievements = false; bool has_leaderboards = false; bool has_rich_presence = false; }; struct AchievementInfo { std::string title; std::string description; std::string badge_path; std::string measured_progress; u32 id = 0; u32 points = 0; u32 unlock_time = 0; u32 state = 0; u32 category = 0; u32 bucket = 0; u32 unlocked = 0; float measured_percent = 0.0f; float rarity = 0.0f; float rarity_hardcore = 0.0f; }; bool GetCurrentUserStats(UserStats* stats); bool GetCurrentGameStats(GameStats* stats); bool GetCurrentAchievementList(std::vector* achievements); } // namespace Achievements /// Functions implemented in the frontend. namespace Host { /// Called if the big picture UI requests achievements login, or token login fails. void OnAchievementsLoginRequested(Achievements::LoginRequestReason reason); /// Called when achievements login completes. void OnAchievementsLoginSuccess(const char* display_name, u32 points, u32 sc_points, u32 unread_messages); /// Called whenever game details or rich presence information is updated. /// Implementers can assume the lock is held when this is called. void OnAchievementsRefreshed(); /// Called whenever hardcore mode is toggled. void OnAchievementsHardcoreModeChanged(bool enabled); /// Returns true when the platform renders RetroAchievements notifications through its /// own native UI (for example the iOS SwiftUI toast). When this returns true the shared /// core routes achievement notifications through OnAchievementNotification instead of /// the ImGui FullscreenUI overlay. That also keeps the (invisible on such platforms) /// FullscreenUI from being initialized, which would otherwise add per-frame render cost /// for nothing. bool HasNativeAchievementNotifications(); /// Presents a RetroAchievements notification natively. Only invoked when /// HasNativeAchievementNotifications() returns true; desktop/Android frontends keep /// using the ImGui overlay and never call this. `key` deduplicates against an in-flight /// notification of the same kind, `duration` is in seconds, and `badge_path` may be /// empty. May be called from any thread; implementations must copy the strings before /// returning. void OnAchievementNotification(const char* key, float duration, const char* title, const char* message, const char* badge_path); } // namespace Host