[Kernel] Simple support for loading achievement data
This commit is contained in:
parent
a25ea03a28
commit
9f0d3d4c5b
|
@ -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,24 +867,28 @@ 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 (db.is_valid()) {
|
||||||
if (XSUCCEEDED(module->GetSection(title_id.c_str(), &resource_data,
|
XLanguage language =
|
||||||
&resource_size))) {
|
db.GetExistingLanguage(static_cast<XLanguage>(cvars::user_language));
|
||||||
kernel::util::XdbfGameData db(
|
title_name_ = db.title(language);
|
||||||
module->memory()->TranslateVirtual(resource_data), resource_size);
|
XELOGI("Title name: {}", title_name_);
|
||||||
if (db.is_valid()) {
|
|
||||||
// TODO(gibbed): get title respective to user locale.
|
// Show achievments data
|
||||||
title_name_ = db.title(XLanguage::kEnglish);
|
XELOGI("-------------------- ACHIEVEMENTS --------------------");
|
||||||
if (title_name_.empty()) {
|
auto ach = db.GetAchievements();
|
||||||
// If English title is unavailable, get the title in default locale.
|
for (const auto& entry : ach) {
|
||||||
title_name_ = db.title();
|
std::string label = db.GetStringTableEntry(language, entry.label_id);
|
||||||
}
|
std::string desc =
|
||||||
XELOGI("Title name: {}", title_name_);
|
db.GetStringTableEntry(language, entry.description_id);
|
||||||
auto icon_block = db.icon();
|
|
||||||
if (icon_block) {
|
XELOGI("{} - {} - {} - {}", entry.id, label, desc, entry.gamerscore);
|
||||||
display_window_->SetIcon(icon_block.buffer, icon_block.size);
|
}
|
||||||
}
|
XELOGI("----------------- END OF ACHIEVEMENTS --------------------");
|
||||||
|
|
||||||
|
auto icon_block = db.icon();
|
||||||
|
if (icon_block) {
|
||||||
|
display_window_->SetIcon(icon_block.buffer, icon_block.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -29,6 +29,70 @@ enum class XdbfSection : uint16_t {
|
||||||
kStringTable = 0x0003,
|
kStringTable = 0x0003,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct XbdfHeader {
|
||||||
|
xe::be<uint32_t> magic;
|
||||||
|
xe::be<uint32_t> version;
|
||||||
|
xe::be<uint32_t> entry_count;
|
||||||
|
xe::be<uint32_t> entry_used;
|
||||||
|
xe::be<uint32_t> free_count;
|
||||||
|
xe::be<uint32_t> free_used;
|
||||||
|
};
|
||||||
|
static_assert_size(XbdfHeader, 24);
|
||||||
|
|
||||||
|
struct XbdfEntry {
|
||||||
|
xe::be<uint16_t> section;
|
||||||
|
xe::be<uint64_t> id;
|
||||||
|
xe::be<uint32_t> offset;
|
||||||
|
xe::be<uint32_t> size;
|
||||||
|
};
|
||||||
|
static_assert_size(XbdfEntry, 18);
|
||||||
|
|
||||||
|
struct XbdfFileLoc {
|
||||||
|
xe::be<uint32_t> offset;
|
||||||
|
xe::be<uint32_t> size;
|
||||||
|
};
|
||||||
|
static_assert_size(XbdfFileLoc, 8);
|
||||||
|
|
||||||
|
struct XdbfXstc {
|
||||||
|
xe::be<uint32_t> magic;
|
||||||
|
xe::be<uint32_t> version;
|
||||||
|
xe::be<uint32_t> size;
|
||||||
|
xe::be<uint32_t> default_language;
|
||||||
|
};
|
||||||
|
static_assert_size(XdbfXstc, 16);
|
||||||
|
|
||||||
|
struct XdbfXstrHeader {
|
||||||
|
xe::be<uint32_t> magic;
|
||||||
|
xe::be<uint32_t> version;
|
||||||
|
xe::be<uint32_t> size;
|
||||||
|
xe::be<uint16_t> string_count;
|
||||||
|
};
|
||||||
|
static_assert_size(XdbfXstrHeader, 14);
|
||||||
|
|
||||||
|
struct XdbfStringTableEntry {
|
||||||
|
xe::be<uint16_t> id;
|
||||||
|
xe::be<uint16_t> string_length;
|
||||||
|
};
|
||||||
|
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)
|
||||||
|
|
||||||
struct XdbfBlock {
|
struct XdbfBlock {
|
||||||
const uint8_t* buffer;
|
const uint8_t* buffer;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
@ -52,55 +116,7 @@ class XdbfWrapper {
|
||||||
// Gets a string from the string table in the given language.
|
// Gets a string from the string table in the given language.
|
||||||
// Returns the empty string if the entry is not found.
|
// Returns the empty string if the entry is not found.
|
||||||
std::string GetStringTableEntry(XLanguage language, uint16_t string_id) const;
|
std::string GetStringTableEntry(XLanguage language, uint16_t string_id) const;
|
||||||
|
std::vector<XdbfAchievementTableEntry> GetAchievements() const;
|
||||||
protected:
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
struct XbdfHeader {
|
|
||||||
xe::be<uint32_t> magic;
|
|
||||||
xe::be<uint32_t> version;
|
|
||||||
xe::be<uint32_t> entry_count;
|
|
||||||
xe::be<uint32_t> entry_used;
|
|
||||||
xe::be<uint32_t> free_count;
|
|
||||||
xe::be<uint32_t> free_used;
|
|
||||||
};
|
|
||||||
static_assert_size(XbdfHeader, 24);
|
|
||||||
|
|
||||||
struct XbdfEntry {
|
|
||||||
xe::be<uint16_t> section;
|
|
||||||
xe::be<uint64_t> id;
|
|
||||||
xe::be<uint32_t> offset;
|
|
||||||
xe::be<uint32_t> size;
|
|
||||||
};
|
|
||||||
static_assert_size(XbdfEntry, 18);
|
|
||||||
|
|
||||||
struct XbdfFileLoc {
|
|
||||||
xe::be<uint32_t> offset;
|
|
||||||
xe::be<uint32_t> size;
|
|
||||||
};
|
|
||||||
static_assert_size(XbdfFileLoc, 8);
|
|
||||||
|
|
||||||
struct XdbfXstc {
|
|
||||||
xe::be<uint32_t> magic;
|
|
||||||
xe::be<uint32_t> version;
|
|
||||||
xe::be<uint32_t> size;
|
|
||||||
xe::be<uint32_t> default_language;
|
|
||||||
};
|
|
||||||
static_assert_size(XdbfXstc, 16);
|
|
||||||
|
|
||||||
struct XdbfXstrHeader {
|
|
||||||
xe::be<uint32_t> magic;
|
|
||||||
xe::be<uint32_t> version;
|
|
||||||
xe::be<uint32_t> size;
|
|
||||||
xe::be<uint16_t> string_count;
|
|
||||||
};
|
|
||||||
static_assert_size(XdbfXstrHeader, 14);
|
|
||||||
|
|
||||||
struct XdbfStringTableEntry {
|
|
||||||
xe::be<uint16_t> id;
|
|
||||||
xe::be<uint16_t> string_length;
|
|
||||||
};
|
|
||||||
static_assert_size(XdbfStringTableEntry, 4);
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const uint8_t* data_ = nullptr;
|
const uint8_t* data_ = nullptr;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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,19 +719,26 @@ 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) {
|
|
||||||
auto item = XStaticAchievementEnumerator::AchievementDetails{
|
if (db.is_valid()) {
|
||||||
i, // dummy achievement id
|
XLanguage language = db.GetExistingLanguage(static_cast<XLanguage>(cvars::user_language));
|
||||||
fmt::format(u"Dummy {}", i),
|
std::vector<kernel::util::XdbfAchievementTableEntry> achievement_list =
|
||||||
u"Dummy description",
|
db.GetAchievements();
|
||||||
u"Dummy unachieved",
|
|
||||||
i, // dummy image id
|
for (const auto& entry : achievement_list) {
|
||||||
0,
|
auto item = XStaticAchievementEnumerator::AchievementDetails{
|
||||||
{0, 0},
|
entry.id,
|
||||||
8}; // flags=8 makes dummy achievements show up in 4D5307DC
|
xe::to_utf16(db.GetStringTableEntry(language, entry.label_id)),
|
||||||
// achievements list.
|
xe::to_utf16(db.GetStringTableEntry(language, entry.description_id)),
|
||||||
e->AppendItem(item);
|
xe::to_utf16(db.GetStringTableEntry(language, entry.unachieved_id)),
|
||||||
|
entry.image_id,
|
||||||
|
entry.gamerscore,
|
||||||
|
{0, 0},
|
||||||
|
entry.flags};
|
||||||
|
|
||||||
|
e->AppendItem(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*handle_ptr = e->handle();
|
*handle_ptr = e->handle();
|
||||||
|
|
Loading…
Reference in New Issue