From d6dc4dc556b2af9089c7e2f9a8e6079b34400787 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Thu, 21 Dec 2023 20:43:19 +0100 Subject: [PATCH] [Kernel] Added Extracting Properties and Contexts out of SPA file + Did a bit of a cleanup in achievement earning code --- src/xenia/emulator.cc | 20 ++++++ src/xenia/kernel/achievement_manager.cc | 50 +++++++------- src/xenia/kernel/util/xdbf_utils.cc | 92 +++++++++++++++++++++++-- src/xenia/kernel/util/xdbf_utils.h | 50 +++++++++++++- src/xenia/kernel/xam/apps/xgi_app.cc | 22 ++++++ 5 files changed, 204 insertions(+), 30 deletions(-) diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index c46cbe39b..b878244cf 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -1042,6 +1042,26 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, } XELOGI("----------------- END OF ACHIEVEMENTS ----------------"); + XELOGI("-------------------- PROPERTIES --------------------"); + const std::vector properties_list = + db.GetProperties(); + + for (const kernel::util::XdbfPropertyTableEntry& entry : + properties_list) { + std::string label = db.GetStringTableEntry(language, entry.string_id); + XELOGI("{:08X} - {} - {}", entry.id, label, entry.data_size); + } + XELOGI("----------------- END OF PROPERTIES ----------------"); + + XELOGI("-------------------- CONTEXTS --------------------"); + const std::vector contexts_list = + db.GetContexts(); + + for (const kernel::util::XdbfContextTableEntry& entry : contexts_list) { + std::string label = db.GetStringTableEntry(language, entry.string_id); + XELOGI("{:08X} - {} - {}", entry.id, label, entry.unk2); + } + XELOGI("----------------- END OF CONTEXTS ----------------"); auto icon_block = db.icon(); if (icon_block) { display_window_->SetIcon(icon_block.buffer, icon_block.size); diff --git a/src/xenia/kernel/achievement_manager.cc b/src/xenia/kernel/achievement_manager.cc index e16571e06..92ccec063 100644 --- a/src/xenia/kernel/achievement_manager.cc +++ b/src/xenia/kernel/achievement_manager.cc @@ -36,36 +36,38 @@ void AchievementManager::EarnAchievement(uint64_t xuid, uint32_t title_id, ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer(); const util::XdbfGameData title_xdbf = kernel_state()->title_xdbf(); - const std::vector achievements = - title_xdbf.GetAchievements(); + const util::XdbfAchievementTableEntry achievement = + title_xdbf.GetAchievement(achievement_id); + + if (!achievement.id) { + return; + } + const XLanguage title_language = title_xdbf.GetExistingLanguage( static_cast(cvars::user_language)); - for (const util::XdbfAchievementTableEntry& entry : achievements) { - if (entry.id == achievement_id) { - const std::string label = - title_xdbf.GetStringTableEntry(title_language, entry.label_id); - const std::string desc = - title_xdbf.GetStringTableEntry(title_language, entry.description_id); + const std::string label = + title_xdbf.GetStringTableEntry(title_language, achievement.label_id); + const std::string desc = title_xdbf.GetStringTableEntry( + title_language, achievement.description_id); - XELOGI("Achievement unlocked: {}", label); - const std::string description = - fmt::format("{}G - {}", entry.gamerscore, label); + XELOGI("Achievement unlocked: {}", label); - unlocked_achievements[achievement_id] = Clock::QueryHostSystemTime(); - // Even if we disable popup we still should store info that this - // achievement was earned. - if (!cvars::show_achievement_notification) { - continue; - } - - app_context.CallInUIThread([imgui_drawer, description]() { - new xe::ui::AchievementNotificationWindow( - imgui_drawer, "Achievement unlocked", description, 0, - kernel_state()->notification_position_); - }); - } + unlocked_achievements[achievement_id] = Clock::QueryHostSystemTime(); + // Even if we disable popup we still should store info that this + // achievement was earned. + if (!cvars::show_achievement_notification) { + return; } + + const std::string description = + fmt::format("{}G - {}", achievement.gamerscore, label); + + app_context.CallInUIThread([imgui_drawer, description]() { + new xe::ui::AchievementNotificationWindow( + imgui_drawer, "Achievement unlocked", description, 0, + kernel_state()->notification_position_); + }); } bool AchievementManager::IsAchievementUnlocked(uint32_t achievement_id) { diff --git a/src/xenia/kernel/util/xdbf_utils.cc b/src/xenia/kernel/util/xdbf_utils.cc index 067035165..3424b79aa 100644 --- a/src/xenia/kernel/util/xdbf_utils.cc +++ b/src/xenia/kernel/util/xdbf_utils.cc @@ -17,10 +17,14 @@ constexpr fourcc_t kXdbfSignatureXdbf = make_fourcc("XDBF"); constexpr fourcc_t kXdbfSignatureXstc = make_fourcc("XSTC"); constexpr fourcc_t kXdbfSignatureXstr = make_fourcc("XSTR"); constexpr fourcc_t kXdbfSignatureXach = make_fourcc("XACH"); +constexpr fourcc_t kXdbfSignatureXprp = make_fourcc("XPRP"); +constexpr fourcc_t kXdbfSignatureXcxt = make_fourcc("XCXT"); constexpr uint64_t kXdbfIdTitle = 0x8000; constexpr uint64_t kXdbfIdXstc = 0x58535443; constexpr uint64_t kXdbfIdXach = 0x58414348; +constexpr uint64_t kXdbfIdXprp = 0x58505250; +constexpr uint64_t kXdbfIdXctx = 0x58435854; XdbfWrapper::XdbfWrapper(const uint8_t* data, size_t data_size) : data_(data), data_size_(data_size) { @@ -69,11 +73,11 @@ std::string XdbfWrapper::GetStringTableEntry(XLanguage language, } auto xstr_head = - reinterpret_cast(language_block.buffer); + reinterpret_cast(language_block.buffer); assert_true(xstr_head->magic == kXdbfSignatureXstr); assert_true(xstr_head->version == 1); - const uint8_t* ptr = language_block.buffer + sizeof(XdbfSectionHeader); + const uint8_t* ptr = language_block.buffer + sizeof(XdbfXstrSectionHeader); for (uint16_t i = 0; i < xstr_head->count; ++i) { auto entry = reinterpret_cast(ptr); ptr += sizeof(XdbfStringTableEntry); @@ -95,11 +99,11 @@ std::vector XdbfWrapper::GetAchievements() const { } auto xach_head = - reinterpret_cast(achievement_table.buffer); + reinterpret_cast(achievement_table.buffer); assert_true(xach_head->magic == kXdbfSignatureXach); assert_true(xach_head->version == 1); - const uint8_t* ptr = achievement_table.buffer + sizeof(XdbfSectionHeader); + const uint8_t* ptr = achievement_table.buffer + sizeof(XdbfXachSectionHeader); for (uint16_t i = 0; i < xach_head->count; ++i) { auto entry = reinterpret_cast(ptr); ptr += sizeof(XdbfAchievementTableEntry); @@ -108,6 +112,86 @@ std::vector XdbfWrapper::GetAchievements() const { return achievements; } +std::vector XdbfWrapper::GetProperties() const { + std::vector properties; + + auto property_table = GetEntry(XdbfSection::kMetadata, kXdbfIdXprp); + if (!property_table) { + return properties; + } + + auto xprp_head = + reinterpret_cast(property_table.buffer); + assert_true(xprp_head->magic == kXdbfSignatureXprp); + assert_true(xprp_head->version == 1); + + const uint8_t* ptr = property_table.buffer + sizeof(XdbfXprpSectionHeader); + for (uint16_t i = 0; i < xprp_head->count; ++i) { + auto entry = reinterpret_cast(ptr); + ptr += sizeof(XdbfPropertyTableEntry); + properties.push_back(*entry); + } + return properties; +} + +std::vector XdbfWrapper::GetContexts() const { + std::vector contexts; + + auto contexts_table = GetEntry(XdbfSection::kMetadata, kXdbfIdXctx); + if (!contexts_table) { + return contexts; + } + + auto xcxt_head = + reinterpret_cast(contexts_table.buffer); + assert_true(xcxt_head->magic == kXdbfSignatureXcxt); + assert_true(xcxt_head->version == 1); + + const uint8_t* ptr = contexts_table.buffer + sizeof(XdbfXcxtSectionHeader); + for (uint16_t i = 0; i < xcxt_head->count; ++i) { + auto entry = reinterpret_cast(ptr); + ptr += sizeof(XdbfContextTableEntry); + contexts.push_back(*entry); + } + return contexts; +} + +XdbfAchievementTableEntry XdbfWrapper::GetAchievement(const uint32_t id) const { + const auto achievements = GetAchievements(); + + for (const auto& entry : achievements) { + if (entry.id != id) { + continue; + } + return entry; + } + return {}; +} + +XdbfPropertyTableEntry XdbfWrapper::GetProperty(const uint32_t id) const { + const auto properties = GetProperties(); + + for (const auto& entry : properties) { + if (entry.id != id) { + continue; + } + return entry; + } + return {}; +} + +XdbfContextTableEntry XdbfWrapper::GetContext(const uint32_t id) const { + const auto contexts = GetContexts(); + + for (const auto& entry : contexts) { + if (entry.id != id) { + continue; + } + return entry; + } + return {}; +} + XLanguage XdbfGameData::GetExistingLanguage(XLanguage language_to_check) const { // A bit of a hack. Check if title in specific language exist. // If it doesn't then for sure language is not supported. diff --git a/src/xenia/kernel/util/xdbf_utils.h b/src/xenia/kernel/util/xdbf_utils.h index f08fd0d6f..94abd6c3d 100644 --- a/src/xenia/kernel/util/xdbf_utils.h +++ b/src/xenia/kernel/util/xdbf_utils.h @@ -62,13 +62,37 @@ struct XdbfXstc { }; static_assert_size(XdbfXstc, 16); -struct XdbfSectionHeader { +struct XdbfXstrSectionHeader { xe::be magic; xe::be version; xe::be size; xe::be count; }; -static_assert_size(XdbfSectionHeader, 14); +static_assert_size(XdbfXstrSectionHeader, 14); + +struct XdbfXprpSectionHeader { + xe::be magic; + xe::be version; + xe::be size; + xe::be count; +}; +static_assert_size(XdbfXprpSectionHeader, 14); + +struct XdbfXachSectionHeader { + xe::be magic; + xe::be version; + xe::be size; + xe::be count; +}; +static_assert_size(XdbfXachSectionHeader, 14); + +struct XdbfXcxtSectionHeader { + xe::be magic; + xe::be version; + xe::be size; + xe::be count; +}; +static_assert_size(XdbfXcxtSectionHeader, 16); struct XdbfStringTableEntry { xe::be id; @@ -76,6 +100,22 @@ struct XdbfStringTableEntry { }; static_assert_size(XdbfStringTableEntry, 4); +struct XdbfContextTableEntry { + xe::be id; + xe::be unk1; + xe::be string_id; + xe::be unk2; + xe::be unk3; +}; +static_assert_size(XdbfContextTableEntry, 16); + +struct XdbfPropertyTableEntry { + xe::be id; + xe::be string_id; + xe::be data_size; +}; +static_assert_size(XdbfPropertyTableEntry, 8); + struct XdbfAchievementTableEntry { xe::be id; xe::be label_id; @@ -117,6 +157,12 @@ class XdbfWrapper { // Returns the empty string if the entry is not found. std::string GetStringTableEntry(XLanguage language, uint16_t string_id) const; std::vector GetAchievements() const; + std::vector GetProperties() const; + std::vector GetContexts() const; + + XdbfAchievementTableEntry GetAchievement(const uint32_t id) const; + XdbfPropertyTableEntry GetProperty(const uint32_t id) const; + XdbfContextTableEntry GetContext(const uint32_t id) const; private: const uint8_t* data_ = nullptr; diff --git a/src/xenia/kernel/xam/apps/xgi_app.cc b/src/xenia/kernel/xam/apps/xgi_app.cc index 9069820e8..1b850e8bb 100644 --- a/src/xenia/kernel/xam/apps/xgi_app.cc +++ b/src/xenia/kernel/xam/apps/xgi_app.cc @@ -43,6 +43,17 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, uint32_t context_value = xe::load_and_swap(buffer + 20); XELOGD("XGIUserSetContextEx({:08X}, {:08X}, {:08X})", user_index, context_id, context_value); + + const util::XdbfGameData title_xdbf = kernel_state_->title_xdbf(); + if (title_xdbf.is_valid()) { + const auto context = title_xdbf.GetContext(context_id); + const XLanguage title_language = title_xdbf.GetExistingLanguage( + static_cast(XLanguage::kEnglish)); + const std::string desc = + title_xdbf.GetStringTableEntry(title_language, context.string_id); + XELOGD("XGIUserSetContextEx: {} - Set to value: {}", desc, + context_value); + } return X_E_SUCCESS; } case 0x000B0007: { @@ -52,6 +63,17 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, uint32_t value_ptr = xe::load_and_swap(buffer + 24); XELOGD("XGIUserSetPropertyEx({:08X}, {:08X}, {}, {:08X})", user_index, property_id, value_size, value_ptr); + + const util::XdbfGameData title_xdbf = kernel_state_->title_xdbf(); + if (title_xdbf.is_valid()) { + const auto property = title_xdbf.GetContext(property_id); + const XLanguage title_language = title_xdbf.GetExistingLanguage( + static_cast(XLanguage::kEnglish)); + const std::string desc = + title_xdbf.GetStringTableEntry(title_language, property.string_id); + XELOGD("XGIUserSetPropertyEx: Setting property: {}", desc); + } + return X_E_SUCCESS; } case 0x000B0008: {