From 5fa9499a122423beb85209dfd9043216e73a2a20 Mon Sep 17 00:00:00 2001 From: x1nixmzeng Date: Fri, 8 Jan 2016 22:55:37 +0000 Subject: [PATCH] Community feedback Updated naming convention Exposed the game name to Emulator for other uses Fixed bug with XDBF parsing --- src/xenia/app/emulator_window.cc | 13 +++-- src/xenia/emulator.cc | 14 ++++-- src/xenia/emulator.h | 4 ++ src/xenia/ui/window.h | 2 +- src/xenia/ui/window_win.cc | 6 +-- src/xenia/ui/window_win.h | 2 +- src/xenia/xdbf/xdbf_utils.cc | 83 +++++++++++++++++++------------- src/xenia/xdbf/xdbf_utils.h | 24 ++++++--- 8 files changed, 94 insertions(+), 54 deletions(-) diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index b5e077aaf..123cbe86e 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -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); } diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index a4960c86c..ca36a53bf 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -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(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); } } } diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index 00e93cfff..72a943371 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -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_; diff --git a/src/xenia/ui/window.h b/src/xenia/ui/window.h index e4c757bf0..ee6cd9ac9 100644 --- a/src/xenia/ui/window.h +++ b/src/xenia/ui/window.h @@ -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) {} diff --git a/src/xenia/ui/window_win.cc b/src/xenia/ui/window_win.cc index d3ab0720e..9a03b5a3c 100644 --- a/src/xenia/ui/window_win.cc +++ b/src/xenia/ui/window_win.cc @@ -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_); } diff --git a/src/xenia/ui/window_win.h b/src/xenia/ui/window_win.h index 3cedf931f..36cc38b95 100644 --- a/src/xenia/ui/window_win.h +++ b/src/xenia/ui/window_win.h @@ -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; diff --git a/src/xenia/xdbf/xdbf_utils.cc b/src/xenia/xdbf/xdbf_utils.cc index 11f09985d..100a57926 100644 --- a/src/xenia/xdbf/xdbf_utils.cc +++ b/src/xenia/xdbf/xdbf_utils.cc @@ -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(ptr); - ptr += (sizeof(XBDF_ENTRY) * state.header->entry_max); + ptr += (sizeof(XBDF_ENTRY) * state.header->entry_count); state.files = reinterpret_cast(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(block.buffer); + assert_true(xstc->magic == kMagicXSTC); + + uint32_t default_language = xstc->default_language; + return static_cast(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(block.buffer); + uint64_t language_id = static_cast(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(def_language)); + if (lang_block.buffer != nullptr) { + XDBF_XSTR_HEADER *xstr_head = + reinterpret_cast(lang_block.buffer); - if (lang_block.buffer != nullptr) { - XDBF_XSTR_HEADER* xstr_head = - reinterpret_cast(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(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(currentAddress); - currentAddress += sizeof(XDBF_STRINGTABLE_ENTRY); - uint16_t len = entry->string_length; - - if (entry->id == 0x00008000) { - title_str.resize(static_cast(len)); - std::copy(currentAddress, currentAddress + len, title_str.begin()); - } - - currentAddress += len; + if (entry->id == static_cast(kIdTitle)) { + title_str.resize(static_cast(len)); + std::copy(currentAddress, currentAddress + len, title_str.begin()); } + + ++s; + currentAddress += len; } } diff --git a/src/xenia/xdbf/xdbf_utils.h b/src/xenia/xdbf/xdbf_utils.h index 4317fff32..be6db4541 100644 --- a/src/xenia/xdbf/xdbf_utils.h +++ b/src/xenia/xdbf/xdbf_utils.h @@ -10,8 +10,8 @@ #ifndef XENIA_XDBF_XDBF_UTILS_H_ #define XENIA_XDBF_XDBF_UTILS_H_ -#include #include +#include #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 magic; xe::be version; - xe::be entry_max; - xe::be entry_current; - xe::be free_max; - xe::be free_current; + xe::be entry_count; + xe::be entry_used; + xe::be free_count; + xe::be 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