[XLast] Presence string parser
Thanks Gliniak for parser.
This commit is contained in:
parent
abc1d4aefa
commit
629707b935
|
@ -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
|
|
@ -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_
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue