From fb93be07b8c553fd411636725929e764ce228567 Mon Sep 17 00:00:00 2001 From: Adrian <78108584+AdrianCassar@users.noreply.github.com> Date: Tue, 17 Sep 2024 21:41:48 +0100 Subject: [PATCH] [XLast] Partially implemented presence string parsing Thanks Gliniak for parser. --- .../kernel/util/presence_string_builder.cc | 151 ++++++++++++++++++ .../kernel/util/presence_string_builder.h | 80 ++++++++++ src/xenia/kernel/util/xlast.cc | 15 ++ src/xenia/kernel/util/xlast.h | 2 + src/xenia/kernel/xam/apps/xgi_app.cc | 4 + src/xenia/kernel/xam/user_profile.cc | 26 +++ src/xenia/kernel/xam/user_profile.h | 2 + 7 files changed, 280 insertions(+) create mode 100644 src/xenia/kernel/util/presence_string_builder.cc create mode 100644 src/xenia/kernel/util/presence_string_builder.h diff --git a/src/xenia/kernel/util/presence_string_builder.cc b/src/xenia/kernel/util/presence_string_builder.cc new file mode 100644 index 0000000000..7c6a739d3a --- /dev/null +++ b/src/xenia/kernel/util/presence_string_builder.cc @@ -0,0 +1,151 @@ +/** + ****************************************************************************** + * 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/presence_string_builder.h" +#include "xenia/kernel/util/shim_utils.h" + +namespace xe { +namespace kernel { +namespace util { + +AttributeStringFormatter::~AttributeStringFormatter() {} + +AttributeStringFormatter::AttributeStringFormatter( + std::string_view attribute_string, XLast* title_xlast, + std::map contexts) + : attribute_string_(attribute_string), attribute_to_string_mapping_() { + contexts_ = contexts; + title_xlast_ = title_xlast; + + presence_string_ = ""; + + if (!ParseAttributeString()) { + return; + } + + BuildPresenceString(); +} + +bool AttributeStringFormatter::ParseAttributeString() { + auto specifiers = GetPresenceFormatSpecifiers(); + + if (specifiers.empty()) { + return true; + } + + std::string specifier; + while (!specifiers.empty()) { + std::string specifier = specifiers.front(); + attribute_to_string_mapping_[specifier] = GetStringFromSpecifier(specifier); + specifiers.pop(); + } + return true; +} + +void AttributeStringFormatter::BuildPresenceString() { + presence_string_ = attribute_string_; + + for (const auto& entry : attribute_to_string_mapping_) { + presence_string_.replace(presence_string_.find(entry.first), + entry.first.length(), entry.second); + } +} + +AttributeStringFormatter::AttributeType +AttributeStringFormatter::GetAttributeTypeFromSpecifier( + std::string_view specifier) const { + if (specifier.length() < 3) { + return AttributeStringFormatter::AttributeType::Unknown; + } + + const char presence_type = specifier.at(1); + if (presence_type == 'c') { + return AttributeStringFormatter::AttributeType::Context; + } + if (presence_type == 'p') { + return AttributeStringFormatter::AttributeType::Property; + } + return AttributeStringFormatter::AttributeType::Unknown; +} + +std::optional AttributeStringFormatter::GetAttributeIdFromSpecifier( + const std::string& specifier, + const AttributeStringFormatter::AttributeType specifier_type) const { + std::smatch string_match; + if (std::regex_search(specifier, string_match, + presence_id_extract_from_specifier)) { + return std::make_optional(stoi(string_match[1].str())); + } + + return std::nullopt; +} + +std::string AttributeStringFormatter::GetStringFromSpecifier( + std::string_view specifier) const { + std::string presence_string = ""; + + const AttributeStringFormatter::AttributeType attribute_type = + GetAttributeTypeFromSpecifier(specifier); + + if (attribute_type == AttributeStringFormatter::AttributeType::Unknown) { + return ""; + } + + const auto attribute_id = + GetAttributeIdFromSpecifier(std::string(specifier), attribute_type); + if (!attribute_id) { + return ""; + } + + if (attribute_type == AttributeStringFormatter::AttributeType::Context) { + // TODO: Different handling for contexts and properties + const auto itr = contexts_.find(attribute_id.value()); + + if (itr == contexts_.cend()) { + auto x = fmt::format("{{c{}}}", attribute_id.value()); + + return x; + } + + const auto attribute_string_id = + title_xlast_->GetContextStringId(attribute_id.value(), itr->second); + + const auto attribute_string = title_xlast_->GetLocalizedString( + attribute_string_id, XLanguage::kEnglish); + + return xe::to_utf8(attribute_string); + } + + if (attribute_type == AttributeStringFormatter::AttributeType::Property) { + return ""; + } + + return ""; +} + +std::queue AttributeStringFormatter::GetPresenceFormatSpecifiers() + const { + std::queue format_specifiers; + + std::smatch match; + + std::string attribute_string = attribute_string_; + + while (std::regex_search(attribute_string, match, + format_specifier_replace_fragment_regex_)) { + for (const auto& presence : match) { + format_specifiers.emplace(presence); + } + attribute_string = match.suffix().str(); + } + return format_specifiers; +} +} // 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 new file mode 100644 index 0000000000..69e8cc13fa --- /dev/null +++ b/src/xenia/kernel/util/presence_string_builder.h @@ -0,0 +1,80 @@ +/** + ****************************************************************************** + * 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_PRESENCE_STRING_BUILDER_H_ +#define XENIA_KERNEL_UTIL_PRESENCE_STRING_BUILDER_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "xenia/kernel/util/xlast.h" + +namespace xe { +namespace kernel { +namespace util { + +class AttributeStringFormatter { + public: + ~AttributeStringFormatter(); + + AttributeStringFormatter(std::string_view attribute_string, + XLast* title_xlast, + std::map contexts); + + bool IsValid() const { return true; } + std::string GetPresenceString() const { return presence_string_; } + + private: + enum class AttributeType { Context = 0, Property = 1, Unknown = 255 }; + + const std::regex presence_id_extract_from_specifier = + std::regex("\\{c(\\d+)\\}"); + const std::regex format_specifier_replace_fragment_regex_ = + std::regex(R"(\{c\d+\}|\{p0x\d+\})"); + + bool ParseAttributeString(); + void BuildPresenceString(); + + std::string GetStringFromSpecifier(std::string_view specifier) const; + std::queue GetPresenceFormatSpecifiers() const; + + AttributeType GetAttributeTypeFromSpecifier(std::string_view specifier) const; + std::optional GetAttributeIdFromSpecifier( + const std::string& specifier, + const AttributeStringFormatter::AttributeType specifier_type) const; + + const std::string attribute_string_; + std::map attribute_to_string_mapping_; + + std::string presence_string_; + + std::map contexts_; + + XLast* title_xlast_; + + // Tests + // + // std::map contexts_ = { + // {0, "Context 0"}, {1, "Context 1"}, {2, "Context 2"}}; + + // std::map properties_ = { + // {0x10000001, "Prop 0"}, {0x20000002, "Prop 2"}, {0x30000001, "Prop + // 3"}}; +}; + +} // namespace util +} // namespace kernel +} // namespace xe + +#endif XENIA_KERNEL_UTIL_PRESENCE_STRING_BUILDER_H_ \ No newline at end of file diff --git a/src/xenia/kernel/util/xlast.cc b/src/xenia/kernel/util/xlast.cc index e595963f2c..8b9cb3f7c6 100644 --- a/src/xenia/kernel/util/xlast.cc +++ b/src/xenia/kernel/util/xlast.cc @@ -11,6 +11,7 @@ #include "third_party/zlib/zlib.h" #include "xenia/base/logging.h" #include "xenia/base/string_util.h" +#include "xenia/kernel/util/presence_string_builder.h" namespace xe { namespace kernel { @@ -182,6 +183,20 @@ const uint32_t XLast::GetPropertyStringId(const uint32_t property_id) { return value; } +const std::u16string XLast::GetPresenceRawString(const uint32_t presence_value, + const XLanguage language) { + const std::optional presence_string_id = + GetPresenceStringId(presence_value); + + std::u16string raw_presence = u""; + + if (presence_string_id.has_value()) { + raw_presence = GetLocalizedString(presence_string_id.value(), language); + } + + return raw_presence; +} + const uint32_t XLast::GetContextStringId(const uint32_t context_id, const uint32_t context_value) { std::string xpath = fmt::format( diff --git a/src/xenia/kernel/util/xlast.h b/src/xenia/kernel/util/xlast.h index 5c7aa2fb0c..5600b10700 100644 --- a/src/xenia/kernel/util/xlast.h +++ b/src/xenia/kernel/util/xlast.h @@ -55,6 +55,8 @@ class XLast { std::u16string GetLocalizedString(uint32_t string_id, XLanguage language); XLastMatchmakingQuery* GetMatchmakingQuery(uint32_t query_id); const std::optional GetPresenceStringId(const uint32_t context_id); + const std::u16string GetPresenceRawString(const uint32_t presence_value, + const XLanguage language); const uint32_t GetPropertyStringId(const uint32_t property_id); const uint32_t GetContextStringId(const uint32_t context_id, const uint32_t context_value); diff --git a/src/xenia/kernel/xam/apps/xgi_app.cc b/src/xenia/kernel/xam/apps/xgi_app.cc index d0d17209af..982610eaba 100644 --- a/src/xenia/kernel/xam/apps/xgi_app.cc +++ b/src/xenia/kernel/xam/apps/xgi_app.cc @@ -419,6 +419,10 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, UserProfile* user_profile = kernel_state_->user_profile(user_index); if (user_profile) { user_profile->contexts_[context_id] = context_value; + + if (context_id == X_CONTEXT_PRESENCE) { + auto presence = user_profile->GetPresenceString(); + } } } return X_E_SUCCESS; diff --git a/src/xenia/kernel/xam/user_profile.cc b/src/xenia/kernel/xam/user_profile.cc index d39d0b3c6c..b4bab53297 100644 --- a/src/xenia/kernel/xam/user_profile.cc +++ b/src/xenia/kernel/xam/user_profile.cc @@ -9,6 +9,7 @@ #include +#include #include "third_party/fmt/include/fmt/format.h" #include "xenia/base/clock.h" #include "xenia/base/cvar.h" @@ -18,6 +19,7 @@ #include "xenia/emulator.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/util/shim_utils.h" +#include "xenia/kernel/util/xlast.h" #include "xenia/kernel/xam/user_profile.h" #include "xenia/kernel/XLiveAPI.h" @@ -504,6 +506,30 @@ const std::vector UserProfile::GetSubscribedXUIDs() const { return subscribed_xuids; } +std::string UserProfile::GetPresenceString() { + if (contexts_.find(X_CONTEXT_PRESENCE) == contexts_.end()) { + return ""; + } + + std::u16string presence = u""; + + const auto xlast = kernel_state()->emulator()->xlast(); + + const std::u16string raw_presence = xlast->GetPresenceRawString( + contexts_[X_CONTEXT_PRESENCE], XLanguage::kEnglish); + + const auto presence_string_formatter = + util::AttributeStringFormatter::AttributeStringFormatter( + xe::to_utf8(raw_presence), xlast, contexts_); + + auto presence_parsed = presence_string_formatter.GetPresenceString(); + + XELOGI("Raw Presence: {}", xe::to_utf8(raw_presence.c_str())); + XELOGI("Parsed Presence: {}", presence_parsed); + + return presence_parsed; +} + void UserProfile::AddSetting(std::unique_ptr setting) { UserSetting* previous_setting = setting.get(); diff --git a/src/xenia/kernel/xam/user_profile.h b/src/xenia/kernel/xam/user_profile.h index 4ef8ac8861..5fae75e349 100644 --- a/src/xenia/kernel/xam/user_profile.h +++ b/src/xenia/kernel/xam/user_profile.h @@ -243,6 +243,8 @@ class UserProfile { const std::vector GetSubscribedXUIDs() const; + std::string GetPresenceString(); + void AddSetting(std::unique_ptr setting); UserSetting* GetSetting(uint32_t setting_id);