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:
parent
86b706d87c
commit
9c93fa5187
|
@ -190,7 +190,6 @@ solution("xenia")
|
||||||
include("src/xenia/ui/gl")
|
include("src/xenia/ui/gl")
|
||||||
include("src/xenia/ui/spirv")
|
include("src/xenia/ui/spirv")
|
||||||
include("src/xenia/vfs")
|
include("src/xenia/vfs")
|
||||||
include("src/xenia/xdbf")
|
|
||||||
|
|
||||||
if os.is("windows") then
|
if os.is("windows") then
|
||||||
include("src/xenia/apu/xaudio2")
|
include("src/xenia/apu/xaudio2")
|
||||||
|
|
|
@ -289,9 +289,9 @@ void EmulatorWindow::ShowHelpWebsite() { LaunchBrowser("http://xenia.jp"); }
|
||||||
void EmulatorWindow::UpdateTitle() {
|
void EmulatorWindow::UpdateTitle() {
|
||||||
std::wstring title(base_title_);
|
std::wstring title(base_title_);
|
||||||
|
|
||||||
const std::wstring &game_title(emulator()->game_title());
|
auto game_title = emulator()->game_title();
|
||||||
if (!game_title.empty()) {
|
if (!game_title.empty()) {
|
||||||
title = game_title + L" - " + title;
|
title += L" - " + game_title;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Clock::guest_time_scalar() != 1.0) {
|
if (Clock::guest_time_scalar() != 1.0) {
|
||||||
|
|
|
@ -25,7 +25,6 @@ project("xenia-app")
|
||||||
"xenia-ui",
|
"xenia-ui",
|
||||||
"xenia-ui-gl",
|
"xenia-ui-gl",
|
||||||
"xenia-vfs",
|
"xenia-vfs",
|
||||||
"xenia-xdbf",
|
|
||||||
})
|
})
|
||||||
flags({
|
flags({
|
||||||
"WinMain", -- Use WinMain instead of main.
|
"WinMain", -- Use WinMain instead of main.
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "xenia/hid/input_system.h"
|
#include "xenia/hid/input_system.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/user_module.h"
|
#include "xenia/kernel/user_module.h"
|
||||||
|
#include "xenia/kernel/util/xdbf_utils.h"
|
||||||
#include "xenia/kernel/xam/xam_module.h"
|
#include "xenia/kernel/xam/xam_module.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_module.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_module.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
|
@ -35,7 +36,6 @@
|
||||||
#include "xenia/vfs/devices/host_path_device.h"
|
#include "xenia/vfs/devices/host_path_device.h"
|
||||||
#include "xenia/vfs/devices/stfs_container_device.h"
|
#include "xenia/vfs/devices/stfs_container_device.h"
|
||||||
#include "xenia/vfs/virtual_file_system.h"
|
#include "xenia/vfs/virtual_file_system.h"
|
||||||
#include "xenia/xdbf/xdbf_utils.h"
|
|
||||||
|
|
||||||
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).");
|
||||||
|
@ -516,22 +516,21 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring &path,
|
||||||
|
|
||||||
kernel_state_->SetExecutableModule(module);
|
kernel_state_->SetExecutableModule(module);
|
||||||
|
|
||||||
// Try and load the resource database (xex only)
|
// Try and load the resource database (xex only).
|
||||||
char title[9] = {0};
|
if (module->title_id()) {
|
||||||
sprintf(title, "%08X", module->title_id());
|
char title_id[9] = {0};
|
||||||
|
std::sprintf(title_id, "%08X", module->title_id());
|
||||||
uint32_t resource_data = 0;
|
uint32_t resource_data = 0;
|
||||||
uint32_t resource_size = 0;
|
uint32_t resource_size = 0;
|
||||||
if (XSUCCEEDED(module->GetSection(title, &resource_data, &resource_size))) {
|
if (XSUCCEEDED(
|
||||||
auto xdb_ptr = module->memory()->TranslateVirtual(resource_data);
|
module->GetSection(title_id, &resource_data, &resource_size))) {
|
||||||
if (xdb_ptr != nullptr) {
|
kernel::util::XdbfGameData db(
|
||||||
xe::xdbf::XdbfWrapper db;
|
module->memory()->TranslateVirtual(resource_data), resource_size);
|
||||||
if (db.initialize(xdb_ptr, static_cast<size_t>(resource_size))) {
|
if (db.is_valid()) {
|
||||||
game_title_ = xe::to_wstring(xe::xdbf::get_title(db));
|
game_title_ = xe::to_wstring(db.title());
|
||||||
xe::xdbf::XdbfBlock icon_block = xe::xdbf::get_icon(db);
|
auto icon_block = db.icon();
|
||||||
if (icon_block.buffer != nullptr) {
|
if (icon_block) {
|
||||||
display_window_->SetIconFromBuffer(icon_block.buffer,
|
display_window_->SetIcon(icon_block.buffer, icon_block.size);
|
||||||
icon_block.size);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ TracePlayer::TracePlayer(xe::ui::Loop* loop, GraphicsSystem* graphics_system)
|
||||||
TracePlayer::~TracePlayer() = default;
|
TracePlayer::~TracePlayer() = default;
|
||||||
|
|
||||||
const TraceReader::Frame* TracePlayer::current_frame() const {
|
const TraceReader::Frame* TracePlayer::current_frame() const {
|
||||||
if (current_frame_index_ > frame_count()) {
|
if (current_frame_index_ >= frame_count()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return frame(current_frame_index_);
|
return frame(current_frame_index_);
|
||||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -53,7 +53,8 @@ class Window {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool SetIconFromBuffer(void* buffer, size_t size) = 0;
|
virtual bool SetIcon(const void* buffer, size_t size) = 0;
|
||||||
|
void ResetIcon() { SetIcon(nullptr, 0); }
|
||||||
|
|
||||||
virtual bool is_fullscreen() const { return false; }
|
virtual bool is_fullscreen() const { return false; }
|
||||||
virtual void ToggleFullscreen(bool fullscreen) {}
|
virtual void ToggleFullscreen(bool fullscreen) {}
|
||||||
|
|
|
@ -31,7 +31,7 @@ Win32Window::~Win32Window() {
|
||||||
CloseWindow(hwnd_);
|
CloseWindow(hwnd_);
|
||||||
hwnd_ = nullptr;
|
hwnd_ = nullptr;
|
||||||
}
|
}
|
||||||
if (icon_ != nullptr) {
|
if (icon_) {
|
||||||
DestroyIcon(icon_);
|
DestroyIcon(icon_);
|
||||||
icon_ = nullptr;
|
icon_ = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -167,20 +167,29 @@ bool Win32Window::set_title(const std::wstring& title) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Win32Window::SetIconFromBuffer(void* buffer, size_t size) {
|
bool Win32Window::SetIcon(const void* buffer, size_t size) {
|
||||||
if (icon_ != nullptr) {
|
if (icon_ != nullptr) {
|
||||||
DestroyIcon(icon_);
|
DestroyIcon(icon_);
|
||||||
|
icon_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
HICON icon = CreateIconFromResourceEx(reinterpret_cast<BYTE*>(buffer),
|
// Reset icon to default.
|
||||||
static_cast<DWORD>(size), TRUE,
|
auto default_icon = LoadIcon(GetModuleHandle(nullptr), L"MAINICON");
|
||||||
0x00030000, 0, 0, LR_DEFAULTCOLOR);
|
SendMessage(hwnd_, WM_SETICON, ICON_BIG,
|
||||||
|
reinterpret_cast<LPARAM>(default_icon));
|
||||||
|
SendMessage(hwnd_, WM_SETICON, ICON_SMALL,
|
||||||
|
reinterpret_cast<LPARAM>(default_icon));
|
||||||
|
if (!buffer || !size) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (icon != nullptr) {
|
// Create icon and set on window (if it's valid).
|
||||||
icon_ = icon;
|
icon_ = CreateIconFromResourceEx(
|
||||||
|
reinterpret_cast<PBYTE>(const_cast<void*>(buffer)),
|
||||||
SendMessage(hwnd_, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(icon));
|
static_cast<DWORD>(size), TRUE, 0x00030000, 0, 0, LR_DEFAULTCOLOR);
|
||||||
SendMessage(hwnd_, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(icon));
|
if (icon_) {
|
||||||
|
SendMessage(hwnd_, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(icon_));
|
||||||
|
SendMessage(hwnd_, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(icon_));
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -31,7 +31,8 @@ class Win32Window : public Window {
|
||||||
HWND hwnd() const { return hwnd_; }
|
HWND hwnd() const { return hwnd_; }
|
||||||
|
|
||||||
bool set_title(const std::wstring& title) override;
|
bool set_title(const std::wstring& title) override;
|
||||||
bool SetIconFromBuffer(void* buffer, size_t size) override;
|
|
||||||
|
bool SetIcon(const void* buffer, size_t size) override;
|
||||||
|
|
||||||
bool is_fullscreen() const override;
|
bool is_fullscreen() const override;
|
||||||
void ToggleFullscreen(bool fullscreen) override;
|
void ToggleFullscreen(bool fullscreen) override;
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
project_root = "../../.."
|
|
||||||
include(project_root.."/tools/build")
|
|
||||||
|
|
||||||
group("src")
|
|
||||||
project("xenia-xdbf")
|
|
||||||
uuid("a95b5fce-1083-4bff-a022-ffdd0bab3db0")
|
|
||||||
kind("StaticLib")
|
|
||||||
language("C++")
|
|
||||||
links({
|
|
||||||
"xenia-base",
|
|
||||||
})
|
|
||||||
defines({
|
|
||||||
})
|
|
||||||
includedirs({
|
|
||||||
project_root.."third_party/gflags/src",
|
|
||||||
})
|
|
||||||
recursive_platform_files()
|
|
|
@ -1,142 +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/xdbf/xdbf_utils.h"
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace xdbf {
|
|
||||||
|
|
||||||
enum XdbfId : uint64_t {
|
|
||||||
kIdTitle = 0x8000,
|
|
||||||
kIdXSTC = 0x58535443,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum XdbgMagic : uint32_t {
|
|
||||||
kMagicXSTC = 'XSTC',
|
|
||||||
kMagicXSTR = 'XSTR',
|
|
||||||
};
|
|
||||||
|
|
||||||
XdbfWrapper::XdbfWrapper() = default;
|
|
||||||
|
|
||||||
XBDF_HEADER& XdbfWrapper::get_header() const { return *state_.header; }
|
|
||||||
|
|
||||||
XBDF_ENTRY& XdbfWrapper::get_entry(uint32_t n) const {
|
|
||||||
return state_.entries[n];
|
|
||||||
}
|
|
||||||
|
|
||||||
XBDF_FILE_LOC& XdbfWrapper::get_file(uint32_t n) const {
|
|
||||||
return state_.files[n];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool XdbfWrapper::initialize(uint8_t* buffer, size_t length) {
|
|
||||||
if (length <= sizeof(XBDF_HEADER)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
XdbfState state;
|
|
||||||
|
|
||||||
state.data = buffer;
|
|
||||||
state.size = length;
|
|
||||||
|
|
||||||
uint8_t* ptr = state.data;
|
|
||||||
|
|
||||||
state.header = reinterpret_cast<XBDF_HEADER*>(ptr);
|
|
||||||
ptr += sizeof(XBDF_HEADER);
|
|
||||||
|
|
||||||
state.entries = reinterpret_cast<XBDF_ENTRY*>(ptr);
|
|
||||||
ptr += (sizeof(XBDF_ENTRY) * state.header->entry_count);
|
|
||||||
|
|
||||||
state.files = reinterpret_cast<XBDF_FILE_LOC*>(ptr);
|
|
||||||
ptr += (sizeof(XBDF_FILE_LOC) * state.header->free_count);
|
|
||||||
|
|
||||||
state.offset = ptr;
|
|
||||||
|
|
||||||
if (state.header->magic == 'XDBF') {
|
|
||||||
state_ = state;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
XdbfBlock XdbfWrapper::get_entry(XdbfSection section, uint64_t id) const {
|
|
||||||
XdbfBlock block = {nullptr, 0};
|
|
||||||
uint32_t x = 0;
|
|
||||||
|
|
||||||
while (x < get_header().entry_used) {
|
|
||||||
auto& entry = get_entry(x);
|
|
||||||
|
|
||||||
if (entry.section == section && entry.id == id) {
|
|
||||||
block.buffer = state_.offset + entry.offset;
|
|
||||||
block.size = entry.size;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
++x;
|
|
||||||
}
|
|
||||||
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
XdbfBlock get_icon(const XdbfWrapper& ref) {
|
|
||||||
return ref.get_entry(kSectionImage, kIdTitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
XdbfLocale get_default_language(const XdbfWrapper& ref) {
|
|
||||||
XdbfBlock block = ref.get_entry(kSectionMetadata, kIdXSTC);
|
|
||||||
if (block.buffer != nullptr) {
|
|
||||||
XDBF_XSTC* xstc = reinterpret_cast<XDBF_XSTC*>(block.buffer);
|
|
||||||
assert_true(xstc->magic == kMagicXSTC);
|
|
||||||
|
|
||||||
uint32_t default_language = xstc->default_language;
|
|
||||||
return static_cast<XdbfLocale>(default_language);
|
|
||||||
}
|
|
||||||
|
|
||||||
return kLocaleEnglish;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_title(const XdbfWrapper& ref) {
|
|
||||||
std::string title_str;
|
|
||||||
|
|
||||||
uint64_t language_id = static_cast<uint64_t>(get_default_language(ref));
|
|
||||||
|
|
||||||
XdbfBlock lang_block = ref.get_entry(kSectionStringTable, language_id);
|
|
||||||
|
|
||||||
if (lang_block.buffer != nullptr) {
|
|
||||||
XDBF_XSTR_HEADER* xstr_head =
|
|
||||||
reinterpret_cast<XDBF_XSTR_HEADER*>(lang_block.buffer);
|
|
||||||
|
|
||||||
assert_true(xstr_head->magic == kMagicXSTR);
|
|
||||||
assert_true(xstr_head->version == 1);
|
|
||||||
|
|
||||||
uint16_t str_count = xstr_head->string_count;
|
|
||||||
uint8_t* currentAddress = lang_block.buffer + sizeof(XDBF_XSTR_HEADER);
|
|
||||||
|
|
||||||
uint16_t s = 0;
|
|
||||||
while (s < str_count && title_str.empty()) {
|
|
||||||
XDBF_STRINGTABLE_ENTRY* entry =
|
|
||||||
reinterpret_cast<XDBF_STRINGTABLE_ENTRY*>(currentAddress);
|
|
||||||
currentAddress += sizeof(XDBF_STRINGTABLE_ENTRY);
|
|
||||||
uint16_t len = entry->string_length;
|
|
||||||
|
|
||||||
if (entry->id == static_cast<uint16_t>(kIdTitle)) {
|
|
||||||
title_str.resize(static_cast<size_t>(len));
|
|
||||||
std::copy(currentAddress, currentAddress + len, title_str.begin());
|
|
||||||
}
|
|
||||||
|
|
||||||
++s;
|
|
||||||
currentAddress += len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return title_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace xdbf
|
|
||||||
} // namespace xe
|
|
|
@ -1,129 +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_XDBF_XDBF_UTILS_H_
|
|
||||||
#define XENIA_XDBF_XDBF_UTILS_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "xenia/base/memory.h"
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace xdbf {
|
|
||||||
|
|
||||||
// http://freestyledash.googlecode.com/svn/trunk/Freestyle/Tools/XEX/SPA.h
|
|
||||||
// http://freestyledash.googlecode.com/svn/trunk/Freestyle/Tools/XEX/SPA.cpp
|
|
||||||
|
|
||||||
enum XdbfSection : uint16_t {
|
|
||||||
kSectionMetadata = 0x0001,
|
|
||||||
kSectionImage = 0x0002,
|
|
||||||
kSectionStringTable = 0x0003,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Found by dumping the kSectionStringTable sections of various games:
|
|
||||||
|
|
||||||
enum XdbfLocale : uint32_t {
|
|
||||||
kLocaleEnglish = 1,
|
|
||||||
kLocaleJapanese = 2,
|
|
||||||
kLocaleGerman = 3,
|
|
||||||
kLocaleFrench = 4,
|
|
||||||
kLocaleSpanish = 5,
|
|
||||||
kLocaleItalian = 6,
|
|
||||||
kLocaleKorean = 7,
|
|
||||||
kLocaleChinese = 8,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct XBDF_HEADER {
|
|
||||||
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(XBDF_HEADER, 24);
|
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
struct XBDF_ENTRY {
|
|
||||||
xe::be<uint16_t> section;
|
|
||||||
xe::be<uint64_t> id;
|
|
||||||
xe::be<uint32_t> offset;
|
|
||||||
xe::be<uint32_t> size;
|
|
||||||
};
|
|
||||||
static_assert_size(XBDF_ENTRY, 18);
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
struct XBDF_FILE_LOC {
|
|
||||||
xe::be<uint32_t> offset;
|
|
||||||
xe::be<uint32_t> size;
|
|
||||||
};
|
|
||||||
static_assert_size(XBDF_FILE_LOC, 8);
|
|
||||||
|
|
||||||
struct XDBF_XSTC {
|
|
||||||
xe::be<uint32_t> magic;
|
|
||||||
xe::be<uint32_t> version;
|
|
||||||
xe::be<uint32_t> size;
|
|
||||||
xe::be<uint32_t> default_language;
|
|
||||||
};
|
|
||||||
static_assert_size(XDBF_XSTC, 16);
|
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
struct XDBF_XSTR_HEADER {
|
|
||||||
xe::be<uint32_t> magic;
|
|
||||||
xe::be<uint32_t> version;
|
|
||||||
xe::be<uint32_t> size;
|
|
||||||
xe::be<uint16_t> string_count;
|
|
||||||
};
|
|
||||||
static_assert_size(XDBF_XSTR_HEADER, 14);
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
struct XDBF_STRINGTABLE_ENTRY {
|
|
||||||
xe::be<uint16_t> id;
|
|
||||||
xe::be<uint16_t> string_length;
|
|
||||||
};
|
|
||||||
static_assert_size(XDBF_STRINGTABLE_ENTRY, 4);
|
|
||||||
|
|
||||||
struct XdbfState {
|
|
||||||
XBDF_HEADER* header;
|
|
||||||
XBDF_ENTRY* entries;
|
|
||||||
XBDF_FILE_LOC* files;
|
|
||||||
uint8_t* offset;
|
|
||||||
uint8_t* data;
|
|
||||||
size_t size;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct XdbfBlock {
|
|
||||||
uint8_t* buffer;
|
|
||||||
size_t size;
|
|
||||||
};
|
|
||||||
|
|
||||||
class XdbfWrapper {
|
|
||||||
public:
|
|
||||||
XdbfWrapper();
|
|
||||||
|
|
||||||
bool initialize(uint8_t* buffer, size_t length);
|
|
||||||
XdbfBlock get_entry(XdbfSection section, uint64_t id) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
XBDF_HEADER& get_header() const;
|
|
||||||
XBDF_ENTRY& get_entry(uint32_t n) const;
|
|
||||||
XBDF_FILE_LOC& get_file(uint32_t n) const;
|
|
||||||
|
|
||||||
XdbfState state_;
|
|
||||||
};
|
|
||||||
|
|
||||||
XdbfBlock get_icon(const XdbfWrapper& ref);
|
|
||||||
XdbfLocale get_default_language(const XdbfWrapper& ref);
|
|
||||||
std::string get_title(const XdbfWrapper& ref);
|
|
||||||
|
|
||||||
} // namespace xdbf
|
|
||||||
} // namespace xe
|
|
||||||
|
|
||||||
#endif // XENIA_XDBF_XDBF_UTILS_H_
|
|
Loading…
Reference in New Issue