From 6cc3440086fc141f9e2f23b8822ed7a5b1f15db8 Mon Sep 17 00:00:00 2001 From: x1nixmzeng Date: Sat, 23 Jul 2016 18:27:13 +0100 Subject: [PATCH 1/2] Find correct launch module for XNA-based games Added utility to parse game information and updated launch logic to use it --- src/xenia/emulator.cc | 40 +++++++++++- src/xenia/emulator.h | 2 + src/xenia/kernel/user_module.cc | 10 ++- src/xenia/kernel/util/gameinfo_utils.cc | 82 +++++++++++++++++++++++++ src/xenia/kernel/util/gameinfo_utils.h | 81 ++++++++++++++++++++++++ 5 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 src/xenia/kernel/util/gameinfo_utils.cc create mode 100644 src/xenia/kernel/util/gameinfo_utils.h diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 1923b74fe..6d3a9bd69 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -27,6 +27,7 @@ #include "xenia/hid/input_system.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/user_module.h" +#include "xenia/kernel/util/gameinfo_utils.h" #include "xenia/kernel/util/xdbf_utils.h" #include "xenia/kernel/xam/xam_module.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_module.h" @@ -252,7 +253,8 @@ X_STATUS Emulator::LaunchDiscImage(std::wstring path) { file_system_->RegisterSymbolicLink("d:", mount_path); // Launch the game. - return CompleteLaunch(path, "game:\\default.xex"); + auto module_path(FindLaunchModule()); + return CompleteLaunch(path, module_path); } X_STATUS Emulator::LaunchStfsContainer(std::wstring path) { @@ -274,7 +276,8 @@ X_STATUS Emulator::LaunchStfsContainer(std::wstring path) { file_system_->RegisterSymbolicLink("d:", mount_path); // Launch the game. - return CompleteLaunch(path, "game:\\default.xex"); + auto module_path(FindLaunchModule()); + return CompleteLaunch(path, module_path); } void Emulator::Pause() { @@ -501,6 +504,39 @@ void Emulator::WaitUntilExit() { on_exit(); } +std::string Emulator::FindLaunchModule() { + std::string path("game:\\"); + std::string default_module("default.xex"); + + auto gameinfo_entry(file_system_->ResolvePath(path + "GameInfo.bin")); + if (gameinfo_entry) { + vfs::File *file = nullptr; + X_STATUS result = + gameinfo_entry->Open(vfs::FileAccess::kGenericRead, &file); + if (XSUCCEEDED(result)) { + std::vector buffer(gameinfo_entry->size()); + size_t bytes_read = 0; + result = file->ReadSync(buffer.data(), buffer.size(), 0, &bytes_read); + if (XSUCCEEDED(result)) { + kernel::util::GameInfo info(buffer); + if (info.is_valid()) { + XELOGI("Found virtual title %s", info.virtual_title_id().c_str()); + + const std::string xna_id("584E07D1"); + auto xna_id_entry(file_system_->ResolvePath(path + xna_id)); + if (xna_id_entry) { + default_module = xna_id + "\\" + info.module_name(); + } else { + XELOGE("Could not find fixed XNA path %s", xna_id.c_str()); + } + } + } + } + } + + return path + default_module; +} + X_STATUS Emulator::CompleteLaunch(const std::wstring& path, const std::string& module_path) { // Allow xam to request module loads. diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index ca8f17aaa..8cae3eb5e 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -139,6 +139,8 @@ class Emulator { static bool ExceptionCallbackThunk(Exception* ex, void* data); bool ExceptionCallback(Exception* ex); + std::string FindLaunchModule(); + X_STATUS CompleteLaunch(const std::wstring& path, const std::string& module_path); diff --git a/src/xenia/kernel/user_module.cc b/src/xenia/kernel/user_module.cc index 35aa956fe..6674aeceb 100644 --- a/src/xenia/kernel/user_module.cc +++ b/src/xenia/kernel/user_module.cc @@ -107,8 +107,14 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) { } else if (magic == 0x7F454C46 /* 0x7F 'ELF' */) { module_format_ = kModuleFormatElf; } else { - XELOGE("Unknown module magic: %.8X", magic); - return X_STATUS_NOT_IMPLEMENTED; + auto magic16 = xe::load_and_swap(addr); + if (magic16 == 'MZ') { + XELOGE("XNA executables are not yet implemented"); + return X_STATUS_NOT_IMPLEMENTED; + } else { + XELOGE("Unknown module magic: %.8X", magic); + return X_STATUS_NOT_IMPLEMENTED; + } } if (module_format_ == kModuleFormatXex) { diff --git a/src/xenia/kernel/util/gameinfo_utils.cc b/src/xenia/kernel/util/gameinfo_utils.cc new file mode 100644 index 000000000..089606325 --- /dev/null +++ b/src/xenia/kernel/util/gameinfo_utils.cc @@ -0,0 +1,82 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/kernel/util/gameinfo_utils.h" + +namespace xe { +namespace kernel { +namespace util { + +constexpr uint32_t kGameInfoExecMagic = 'EXEC'; +constexpr uint32_t kGameInfoCommMagic = 'COMM'; +constexpr uint32_t kGameInfoTitlMagic = 'TITL'; + +GameInfoWrapper::GameInfoWrapper(const uint8_t *data, size_t data_size) + : data_(data), data_size_(data_size) { + if (!data) { + return; + } + + const GameInfoBlockHeader *block_header(nullptr); + size_t data_offset(0); + while (data_offset < data_size_) { + block_header = + reinterpret_cast(data_ + data_offset); + data_offset += sizeof(GameInfoBlockHeader); + + switch (block_header->magic) { + case kGameInfoExecMagic: + exec_.virtual_titleid = + reinterpret_cast(data_ + data_offset); + data_offset += exec_.VirtualTitleIdLength + 1; + exec_.module_name = reinterpret_cast(data_ + data_offset); + data_offset += exec_.ModuleNameLength + 1; + exec_.build_description = + reinterpret_cast(data_ + data_offset); + data_offset += exec_.BuildDescriptionLength + 1; + break; + case kGameInfoCommMagic: + assert_true(block_header->block_size == sizeof(GameInfoBlockComm)); + comm_ = reinterpret_cast(data_ + data_offset); + data_offset += block_header->block_size; + break; + case kGameInfoTitlMagic: + assert_true(block_header->block_size == sizeof(GameInfoBlockTitl)); + titl_ = reinterpret_cast(data_ + data_offset); + data_offset += block_header->block_size; + break; + default: + // Unsupported headers + data_ = nullptr; + return; + } + } + + if ((comm_ == nullptr) || (titl_ == nullptr) || + (exec_.virtual_titleid == nullptr)) { + data_ = nullptr; + } +} + +uint32_t GameInfo::title_id() const { return comm_->title_id; } + +std::string GameInfo::virtual_title_id() const { + size_t virtual_titleid_length(std::strlen(exec_.virtual_titleid)); + return std::string(exec_.virtual_titleid, + exec_.virtual_titleid + virtual_titleid_length); +} + +std::string GameInfo::module_name() const { + size_t module_name_length(std::strlen(exec_.module_name)); + return std::string(exec_.module_name, exec_.module_name + module_name_length); +} + +} // namespace util +} // namespace kernel +} // namespace xe diff --git a/src/xenia/kernel/util/gameinfo_utils.h b/src/xenia/kernel/util/gameinfo_utils.h new file mode 100644 index 000000000..137567f2e --- /dev/null +++ b/src/xenia/kernel/util/gameinfo_utils.h @@ -0,0 +1,81 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_UTIL_GAMEINFO_UTILS_H_ +#define XENIA_KERNEL_UTIL_GAMEINFO_UTILS_H_ + +#include +#include + +#include "xenia/base/memory.h" + +namespace xe { +namespace kernel { +namespace util { + +class GameInfoWrapper { +public: + GameInfoWrapper(const uint8_t *data, size_t data_size); + + bool is_valid() const { return data_ != nullptr; } + +protected: + struct GameInfoBlockHeader { + xe::be magic; + xe::be block_size; + }; + static_assert_size(GameInfoBlockHeader, 8); + + struct GameInfoBlockExec { + const char *virtual_titleid; + const char *module_name; + const char *build_description; + + const uint32_t VirtualTitleIdLength = 32; + const uint32_t ModuleNameLength = 42; + const uint32_t BuildDescriptionLength = 64; + }; + + struct GameInfoBlockComm { + xe::be title_id; + }; + static_assert_size(GameInfoBlockComm, 4); + + struct GameInfoBlockTitl { + xe::be title[128]; + xe::be description[256]; + xe::be publisher[256]; // assumed field name from wxPirs + }; + +private: + const uint8_t *data_ = nullptr; + size_t data_size_ = 0; + +protected: + GameInfoBlockExec exec_; + const GameInfoBlockComm *comm_ = nullptr; + const GameInfoBlockTitl *titl_ = nullptr; +}; + +class GameInfo : public GameInfoWrapper { +public: + GameInfo(const std::vector &data) + : GameInfoWrapper(reinterpret_cast(data.data()), + data.size()) {} + + uint32_t title_id() const; + std::string virtual_title_id() const; + std::string module_name() const; +}; + +} // namespace util +} // namespace kernel +} // namespace xe + +#endif // XENIA_KERNEL_UTIL_GAMEINFO_UTILS_H_ From 5be1a24f7a32531c69381fd767331bf3daeed9b5 Mon Sep 17 00:00:00 2001 From: x1nixmzeng Date: Sat, 30 Jul 2016 15:00:51 +0100 Subject: [PATCH 2/2] Clang formatting --- src/xenia/emulator.cc | 2 +- src/xenia/kernel/util/gameinfo_utils.cc | 60 ++++++++++++------------- src/xenia/kernel/util/gameinfo_utils.h | 38 ++++++++-------- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 6d3a9bd69..2c5969de4 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -510,7 +510,7 @@ std::string Emulator::FindLaunchModule() { auto gameinfo_entry(file_system_->ResolvePath(path + "GameInfo.bin")); if (gameinfo_entry) { - vfs::File *file = nullptr; + vfs::File* file = nullptr; X_STATUS result = gameinfo_entry->Open(vfs::FileAccess::kGenericRead, &file); if (XSUCCEEDED(result)) { diff --git a/src/xenia/kernel/util/gameinfo_utils.cc b/src/xenia/kernel/util/gameinfo_utils.cc index 089606325..aabd4f0d0 100644 --- a/src/xenia/kernel/util/gameinfo_utils.cc +++ b/src/xenia/kernel/util/gameinfo_utils.cc @@ -17,44 +17,44 @@ constexpr uint32_t kGameInfoExecMagic = 'EXEC'; constexpr uint32_t kGameInfoCommMagic = 'COMM'; constexpr uint32_t kGameInfoTitlMagic = 'TITL'; -GameInfoWrapper::GameInfoWrapper(const uint8_t *data, size_t data_size) +GameInfoWrapper::GameInfoWrapper(const uint8_t* data, size_t data_size) : data_(data), data_size_(data_size) { if (!data) { return; } - const GameInfoBlockHeader *block_header(nullptr); + const GameInfoBlockHeader* block_header(nullptr); size_t data_offset(0); while (data_offset < data_size_) { block_header = - reinterpret_cast(data_ + data_offset); + reinterpret_cast(data_ + data_offset); data_offset += sizeof(GameInfoBlockHeader); switch (block_header->magic) { - case kGameInfoExecMagic: - exec_.virtual_titleid = - reinterpret_cast(data_ + data_offset); - data_offset += exec_.VirtualTitleIdLength + 1; - exec_.module_name = reinterpret_cast(data_ + data_offset); - data_offset += exec_.ModuleNameLength + 1; - exec_.build_description = - reinterpret_cast(data_ + data_offset); - data_offset += exec_.BuildDescriptionLength + 1; - break; - case kGameInfoCommMagic: - assert_true(block_header->block_size == sizeof(GameInfoBlockComm)); - comm_ = reinterpret_cast(data_ + data_offset); - data_offset += block_header->block_size; - break; - case kGameInfoTitlMagic: - assert_true(block_header->block_size == sizeof(GameInfoBlockTitl)); - titl_ = reinterpret_cast(data_ + data_offset); - data_offset += block_header->block_size; - break; - default: - // Unsupported headers - data_ = nullptr; - return; + case kGameInfoExecMagic: + exec_.virtual_titleid = + reinterpret_cast(data_ + data_offset); + data_offset += exec_.VirtualTitleIdLength + 1; + exec_.module_name = reinterpret_cast(data_ + data_offset); + data_offset += exec_.ModuleNameLength + 1; + exec_.build_description = + reinterpret_cast(data_ + data_offset); + data_offset += exec_.BuildDescriptionLength + 1; + break; + case kGameInfoCommMagic: + assert_true(block_header->block_size == sizeof(GameInfoBlockComm)); + comm_ = reinterpret_cast(data_ + data_offset); + data_offset += block_header->block_size; + break; + case kGameInfoTitlMagic: + assert_true(block_header->block_size == sizeof(GameInfoBlockTitl)); + titl_ = reinterpret_cast(data_ + data_offset); + data_offset += block_header->block_size; + break; + default: + // Unsupported headers + data_ = nullptr; + return; } } @@ -77,6 +77,6 @@ std::string GameInfo::module_name() const { return std::string(exec_.module_name, exec_.module_name + module_name_length); } -} // namespace util -} // namespace kernel -} // namespace xe +} // namespace util +} // namespace kernel +} // namespace xe diff --git a/src/xenia/kernel/util/gameinfo_utils.h b/src/xenia/kernel/util/gameinfo_utils.h index 137567f2e..189ac46ef 100644 --- a/src/xenia/kernel/util/gameinfo_utils.h +++ b/src/xenia/kernel/util/gameinfo_utils.h @@ -20,12 +20,12 @@ namespace kernel { namespace util { class GameInfoWrapper { -public: - GameInfoWrapper(const uint8_t *data, size_t data_size); + public: + GameInfoWrapper(const uint8_t* data, size_t data_size); bool is_valid() const { return data_ != nullptr; } -protected: + protected: struct GameInfoBlockHeader { xe::be magic; xe::be block_size; @@ -33,9 +33,9 @@ protected: static_assert_size(GameInfoBlockHeader, 8); struct GameInfoBlockExec { - const char *virtual_titleid; - const char *module_name; - const char *build_description; + const char* virtual_titleid; + const char* module_name; + const char* build_description; const uint32_t VirtualTitleIdLength = 32; const uint32_t ModuleNameLength = 42; @@ -50,23 +50,23 @@ protected: struct GameInfoBlockTitl { xe::be title[128]; xe::be description[256]; - xe::be publisher[256]; // assumed field name from wxPirs + xe::be publisher[256]; // assumed field name from wxPirs }; -private: - const uint8_t *data_ = nullptr; + private: + const uint8_t* data_ = nullptr; size_t data_size_ = 0; -protected: + protected: GameInfoBlockExec exec_; - const GameInfoBlockComm *comm_ = nullptr; - const GameInfoBlockTitl *titl_ = nullptr; + const GameInfoBlockComm* comm_ = nullptr; + const GameInfoBlockTitl* titl_ = nullptr; }; class GameInfo : public GameInfoWrapper { -public: - GameInfo(const std::vector &data) - : GameInfoWrapper(reinterpret_cast(data.data()), + public: + GameInfo(const std::vector& data) + : GameInfoWrapper(reinterpret_cast(data.data()), data.size()) {} uint32_t title_id() const; @@ -74,8 +74,8 @@ public: std::string module_name() const; }; -} // namespace util -} // namespace kernel -} // namespace xe +} // namespace util +} // namespace kernel +} // namespace xe -#endif // XENIA_KERNEL_UTIL_GAMEINFO_UTILS_H_ +#endif // XENIA_KERNEL_UTIL_GAMEINFO_UTILS_H_