From 12f5084b9317e4898090263caa18679837b1b092 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Mon, 10 Aug 2026 09:33:11 +0300 Subject: [PATCH] [XAM] Allow deleting titles with achievements earned from profiles --- src/xenia/kernel/xam/ui/title_info_ui.cc | 28 ++++++++++++++++--- src/xenia/kernel/xam/user_profile.cc | 17 ++++++++++++ src/xenia/kernel/xam/user_profile.h | 1 + src/xenia/kernel/xam/user_tracker.cc | 34 +++++++++++++++++++++--- 4 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/xenia/kernel/xam/ui/title_info_ui.cc b/src/xenia/kernel/xam/ui/title_info_ui.cc index c237758fc..181253dac 100644 --- a/src/xenia/kernel/xam/ui/title_info_ui.cc +++ b/src/xenia/kernel/xam/ui/title_info_ui.cc @@ -8,6 +8,8 @@ */ #include "xenia/kernel/xam/ui/title_info_ui.h" + +#include "xenia/emulator.h" #include "xenia/kernel/xam/ui/game_achievements_ui.h" #include "xenia/base/system.h" @@ -142,6 +144,8 @@ void TitleListUI::DrawTitleEntry(ImGuiIO& io, TitleInfo& entry) { ImGui::Separator(); + ImGui::BeginDisabled(kernel_state()->emulator()->is_title_open()); + const auto title_info = kernel_state()->xam_state()->user_tracker()->GetUserTitleInfo( profile_->xuid(), entry.id); @@ -156,13 +160,29 @@ void TitleListUI::DrawTitleEntry(ImGuiIO& io, TitleInfo& entry) { } if (title_info) { - if (ImGui::MenuItem("Delete title", nullptr, nullptr, - !title_info->unlocked_achievements_count)) { - kernel_state()->xam_state()->user_tracker()->RemoveTitleFromPlayedList( - profile_->xuid(), entry.id); + if (ImGui::BeginMenu("Delete title")) { + if (entry.unlocked_achievements_count != 0) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted( + fmt::format( + "This will erase all unlocked achievements and potentially " + "erase progress for this title. Are you sure?") + .c_str()); + ImGui::EndTooltip(); + } + + if (ImGui::MenuItem("Yes, delete it!")) { + kernel_state() + ->xam_state() + ->user_tracker() + ->RemoveTitleFromPlayedList(profile_->xuid(), entry.id); + } + ImGui::EndMenu(); } } + ImGui::EndDisabled(); + ImGui::EndPopup(); } } diff --git a/src/xenia/kernel/xam/user_profile.cc b/src/xenia/kernel/xam/user_profile.cc index 605cb7cf1..afe4f1faa 100644 --- a/src/xenia/kernel/xam/user_profile.cc +++ b/src/xenia/kernel/xam/user_profile.cc @@ -181,6 +181,23 @@ bool UserProfile::WriteGpd(const uint32_t title_id) { return true; } +bool UserProfile::RemoveGpd(const uint32_t title_id) { + auto it = games_gpd_.find(title_id); + if (it == games_gpd_.end()) { + return false; + } + + const std::string mounted_path = + fmt::format("User_{:016X}:\\{:08X}.gpd", xuid_, title_id); + + if (!kernel_state()->file_system()->DeletePath(mounted_path)) { + return false; + } + + games_gpd_.erase(it); + return true; +} + } // namespace xam } // namespace kernel } // namespace xe diff --git a/src/xenia/kernel/xam/user_profile.h b/src/xenia/kernel/xam/user_profile.h index f372493e9..db27063ef 100644 --- a/src/xenia/kernel/xam/user_profile.h +++ b/src/xenia/kernel/xam/user_profile.h @@ -174,6 +174,7 @@ class UserProfile { std::span icon_data); std::vector LoadGpd(const uint32_t title_id); bool WriteGpd(const uint32_t title_id); + bool RemoveGpd(const uint32_t title_id); }; } // namespace xam diff --git a/src/xenia/kernel/xam/user_tracker.cc b/src/xenia/kernel/xam/user_tracker.cc index d6dc2686e..cb3e93353 100644 --- a/src/xenia/kernel/xam/user_tracker.cc +++ b/src/xenia/kernel/xam/user_tracker.cc @@ -191,11 +191,37 @@ void UserTracker::RemoveTitleFromPlayedList(uint64_t xuid, uint32_t title_id) { return; } - if (user->dashboard_gpd_.RemoveTitle(title_id)) { - UpdateSettingValue(xuid, kDashboardID, - UserSettingId::XPROFILE_GAMERCARD_TITLES_PLAYED, -1); - FlushUserData(xuid); + auto title_data = user->dashboard_gpd_.GetTitleInfo(title_id); + if (!title_data) { + return; } + + // Get the number of achievements and gamer score we got from this title. + int32_t gamerscore_earned = + static_cast(title_data->gamerscore_earned); + int32_t achievements_unlocked = + static_cast(title_data->achievements_unlocked); + + // Remove the title's GPD. + if (!user->RemoveGpd(title_id)) { + return; + } + + // Remove the title from dashboard GPD. + if (!user->dashboard_gpd_.RemoveTitle(title_id)) { + return; + } + + // Lower the profile's stats. + UpdateSettingValue(xuid, kDashboardID, + UserSettingId::XPROFILE_GAMERCARD_TITLES_PLAYED, -1); + UpdateSettingValue(xuid, kDashboardID, UserSettingId::XPROFILE_GAMERCARD_CRED, + -gamerscore_earned); + UpdateSettingValue(xuid, kDashboardID, + UserSettingId::XPROFILE_GAMERCARD_ACHIEVEMENTS_EARNED, + -achievements_unlocked); + + FlushUserData(xuid); } // Privates