[Kernel] Simple support for loading achievement data

This commit is contained in:
Gliniak 2022-12-25 13:16:30 +01:00
parent a25ea03a28
commit 9f0d3d4c5b
6 changed files with 171 additions and 82 deletions

View File

@ -55,6 +55,8 @@
#include "xenia/cpu/backend/x64/x64_backend.h" #include "xenia/cpu/backend/x64/x64_backend.h"
#endif // XE_ARCH #endif // XE_ARCH
DECLARE_int32(user_language);
DEFINE_double(time_scalar, 1.0, DEFINE_double(time_scalar, 1.0,
"Scalar used to speed or slow time (1x, 2x, 1/2x, etc).", "Scalar used to speed or slow time (1x, 2x, 1/2x, etc).",
"General"); "General");
@ -865,27 +867,31 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
} }
game_config_load_callback_loop_next_index_ = SIZE_MAX; game_config_load_callback_loop_next_index_ = SIZE_MAX;
uint32_t resource_data = 0; const kernel::util::XdbfGameData db = kernel_state_->module_xdbf(module);
uint32_t resource_size = 0;
if (XSUCCEEDED(module->GetSection(title_id.c_str(), &resource_data,
&resource_size))) {
kernel::util::XdbfGameData db(
module->memory()->TranslateVirtual(resource_data), resource_size);
if (db.is_valid()) { if (db.is_valid()) {
// TODO(gibbed): get title respective to user locale. XLanguage language =
title_name_ = db.title(XLanguage::kEnglish); db.GetExistingLanguage(static_cast<XLanguage>(cvars::user_language));
if (title_name_.empty()) { title_name_ = db.title(language);
// If English title is unavailable, get the title in default locale.
title_name_ = db.title();
}
XELOGI("Title name: {}", title_name_); XELOGI("Title name: {}", title_name_);
// Show achievments data
XELOGI("-------------------- ACHIEVEMENTS --------------------");
auto ach = db.GetAchievements();
for (const auto& entry : ach) {
std::string label = db.GetStringTableEntry(language, entry.label_id);
std::string desc =
db.GetStringTableEntry(language, entry.description_id);
XELOGI("{} - {} - {} - {}", entry.id, label, desc, entry.gamerscore);
}
XELOGI("----------------- END OF ACHIEVEMENTS --------------------");
auto icon_block = db.icon(); auto icon_block = db.icon();
if (icon_block) { if (icon_block) {
display_window_->SetIcon(icon_block.buffer, icon_block.size); display_window_->SetIcon(icon_block.buffer, icon_block.size);
} }
} }
} }
}
// Initializing the shader storage in a blocking way so the user doesn't // Initializing the shader storage in a blocking way so the user doesn't
// miss the initial seconds - for instance, sound from an intro video may // miss the initial seconds - for instance, sound from an intro video may

View File

