From bfc6effcb99738f42d587cdcacc04abf6a839607 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Sun, 24 Mar 2024 18:25:11 +0100 Subject: [PATCH] [Kernel] Added GameInfoDatabase This aggregates XDBF and XLAST information into one entity --- src/xenia/emulator.cc | 61 ++--- src/xenia/emulator.h | 3 +- src/xenia/kernel/util/game_info_database.cc | 285 ++++++++++++++++++++ src/xenia/kernel/util/game_info_database.h | 135 ++++++++++ src/xenia/kernel/util/xlast.cc | 73 ++++- src/xenia/kernel/util/xlast.h | 36 ++- src/xenia/kernel/util/xuserdata.h | 20 +- src/xenia/kernel/xam/user_profile.h | 2 +- src/xenia/kernel/xam/xam_user.cc | 2 +- 9 files changed, 559 insertions(+), 58 deletions(-) create mode 100644 src/xenia/kernel/util/game_info_database.cc create mode 100644 src/xenia/kernel/util/game_info_database.h diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index da81f91da..5078dec43 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -118,7 +118,7 @@ Emulator::Emulator(const std::filesystem::path& command_line, kernel_state_(), main_thread_(), title_id_(std::nullopt), - title_xlast_(), + game_info_database_(), paused_(false), restoring_(false), restore_fence_() { @@ -1121,10 +1121,12 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, game_config_load_callback_loop_next_index_ = SIZE_MAX; const kernel::util::XdbfGameData db = kernel_state_->module_xdbf(module); - if (db.is_valid()) { - XLanguage language = - db.GetExistingLanguage(static_cast(cvars::user_language)); - title_name_ = db.title(language); + + game_info_database_ = std::make_unique(&db); + + if (game_info_database_->IsValid()) { + title_name_ = game_info_database_->GetTitleName( + static_cast(cvars::user_language)); XELOGI("Title name: {}", title_name_); // Show achievments data @@ -1132,48 +1134,44 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, table.format().multi_byte_characters(true); table.add_row({"ID", "Title", "Description", "Gamerscore"}); - const std::vector - achievement_list = db.GetAchievements(); - for (const kernel::util::XdbfAchievementTableEntry& entry : + const std::vector + achievement_list = game_info_database_->GetAchievements(); + for (const kernel::util::GameInfoDatabase::Achievement& entry : achievement_list) { - std::string label = string_util::remove_eol(string_util::trim( - db.GetStringTableEntry(language, entry.label_id))); - std::string desc = string_util::remove_eol(string_util::trim( - db.GetStringTableEntry(language, entry.description_id))); - - table.add_row({fmt::format("{}", entry.id), label, desc, - fmt::format("{}", entry.gamerscore)}); + table.add_row({fmt::format("{}", entry.id), entry.label, + entry.description, fmt::format("{}", entry.gamerscore)}); } XELOGI("-------------------- ACHIEVEMENTS --------------------\n{}", table.str()); - const std::vector properties_list = - db.GetProperties(); + const std::vector + properties_list = game_info_database_->GetProperties(); table = tabulate::Table(); table.format().multi_byte_characters(true); table.add_row({"ID", "Name", "Data Size"}); - for (const kernel::util::XdbfPropertyTableEntry& entry : + for (const kernel::util::GameInfoDatabase::Property& entry : properties_list) { - std::string label = string_util::remove_eol(string_util::trim( - db.GetStringTableEntry(language, entry.string_id))); + std::string label = + string_util::remove_eol(string_util::trim(entry.description)); table.add_row({fmt::format("{:08X}", entry.id), label, fmt::format("{}", entry.data_size)}); } XELOGI("-------------------- PROPERTIES --------------------\n{}", table.str()); - const std::vector contexts_list = - db.GetContexts(); + const std::vector contexts_list = + game_info_database_->GetContexts(); table = tabulate::Table(); table.format().multi_byte_characters(true); table.add_row({"ID", "Name", "Default Value", "Max Value"}); - for (const kernel::util::XdbfContextTableEntry& entry : contexts_list) { - std::string label = string_util::remove_eol(string_util::trim( - db.GetStringTableEntry(language, entry.string_id))); + for (const kernel::util::GameInfoDatabase::Context& entry : + contexts_list) { + std::string label = + string_util::remove_eol(string_util::trim(entry.description)); table.add_row({fmt::format("{:08X}", entry.id), label, fmt::format("{}", entry.default_value), fmt::format("{}", entry.max_value)}); @@ -1181,16 +1179,9 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, XELOGI("-------------------- CONTEXTS --------------------\n{}", table.str()); - uint32_t compressed_size, decompressed_size = 0; - const uint8_t* xlast_ptr = - db.ReadXLast(compressed_size, decompressed_size); - - title_xlast_ = std::make_unique( - xlast_ptr, compressed_size, decompressed_size); - - auto icon_block = db.icon(); - if (icon_block) { - display_window_->SetIcon(icon_block.buffer, icon_block.size); + auto icon_block = game_info_database_->GetIcon(); + if (!icon_block.empty()) { + display_window_->SetIcon(icon_block.data(), icon_block.size()); } } } diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index 050bbeaaf..9b8cad23b 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -20,6 +20,7 @@ #include "xenia/base/delegate.h" #include "xenia/base/exception_handler.h" #include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/util/game_info_database.h" #include "xenia/kernel/util/xlast.h" #include "xenia/memory.h" #include "xenia/patcher/patcher.h" @@ -299,7 +300,7 @@ class Emulator { kernel::object_ref main_thread_; kernel::object_ref plugin_loader_thread_; std::optional title_id_; // Currently running title ID - std::unique_ptr title_xlast_; + std::unique_ptr game_info_database_; bool paused_; bool restoring_; diff --git a/src/xenia/kernel/util/game_info_database.cc b/src/xenia/kernel/util/game_info_database.cc new file mode 100644 index 000000000..118a397b8 --- /dev/null +++ b/src/xenia/kernel/util/game_info_database.cc @@ -0,0 +1,285 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2024 Xenia Canary. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/kernel/util/game_info_database.h" +#include "xenia/base/logging.h" + +namespace xe { +namespace kernel { +namespace util { + +GameInfoDatabase::GameInfoDatabase(const XdbfGameData* data) { + if (!data) { + return; + } + + if (!data->is_valid()) { + return; + } + + is_valid_ = true; + xdbf_gamedata_ = std::make_unique(*data); + + uint32_t compressed_size, decompressed_size = 0; + const uint8_t* xlast_ptr = + xdbf_gamedata_->ReadXLast(compressed_size, decompressed_size); + + if (!xlast_ptr) { + XELOGW( + "GameDatabase: Title doesn't contain XLAST data! Multiplayer " + "functionality might be limited."); + return; + } + + xlast_gamedata_ = + std::make_unique(xlast_ptr, compressed_size, decompressed_size); + + if (!xlast_gamedata_) { + XELOGW( + "GameDatabase: Title XLAST data is corrupted! Multiplayer " + "functionality might be limited."); + return; + } +} + +GameInfoDatabase::~GameInfoDatabase() {} + +std::string GameInfoDatabase::GetTitleName(const XLanguage language) const { + if (!is_valid_) { + return ""; + } + + return xdbf_gamedata_->title(xdbf_gamedata_->GetExistingLanguage(language)); +} + +std::vector GameInfoDatabase::GetIcon() const { + std::vector data; + + if (!is_valid_) { + return data; + } + + const XdbfBlock icon = xdbf_gamedata_->icon(); + data.resize(icon.size); + std::memcpy(data.data(), icon.buffer, icon.size); + return data; +} + +XLanguage GameInfoDatabase::GetDefaultLanguage() const { + if (!is_valid_) { + return XLanguage::kEnglish; + } + + return xdbf_gamedata_->default_language(); +} + +std::string GameInfoDatabase::GetLocalizedString(const uint32_t id, + XLanguage language) const { + if (!is_valid_) { + return ""; + } + + return xdbf_gamedata_->GetStringTableEntry( + xdbf_gamedata_->GetExistingLanguage(language), id); +} + +GameInfoDatabase::Context GameInfoDatabase::GetContext( + const uint32_t id) const { + Context context = {}; + + if (!is_valid_) { + return context; + } + + const auto xdbf_context = xdbf_gamedata_->GetContext(id); + + context.id = xdbf_context.id; + context.default_value = xdbf_context.default_value; + context.max_value = xdbf_context.max_value; + context.description = GetLocalizedString(xdbf_context.string_id); + return context; +} + +GameInfoDatabase::Property GameInfoDatabase::GetProperty( + const uint32_t id) const { + Property property = {}; + + if (!is_valid_) { + return property; + } + + const auto xdbf_property = xdbf_gamedata_->GetProperty(id); + + property.id = xdbf_property.id; + property.data_size = xdbf_property.data_size; + property.description = GetLocalizedString(xdbf_property.string_id); + return property; +} + +GameInfoDatabase::Achievement GameInfoDatabase::GetAchievement( + const uint32_t id) const { + Achievement achievement = {}; + + if (!is_valid_) { + return achievement; + } + + const auto xdbf_achievement = xdbf_gamedata_->GetAchievement(id); + + achievement.id = xdbf_achievement.id; + achievement.image_id = xdbf_achievement.id; + achievement.gamerscore = xdbf_achievement.gamerscore; + achievement.flags = xdbf_achievement.flags; + + achievement.label = GetLocalizedString(xdbf_achievement.label_id); + achievement.description = GetLocalizedString(xdbf_achievement.description_id); + achievement.unachieved_description = + GetLocalizedString(xdbf_achievement.unachieved_id); + return achievement; +} + +std::vector GameInfoDatabase::GetMatchmakingAttributes( + const uint32_t id) const { + std::vector result; + + const auto xdbf_matchmaking_data = xdbf_gamedata_->GetMatchCollection(); + + result.insert(result.end(), xdbf_matchmaking_data.contexts.cbegin(), + xdbf_matchmaking_data.contexts.cend()); + result.insert(result.end(), xdbf_matchmaking_data.properties.cbegin(), + xdbf_matchmaking_data.properties.cend()); + + return result; +} + +// XLAST +GameInfoDatabase::Query GameInfoDatabase::GetQueryData( + const uint32_t id) const { + Query query = {}; + + if (!xlast_gamedata_) { + return query; + } + + const auto xlast_query = xlast_gamedata_->GetMatchmakingQuery(id); + if (!xlast_query) { + return query; + } + + query.id = id; + query.name = xlast_query->GetName(); + query.input_parameters = xlast_query->GetParameters(); + query.filters = xlast_query->GetFilters(); + query.expected_return = xlast_query->GetReturns(); + return query; +} + +std::vector GameInfoDatabase::GetSupportedLanguages() const { + if (!xlast_gamedata_) { + return {}; + } + + return xlast_gamedata_->GetSupportedLanguages(); +} + +GameInfoDatabase::ProductInformation GameInfoDatabase::GetProductInformation() + const { + if (!xlast_gamedata_) { + return {}; + } + + ProductInformation info = {}; + + const auto attributes = xlast_gamedata_->GetProductInformationAttributes(); + for (const auto& attribute : attributes) { + switch (attribute.first) { + case ProductInformationEntry::MaxOfflinePlayers: + info.max_offline_players_count = attribute.second; + break; + case ProductInformationEntry::MaxSystemLinkPlayers: + info.max_systemlink_players_count = attribute.second; + break; + case ProductInformationEntry::MaxLivePlayers: + info.max_live_players_count = attribute.second; + break; + case ProductInformationEntry::PublisherString: + info.publisher_name = xe::to_utf8(xlast_gamedata_->GetLocalizedString( + attribute.second, XLanguage::kEnglish)); + break; + case ProductInformationEntry::DeveloperString: + info.developer_name = xe::to_utf8(xlast_gamedata_->GetLocalizedString( + attribute.second, XLanguage::kEnglish)); + break; + case ProductInformationEntry::MarketingString: + info.marketing_info = xe::to_utf8(xlast_gamedata_->GetLocalizedString( + attribute.second, XLanguage::kEnglish)); + break; + case ProductInformationEntry::GenreTypeString: + info.genre_description = + xe::to_utf8(xlast_gamedata_->GetLocalizedString( + attribute.second, XLanguage::kEnglish)); + break; + default: + break; + } + } + return info; +} + +// Aggregators +std::vector GameInfoDatabase::GetContexts() const { + std::vector contexts; + + if (!is_valid_) { + return contexts; + } + + const auto xdbf_contexts = xdbf_gamedata_->GetContexts(); + for (const auto& entry : xdbf_contexts) { + contexts.push_back(GetContext(entry.id)); + } + + return contexts; +} + +std::vector GameInfoDatabase::GetProperties() + const { + std::vector properties; + + if (!is_valid_) { + return properties; + } + + const auto xdbf_properties = xdbf_gamedata_->GetProperties(); + for (const auto& entry : xdbf_properties) { + properties.push_back(GetProperty(entry.id)); + } + + return properties; +} + +std::vector GameInfoDatabase::GetAchievements() + const { + std::vector achievements; + + if (!is_valid_) { + return achievements; + } + + const auto xdbf_achievements = xdbf_gamedata_->GetAchievements(); + for (const auto& entry : xdbf_achievements) { + achievements.push_back(GetAchievement(entry.id)); + } + + return achievements; +} + +} // namespace util +} // namespace kernel +} // namespace xe \ No newline at end of file diff --git a/src/xenia/kernel/util/game_info_database.h b/src/xenia/kernel/util/game_info_database.h new file mode 100644 index 000000000..5d3c608f6 --- /dev/null +++ b/src/xenia/kernel/util/game_info_database.h @@ -0,0 +1,135 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2024 Xenia Canary. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_UTIL_GAME_INFO_DATABASE_H_ +#define XENIA_KERNEL_UTIL_GAME_INFO_DATABASE_H_ + +#include +#include + +#include "xenia/kernel/util/xdbf_utils.h" +#include "xenia/kernel/util/xlast.h" + +namespace xe { +namespace kernel { +namespace util { + +class GameInfoDatabase { + public: + struct Context { + uint32_t id; + uint32_t max_value; + uint32_t default_value; + std::string description; + }; + + struct Property { + uint32_t id; + uint32_t data_size; + std::string description; + }; + + struct Achievement { + uint32_t id; + std::string label; + std::string description; + std::string unachieved_description; + uint32_t image_id; + uint32_t gamerscore; + uint32_t flags; + }; + + struct Query { + uint32_t id; + std::string name; + std::vector input_parameters; + std::vector filters; + std::vector expected_return; + }; + + struct Filter { + uint32_t left_id; + uint32_t right_id; + std::string comparation_operator; + }; + + struct Field { + uint32_t ordinal; + std::string name; + bool is_hidden; + uint16_t attribute_id; + // std::map + std::map property_aggregation; + }; + + struct StatsView { + uint32_t id; + std::string name; + std::vector fields; + }; + + struct ProductInformation { + uint32_t max_offline_players_count; + uint32_t max_systemlink_players_count; + uint32_t max_live_players_count; + std::string publisher_name; + std::string developer_name; + std::string marketing_info; + std::string genre_description; + std::vector features; + }; + + // Normally titles have at least XDBF file embedded into xex. There are + // certain exceptions and that's why we need to check if it is even valid. + GameInfoDatabase(const XdbfGameData* data); + ~GameInfoDatabase(); + + bool IsValid() const { return is_valid_; } + + // This is mostly extracted from XDBF. + std::string GetTitleName( + const XLanguage language = XLanguage::kInvalid) const; + + XLanguage GetDefaultLanguage() const; + std::string GetLocalizedString( + const uint32_t id, XLanguage language = XLanguage::kInvalid) const; + + std::vector GetIcon() const; + + Context GetContext(const uint32_t id) const; + Property GetProperty(const uint32_t id) const; + Achievement GetAchievement(const uint32_t id) const; + + // TODO: Implement it in the future. + StatsView GetStatsView(const uint32_t id) const; + std::vector GetMatchmakingAttributes(const uint32_t id) const; + + // This is extracted from XLast. + Query GetQueryData(const uint32_t id) const; + std::vector GetSupportedLanguages() const; + ProductInformation GetProductInformation() const; + + // Aggregators for specific usecases + std::vector GetContexts() const; + std::vector GetProperties() const; + std::vector GetAchievements() const; + // TODO: Implement it in the future. + std::vector GetStatsViews() const; + + private: + bool is_valid_ = false; + std::unique_ptr xdbf_gamedata_; + std::unique_ptr xlast_gamedata_; +}; + +} // namespace util +} // namespace kernel +} // namespace xe + +#endif // XENIA_KERNEL_UTIL_GAME_INFO_DATABASE_H_ \ No newline at end of file diff --git a/src/xenia/kernel/util/xlast.cc b/src/xenia/kernel/util/xlast.cc index 464d4209c..19f5f485a 100644 --- a/src/xenia/kernel/util/xlast.cc +++ b/src/xenia/kernel/util/xlast.cc @@ -86,7 +86,7 @@ XLast::XLast(const uint8_t* compressed_xml_data, XLast::~XLast() {} -std::u16string XLast::GetTitleName() { +std::u16string XLast::GetTitleName() const { std::string xpath = "/XboxLiveSubmissionProject/GameConfigProject"; const pugi::xpath_node node = parsed_xlast_->select_node(xpath.c_str()); @@ -97,8 +97,70 @@ std::u16string XLast::GetTitleName() { return xe::to_utf16(node.node().attribute("titleName").value()); } +std::map +XLast::GetProductInformationAttributes() const { + std::map attributes; + + std::string xpath = + "/XboxLiveSubmissionProject/GameConfigProject/ProductInformation"; + + const pugi::xpath_node node = parsed_xlast_->select_node(xpath.c_str()); + if (!node) { + return attributes; + } + + const auto node_attributes = node.node().attributes(); + for (const auto& attribute : node_attributes) { + const auto entry = + product_information_entry_string_to_enum.find(attribute.name()); + if (entry == product_information_entry_string_to_enum.cend()) { + XELOGW("GetProductInformationAttributes: Missing attribute: {}", + attribute.name()); + continue; + } + + std::string attribute_value = std::string(attribute.value()); + if (attribute_value.empty()) { + XELOGW( + "GetProductInformationAttributes: Attribute: {} Contains no value!", + attribute.name()); + continue; + } + + attributes.emplace(entry->second, + xe::string_util::from_string(attribute_value)); + } + + return attributes; +} + +std::vector XLast::GetSupportedLanguages() const { + std::vector launguages; + + std::string xpath = fmt::format( + "/XboxLiveSubmissionProject/GameConfigProject/LocalizedStrings"); + + const pugi::xpath_node node = parsed_xlast_->select_node(xpath.c_str()); + if (!node) { + return launguages; + } + + const auto locale = node.node().children("SupportedLocale"); + for (auto itr = locale.begin(); itr != locale.end(); itr++) { + const std::string locale_name = itr->attribute("locale").value(); + + for (const auto& language : language_mapping) { + if (language.second == locale_name) { + launguages.push_back(language.first); + } + } + } + + return launguages; +} + std::u16string XLast::GetLocalizedString(uint32_t string_id, - XLanguage language) { + XLanguage language) const { std::string xpath = fmt::format( "/XboxLiveSubmissionProject/GameConfigProject/LocalizedStrings/" "LocalizedString[@id = \"{}\"]", @@ -120,7 +182,8 @@ std::u16string XLast::GetLocalizedString(uint32_t string_id, return xe::to_utf16(locale_node.child_value()); } -XLastMatchmakingQuery* XLast::GetMatchmakingQuery(const uint32_t query_id) { +XLastMatchmakingQuery* XLast::GetMatchmakingQuery( + const uint32_t query_id) const { std::string xpath = fmt::format( "/XboxLiveSubmissionProject/GameConfigProject/Matchmaking/Queries/" "Query[@id = \"{}\"]", @@ -151,7 +214,7 @@ std::vector XLast::GetAllValuesFromNode( return result; } -void XLast::Dump(std::string file_name) { +void XLast::Dump(std::string file_name) const { if (xlast_decompressed_xml_.empty()) { return; } @@ -171,7 +234,7 @@ void XLast::Dump(std::string file_name) { fclose(outfile); } -std::string XLast::GetLocaleStringFromLanguage(XLanguage language) { +std::string XLast::GetLocaleStringFromLanguage(XLanguage language) const { const auto value = language_mapping.find(language); if (value != language_mapping.cend()) { return value->second; diff --git a/src/xenia/kernel/util/xlast.h b/src/xenia/kernel/util/xlast.h index 8d6e197b2..45aaf8564 100644 --- a/src/xenia/kernel/util/xlast.h +++ b/src/xenia/kernel/util/xlast.h @@ -21,6 +21,27 @@ namespace xe { namespace kernel { namespace util { +enum class ProductInformationEntry { + MaxOfflinePlayers, + MaxSystemLinkPlayers, + MaxLivePlayers, + PublisherString, + DeveloperString, + MarketingString, + GenreTypeString +}; + +static const std::map + product_information_entry_string_to_enum = { + {"offlinePlayersMax", ProductInformationEntry::MaxOfflinePlayers}, + {"systemLinkPlayersMax", ProductInformationEntry::MaxSystemLinkPlayers}, + {"livePlayersMax", ProductInformationEntry::MaxLivePlayers}, + {"publisherStringId", ProductInformationEntry::PublisherString}, + {"developerStringId", ProductInformationEntry::DeveloperString}, + {"sellTextStringId", ProductInformationEntry::MarketingString}, + {"genreTextStringId", ProductInformationEntry::GenreTypeString}, +}; + static const std::map language_mapping = { {XLanguage::kEnglish, "en-US"}, {XLanguage::kJapanese, "ja-JP"}, {XLanguage::kGerman, "de-DE"}, {XLanguage::kFrench, "fr-FR"}, @@ -50,17 +71,22 @@ class XLast { const uint32_t decompressed_data_size); ~XLast(); - std::u16string GetTitleName(); - std::u16string GetLocalizedString(uint32_t string_id, XLanguage language); - XLastMatchmakingQuery* GetMatchmakingQuery(uint32_t query_id); + std::u16string GetTitleName() const; + std::map GetProductInformationAttributes() + const; + + std::vector GetSupportedLanguages() const; + std::u16string GetLocalizedString(uint32_t string_id, + XLanguage language) const; + XLastMatchmakingQuery* GetMatchmakingQuery(uint32_t query_id) const; static std::vector GetAllValuesFromNode( const pugi::xpath_node node, const std::string child_name, const std::string attirbute_name); - void Dump(std::string file_name); + void Dump(std::string file_name) const; private: - std::string GetLocaleStringFromLanguage(XLanguage language); + std::string GetLocaleStringFromLanguage(XLanguage language) const; std::vector xlast_decompressed_xml_; std::unique_ptr parsed_xlast_ = nullptr; diff --git a/src/xenia/kernel/util/xuserdata.h b/src/xenia/kernel/util/xuserdata.h index 794882b6d..88ea6e6d6 100644 --- a/src/xenia/kernel/util/xuserdata.h +++ b/src/xenia/kernel/util/xuserdata.h @@ -16,6 +16,16 @@ namespace xe { namespace kernel { +union AttributeKey { + uint32_t value; + struct { + uint32_t id : 14; + uint32_t unk : 2; + uint32_t size : 12; + uint32_t type : 4; + }; +}; + enum class X_USER_DATA_TYPE : uint8_t { CONTENT = 0, INT32 = 1, @@ -64,16 +74,6 @@ class DataByteStream : public ByteStream { class UserData { public: - union Key { - uint32_t value; - struct { - uint32_t id : 14; - uint32_t unk : 2; - uint32_t size : 12; - uint32_t type : 4; - }; - }; - UserData(){}; UserData(X_USER_DATA_TYPE type) { data_.type = type; } diff --git a/src/xenia/kernel/xam/user_profile.h b/src/xenia/kernel/xam/user_profile.h index 0d226ed2f..a8d684f1b 100644 --- a/src/xenia/kernel/xam/user_profile.h +++ b/src/xenia/kernel/xam/user_profile.h @@ -146,7 +146,7 @@ class UserSetting { X_USER_PROFILE_SETTING_SOURCE::DEFAULT; X_USER_PROFILE_SETTING_HEADER header_ = {}; - UserData::Key setting_id_ = {}; + AttributeKey setting_id_ = {}; std::unique_ptr user_data_ = nullptr; }; diff --git a/src/xenia/kernel/xam/xam_user.cc b/src/xenia/kernel/xam/xam_user.cc index 41e84fbe3..390657111 100644 --- a/src/xenia/kernel/xam/xam_user.cc +++ b/src/xenia/kernel/xam/xam_user.cc @@ -199,7 +199,7 @@ uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index, uint32_t needed_data_size = 0; for (uint32_t i = 0; i < setting_count; ++i) { needed_header_size += sizeof(X_USER_PROFILE_SETTING); - UserData::Key setting_key; + AttributeKey setting_key; setting_key.value = static_cast(setting_ids[i]); switch (static_cast(setting_key.type)) { case X_USER_DATA_TYPE::WSTRING: