From b117f6da5d32918ac77bd13bda5e2972a8eebd15 Mon Sep 17 00:00:00 2001 From: Adrian <78108584+AdrianCassar@users.noreply.github.com> Date: Thu, 30 Jan 2025 21:34:43 +0000 Subject: [PATCH] [Kernel] Support contexts as properties --- src/xenia/app/discord/discord_presence.cc | 21 +- src/xenia/app/discord/discord_presence.h | 5 +- src/xenia/app/xenia_main.cc | 9 +- src/xenia/emulator.cc | 9 + src/xenia/kernel/XLiveAPI.cpp | 67 +---- src/xenia/kernel/XLiveAPI.h | 2 +- .../kernel/json/properties_object_json.cc | 26 +- .../kernel/util/presence_string_builder.cc | 79 +++--- .../kernel/util/presence_string_builder.h | 12 +- src/xenia/kernel/util/property.cc | 6 +- src/xenia/kernel/util/property.h | 3 +- src/xenia/kernel/util/xlast.cc | 229 ++++++++++++++---- src/xenia/kernel/util/xlast.h | 47 +++- src/xenia/kernel/util/xuserdata.h | 2 +- src/xenia/kernel/xam/apps/xgi_app.cc | 181 +++++++++----- src/xenia/kernel/xam/profile_manager.cc | 2 + src/xenia/kernel/xam/user_profile.cc | 114 +++++++-- src/xenia/kernel/xam/user_profile.h | 8 +- src/xenia/kernel/xam/xam_user.cc | 2 +- src/xenia/kernel/xnet.h | 2 +- src/xenia/kernel/xsession.cc | 92 +++++-- src/xenia/kernel/xsession.h | 21 +- 22 files changed, 635 insertions(+), 304 deletions(-) diff --git a/src/xenia/app/discord/discord_presence.cc b/src/xenia/app/discord/discord_presence.cc index 4140a90767c..abfd5b5593c 100644 --- a/src/xenia/app/discord/discord_presence.cc +++ b/src/xenia/app/discord/discord_presence.cc @@ -7,9 +7,11 @@ ****************************************************************************** */ -#include "discord_presence.h" #include -#include "third_party/discord-rpc/include/discord_rpc.h" + +#include + +#include "xenia/app/discord/discord_presence.h" #include "xenia/base/string.h" // TODO: This library has been deprecated in favor of Discord's GameSDK. @@ -37,23 +39,28 @@ void DiscordPresence::NotPlaying() { discordPresence.state = "Idle"; discordPresence.details = "Standby"; discordPresence.largeImageKey = "app"; - discordPresence.largeImageText = "Xenia Canary - Experimental Testing branch"; + discordPresence.largeImageText = "Xenia Canary - Netplay"; discordPresence.startTimestamp = time(0); discordPresence.instance = 1; Discord_UpdatePresence(&discordPresence); } -void DiscordPresence::PlayingTitle(const std::string_view game_title) { +void DiscordPresence::PlayingTitle(const std::string_view game_title, + const std::string_view state) { + if (!start_time) { + start_time = time(0); + } + auto details = std::string(game_title); DiscordRichPresence discordPresence = {}; - discordPresence.state = "In Game"; + discordPresence.state = state.data(); discordPresence.details = details.c_str(); // TODO(gibbed): we don't have state icons yet. // discordPresence.smallImageKey = "app"; // discordPresence.largeImageKey = "state_ingame"; discordPresence.largeImageKey = "app"; - discordPresence.largeImageText = "Xenia Canary - Experimental Testing branch"; - discordPresence.startTimestamp = time(0); + discordPresence.largeImageText = "Xenia Canary - Netplay"; + discordPresence.startTimestamp = start_time; discordPresence.instance = 1; Discord_UpdatePresence(&discordPresence); } diff --git a/src/xenia/app/discord/discord_presence.h b/src/xenia/app/discord/discord_presence.h index 141385a7b77..f2b3b588ad5 100644 --- a/src/xenia/app/discord/discord_presence.h +++ b/src/xenia/app/discord/discord_presence.h @@ -19,8 +19,11 @@ class DiscordPresence { public: static void Initialize(); static void NotPlaying(); - static void PlayingTitle(const std::string_view game_title); + static void PlayingTitle(const std::string_view game_titleconst, + std::string_view state); static void Shutdown(); + + inline static time_t start_time; }; } // namespace discord diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index 1aedca917af..2df0db4b6c3 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -639,7 +639,8 @@ void EmulatorApp::EmulatorThread() { emulator_->on_launch.AddListener([&](auto title_id, const auto& game_title) { if (cvars::discord) { discord::DiscordPresence::PlayingTitle( - game_title.empty() ? "Unknown Title" : std::string(game_title)); + game_title.empty() ? "Unknown Title" : std::string(game_title), + "In Game"); } app_context().CallInUIThread([this]() { emulator_window_->UpdateTitle(); }); emulator_thread_event_->Set(); @@ -648,11 +649,11 @@ void EmulatorApp::EmulatorThread() { emulator_->on_presence_change.AddListener( [&](const auto& game_title, const auto& presence_string) { if (cvars::discord) { - std::string title = + const std::string title = game_title.empty() ? "Unknown Title" : std::string(game_title); - discord::DiscordPresence::PlayingTitle( - fmt::format("{}\n{}", title, xe::to_utf8(presence_string))); + discord::DiscordPresence::PlayingTitle(title, + xe::to_utf8(presence_string)); } }); diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 01fb7f3f4d9..d4eb3f3d782 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -1519,6 +1519,15 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, } } } + + for (uint32_t i = 0; i < XUserMaxUserCount; i++) { + if (kernel_state()->xam_state()->IsUserSignedIn(i)) { + kernel_state() + ->xam_state() + ->GetUserProfile(i) + ->InitializeSystemContexts(); + } + } } // Initializing the shader storage in a blocking way so the user doesn't diff --git a/src/xenia/kernel/XLiveAPI.cpp b/src/xenia/kernel/XLiveAPI.cpp index 1c1280468b5..0f638e866b6 100644 --- a/src/xenia/kernel/XLiveAPI.cpp +++ b/src/xenia/kernel/XLiveAPI.cpp @@ -9,7 +9,7 @@ #include -#include +#include "third_party/rapidcsv/src/rapidcsv.h" #include "xenia/base/cvar.h" #include "xenia/base/logging.h" @@ -1131,71 +1131,8 @@ void XLiveAPI::XSessionCreate(uint64_t sessionId, XSessionData* data) { XELOGI("XSessionCreate POST Success"); } -// A context is a type of property therefore replace with properties endpoint -void XLiveAPI::SessionContextSet(uint64_t session_id, - std::map contexts) { - std::string endpoint = fmt::format("title/{:08X}/sessions/{:016x}/context", - kernel_state()->title_id(), session_id); - - Document doc; - doc.SetObject(); - - Value contextsJson(kArrayType); - - for (const auto& entry : contexts) { - Value contextJson(kObjectType); - contextJson.AddMember("contextId", entry.first, doc.GetAllocator()); - contextJson.AddMember("value", entry.second, doc.GetAllocator()); - contextsJson.PushBack(contextJson.Move(), doc.GetAllocator()); - } - - doc.AddMember("contexts", contextsJson, doc.GetAllocator()); - - rapidjson::StringBuffer buffer; - PrettyWriter writer(buffer); - doc.Accept(writer); - - std::unique_ptr response = - Post(endpoint, (uint8_t*)buffer.GetString()); - - if (response->StatusCode() != HTTP_STATUS_CODE::HTTP_CREATED) { - XELOGE("SessionContextSet error message: {}", response->Message()); - assert_always(); - } -} - -const std::map XLiveAPI::SessionContextGet( - uint64_t session_id) { - std::string endpoint = fmt::format("title/{:08X}/sessions/{:016x}/context", - kernel_state()->title_id(), session_id); - - std::map result = {}; - - std::unique_ptr response = Get(endpoint); - - if (response->StatusCode() != HTTP_STATUS_CODE::HTTP_OK) { - XELOGE("SessionContextGet error message: {}", response->Message()); - assert_always(); - - return result; - } - - Document doc; - doc.Parse(response->RawResponse().response); - - const Value& contexts = doc["context"]; - - for (auto itr = contexts.MemberBegin(); itr != contexts.MemberEnd(); itr++) { - const uint32_t context_id = - xe::string_util::from_string(itr->name.GetString(), true); - result.insert({context_id, itr->value.GetUint()}); - } - - return result; -} - void XLiveAPI::SessionPropertiesAdd(uint64_t session_id, - std::vector properties) { + std::vector& properties) { std::string endpoint = fmt::format("title/{:08X}/sessions/{:016x}/properties", kernel_state()->title_id(), session_id); diff --git a/src/xenia/kernel/XLiveAPI.h b/src/xenia/kernel/XLiveAPI.h index ff06edb3bba..bcd49f7331a 100644 --- a/src/xenia/kernel/XLiveAPI.h +++ b/src/xenia/kernel/XLiveAPI.h @@ -111,7 +111,7 @@ class XLiveAPI { uint64_t session_id); static void SessionPropertiesAdd(uint64_t session_id, - std::vector properties); + std::vector& properties); static const std::vector SessionPropertiesGet(uint64_t session_id); diff --git a/src/xenia/kernel/json/properties_object_json.cc b/src/xenia/kernel/json/properties_object_json.cc index 72d6f58c828..41151e6fb0f 100644 --- a/src/xenia/kernel/json/properties_object_json.cc +++ b/src/xenia/kernel/json/properties_object_json.cc @@ -22,32 +22,30 @@ PropertiesObjectJSON::PropertiesObjectJSON() : properties_({}) {} PropertiesObjectJSON::~PropertiesObjectJSON() {} bool PropertiesObjectJSON::Deserialize(const rapidjson::Value& obj) { - bool valid = false; - if (obj.HasMember("properties")) { auto& propertiesObj = obj["properties"]; if (!propertiesObj.IsArray()) { - return valid; + return false; } - valid = true; - for (auto& serialized_property : propertiesObj.GetArray()) { - std::string base64 = serialized_property.GetString(); + const std::string base64 = serialized_property.GetString(); std::uint32_t base64_size = static_cast(base64.size()); std::uint32_t base64_decode_size = AV_BASE64_DECODE_SIZE(base64_size); - uint8_t* data_out = new uint8_t[base64_decode_size]; - auto out = av_base64_decode(data_out, base64.c_str(), base64_decode_size); + std::vector data_out(base64_decode_size); + + auto out = + av_base64_decode(data_out.data(), base64.c_str(), base64_decode_size); - const Property property_ = Property(data_out, base64_decode_size); + const Property property_ = Property(data_out.data(), base64_decode_size); properties_.push_back(property_); } } - return valid; + return true; } bool PropertiesObjectJSON::Serialize( @@ -64,12 +62,12 @@ bool PropertiesObjectJSON::Serialize( const uint32_t entry_size = static_cast(entry_data.size()); const uint32_t entry_out_size = AV_BASE64_SIZE(entry_size); - char* entry_serialized = new char[entry_out_size]; - auto out = av_base64_encode(entry_serialized, entry_out_size, + std::vector entry_serialized(entry_out_size); + + auto out = av_base64_encode(entry_serialized.data(), entry_out_size, entry_data.data(), entry_size); - std::string base64_out = std::string(entry_serialized); - const uint32_t base64_out_size = static_cast(base64_out.size()); + std::string base64_out = std::string(entry_serialized.data()); writer->String(base64_out); } diff --git a/src/xenia/kernel/util/presence_string_builder.cc b/src/xenia/kernel/util/presence_string_builder.cc index 59bca449ad0..a1d2ad12d46 100644 --- a/src/xenia/kernel/util/presence_string_builder.cc +++ b/src/xenia/kernel/util/presence_string_builder.cc @@ -24,7 +24,6 @@ AttributeStringFormatter::AttributeStringFormatter( : attribute_string_(attribute_string), attribute_to_string_mapping_() { auto const profile = kernel_state()->xam_state()->GetUserProfile(user_index); - contexts_ = profile->contexts_; properties_ = profile->properties_; title_xlast_ = title_xlast; @@ -36,6 +35,12 @@ AttributeStringFormatter::AttributeStringFormatter( } BuildPresenceString(); + + // Check if precebse string is completed + attribute_string_ = presence_string_; + + const auto specifiers = GetPresenceFormatSpecifiers(); + is_complete_ = specifiers.empty(); } bool AttributeStringFormatter::ParseAttributeString() { @@ -80,7 +85,8 @@ AttributeStringFormatter::GetAttributeTypeFromSpecifier( return AttributeStringFormatter::AttributeType::Unknown; } -std::optional AttributeStringFormatter::GetAttributeIdFromSpecifier( +std::optional +AttributeStringFormatter::GetAttributeIdFromSpecifier( const std::string& specifier, const AttributeStringFormatter::AttributeType specifier_type) const { std::smatch string_match; @@ -96,14 +102,14 @@ std::optional AttributeStringFormatter::GetAttributeIdFromSpecifier( id = string_util::from_string(string_match[4].str(), true); } - return std::make_optional(id); + return std::make_optional(id); } return std::nullopt; } std::u16string AttributeStringFormatter::GetStringFromSpecifier( - std::string_view specifier) const { + std::string_view specifier) { const AttributeStringFormatter::AttributeType attribute_type = GetAttributeTypeFromSpecifier(specifier); @@ -114,29 +120,30 @@ std::u16string AttributeStringFormatter::GetStringFromSpecifier( const auto attribute_id = GetAttributeIdFromSpecifier(std::string(specifier), attribute_type); - if (!attribute_id) { + if (!attribute_id.has_value()) { return u""; } - if (attribute_type == AttributeStringFormatter::AttributeType::Context) { - const auto itr = contexts_.find(attribute_id.value()); + const auto property = GetProperty(attribute_id.value()); - if (itr == contexts_.cend()) { + if (attribute_type == AttributeStringFormatter::AttributeType::Context) { + if (property == nullptr) { const auto attribute_placeholder = - xe::to_utf16(fmt::format("{{c{}}}", attribute_id.value())); + xe::to_utf16(fmt::format("{{c{}}}", attribute_id.value().value)); return attribute_placeholder; } std::optional attribute_string_id = std::nullopt; - if (attribute_id == X_CONTEXT_GAME_MODE && - contexts_.contains(X_CONTEXT_GAME_MODE)) { - attribute_string_id = - title_xlast_->GetGameModeStringId(contexts_.at(X_CONTEXT_GAME_MODE)); + const uint32_t value = std::get(property->GetValueGuest()); + + if (attribute_id.value().id == X_CONTEXT_GAME_MODE) { + attribute_string_id = title_xlast_->GetGameModeStringId(value); } else { attribute_string_id = - title_xlast_->GetContextStringId(attribute_id.value(), itr->second); + title_xlast_->GetContextsQuery()->GetContextValueStringID( + attribute_id.value().value, value); } if (!attribute_string_id.has_value()) { @@ -151,24 +158,27 @@ std::u16string AttributeStringFormatter::GetStringFromSpecifier( } if (attribute_type == AttributeStringFormatter::AttributeType::Property) { - auto itr = std::find_if( - properties_.begin(), properties_.end(), [attribute_id](Property prop) { - if (prop.GetPropertyId().value == attribute_id.value()) { - return true; - } - - return false; - }); - - if (itr == properties_.end()) { - const auto attribute_placeholder = - xe::to_utf16(fmt::format("{{p0x{:08X}}}", attribute_id.value())); + if (property == nullptr) { + const auto attribute_placeholder = xe::to_utf16( + fmt::format("{{p0x{:08X}}}", attribute_id.value().value)); return attribute_placeholder; } - // Support properties: INT32, INT64, float, double - const uint64_t value = std::get(itr->GetValueGuest()); + uint64_t value = 0; + + switch (property->GetType()) { + case X_USER_DATA_TYPE::INT32: + value = std::get(property->GetValueGuest()); + break; + case X_USER_DATA_TYPE::INT64: + case X_USER_DATA_TYPE::DATETIME: + value = std::get(property->GetValueGuest()); + break; + default: + XELOGI("Unsupported property type {}", property->GetPropertyId().type); + break; + } return xe::to_utf16(fmt::format("{}", value)); } @@ -199,6 +209,19 @@ std::queue AttributeStringFormatter::GetPresenceFormatSpecifiers() return format_specifiers; } + +Property* AttributeStringFormatter::GetProperty(const AttributeKey id) { + for (auto& entry : properties_) { + if (entry.GetPropertyId().value != id.value) { + continue; + } + + return &entry; + } + + return nullptr; +} + } // namespace util } // namespace kernel } // namespace xe \ No newline at end of file diff --git a/src/xenia/kernel/util/presence_string_builder.h b/src/xenia/kernel/util/presence_string_builder.h index 03f2931c305..fd938051b8b 100644 --- a/src/xenia/kernel/util/presence_string_builder.h +++ b/src/xenia/kernel/util/presence_string_builder.h @@ -33,6 +33,7 @@ class AttributeStringFormatter { XLast* title_xlast, uint32_t user_index); bool IsValid() const { return true; } + bool IsComplete() const { return is_complete_; } std::u16string GetPresenceString() const { return presence_string_; } private: @@ -46,24 +47,27 @@ class AttributeStringFormatter { bool ParseAttributeString(); void BuildPresenceString(); - std::u16string GetStringFromSpecifier(std::string_view specifier) const; + std::u16string GetStringFromSpecifier(std::string_view specifier); std::queue GetPresenceFormatSpecifiers() const; + Property* GetProperty(const AttributeKey id); + AttributeType GetAttributeTypeFromSpecifier(std::string_view specifier) const; - std::optional GetAttributeIdFromSpecifier( + std::optional GetAttributeIdFromSpecifier( const std::string& specifier, const AttributeStringFormatter::AttributeType specifier_type) const; - const std::u16string attribute_string_; + std::u16string attribute_string_; std::map attribute_to_string_mapping_; std::u16string presence_string_; - std::map contexts_; std::vector properties_; XLast* title_xlast_; + bool is_complete_; + // Tests // // std::map contexts_ = { diff --git a/src/xenia/kernel/util/property.cc b/src/xenia/kernel/util/property.cc index bf4ee8ec254..73f85bd347f 100644 --- a/src/xenia/kernel/util/property.cc +++ b/src/xenia/kernel/util/property.cc @@ -69,7 +69,6 @@ void Property::Write(Memory* memory, XUSER_PROPERTY* property) const { property->data.type = data_type_; switch (data_type_) { - case X_USER_DATA_TYPE::CONTENT: case X_USER_DATA_TYPE::WSTRING: case X_USER_DATA_TYPE::BINARY: property->data.binary.size = value_size_; @@ -77,6 +76,7 @@ void Property::Write(Memory* memory, XUSER_PROPERTY* property) const { memcpy(memory->TranslateVirtual(property->data.binary.ptr), value_.data(), value_size_); break; + case X_USER_DATA_TYPE::CONTEXT: case X_USER_DATA_TYPE::INT32: memcpy(reinterpret_cast(&property->data.s32), value_.data(), value_size_); @@ -106,7 +106,7 @@ userDataVariant Property::GetValueGuest() const { switch (data_type_) { case X_USER_DATA_TYPE::BINARY: return value_; - case X_USER_DATA_TYPE::CONTENT: + case X_USER_DATA_TYPE::CONTEXT: case X_USER_DATA_TYPE::INT32: return xe::load_and_swap(value_.data()); case X_USER_DATA_TYPE::INT64: @@ -127,9 +127,9 @@ userDataVariant Property::GetValueGuest() const { userDataVariant Property::GetValueHost() const { switch (data_type_) { - case X_USER_DATA_TYPE::CONTENT: case X_USER_DATA_TYPE::BINARY: return value_; + case X_USER_DATA_TYPE::CONTEXT: case X_USER_DATA_TYPE::INT32: return *reinterpret_cast(value_.data()); case X_USER_DATA_TYPE::INT64: diff --git a/src/xenia/kernel/util/property.h b/src/xenia/kernel/util/property.h index 4d47a5d6bd0..72d73046b1d 100644 --- a/src/xenia/kernel/util/property.h +++ b/src/xenia/kernel/util/property.h @@ -51,11 +51,10 @@ class Property { void Write(Memory* memory, XUSER_PROPERTY* property) const; uint32_t GetSize() const { return value_size_; } + X_USER_DATA_TYPE GetType() const { return data_type_; } bool RequiresPointer() const { return static_cast(property_id_.type) == - X_USER_DATA_TYPE::CONTENT || - static_cast(property_id_.type) == X_USER_DATA_TYPE::WSTRING || static_cast(property_id_.type) == X_USER_DATA_TYPE::BINARY; diff --git a/src/xenia/kernel/util/xlast.cc b/src/xenia/kernel/util/xlast.cc index feaf22dcf14..e416f64e149 100644 --- a/src/xenia/kernel/util/xlast.cc +++ b/src/xenia/kernel/util/xlast.cc @@ -18,6 +18,8 @@ namespace xe { namespace kernel { namespace util { +#pragma region XLastMatchmakingQuery + XLastMatchmakingQuery::XLastMatchmakingQuery() {} XLastMatchmakingQuery::XLastMatchmakingQuery( const pugi::xpath_node query_node) { @@ -34,11 +36,12 @@ pugi::xml_node XLastMatchmakingQuery::GetQuery(uint32_t query_id) const { return query_node; } std::vector XLastMatchmakingQuery::GetSchema() const { - return XLast::GetAllValuesFromNode(node_, "Schema", "id"); + return XLast::GetAllValuesFromNode(node_.parent().parent(), "Schema", "id"); } std::vector XLastMatchmakingQuery::GetConstants() const { - return XLast::GetAllValuesFromNode(node_, "Constants", "id"); + return XLast::GetAllValuesFromNode(node_.parent().parent(), "Constants", + "id"); } std::string XLastMatchmakingQuery::GetName(uint32_t query_id) const { @@ -65,13 +68,24 @@ std::vector XLastMatchmakingQuery::GetFiltersRight( return XLast::GetAllValuesFromNode(GetQuery(query_id), "Filters", "right"); } +#pragma endregion + +#pragma region XLastPropertiesQuery + XLastPropertiesQuery::XLastPropertiesQuery() {} XLastPropertiesQuery::XLastPropertiesQuery(const pugi::xpath_node query_node) { node_ = query_node; } std::vector XLastPropertiesQuery::GetPropertyIDs() const { - return XLast::GetAllValuesFromNode(node_, "Property", "id"); + std::vector result = {}; + + for (pugi::xml_node child : node_.node().children()) { + result.push_back(xe::string_util::from_string( + child.attribute("id").value(), true)); + } + + return result; } pugi::xml_node XLastPropertiesQuery::GetPropertyNode( @@ -85,16 +99,43 @@ pugi::xml_node XLastPropertiesQuery::GetPropertyNode( return property_node; } -std::string XLastPropertiesQuery::GetPropertyName(uint32_t property_id) const { - return GetPropertyNode(property_id).attribute("friendlyName").as_string(); +std::optional XLastPropertiesQuery::GetPropertyFriendlyName( + uint32_t property_id) const { + std::optional value = std::nullopt; + + pugi::xml_node property_node = GetPropertyNode(property_id); + + if (property_node) { + value = property_node.attribute("friendlyName").as_string(); + } + + return value; } -uint32_t XLastPropertiesQuery::GetPropertySize(uint32_t property_id) const { - return GetPropertyNode(property_id).attribute("dataSize").as_uint(); +std::optional XLastPropertiesQuery::GetPropertySize( + uint32_t property_id) const { + std::optional value = std::nullopt; + + pugi::xml_node property_node = GetPropertyNode(property_id); + + if (property_node) { + value = property_node.attribute("dataSize").as_uint(); + } + + return value; } -uint32_t XLastPropertiesQuery::GetPropertyStringID(uint32_t property_id) const { - return GetPropertyNode(property_id).attribute("stringId").as_uint(); +std::optional XLastPropertiesQuery::GetPropertyStringID( + uint32_t property_id) const { + std::optional value = std::nullopt; + + pugi::xml_node property_node = GetPropertyNode(property_id); + + if (property_node) { + value = property_node.attribute("stringId").as_uint(); + } + + return value; } pugi::xml_node XLastPropertiesQuery::GetPropertyFormat( @@ -102,6 +143,109 @@ pugi::xml_node XLastPropertiesQuery::GetPropertyFormat( return GetPropertyNode(property_id).child("Format"); } +#pragma endregion + +#pragma region XLastContextsQuery + +XLastContextsQuery::XLastContextsQuery() {} +XLastContextsQuery::XLastContextsQuery(const pugi::xpath_node query_node) { + node_ = query_node; +} + +std::vector XLastContextsQuery::GetContextsIDs() const { + std::vector result = {}; + + for (pugi::xml_node child : node_.node().children()) { + result.push_back(xe::string_util::from_string( + child.attribute("id").value(), true)); + } + + return result; +} + +pugi::xml_node XLastContextsQuery::GetContextNode(uint32_t property_id) const { + pugi::xml_node property_node; + + std::string xpath = fmt::format("Context[@id = \"0x{:08X}\"]", property_id); + + property_node = node_.node().select_node(xpath.c_str()).node(); + + return property_node; +} + +std::optional XLastContextsQuery::GetContextFriendlyName( + uint32_t property_id) const { + std::optional value = std::nullopt; + + pugi::xml_node property_node = GetContextNode(property_id); + + if (property_node) { + value = property_node.attribute("friendlyName").as_string(); + } + + return value; +} + +std::optional XLastContextsQuery::GetContextDefaultValue( + uint32_t property_id) const { + std::optional value = std::nullopt; + + pugi::xml_node property_node = GetContextNode(property_id); + + if (property_node) { + value = property_node.attribute("defaultValue").as_uint(); + } + + return value; +} + +pugi::xml_node XLastContextsQuery::GetContextValueNode(uint32_t property_id, + uint32_t value) const { + pugi::xml_node context_node = GetContextNode(property_id); + + pugi::xml_node context_value_node; + + std::string xpath = fmt::format("ContextValue[@value = \"{}\"]", value); + + if (context_node) { + context_value_node = context_node.select_node(xpath.c_str()).node(); + } + + return context_value_node; +} + +std::optional XLastContextsQuery::GetContextValueStringID( + uint32_t property_id, uint32_t value) const { + pugi::xml_node context_value_node = GetContextValueNode(property_id, value); + + std::optional string_id = std::nullopt; + + if (context_value_node) { + string_id = context_value_node.attribute("stringId").as_uint(); + } + + return string_id; +} + +#pragma endregion + +#pragma region XLastGameModeQuery + +XLastGameModeQuery::XLastGameModeQuery() {} +XLastGameModeQuery::XLastGameModeQuery(const pugi::xpath_node query_node) { + node_ = query_node; +} + +std::optional XLastGameModeQuery::GetGameModeDefaultValue() const { + std::optional value = std::nullopt; + + value = node_.node().attribute("defaultValue").as_uint(); + + return value; +} + +#pragma endregion + XLast::XLast() : parsed_xlast_(nullptr) {} XLast::XLast(const uint8_t* compressed_xml_data, @@ -304,32 +448,13 @@ const std::optional XLast::GetPresenceStringId( return string_id; } -const std::optional XLast::GetPropertyStringId( - const uint32_t property_id) { - std::string xpath = fmt::format( - "/XboxLiveSubmissionProject/GameConfigProject/Properties/Property[@id = " - "\"0x{:08X}\"]", - property_id); - - std::optional value = std::nullopt; +const std::u16string XLast::GetPresenceRawString( + const Property* presence_property, const XLanguage language) { + const uint32_t context_value = + std::get(presence_property->GetValueGuest()); - if (!HasXLast()) { - return value; - } - - pugi::xpath_node node = parsed_xlast_->select_node(xpath.c_str()); - - if (node) { - value = node.node().attribute("stringId").as_uint(); - } - - return value; -} - -const std::u16string XLast::GetPresenceRawString(const uint32_t presence_value, - const XLanguage language) { const std::optional presence_string_id = - GetPresenceStringId(presence_value); + GetPresenceStringId(context_value); std::u16string raw_presence = u""; @@ -340,30 +465,40 @@ const std::u16string XLast::GetPresenceRawString(const uint32_t presence_value, return raw_presence; } -const std::optional XLast::GetContextStringId( - const uint32_t context_id, const uint32_t context_value) { - std::string xpath = fmt::format( - "/XboxLiveSubmissionProject/GameConfigProject/Contexts/Context[@id = " - "\"0x{:08X}\"]/ContextValue[@value = \"{}\"]", - context_id, context_value); +XLastGameModeQuery* XLast::GameModeQuery() const { + std::string xpath = + fmt::format("/XboxLiveSubmissionProject/GameConfigProject/GameModes"); - std::optional value = std::nullopt; + XLastGameModeQuery* query = nullptr; if (!HasXLast()) { - return value; + return query; } pugi::xpath_node node = parsed_xlast_->select_node(xpath.c_str()); + if (!node) { + return query; + } - if (node) { - // const auto default_value = - // node.node().parent().attribute("defaultValue").value(); - // value = xe::string_util::from_string(default_value); + return new XLastGameModeQuery(node); +} - value = node.node().attribute("stringId").as_uint(); +XLastContextsQuery* XLast::GetContextsQuery() const { + std::string xpath = + fmt::format("/XboxLiveSubmissionProject/GameConfigProject/Contexts"); + + XLastContextsQuery* query = nullptr; + + if (!HasXLast()) { + return query; } - return value; + pugi::xpath_node node = parsed_xlast_->select_node(xpath.c_str()); + if (!node) { + return query; + } + + return new XLastContextsQuery(node); } XLastPropertiesQuery* XLast::GetPropertiesQuery() const { diff --git a/src/xenia/kernel/util/xlast.h b/src/xenia/kernel/util/xlast.h index fe4b093407b..5237a5bcf7b 100644 --- a/src/xenia/kernel/util/xlast.h +++ b/src/xenia/kernel/util/xlast.h @@ -16,6 +16,7 @@ #include #include "third_party/pugixml/src/pugixml.hpp" +#include "xenia/kernel/util/property.h" #include "xenia/xbox.h" namespace xe { @@ -77,15 +78,49 @@ class XLastPropertiesQuery { std::vector GetPropertyIDs() const; pugi::xml_node GetPropertyNode(uint32_t property_id) const; - std::string GetPropertyName(uint32_t property_id) const; - uint32_t GetPropertySize(uint32_t property_id) const; - uint32_t GetPropertyStringID(uint32_t property_id) const; + std::optional GetPropertyFriendlyName( + uint32_t property_id) const; + std::optional GetPropertySize(uint32_t property_id) const; + std::optional GetPropertyStringID(uint32_t property_id) const; pugi::xml_node GetPropertyFormat(uint32_t property_id) const; private: pugi::xpath_node node_; }; +class XLastContextsQuery { + public: + XLastContextsQuery(); + XLastContextsQuery(const pugi::xpath_node query_node); + + std::vector GetContextsIDs() const; + pugi::xml_node GetContextNode(uint32_t property_id) const; + std::optional GetContextFriendlyName(uint32_t property_id) const; + std::optional GetContextDefaultValue(uint32_t property_id) const; + pugi::xml_node GetContextValueNode(uint32_t property_id, + uint32_t value) const; + std::optional GetContextValueStringID(uint32_t property_id, + uint32_t value) const; + + private: + pugi::xpath_node node_; +}; + +class XLastGameModeQuery { + public: + XLastGameModeQuery(); + XLastGameModeQuery(const pugi::xpath_node query_node); + + std::vector GetGameModeValues() const; + pugi::xml_node GetGameModeNode(uint32_t value) const; + std::optional GetGameModeFriendlyName(uint32_t value) const; + std::optional GetGameModeDefaultValue() const; + std::optional GetGameModeStringID(uint32_t value) const; + + private: + pugi::xpath_node node_; +}; + class XLast { public: XLast(); @@ -104,10 +139,10 @@ class XLast { XLanguage language) const; const std::optional GetPresenceStringId(const uint32_t context_id); const std::optional GetPropertyStringId(const uint32_t property_id); - const std::u16string GetPresenceRawString(const uint32_t presence_value, + const std::u16string GetPresenceRawString(const Property* presence_property, const XLanguage language); - const std::optional GetContextStringId( - const uint32_t context_id, const uint32_t context_value); + XLastGameModeQuery* GameModeQuery() const; + XLastContextsQuery* GetContextsQuery() const; XLastPropertiesQuery* GetPropertiesQuery() const; XLastMatchmakingQuery* GetMatchmakingQuery() const; static std::vector GetAllValuesFromNode( diff --git a/src/xenia/kernel/util/xuserdata.h b/src/xenia/kernel/util/xuserdata.h index 1e4f9ba0ca7..8b664b87c93 100644 --- a/src/xenia/kernel/util/xuserdata.h +++ b/src/xenia/kernel/util/xuserdata.h @@ -16,7 +16,7 @@ namespace xe { enum class X_USER_DATA_TYPE : uint8_t { - CONTENT = 0, + CONTEXT = 0, INT32 = 1, INT64 = 2, DOUBLE = 3, diff --git a/src/xenia/kernel/xam/apps/xgi_app.cc b/src/xenia/kernel/xam/apps/xgi_app.cc index 25aeff9d073..ae85f0a9aaf 100644 --- a/src/xenia/kernel/xam/apps/xgi_app.cc +++ b/src/xenia/kernel/xam/apps/xgi_app.cc @@ -71,6 +71,32 @@ struct XUSER_ANID { xe::be value_const; // 1 }; +struct XGISetContext { + xe::be user_index; + xe::be unkn_ptr; + xe::be unkn_2; + XUSER_CONTEXT context; +}; + +// 00000000 2789fecc 00000000 00000000 200491e0 00000000 200491f0 20049340 +struct XGIGetContext { + xe::be user_index; + xe::be unkn_1; + xe::be unkn_2; + xe::be unkn_3; + xe::be context_address; + xe::be unkn_4; +}; + +struct XGISetProperty { + xe::be user_index; + xe::be unkn_1; + xe::be unkn_2; + xe::be property_id; + xe::be data_size; + xe::be data_address; +}; + XgiApp::XgiApp(KernelState* kernel_state) : App(kernel_state, 0xFB) {} // http://mb.mirage.org/bugzilla/xliveless/main.c @@ -319,8 +345,8 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, X_USER_DATA_TYPE stat_type = stat[statIndex].Value.type; switch (stat_type) { - case X_USER_DATA_TYPE::CONTENT: { - XELOGW("Statistic type: CONTENT"); + case X_USER_DATA_TYPE::CONTEXT: { + XELOGW("Statistic type: CONTEXT"); } break; case X_USER_DATA_TYPE::INT32: { XELOGW("Statistic type: INT32"); @@ -405,74 +431,82 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, return session->RegisterArbitration(data); } case 0x000B0006: { - assert_true(!buffer_length || buffer_length == 24); - - // dword r3 user index - // dword (unwritten?) - // qword 0 - // dword r4 context enum - // dword r5 value - uint32_t user_index = xe::load_and_swap(buffer + 0); - uint32_t context_id = xe::load_and_swap(buffer + 16); - uint32_t context_value = xe::load_and_swap(buffer + 20); - XELOGD("XGIUserSetContextEx({:08X}, {:08X}, {:08X})", user_index, - context_id, context_value); + assert_true(!buffer_length || buffer_length == sizeof(XGISetContext)); + + XGISetContext* XGISetContextBuffer = + reinterpret_cast(buffer); + + XELOGD("XGIUserSetContextEx({:08X}, {:08X}, {:08X})", + XGISetContextBuffer->user_index.get(), + XGISetContextBuffer->context.context_id.get(), + XGISetContextBuffer->context.value.get()); const util::XdbfGameData title_xdbf = kernel_state_->title_xdbf(); if (title_xdbf.is_valid()) { - const auto context = title_xdbf.GetContext(context_id); + const auto context = + title_xdbf.GetContext(XGISetContextBuffer->context.context_id); const XLanguage title_language = title_xdbf.GetExistingLanguage( static_cast(XLanguage::kEnglish)); const std::string desc = title_xdbf.GetStringTableEntry(title_language, context.string_id); XELOGD("XGIUserSetContextEx: {} - Set to value: {}", desc, - context_value); + XGISetContextBuffer->context.value.get()); + + UserProfile* user_profile = kernel_state_->xam_state()->GetUserProfile( + XGISetContextBuffer->user_index); - UserProfile* user_profile = - kernel_state_->xam_state()->GetUserProfile(user_index); if (user_profile) { - user_profile->contexts_[context_id] = context_value; + Property context_property = Property( + XGISetContextBuffer->context.context_id, sizeof(uint32_t), + reinterpret_cast(&XGISetContextBuffer->context.value)); - if (context_id == X_CONTEXT_PRESENCE) { - if (user_profile->UpdatePresenceString()) { - const auto presence_string = user_profile->GetPresenceString(); + user_profile->AddProperty(&context_property); - XELOGI("{} presence: {}", user_profile->name(), - xe::to_utf8(presence_string)); + const bool update_discord = cvars::discord_presence_user_index == + XGISetContextBuffer->user_index; - if (cvars::discord_presence_user_index == user_index) { - kernel_state()->emulator()->on_presence_change( - kernel_state()->emulator()->title_name(), presence_string); - } - } - } + user_profile->UpdatePresence(update_discord); } } return X_E_SUCCESS; } case 0x000B0007: { - uint32_t user_index = xe::load_and_swap(buffer + 0); - uint32_t property_id = xe::load_and_swap(buffer + 16); - uint32_t value_size = xe::load_and_swap(buffer + 20); - uint32_t value_ptr = xe::load_and_swap(buffer + 24); - XELOGD("XGIUserSetPropertyEx({:08X}, {:08X}, {}, {:08X})", user_index, - property_id, value_size, value_ptr); + XGISetProperty* XGISetPropertyBuffer = + reinterpret_cast(buffer); + + if (!XGISetPropertyBuffer->data_address) { + return X_E_SUCCESS; + } + + XELOGD("XGIUserSetPropertyEx({:08X}, {:08X}, {}, {:08X})", + XGISetPropertyBuffer->user_index.get(), + XGISetPropertyBuffer->property_id.get(), + XGISetPropertyBuffer->data_size.get(), + XGISetPropertyBuffer->data_address.get()); const util::XdbfGameData title_xdbf = kernel_state_->title_xdbf(); if (title_xdbf.is_valid()) { - const auto property_xdbf = title_xdbf.GetProperty(property_id); + const auto property_xdbf = + title_xdbf.GetProperty(XGISetPropertyBuffer->property_id); const XLanguage title_language = title_xdbf.GetExistingLanguage( static_cast(XLanguage::kEnglish)); const std::string desc = title_xdbf.GetStringTableEntry( title_language, property_xdbf.string_id); - Property property = - Property(property_id, value_size, - memory_->TranslateVirtual(value_ptr)); + Property property = Property(XGISetPropertyBuffer->property_id, + XGISetPropertyBuffer->data_size, + memory_->TranslateVirtual( + XGISetPropertyBuffer->data_address)); - auto user = kernel_state_->xam_state()->GetUserProfile(user_index); - if (user) { - user->AddProperty(&property); + auto user_profile = kernel_state_->xam_state()->GetUserProfile( + XGISetPropertyBuffer->user_index); + if (user_profile) { + user_profile->AddProperty(&property); + + const bool update_discord = cvars::discord_presence_user_index == + XGISetPropertyBuffer->user_index; + + user_profile->UpdatePresence(update_discord); } XELOGD("XGIUserSetPropertyEx: Setting property: {}", desc); } @@ -703,28 +737,53 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, return X_E_FAIL; } case 0x000B0041: { - assert_true(!buffer_length || buffer_length == 32); - // 00000000 2789fecc 00000000 00000000 200491e0 00000000 200491f0 20049340 - uint32_t user_index = xe::load_and_swap(buffer + 0); - uint32_t context_ptr = xe::load_and_swap(buffer + 16); - auto context = - context_ptr ? memory_->TranslateVirtual(context_ptr) : nullptr; - uint32_t context_id = - context ? xe::load_and_swap(context + 0) : 0; - XELOGD("XGIUserGetContext({:08X}, {:08X}{:08X}))", user_index, - context_ptr, context_id); + assert_true(!buffer_length || buffer_length == sizeof(XGIGetContext)); + + XGIGetContext* XGIGetContextBuffer = + reinterpret_cast(buffer); + + XUSER_CONTEXT* context_ptr = + XGIGetContextBuffer->context_address + ? memory_->TranslateVirtual( + XGIGetContextBuffer->context_address) + : nullptr; + + XELOGD("XGIUserGetContext({:08X}, {:08X}))", + XGIGetContextBuffer->user_index.get(), + XGIGetContextBuffer->context_address.get()); + + if (!context_ptr) { + return X_E_SUCCESS; + } + uint32_t value = 0; - if (context) { - UserProfile* user_profile = - kernel_state_->xam_state()->GetUserProfile(user_index); - if (user_profile) { - if (user_profile->contexts_.find(context_id) != - user_profile->contexts_.cend()) { - value = user_profile->contexts_[context_id]; + + UserProfile* user_profile = kernel_state_->xam_state()->GetUserProfile( + XGIGetContextBuffer->user_index); + + if (user_profile) { + const auto property = user_profile->GetProperty( + static_cast(context_ptr->context_id)); + + if (property) { + value = std::get(property->GetValueGuest()); + } else { + const auto xlast = + kernel_state_->emulator()->game_info_database()->GetXLast(); + + if (xlast) { + const auto default_value = + xlast->GetContextsQuery()->GetContextDefaultValue( + context_ptr->context_id); + + if (default_value.has_value()) { + value = default_value.value(); + } } } - xe::store_and_swap(context + 4, value); } + + context_ptr->value = value; return X_E_SUCCESS; } case 0x000B0071: { diff --git a/src/xenia/kernel/xam/profile_manager.cc b/src/xenia/kernel/xam/profile_manager.cc index 35e35176b45..5eb4b4625b7 100644 --- a/src/xenia/kernel/xam/profile_manager.cc +++ b/src/xenia/kernel/xam/profile_manager.cc @@ -336,6 +336,8 @@ void ProfileManager::Login(const uint64_t xuid, const uint8_t user_index, kernel_state_->xam_state()->achievement_manager()->LoadTitleAchievements( xuid, db); } + + logged_profiles_[assigned_user_slot]->InitializeSystemContexts(); } if (notify) { diff --git a/src/xenia/kernel/xam/user_profile.cc b/src/xenia/kernel/xam/user_profile.cc index a4ae9ac19bf..d7ef71d75fb 100644 --- a/src/xenia/kernel/xam/user_profile.cc +++ b/src/xenia/kernel/xam/user_profile.cc @@ -133,33 +133,33 @@ UserProfile::UserProfile(uint64_t xuid, X_XAMACCOUNTINFO* account_info) Property GAMER_NAME = Property(X_PROPERTY_GAMERNAME, gamertag_size, reinterpret_cast(gamertag.data())); - uint32_t gamer_zone = 2; // Pro + xe::be gamer_zone = 2; // Pro Property GAMER_ZONE = Property(X_PROPERTY_GAMER_ZONE, sizeof(uint32_t), reinterpret_cast(&gamer_zone)); - uint32_t gamer_county = cvars::user_country; + xe::be gamer_county = cvars::user_country; Property GAMER_COUNTRY = Property(X_PROPERTY_GAMER_COUNTRY, sizeof(uint32_t), reinterpret_cast(&gamer_county)); - uint32_t gamer_lanuage = cvars::user_language; + xe::be gamer_lanuage = cvars::user_language; Property GAMER_LANGUAGE = Property(X_PROPERTY_GAMER_LANGUAGE, sizeof(uint32_t), reinterpret_cast(&gamer_lanuage)); - uint32_t platform_type = PLATFORM_TYPE::Xbox360; + xe::be platform_type = PLATFORM_TYPE::Xbox360; Property PLATFORM_TYPE = Property(X_PROPERTY_PLATFORM_TYPE, sizeof(uint32_t), reinterpret_cast(&gamer_lanuage)); - uint64_t gamer_mu = 0; + xe::be gamer_mu = 0; Property GAMER_MU = Property(X_PROPERTY_GAMER_MU, sizeof(uint64_t), reinterpret_cast(&gamer_mu)); - uint64_t gamer_sigma = 0; + xe::be gamer_sigma = 0; Property GAMER_SIGMA = Property(X_PROPERTY_GAMER_SIGMA, sizeof(uint64_t), reinterpret_cast(&gamer_sigma)); @@ -175,6 +175,59 @@ UserProfile::UserProfile(uint64_t xuid, X_XAMACCOUNTINFO* account_info) AddProperty(&GAMER_SIGMA); } +void UserProfile::InitializeSystemContexts() { + const auto gdb = kernel_state()->emulator()->game_info_database(); + + xe::be game_mode = 0; + xe::be game_type = 0; + + Property GAME_MODE = Property(X_CONTEXT_GAME_MODE, sizeof(uint32_t), + reinterpret_cast(&game_mode)); + Property GAME_TYPE = Property(X_CONTEXT_GAME_TYPE, sizeof(uint32_t), + reinterpret_cast(&game_type)); + + if (gdb->HasXLast()) { + const auto xlast = gdb->GetXLast(); + + const bool initialize_all_contexts = false; + + // Initialize all contexts to default values + if (initialize_all_contexts) { + const auto context_ids = xlast->GetContextsQuery()->GetContextsIDs(); + + for (const auto context_id : context_ids) { + const auto default_value = + xlast->GetContextsQuery()->GetContextDefaultValue(context_id); + + if (default_value.has_value()) { + xe::be value = default_value.value(); + + auto prop = Property(context_id, sizeof(uint32_t), + reinterpret_cast(&value)); + + AddProperty(&prop); + } + } + } + + // Initialize default system context values + game_mode = xlast->GameModeQuery()->GetGameModeDefaultValue().value(); + + GAME_MODE = Property(X_CONTEXT_GAME_MODE, sizeof(uint32_t), + reinterpret_cast(&game_mode)); + + game_type = xlast->GetContextsQuery() + ->GetContextDefaultValue(X_CONTEXT_GAME_TYPE) + .value(); + + GAME_TYPE = Property(X_CONTEXT_GAME_TYPE, sizeof(uint32_t), + reinterpret_cast(&game_type)); + } + + AddProperty(&GAME_MODE); + AddProperty(&GAME_TYPE); +} + X_ONLINE_FRIEND UserProfile::GenerateDummyFriend() { std::random_device rnd; std::mt19937_64 gen(rnd()); @@ -444,28 +497,55 @@ const std::vector UserProfile::GetSubscribedXUIDs() const { return subscribed_xuids; } -std::u16string UserProfile::GetPresenceString() { +std::u16string UserProfile::GetPresenceString() const { return online_presence_desc_; } -bool UserProfile::UpdatePresenceString() { +bool UserProfile::UpdatePresence(bool update_discord) { + const auto current_presence = GetPresenceString(); + bool updated = false; - if (contexts_.find(X_CONTEXT_PRESENCE) == contexts_.end()) { - return updated; + if (BuildPresenceString()) { + const auto updated_presence = GetPresenceString(); + + updated = current_presence != updated_presence; + + if (!updated) { + return false; + } + + XELOGI("{}: {} - {}", __func__, name(), xe::to_utf8(updated_presence)); + + if (update_discord) { + kernel_state()->emulator()->on_presence_change( + kernel_state()->emulator()->title_name(), updated_presence); + } + } + + return updated; +} + +bool UserProfile::BuildPresenceString() { + bool completed = false; + + const auto context_property = + GetProperty(static_cast(X_CONTEXT_PRESENCE)); + + if (!context_property) { + return completed; } const auto gdb = kernel_state()->emulator()->game_info_database(); if (!gdb->HasXLast()) { - return updated; + return completed; } const auto xlast = gdb->GetXLast(); - const std::u16string raw_presence = - xlast->GetPresenceRawString(contexts_[X_CONTEXT_PRESENCE], - static_cast(cvars::user_language)); + const std::u16string raw_presence = xlast->GetPresenceRawString( + context_property, static_cast(cvars::user_language)); const auto user_index = kernel_state()->xam_state()->GetUserIndexAssignedToProfileFromXUID(xuid_); @@ -478,11 +558,11 @@ bool UserProfile::UpdatePresenceString() { if (online_presence_desc_ != presence_parsed) { online_presence_desc_ = presence_parsed; - - updated = true; } - return updated; + completed = presence_string_formatter.IsComplete(); + + return completed; } void UserProfile::AddSetting(std::unique_ptr setting) { diff --git a/src/xenia/kernel/xam/user_profile.h b/src/xenia/kernel/xam/user_profile.h index c1ed420a52f..1b76455a501 100644 --- a/src/xenia/kernel/xam/user_profile.h +++ b/src/xenia/kernel/xam/user_profile.h @@ -182,6 +182,8 @@ class UserProfile { public: UserProfile(uint64_t xuid, X_XAMACCOUNTINFO* account_info); + void InitializeSystemContexts(); + uint64_t xuid() const { return xuid_; } uint64_t GetOnlineXUID() const { return IsLiveEnabled() ? static_cast(account_info_.xuid_online) @@ -240,8 +242,9 @@ class UserProfile { const std::vector GetSubscribedXUIDs() const; - std::u16string GetPresenceString(); - bool UpdatePresenceString(); + std::u16string GetPresenceString() const; + bool UpdatePresence(bool update_discord); + bool BuildPresenceString(); void AddSetting(std::unique_ptr setting); UserSetting* GetSetting(uint32_t setting_id); @@ -249,7 +252,6 @@ class UserProfile { bool AddProperty(const Property* property); Property* GetProperty(const AttributeKey id); - std::map contexts_; std::vector properties_; friend class GpdAchievementBackend; diff --git a/src/xenia/kernel/xam/xam_user.cc b/src/xenia/kernel/xam/xam_user.cc index 4d1e10963e0..41b3d6edf6f 100644 --- a/src/xenia/kernel/xam/xam_user.cc +++ b/src/xenia/kernel/xam/xam_user.cc @@ -475,7 +475,7 @@ dword_result_t XamUserWriteProfileSettings_entry( static_cast(setting.data.type)); switch (setting_type) { - case X_USER_DATA_TYPE::CONTENT: + case X_USER_DATA_TYPE::CONTEXT: case X_USER_DATA_TYPE::BINARY: { uint8_t* binary_ptr = kernel_state()->memory()->TranslateVirtual(setting.data.binary.ptr); diff --git a/src/xenia/kernel/xnet.h b/src/xenia/kernel/xnet.h index bae1317adec..42ccc7bd871 100644 --- a/src/xenia/kernel/xnet.h +++ b/src/xenia/kernel/xnet.h @@ -79,7 +79,7 @@ namespace xe { #define X_PROPERTY_ID_MASK 0x00007FFF #define X_PROPERTYID(scope, type, id) ((scope ? X_PROPERTY_SCOPE_MASK : 0) | ((static_cast(type) << 28) & X_PROPERTY_TYPE_MASK) | (id & X_PROPERTY_ID_MASK)) -#define X_CONTEXTID(scope, id) X_PROPERTYID(scope, static_cast(X_USER_DATA_TYPE::CONTENT), id) +#define X_CONTEXTID(scope, id) X_PROPERTYID(scope, static_cast(X_USER_DATA_TYPE::CONTEXT), id) #define X_PROPERTYTYPEFROMID(id) ((id >> 28) & 0xF) #define X_ISSYSTEMPROPERTY(id) (id & X_PROPERTY_SCOPE_MASK) diff --git a/src/xenia/kernel/xsession.cc b/src/xenia/kernel/xsession.cc index d239860170d..95b57cd8439 100644 --- a/src/xenia/kernel/xsession.cc +++ b/src/xenia/kernel/xsession.cc @@ -87,10 +87,8 @@ X_RESULT XSession::CreateSession(uint8_t user_index, uint8_t public_slots, // - Explicitly create a presence session (Frogger without HOST bit) // Based on Presence flag set? - // Write user contexts. After session creation these are read only! - contexts_.insert(user_profile->contexts_.cbegin(), - user_profile->contexts_.cend()); - + // Write user contexts and properties. After session creation these are read + // only! properties_ = user_profile->properties_; if (IsSystemlinkFlags(flags)) { @@ -108,8 +106,10 @@ X_RESULT XSession::CreateSession(uint8_t user_index, uint8_t public_slots, JoinExistingSession(SessionInfo_ptr); } - local_details_.GameType = GetGameTypeContext(); - local_details_.GameMode = GetGameModeContext(); + local_details_.GameType = + std::get(GetGameTypeContext()->GetValueGuest()); + local_details_.GameMode = + std::get(GetGameModeContext()->GetValueGuest()); local_details_.Flags = flags; local_details_.MaxPublicSlots = public_slots; local_details_.MaxPrivateSlots = private_slots; @@ -167,7 +167,6 @@ X_RESULT XSession::CreateHostSession(XSESSION_INFO* session_info, session_id_ = GenerateSessionId(XNKID_ONLINE); XLiveAPI::XSessionCreate(session_id_, session_data); - XLiveAPI::SessionContextSet(session_id_, contexts_); XLiveAPI::SessionPropertiesAdd(session_id_, properties_); } @@ -566,9 +565,25 @@ X_RESULT XSession::MigrateHost(XSessionMigate* data) { if (!result->SessionID_UInt()) { XELOGI("Session Migration Failed"); + + // Returning X_E_FAIL will cause 5454082B to restart return X_E_FAIL; } + if (data->user_index == XUserIndexNone) { + XELOGI("Session migration we're not host!"); + } + + if (kernel_state()->xam_state()->IsUserSignedIn(data->user_index)) { + properties_ = kernel_state() + ->xam_state() + ->GetUserProfile(data->user_index) + ->properties_; + + // Update properties, what if they're changed after migration? + XLiveAPI::SessionPropertiesAdd(result->SessionID_UInt(), properties_); + } + memset(SessionInfo_ptr, 0, sizeof(XSESSION_INFO)); Uint64toXNKID(result->SessionID_UInt(), &SessionInfo_ptr->sessionID); @@ -735,11 +750,11 @@ X_RESULT XSession::GetSessions(Memory* memory, util::XLast* xlast, matchmaking_query = xlast->GetMatchmakingQuery(); xlast_properties = xlast->GetPropertiesQuery(); - auto const paramaters = + const auto paramaters = matchmaking_query->GetParameters(search_data->proc_index); - auto const filters = + const auto filters = matchmaking_query->GetFiltersLeft(search_data->proc_index); - auto const returns = matchmaking_query->GetReturns(search_data->proc_index); + const auto returns = matchmaking_query->GetReturns(search_data->proc_index); XELOGI("MatchmakingQuery: {}", matchmaking_query->GetName(search_data->proc_index)); @@ -748,7 +763,8 @@ X_RESULT XSession::GetSessions(Memory* memory, util::XLast* xlast, XUSER_CONTEXT& context = search_contexts_ptr[i]; const auto context_string_id = - xlast->GetContextStringId(context.context_id, context.value); + xlast->GetContextsQuery()->GetContextValueStringID(context.context_id, + context.value); if (context_string_id.has_value()) { const auto context_string = xlast->GetLocalizedString( @@ -761,10 +777,11 @@ X_RESULT XSession::GetSessions(Memory* memory, util::XLast* xlast, for (uint32_t i = 0; i < search_data->num_props; i++) { XUSER_PROPERTY& property = search_properties_ptr[i]; - const auto name = xlast_properties->GetPropertyName(property.property_id); + const auto name = + xlast_properties->GetPropertyFriendlyName(property.property_id); const auto property_string_id = - xlast->GetPropertyStringId(property.property_id); + xlast_properties->GetPropertyStringID(property.property_id); if (property_string_id.has_value()) { const auto property_string = xlast->GetLocalizedString( @@ -776,14 +793,25 @@ X_RESULT XSession::GetSessions(Memory* memory, util::XLast* xlast, } for (uint32_t i = 0; i < session_count; i++) { - const auto context = - XLiveAPI::SessionContextGet(sessions.at(i)->SessionID_UInt()); + std::vector contexts = {}; + std::vector properties = {}; - const auto properties = + const auto all_properties = XLiveAPI::SessionPropertiesGet(sessions.at(i)->SessionID_UInt()); + for (const auto& property : all_properties) { + const X_USER_DATA_TYPE type = + static_cast(property.GetPropertyId().type); + + if (type == X_USER_DATA_TYPE::CONTEXT) { + contexts.push_back(property); + } else { + properties.push_back(property); + } + } + FillSessionContext(memory, search_data->proc_index, matchmaking_query, - context, search_data->num_props, search_contexts_ptr, + contexts, search_data->num_ctx, search_contexts_ptr, &search_results_ptr->results_ptr[i]); FillSessionProperties(memory, search_data->proc_index, matchmaking_query, properties, search_data->num_props, @@ -951,13 +979,13 @@ void XSession::FillSessionSearchResult( void XSession::FillSessionContext( Memory* memory, uint32_t matchmaking_index, util::XLastMatchmakingQuery* matchmaking_query, - std::map contexts, uint32_t filter_contexts_count, + std::vector contexts, uint32_t filter_contexts_count, XUSER_CONTEXT* filter_contexts_ptr, XSESSION_SEARCHRESULT* result) { if (matchmaking_query) { - auto const filters = matchmaking_query->GetParameters(matchmaking_index); - auto const paramaters = + const auto filters = matchmaking_query->GetParameters(matchmaking_index); + const auto paramaters = matchmaking_query->GetFiltersLeft(matchmaking_index); - auto const returns = matchmaking_query->GetReturns(matchmaking_index); + const auto returns = matchmaking_query->GetReturns(matchmaking_index); } result->contexts_count = static_cast(contexts.size()); @@ -974,8 +1002,8 @@ void XSession::FillSessionContext( uint32_t i = 0; for (const auto& context : contexts) { - contexts_to_get[i].context_id = context.first; - contexts_to_get[i].value = context.second; + contexts_to_get[i].context_id = context.GetPropertyId().value; + contexts_to_get[i].value = std::get(context.GetValueGuest()); i++; } @@ -988,9 +1016,9 @@ void XSession::FillSessionProperties( std::vector properties, uint32_t filter_properties_count, XUSER_PROPERTY* filter_properties_ptr, XSESSION_SEARCHRESULT* result) { if (matchmaking_query) { - auto const paramaters = matchmaking_query->GetParameters(matchmaking_index); - auto const filters = matchmaking_query->GetFiltersLeft(matchmaking_index); - auto const returns = matchmaking_query->GetReturns(matchmaking_index); + const auto paramaters = matchmaking_query->GetParameters(matchmaking_index); + const auto filters = matchmaking_query->GetFiltersLeft(matchmaking_index); + const auto returns = matchmaking_query->GetReturns(matchmaking_index); } result->properties_count = static_cast(properties.size()); @@ -1019,6 +1047,18 @@ void XSession::FillSessionProperties( result->properties_ptr = properties_ptr; } +Property* XSession::GetProperty(const AttributeKey id) { + for (auto& entry : properties_) { + if (entry.GetPropertyId().value != id.value) { + continue; + } + + return &entry; + } + + return nullptr; +} + void XSession::PrintSessionDetails() { XELOGI( "\n***************** PrintSessionDetails *****************\n" diff --git a/src/xenia/kernel/xsession.h b/src/xenia/kernel/xsession.h index 08b1c93a73b..386e2e29fef 100644 --- a/src/xenia/kernel/xsession.h +++ b/src/xenia/kernel/xsession.h @@ -396,16 +396,12 @@ class XSession : public XObject { return members_size; } - const uint32_t GetGameModeContext() { - return contexts_.find(X_CONTEXT_GAME_MODE) != contexts_.end() - ? contexts_[X_CONTEXT_GAME_MODE] - : 0; + const Property* GetGameModeContext() { + return GetProperty(static_cast(X_CONTEXT_GAME_MODE)); } - const uint32_t GetGameTypeContext() { - return contexts_.find(X_CONTEXT_GAME_TYPE) != contexts_.end() - ? contexts_[X_CONTEXT_GAME_TYPE] - : X_CONTEXT_GAME_TYPE_STANDARD; + const Property* GetGameTypeContext() { + return GetProperty(static_cast(X_CONTEXT_GAME_TYPE)); } const bool IsCreated() const { @@ -460,7 +456,7 @@ class XSession : public XObject { static void FillSessionContext(Memory* memory, uint32_t matchmaking_index, util::XLastMatchmakingQuery* matchmaking_query, - std::map contexts, + std::vector contexts, uint32_t filter_contexts_count, XUSER_CONTEXT* filter_contexts_ptr, XSESSION_SEARCHRESULT* result); @@ -471,6 +467,8 @@ class XSession : public XObject { std::vector properties, uint32_t filter_properties_count, XUSER_PROPERTY* filter_properties_ptr, XSESSION_SEARCHRESULT* result); + Property* GetProperty(const AttributeKey id); + // uint64_t migrated_session_id_; uint64_t session_id_ = 0; uint32_t state_ = 0; @@ -482,9 +480,8 @@ class XSession : public XObject { std::map local_members_{}; std::map remote_members_{}; - // These are all contexts that host provided during creation of a session. - // These are constant for single session. - std::map contexts_; + // These are all contexts and properties that host provided during creation of + // a session. Contexts are constant for single session. std::vector properties_; // TODO!