From 91370ee5ac1b60b7b5fe304abe7df367e840754d Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 21 Feb 2025 21:49:56 +1000 Subject: [PATCH] Achievements: Update both HC and SC counts on unlock/boot --- src/core/achievements.cpp | 55 +++++++++++++++++++++++++++------------ src/core/game_list.cpp | 11 +++----- src/core/game_list.h | 2 +- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/core/achievements.cpp b/src/core/achievements.cpp index e9dd4eab8..99803c52e 100644 --- a/src/core/achievements.cpp +++ b/src/core/achievements.cpp @@ -4207,18 +4207,39 @@ void Achievements::UpdateProgressDatabase(bool force) if (rc_client_get_spectator_mode_enabled(s_state.client)) return; + // query list to get both hardcore and softcore counts + rc_client_achievement_list_t* const achievements = + rc_client_create_achievement_list(s_state.client, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE, 0); + u32 num_achievements = 0; + u32 achievements_unlocked = 0; + u32 achievements_unlocked_hardcore = 0; + if (achievements) + { + for (const rc_client_achievement_bucket_t& bucket : + std::span(achievements->buckets, achievements->num_buckets)) + { + for (const rc_client_achievement_t* achievement : + std::span(bucket.achievements, bucket.num_achievements)) + { + achievements_unlocked += BoolToUInt32((achievement->unlocked & RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE) != 0); + achievements_unlocked_hardcore += + BoolToUInt32((achievement->unlocked & RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE) != 0); + } + + num_achievements += bucket.num_achievements; + } + rc_client_destroy_achievement_list(achievements); + } + // update the game list, this should be fairly quick if (s_state.game_hash.has_value()) { - GameList::UpdateAchievementData(s_state.game_hash.value(), s_state.game_id, - s_state.game_summary.num_core_achievements, - s_state.game_summary.num_unlocked_achievements, IsHardcoreModeActive()); + GameList::UpdateAchievementData(s_state.game_hash.value(), s_state.game_id, num_achievements, achievements_unlocked, + achievements_unlocked_hardcore); } // done asynchronously so we don't hitch on disk I/O - System::QueueAsyncTask([game_id = s_state.game_id, - achievements_unlocked = s_state.game_summary.num_unlocked_achievements, - hardcore = IsHardcoreModeActive(), force]() { + System::QueueAsyncTask([game_id = s_state.game_id, achievements_unlocked, achievements_unlocked_hardcore, force]() { // no point storing it in memory, just write directly to the file Error error; FileSystem::ManagedCFilePtr fp = OpenProgressDatabase(true, false, &error); @@ -4246,16 +4267,18 @@ void Achievements::UpdateProgressDatabase(bool force) // do we even need to change it? const u16 current_achievements_unlocked = reader.ReadU16(); const u16 current_achievements_unlocked_hardcore = reader.ReadU16(); - const u16 current_unlocked = hardcore ? current_achievements_unlocked_hardcore : current_achievements_unlocked; // if we're not forced, then take the greater count - if (force ? (current_unlocked <= achievements_unlocked) : (current_unlocked == achievements_unlocked)) + if (force ? (current_achievements_unlocked == achievements_unlocked && + current_achievements_unlocked_hardcore == achievements_unlocked_hardcore) : + (current_achievements_unlocked <= achievements_unlocked && + current_achievements_unlocked_hardcore <= achievements_unlocked_hardcore)) { VERBOSE_LOG("No update to progress database needed for game {}", game_id); return; } - found_offset = FileSystem::FTell64(fp.get()); + found_offset = FileSystem::FTell64(fp.get()) - sizeof(u16) - sizeof(u16); break; } @@ -4278,25 +4301,25 @@ void Achievements::UpdateProgressDatabase(bool force) // append/update the entry if (found_offset > 0) { - INFO_LOG("Updating game {} with {} unlocked{}", game_id, achievements_unlocked, hardcore ? " (hardcore)" : ""); + INFO_LOG("Updating game {} with {}/{} unlocked", game_id, achievements_unlocked, achievements_unlocked_hardcore); // need to seek when switching read->write - const s32 hardcore_offset = hardcore ? sizeof(u16) : 0; - if (!FileSystem::FSeek64(fp.get(), found_offset + hardcore_offset, SEEK_SET, &error)) + if (!FileSystem::FSeek64(fp.get(), found_offset, SEEK_SET, &error)) { ERROR_LOG("Failed to write seek in progress database: {}", error.GetDescription()); return; } writer.WriteU16(Truncate16(achievements_unlocked)); + writer.WriteU16(Truncate16(achievements_unlocked_hardcore)); } else { // don't write zeros to the file. we could still end up with zeros here after reset, but that's rare - if (achievements_unlocked == 0) + if (achievements_unlocked == 0 && achievements_unlocked_hardcore == 0) return; - INFO_LOG("Appending game {} with {} unlocked", game_id, achievements_unlocked, hardcore ? " (hardcore)" : ""); + INFO_LOG("Appending game {} with {}/{} unlocked", game_id, achievements_unlocked, achievements_unlocked_hardcore); if (size == 0) { @@ -4315,8 +4338,8 @@ void Achievements::UpdateProgressDatabase(bool force) } writer.WriteU32(game_id); - writer.WriteU16(Truncate16(hardcore ? 0 : achievements_unlocked)); - writer.WriteU16(Truncate16(hardcore ? achievements_unlocked : 0)); + writer.WriteU16(Truncate16(achievements_unlocked)); + writer.WriteU16(Truncate16(achievements_unlocked_hardcore)); } if (!writer.Flush(&error)) diff --git a/src/core/game_list.cpp b/src/core/game_list.cpp index a66f7350b..2bc0aaae0 100644 --- a/src/core/game_list.cpp +++ b/src/core/game_list.cpp @@ -724,7 +724,7 @@ void GameList::PopulateEntryAchievements(Entry* entry, const Achievements::Progr } void GameList::UpdateAchievementData(const std::span hash, u32 game_id, u32 num_achievements, u32 num_unlocked, - bool hardcore) + u32 num_unlocked_hardcore) { std::unique_lock lock(s_mutex); llvm::SmallVector changed_indices; @@ -738,19 +738,16 @@ void GameList::UpdateAchievementData(const std::span hash, u32 game_id, continue; } - const u32 current_unlocked = hardcore ? entry.unlocked_achievements_hc : entry.unlocked_achievements; if (entry.achievements_game_id == game_id && entry.num_achievements == num_achievements && - current_unlocked == num_unlocked) + entry.unlocked_achievements == num_unlocked && entry.unlocked_achievements_hc == num_unlocked_hardcore) { continue; } entry.achievements_game_id = game_id; entry.num_achievements = Truncate16(num_achievements); - if (hardcore) - entry.unlocked_achievements_hc = Truncate16(num_unlocked); - else - entry.unlocked_achievements = Truncate16(num_unlocked); + entry.unlocked_achievements = Truncate16(num_unlocked); + entry.unlocked_achievements_hc = Truncate16(num_unlocked_hardcore); changed_indices.push_back(static_cast(i)); } diff --git a/src/core/game_list.h b/src/core/game_list.h index 246d1180a..a3b4dc73c 100644 --- a/src/core/game_list.h +++ b/src/core/game_list.h @@ -156,7 +156,7 @@ void ReloadMemcardTimestampCache(); /// Updates game list with new achievement unlocks. void UpdateAchievementData(const std::span hash, u32 game_id, u32 num_achievements, u32 num_unlocked, - bool hardcore); + u32 num_unlocked_hardcore); void UpdateAllAchievementData(); }; // namespace GameList