Cleaning up xdbf stuff.
- moving next to xex utils - fixing error cases around icon setting (and allowing reset) - making C++11 - abstracting a bit so future additions will be easier - fixing non-xex uses
This commit is contained in:
107
src/xenia/kernel/util/xdbf_utils.cc
Normal file
107
src/xenia/kernel/util/xdbf_utils.cc
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace util {
|
||||
|
||||
constexpr uint32_t kXdbfMagicXdbf = 'XDBF';
|
||||
constexpr uint32_t kXdbfMagicXstc = 'XSTC';
|
||||
constexpr uint32_t kXdbfMagicXstr = 'XSTR';
|
||||
|
||||
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 != kXdbfMagicXdbf) {
|
||||
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(XdbfLocale locale,
|
||||
uint16_t string_id) const {
|
||||
auto language_block =
|
||||
GetEntry(XdbfSection::kStringTable, static_cast<uint64_t>(locale));
|
||||
if (!language_block) {
|
||||
return "";
|
||||
}
|
||||
|
||||
auto xstr_head =
|
||||
reinterpret_cast<const XdbfXstrHeader*>(language_block.buffer);
|
||||
assert_true(xstr_head->magic == kXdbfMagicXstr);
|
||||
assert_true(xstr_head->version == 1);
|
||||
|
||||
const uint8_t* ptr = language_block.buffer + sizeof(XdbfXstrHeader);
|
||||
for (uint16_t i = 0; i < xstr_head->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 "";
|
||||
}
|
||||
|
||||
constexpr uint64_t kXdbfIdTitle = 0x8000;
|
||||
constexpr uint64_t kXdbfIdXstc = 0x58535443;
|
||||
|
||||
XdbfBlock XdbfGameData::icon() const {
|
||||
return GetEntry(XdbfSection::kImage, kXdbfIdTitle);
|
||||
}
|
||||
|
||||
XdbfLocale XdbfGameData::default_language() const {
|
||||
auto block = GetEntry(XdbfSection::kMetadata, kXdbfIdXstc);
|
||||
if (!block.buffer) {
|
||||
return XdbfLocale::kEnglish;
|
||||
}
|
||||
auto xstc = reinterpret_cast<const XdbfXstc*>(block.buffer);
|
||||
assert_true(xstc->magic == kXdbfMagicXstc);
|
||||
return static_cast<XdbfLocale>(static_cast<uint32_t>(xstc->default_language));
|
||||
}
|
||||
|
||||
std::string XdbfGameData::title() const {
|
||||
return GetStringTableEntry(default_language(), kXdbfIdTitle);
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
146
src/xenia/kernel/util/xdbf_utils.h
Normal file
146
src/xenia/kernel/util/xdbf_utils.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace util {
|
||||
|
||||
// http://freestyledash.googlecode.com/svn/trunk/Freestyle/Tools/XEX/SPA.h
|
||||
// http://freestyledash.googlecode.com/svn/trunk/Freestyle/Tools/XEX/SPA.cpp
|
||||
|
||||
enum class XdbfSection : uint16_t {
|
||||
kMetadata = 0x0001,
|
||||
kImage = 0x0002,
|
||||
kStringTable = 0x0003,
|
||||
};
|
||||
|
||||
// Found by dumping the kSectionStringTable sections of various games:
|
||||
enum class XdbfLocale : uint32_t {
|
||||
kUnknown = 0,
|
||||
kEnglish = 1,
|
||||
kJapanese = 2,
|
||||
kGerman = 3,
|
||||
kFrench = 4,
|
||||
kSpanish = 5,
|
||||
kItalian = 6,
|
||||
kKorean = 7,
|
||||
kChinese = 8,
|
||||
};
|
||||
|
||||
struct XdbfBlock {
|
||||
const uint8_t* buffer;
|
||||
size_t size;
|
||||
|
||||
operator bool() const { return buffer != nullptr; }
|
||||
};
|
||||
|
||||
// Wraps an XBDF (XboxDataBaseFormat) in-memory database.
|
||||
// http://www.free60.org/wiki/XDBF
|
||||
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(XdbfLocale locale, uint16_t string_id) 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:
|
||||
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) {}
|
||||
|
||||
// The game icon image, if found.
|
||||
XdbfBlock icon() const;
|
||||
|
||||
// The game's default language.
|
||||
XdbfLocale default_language() const;
|
||||
|
||||
// The game's title in its default language.
|
||||
std::string title() const;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_KERNEL_UTIL_XDBF_UTILS_H_
|
||||
Reference in New Issue
Block a user