Merge pull request #509 from x1nixmzeng/xex-resources
Rebrand the Xenia window with module metadata
This commit is contained in:
commit
86b706d87c
|
@ -190,6 +190,7 @@ solution("xenia")
|
|||
include("src/xenia/ui/gl")
|
||||
include("src/xenia/ui/spirv")
|
||||
include("src/xenia/vfs")
|
||||
include("src/xenia/xdbf")
|
||||
|
||||
if os.is("windows") then
|
||||
include("src/xenia/apu/xaudio2")
|
||||
|
|
|
@ -288,11 +288,16 @@ void EmulatorWindow::ShowHelpWebsite() { LaunchBrowser("http://xenia.jp"); }
|
|||
|
||||
void EmulatorWindow::UpdateTitle() {
|
||||
std::wstring title(base_title_);
|
||||
if (Clock::guest_time_scalar() != 1.0) {
|
||||
title += L" (@";
|
||||
title += xe::to_wstring(std::to_string(Clock::guest_time_scalar()));
|
||||
title += L"x)";
|
||||
|
||||
const std::wstring &game_title(emulator()->game_title());
|
||||
if (!game_title.empty()) {
|
||||
title = game_title + L" - " + title;
|
||||
}
|
||||
|
||||
if (Clock::guest_time_scalar() != 1.0) {
|
||||
title += xe::format_string(L" (@%.2fx)", Clock::guest_time_scalar());
|
||||
}
|
||||
|
||||
window_->set_title(title);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,11 +35,12 @@ class EmulatorWindow {
|
|||
ui::Loop* loop() const { return loop_.get(); }
|
||||
ui::Window* window() const { return window_.get(); }
|
||||
|
||||
void UpdateTitle();
|
||||
|
||||
private:
|
||||
explicit EmulatorWindow(Emulator* emulator);
|
||||
|
||||
bool Initialize();
|
||||
void UpdateTitle();
|
||||
|
||||
void CpuTimeScalarReset();
|
||||
void CpuTimeScalarSetHalf();
|
||||
|
|
|
@ -25,6 +25,7 @@ project("xenia-app")
|
|||
"xenia-ui",
|
||||
"xenia-ui-gl",
|
||||
"xenia-vfs",
|
||||
"xenia-xdbf",
|
||||
})
|
||||
flags({
|
||||
"WinMain", -- Use WinMain instead of main.
|
||||
|
|
|
@ -195,7 +195,8 @@ int xenia_main(const std::vector<std::wstring>& args) {
|
|||
// Normalize the path and make absolute.
|
||||
std::wstring abs_path = xe::to_absolute_path(path);
|
||||
|
||||
result = emulator->LaunchPath(abs_path);
|
||||
result = emulator->LaunchPath(abs_path,
|
||||
[&]() { emulator_window->UpdateTitle(); });
|
||||
if (XFAILED(result)) {
|
||||
XELOGE("Failed to launch target: %.8X", result);
|
||||
emulator.reset();
|
||||
|
|
|
@ -470,8 +470,8 @@ bool XexModule::SetupLibraryImports(const char* name,
|
|||
GuestFunction::ExternHandler handler = nullptr;
|
||||
if (kernel_export) {
|
||||
if (kernel_export->function_data.trampoline) {
|
||||
handler = (GuestFunction::ExternHandler)kernel_export
|
||||
->function_data.trampoline;
|
||||
handler = (GuestFunction::ExternHandler)
|
||||
kernel_export->function_data.trampoline;
|
||||
} else {
|
||||
handler =
|
||||
(GuestFunction::ExternHandler)kernel_export->function_data.shim;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "xenia/vfs/devices/host_path_device.h"
|
||||
#include "xenia/vfs/devices/stfs_container_device.h"
|
||||
#include "xenia/vfs/virtual_file_system.h"
|
||||
#include "xenia/xdbf/xdbf_utils.h"
|
||||
|
||||
DEFINE_double(time_scalar, 1.0,
|
||||
"Scalar used to speed or slow time (1x, 2x, 1/2x, etc).");
|
||||
|
@ -191,7 +192,8 @@ X_STATUS Emulator::Setup(
|
|||
return result;
|
||||
}
|
||||
|
||||
X_STATUS Emulator::LaunchPath(std::wstring path) {
|
||||
X_STATUS Emulator::LaunchPath(std::wstring path,
|
||||
std::function<void()> on_launch) {
|
||||
// Launch based on file type.
|
||||
// This is a silly guess based on file extension.
|
||||
auto last_slash = path.find_last_of(xe::kPathSeparator);
|
||||
|
@ -201,18 +203,19 @@ X_STATUS Emulator::LaunchPath(std::wstring path) {
|
|||
}
|
||||
if (last_dot == std::wstring::npos) {
|
||||
// Likely an STFS container.
|
||||
return LaunchStfsContainer(path);
|
||||
return LaunchStfsContainer(path, on_launch);
|
||||
} else if (path.substr(last_dot) == L".xex" ||
|
||||
path.substr(last_dot) == L".elf") {
|
||||
// Treat as a naked xex file.
|
||||
return LaunchXexFile(path);
|
||||
return LaunchXexFile(path, on_launch);
|
||||
} else {
|
||||
// Assume a disc image.
|
||||
return LaunchDiscImage(path);
|
||||
return LaunchDiscImage(path, on_launch);
|
||||
}
|
||||
}
|
||||
|
||||
X_STATUS Emulator::LaunchXexFile(std::wstring path) {
|
||||
X_STATUS Emulator::LaunchXexFile(std::wstring path,
|
||||
std::function<void()> on_launch) {
|
||||
// We create a virtual filesystem pointing to its directory and symlink
|
||||
// that to the game filesystem.
|
||||
// e.g., /my/files/foo.xex will get a local fs at:
|
||||
|
@ -244,10 +247,11 @@ X_STATUS Emulator::LaunchXexFile(std::wstring path) {
|
|||
|
||||
// Launch the game.
|
||||
std::string fs_path = "game:\\" + xe::to_string(file_name);
|
||||
return CompleteLaunch(path, fs_path);
|
||||
return CompleteLaunch(path, fs_path, on_launch);
|
||||
}
|
||||
|
||||
X_STATUS Emulator::LaunchDiscImage(std::wstring path) {
|
||||
X_STATUS Emulator::LaunchDiscImage(std::wstring path,
|
||||
std::function<void()> on_launch) {
|
||||
auto mount_path = "\\Device\\Cdrom0";
|
||||
|
||||
// Register the disc image in the virtual filesystem.
|
||||
|
@ -266,10 +270,11 @@ X_STATUS Emulator::LaunchDiscImage(std::wstring path) {
|
|||
file_system_->RegisterSymbolicLink("d:", mount_path);
|
||||
|
||||
// Launch the game.
|
||||
return CompleteLaunch(path, "game:\\default.xex");
|
||||
return CompleteLaunch(path, "game:\\default.xex", on_launch);
|
||||
}
|
||||
|
||||
X_STATUS Emulator::LaunchStfsContainer(std::wstring path) {
|
||||
X_STATUS Emulator::LaunchStfsContainer(std::wstring path,
|
||||
std::function<void()> on_launch) {
|
||||
auto mount_path = "\\Device\\Cdrom0";
|
||||
|
||||
// Register the container in the virtual filesystem.
|
||||
|
@ -288,7 +293,7 @@ X_STATUS Emulator::LaunchStfsContainer(std::wstring path) {
|
|||
file_system_->RegisterSymbolicLink("d:", mount_path);
|
||||
|
||||
// Launch the game.
|
||||
return CompleteLaunch(path, "game:\\default.xex");
|
||||
return CompleteLaunch(path, "game:\\default.xex", on_launch);
|
||||
}
|
||||
|
||||
void Emulator::Pause() {
|
||||
|
@ -493,12 +498,13 @@ void Emulator::WaitUntilExit() {
|
|||
}
|
||||
}
|
||||
|
||||
X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
|
||||
const std::string& module_path) {
|
||||
X_STATUS Emulator::CompleteLaunch(const std::wstring &path,
|
||||
const std::string &module_path,
|
||||
std::function<void()> on_launch) {
|
||||
// Allow xam to request module loads.
|
||||
auto xam = kernel_state()->GetKernelModule<kernel::xam::XamModule>("xam.xex");
|
||||
|
||||
int result = 0;
|
||||
X_STATUS result = X_STATUS_SUCCESS;
|
||||
auto next_module = module_path;
|
||||
while (next_module != "") {
|
||||
XELOGI("Launching module %s", next_module.c_str());
|
||||
|
@ -509,11 +515,34 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
|
|||
}
|
||||
|
||||
kernel_state_->SetExecutableModule(module);
|
||||
|
||||
// Try and load the resource database (xex only)
|
||||
char title[9] = {0};
|
||||
sprintf(title, "%08X", module->title_id());
|
||||
|
||||
uint32_t resource_data = 0;
|
||||
uint32_t resource_size = 0;
|
||||
if (XSUCCEEDED(module->GetSection(title, &resource_data, &resource_size))) {
|
||||
auto xdb_ptr = module->memory()->TranslateVirtual(resource_data);
|
||||
if (xdb_ptr != nullptr) {
|
||||
xe::xdbf::XdbfWrapper db;
|
||||
if (db.initialize(xdb_ptr, static_cast<size_t>(resource_size))) {
|
||||
game_title_ = xe::to_wstring(xe::xdbf::get_title(db));
|
||||
xe::xdbf::XdbfBlock icon_block = xe::xdbf::get_icon(db);
|
||||
if (icon_block.buffer != nullptr) {
|
||||
display_window_->SetIconFromBuffer(icon_block.buffer,
|
||||
icon_block.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto main_xthread = module->Launch();
|
||||
if (!main_xthread) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
on_launch();
|
||||
main_thread_ = main_xthread->thread();
|
||||
WaitUntilExit();
|
||||
|
||||
|
|
|
@ -53,6 +53,9 @@ class Emulator {
|
|||
// Full command line used when launching the process.
|
||||
const std::wstring& command_line() const { return command_line_; }
|
||||
|
||||
// Title of the game in the default language.
|
||||
const std::wstring& game_title() const { return game_title_; }
|
||||
|
||||
// Window used for displaying graphical output.
|
||||
ui::Window* display_window() const { return display_window_; }
|
||||
|
||||
|
@ -106,17 +109,18 @@ class Emulator {
|
|||
// Launches a game from the given file path.
|
||||
// This will attempt to infer the type of the given file (such as an iso, etc)
|
||||
// using heuristics.
|
||||
X_STATUS LaunchPath(std::wstring path);
|
||||
X_STATUS LaunchPath(std::wstring path, std::function<void()> on_launch);
|
||||
|
||||
// Launches a game from a .xex file by mounting the containing folder as if it
|
||||
// was an extracted STFS container.
|
||||
X_STATUS LaunchXexFile(std::wstring path);
|
||||
X_STATUS LaunchXexFile(std::wstring path, std::function<void()> on_launch);
|
||||
|
||||
// Launches a game from a disc image file (.iso, etc).
|
||||
X_STATUS LaunchDiscImage(std::wstring path);
|
||||
X_STATUS LaunchDiscImage(std::wstring path, std::function<void()> on_launch);
|
||||
|
||||
// Launches a game from an STFS container file.
|
||||
X_STATUS LaunchStfsContainer(std::wstring path);
|
||||
X_STATUS LaunchStfsContainer(std::wstring path,
|
||||
std::function<void()> on_launch);
|
||||
|
||||
void Pause();
|
||||
void Resume();
|
||||
|
@ -131,10 +135,12 @@ class Emulator {
|
|||
static bool ExceptionCallbackThunk(Exception* ex, void* data);
|
||||
bool ExceptionCallback(Exception* ex);
|
||||
|
||||
X_STATUS CompleteLaunch(const std::wstring& path,
|
||||
const std::string& module_path);
|
||||
X_STATUS CompleteLaunch(const std::wstring &path,
|
||||
const std::string &module_path,
|
||||
std::function<void()> on_launch);
|
||||
|
||||
std::wstring command_line_;
|
||||
std::wstring game_title_;
|
||||
|
||||
ui::Window* display_window_;
|
||||
|
||||
|
|
|
@ -105,8 +105,8 @@ uint32_t KernelModule::GetProcAddressByOrdinal(uint16_t ordinal) {
|
|||
|
||||
cpu::GuestFunction::ExternHandler handler = nullptr;
|
||||
if (export_entry->function_data.trampoline) {
|
||||
handler = (cpu::GuestFunction::ExternHandler)export_entry
|
||||
->function_data.trampoline;
|
||||
handler = (cpu::GuestFunction::ExternHandler)
|
||||
export_entry->function_data.trampoline;
|
||||
} else {
|
||||
handler =
|
||||
(cpu::GuestFunction::ExternHandler)export_entry->function_data.shim;
|
||||
|
|
|
@ -208,7 +208,7 @@ X_STATUS UserModule::GetSection(const char* name, uint32_t* out_section_data,
|
|||
return X_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
uint32_t count = (resource_header->size - 4) / 16;
|
||||
uint32_t count = (resource_header->size - 4) / sizeof(xex2_resource);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
auto& res = resource_header->resources[i];
|
||||
if (std::strncmp(name, res.name, 8) == 0) {
|
||||
|
|
|
@ -53,6 +53,8 @@ class Window {
|
|||
return true;
|
||||
}
|
||||
|
||||
virtual bool SetIconFromBuffer(void* buffer, size_t size) = 0;
|
||||
|
||||
virtual bool is_fullscreen() const { return false; }
|
||||
virtual void ToggleFullscreen(bool fullscreen) {}
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@ Win32Window::~Win32Window() {
|
|||
CloseWindow(hwnd_);
|
||||
hwnd_ = nullptr;
|
||||
}
|
||||
if (icon_ != nullptr) {
|
||||
DestroyIcon(icon_);
|
||||
icon_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NativePlatformHandle Win32Window::native_platform_handle() const {
|
||||
|
@ -163,6 +167,25 @@ bool Win32Window::set_title(const std::wstring& title) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Win32Window::SetIconFromBuffer(void* buffer, size_t size) {
|
||||
if (icon_ != nullptr) {
|
||||
DestroyIcon(icon_);
|
||||
}
|
||||
|
||||
HICON icon = CreateIconFromResourceEx(reinterpret_cast<BYTE*>(buffer),
|
||||
static_cast<DWORD>(size), TRUE,
|
||||
0x00030000, 0, 0, LR_DEFAULTCOLOR);
|
||||
|
||||
if (icon != nullptr) {
|
||||
icon_ = icon;
|
||||
|
||||
SendMessage(hwnd_, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(icon));
|
||||
SendMessage(hwnd_, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(icon));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Win32Window::is_fullscreen() const { return fullscreen_; }
|
||||
|
||||
void Win32Window::ToggleFullscreen(bool fullscreen) {
|
||||
|
|
|
@ -31,6 +31,7 @@ class Win32Window : public Window {
|
|||
HWND hwnd() const { return hwnd_; }
|
||||
|
||||
bool set_title(const std::wstring& title) override;
|
||||
bool SetIconFromBuffer(void* buffer, size_t size) override;
|
||||
|
||||
bool is_fullscreen() const override;
|
||||
void ToggleFullscreen(bool fullscreen) override;
|
||||
|
@ -68,6 +69,7 @@ class Win32Window : public Window {
|
|||
bool HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
HWND hwnd_ = nullptr;
|
||||
HICON icon_ = nullptr;
|
||||
bool closing_ = false;
|
||||
bool fullscreen_ = false;
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
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()
|
|
@ -0,0 +1,142 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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
|
|
@ -0,0 +1,129 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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