[XLast] Presence string parser

Thanks Gliniak for parser.
This commit is contained in:
Adrian 2024-09-21 18:08:02 +01:00 committed by Radosław Gliński
parent abc1d4aefa
commit 629707b935
4 changed files with 262 additions and 0 deletions

View File

@ -0,0 +1,153 @@
/**
******************************************************************************
* 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<uint32_t, uint32_t> 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<uint32_t> 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<uint32_t>(stoi(string_match[1].str()));
}
return std::nullopt;
}
std::string AttributeStringFormatter::GetStringFromSpecifier(
std::string_view specifier) const {
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 attribute_placeholder = fmt::format("{{c{}}}", attribute_id.value());
return attribute_placeholder;
}
const auto attribute_string_id =
title_xlast_->GetContextStringId(attribute_id.value(), itr->second);
if (!attribute_string_id.has_value()) {
return "";
}
const auto attribute_string = title_xlast_->GetLocalizedString(
attribute_string_id.value(), XLanguage::kEnglish);
return xe::to_utf8(attribute_string);
}
if (attribute_type == AttributeStringFormatter::AttributeType::Property) {
return "";
}
return "";
}
std::queue<std::string> AttributeStringFormatter::GetPresenceFormatSpecifiers()
const {
std::queue<std::string> 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

View File

@ -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 <cstdint>
#include <map>
#include <optional>
#include <queue>
#include <regex>
#include <string>
#include <string_view>
#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<uint32_t, uint32_t> 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<std::string> GetPresenceFormatSpecifiers() const;
AttributeType GetAttributeTypeFromSpecifier(std::string_view specifier) const;
std::optional<uint32_t> GetAttributeIdFromSpecifier(
const std::string& specifier,
const AttributeStringFormatter::AttributeType specifier_type) const;
const std::string attribute_string_;
std::map<std::string, std::string> attribute_to_string_mapping_;
std::string presence_string_;
std::map<uint32_t, uint32_t> contexts_;
XLast* title_xlast_;
// Tests
//
// std::map<uint32_t, std::string> contexts_ = {
// {0, "Context 0"}, {1, "Context 1"}, {2, "Context 2"}};
// std::map<uint32_t, std::string> properties_ = {
// {0x10000001, "Prop 0"}, {0x20000002, "Prop 2"}, {0x30000001, "Prop
// 3"}};
};
} // namespace util
} // namespace kernel
} // namespace xe
#endif XENIA_KERNEL_UTIL_PRESENCE_STRING_BUILDER_H_

View File

@ -258,6 +258,33 @@ const std::u16string XLast::GetPresenceRawString(const uint32_t presence_value,
return raw_presence;
}
const std::optional<uint32_t> 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);
std::optional<uint32_t> value = std::nullopt;
if (!HasXLast()) {
return value;
}
pugi::xpath_node node = parsed_xlast_->select_node(xpath.c_str());
if (node) {
// const auto default_value =
// node.node().parent().attribute("defaultValue").value();
// value = xe::string_util::from_string<uint32_t>(default_value);
const auto string_id_value = node.node().attribute("stringId").value();
value = xe::string_util::from_string<uint32_t>(string_id_value);
}
return value;
}
XLastMatchmakingQuery* XLast::GetMatchmakingQuery(
const uint32_t query_id) const {
std::string xpath = fmt::format(

View File

@ -83,6 +83,8 @@ class XLast {
const std::optional<uint32_t> GetPropertyStringId(const uint32_t property_id);
const std::u16string GetPresenceRawString(const uint32_t presence_value,
const XLanguage language);
const std::optional<uint32_t> GetContextStringId(
const uint32_t context_id, const uint32_t context_value);
XLastMatchmakingQuery* GetMatchmakingQuery(uint32_t query_id) const;
static std::vector<uint32_t> GetAllValuesFromNode(
const pugi::xpath_node node, const std::string child_name,