From 6194f08a99a3b9ce31d827ec5fe54e9df871bbb1 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 6 Jul 2026 14:45:47 +1000 Subject: [PATCH] Achievements: Always identify game on startup --- src/core/achievements.cpp | 106 +++++++--------------------- src/core/achievements.h | 3 - src/core/fullscreenui_game_list.cpp | 2 +- 3 files changed, 27 insertions(+), 84 deletions(-) diff --git a/src/core/achievements.cpp b/src/core/achievements.cpp index 4086c4d77..2329368cb 100644 --- a/src/core/achievements.cpp +++ b/src/core/achievements.cpp @@ -115,15 +115,12 @@ static void ReportFmtError(fmt::format_string fmt, T&&... args); template static void ReportRCError(int err, fmt::format_string fmt, T&&... args); static void ClearGameInfo(); -static void ClearGameHash(); static bool TryLoggingInWithToken(); static void EnableHardcoreMode(bool display_message, bool display_game_summary); static void OnHardcoreModeChanged(bool enabled, bool display_message, bool display_game_summary); static bool IsRAIntegrationInitializing(); static void FinishInitialize(); static void FinishLogin(); -static bool IdentifyGame(CDImage* image); -static bool IdentifyCurrentGame(); static void BeginLoadGame(); static void UpdateGameSummary(); static void UpdateModeSettings(const Settings& old_config); @@ -241,7 +238,6 @@ struct State std::optional game_hash; u32 game_id = 0; - std::string game_path; std::string game_title; std::string game_badge_url; rc_client_user_game_summary_t game_summary = {}; @@ -352,12 +348,13 @@ void Achievements::ReportRCError(int err, fmt::format_string fmt, T&&... a std::optional Achievements::GetGameHash(CDImage* image) { - std::optional ret; + if (!image) + return std::nullopt; std::string executable_name; std::vector executable_data; if (!System::ReadExecutableFromImage(image, &executable_name, &executable_data)) - return ret; + return std::nullopt; return GetGameHash(executable_name, executable_data); } @@ -365,14 +362,12 @@ std::optional Achievements::GetGameHash(CDImage* image) std::optional Achievements::GetGameHash(const std::string_view executable_name, std::span executable_data) { - std::optional ret; - // NOTE: Assumes executable_data is aligned to 4 bytes at least.. it should be. const BIOS::PSEXEHeader* header = reinterpret_cast(executable_data.data()); if (executable_data.size() < sizeof(BIOS::PSEXEHeader) || !BIOS::IsValidPSExeHeader(*header, executable_data.size())) { ERROR_LOG("PS-EXE header is invalid in '{}' ({} bytes)", executable_name, executable_data.size()); - return ret; + return std::nullopt; } const u32 hash_size = std::min(header->file_size + 2048, static_cast(executable_data.size())); @@ -382,8 +377,8 @@ std::optional Achievements::GetGameHash(const std::strin if (hash_size > 0) digest.Update(executable_data.data(), hash_size); - ret.emplace(); - digest.Final(ret.value()); + std::optional ret; + digest.Final(ret.emplace()); INFO_COLOR_LOG(StrongOrange, "RA Hash for '{}': {} ({} bytes hashed)", executable_name, GameHashToString(ret), hash_size); @@ -560,11 +555,6 @@ const std::string& Achievements::GetCurrentGameTitle() return s_state.game_title; } -const std::string& Achievements::GetCurrentGamePath() -{ - return s_state.game_path; -} - const std::string& Achievements::GetCurrentGameBadgeURL() { return s_state.game_badge_url; @@ -657,10 +647,6 @@ bool Achievements::CreateClient(std::unique_lock& lock, bo void Achievements::FinishInitialize() { - // Identify game regardless of login status. - if (System::IsValid()) - IdentifyCurrentGame(); - // Start logging in. This can take a while. if (!IsLoggedInOrLoggingIn()) { @@ -685,7 +671,6 @@ void Achievements::DestroyClient(std::unique_lock& lock) WaitForServerCallsWithYield(lock); ClearGameInfo(); - ClearGameHash(); DisableHardcoreMode(false, false); CancelFetchGameListRequest(); CancelFetchAllProgressRequest(); @@ -1078,8 +1063,13 @@ void Achievements::UpdateRichPresence(std::unique_lock& lo void Achievements::OnSystemStarting(CDImage* image, bool disable_hardcore_mode) { + const std::optional game_hash = GetGameHash(image); + std::unique_lock lock(s_state.mutex); + // Always set hash in case we late enable. + s_state.game_hash = game_hash; + if (!IsActive() || IsRAIntegrationInitializing()) return; @@ -1106,16 +1096,20 @@ void Achievements::OnSystemStarting(CDImage* image, bool disable_hardcore_mode) } // now we can finally identify the game - IdentifyGame(image); BeginLoadGame(); } void Achievements::OnSystemDestroyed() { const auto lock = GetLock(); - ClearGameInfo(); - ClearGameHash(); - DisableHardcoreMode(false, false); + + s_state.game_hash.reset(); + + if (IsActive()) + { + ClearGameInfo(); + DisableHardcoreMode(false, false); + } } void Achievements::OnSystemReset() @@ -1148,13 +1142,17 @@ void Achievements::OnSystemReset() void Achievements::GameChanged(CDImage* image) { + const auto game_hash = GetGameHash(image); + std::unique_lock lock(s_state.mutex); - if (!IsActive() || IsRAIntegrationInitializing()) + // disc changed? + if (s_state.game_hash == game_hash) return; - // disc changed? - if (!IdentifyGame(image)) + s_state.game_hash = game_hash; + + if (!IsActive() || IsRAIntegrationInitializing()) return; // cancel previous requests @@ -1172,52 +1170,6 @@ void Achievements::GameChanged(CDImage* image) s_state.reload_game_on_reset = true; } -bool Achievements::IdentifyGame(CDImage* image) -{ - std::optional game_hash; - if (image) - { - game_hash = GetGameHash(image); - - if (!game_hash.has_value() && !rc_client_is_game_loaded(s_state.client)) - { - // If we are starting with this game and it's bad, notify the user that this is why. - Host::AddIconOSDMessage(OSDMessageType::Error, "AchievementsHashFailed", ICON_EMOJI_WARNING, - TRANSLATE_STR("Achievements", "Failed to read executable from disc."), - TRANSLATE_STR("Achievements", "Achievements have been disabled.")); - } - } - - s_state.game_path = image ? image->GetPath() : std::string(); - - if (s_state.game_hash == game_hash) - { - // only the path has changed - different format/save state/etc. - INFO_LOG("Detected path change to '{}'", s_state.game_path); - return false; - } - - s_state.game_hash = game_hash; - return true; -} - -bool Achievements::IdentifyCurrentGame() -{ - DebugAssert(System::IsValid()); - - // this crap is only needed because we can't grab the image from the reader... - std::unique_ptr temp_image; - if (const std::string& disc_path = System::GetGamePath(); !disc_path.empty()) - { - Error error; - temp_image = CDImage::Open(disc_path.c_str(), g_settings.cdrom_load_image_patches, &error); - if (!temp_image) - ERROR_LOG("Failed to open disc for late game identification: {}", error.GetDescription()); - } - - return IdentifyGame(temp_image.get()); -} - void Achievements::BeginLoadGame() { if (!s_state.game_hash.has_value()) @@ -1369,12 +1321,6 @@ void Achievements::ClearGameInfo() s_state.game_summary = {}; } -void Achievements::ClearGameHash() -{ - s_state.game_path = {}; - s_state.game_hash.reset(); -} - void Achievements::DisplayAchievementSummary() { if (g_settings.achievements_notifications) diff --git a/src/core/achievements.h b/src/core/achievements.h index 7f0b327de..3d7dc0713 100644 --- a/src/core/achievements.h +++ b/src/core/achievements.h @@ -154,9 +154,6 @@ const std::string& GetCurrentGameBadgeURL(); /// Should be called with the lock held. const std::string& GetCurrentGameTitle(); -/// Returns the path for the game that is current hashed/running. -const std::string& GetCurrentGamePath(); - /// Returns true if the user has been successfully logged in. bool IsLoggedIn(); diff --git a/src/core/fullscreenui_game_list.cpp b/src/core/fullscreenui_game_list.cpp index a1a6763ab..947183d4d 100644 --- a/src/core/fullscreenui_game_list.cpp +++ b/src/core/fullscreenui_game_list.cpp @@ -1126,7 +1126,7 @@ GPUTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry, bool fa if (fallback_to_achievements_icon && cover_it->second.empty() && Achievements::IsActive()) { const auto lock = Achievements::GetLock(); - if (Achievements::GetCurrentGamePath() == entry->path) + if (VideoThread::GetGamePath() == entry->path) cover_it->second = Achievements::GetCurrentGameBadgeURL(); } }