[XAM] Implemented SPA XMAT

This commit is contained in:
Adrian
2025-11-08 15:28:18 +00:00
committed by Radosław Gliński
parent 520353e95a
commit 6783bea223
5 changed files with 59 additions and 3 deletions

View File

@@ -1548,13 +1548,15 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
table = tabulate::Table(); table = tabulate::Table();
table.format().multi_byte_characters(true); table.format().multi_byte_characters(true);
table.add_row({"ID", "Name", "Data Size"}); table.add_row({"ID", "Name", "Matchmaking", "Data Size"});
for (const kernel::util::GameInfoDatabase::Property& entry : for (const kernel::util::GameInfoDatabase::Property& entry :
properties_list) { properties_list) {
std::string label = std::string label =
string_util::remove_eol(string_util::trim(entry.description)); string_util::remove_eol(string_util::trim(entry.description));
table.add_row({fmt::format("{:08X}", entry.id), label, table.add_row({fmt::format("{:08X}", entry.id), label,
entry.is_matchmaking ? "True" : "False",
fmt::format("{}", entry.data_size)}); fmt::format("{}", entry.data_size)});
} }
XELOGI("\n-------------------- PROPERTIES --------------------\n{}", XELOGI("\n-------------------- PROPERTIES --------------------\n{}",
@@ -1565,13 +1567,16 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
table = tabulate::Table(); table = tabulate::Table();
table.format().multi_byte_characters(true); table.format().multi_byte_characters(true);
table.add_row({"ID", "Name", "Default Value", "Max Value"}); table.add_row(
{"ID", "Name", "Matchmaking", "Default Value", "Max Value"});
for (const kernel::util::GameInfoDatabase::Context& entry : for (const kernel::util::GameInfoDatabase::Context& entry :
contexts_list) { contexts_list) {
std::string label = std::string label =
string_util::remove_eol(string_util::trim(entry.description)); string_util::remove_eol(string_util::trim(entry.description));
table.add_row({fmt::format("{:08X}", entry.id), label, table.add_row({fmt::format("{:08X}", entry.id), label,
entry.is_matchmaking ? "True" : "False",
fmt::format("{}", entry.default_value), fmt::format("{}", entry.default_value),
fmt::format("{}", entry.max_value)}); fmt::format("{}", entry.max_value)});
} }

View File

@@ -103,6 +103,8 @@ GameInfoDatabase::Context GameInfoDatabase::GetContext(
context.max_value = xdbf_context->max_value; context.max_value = xdbf_context->max_value;
context.is_system = xam::UserData::is_system_property(xdbf_context->id); context.is_system = xam::UserData::is_system_property(xdbf_context->id);
context.is_presence = GetPresence().property_bag.contexts.contains(id); context.is_presence = GetPresence().property_bag.contexts.contains(id);
context.is_matchmaking =
GetMatchmakingCollection().contexts.contains(xdbf_context->id);
context.description = GetLocalizedString(xdbf_context->string_id); context.description = GetLocalizedString(xdbf_context->string_id);
return context; return context;
} }
@@ -124,6 +126,8 @@ GameInfoDatabase::Property GameInfoDatabase::GetProperty(
property.data_size = xdbf_property->data_size; property.data_size = xdbf_property->data_size;
property.is_system = xam::UserData::is_system_property(xdbf_property->id); property.is_system = xam::UserData::is_system_property(xdbf_property->id);
property.is_presence = GetPresence().property_bag.properties.contains(id); property.is_presence = GetPresence().property_bag.properties.contains(id);
property.is_matchmaking =
GetMatchmakingCollection().properties.contains(xdbf_property->id);
property.description = GetLocalizedString(xdbf_property->string_id); property.description = GetLocalizedString(xdbf_property->string_id);
return property; return property;
} }
@@ -350,6 +354,11 @@ GameInfoDatabase::ProductInformation GameInfoDatabase::GetProductInformation()
return info; return info;
} }
GameInfoDatabase::PropertyBag GameInfoDatabase::GetMatchmakingCollection()
const {
return GetPropertyBag(*spa_gamedata_->GetMatchCollection());
}
// Aggregators // Aggregators
std::vector<GameInfoDatabase::Context> GameInfoDatabase::GetContexts() const { std::vector<GameInfoDatabase::Context> GameInfoDatabase::GetContexts() const {
std::vector<Context> contexts; std::vector<Context> contexts;

View File

@@ -34,6 +34,7 @@ class GameInfoDatabase {
uint32_t default_value; uint32_t default_value;
bool is_system; bool is_system;
bool is_presence; bool is_presence;
bool is_matchmaking;
std::string description; std::string description;
}; };
@@ -42,6 +43,7 @@ class GameInfoDatabase {
uint32_t data_size; uint32_t data_size;
bool is_system; bool is_system;
bool is_presence; bool is_presence;
bool is_matchmaking;
std::string description; std::string description;
}; };
@@ -160,6 +162,7 @@ class GameInfoDatabase {
Query GetQueryData(const uint32_t id) const; Query GetQueryData(const uint32_t id) const;
std::vector<XLanguage> GetSupportedLanguages() const; std::vector<XLanguage> GetSupportedLanguages() const;
ProductInformation GetProductInformation() const; ProductInformation GetProductInformation() const;
PropertyBag GetMatchmakingCollection() const;
// Aggregators for specific usecases // Aggregators for specific usecases
std::vector<Context> GetContexts() const; std::vector<Context> GetContexts() const;

View File

@@ -25,6 +25,7 @@ void SpaInfo::Load() {
LoadProperties(); LoadProperties();
LoadContexts(); LoadContexts();
LoadPresenceModes(); LoadPresenceModes();
LoadMatchmaking();
LoadStatsViews(); LoadStatsViews();
} }
@@ -329,6 +330,41 @@ void SpaInfo::LoadPresenceModes() {
} }
} }
void SpaInfo::LoadMatchmaking() {
auto matchmaking_schema =
GetEntry(static_cast<uint16_t>(SpaSection::kMetadata), kXdbfIdXmat);
if (!matchmaking_schema) {
return;
}
auto xrpt_head = reinterpret_cast<const XdbfSectionHeader*>(
matchmaking_schema->data.data());
assert_true(xrpt_head->magic == kXdbfSignatureXmat);
assert_true(xrpt_head->version == 1);
auto xpbm_head = reinterpret_cast<const XdbfSectionHeader*>(xrpt_head + 1);
assert_true(xpbm_head->magic == kXdbfSignatureXpbm);
assert_true(xpbm_head->version == 1);
const PropertyBagEntry* property_bag_header_ptr =
reinterpret_cast<const PropertyBagEntry*>(xpbm_head + 1);
auto contexts_ptr =
reinterpret_cast<const xe::be<uint32_t>*>(property_bag_header_ptr + 1);
auto properties_ptr = reinterpret_cast<const xe::be<uint32_t>*>(
contexts_ptr + property_bag_header_ptr->contexts_count);
for (uint32_t i = 0; i < property_bag_header_ptr->contexts_count; i++) {
matchmaking_.contexts.insert(contexts_ptr[i]);
}
for (uint32_t i = 0; i < property_bag_header_ptr->properties_count; i++) {
matchmaking_.properties.insert(properties_ptr[i]);
}
}
const uint8_t* SpaInfo::ReadXLast(uint32_t& compressed_size, const uint8_t* SpaInfo::ReadXLast(uint32_t& compressed_size,
uint32_t& decompressed_size) { uint32_t& decompressed_size) {
auto xlast_table = auto xlast_table =

View File

@@ -297,6 +297,8 @@ class SpaInfo : public XdbfFile {
const PresenceTableEntry* GetPresence() const { return &presence_; } const PresenceTableEntry* GetPresence() const { return &presence_; }
const PropertyBag* GetMatchCollection() const { return &matchmaking_; }
const XdbfContextTableEntry* GetContext(uint32_t id); const XdbfContextTableEntry* GetContext(uint32_t id);
const XdbfPropertyTableEntry* GetProperty(uint32_t id); const XdbfPropertyTableEntry* GetProperty(uint32_t id);
const std::optional<ViewTable> GetStatsView(uint32_t id); const std::optional<ViewTable> GetStatsView(uint32_t id);
@@ -328,6 +330,7 @@ class SpaInfo : public XdbfFile {
std::vector<const XdbfPropertyTableEntry*> properties_; std::vector<const XdbfPropertyTableEntry*> properties_;
std::vector<ViewTable> stats_views_; std::vector<ViewTable> stats_views_;
PresenceTableEntry presence_; PresenceTableEntry presence_;
PropertyBag matchmaking_;
using XdbfLanguageStrings = std::map<uint16_t, std::string>; using XdbfLanguageStrings = std::map<uint16_t, std::string>;
@@ -342,8 +345,8 @@ class SpaInfo : public XdbfFile {
void LoadProperties(); void LoadProperties();
void LoadStatsViews(); void LoadStatsViews();
void LoadPresenceModes(); void LoadPresenceModes();
void LoadMatchmaking();
template <typename T> template <typename T>
static T GetSpaEntry(std::vector<T>& container, uint32_t id); static T GetSpaEntry(std::vector<T>& container, uint32_t id);