diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 60735569b..da81f91da 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -1185,8 +1185,8 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, const uint8_t* xlast_ptr = db.ReadXLast(compressed_size, decompressed_size); - title_xlast_ = - kernel::util::XLast(xlast_ptr, compressed_size, decompressed_size); + title_xlast_ = std::make_unique( + xlast_ptr, compressed_size, decompressed_size); auto icon_block = db.icon(); if (icon_block) { diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index f230b2708..050bbeaaf 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -299,7 +299,7 @@ class Emulator { kernel::object_ref main_thread_; kernel::object_ref plugin_loader_thread_; std::optional title_id_; // Currently running title ID - kernel::util::XLast title_xlast_; + std::unique_ptr title_xlast_; bool paused_; bool restoring_; diff --git a/src/xenia/kernel/util/xlast.cc b/src/xenia/kernel/util/xlast.cc index 6eebd53d0..ad46fc74d 100644 --- a/src/xenia/kernel/util/xlast.cc +++ b/src/xenia/kernel/util/xlast.cc @@ -11,12 +11,35 @@ #include "third_party/zlib/zlib.h" #include "xenia/base/filesystem.h" #include "xenia/base/logging.h" +#include "xenia/base/string_util.h" namespace xe { namespace kernel { namespace util { -XLast::XLast() {} +XLastMatchmakingQuery::XLastMatchmakingQuery() {} +XLastMatchmakingQuery::XLastMatchmakingQuery( + const pugi::xpath_node query_node) { + node_ = query_node; +} + +std::string XLastMatchmakingQuery::GetName() const { + return node_.node().attribute("friendlyName").value(); +} + +std::vector XLastMatchmakingQuery::GetReturns() const { + return XLast::GetAllValuesFromNode(node_, "Returns", "id"); +} + +std::vector XLastMatchmakingQuery::GetParameters() const { + return XLast::GetAllValuesFromNode(node_, "Parameters", "id"); +} + +std::vector XLastMatchmakingQuery::GetFilters() const { + return XLast::GetAllValuesFromNode(node_, "Filters", "left"); +} + +XLast::XLast() : parsed_xlast_(nullptr) {} XLast::XLast(const uint8_t* compressed_xml_data, const uint32_t compressed_data_size, @@ -26,6 +49,7 @@ XLast::XLast(const uint8_t* compressed_xml_data, return; } + parsed_xlast_ = std::make_unique(); xlast_decompressed_xml_.resize(decompressed_data_size); z_stream stream; @@ -55,17 +79,85 @@ XLast::XLast(const uint8_t* compressed_xml_data, return; } inflateEnd(&stream); + + parse_result_ = parsed_xlast_->load_buffer(xlast_decompressed_xml_.data(), + xlast_decompressed_xml_.size()); } XLast::~XLast() {} +std::u16string XLast::GetTitleName() { + std::string xpath = "/XboxLiveSubmissionProject/GameConfigProject"; + + const pugi::xpath_node node = parsed_xlast_->select_node(xpath.c_str()); + if (!node) { + return std::u16string(); + } + + return xe::to_utf16(node.node().attribute("titleName").value()); +} + +std::u16string XLast::GetLocalizedString(uint32_t string_id, + XLanguage language) { + std::string xpath = fmt::format( + "/XboxLiveSubmissionProject/GameConfigProject/LocalizedStrings/" + "LocalizedString[@id = \"{}\"]", + string_id); + + const pugi::xpath_node node = parsed_xlast_->select_node(xpath.c_str()); + if (!node) { + return std::u16string(); + } + + const std::string locale_name = GetLocaleStringFromLanguage(language); + const pugi::xml_node locale_node = + node.node().find_child_by_attribute("locale", locale_name.c_str()); + + if (!locale_node) { + return std::u16string(); + } + + return xe::to_utf16(locale_node.child_value()); +} + +XLastMatchmakingQuery* XLast::GetMatchmakingQuery(const uint32_t query_id) { + std::string xpath = fmt::format( + "/XboxLiveSubmissionProject/GameConfigProject/Matchmaking/Queries/" + "Query[@id = \"{}\"]", + query_id); + + XLastMatchmakingQuery* query = nullptr; + pugi::xpath_node node = parsed_xlast_->select_node(xpath.c_str()); + if (!node) { + return query; + } + + return new XLastMatchmakingQuery(node); +} + +std::vector XLast::GetAllValuesFromNode( + const pugi::xpath_node node, const std::string child_name, + const std::string attirbute_name) { + std::vector result{}; + + const auto searched_child = node.node().child(child_name.c_str()); + + for (pugi::xml_node_iterator itr = searched_child.begin(); + itr != searched_child.end(); itr++) { + result.push_back(xe::string_util::from_string( + itr->attribute(attirbute_name.c_str()).value(), true)); + } + + return result; +} + void XLast::Dump(std::string file_name) { if (xlast_decompressed_xml_.empty()) { return; } if (file_name.empty()) { - // TODO: Read default xlast name from it and use that one. + file_name = xe::to_utf8(GetTitleName()); } FILE* outfile = @@ -79,6 +171,15 @@ void XLast::Dump(std::string file_name) { fclose(outfile); } +std::string XLast::GetLocaleStringFromLanguage(XLanguage language) { + const auto value = language_mapping.find(language); + if (value != language_mapping.cend()) { + return value->second; + } + + return language_mapping.at(XLanguage::kEnglish); +} + } // namespace util } // namespace kernel } // namespace xe \ No newline at end of file diff --git a/src/xenia/kernel/util/xlast.h b/src/xenia/kernel/util/xlast.h index 110b8237d..8d6e197b2 100644 --- a/src/xenia/kernel/util/xlast.h +++ b/src/xenia/kernel/util/xlast.h @@ -10,12 +10,39 @@ #ifndef XENIA_KERNEL_UTIL_XLAST_H_ #define XENIA_KERNEL_UTIL_XLAST_H_ +#include #include #include +#include "third_party/pugixml/src/pugixml.hpp" +#include "xenia/xbox.h" + namespace xe { namespace kernel { namespace util { + +static const std::map language_mapping = { + {XLanguage::kEnglish, "en-US"}, {XLanguage::kJapanese, "ja-JP"}, + {XLanguage::kGerman, "de-DE"}, {XLanguage::kFrench, "fr-FR"}, + {XLanguage::kSpanish, "es-ES"}, {XLanguage::kItalian, "it-IT"}, + {XLanguage::kKorean, "ko-KR"}, {XLanguage::kTChinese, "zh-CHT"}, + {XLanguage::kPortuguese, "pt-PT"}, {XLanguage::kPolish, "pl-PL"}, + {XLanguage::kRussian, "ru-RU"}}; + +class XLastMatchmakingQuery { + public: + XLastMatchmakingQuery(); + XLastMatchmakingQuery(const pugi::xpath_node query_node); + + std::string GetName() const; + std::vector GetReturns() const; + std::vector GetParameters() const; + std::vector GetFilters() const; + + private: + pugi::xpath_node node_; +}; + class XLast { public: XLast(); @@ -23,10 +50,21 @@ 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); + 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); private: + std::string GetLocaleStringFromLanguage(XLanguage language); + std::vector xlast_decompressed_xml_; + std::unique_ptr parsed_xlast_ = nullptr; + pugi::xml_parse_result parse_result_ = {}; }; } // namespace util