Community feedback
Updated naming convention Exposed the game name to Emulator for other uses Fixed bug with XDBF parsing
This commit is contained in:
parent
e4cef38d95
commit
5fa9499a12
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -526,13 +526,19 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
|
|||
if (xdb_ptr != nullptr) {
|
||||
xe::xdbf::XdbfWrapper db;
|
||||
if (db.initialize(xdb_ptr, static_cast<size_t>(resource_size))) {
|
||||
std::wstring title(xe::to_wstring(xe::xdbf::get_title(db)));
|
||||
display_window_->set_title(title);
|
||||
|
||||
std::string game_title(xe::xdbf::get_title(db));
|
||||
if (!game_title.empty()) {
|
||||
game_title_ = xe::to_wstring(game_title);
|
||||
// TODO(x1nixmzeng): Need to somehow callback to
|
||||
// EmulatorWindow::UpdateTitle
|
||||
display_window_->set_title(game_title_ + L" - " +
|
||||
display_window_->title());
|
||||
}
|
||||
xe::xdbf::XdbfBlock icon_block = xe::xdbf::get_icon(db);
|
||||
if (icon_block.buffer != nullptr) {
|
||||
display_window_->set_icon_from_buffer(icon_block.buffer,
|
||||
icon_block.size);
|
||||
display_window_->SetIconFromBuffer(icon_block.buffer,
|
||||
icon_block.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_; }
|
||||
|
||||
|
@ -135,6 +138,7 @@ class Emulator {
|
|||
const std::string& module_path);
|
||||
|
||||
std::wstring command_line_;
|
||||
std::wstring game_title_;
|
||||
|
||||
ui::Window* display_window_;
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ class Window {
|
|||
return true;
|
||||
}
|
||||
|
||||
virtual bool set_icon_from_buffer(void *buffer, size_t size) = 0;
|
||||
virtual bool SetIconFromBuffer(void *buffer, size_t size) = 0;
|
||||
|
||||
virtual bool is_fullscreen() const { return false; }
|
||||
virtual void ToggleFullscreen(bool fullscreen) {}
|
||||
|
|
|
@ -32,8 +32,8 @@ Win32Window::~Win32Window() {
|
|||
hwnd_ = nullptr;
|
||||
}
|
||||
if (icon_ != nullptr) {
|
||||
DestroyIcon(icon_);
|
||||
icon_ = nullptr;
|
||||
DestroyIcon(icon_);
|
||||
icon_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,7 +167,7 @@ bool Win32Window::set_title(const std::wstring& title) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Win32Window::set_icon_from_buffer(void* buffer, size_t size) {
|
||||
bool Win32Window::SetIconFromBuffer(void *buffer, size_t size) {
|
||||
if (icon_ != nullptr) {
|
||||
DestroyIcon(icon_);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class Win32Window : public Window {
|
|||
HWND hwnd() const { return hwnd_; }
|
||||
|
||||
bool set_title(const std::wstring& title) override;
|
||||
bool set_icon_from_buffer(void *buffer, size_t size) override;
|
||||
bool SetIconFromBuffer(void *buffer, size_t size) override;
|
||||
|
||||
bool is_fullscreen() const override;
|
||||
void ToggleFullscreen(bool fullscreen) override;
|
||||
|
|
|
@ -12,6 +12,16 @@
|
|||
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; }
|
||||
|
@ -40,10 +50,10 @@ bool XdbfWrapper::initialize(uint8_t* buffer, size_t length) {
|
|||
ptr += sizeof(XBDF_HEADER);
|
||||
|
||||
state.entries = reinterpret_cast<XBDF_ENTRY*>(ptr);
|
||||
ptr += (sizeof(XBDF_ENTRY) * state.header->entry_max);
|
||||
ptr += (sizeof(XBDF_ENTRY) * state.header->entry_count);
|
||||
|
||||
state.files = reinterpret_cast<XBDF_FILE_LOC*>(ptr);
|
||||
ptr += (sizeof(XBDF_FILE_LOC) * state.header->free_max);
|
||||
ptr += (sizeof(XBDF_FILE_LOC) * state.header->free_count);
|
||||
|
||||
state.offset = ptr;
|
||||
|
||||
|
@ -59,7 +69,7 @@ XdbfBlock XdbfWrapper::get_entry(XdbfSection section, uint64_t id) const {
|
|||
XdbfBlock block = {nullptr, 0};
|
||||
uint32_t x = 0;
|
||||
|
||||
while (x < get_header().entry_current) {
|
||||
while (x < get_header().entry_used) {
|
||||
auto& entry = get_entry(x);
|
||||
|
||||
if (entry.section == section && entry.id == id) {
|
||||
|
@ -74,47 +84,54 @@ XdbfBlock XdbfWrapper::get_entry(XdbfSection section, uint64_t id) const {
|
|||
return block;
|
||||
}
|
||||
|
||||
XdbfBlock get_icon(XdbfWrapper& ref) {
|
||||
return ref.get_entry(kSectionImage, 0x8000);
|
||||
XdbfBlock get_icon(const XdbfWrapper &ref) {
|
||||
return ref.get_entry(kSectionImage, kIdTitle);
|
||||
}
|
||||
|
||||
std::string get_title(XdbfWrapper& ref) {
|
||||
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;
|
||||
|
||||
XdbfBlock block = ref.get_entry(kSectionMetadata, 0x58535443);
|
||||
if (block.buffer != nullptr) {
|
||||
XDBF_XSTC* xstc = reinterpret_cast<XDBF_XSTC*>(block.buffer);
|
||||
uint64_t language_id = static_cast<uint64_t>(get_default_language(ref));
|
||||
|
||||
assert_true(xstc->magic == 'XSTC');
|
||||
uint32_t def_language = xstc->default_language;
|
||||
XdbfBlock lang_block = ref.get_entry(kSectionStringTable, language_id);
|
||||
|
||||
XdbfBlock lang_block =
|
||||
ref.get_entry(kSectionStringTable, static_cast<uint64_t>(def_language));
|
||||
if (lang_block.buffer != nullptr) {
|
||||
XDBF_XSTR_HEADER *xstr_head =
|
||||
reinterpret_cast<XDBF_XSTR_HEADER *>(lang_block.buffer);
|
||||
|
||||
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);
|
||||
|
||||
assert_true(xstr_head->magic == 'XSTR');
|
||||
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 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;
|
||||
|
||||
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 == 0x00008000) {
|
||||
title_str.resize(static_cast<size_t>(len));
|
||||
std::copy(currentAddress, currentAddress + len, title_str.begin());
|
||||
}
|
||||
|
||||
currentAddress += len;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#ifndef XENIA_XDBF_XDBF_UTILS_H_
|
||||
#define XENIA_XDBF_XDBF_UTILS_H_
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/memory.h"
|
||||
|
||||
|
@ -27,19 +27,26 @@ enum XdbfSection : uint16_t {
|
|||
kSectionStringTable = 0x0003,
|
||||
};
|
||||
|
||||
// Found by dumping the kSectionStringTable sections of various games:
|
||||
|
||||
enum XdbfLocale : uint32_t {
|
||||
kLocaleDefault = 0,
|
||||
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_max;
|
||||
xe::be<uint32_t> entry_current;
|
||||
xe::be<uint32_t> free_max;
|
||||
xe::be<uint32_t> free_current;
|
||||
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);
|
||||
|
||||
|
@ -112,8 +119,9 @@ class XdbfWrapper {
|
|||
XdbfState state_;
|
||||
};
|
||||
|
||||
XdbfBlock get_icon(XdbfWrapper& ref);
|
||||
std::string get_title(XdbfWrapper& ref);
|
||||
XdbfBlock get_icon(const XdbfWrapper &ref);
|
||||
XdbfLocale get_default_language(const XdbfWrapper &ref);
|
||||
std::string get_title(const XdbfWrapper &ref);
|
||||
|
||||
} // namespace xdbf
|
||||
} // namespace xe
|
||||
|
|
Loading…
Reference in New Issue