[Kernel] Added support for writing/reading GPD files
This breaks settings in games that are using them and savefiles in games that use settings to store progress
This commit is contained in:
committed by
Radosław Gliński
parent
ccf7adf015
commit
1110cdd372
@@ -14,21 +14,24 @@ namespace xe {
|
||||
namespace kernel {
|
||||
namespace util {
|
||||
|
||||
GameInfoDatabase::GameInfoDatabase(const XdbfGameData* data) {
|
||||
GameInfoDatabase::GameInfoDatabase(const xam::SpaInfo* data) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data->is_valid()) {
|
||||
return;
|
||||
}
|
||||
Init(data);
|
||||
}
|
||||
|
||||
GameInfoDatabase::~GameInfoDatabase() {}
|
||||
|
||||
void GameInfoDatabase::Init(const xam::SpaInfo* data) {
|
||||
spa_gamedata_ = std::make_unique<xam::SpaInfo>(*data);
|
||||
spa_gamedata_->Load();
|
||||
is_valid_ = true;
|
||||
xdbf_gamedata_ = std::make_unique<XdbfGameData>(*data);
|
||||
|
||||
uint32_t compressed_size, decompressed_size = 0;
|
||||
const uint8_t* xlast_ptr =
|
||||
xdbf_gamedata_->ReadXLast(compressed_size, decompressed_size);
|
||||
spa_gamedata_->ReadXLast(compressed_size, decompressed_size);
|
||||
|
||||
if (!xlast_ptr) {
|
||||
XELOGW(
|
||||
@@ -48,26 +51,24 @@ GameInfoDatabase::GameInfoDatabase(const XdbfGameData* data) {
|
||||
}
|
||||
}
|
||||
|
||||
GameInfoDatabase::~GameInfoDatabase() {}
|
||||
|
||||
std::string GameInfoDatabase::GetTitleName(const XLanguage language) const {
|
||||
if (!is_valid_) {
|
||||
return "";
|
||||
void GameInfoDatabase::Update(const xam::SpaInfo* new_spa) {
|
||||
if (*spa_gamedata_ <= *new_spa) {
|
||||
return;
|
||||
}
|
||||
|
||||
return xdbf_gamedata_->title(xdbf_gamedata_->GetExistingLanguage(language));
|
||||
Init(new_spa);
|
||||
}
|
||||
|
||||
std::string GameInfoDatabase::GetTitleName(const XLanguage language) const {
|
||||
return spa_gamedata_->title_name(
|
||||
spa_gamedata_->GetExistingLanguage(language));
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GameInfoDatabase::GetIcon() const {
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
if (!is_valid_) {
|
||||
return data;
|
||||
}
|
||||
|
||||
const XdbfBlock icon = xdbf_gamedata_->icon();
|
||||
data.resize(icon.size);
|
||||
std::memcpy(data.data(), icon.buffer, icon.size);
|
||||
const auto icon = spa_gamedata_->title_icon();
|
||||
data.insert(data.begin(), icon.begin(), icon.end());
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -76,17 +77,13 @@ XLanguage GameInfoDatabase::GetDefaultLanguage() const {
|
||||
return XLanguage::kEnglish;
|
||||
}
|
||||
|
||||
return xdbf_gamedata_->default_language();
|
||||
return spa_gamedata_->default_language();
|
||||
}
|
||||
|
||||
std::string GameInfoDatabase::GetLocalizedString(const uint32_t id,
|
||||
XLanguage language) const {
|
||||
if (!is_valid_) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return xdbf_gamedata_->GetStringTableEntry(
|
||||
xdbf_gamedata_->GetExistingLanguage(language), id);
|
||||
return spa_gamedata_->GetStringTableEntry(
|
||||
spa_gamedata_->GetExistingLanguage(language), id);
|
||||
}
|
||||
|
||||
GameInfoDatabase::Context GameInfoDatabase::GetContext(
|
||||
@@ -97,12 +94,15 @@ GameInfoDatabase::Context GameInfoDatabase::GetContext(
|
||||
return context;
|
||||
}
|
||||
|
||||
const auto xdbf_context = xdbf_gamedata_->GetContext(id);
|
||||
const auto xdbf_context = spa_gamedata_->GetContext(id);
|
||||
if (!xdbf_context) {
|
||||
return context;
|
||||
}
|
||||
|
||||
context.id = xdbf_context.id;
|
||||
context.default_value = xdbf_context.default_value;
|
||||
context.max_value = xdbf_context.max_value;
|
||||
context.description = GetLocalizedString(xdbf_context.string_id);
|
||||
context.id = xdbf_context->id;
|
||||
context.default_value = xdbf_context->default_value;
|
||||
context.max_value = xdbf_context->max_value;
|
||||
context.description = GetLocalizedString(xdbf_context->string_id);
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -114,11 +114,14 @@ GameInfoDatabase::Property GameInfoDatabase::GetProperty(
|
||||
return property;
|
||||
}
|
||||
|
||||
const auto xdbf_property = xdbf_gamedata_->GetProperty(id);
|
||||
const auto xdbf_property = spa_gamedata_->GetProperty(id);
|
||||
if (!xdbf_property) {
|
||||
return property;
|
||||
}
|
||||
|
||||
property.id = xdbf_property.id;
|
||||
property.data_size = xdbf_property.data_size;
|
||||
property.description = GetLocalizedString(xdbf_property.string_id);
|
||||
property.id = xdbf_property->id;
|
||||
property.data_size = xdbf_property->data_size;
|
||||
property.description = GetLocalizedString(xdbf_property->string_id);
|
||||
return property;
|
||||
}
|
||||
|
||||
@@ -130,31 +133,29 @@ GameInfoDatabase::Achievement GameInfoDatabase::GetAchievement(
|
||||
return achievement;
|
||||
}
|
||||
|
||||
const auto xdbf_achievement = xdbf_gamedata_->GetAchievement(id);
|
||||
const auto xdbf_achievement = spa_gamedata_->GetAchievement(id);
|
||||
if (!xdbf_achievement) {
|
||||
return achievement;
|
||||
}
|
||||
|
||||
achievement.id = xdbf_achievement.id;
|
||||
achievement.image_id = xdbf_achievement.id;
|
||||
achievement.gamerscore = xdbf_achievement.gamerscore;
|
||||
achievement.flags = xdbf_achievement.flags;
|
||||
achievement.id = xdbf_achievement->id;
|
||||
achievement.image_id = xdbf_achievement->id;
|
||||
achievement.gamerscore = xdbf_achievement->gamerscore;
|
||||
achievement.flags = xdbf_achievement->flags;
|
||||
|
||||
achievement.label = GetLocalizedString(xdbf_achievement.label_id);
|
||||
achievement.description = GetLocalizedString(xdbf_achievement.description_id);
|
||||
achievement.label = GetLocalizedString(xdbf_achievement->label_id);
|
||||
achievement.description =
|
||||
GetLocalizedString(xdbf_achievement->description_id);
|
||||
achievement.unachieved_description =
|
||||
GetLocalizedString(xdbf_achievement.unachieved_id);
|
||||
GetLocalizedString(xdbf_achievement->unachieved_id);
|
||||
return achievement;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> GameInfoDatabase::GetMatchmakingAttributes(
|
||||
const uint32_t id) const {
|
||||
// TODO(Gliniak): Implement when we will fully understand how to read it from
|
||||
// SPA.
|
||||
std::vector<uint32_t> result;
|
||||
|
||||
const auto xdbf_matchmaking_data = xdbf_gamedata_->GetMatchCollection();
|
||||
|
||||
result.insert(result.end(), xdbf_matchmaking_data.contexts.cbegin(),
|
||||
xdbf_matchmaking_data.contexts.cend());
|
||||
result.insert(result.end(), xdbf_matchmaking_data.properties.cbegin(),
|
||||
xdbf_matchmaking_data.properties.cend());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -240,9 +241,9 @@ std::vector<GameInfoDatabase::Context> GameInfoDatabase::GetContexts() const {
|
||||
return contexts;
|
||||
}
|
||||
|
||||
const auto xdbf_contexts = xdbf_gamedata_->GetContexts();
|
||||
const auto xdbf_contexts = spa_gamedata_->GetContexts();
|
||||
for (const auto& entry : xdbf_contexts) {
|
||||
contexts.push_back(GetContext(entry.id));
|
||||
contexts.push_back(GetContext(entry->id));
|
||||
}
|
||||
|
||||
return contexts;
|
||||
@@ -256,9 +257,9 @@ std::vector<GameInfoDatabase::Property> GameInfoDatabase::GetProperties()
|
||||
return properties;
|
||||
}
|
||||
|
||||
const auto xdbf_properties = xdbf_gamedata_->GetProperties();
|
||||
const auto xdbf_properties = spa_gamedata_->GetProperties();
|
||||
for (const auto& entry : xdbf_properties) {
|
||||
properties.push_back(GetProperty(entry.id));
|
||||
properties.push_back(GetProperty(entry->id));
|
||||
}
|
||||
|
||||
return properties;
|
||||
@@ -272,9 +273,9 @@ std::vector<GameInfoDatabase::Achievement> GameInfoDatabase::GetAchievements()
|
||||
return achievements;
|
||||
}
|
||||
|
||||
const auto xdbf_achievements = xdbf_gamedata_->GetAchievements();
|
||||
const auto xdbf_achievements = spa_gamedata_->GetAchievements();
|
||||
for (const auto& entry : xdbf_achievements) {
|
||||
achievements.push_back(GetAchievement(entry.id));
|
||||
achievements.push_back(GetAchievement(entry->id));
|
||||
}
|
||||
|
||||
return achievements;
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/kernel/util/xdbf_utils.h"
|
||||
#include "xenia/kernel/util/xlast.h"
|
||||
#include "xenia/kernel/xam/xdbf/spa_info.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
@@ -87,11 +87,12 @@ class GameInfoDatabase {
|
||||
|
||||
// Normally titles have at least XDBF file embedded into xex. There are
|
||||
// certain exceptions and that's why we need to check if it is even valid.
|
||||
GameInfoDatabase(const XdbfGameData* data);
|
||||
GameInfoDatabase(const xam::SpaInfo* data);
|
||||
~GameInfoDatabase();
|
||||
|
||||
bool IsValid() const { return is_valid_; }
|
||||
|
||||
void Update(const xam::SpaInfo* new_spa);
|
||||
// This is mostly extracted from XDBF.
|
||||
std::string GetTitleName(
|
||||
const XLanguage language = XLanguage::kInvalid) const;
|
||||
@@ -123,8 +124,10 @@ class GameInfoDatabase {
|
||||
std::vector<StatsView> GetStatsViews() const;
|
||||
|
||||
private:
|
||||
void Init(const xam::SpaInfo* data);
|
||||
|
||||
bool is_valid_ = false;
|
||||
std::unique_ptr<XdbfGameData> xdbf_gamedata_;
|
||||
std::unique_ptr<xam::SpaInfo> spa_gamedata_;
|
||||
std::unique_ptr<XLast> xlast_gamedata_;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2024 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/kernel/util/property.h"
|
||||
#include "xenia/base/logging.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
Property::Property(uint32_t property_id, uint32_t value_size,
|
||||
uint8_t* value_ptr) {
|
||||
property_id_.value = property_id;
|
||||
data_type_ = static_cast<X_USER_DATA_TYPE>(property_id_.type);
|
||||
|
||||
value_size_ = value_size;
|
||||
value_.resize(value_size);
|
||||
|
||||
if (value_ptr != 0) {
|
||||
memcpy(value_.data(), value_ptr, value_size);
|
||||
} else {
|
||||
XELOGW("{} Ctor: provided value_ptr is nullptr!", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
Property::Property(const uint8_t* serialized_data, size_t data_size) {
|
||||
if (data_size < 8) {
|
||||
XELOGW("Property::Property lacks information. Skipping!");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&property_id_, serialized_data, sizeof(property_id_));
|
||||
data_type_ = static_cast<X_USER_DATA_TYPE>(property_id_.type);
|
||||
|
||||
memcpy(&value_size_, serialized_data + 4, sizeof(value_size_));
|
||||
|
||||
value_.resize(value_size_);
|
||||
memcpy(value_.data(), serialized_data + 8, value_size_);
|
||||
}
|
||||
|
||||
Property::~Property() {};
|
||||
|
||||
std::vector<uint8_t> Property::Serialize() const {
|
||||
std::vector<uint8_t> serialized_property;
|
||||
serialized_property.resize(sizeof(property_id_) + sizeof(value_size_) +
|
||||
value_.size());
|
||||
|
||||
size_t offset = 0;
|
||||
memcpy(serialized_property.data(), &property_id_, sizeof(property_id_));
|
||||
offset += sizeof(property_id_);
|
||||
|
||||
memcpy(serialized_property.data() + offset, &value_size_,
|
||||
sizeof(value_size_));
|
||||
|
||||
offset += sizeof(value_size_);
|
||||
|
||||
memcpy(serialized_property.data() + offset, value_.data(), value_.size());
|
||||
|
||||
return serialized_property;
|
||||
}
|
||||
|
||||
void Property::Write(Memory* memory, XUSER_PROPERTY* property) const {
|
||||
property->property_id = property_id_.value;
|
||||
property->data.type = data_type_;
|
||||
|
||||
switch (data_type_) {
|
||||
case X_USER_DATA_TYPE::WSTRING:
|
||||
property->data.binary.size = value_size_;
|
||||
break;
|
||||
case X_USER_DATA_TYPE::CONTENT:
|
||||
case X_USER_DATA_TYPE::BINARY:
|
||||
property->data.binary.size = value_size_;
|
||||
// Property pointer must be valid at this point!
|
||||
memcpy(memory->TranslateVirtual(property->data.binary.ptr), value_.data(),
|
||||
value_size_);
|
||||
break;
|
||||
case X_USER_DATA_TYPE::INT32:
|
||||
memcpy(reinterpret_cast<uint8_t*>(&property->data.s32), value_.data(),
|
||||
value_size_);
|
||||
break;
|
||||
case X_USER_DATA_TYPE::INT64:
|
||||
memcpy(reinterpret_cast<uint8_t*>(&property->data.s64), value_.data(),
|
||||
value_size_);
|
||||
break;
|
||||
case X_USER_DATA_TYPE::DOUBLE:
|
||||
memcpy(reinterpret_cast<uint8_t*>(&property->data.f64), value_.data(),
|
||||
value_size_);
|
||||
break;
|
||||
case X_USER_DATA_TYPE::FLOAT:
|
||||
memcpy(reinterpret_cast<uint8_t*>(&property->data.f32), value_.data(),
|
||||
value_size_);
|
||||
break;
|
||||
case X_USER_DATA_TYPE::DATETIME:
|
||||
memcpy(reinterpret_cast<uint8_t*>(&property->data.filetime),
|
||||
value_.data(), value_size_);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
userDataVariant Property::GetValue() const {
|
||||
switch (data_type_) {
|
||||
case X_USER_DATA_TYPE::CONTENT:
|
||||
case X_USER_DATA_TYPE::BINARY:
|
||||
return value_;
|
||||
case X_USER_DATA_TYPE::INT32:
|
||||
return *reinterpret_cast<const uint32_t*>(value_.data());
|
||||
case X_USER_DATA_TYPE::INT64:
|
||||
case X_USER_DATA_TYPE::DATETIME:
|
||||
return *reinterpret_cast<const uint64_t*>(value_.data());
|
||||
case X_USER_DATA_TYPE::DOUBLE:
|
||||
return *reinterpret_cast<const double*>(value_.data());
|
||||
case X_USER_DATA_TYPE::WSTRING:
|
||||
return std::u16string(reinterpret_cast<const char16_t*>(value_.data()));
|
||||
case X_USER_DATA_TYPE::FLOAT:
|
||||
return *reinterpret_cast<const float*>(value_.data());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return value_;
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
@@ -1,71 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2024 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_UTIL_PROPERTY_H_
|
||||
#define XENIA_KERNEL_UTIL_PROPERTY_H_
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "xenia/base/byte_stream.h"
|
||||
#include "xenia/kernel/util/xuserdata.h"
|
||||
#include "xenia/memory.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
struct XUSER_PROPERTY {
|
||||
xe::be<uint32_t> property_id;
|
||||
X_USER_DATA data;
|
||||
};
|
||||
|
||||
using userDataVariant = std::variant<uint32_t, uint64_t, float, double,
|
||||
std::u16string, std::vector<uint8_t> >;
|
||||
|
||||
class Property {
|
||||
public:
|
||||
// Ctor used while guest is creating property.
|
||||
Property(uint32_t property_id, uint32_t value_size, uint8_t* value_ptr);
|
||||
// Ctor used for deserialization
|
||||
Property(const uint8_t* serialized_data, size_t data_size);
|
||||
~Property();
|
||||
|
||||
const AttributeKey GetPropertyId() const { return property_id_; }
|
||||
|
||||
bool IsValid() const { return property_id_.value != 0; }
|
||||
std::vector<uint8_t> Serialize() const;
|
||||
|
||||
// Writer back to guest structure
|
||||
void Write(Memory* memory, XUSER_PROPERTY* property) const;
|
||||
uint32_t GetSize() const { return value_size_; }
|
||||
|
||||
bool RequiresPointer() const {
|
||||
return static_cast<X_USER_DATA_TYPE>(property_id_.type) ==
|
||||
X_USER_DATA_TYPE::CONTENT ||
|
||||
static_cast<X_USER_DATA_TYPE>(property_id_.type) ==
|
||||
X_USER_DATA_TYPE::WSTRING ||
|
||||
static_cast<X_USER_DATA_TYPE>(property_id_.type) ==
|
||||
X_USER_DATA_TYPE::BINARY;
|
||||
}
|
||||
|
||||
// Returns variant for specific value in LE (host) notation.
|
||||
userDataVariant GetValue() const;
|
||||
|
||||
private:
|
||||
AttributeKey property_id_ = {};
|
||||
X_USER_DATA_TYPE data_type_ = X_USER_DATA_TYPE::UNSET;
|
||||
|
||||
uint32_t value_size_ = 0;
|
||||
std::vector<uint8_t> value_;
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
#endif
|
||||
@@ -1,419 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2016 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/kernel/util/xdbf_utils.h"
|
||||
#include <map>
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace util {
|
||||
|
||||
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 fourcc_t kXdbfSignatureXvc2 = make_fourcc("XVC2");
|
||||
constexpr fourcc_t kXdbfSignatureXmat = make_fourcc("XMAT");
|
||||
constexpr fourcc_t kXdbfSignatureXsrc = make_fourcc("XSRC");
|
||||
constexpr fourcc_t kXdbfSignatureXthd = make_fourcc("XTHD");
|
||||
|
||||
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;
|
||||
constexpr uint64_t kXdbfIdXvc2 = 0x58564332;
|
||||
constexpr uint64_t kXdbfIdXmat = 0x584D4154;
|
||||
constexpr uint64_t kXdbfIdXsrc = 0x58535243;
|
||||
constexpr uint64_t kXdbfIdXthd = 0x58544844;
|
||||
|
||||
XdbfWrapper::XdbfWrapper(const uint8_t* data, size_t data_size)
|
||||
: data_(data), data_size_(data_size) {
|
||||
if (!data || data_size <= sizeof(XbdfHeader)) {
|
||||
data_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t* ptr = data_;
|
||||
|
||||
header_ = reinterpret_cast<const XbdfHeader*>(ptr);
|
||||
ptr += sizeof(XbdfHeader);
|
||||
if (header_->magic != kXdbfSignatureXdbf) {
|
||||
data_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
entries_ = reinterpret_cast<const XbdfEntry*>(ptr);
|
||||
ptr += sizeof(XbdfEntry) * header_->entry_count;
|
||||
|
||||
files_ = reinterpret_cast<const XbdfFileLoc*>(ptr);
|
||||
ptr += sizeof(XbdfFileLoc) * header_->free_count;
|
||||
|
||||
content_offset_ = ptr;
|
||||
}
|
||||
|
||||
XdbfBlock XdbfWrapper::GetEntry(XdbfSection section, uint64_t id) const {
|
||||
for (uint32_t i = 0; i < header_->entry_used; ++i) {
|
||||
auto& entry = entries_[i];
|
||||
if (entry.section == static_cast<uint16_t>(section) && entry.id == id) {
|
||||
XdbfBlock block;
|
||||
block.buffer = content_offset_ + entry.offset;
|
||||
block.size = entry.size;
|
||||
return block;
|
||||
}
|
||||
}
|
||||
return {0};
|
||||
}
|
||||
|
||||
std::string XdbfWrapper::GetStringTableEntry(XLanguage language,
|
||||
uint16_t string_id) const {
|
||||
auto language_block =
|
||||
GetEntry(XdbfSection::kStringTable, static_cast<uint64_t>(language));
|
||||
if (!language_block) {
|
||||
return "";
|
||||
}
|
||||
|
||||
auto xstr_head =
|
||||
reinterpret_cast<const XdbfSectionHeader*>(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 uint16_t string_count = xe::byte_swap<uint16_t>(*(uint16_t*)ptr);
|
||||
ptr += sizeof(uint16_t);
|
||||
|
||||
for (uint16_t i = 0; i < string_count; ++i) {
|
||||
auto entry = reinterpret_cast<const XdbfStringTableEntry*>(ptr);
|
||||
ptr += sizeof(XdbfStringTableEntry);
|
||||
if (entry->id == string_id) {
|
||||
return std::string(reinterpret_cast<const char*>(ptr),
|
||||
entry->string_length);
|
||||
}
|
||||
ptr += entry->string_length;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<XdbfAchievementTableEntry> XdbfWrapper::GetAchievements() const {
|
||||
std::vector<XdbfAchievementTableEntry> achievements;
|
||||
|
||||
auto achievement_table = GetEntry(XdbfSection::kMetadata, kXdbfIdXach);
|
||||
if (!achievement_table) {
|
||||
return achievements;
|
||||
}
|
||||
|
||||
auto xach_head =
|
||||
reinterpret_cast<const XdbfSectionHeader*>(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 uint16_t achievement_count = xe::byte_swap<uint16_t>(*(uint16_t*)ptr);
|
||||
ptr += sizeof(uint16_t);
|
||||
|
||||
for (uint16_t i = 0; i < achievement_count; ++i) {
|
||||
auto entry = reinterpret_cast<const XdbfAchievementTableEntry*>(ptr);
|
||||
ptr += sizeof(XdbfAchievementTableEntry);
|
||||
achievements.push_back(*entry);
|
||||
}
|
||||
return achievements;
|
||||
}
|
||||
|
||||
std::vector<XdbfPropertyTableEntry> XdbfWrapper::GetProperties() const {
|
||||
std::vector<XdbfPropertyTableEntry> properties;
|
||||
|
||||
auto property_table = GetEntry(XdbfSection::kMetadata, kXdbfIdXprp);
|
||||
if (!property_table) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
auto xprp_head =
|
||||
reinterpret_cast<const XdbfSectionHeader*>(property_table.buffer);
|
||||
assert_true(xprp_head->magic == kXdbfSignatureXprp);
|
||||
assert_true(xprp_head->version == 1);
|
||||
|
||||
const uint8_t* ptr = property_table.buffer + sizeof(XdbfSectionHeader);
|
||||
const uint16_t properties_count = xe::byte_swap<uint16_t>(*(uint16_t*)ptr);
|
||||
ptr += sizeof(uint16_t);
|
||||
|
||||
for (uint16_t i = 0; i < properties_count; i++) {
|
||||
auto entry = reinterpret_cast<const XdbfPropertyTableEntry*>(ptr);
|
||||
ptr += sizeof(XdbfPropertyTableEntry);
|
||||
properties.push_back(*entry);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::vector<XdbfContextTableEntry> XdbfWrapper::GetContexts() const {
|
||||
std::vector<XdbfContextTableEntry> contexts;
|
||||
|
||||
auto contexts_table = GetEntry(XdbfSection::kMetadata, kXdbfIdXctx);
|
||||
if (!contexts_table) {
|
||||
return contexts;
|
||||
}
|
||||
|
||||
auto xcxt_head =
|
||||
reinterpret_cast<const XdbfSectionHeader*>(contexts_table.buffer);
|
||||
assert_true(xcxt_head->magic == kXdbfSignatureXcxt);
|
||||
assert_true(xcxt_head->version == 1);
|
||||
|
||||
const uint8_t* ptr = contexts_table.buffer + sizeof(XdbfSectionHeader);
|
||||
const uint32_t contexts_count = xe::byte_swap<uint32_t>(*(uint32_t*)ptr);
|
||||
ptr += sizeof(uint32_t);
|
||||
|
||||
for (uint32_t i = 0; i < contexts_count; i++) {
|
||||
auto entry = reinterpret_cast<const XdbfContextTableEntry*>(ptr);
|
||||
ptr += sizeof(XdbfContextTableEntry);
|
||||
contexts.push_back(*entry);
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
std::vector<XdbfViewTable> XdbfWrapper::GetStatsView() const {
|
||||
std::vector<XdbfViewTable> entries;
|
||||
|
||||
auto stats_table = GetEntry(XdbfSection::kMetadata, kXdbfIdXvc2);
|
||||
if (!stats_table) {
|
||||
return entries;
|
||||
}
|
||||
|
||||
auto xvc2_head =
|
||||
reinterpret_cast<const XdbfSectionHeader*>(stats_table.buffer);
|
||||
assert_true(xvc2_head->magic == kXdbfSignatureXvc2);
|
||||
assert_true(xvc2_head->version == 1);
|
||||
|
||||
const uint8_t* ptr = stats_table.buffer + sizeof(XdbfSectionHeader);
|
||||
const uint16_t shared_view_count = xe::byte_swap<uint16_t>(*(uint16_t*)ptr);
|
||||
ptr += sizeof(uint16_t);
|
||||
|
||||
std::map<uint16_t, XdbfSharedView> shared_view_entries;
|
||||
for (uint16_t i = 0; i < shared_view_count; i++) {
|
||||
uint32_t byte_count = 0;
|
||||
shared_view_entries.emplace(i, GetSharedView(ptr, byte_count));
|
||||
ptr += byte_count;
|
||||
}
|
||||
|
||||
const uint16_t views_count = xe::byte_swap(*(uint16_t*)ptr);
|
||||
ptr += sizeof(uint16_t);
|
||||
|
||||
for (uint16_t i = 0; i < views_count; i++) {
|
||||
auto stat = reinterpret_cast<const XdbfStatsViewTableEntry*>(
|
||||
ptr + i * sizeof(XdbfStatsViewTableEntry));
|
||||
|
||||
XdbfViewTable table;
|
||||
table.view_entry = *stat;
|
||||
table.shared_view = shared_view_entries[stat->shared_index];
|
||||
entries.push_back(table);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
const uint8_t* XdbfWrapper::ReadXLast(uint32_t& compressed_size,
|
||||
uint32_t& decompressed_size) const {
|
||||
auto xlast_table = GetEntry(XdbfSection::kMetadata, kXdbfIdXsrc);
|
||||
if (!xlast_table) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto xlast_head =
|
||||
reinterpret_cast<const XdbfSectionHeader*>(xlast_table.buffer);
|
||||
assert_true(xlast_head->magic == kXdbfSignatureXsrc);
|
||||
assert_true(xlast_head->version == 1);
|
||||
|
||||
const uint8_t* ptr = xlast_table.buffer + sizeof(XdbfSectionHeader);
|
||||
|
||||
const uint32_t filename_length = xe::byte_swap(*(uint32_t*)ptr);
|
||||
ptr += sizeof(uint32_t) + filename_length;
|
||||
|
||||
decompressed_size = xe::byte_swap(*(uint32_t*)ptr);
|
||||
ptr += sizeof(uint32_t);
|
||||
|
||||
compressed_size = xe::byte_swap(*(uint32_t*)ptr);
|
||||
ptr += sizeof(uint32_t);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
XdbfTitleHeaderData XdbfWrapper::GetTitleInformation() const {
|
||||
auto xlast_table = GetEntry(XdbfSection::kMetadata, kXdbfIdXthd);
|
||||
if (!xlast_table) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto xlast_head =
|
||||
reinterpret_cast<const XdbfSectionHeader*>(xlast_table.buffer);
|
||||
assert_true(xlast_head->magic == kXdbfSignatureXthd);
|
||||
assert_true(xlast_head->version == 1);
|
||||
|
||||
const XdbfTitleHeaderData* ptr = reinterpret_cast<const XdbfTitleHeaderData*>(
|
||||
xlast_table.buffer + sizeof(XdbfSectionHeader));
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
|
||||
XdbfSharedView XdbfWrapper::GetSharedView(const uint8_t* ptr,
|
||||
uint32_t& byte_count) const {
|
||||
XdbfSharedView shared_view;
|
||||
|
||||
byte_count += sizeof(XdbfSharedViewMetaTableEntry);
|
||||
auto table_header =
|
||||
reinterpret_cast<const XdbfSharedViewMetaTableEntry*>(ptr);
|
||||
ptr += sizeof(XdbfSharedViewMetaTableEntry);
|
||||
|
||||
for (uint16_t i = 0; i < table_header->column_count - 1; i++) {
|
||||
auto view_field = reinterpret_cast<const XdbfViewFieldEntry*>(
|
||||
ptr + (i * sizeof(XdbfViewFieldEntry)));
|
||||
shared_view.column_entries.push_back(*view_field);
|
||||
}
|
||||
|
||||
// Move pointer forward to next data
|
||||
ptr += (table_header->column_count * sizeof(XdbfViewFieldEntry));
|
||||
byte_count += (table_header->column_count * sizeof(XdbfViewFieldEntry));
|
||||
|
||||
for (uint16_t i = 0; i < table_header->row_count - 1; i++) {
|
||||
auto view_field = reinterpret_cast<const XdbfViewFieldEntry*>(
|
||||
ptr + (i * sizeof(XdbfViewFieldEntry)));
|
||||
shared_view.row_entries.push_back(*view_field);
|
||||
}
|
||||
|
||||
ptr += (table_header->row_count * sizeof(XdbfViewFieldEntry));
|
||||
byte_count += (table_header->row_count * sizeof(XdbfViewFieldEntry));
|
||||
|
||||
std::vector<xe::be<uint32_t>> contexts, properties;
|
||||
GetPropertyBagMetadata(ptr, byte_count, contexts, properties);
|
||||
|
||||
shared_view.property_bag.contexts = contexts;
|
||||
shared_view.property_bag.properties = properties;
|
||||
|
||||
return shared_view;
|
||||
}
|
||||
|
||||
void XdbfWrapper::GetPropertyBagMetadata(
|
||||
const uint8_t* ptr, uint32_t& byte_count,
|
||||
std::vector<xe::be<uint32_t>>& contexts,
|
||||
std::vector<xe::be<uint32_t>>& properties) const {
|
||||
auto xpbm_header = reinterpret_cast<const XdbfSectionHeader*>(ptr);
|
||||
ptr += sizeof(XdbfSectionHeader);
|
||||
|
||||
byte_count += sizeof(XdbfSectionHeader) + 2 * sizeof(uint32_t);
|
||||
|
||||
uint32_t context_count = xe::byte_swap<uint32_t>(*(uint32_t*)ptr);
|
||||
ptr += sizeof(uint32_t);
|
||||
|
||||
uint32_t properties_count = xe::byte_swap<uint32_t>(*(uint32_t*)ptr);
|
||||
ptr += sizeof(uint32_t);
|
||||
|
||||
contexts = std::vector<xe::be<uint32_t>>(context_count);
|
||||
std::memcpy(contexts.data(), ptr, context_count * sizeof(uint32_t));
|
||||
|
||||
ptr += context_count * sizeof(uint32_t);
|
||||
|
||||
properties = std::vector<xe::be<uint32_t>>(properties_count);
|
||||
std::memcpy(properties.data(), ptr, sizeof(uint32_t) * properties_count);
|
||||
|
||||
byte_count += (context_count + properties_count) * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
XdbfPropertyBag XdbfWrapper::GetMatchCollection() const {
|
||||
XdbfPropertyBag property_bag;
|
||||
|
||||
auto stats_table = GetEntry(XdbfSection::kMetadata, kXdbfIdXmat);
|
||||
if (!stats_table) {
|
||||
return property_bag;
|
||||
}
|
||||
|
||||
auto xvc2_head =
|
||||
reinterpret_cast<const XdbfSectionHeader*>(stats_table.buffer);
|
||||
assert_true(xvc2_head->magic == kXdbfSignatureXmat);
|
||||
assert_true(xvc2_head->version == 1);
|
||||
|
||||
const uint8_t* ptr = stats_table.buffer + sizeof(XdbfSectionHeader);
|
||||
|
||||
std::vector<xe::be<uint32_t>> contexts, properties;
|
||||
uint32_t byte_count = 0;
|
||||
|
||||
GetPropertyBagMetadata(ptr, byte_count, contexts, properties);
|
||||
|
||||
property_bag.contexts = contexts;
|
||||
property_bag.properties = properties;
|
||||
|
||||
return property_bag;
|
||||
}
|
||||
|
||||
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 {
|
||||
return GetEntry(XdbfSection::kImage, kXdbfIdTitle);
|
||||
}
|
||||
|
||||
XLanguage XdbfGameData::default_language() const {
|
||||
auto block = GetEntry(XdbfSection::kMetadata, kXdbfIdXstc);
|
||||
if (!block.buffer) {
|
||||
return XLanguage::kEnglish;
|
||||
}
|
||||
auto xstc = reinterpret_cast<const XdbfXstc*>(block.buffer);
|
||||
assert_true(xstc->magic == kXdbfSignatureXstc);
|
||||
return static_cast<XLanguage>(static_cast<uint32_t>(xstc->default_language));
|
||||
}
|
||||
|
||||
std::string XdbfGameData::title() const {
|
||||
return GetStringTableEntry(default_language(), kXdbfIdTitle);
|
||||
}
|
||||
|
||||
std::string XdbfGameData::title(XLanguage language) const {
|
||||
return GetStringTableEntry(language, kXdbfIdTitle);
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
@@ -1,250 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2016 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_UTIL_XDBF_UTILS_H_
|
||||
#define XENIA_KERNEL_UTIL_XDBF_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace util {
|
||||
|
||||
// https://github.com/oukiar/freestyledash/blob/master/Freestyle/Tools/XEX/SPA.h
|
||||
// https://github.com/oukiar/freestyledash/blob/master/Freestyle/Tools/XEX/SPA.cpp
|
||||
|
||||
enum class XdbfSection : uint16_t {
|
||||
kMetadata = 0x0001,
|
||||
kImage = 0x0002,
|
||||
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 XdbfSectionHeader {
|
||||
xe::be<uint32_t> magic;
|
||||
xe::be<uint32_t> version;
|
||||
xe::be<uint32_t> size;
|
||||
};
|
||||
static_assert_size(XdbfSectionHeader, 12);
|
||||
|
||||
struct XdbfStringTableEntry {
|
||||
xe::be<uint16_t> id;
|
||||
xe::be<uint16_t> string_length;
|
||||
};
|
||||
static_assert_size(XdbfStringTableEntry, 4);
|
||||
|
||||
struct XdbfTitleHeaderData {
|
||||
xe::be<uint32_t> title_id;
|
||||
xe::be<uint32_t> title_type;
|
||||
xe::be<uint16_t> major;
|
||||
xe::be<uint16_t> minor;
|
||||
xe::be<uint16_t> build;
|
||||
xe::be<uint16_t> revision;
|
||||
xe::be<uint32_t> padding_0;
|
||||
xe::be<uint32_t> padding_1;
|
||||
xe::be<uint32_t> padding_2;
|
||||
xe::be<uint32_t> padding_3;
|
||||
};
|
||||
static_assert_size(XdbfTitleHeaderData, 32);
|
||||
|
||||
struct XdbfContextTableEntry {
|
||||
xe::be<uint32_t> id;
|
||||
xe::be<uint16_t> unk1;
|
||||
xe::be<uint16_t> string_id;
|
||||
xe::be<uint32_t> max_value;
|
||||
xe::be<uint32_t> default_value;
|
||||
};
|
||||
static_assert_size(XdbfContextTableEntry, 16);
|
||||
|
||||
struct XdbfPropertyTableEntry {
|
||||
xe::be<uint32_t> id;
|
||||
xe::be<uint16_t> string_id;
|
||||
xe::be<uint16_t> data_size;
|
||||
};
|
||||
static_assert_size(XdbfPropertyTableEntry, 8);
|
||||
|
||||
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);
|
||||
|
||||
struct XdbfStatsViewTableEntry {
|
||||
xe::be<uint32_t> id;
|
||||
xe::be<uint32_t> flags;
|
||||
xe::be<uint16_t> shared_index;
|
||||
xe::be<uint16_t> string_id;
|
||||
xe::be<uint32_t> unused;
|
||||
};
|
||||
static_assert_size(XdbfStatsViewTableEntry, 0x10);
|
||||
|
||||
struct XdbfViewFieldEntry {
|
||||
xe::be<uint32_t> size;
|
||||
xe::be<uint32_t> property_id;
|
||||
xe::be<uint32_t> flags;
|
||||
xe::be<uint16_t> attribute_id;
|
||||
xe::be<uint16_t> string_id;
|
||||
xe::be<uint16_t> aggregation_type;
|
||||
xe::be<uint8_t> ordinal;
|
||||
xe::be<uint8_t> field_type;
|
||||
xe::be<uint32_t> format_type;
|
||||
xe::be<uint32_t> unused_1;
|
||||
xe::be<uint32_t> unused_2;
|
||||
};
|
||||
static_assert_size(XdbfViewFieldEntry, 0x20);
|
||||
|
||||
struct XdbfSharedViewMetaTableEntry {
|
||||
xe::be<uint16_t> column_count;
|
||||
xe::be<uint16_t> row_count;
|
||||
xe::be<uint32_t> unused_1;
|
||||
xe::be<uint32_t> unused_2;
|
||||
};
|
||||
static_assert_size(XdbfSharedViewMetaTableEntry, 0xC);
|
||||
#pragma pack(pop)
|
||||
|
||||
struct XdbfPropertyBag {
|
||||
std::vector<xe::be<uint32_t>> contexts;
|
||||
std::vector<xe::be<uint32_t>> properties;
|
||||
};
|
||||
|
||||
struct XdbfSharedView {
|
||||
std::vector<XdbfViewFieldEntry> column_entries;
|
||||
std::vector<XdbfViewFieldEntry> row_entries;
|
||||
XdbfPropertyBag property_bag;
|
||||
};
|
||||
|
||||
struct XdbfViewTable {
|
||||
XdbfStatsViewTableEntry view_entry;
|
||||
XdbfSharedView shared_view;
|
||||
};
|
||||
|
||||
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;
|
||||
std::vector<XdbfPropertyTableEntry> GetProperties() const;
|
||||
std::vector<XdbfContextTableEntry> GetContexts() const;
|
||||
|
||||
XdbfTitleHeaderData GetTitleInformation() const;
|
||||
XdbfAchievementTableEntry GetAchievement(const uint32_t id) const;
|
||||
XdbfPropertyTableEntry GetProperty(const uint32_t id) const;
|
||||
XdbfContextTableEntry GetContext(const uint32_t id) const;
|
||||
std::vector<XdbfViewTable> GetStatsView() const;
|
||||
XdbfSharedView GetSharedView(const uint8_t* ptr, uint32_t& byte_count) const;
|
||||
|
||||
void GetPropertyBagMetadata(const uint8_t* ptr, uint32_t& byte_count,
|
||||
std::vector<xe::be<uint32_t>>& contexts,
|
||||
std::vector<xe::be<uint32_t>>& properties) const;
|
||||
|
||||
XdbfPropertyBag GetMatchCollection() const;
|
||||
|
||||
const uint8_t* ReadXLast(uint32_t& compressed_size,
|
||||
uint32_t& decompressed_size) const;
|
||||
|
||||
private:
|
||||
const uint8_t* data_ = nullptr;
|
||||
size_t data_size_ = 0;
|
||||
const uint8_t* content_offset_ = nullptr;
|
||||
|
||||
const XbdfHeader* header_ = nullptr;
|
||||
const XbdfEntry* entries_ = nullptr;
|
||||
const XbdfFileLoc* files_ = nullptr;
|
||||
};
|
||||
|
||||
class XdbfGameData : public XdbfWrapper {
|
||||
public:
|
||||
XdbfGameData(const uint8_t* data, size_t 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.
|
||||
XdbfBlock icon() const;
|
||||
|
||||
// The game's default language.
|
||||
XLanguage default_language() const;
|
||||
|
||||
// The game's title in its default language.
|
||||
std::string title() const;
|
||||
|
||||
std::string title(XLanguage language) const;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_KERNEL_UTIL_XDBF_UTILS_H_
|
||||
@@ -1,234 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2024 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_UTIL_XUSERDATA_H_
|
||||
#define XENIA_KERNEL_UTIL_XUSERDATA_H_
|
||||
|
||||
#include "xenia/base/byte_stream.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
union AttributeKey {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint32_t id : 14;
|
||||
uint32_t unk : 2;
|
||||
uint32_t size : 12;
|
||||
uint32_t type : 4;
|
||||
};
|
||||
};
|
||||
|
||||
enum class X_USER_DATA_TYPE : uint8_t {
|
||||
CONTENT = 0,
|
||||
INT32 = 1,
|
||||
INT64 = 2,
|
||||
DOUBLE = 3,
|
||||
WSTRING = 4,
|
||||
FLOAT = 5,
|
||||
BINARY = 6,
|
||||
DATETIME = 7,
|
||||
UNSET = 0xFF,
|
||||
};
|
||||
|
||||
struct X_USER_DATA {
|
||||
X_USER_DATA_TYPE type;
|
||||
|
||||
union {
|
||||
be<int32_t> s32;
|
||||
be<int64_t> s64;
|
||||
be<uint32_t> u32;
|
||||
be<double> f64;
|
||||
struct {
|
||||
be<uint32_t> size;
|
||||
be<uint32_t> ptr;
|
||||
} unicode;
|
||||
be<float> f32;
|
||||
struct {
|
||||
be<uint32_t> size;
|
||||
be<uint32_t> ptr;
|
||||
} binary;
|
||||
be<uint64_t> filetime;
|
||||
};
|
||||
};
|
||||
static_assert_size(X_USER_DATA, 16);
|
||||
|
||||
class DataByteStream : public ByteStream {
|
||||
public:
|
||||
DataByteStream(uint32_t ptr, uint8_t* data, size_t data_length,
|
||||
size_t offset = 0)
|
||||
: ByteStream(data, data_length, offset), ptr_(ptr) {}
|
||||
|
||||
uint32_t ptr() const { return static_cast<uint32_t>(ptr_ + offset()); }
|
||||
|
||||
private:
|
||||
uint32_t ptr_;
|
||||
};
|
||||
|
||||
class UserData {
|
||||
public:
|
||||
UserData() {};
|
||||
UserData(X_USER_DATA_TYPE type) { data_.type = type; }
|
||||
|
||||
virtual void Append(X_USER_DATA* data, DataByteStream* stream) {
|
||||
data->type = data_.type;
|
||||
}
|
||||
|
||||
virtual std::vector<uint8_t> Serialize() const {
|
||||
return std::vector<uint8_t>();
|
||||
}
|
||||
virtual void Deserialize(std::vector<uint8_t>) {}
|
||||
|
||||
private:
|
||||
X_USER_DATA data_ = {};
|
||||
};
|
||||
|
||||
class Int32UserData : public UserData {
|
||||
public:
|
||||
Int32UserData(int32_t value)
|
||||
: UserData(X_USER_DATA_TYPE::INT32), value_(value) {}
|
||||
void Append(X_USER_DATA* data, DataByteStream* stream) override {
|
||||
UserData::Append(data, stream);
|
||||
data->s32 = value_;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t value_;
|
||||
};
|
||||
|
||||
class Uint32UserData : public UserData {
|
||||
public:
|
||||
Uint32UserData(uint32_t value)
|
||||
: UserData(X_USER_DATA_TYPE::INT32), value_(value) {}
|
||||
void Append(X_USER_DATA* data, DataByteStream* stream) override {
|
||||
UserData::Append(data, stream);
|
||||
data->u32 = value_;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t value_;
|
||||
};
|
||||
|
||||
class Int64UserData : public UserData {
|
||||
public:
|
||||
Int64UserData(int64_t value)
|
||||
: UserData(X_USER_DATA_TYPE::INT64), value_(value) {}
|
||||
void Append(X_USER_DATA* data, DataByteStream* stream) override {
|
||||
UserData::Append(data, stream);
|
||||
data->s64 = value_;
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t value_;
|
||||
};
|
||||
|
||||
class FloatUserData : public UserData {
|
||||
public:
|
||||
FloatUserData(float value)
|
||||
: UserData(X_USER_DATA_TYPE::FLOAT), value_(value) {}
|
||||
|
||||
void Append(X_USER_DATA* data, DataByteStream* stream) override {
|
||||
UserData::Append(data, stream);
|
||||
data->f32 = value_;
|
||||
}
|
||||
|
||||
private:
|
||||
float value_;
|
||||
};
|
||||
|
||||
class DoubleUserData : public UserData {
|
||||
public:
|
||||
DoubleUserData(double value)
|
||||
: UserData(X_USER_DATA_TYPE::DOUBLE), value_(value) {}
|
||||
void Append(X_USER_DATA* data, DataByteStream* stream) override {
|
||||
UserData::Append(data, stream);
|
||||
data->f64 = value_;
|
||||
}
|
||||
|
||||
private:
|
||||
double value_;
|
||||
};
|
||||
|
||||
class UnicodeUserData : public UserData {
|
||||
public:
|
||||
UnicodeUserData(const std::u16string& value)
|
||||
: UserData(X_USER_DATA_TYPE::WSTRING), value_(value) {}
|
||||
void Append(X_USER_DATA* data, DataByteStream* stream) override {
|
||||
UserData::Append(data, stream);
|
||||
|
||||
if (value_.empty()) {
|
||||
data->unicode.size = 0;
|
||||
data->unicode.ptr = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t count = value_.size() + 1;
|
||||
size_t size = 2 * count;
|
||||
assert_true(size <= std::numeric_limits<uint32_t>::max());
|
||||
data->unicode.size = static_cast<uint32_t>(size);
|
||||
data->unicode.ptr = stream->ptr();
|
||||
auto buffer =
|
||||
reinterpret_cast<uint16_t*>(&stream->data()[stream->offset()]);
|
||||
stream->Advance(size);
|
||||
copy_and_swap(buffer, (uint16_t*)value_.data(), count);
|
||||
}
|
||||
|
||||
private:
|
||||
std::u16string value_;
|
||||
};
|
||||
|
||||
class BinaryUserData : public UserData {
|
||||
public:
|
||||
BinaryUserData(const std::vector<uint8_t>& value)
|
||||
: UserData(X_USER_DATA_TYPE::BINARY), value_(value) {}
|
||||
void Append(X_USER_DATA* data, DataByteStream* stream) override {
|
||||
UserData::Append(data, stream);
|
||||
|
||||
if (value_.empty()) {
|
||||
data->binary.size = 0;
|
||||
data->binary.ptr = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t size = value_.size();
|
||||
assert_true(size <= std::numeric_limits<uint32_t>::max());
|
||||
data->binary.size = static_cast<uint32_t>(size);
|
||||
data->binary.ptr = stream->ptr();
|
||||
stream->Write(value_.data(), size);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Serialize() const override {
|
||||
return std::vector<uint8_t>(value_.data(), value_.data() + value_.size());
|
||||
}
|
||||
|
||||
void Deserialize(std::vector<uint8_t> data) override { value_ = data; }
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> value_;
|
||||
};
|
||||
|
||||
class DateTimeUserData : public UserData {
|
||||
public:
|
||||
DateTimeUserData(int64_t value)
|
||||
: UserData(X_USER_DATA_TYPE::DATETIME), value_(value) {}
|
||||
|
||||
void Append(X_USER_DATA* data, DataByteStream* stream) override {
|
||||
UserData::Append(data, stream);
|
||||
data->filetime = value_;
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t value_;
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user