From 6cc3440086fc141f9e2f23b8822ed7a5b1f15db8 Mon Sep 17 00:00:00 2001 From: x1nixmzeng Date: Sat, 23 Jul 2016 18:27:13 +0100 Subject: [PATCH] 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_