diff --git a/src/xenia/kernel/xam/xam_user.cc b/src/xenia/kernel/xam/xam_user.cc index ac9a22056..0fd6d696f 100644 --- a/src/xenia/kernel/xam/xam_user.cc +++ b/src/xenia/kernel/xam/xam_user.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2022 Ben Vanik. All rights reserved. * + * Copyright 2025 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -600,17 +600,43 @@ dword_result_t XamUserCreateTitlesPlayedEnumerator_entry( !handle_ptr) { return X_ERROR_INVALID_PARAMETER; } - if (!kernel_state()->xam_state()->IsUserSignedIn(user_index)) { + + const uint32_t kEntrySize = sizeof(XTitleEnumerator::XTITLE_PLAYED); + if (buffer_size_ptr) { + *buffer_size_ptr = kEntrySize * game_count; + } + + const auto user = kernel_state()->xam_state()->GetUserProfile(user_index); + if (!user) { return X_ERROR_INVALID_PARAMETER; } - // auto e = new XStaticEnumerator(kernel_state(), - // game_count); auto result = e->Initialize(user_index, 0xFB, 0xB0050, - // 0xB000B, 0x20, game_count, 0); + auto e = object_ref( + new XTitleEnumerator(kernel_state(), game_count)); + auto result = + e->Initialize(user_index, 0xFB, 0xB0050, 0xB000B, 0x20, game_count, 0); + if (XFAILED(result)) { + return result; + } - XELOGD("XamUserCreateTitlesPlayedEnumerator: Stubbed"); + const auto user_titles = + kernel_state()->xam_state()->user_tracker()->GetPlayedTitles( + user->xuid()); - return X_ERROR_FUNCTION_FAILED; + if (!user_titles.empty()) { + for (const auto& title : user_titles) { + if (title.id == kDashboardID) { + continue; + } + if (!title.achievements_count || !title.gamerscore_amount) { + continue; + } + e->AppendItem(title); + } + } + + *handle_ptr = e->handle(); + return X_ERROR_SUCCESS; } DECLARE_XAM_EXPORT1(XamUserCreateTitlesPlayedEnumerator, kUserProfiles, kStub); diff --git a/src/xenia/kernel/xenumerator.cc b/src/xenia/kernel/xenumerator.cc index 19bf472a3..8fd83ae06 100644 --- a/src/xenia/kernel/xenumerator.cc +++ b/src/xenia/kernel/xenumerator.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2025 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -118,6 +118,50 @@ uint32_t XAchievementEnumerator::WriteItems(uint32_t buffer_ptr, return X_ERROR_SUCCESS; } +uint32_t XTitleEnumerator::WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data, + uint32_t* written_count) { + size_t count = std::min(items_.size() - current_item_, items_per_enumerate()); + if (!count) { + return X_ERROR_NO_MORE_FILES; + } + + size_t size = count * item_size(); + auto details = reinterpret_cast(buffer_data); + + for (size_t i = 0, o = current_item_; i < count; ++i, ++current_item_) { + const auto& item = items_[current_item_]; + details[i].base.title_id = item.id; + details[i].base.achievements_count = item.achievements_count; + details[i].base.achievements_unlocked = item.unlocked_achievements_count; + details[i].base.gamerscore_total = item.gamerscore_amount; + details[i].base.gamerscore_earned = item.title_earned_gamerscore; + details[i].base.online_achievement_count = + item.online_unlocked_achievements; + details[i].base.all_avatar_awards.earned = item.all_avatar_awards.earned; + details[i].base.all_avatar_awards.possible = + item.all_avatar_awards.possible; + details[i].base.male_avatar_awards.earned = item.male_avatar_awards.earned; + details[i].base.male_avatar_awards.possible = + item.male_avatar_awards.possible; + details[i].base.female_avatar_awards.earned = + item.female_avatar_awards.earned; + details[i].base.female_avatar_awards.possible = + item.female_avatar_awards.possible; + details[i].base.flags = item.flags; + + // On console if title is played offline this field is set to 0. + details[i].base.last_played = X_FILETIME((uint64_t)0); + string_util::copy_and_swap_truncating((char16_t*)&details[i].title_name, + item.title_name, 128); + } + + if (written_count) { + *written_count = static_cast(count); + } + + return X_ERROR_SUCCESS; +} + uint32_t XUserStatsEnumerator::WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data, uint32_t* written_count) { diff --git a/src/xenia/kernel/xenumerator.h b/src/xenia/kernel/xenumerator.h index d3b69a227..3fd8145ef 100644 --- a/src/xenia/kernel/xenumerator.h +++ b/src/xenia/kernel/xenumerator.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2025 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -15,6 +15,7 @@ #include #include "xenia/kernel/xam/achievement_manager.h" +#include "xenia/kernel/xam/user_tracker.h" #include "xenia/kernel/xobject.h" #include "xenia/xbox.h" @@ -161,6 +162,26 @@ class XAchievementEnumerator : public XEnumerator { size_t current_item_ = 0; }; +class XTitleEnumerator : public XEnumerator { + public: + struct XTITLE_PLAYED { + xam::X_XDBF_GPD_TITLE_PLAYED base; + xe::be title_name[64]; + }; + + XTitleEnumerator(KernelState* kernel_state, size_t items_per_enumerate) + : XEnumerator(kernel_state, items_per_enumerate, sizeof(XTITLE_PLAYED)) {} + + void AppendItem(const xam::TitleInfo& item) { items_.push_back(item); } + + uint32_t WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data, + uint32_t* written_count) override; + + private: + std::vector items_; + size_t current_item_ = 0; +}; + class XUserStatsEnumerator : public XEnumerator { public: struct XUSER_STATS_SPEC {