@ -107,6 +107,26 @@ uint32_t KernelState::title_id() const {
return 0; return 0;
} }
util::XdbfGameData KernelState::title_xdbf() const {
return module_xdbf(executable_module_);
}
util::XdbfGameData KernelState::module_xdbf(
object_ref<UserModule> exec_module) const {
assert_not_null(exec_module);
uint32_t resource_data = 0;
uint32_t resource_size = 0;
if (XSUCCEEDED(exec_module->GetSection(
fmt::format("{:08X}", exec_module->title_id()).c_str(),
&resource_data, &resource_size))) {
util::XdbfGameData db(memory()->TranslateVirtual(resource_data),
resource_size);
return db;
}
return util::XdbfGameData(nullptr, resource_size);
}
uint32_t KernelState::process_type() const { uint32_t KernelState::process_type() const {
auto pib = auto pib =
memory_->TranslateVirtual<ProcessInfoBlock*>(process_info_block_address_); memory_->TranslateVirtual<ProcessInfoBlock*>(process_info_block_address_);

View File

@ -23,6 +23,7 @@
#include "xenia/cpu/export_resolver.h" #include "xenia/cpu/export_resolver.h"
#include "xenia/kernel/util/native_list.h" #include "xenia/kernel/util/native_list.h"
#include "xenia/kernel/util/object_table.h" #include "xenia/kernel/util/object_table.h"
#include "xenia/kernel/util/xdbf_utils.h"
#include "xenia/kernel/xam/app_manager.h" #include "xenia/kernel/xam/app_manager.h"
#include "xenia/kernel/xam/content_manager.h" #include "xenia/kernel/xam/content_manager.h"
#include "xenia/kernel/xam/user_profile.h" #include "xenia/kernel/xam/user_profile.h"
@ -99,6 +100,8 @@ class KernelState {
vfs::VirtualFileSystem* file_system() const { return file_system_; } vfs::VirtualFileSystem* file_system() const { return file_system_; }
uint32_t title_id() const; uint32_t title_id() const;
util::XdbfGameData title_xdbf() const;
util::XdbfGameData module_xdbf(object_ref<UserModule> exec_module) const;
xam::AppManager* app_manager() const { return app_manager_.get(); } xam::AppManager* app_manager() const { return app_manager_.get(); }
xam::ContentManager* content_manager() const { xam::ContentManager* content_manager() const {

View File

@ -16,6 +16,11 @@ namespace util {
constexpr fourcc_t kXdbfSignatureXdbf = make_fourcc("XDBF"); constexpr fourcc_t kXdbfSignatureXdbf = make_fourcc("XDBF");
constexpr fourcc_t kXdbfSignatureXstc = make_fourcc("XSTC"); constexpr fourcc_t kXdbfSignatureXstc = make_fourcc("XSTC");
constexpr fourcc_t kXdbfSignatureXstr = make_fourcc("XSTR"); constexpr fourcc_t kXdbfSignatureXstr = make_fourcc("XSTR");
constexpr fourcc_t kXdbfSignatureXach = make_fourcc("XACH");
constexpr uint64_t kXdbfIdTitle = 0x8000;
constexpr uint64_t kXdbfIdXstc = 0x58535443;
constexpr uint64_t kXdbfIdXach = 0x58414348;
XdbfWrapper::XdbfWrapper(const uint8_t* data, size_t data_size) XdbfWrapper::XdbfWrapper(const uint8_t* data, size_t data_size)
: data_(data), data_size_(data_size) { : data_(data), data_size_(data_size) {
@ -81,8 +86,35 @@ std::string XdbfWrapper::GetStringTableEntry(XLanguage language,
return ""; return "";
} }
constexpr uint64_t kXdbfIdTitle = 0x8000; std::vector<XdbfAchievementTableEntry> XdbfWrapper::GetAchievements() const {
constexpr uint64_t kXdbfIdXstc = 0x58535443; std::vector<XdbfAchievementTableEntry> achievements;
auto achievement_table = GetEntry(XdbfSection::kMetadata, kXdbfIdXach);
if (!achievement_table) {
return achievements;
}
auto xstr_head =
reinterpret_cast<const XdbfXstrHeader*>(achievement_table.buffer);
assert_true(xstr_head->magic == kXdbfSignatureXach);
assert_true(xstr_head->version == 1);
const uint8_t* ptr = achievement_table.buffer + sizeof(XdbfXstrHeader);
for (uint16_t i = 0; i < xstr_head->string_count; ++i) {
auto entry = reinterpret_cast<const XdbfAchievementTableEntry*>(ptr);
ptr += sizeof(XdbfAchievementTableEntry);
achievements.push_back(*entry);
}
return achievements;
}
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.
return title(language_to_check).empty() ? default_language()
: language_to_check;
}
XdbfBlock XdbfGameData::icon() const { XdbfBlock XdbfGameData::icon() const {
return GetEntry(XdbfSection::kImage, kXdbfIdTitle); return GetEntry(XdbfSection::kImage, kXdbfIdTitle);

View File

@ -29,31 +29,6 @@ enum class XdbfSection : uint16_t {
kStringTable = 0x0003, kStringTable = 0x0003,
}; };
struct XdbfBlock {
const uint8_t* buffer;
size_t size;
operator bool() const { return buffer != nullptr; }
};
// Wraps an XBDF (XboxDataBaseFormat) in-memory database.
// https://free60project.github.io/wiki/XDBF.html
class XdbfWrapper {
public:
XdbfWrapper(const uint8_t* data, size_t data_size);
// True if the target memory contains a valid XDBF instance.
bool is_valid() const { return data_ != nullptr; }
// Gets an entry in the given section.
// If the entry is not found the returned block will be nullptr.
XdbfBlock GetEntry(XdbfSection section, uint64_t id) const;
// Gets a string from the string table in the given language.
// Returns the empty string if the entry is not found.
std::string GetStringTableEntry(XLanguage language, uint16_t string_id) const;
protected:
#pragma pack(push, 1) #pragma pack(push, 1)
struct XbdfHeader { struct XbdfHeader {
xe::be<uint32_t> magic; xe::be<uint32_t> magic;
@ -100,8 +75,49 @@ class XdbfWrapper {
xe::be<uint16_t> string_length; xe::be<uint16_t> string_length;
}; };
static_assert_size(XdbfStringTableEntry, 4); static_assert_size(XdbfStringTableEntry, 4);
struct XdbfAchievementTableEntry {
xe::be<uint16_t> id;
xe::be<uint16_t> label_id;
xe::be<uint16_t> description_id;
xe::be<uint16_t> unachieved_id;
xe::be<uint32_t> image_id;
xe::be<uint16_t> gamerscore;
xe::be<uint16_t> unkE;
xe::be<uint32_t> flags;
xe::be<uint32_t> unk14;
xe::be<uint32_t> unk18;
xe::be<uint32_t> unk1C;
xe::be<uint32_t> unk20;
};
static_assert_size(XdbfAchievementTableEntry, 0x24);
#pragma pack(pop) #pragma pack(pop)
struct XdbfBlock {
const uint8_t* buffer;
size_t size;
operator bool() const { return buffer != nullptr; }
};
// Wraps an XBDF (XboxDataBaseFormat) in-memory database.
// https://free60project.github.io/wiki/XDBF.html
class XdbfWrapper {
public:
XdbfWrapper(const uint8_t* data, size_t data_size);
// True if the target memory contains a valid XDBF instance.
bool is_valid() const { return data_ != nullptr; }
// Gets an entry in the given section.
// If the entry is not found the returned block will be nullptr.
XdbfBlock GetEntry(XdbfSection section, uint64_t id) const;
// Gets a string from the string table in the given language.
// Returns the empty string if the entry is not found.
std::string GetStringTableEntry(XLanguage language, uint16_t string_id) const;
std::vector<XdbfAchievementTableEntry> GetAchievements() const;
private: private:
const uint8_t* data_ = nullptr; const uint8_t* data_ = nullptr;
size_t data_size_ = 0; size_t data_size_ = 0;
@ -117,6 +133,9 @@ class XdbfGameData : public XdbfWrapper {
XdbfGameData(const uint8_t* data, size_t data_size) XdbfGameData(const uint8_t* data, size_t data_size)
: XdbfWrapper(data, data_size) {} : XdbfWrapper(data, data_size) {}
// Checks if provided language exist, if not returns default title language.
XLanguage GetExistingLanguage(XLanguage language_to_check) const;
// The game icon image, if found. // The game icon image, if found.
XdbfBlock icon() const; XdbfBlock icon() const;

View File

@ -20,6 +20,8 @@
#include "xenia/kernel/xthread.h" #include "xenia/kernel/xthread.h"
#include "xenia/xbox.h" #include "xenia/xbox.h"
DECLARE_int32(user_language);
namespace xe { namespace xe {
namespace kernel { namespace kernel {
namespace xam { namespace xam {
@ -717,20 +719,27 @@ dword_result_t XamUserCreateAchievementEnumerator_entry(
return result; return result;
} }
uint32_t dummy_count = std::min(100u, uint32_t(count)); const kernel::util::XdbfGameData db = kernel_state()->title_xdbf();
for (uint32_t i = 1; i <= dummy_count; ++i) {
if (db.is_valid()) {
XLanguage language = db.GetExistingLanguage(static_cast<XLanguage>(cvars::user_language));
std::vector<kernel::util::XdbfAchievementTableEntry> achievement_list =
db.GetAchievements();
for (const auto& entry : achievement_list) {
auto item = XStaticAchievementEnumerator::AchievementDetails{ auto item = XStaticAchievementEnumerator::AchievementDetails{
i, // dummy achievement id entry.id,
fmt::format(u"Dummy {}", i), xe::to_utf16(db.GetStringTableEntry(language, entry.label_id)),
u"Dummy description", xe::to_utf16(db.GetStringTableEntry(language, entry.description_id)),
u"Dummy unachieved", xe::to_utf16(db.GetStringTableEntry(language, entry.unachieved_id)),
i, // dummy image id entry.image_id,
0, entry.gamerscore,
{0, 0}, {0, 0},
8}; // flags=8 makes dummy achievements show up in 4D5307DC entry.flags};
// achievements list.
e->AppendItem(item); e->AppendItem(item);
} }
}
*handle_ptr = e->handle(); *handle_ptr = e->handle();
return X_ERROR_SUCCESS; return X_ERROR_SUCCESS;