[XAM] Implemented SPA XRPT
This commit is contained in:
@@ -1611,10 +1611,26 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
|
|||||||
totals = fmt::format("\nViews: {}/{}", stats_views_limit.size(),
|
totals = fmt::format("\nViews: {}/{}", stats_views_limit.size(),
|
||||||
stats_views.size());
|
stats_views.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
XELOGI("\n-------------------- Stats Views --------------------{}\n{}",
|
XELOGI("\n-------------------- Stats Views --------------------{}\n{}",
|
||||||
totals.c_str(), table.str());
|
totals.c_str(), table.str());
|
||||||
|
|
||||||
|
const std::vector<kernel::util::GameInfoDatabase::PresenceMode>
|
||||||
|
presence_modes = game_info_database_->GetPresenceModes();
|
||||||
|
|
||||||
|
table = tabulate::Table();
|
||||||
|
table.format().multi_byte_characters(true);
|
||||||
|
table.add_row({"Context Value", "Contexts Count", "Properties Count"});
|
||||||
|
|
||||||
|
for (const kernel::util::GameInfoDatabase::PresenceMode& entry :
|
||||||
|
presence_modes) {
|
||||||
|
table.add_row(
|
||||||
|
{fmt::format("{}", entry.context_value),
|
||||||
|
fmt::format("{}", entry.property_bag.contexts.size()),
|
||||||
|
fmt::format("{}", entry.property_bag.properties.size())});
|
||||||
|
}
|
||||||
|
XELOGI("\n-------------------- PRESENCE MODES --------------------\n{}",
|
||||||
|
table.str());
|
||||||
|
|
||||||
auto icon_block = game_info_database_->GetIcon();
|
auto icon_block = game_info_database_->GetIcon();
|
||||||
if (!icon_block.empty()) {
|
if (!icon_block.empty()) {
|
||||||
display_window_->SetIcon(icon_block.data(), icon_block.size());
|
display_window_->SetIcon(icon_block.data(), icon_block.size());
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ GameInfoDatabase::Context GameInfoDatabase::GetContext(
|
|||||||
context.default_value = xdbf_context->default_value;
|
context.default_value = xdbf_context->default_value;
|
||||||
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.description = GetLocalizedString(xdbf_context->string_id);
|
context.description = GetLocalizedString(xdbf_context->string_id);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
@@ -122,6 +123,7 @@ GameInfoDatabase::Property GameInfoDatabase::GetProperty(
|
|||||||
property.id = xdbf_property->id;
|
property.id = xdbf_property->id;
|
||||||
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.description = GetLocalizedString(xdbf_property->string_id);
|
property.description = GetLocalizedString(xdbf_property->string_id);
|
||||||
return property;
|
return property;
|
||||||
}
|
}
|
||||||
@@ -231,6 +233,41 @@ GameInfoDatabase::StatsView GameInfoDatabase::GetStatsView(
|
|||||||
return stats_view;
|
return stats_view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GameInfoDatabase::Presence GameInfoDatabase::GetPresence() const {
|
||||||
|
Presence presence;
|
||||||
|
|
||||||
|
if (!is_valid_) {
|
||||||
|
return presence;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto xdbf_presence = spa_gamedata_->GetPresence();
|
||||||
|
|
||||||
|
presence.property_bag = GetPropertyBag(xdbf_presence->property_bag);
|
||||||
|
presence.presence_modes = GetPresenceModes();
|
||||||
|
|
||||||
|
return presence;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameInfoDatabase::PresenceMode GameInfoDatabase::GetPresenceMode(
|
||||||
|
const uint32_t context_value) const {
|
||||||
|
PresenceMode presence_mode = {};
|
||||||
|
|
||||||
|
if (!is_valid_) {
|
||||||
|
return presence_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto xdbf_presence_mode = spa_gamedata_->GetPresenceMode(context_value);
|
||||||
|
|
||||||
|
if (!xdbf_presence_mode.has_value()) {
|
||||||
|
return presence_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
presence_mode.context_value = context_value;
|
||||||
|
presence_mode.property_bag = GetPropertyBag(xdbf_presence_mode.value());
|
||||||
|
|
||||||
|
return presence_mode;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<uint32_t> GameInfoDatabase::GetMatchmakingAttributes(
|
std::vector<uint32_t> GameInfoDatabase::GetMatchmakingAttributes(
|
||||||
const uint32_t id) const {
|
const uint32_t id) const {
|
||||||
// TODO(Gliniak): Implement when we will fully understand how to read it from
|
// TODO(Gliniak): Implement when we will fully understand how to read it from
|
||||||
@@ -378,6 +415,25 @@ std::vector<GameInfoDatabase::StatsView> GameInfoDatabase::GetStatsViews()
|
|||||||
return stats_views;
|
return stats_views;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<GameInfoDatabase::PresenceMode> GameInfoDatabase::GetPresenceModes()
|
||||||
|
const {
|
||||||
|
std::vector<PresenceMode> presence_modes;
|
||||||
|
|
||||||
|
if (!is_valid_) {
|
||||||
|
return presence_modes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& xdbf_presence_modes =
|
||||||
|
spa_gamedata_->GetPresence()->presence_modes;
|
||||||
|
|
||||||
|
for (uint32_t context_value = 0; context_value < xdbf_presence_modes.size();
|
||||||
|
context_value++) {
|
||||||
|
presence_modes.push_back(GetPresenceMode(context_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return presence_modes;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class GameInfoDatabase {
|
|||||||
uint32_t max_value;
|
uint32_t max_value;
|
||||||
uint32_t default_value;
|
uint32_t default_value;
|
||||||
bool is_system;
|
bool is_system;
|
||||||
|
bool is_presence;
|
||||||
std::string description;
|
std::string description;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ class GameInfoDatabase {
|
|||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint32_t data_size;
|
uint32_t data_size;
|
||||||
bool is_system;
|
bool is_system;
|
||||||
|
bool is_presence;
|
||||||
std::string description;
|
std::string description;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -53,6 +55,16 @@ class GameInfoDatabase {
|
|||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PresenceMode {
|
||||||
|
uint32_t context_value;
|
||||||
|
PropertyBag property_bag;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Presence {
|
||||||
|
PropertyBag property_bag;
|
||||||
|
std::vector<PresenceMode> presence_modes;
|
||||||
|
};
|
||||||
|
|
||||||
struct Query {
|
struct Query {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -136,6 +148,10 @@ class GameInfoDatabase {
|
|||||||
PropertyBag GetPropertyBag(const xam::PropertyBag& property_bag) const;
|
PropertyBag GetPropertyBag(const xam::PropertyBag& property_bag) const;
|
||||||
Field GetField(const xam::ViewFieldEntry& field_entry) const;
|
Field GetField(const xam::ViewFieldEntry& field_entry) const;
|
||||||
StatsView GetStatsView(const uint32_t id) const;
|
StatsView GetStatsView(const uint32_t id) const;
|
||||||
|
Presence GetPresence() const;
|
||||||
|
PresenceMode GetPresenceMode(const uint32_t context_value) const;
|
||||||
|
std::vector<PresenceMode> GetPresenceModes(
|
||||||
|
const std::vector<xam::PropertyBag> property_bags) const;
|
||||||
|
|
||||||
// TODO: Implement it in the future.
|
// TODO: Implement it in the future.
|
||||||
std::vector<uint32_t> GetMatchmakingAttributes(const uint32_t id) const;
|
std::vector<uint32_t> GetMatchmakingAttributes(const uint32_t id) const;
|
||||||
@@ -149,6 +165,7 @@ class GameInfoDatabase {
|
|||||||
std::vector<Context> GetContexts() const;
|
std::vector<Context> GetContexts() const;
|
||||||
std::vector<Property> GetProperties() const;
|
std::vector<Property> GetProperties() const;
|
||||||
std::vector<Achievement> GetAchievements() const;
|
std::vector<Achievement> GetAchievements() const;
|
||||||
|
std::vector<PresenceMode> GetPresenceModes() const;
|
||||||
std::vector<StatsView> GetStatsViews() const;
|
std::vector<StatsView> GetStatsViews() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ void SpaInfo::Load() {
|
|||||||
LoadAchievements();
|
LoadAchievements();
|
||||||
LoadProperties();
|
LoadProperties();
|
||||||
LoadContexts();
|
LoadContexts();
|
||||||
|
LoadPresenceModes();
|
||||||
LoadStatsViews();
|
LoadStatsViews();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,6 +253,82 @@ void SpaInfo::LoadStatsViews() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpaInfo::LoadPresenceModes() {
|
||||||
|
auto presence_modes_table =
|
||||||
|
GetEntry(static_cast<uint16_t>(SpaSection::kMetadata), kXdbfIdXrpt);
|
||||||
|
if (!presence_modes_table) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xrpt_head = reinterpret_cast<const XdbfSectionHeader*>(
|
||||||
|
presence_modes_table->data.data());
|
||||||
|
assert_true(xrpt_head->magic == kXdbfSignatureXrpt);
|
||||||
|
assert_true(xrpt_head->version == 1);
|
||||||
|
|
||||||
|
auto xpbm_head_start_ptr = reinterpret_cast<const uint8_t*>(xrpt_head + 1);
|
||||||
|
|
||||||
|
auto xpbm_head =
|
||||||
|
reinterpret_cast<const XdbfSectionHeader*>(xpbm_head_start_ptr);
|
||||||
|
|
||||||
|
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++) {
|
||||||
|
presence_.property_bag.contexts.insert(contexts_ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < property_bag_header_ptr->properties_count; i++) {
|
||||||
|
presence_.property_bag.properties.insert(properties_ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
xpbm_head_start_ptr += xpbm_head->size + sizeof(uint32_t);
|
||||||
|
|
||||||
|
const uint16_t presence_modes_count =
|
||||||
|
xe::load_and_swap<uint16_t>(xpbm_head_start_ptr);
|
||||||
|
|
||||||
|
xpbm_head_start_ptr += sizeof(uint16_t);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < presence_modes_count; i++) {
|
||||||
|
PropertyBag property_bag = {};
|
||||||
|
|
||||||
|
auto xpbm_head =
|
||||||
|
reinterpret_cast<const XdbfSectionHeader*>(xpbm_head_start_ptr);
|
||||||
|
|
||||||
|
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++) {
|
||||||
|
property_bag.contexts.insert(contexts_ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < property_bag_header_ptr->properties_count; i++) {
|
||||||
|
property_bag.properties.insert(properties_ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
xpbm_head_start_ptr += xpbm_head->size + sizeof(uint32_t);
|
||||||
|
|
||||||
|
presence_.presence_modes.push_back(property_bag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 =
|
||||||
@@ -380,6 +457,17 @@ const XdbfPropertyTableEntry* SpaInfo::GetProperty(uint32_t id) {
|
|||||||
return GetSpaEntry<const XdbfPropertyTableEntry*>(properties_, id);
|
return GetSpaEntry<const XdbfPropertyTableEntry*>(properties_, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::optional<PropertyBag> SpaInfo::GetPresenceMode(
|
||||||
|
uint32_t context_value) const {
|
||||||
|
std::optional<PropertyBag> entry = std::nullopt;
|
||||||
|
|
||||||
|
if (context_value < presence_.presence_modes.size()) {
|
||||||
|
entry = presence_.presence_modes.at(context_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
const std::optional<ViewTable> SpaInfo::GetStatsView(uint32_t id) {
|
const std::optional<ViewTable> SpaInfo::GetStatsView(uint32_t id) {
|
||||||
const auto itr = std::ranges::find_if(
|
const auto itr = std::ranges::find_if(
|
||||||
stats_views_,
|
stats_views_,
|
||||||
|
|||||||
@@ -222,6 +222,11 @@ struct ViewTable {
|
|||||||
SharedView shared_view;
|
SharedView shared_view;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PresenceTableEntry {
|
||||||
|
PropertyBag property_bag;
|
||||||
|
std::vector<PropertyBag> presence_modes;
|
||||||
|
};
|
||||||
|
|
||||||
struct AchievementTableEntry {
|
struct AchievementTableEntry {
|
||||||
xe::be<uint16_t> id;
|
xe::be<uint16_t> id;
|
||||||
xe::be<uint16_t> label_id;
|
xe::be<uint16_t> label_id;
|
||||||
@@ -290,9 +295,13 @@ class SpaInfo : public XdbfFile {
|
|||||||
|
|
||||||
const std::vector<ViewTable>* GetStatsViews() const { return &stats_views_; }
|
const std::vector<ViewTable>* GetStatsViews() const { return &stats_views_; }
|
||||||
|
|
||||||
|
const PresenceTableEntry* GetPresence() const { return &presence_; }
|
||||||
|
|
||||||
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);
|
||||||
|
const std::optional<PropertyBag> GetPresenceMode(
|
||||||
|
uint32_t context_value) const;
|
||||||
|
|
||||||
uint32_t total_gamerscore() const {
|
uint32_t total_gamerscore() const {
|
||||||
return std::accumulate(achievements_.cbegin(), achievements_.cend(), 0,
|
return std::accumulate(achievements_.cbegin(), achievements_.cend(), 0,
|
||||||
@@ -318,6 +327,7 @@ class SpaInfo : public XdbfFile {
|
|||||||
std::vector<const XdbfContextTableEntry*> contexts_;
|
std::vector<const XdbfContextTableEntry*> contexts_;
|
||||||
std::vector<const XdbfPropertyTableEntry*> properties_;
|
std::vector<const XdbfPropertyTableEntry*> properties_;
|
||||||
std::vector<ViewTable> stats_views_;
|
std::vector<ViewTable> stats_views_;
|
||||||
|
PresenceTableEntry presence_;
|
||||||
|
|
||||||
using XdbfLanguageStrings = std::map<uint16_t, std::string>;
|
using XdbfLanguageStrings = std::map<uint16_t, std::string>;
|
||||||
|
|
||||||
@@ -333,6 +343,8 @@ class SpaInfo : public XdbfFile {
|
|||||||
|
|
||||||
void LoadStatsViews();
|
void LoadStatsViews();
|
||||||
|
|
||||||
|
void LoadPresenceModes();
|
||||||
|
|
||||||
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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ constexpr fourcc_t kXdbfSignatureXvc2 = make_fourcc("XVC2");
|
|||||||
constexpr fourcc_t kXdbfSignatureXmat = make_fourcc("XMAT");
|
constexpr fourcc_t kXdbfSignatureXmat = make_fourcc("XMAT");
|
||||||
constexpr fourcc_t kXdbfSignatureXsrc = make_fourcc("XSRC");
|
constexpr fourcc_t kXdbfSignatureXsrc = make_fourcc("XSRC");
|
||||||
constexpr fourcc_t kXdbfSignatureXthd = make_fourcc("XTHD");
|
constexpr fourcc_t kXdbfSignatureXthd = make_fourcc("XTHD");
|
||||||
|
constexpr fourcc_t kXdbfSignatureXrpt = make_fourcc("XRPT");
|
||||||
constexpr fourcc_t kXdbfSignatureXpbm = make_fourcc("XPBM");
|
constexpr fourcc_t kXdbfSignatureXpbm = make_fourcc("XPBM");
|
||||||
|
|
||||||
constexpr uint64_t kXdbfIdTitle = 0x8000;
|
constexpr uint64_t kXdbfIdTitle = 0x8000;
|
||||||
@@ -46,6 +47,7 @@ constexpr uint64_t kXdbfIdXmat = 0x584D4154;
|
|||||||
constexpr uint64_t kXdbfIdXsrc = 0x58535243;
|
constexpr uint64_t kXdbfIdXsrc = 0x58535243;
|
||||||
constexpr uint64_t kXdbfIdXthd = 0x58544844;
|
constexpr uint64_t kXdbfIdXthd = 0x58544844;
|
||||||
constexpr uint64_t kXdbfIdXpbm = 0x5850424D;
|
constexpr uint64_t kXdbfIdXpbm = 0x5850424D;
|
||||||
|
constexpr uint64_t kXdbfIdXrpt = 0x58525054;
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct XdbfHeader {
|
struct XdbfHeader {
|
||||||
|
|||||||
Reference in New Issue
Block a user