From 729a1af0d52baac18c3a4249c8a794f6f10be4c8 Mon Sep 17 00:00:00 2001 From: x1nixmzeng Date: Thu, 7 Jan 2016 00:51:27 +0000 Subject: [PATCH 1/7] Added XDBF utility library Utility library to support the XDBF file format for XEX module resources --- premake5.lua | 1 + src/xenia/app/premake5.lua | 1 + src/xenia/xdbf/premake5.lua | 17 +++++ src/xenia/xdbf/xdbf_utils.cc | 141 +++++++++++++++++++++++++++++++++++ src/xenia/xdbf/xdbf_utils.h | 122 ++++++++++++++++++++++++++++++ 5 files changed, 282 insertions(+) create mode 100644 src/xenia/xdbf/premake5.lua create mode 100644 src/xenia/xdbf/xdbf_utils.cc create mode 100644 src/xenia/xdbf/xdbf_utils.h diff --git a/premake5.lua b/premake5.lua index 3431b60da..c559c7361 100644 --- a/premake5.lua +++ b/premake5.lua @@ -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") diff --git a/src/xenia/app/premake5.lua b/src/xenia/app/premake5.lua index d72059e22..245e90d69 100644 --- a/src/xenia/app/premake5.lua +++ b/src/xenia/app/premake5.lua @@ -25,6 +25,7 @@ project("xenia-app") "xenia-ui", "xenia-ui-gl", "xenia-vfs", + "xenia-xdbf", }) flags({ "WinMain", -- Use WinMain instead of main. diff --git a/src/xenia/xdbf/premake5.lua b/src/xenia/xdbf/premake5.lua new file mode 100644 index 000000000..44e9e6a64 --- /dev/null +++ b/src/xenia/xdbf/premake5.lua @@ -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() diff --git a/src/xenia/xdbf/xdbf_utils.cc b/src/xenia/xdbf/xdbf_utils.cc new file mode 100644 index 000000000..993f88b44 --- /dev/null +++ b/src/xenia/xdbf/xdbf_utils.cc @@ -0,0 +1,141 @@ +/** + ****************************************************************************** + * 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 { + + 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(ptr); + ptr += sizeof(XBDF_HEADER); + + state.entries = reinterpret_cast(ptr); + ptr += (sizeof(XBDF_ENTRY) * state.header->entry_max); + + state.files = reinterpret_cast(ptr); + ptr += (sizeof(XBDF_FILE_LOC) * state.header->free_max); + + 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_current ) { + 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; + } + + std::vector get_icon(XdbfWrapper& ref) + { + std::vector icon; + + XdbfBlock block = ref.get_entry(kSectionImage, 0x8000); + + if (block.buffer != nullptr) { + icon.resize(block.size); + std::copy(block.buffer, block.buffer + block.size, icon.data()); + } + + return icon; + } + + std::string get_title(XdbfWrapper& ref) + { + std::string title_str; + + XdbfBlock block = ref.get_entry(kSectionMetadata, 0x58535443); + if (block.buffer != nullptr) { + XDBF_XSTC* xstc = reinterpret_cast(block.buffer); + + assert_true(xstc->magic == 'XSTC'); + uint32_t def_language = xstc->default_language; + + 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); + + 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 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; + } + } + } + + return title_str; + } + + +} // namespace xdbf +} // namespace xe diff --git a/src/xenia/xdbf/xdbf_utils.h b/src/xenia/xdbf/xdbf_utils.h new file mode 100644 index 000000000..f8b80c1af --- /dev/null +++ b/src/xenia/xdbf/xdbf_utils.h @@ -0,0 +1,122 @@ +/** + ****************************************************************************** + * 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 +#include + +#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, + }; + + enum XdbfLocale : uint32_t { + kLocaleDefault = 0, + kLocaleEnglish = 1, + kLocaleJapanese = 2, + }; + + 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; + }; + static_assert_size(XBDF_HEADER, 24); + +#pragma pack(push, 1) + struct XBDF_ENTRY { + xe::be section; + xe::be id; + xe::be offset; + xe::be size; + }; + static_assert_size(XBDF_ENTRY, 18); +#pragma pack(pop) + + struct XBDF_FILE_LOC { + xe::be offset; + xe::be size; + }; + static_assert_size(XBDF_FILE_LOC, 8); + + struct XDBF_XSTC { + xe::be magic; + xe::be version; + xe::be size; + xe::be default_language; + }; + static_assert_size(XDBF_XSTC, 16); + +#pragma pack(push, 1) + struct XDBF_XSTR_HEADER { + xe::be magic; + xe::be version; + xe::be size; + xe::be string_count; + }; + static_assert_size(XDBF_XSTR_HEADER, 14); +#pragma pack(pop) + + struct XDBF_STRINGTABLE_ENTRY { + xe::be id; + xe::be 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_; + }; + + std::vector get_icon(XdbfWrapper& ref); + std::string get_title(XdbfWrapper& ref); + + +} // namespace xdbf +} // namespace xe + +#endif // XENIA_XDBF_XDBF_UTILS_H_ From 321e2d8873ee4d4b10dcc1aadb1ba7e1bbd517b3 Mon Sep 17 00:00:00 2001 From: x1nixmzeng Date: Thu, 7 Jan 2016 00:58:05 +0000 Subject: [PATCH 2/7] Set the window title from the module resource pool --- src/xenia/emulator.cc | 21 ++++++++++++++++++++- src/xenia/kernel/user_module.cc | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index ccfec481e..b96078a75 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -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)."); @@ -498,7 +499,7 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring& path, // Allow xam to request module loads. auto xam = kernel_state()->GetKernelModule("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()); @@ -513,6 +514,24 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring& path, if (!main_xthread) { return X_STATUS_UNSUCCESSFUL; } + + // 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(resource_size))) { + // TODO(x1nixmzeng): Set the application icon + std::wstring title(xe::to_wstring(xe::xdbf::get_title(db))); + display_window_->set_title(title); + } + } + } main_thread_ = main_xthread->thread(); WaitUntilExit(); diff --git a/src/xenia/kernel/user_module.cc b/src/xenia/kernel/user_module.cc index 07894984d..955acac46 100644 --- a/src/xenia/kernel/user_module.cc +++ b/src/xenia/kernel/user_module.cc @@ -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) { From 92c8409b0a4a6506c336383baef6f3d738b164e8 Mon Sep 17 00:00:00 2001 From: x1nixmzeng Date: Thu, 7 Jan 2016 01:32:56 +0000 Subject: [PATCH 3/7] Set the window icon from the module resource pool Makes use of the stb_image library Conversion to HICON seems to have broken somewhere --- src/xenia/emulator.cc | 6 ++++- src/xenia/ui/window.h | 2 ++ src/xenia/ui/window_win.cc | 48 ++++++++++++++++++++++++++++++++++++ src/xenia/ui/window_win.h | 2 ++ src/xenia/xdbf/xdbf_utils.cc | 13 ++-------- src/xenia/xdbf/xdbf_utils.h | 2 +- 6 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index b96078a75..da8bfd71b 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -526,9 +526,13 @@ 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))) { - // TODO(x1nixmzeng): Set the application icon std::wstring title(xe::to_wstring(xe::xdbf::get_title(db))); display_window_->set_title(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); + } } } } diff --git a/src/xenia/ui/window.h b/src/xenia/ui/window.h index cd68db447..e4c757bf0 100644 --- a/src/xenia/ui/window.h +++ b/src/xenia/ui/window.h @@ -53,6 +53,8 @@ class Window { return true; } + virtual bool set_icon_from_buffer(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 cefaf908d..52cdea006 100644 --- a/src/xenia/ui/window_win.cc +++ b/src/xenia/ui/window_win.cc @@ -14,6 +14,11 @@ #include "xenia/base/platform_win.h" #include "xenia/ui/window_win.h" +#include "third_party/stb/stb_image.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "third_party/stb/stb_image.h" + namespace xe { namespace ui { @@ -163,6 +168,46 @@ bool Win32Window::set_title(const std::wstring& title) { return true; } +bool Win32Window::set_icon_from_buffer(void *buffer, size_t size) { + + if (icon_ != nullptr) { + DestroyIcon(icon_); + } + + // Convert the buffer for Windows + int width, height, bpp; + unsigned char *data = stbi_load_from_memory(reinterpret_cast(buffer), static_cast(size), &width, &height, &bpp, 0); + + int iSize = sizeof(BITMAPINFOHEADER); + int iSizeImage = width * height * bpp; + + std::vector bitmap(iSize + iSizeImage); + BITMAPINFOHEADER *pBitmap(reinterpret_cast(bitmap.data())); + + pBitmap->biSize = sizeof(BITMAPINFOHEADER); + pBitmap->biWidth = width; + pBitmap->biHeight = height; + pBitmap->biPlanes = 1; + pBitmap->biBitCount = bpp * 8; + pBitmap->biCompression = BI_RGB; + pBitmap->biSizeImage = iSizeImage; + + unsigned char *pImage = bitmap.data() + iSize; + + std::copy(data, data + iSizeImage, pImage); + + HICON icon = CreateIconFromResourceEx((BYTE *)pBitmap, iSize + iSizeImage, TRUE, 0x00030000, width, height, LR_DEFAULTCOLOR); + + if (icon != nullptr) { + SendMessage(hwnd_, WM_SETICON, ICON_BIG, reinterpret_cast(icon)); + SendMessage(hwnd_, WM_SETICON, ICON_SMALL, reinterpret_cast(icon)); + + icon_ = icon; + } + + return false; +} + bool Win32Window::is_fullscreen() const { return fullscreen_; } void Win32Window::ToggleFullscreen(bool fullscreen) { @@ -294,6 +339,9 @@ void Win32Window::Close() { Close(); OnClose(); DestroyWindow(hwnd_); + if (icon_ != nullptr) { + DestroyIcon(icon_); + } } void Win32Window::OnMainMenuChange() { diff --git a/src/xenia/ui/window_win.h b/src/xenia/ui/window_win.h index 0f34f6e28..3cedf931f 100644 --- a/src/xenia/ui/window_win.h +++ b/src/xenia/ui/window_win.h @@ -31,6 +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 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; diff --git a/src/xenia/xdbf/xdbf_utils.cc b/src/xenia/xdbf/xdbf_utils.cc index 993f88b44..ac08b800a 100644 --- a/src/xenia/xdbf/xdbf_utils.cc +++ b/src/xenia/xdbf/xdbf_utils.cc @@ -81,18 +81,9 @@ namespace xdbf { return block; } - std::vector get_icon(XdbfWrapper& ref) + XdbfBlock get_icon(XdbfWrapper& ref) { - std::vector icon; - - XdbfBlock block = ref.get_entry(kSectionImage, 0x8000); - - if (block.buffer != nullptr) { - icon.resize(block.size); - std::copy(block.buffer, block.buffer + block.size, icon.data()); - } - - return icon; + return ref.get_entry(kSectionImage, 0x8000); } std::string get_title(XdbfWrapper& ref) diff --git a/src/xenia/xdbf/xdbf_utils.h b/src/xenia/xdbf/xdbf_utils.h index f8b80c1af..8299c420f 100644 --- a/src/xenia/xdbf/xdbf_utils.h +++ b/src/xenia/xdbf/xdbf_utils.h @@ -112,7 +112,7 @@ namespace xdbf { XdbfState state_; }; - std::vector get_icon(XdbfWrapper& ref); + XdbfBlock get_icon(XdbfWrapper& ref); std::string get_title(XdbfWrapper& ref); From effe241a11c48ae8aba339afaee532786005c17c Mon Sep 17 00:00:00 2001 From: x1nixmzeng Date: Thu, 7 Jan 2016 20:38:27 +0000 Subject: [PATCH 4/7] Removed stb_image dependency Windows actually supports PNG resources --- src/xenia/ui/window_win.cc | 40 +++++++------------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) diff --git a/src/xenia/ui/window_win.cc b/src/xenia/ui/window_win.cc index 52cdea006..f369cedd4 100644 --- a/src/xenia/ui/window_win.cc +++ b/src/xenia/ui/window_win.cc @@ -14,11 +14,6 @@ #include "xenia/base/platform_win.h" #include "xenia/ui/window_win.h" -#include "third_party/stb/stb_image.h" - -#define STB_IMAGE_IMPLEMENTATION -#include "third_party/stb/stb_image.h" - namespace xe { namespace ui { @@ -36,6 +31,10 @@ Win32Window::~Win32Window() { CloseWindow(hwnd_); hwnd_ = nullptr; } + if (icon_ != nullptr) { + DestroyIcon(icon_); + icon_ = nullptr; + } } NativePlatformHandle Win32Window::native_platform_handle() const { @@ -174,35 +173,13 @@ bool Win32Window::set_icon_from_buffer(void *buffer, size_t size) { DestroyIcon(icon_); } - // Convert the buffer for Windows - int width, height, bpp; - unsigned char *data = stbi_load_from_memory(reinterpret_cast(buffer), static_cast(size), &width, &height, &bpp, 0); - - int iSize = sizeof(BITMAPINFOHEADER); - int iSizeImage = width * height * bpp; - - std::vector bitmap(iSize + iSizeImage); - BITMAPINFOHEADER *pBitmap(reinterpret_cast(bitmap.data())); - - pBitmap->biSize = sizeof(BITMAPINFOHEADER); - pBitmap->biWidth = width; - pBitmap->biHeight = height; - pBitmap->biPlanes = 1; - pBitmap->biBitCount = bpp * 8; - pBitmap->biCompression = BI_RGB; - pBitmap->biSizeImage = iSizeImage; - - unsigned char *pImage = bitmap.data() + iSize; - - std::copy(data, data + iSizeImage, pImage); - - HICON icon = CreateIconFromResourceEx((BYTE *)pBitmap, iSize + iSizeImage, TRUE, 0x00030000, width, height, LR_DEFAULTCOLOR); + HICON icon = CreateIconFromResourceEx(reinterpret_cast(buffer), static_cast(size), TRUE, 0x00030000, 0, 0, LR_DEFAULTCOLOR); if (icon != nullptr) { + icon_ = icon; + SendMessage(hwnd_, WM_SETICON, ICON_BIG, reinterpret_cast(icon)); SendMessage(hwnd_, WM_SETICON, ICON_SMALL, reinterpret_cast(icon)); - - icon_ = icon; } return false; @@ -339,9 +316,6 @@ void Win32Window::Close() { Close(); OnClose(); DestroyWindow(hwnd_); - if (icon_ != nullptr) { - DestroyIcon(icon_); - } } void Win32Window::OnMainMenuChange() { From e4cef38d95f72221b6437ef02e2db522f8c5720d Mon Sep 17 00:00:00 2001 From: x1nixmzeng Date: Thu, 7 Jan 2016 20:51:28 +0000 Subject: [PATCH 5/7] Formatting of changes As per the style guide --- src/xenia/emulator.cc | 43 ++++----- src/xenia/ui/window_win.cc | 27 +++--- src/xenia/xdbf/xdbf_utils.cc | 163 +++++++++++++++++------------------ src/xenia/xdbf/xdbf_utils.h | 157 +++++++++++++++++---------------- 4 files changed, 192 insertions(+), 198 deletions(-) diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index da8bfd71b..a4960c86c 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -514,28 +514,29 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring& path, if (!main_xthread) { return X_STATUS_UNSUCCESSFUL; } - - // 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(resource_size))) { - std::wstring title(xe::to_wstring(xe::xdbf::get_title(db))); - display_window_->set_title(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); - } - } - } - } + // 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(resource_size))) { + std::wstring title(xe::to_wstring(xe::xdbf::get_title(db))); + display_window_->set_title(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); + } + } + } + } main_thread_ = main_xthread->thread(); WaitUntilExit(); diff --git a/src/xenia/ui/window_win.cc b/src/xenia/ui/window_win.cc index f369cedd4..d3ab0720e 100644 --- a/src/xenia/ui/window_win.cc +++ b/src/xenia/ui/window_win.cc @@ -167,22 +167,23 @@ bool Win32Window::set_title(const std::wstring& title) { return true; } -bool Win32Window::set_icon_from_buffer(void *buffer, size_t size) { +bool Win32Window::set_icon_from_buffer(void* buffer, size_t size) { + if (icon_ != nullptr) { + DestroyIcon(icon_); + } - if (icon_ != nullptr) { - DestroyIcon(icon_); - } - - HICON icon = CreateIconFromResourceEx(reinterpret_cast(buffer), static_cast(size), TRUE, 0x00030000, 0, 0, LR_DEFAULTCOLOR); + HICON icon = CreateIconFromResourceEx(reinterpret_cast(buffer), + static_cast(size), TRUE, + 0x00030000, 0, 0, LR_DEFAULTCOLOR); - if (icon != nullptr) { - icon_ = icon; + if (icon != nullptr) { + icon_ = icon; - SendMessage(hwnd_, WM_SETICON, ICON_BIG, reinterpret_cast(icon)); - SendMessage(hwnd_, WM_SETICON, ICON_SMALL, reinterpret_cast(icon)); - } - - return false; + SendMessage(hwnd_, WM_SETICON, ICON_BIG, reinterpret_cast(icon)); + SendMessage(hwnd_, WM_SETICON, ICON_SMALL, reinterpret_cast(icon)); + } + + return false; } bool Win32Window::is_fullscreen() const { return fullscreen_; } diff --git a/src/xenia/xdbf/xdbf_utils.cc b/src/xenia/xdbf/xdbf_utils.cc index ac08b800a..11f09985d 100644 --- a/src/xenia/xdbf/xdbf_utils.cc +++ b/src/xenia/xdbf/xdbf_utils.cc @@ -12,121 +12,114 @@ namespace xe { namespace xdbf { - XdbfWrapper::XdbfWrapper() = default; +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_HEADER& XdbfWrapper::get_header() const { return *state_.header; } - XBDF_FILE_LOC& XdbfWrapper::get_file(uint32_t n) const - { - return state_.files[n]; - } +XBDF_ENTRY& XdbfWrapper::get_entry(uint32_t n) const { + return state_.entries[n]; +} - bool XdbfWrapper::initialize(uint8_t* buffer, size_t length) - { - if (length <= sizeof(XBDF_HEADER)) { - return false; - } +XBDF_FILE_LOC& XdbfWrapper::get_file(uint32_t n) const { + return state_.files[n]; +} - XdbfState state; +bool XdbfWrapper::initialize(uint8_t* buffer, size_t length) { + if (length <= sizeof(XBDF_HEADER)) { + return false; + } - state.data = buffer; - state.size = length; + XdbfState state; - uint8_t* ptr = state.data; + state.data = buffer; + state.size = length; - state.header = reinterpret_cast(ptr); - ptr += sizeof(XBDF_HEADER); + uint8_t* ptr = state.data; - state.entries = reinterpret_cast(ptr); - ptr += (sizeof(XBDF_ENTRY) * state.header->entry_max); + state.header = reinterpret_cast(ptr); + ptr += sizeof(XBDF_HEADER); - state.files = reinterpret_cast(ptr); - ptr += (sizeof(XBDF_FILE_LOC) * state.header->free_max); + state.entries = reinterpret_cast(ptr); + ptr += (sizeof(XBDF_ENTRY) * state.header->entry_max); - state.offset = ptr; + state.files = reinterpret_cast(ptr); + ptr += (sizeof(XBDF_FILE_LOC) * state.header->free_max); - if (state.header->magic == 'XDBF') { - state_ = state; - return true; - } + state.offset = ptr; - return false; - } + if (state.header->magic == 'XDBF') { + state_ = state; + return true; + } - XdbfBlock XdbfWrapper::get_entry(XdbfSection section, uint64_t id) const { - - XdbfBlock block = { nullptr,0 }; - uint32_t x = 0; + return false; +} - while( x < get_header().entry_current ) { - auto &entry = get_entry(x); +XdbfBlock XdbfWrapper::get_entry(XdbfSection section, uint64_t id) const { + XdbfBlock block = {nullptr, 0}; + uint32_t x = 0; - if (entry.section == section && entry.id == id) { - block.buffer = state_.offset + entry.offset; - block.size = entry.size; - break; - } + while (x < get_header().entry_current) { + auto& entry = get_entry(x); - ++x; - } + if (entry.section == section && entry.id == id) { + block.buffer = state_.offset + entry.offset; + block.size = entry.size; + break; + } - return block; - } + ++x; + } - XdbfBlock get_icon(XdbfWrapper& ref) - { - return ref.get_entry(kSectionImage, 0x8000); - } + return block; +} - std::string get_title(XdbfWrapper& ref) - { - std::string title_str; +XdbfBlock get_icon(XdbfWrapper& ref) { + return ref.get_entry(kSectionImage, 0x8000); +} - XdbfBlock block = ref.get_entry(kSectionMetadata, 0x58535443); - if (block.buffer != nullptr) { - XDBF_XSTC* xstc = reinterpret_cast(block.buffer); +std::string get_title(XdbfWrapper& ref) { + std::string title_str; - assert_true(xstc->magic == 'XSTC'); - uint32_t def_language = xstc->default_language; + XdbfBlock block = ref.get_entry(kSectionMetadata, 0x58535443); + if (block.buffer != nullptr) { + XDBF_XSTC* xstc = reinterpret_cast(block.buffer); - XdbfBlock lang_block = ref.get_entry(kSectionStringTable, static_cast(def_language)); + assert_true(xstc->magic == 'XSTC'); + uint32_t def_language = xstc->default_language; - if (lang_block.buffer != nullptr) { - XDBF_XSTR_HEADER* xstr_head = reinterpret_cast(lang_block.buffer); + XdbfBlock lang_block = + ref.get_entry(kSectionStringTable, static_cast(def_language)); - assert_true(xstr_head->magic == 'XSTR'); - assert_true(xstr_head->version == 1); + if (lang_block.buffer != nullptr) { + XDBF_XSTR_HEADER* xstr_head = + reinterpret_cast(lang_block.buffer); - uint16_t str_count = xstr_head->string_count; - uint8_t *currentAddress = lang_block.buffer + sizeof(XDBF_XSTR_HEADER); + assert_true(xstr_head->magic == 'XSTR'); + assert_true(xstr_head->version == 1); - 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 str_count = xstr_head->string_count; + uint8_t* currentAddress = lang_block.buffer + sizeof(XDBF_XSTR_HEADER); - if (entry->id == 0x00008000) { - title_str.resize(static_cast(len)); - std::copy(currentAddress, currentAddress + len, title_str.begin()); - } + 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; - currentAddress += len; - } - } - } + if (entry->id == 0x00008000) { + title_str.resize(static_cast(len)); + std::copy(currentAddress, currentAddress + len, title_str.begin()); + } - return title_str; - } + currentAddress += len; + } + } + } + return title_str; +} } // namespace xdbf } // namespace xe diff --git a/src/xenia/xdbf/xdbf_utils.h b/src/xenia/xdbf/xdbf_utils.h index 8299c420f..4317fff32 100644 --- a/src/xenia/xdbf/xdbf_utils.h +++ b/src/xenia/xdbf/xdbf_utils.h @@ -17,104 +17,103 @@ 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, - }; +// http://freestyledash.googlecode.com/svn/trunk/Freestyle/Tools/XEX/SPA.h +// http://freestyledash.googlecode.com/svn/trunk/Freestyle/Tools/XEX/SPA.cpp - enum XdbfLocale : uint32_t { - kLocaleDefault = 0, - kLocaleEnglish = 1, - kLocaleJapanese = 2, - }; +enum XdbfSection : uint16_t { + kSectionMetadata = 0x0001, + kSectionImage = 0x0002, + kSectionStringTable = 0x0003, +}; - 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; - }; - static_assert_size(XBDF_HEADER, 24); +enum XdbfLocale : uint32_t { + kLocaleDefault = 0, + kLocaleEnglish = 1, + kLocaleJapanese = 2, +}; + +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; +}; +static_assert_size(XBDF_HEADER, 24); #pragma pack(push, 1) - struct XBDF_ENTRY { - xe::be section; - xe::be id; - xe::be offset; - xe::be size; - }; - static_assert_size(XBDF_ENTRY, 18); +struct XBDF_ENTRY { + xe::be section; + xe::be id; + xe::be offset; + xe::be size; +}; +static_assert_size(XBDF_ENTRY, 18); #pragma pack(pop) - struct XBDF_FILE_LOC { - xe::be offset; - xe::be size; - }; - static_assert_size(XBDF_FILE_LOC, 8); +struct XBDF_FILE_LOC { + xe::be offset; + xe::be size; +}; +static_assert_size(XBDF_FILE_LOC, 8); - struct XDBF_XSTC { - xe::be magic; - xe::be version; - xe::be size; - xe::be default_language; - }; - static_assert_size(XDBF_XSTC, 16); +struct XDBF_XSTC { + xe::be magic; + xe::be version; + xe::be size; + xe::be default_language; +}; +static_assert_size(XDBF_XSTC, 16); #pragma pack(push, 1) - struct XDBF_XSTR_HEADER { - xe::be magic; - xe::be version; - xe::be size; - xe::be string_count; - }; - static_assert_size(XDBF_XSTR_HEADER, 14); +struct XDBF_XSTR_HEADER { + xe::be magic; + xe::be version; + xe::be size; + xe::be string_count; +}; +static_assert_size(XDBF_XSTR_HEADER, 14); #pragma pack(pop) - struct XDBF_STRINGTABLE_ENTRY { - xe::be id; - xe::be string_length; - }; - static_assert_size(XDBF_STRINGTABLE_ENTRY, 4); +struct XDBF_STRINGTABLE_ENTRY { + xe::be id; + xe::be 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 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; - }; +struct XdbfBlock { + uint8_t* buffer; + size_t size; +}; - class XdbfWrapper { - public: - XdbfWrapper(); +class XdbfWrapper { + public: + XdbfWrapper(); - bool initialize(uint8_t* buffer, size_t length); - XdbfBlock get_entry(XdbfSection section, uint64_t id) const; + 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; + 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(XdbfWrapper& ref); - std::string get_title(XdbfWrapper& ref); + XdbfState state_; +}; +XdbfBlock get_icon(XdbfWrapper& ref); +std::string get_title(XdbfWrapper& ref); } // namespace xdbf } // namespace xe From 5fa9499a122423beb85209dfd9043216e73a2a20 Mon Sep 17 00:00:00 2001 From: x1nixmzeng Date: Fri, 8 Jan 2016 22:55:37 +0000 Subject: [PATCH 6/7] 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 From 6530b9dc4915d220fca471b1662ee58a11aa3a71 Mon Sep 17 00:00:00 2001 From: x1nixmzeng Date: Sat, 9 Jan 2016 17:36:46 +0000 Subject: [PATCH 7/7] Added callback when new module is launched This avoids having to guess the display window title format Also manually fixed the linting errors picked out by travis which do not get picked up using xb format locally --- src/xenia/app/emulator_window.h | 3 +- src/xenia/app/xenia_main.cc | 3 +- src/xenia/cpu/xex_module.cc | 4 +-- src/xenia/emulator.cc | 49 +++++++++++++++---------------- src/xenia/emulator.h | 16 +++++----- src/xenia/kernel/kernel_module.cc | 4 +-- src/xenia/ui/window.h | 2 +- src/xenia/ui/window_win.cc | 2 +- src/xenia/ui/window_win.h | 2 +- src/xenia/xdbf/xdbf_utils.cc | 18 ++++++------ src/xenia/xdbf/xdbf_utils.h | 6 ++-- 11 files changed, 56 insertions(+), 53 deletions(-) diff --git a/src/xenia/app/emulator_window.h b/src/xenia/app/emulator_window.h index 54ddae867..fa8db88b3 100644 --- a/src/xenia/app/emulator_window.h +++ b/src/xenia/app/emulator_window.h @@ -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(); diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index 865d28cf3..ab4934666 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -195,7 +195,8 @@ int xenia_main(const std::vector& 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(); diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index 860b204b5..da3d79773 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -465,8 +465,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; diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index ca36a53bf..f2182f62f 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -192,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 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); @@ -202,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 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: @@ -245,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 on_launch) { auto mount_path = "\\Device\\Cdrom0"; // Register the disc image in the virtual filesystem. @@ -267,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 on_launch) { auto mount_path = "\\Device\\Cdrom0"; // Register the container in the virtual filesystem. @@ -289,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() { @@ -494,8 +498,9 @@ 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 on_launch) { // Allow xam to request module loads. auto xam = kernel_state()->GetKernelModule("xam.xex"); @@ -510,10 +515,6 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring& path, } kernel_state_->SetExecutableModule(module); - auto main_xthread = module->Launch(); - if (!main_xthread) { - return X_STATUS_UNSUCCESSFUL; - } // Try and load the resource database (xex only) char title[9] = {0}; @@ -526,15 +527,7 @@ 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::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()); - } + 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, @@ -544,6 +537,12 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring& path, } } + auto main_xthread = module->Launch(); + if (!main_xthread) { + return X_STATUS_UNSUCCESSFUL; + } + + on_launch(); main_thread_ = main_xthread->thread(); WaitUntilExit(); diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index 72a943371..23a71561f 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -54,7 +54,7 @@ class Emulator { 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_; } + const std::wstring& game_title() const { return game_title_; } // Window used for displaying graphical output. ui::Window* display_window() const { return display_window_; } @@ -109,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 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 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 on_launch); // Launches a game from an STFS container file. - X_STATUS LaunchStfsContainer(std::wstring path); + X_STATUS LaunchStfsContainer(std::wstring path, + std::function on_launch); void Pause(); void Resume(); @@ -134,8 +135,9 @@ 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 on_launch); std::wstring command_line_; std::wstring game_title_; diff --git a/src/xenia/kernel/kernel_module.cc b/src/xenia/kernel/kernel_module.cc index befa08d33..fdad0256f 100644 --- a/src/xenia/kernel/kernel_module.cc +++ b/src/xenia/kernel/kernel_module.cc @@ -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; diff --git a/src/xenia/ui/window.h b/src/xenia/ui/window.h index ee6cd9ac9..d894d9276 100644 --- a/src/xenia/ui/window.h +++ b/src/xenia/ui/window.h @@ -53,7 +53,7 @@ class Window { return true; } - virtual bool SetIconFromBuffer(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 9a03b5a3c..d3c6224b7 100644 --- a/src/xenia/ui/window_win.cc +++ b/src/xenia/ui/window_win.cc @@ -167,7 +167,7 @@ bool Win32Window::set_title(const std::wstring& title) { return true; } -bool Win32Window::SetIconFromBuffer(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 36cc38b95..296baba45 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 SetIconFromBuffer(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 100a57926..e60a88d80 100644 --- a/src/xenia/xdbf/xdbf_utils.cc +++ b/src/xenia/xdbf/xdbf_utils.cc @@ -84,14 +84,14 @@ XdbfBlock XdbfWrapper::get_entry(XdbfSection section, uint64_t id) const { return block; } -XdbfBlock get_icon(const XdbfWrapper &ref) { +XdbfBlock get_icon(const XdbfWrapper& ref) { return ref.get_entry(kSectionImage, kIdTitle); } -XdbfLocale get_default_language(const 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); + XDBF_XSTC* xstc = reinterpret_cast(block.buffer); assert_true(xstc->magic == kMagicXSTC); uint32_t default_language = xstc->default_language; @@ -101,7 +101,7 @@ XdbfLocale get_default_language(const XdbfWrapper &ref) { return kLocaleEnglish; } -std::string get_title(const XdbfWrapper &ref) { +std::string get_title(const XdbfWrapper& ref) { std::string title_str; uint64_t language_id = static_cast(get_default_language(ref)); @@ -109,19 +109,19 @@ std::string get_title(const XdbfWrapper &ref) { XdbfBlock lang_block = ref.get_entry(kSectionStringTable, language_id); if (lang_block.buffer != nullptr) { - XDBF_XSTR_HEADER *xstr_head = - reinterpret_cast(lang_block.buffer); + XDBF_XSTR_HEADER* xstr_head = + reinterpret_cast(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); + 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); + XDBF_STRINGTABLE_ENTRY* entry = + reinterpret_cast(currentAddress); currentAddress += sizeof(XDBF_STRINGTABLE_ENTRY); uint16_t len = entry->string_length; diff --git a/src/xenia/xdbf/xdbf_utils.h b/src/xenia/xdbf/xdbf_utils.h index be6db4541..4431eeda1 100644 --- a/src/xenia/xdbf/xdbf_utils.h +++ b/src/xenia/xdbf/xdbf_utils.h @@ -119,9 +119,9 @@ class XdbfWrapper { XdbfState state_; }; -XdbfBlock get_icon(const XdbfWrapper &ref); -XdbfLocale get_default_language(const XdbfWrapper &ref); -std::string get_title(const 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