From 3de39aaf10333cee9ed70c3c9e11e4773d507302 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Fri, 15 Aug 2014 10:19:59 -0700 Subject: [PATCH] Postmortem debug target now loads/scans the trace and inits the filesystem. --- src/poly/memory.h | 29 +++ src/poly/poly.h | 1 + src/poly/sources.gypi | 2 + src/poly/string.cc | 26 +++ src/poly/string.h | 24 +++ src/xdb/debug_target.cc | 16 +- src/xdb/debug_target.h | 10 + src/xdb/postmortem_debug_target.cc | 196 +++++++++++++++++- src/xdb/postmortem_debug_target.h | 18 +- src/xdb/protocol.h | 11 +- src/xdb/ui/xdb_app.cc | 15 +- src/xenia/debug_agent.cc | 6 +- src/xenia/emulator.cc | 134 ++++-------- src/xenia/emulator.h | 15 +- src/xenia/kernel/fs/device.cc | 14 +- src/xenia/kernel/fs/device.h | 19 +- .../kernel/fs/devices/disc_image_device.cc | 14 +- .../kernel/fs/devices/disc_image_device.h | 26 ++- .../kernel/fs/devices/host_path_device.cc | 14 +- .../kernel/fs/devices/host_path_device.h | 23 +- .../fs/devices/stfs_container_device.cc | 15 +- .../kernel/fs/devices/stfs_container_device.h | 26 ++- src/xenia/kernel/fs/filesystem.cc | 75 ++++++- src/xenia/kernel/fs/filesystem.h | 30 ++- src/xenia/kernel/xboxkrnl_rtl.cc | 7 +- tools/xenia-debug/xenia-debug.cc | 5 +- tools/xenia-run/xenia-run.cc | 25 +-- 27 files changed, 541 insertions(+), 255 deletions(-) create mode 100644 src/poly/string.cc create mode 100644 src/poly/string.h diff --git a/src/poly/memory.h b/src/poly/memory.h index 531c0ea4d..e4d24a3f9 100644 --- a/src/poly/memory.h +++ b/src/poly/memory.h @@ -12,6 +12,7 @@ #include +#include #include namespace poly { @@ -60,6 +61,20 @@ template <> inline double load(const void* mem) { return *reinterpret_cast(mem); } +template +inline T load(const void* mem) { + if (sizeof(T) == 1) { + return static_cast(load(mem)); + } else if (sizeof(T) == 2) { + return static_cast(load(mem)); + } else if (sizeof(T) == 4) { + return static_cast(load(mem)); + } else if (sizeof(T) == 8) { + return static_cast(load(mem)); + } else { + assert_always("Invalid poly::load size"); + } +} template T load_and_swap(const void* mem); @@ -172,6 +187,20 @@ template <> inline void store(void* mem, double value) { *reinterpret_cast(mem) = value; } +template +inline void store(const void* mem, T value) { + if (sizeof(T) == 1) { + store(mem, static_cast(value)); + } else if (sizeof(T) == 2) { + store(mem, static_cast(value)); + } else if (sizeof(T) == 4) { + store(mem, static_cast(value)); + } else if (sizeof(T) == 8) { + store(mem, static_cast(value)); + } else { + assert_always("Invalid poly::store size"); + } +} template void store_and_swap(void* mem, T value); diff --git a/src/poly/poly.h b/src/poly/poly.h index 1cdadbed6..4fc966b73 100644 --- a/src/poly/poly.h +++ b/src/poly/poly.h @@ -19,6 +19,7 @@ #include #include #include +#include #include namespace poly {} // namespace poly diff --git a/src/poly/sources.gypi b/src/poly/sources.gypi index 6765121fa..19e08905b 100644 --- a/src/poly/sources.gypi +++ b/src/poly/sources.gypi @@ -15,6 +15,8 @@ 'poly-private.h', 'poly.cc', 'poly.h', + 'string.cc', + 'string.h', 'threading.h', ], diff --git a/src/poly/string.cc b/src/poly/string.cc new file mode 100644 index 000000000..1d8bb53e2 --- /dev/null +++ b/src/poly/string.cc @@ -0,0 +1,26 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include + +#include + +namespace poly { + +std::string to_string(const std::wstring& source) { + static std::wstring_convert> converter; + return converter.to_bytes(source); +} + +std::wstring to_wstring(const std::string& source) { + static std::wstring_convert> converter; + return converter.from_bytes(source); +} + +} // namespace poly diff --git a/src/poly/string.h b/src/poly/string.h new file mode 100644 index 000000000..20b684e6d --- /dev/null +++ b/src/poly/string.h @@ -0,0 +1,24 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef POLY_STRING_H_ +#define POLY_STRING_H_ + +#include + +#include + +namespace poly { + +std::string to_string(const std::wstring& source); +std::wstring to_wstring(const std::string& source); + +} // namespace poly + +#endif // POLY_STRING_H_ diff --git a/src/xdb/debug_target.cc b/src/xdb/debug_target.cc index 197ab2be6..65b1ea810 100644 --- a/src/xdb/debug_target.cc +++ b/src/xdb/debug_target.cc @@ -11,6 +11,20 @@ namespace xdb { -// +bool DebugTarget::InitializeFileSystem(const std::wstring& path) { + file_system_.reset(new xe::kernel::fs::FileSystem()); + + // Infer the type (stfs/iso/etc) from the path. + auto file_system_type = file_system_->InferType(path); + + // Setup the file system exactly like the emulator does - this way we can + // access all the same files. + if (file_system_->InitializeFromPath(file_system_type, path)) { + XELOGE("Unable to initialize filesystem from path"); + return false; + } + + return true; +} } // namespace xdb diff --git a/src/xdb/debug_target.h b/src/xdb/debug_target.h index 2ff238f5d..7724daefd 100644 --- a/src/xdb/debug_target.h +++ b/src/xdb/debug_target.h @@ -10,12 +10,22 @@ #ifndef XDB_DEBUG_TARGET_H_ #define XDB_DEBUG_TARGET_H_ +#include + namespace xdb { class DebugTarget { public: + virtual ~DebugTarget() = default; + + xe::kernel::fs::FileSystem* file_system() const { return file_system_.get(); } + protected: DebugTarget() = default; + + bool InitializeFileSystem(const std::wstring& path); + + std::unique_ptr file_system_; }; } // namespace xdb diff --git a/src/xdb/postmortem_debug_target.cc b/src/xdb/postmortem_debug_target.cc index fa541a72d..6a3009385 100644 --- a/src/xdb/postmortem_debug_target.cc +++ b/src/xdb/postmortem_debug_target.cc @@ -9,19 +9,109 @@ #include +#include +#include + namespace xdb { -bool PostmortemDebugTarget::LoadTrace(const std::wstring& path) { - // TODO(benvanik): memory map trace. - return true; +using xdb::protocol::EventType; + +// Matches the EventType ordering to allow for quick event size checks. +const size_t event_sizes[] = { + 0, + sizeof(protocol::ProcessStartEvent), + sizeof(protocol::ProcessExitEvent), + sizeof(protocol::ModuleLoadEvent), + sizeof(protocol::ModuleUnloadEvent), + sizeof(protocol::ThreadCreateEvent), + sizeof(protocol::ThreadInfoEvent), + sizeof(protocol::ThreadExitEvent), + sizeof(protocol::FunctionCompiledEvent), + sizeof(protocol::OutputStringEvent), + sizeof(protocol::KernelCallEvent), + sizeof(protocol::KernelCallReturnEvent), + sizeof(protocol::UserCallEvent), + sizeof(protocol::UserCallReturnEvent), + sizeof(protocol::InstrEvent), + sizeof(protocol::InstrEventR8), + sizeof(protocol::InstrEventR8R8), + sizeof(protocol::InstrEventR8R16), + sizeof(protocol::InstrEventR16), + sizeof(protocol::InstrEventR16R8), + sizeof(protocol::InstrEventR16R16), +}; + +PostmortemDebugTarget::PostmortemDebugTarget() + : file_(nullptr), + file_mapping_(nullptr), + trace_base_(0), + process_start_event_(nullptr), + process_exit_event_(nullptr) {} + +PostmortemDebugTarget::~PostmortemDebugTarget() { + if (trace_base_) { + UnmapViewOfFile(trace_base_); + } + CloseHandle(file_mapping_); + CloseHandle(file_); } -bool PostmortemDebugTarget::LoadContent(const std::wstring& path) { - // If no path is provided attempt to infer from the trace. - if (path.empty()) { - // TODO(benvanik): find process info block and read source path. +bool PostmortemDebugTarget::LoadTrace(const std::wstring& path, + const std::wstring& content_path) { + file_ = CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY, nullptr); + if (!file_) { + XELOGE("Could not open trace file for writing"); + return false; } - // TODO(benvanik): initialize filesystem and load iso/stfs/etc to get at xex. + + file_mapping_ = CreateFileMapping(file_, nullptr, PAGE_READONLY, 0, 0, + L"Local\\xenia_xdb_trace"); + if (!file_mapping_) { + XELOGE("Could not create trace file mapping"); + return false; + } + + trace_base_ = reinterpret_cast( + MapViewOfFile(file_mapping_, FILE_MAP_READ, 0, 0, 0)); + if (!trace_base_) { + XELOGE("Could not map view of trace file"); + return false; + } + + // Find the process start event - it should be near the top and we need it for + // path lookup. + const uint8_t* ptr = trace_base_ + 8; + EventType event_type; + while ((event_type = poly::load(ptr)) != + EventType::END_OF_STREAM) { + switch (event_type) { + case EventType::PROCESS_START: { + process_start_event_ = protocol::ProcessStartEvent::Get(ptr); + break; + } + } + if (process_start_event_) { + break; + } + ptr += event_sizes[static_cast(event_type)]; + } + + bool initialized_filesystem = false; + if (!content_path.empty()) { + initialized_filesystem = InitializeFileSystem(content_path); + } else { + // If no path was provided just use what's in the trace. + auto trace_content_path = + poly::to_wstring(std::string(process_start_event_->launch_path)); + initialized_filesystem = InitializeFileSystem(trace_content_path); + } + if (!initialized_filesystem) { + XELOGE("Unable to initialize filesystem."); + return false; + } + return true; } @@ -32,6 +122,96 @@ bool PostmortemDebugTarget::Prepare() { bool PostmortemDebugTarget::Prepare(std::atomic& cancelled) { // TODO(benvanik): scan file, build indicies, etc. + + const uint8_t* ptr = trace_base_ + 8; + EventType event_type; + while ((event_type = poly::load(ptr)) != + EventType::END_OF_STREAM) { + switch (event_type) { + case EventType::PROCESS_START: { + process_start_event_ = protocol::ProcessStartEvent::Get(ptr); + break; + } + case EventType::PROCESS_EXIT: { + process_exit_event_ = protocol::ProcessExitEvent::Get(ptr); + break; + } + case EventType::MODULE_LOAD: { + auto ev = protocol::ModuleLoadEvent::Get(ptr); + break; + } + case EventType::MODULE_UNLOAD: { + auto ev = protocol::ModuleUnloadEvent::Get(ptr); + break; + } + case EventType::THREAD_CREATE: { + auto ev = protocol::ThreadCreateEvent::Get(ptr); + break; + } + case EventType::THREAD_INFO: { + auto ev = protocol::ThreadInfoEvent::Get(ptr); + break; + } + case EventType::THREAD_EXIT: { + auto ev = protocol::ThreadExitEvent::Get(ptr); + break; + } + case EventType::FUNCTION_COMPILED: { + auto ev = protocol::FunctionCompiledEvent::Get(ptr); + break; + } + case EventType::OUTPUT_STRING: { + auto ev = protocol::OutputStringEvent::Get(ptr); + break; + } + case EventType::KERNEL_CALL: { + auto ev = protocol::KernelCallEvent::Get(ptr); + break; + } + case EventType::KERNEL_CALL_RETURN: { + auto ev = protocol::KernelCallReturnEvent::Get(ptr); + break; + } + case EventType::USER_CALL: { + auto ev = protocol::UserCallEvent::Get(ptr); + break; + } + case EventType::USER_CALL_RETURN: { + auto ev = protocol::UserCallReturnEvent::Get(ptr); + break; + } + case EventType::INSTR: { + auto ev = protocol::InstrEvent::Get(ptr); + break; + } + case EventType::INSTR_R8: { + auto ev = protocol::InstrEventR8::Get(ptr); + break; + } + case EventType::INSTR_R8_R8: { + auto ev = protocol::InstrEventR8R8::Get(ptr); + break; + } + case EventType::INSTR_R8_R16: { + auto ev = protocol::InstrEventR8R16::Get(ptr); + break; + } + case EventType::INSTR_R16: { + auto ev = protocol::InstrEventR16::Get(ptr); + break; + } + case EventType::INSTR_R16_R8: { + auto ev = protocol::InstrEventR16R8::Get(ptr); + break; + } + case EventType::INSTR_R16_R16: { + auto ev = protocol::InstrEventR16R16::Get(ptr); + break; + } + } + ptr += event_sizes[static_cast(event_type)]; + }; + return true; } diff --git a/src/xdb/postmortem_debug_target.h b/src/xdb/postmortem_debug_target.h index 4071ea19e..6f4575337 100644 --- a/src/xdb/postmortem_debug_target.h +++ b/src/xdb/postmortem_debug_target.h @@ -10,22 +10,34 @@ #ifndef XDB_POSTMORTEM_DEBUG_TARGET_H_ #define XDB_POSTMORTEM_DEBUG_TARGET_H_ +// TODO(benvanik): abstract mapping type. +#include #include #include #include +#include namespace xdb { class PostmortemDebugTarget : public DebugTarget { public: - PostmortemDebugTarget() = default; + PostmortemDebugTarget(); + ~PostmortemDebugTarget() override; - bool LoadTrace(const std::wstring& path); - bool LoadContent(const std::wstring& path = L""); + bool LoadTrace(const std::wstring& path, + const std::wstring& content_path = L""); bool Prepare(); bool Prepare(std::atomic& cancelled); + + private: + HANDLE file_; + HANDLE file_mapping_; + const uint8_t* trace_base_; + + const protocol::ProcessStartEvent* process_start_event_; + const protocol::ProcessExitEvent* process_exit_event_; }; } // namespace xdb diff --git a/src/xdb/protocol.h b/src/xdb/protocol.h index 51f9628da..b1b4be039 100644 --- a/src/xdb/protocol.h +++ b/src/xdb/protocol.h @@ -23,7 +23,7 @@ using vec128_t = alloy::vec128_t; #pragma pack(push, 4) enum class EventType : uint8_t { - SETUP, + END_OF_STREAM = 0, PROCESS_START, PROCESS_EXIT, @@ -61,15 +61,16 @@ struct Event { sizeof(T), reinterpret_cast(trace_base)); return reinterpret_cast(ptr); } -}; -struct SetupEvent : public Event { - EventType type; - uint64_t membase; + static const T* Get(const void* ptr) { + return reinterpret_cast(ptr); + } }; struct ProcessStartEvent : public Event { EventType type; + uint64_t membase; + char launch_path[256]; }; struct ProcessExitEvent : public Event { diff --git a/src/xdb/ui/xdb_app.cc b/src/xdb/ui/xdb_app.cc index c902142b7..d02c67136 100644 --- a/src/xdb/ui/xdb_app.cc +++ b/src/xdb/ui/xdb_app.cc @@ -10,10 +10,9 @@ #include #include -#include #include -#include +#include #include #include #include @@ -46,25 +45,19 @@ void XdbApp::OpenEmpty() { bool XdbApp::OpenTraceFile(const std::string& trace_file_path, const std::string& content_file_path) { - std::wstring_convert> converter; - return OpenTraceFile(converter.from_bytes(trace_file_path), - converter.from_bytes(content_file_path)); + return OpenTraceFile(poly::to_wstring(trace_file_path), + poly::to_wstring(content_file_path)); } bool XdbApp::OpenTraceFile(const std::wstring& trace_file_path, const std::wstring& content_file_path) { std::unique_ptr target(new PostmortemDebugTarget()); - if (!target->LoadTrace(trace_file_path)) { + if (!target->LoadTrace(trace_file_path, content_file_path)) { HandleOpenError("Unable to load trace file."); return false; } - if (!target->LoadContent(content_file_path)) { - HandleOpenError("Unable to load source game content module."); - return false; - } - wxProgressDialog progress_dialog( "Preparing trace...", "This may take some time.", 100, nullptr, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_CAN_ABORT | diff --git a/src/xenia/debug_agent.cc b/src/xenia/debug_agent.cc index 3b2c63c82..4c552a31a 100644 --- a/src/xenia/debug_agent.cc +++ b/src/xenia/debug_agent.cc @@ -9,8 +9,7 @@ #include -#include - +#include #include DEFINE_string(trace_file, "", "Trace to the given file."); @@ -45,8 +44,7 @@ int DebugAgent::Initialize() { } int DebugAgent::SetupTracing(const std::string& trace_file, uint64_t capacity) { - std::wstring_convert> cvt; - auto file_path = cvt.from_bytes(trace_file); + auto file_path = poly::to_wstring(trace_file); file_ = CreateFile(file_path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY, diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 5cbc64675..fc054e6fc 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -149,7 +150,7 @@ void Emulator::set_main_window(Window* window) { }); } -X_STATUS Emulator::LaunchXexFile(const xechar_t* path) { +X_STATUS Emulator::LaunchXexFile(const std::wstring& path) { // 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: @@ -157,111 +158,60 @@ X_STATUS Emulator::LaunchXexFile(const xechar_t* path) { // and then get that symlinked to game:\, so // -> game:\foo.xex - auto ev = xdb::protocol::ProcessStartEvent::Append(memory()->trace_base()); - if (ev) { - ev->type = xdb::protocol::EventType::PROCESS_START; + int result_code = + file_system_->InitializeFromPath(FileSystemType::XEX_FILE, path); + if (result_code) { + return X_STATUS_INVALID_PARAMETER; } - int result_code = 0; - // Get just the filename (foo.xex). - const xechar_t* file_name = xestrrchr(path, poly::path_separator); - if (file_name) { - // Skip slash. - file_name++; - } else { + std::wstring file_name; + auto last_slash = path.find_last_of(poly::path_separator); + if (last_slash == std::string::npos) { // No slash found, whole thing is a file. file_name = path; + } else { + // Skip slash. + file_name = path.substr(last_slash + 1); } - // Get the parent path of the file. - xechar_t parent_path[poly::max_path]; - XEIGNORE(xestrcpy(parent_path, XECOUNT(parent_path), path)); - parent_path[file_name - path] = 0; - - // Register the local directory in the virtual filesystem. - result_code = file_system_->RegisterHostPathDevice( - "\\Device\\Harddisk1\\Partition0", parent_path); - if (result_code) { - XELOGE("Unable to mount local directory"); - return result_code; - } - - // Create symlinks to the device. - file_system_->CreateSymbolicLink( - "game:", "\\Device\\Harddisk1\\Partition0"); - file_system_->CreateSymbolicLink( - "d:", "\\Device\\Harddisk1\\Partition0"); - - // Get the file name of the module to load from the filesystem. - char fs_path[poly::max_path]; - XEIGNORE(xestrcpya(fs_path, XECOUNT(fs_path), "game:\\")); - char* fs_path_ptr = fs_path + xestrlena(fs_path); - *fs_path_ptr = 0; -#if XE_WCHAR - XEIGNORE(xestrnarrow(fs_path_ptr, XECOUNT(fs_path), file_name)); -#else - XEIGNORE(xestrcpya(fs_path_ptr, XECOUNT(fs_path), file_name)); -#endif - // Launch the game. - return xboxkrnl_->LaunchModule(fs_path); + std::string fs_path = "game:\\" + poly::to_string(file_name); + return CompleteLaunch(path, fs_path); } -X_STATUS Emulator::LaunchDiscImage(const xechar_t* path) { - int result_code = 0; +X_STATUS Emulator::LaunchDiscImage(const std::wstring& path) { + int result_code = + file_system_->InitializeFromPath(FileSystemType::DISC_IMAGE, path); + if (result_code) { + return X_STATUS_INVALID_PARAMETER; + } + // Launch the game. + return CompleteLaunch(path, "game:\\default.xex"); +} + +X_STATUS Emulator::LaunchSTFSTitle(const std::wstring& path) { + int result_code = + file_system_->InitializeFromPath(FileSystemType::STFS_TITLE, path); + if (result_code) { + return X_STATUS_INVALID_PARAMETER; + } + + // Launch the game. + return CompleteLaunch(path, "game:\\default.xex"); +} + +X_STATUS Emulator::CompleteLaunch(const std::wstring& path, + const std::string& module_path) { auto ev = xdb::protocol::ProcessStartEvent::Append(memory()->trace_base()); if (ev) { ev->type = xdb::protocol::EventType::PROCESS_START; + ev->membase = reinterpret_cast(memory()->membase()); + auto path_length = poly::to_string(path) + .copy(ev->launch_path, sizeof(ev->launch_path) - 1); + ev->launch_path[path_length] = 0; } - // Register the disc image in the virtual filesystem. - result_code = file_system_->RegisterDiscImageDevice( - "\\Device\\Cdrom0", path); - if (result_code) { - XELOGE("Unable to mount disc image"); - return result_code; - } - - // Create symlinks to the device. - file_system_->CreateSymbolicLink( - "game:", - "\\Device\\Cdrom0"); - file_system_->CreateSymbolicLink( - "d:", - "\\Device\\Cdrom0"); - - // Launch the game. - return xboxkrnl_->LaunchModule("game:\\default.xex"); -} - -X_STATUS Emulator::LaunchSTFSTitle(const xechar_t* path) { - int result_code = 0; - - auto ev = xdb::protocol::ProcessStartEvent::Append(memory()->trace_base()); - if (ev) { - ev->type = xdb::protocol::EventType::PROCESS_START; - } - - // TODO(benvanik): figure out paths. - - // Register the disc image in the virtual filesystem. - result_code = file_system_->RegisterSTFSContainerDevice( - "\\Device\\Cdrom0", path); - if (result_code) { - XELOGE("Unable to mount STFS container"); - return result_code; - } - - // Create symlinks to the device. - file_system_->CreateSymbolicLink( - "game:", - "\\Device\\Cdrom0"); - file_system_->CreateSymbolicLink( - "d:", - "\\Device\\Cdrom0"); - - // Launch the game. - return xboxkrnl_->LaunchModule("game:\\default.xex"); + return xboxkrnl_->LaunchModule(module_path.c_str()); } diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index f3120c8d5..23d24bf37 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -10,6 +10,8 @@ #ifndef XENIA_EMULATOR_H_ #define XENIA_EMULATOR_H_ +#include + #include #include #include @@ -32,7 +34,7 @@ XEDECLARECLASS2(xe, ui, Window); namespace xe { class Emulator { -public: + public: Emulator(const xechar_t* command_line); ~Emulator(); @@ -59,11 +61,14 @@ public: X_STATUS Setup(); // TODO(benvanik): raw binary. - X_STATUS LaunchXexFile(const xechar_t* path); - X_STATUS LaunchDiscImage(const xechar_t* path); - X_STATUS LaunchSTFSTitle(const xechar_t* path); + X_STATUS LaunchXexFile(const std::wstring& path); + X_STATUS LaunchDiscImage(const std::wstring& path); + X_STATUS LaunchSTFSTitle(const std::wstring& path); + + private: + X_STATUS CompleteLaunch(const std::wstring& path, + const std::string& module_path); -private: xechar_t command_line_[poly::max_path]; ui::Window* main_window_; diff --git a/src/xenia/kernel/fs/device.cc b/src/xenia/kernel/fs/device.cc index d49c56362..c9c37664a 100644 --- a/src/xenia/kernel/fs/device.cc +++ b/src/xenia/kernel/fs/device.cc @@ -9,20 +9,12 @@ #include - using namespace xe; using namespace xe::kernel; using namespace xe::kernel::fs; +Device::Device(const std::string& path) : path_(path) {} -Device::Device(const char* path) { - path_ = xestrdupa(path); -} +Device::~Device() {} -Device::~Device() { - xe_free(path_); -} - -const char* Device::path() { - return path_; -} +const char* Device::path() const { return path_.c_str(); } diff --git a/src/xenia/kernel/fs/device.h b/src/xenia/kernel/fs/device.h index 1c76b63f7..01215e47a 100644 --- a/src/xenia/kernel/fs/device.h +++ b/src/xenia/kernel/fs/device.h @@ -10,37 +10,36 @@ #ifndef XENIA_KERNEL_FS_DEVICE_H_ #define XENIA_KERNEL_FS_DEVICE_H_ +#include + #include #include #include - namespace xe { namespace kernel { namespace fs { - class Device { -public: - Device(const char* path); + public: + Device(const std::string& path); virtual ~Device(); - const char* path(); + const char* path() const; virtual Entry* ResolvePath(const char* path) = 0; virtual X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length) = 0; - virtual X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info, size_t length) = 0; + virtual X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info, + size_t length) = 0; -protected: - char* path_; + protected: + std::string path_; }; - } // namespace fs } // namespace kernel } // namespace xe - #endif // XENIA_KERNEL_FS_DEVICE_H_ diff --git a/src/xenia/kernel/fs/devices/disc_image_device.cc b/src/xenia/kernel/fs/devices/disc_image_device.cc index b64172919..da8b8912a 100644 --- a/src/xenia/kernel/fs/devices/disc_image_device.cc +++ b/src/xenia/kernel/fs/devices/disc_image_device.cc @@ -17,23 +17,17 @@ using namespace xe; using namespace xe::kernel; using namespace xe::kernel::fs; - - -DiscImageDevice::DiscImageDevice(const char* path, const xechar_t* local_path) : - Device(path) { - local_path_ = xestrdup(local_path); - mmap_ = NULL; - gdfx_ = NULL; -} +DiscImageDevice::DiscImageDevice(const std::string& path, + const std::wstring& local_path) + : Device(path), local_path_(local_path), mmap_(nullptr), gdfx_(nullptr) {} DiscImageDevice::~DiscImageDevice() { delete gdfx_; xe_mmap_release(mmap_); - xe_free(local_path_); } int DiscImageDevice::Init() { - mmap_ = xe_mmap_open(kXEFileModeRead, local_path_, 0, 0); + mmap_ = xe_mmap_open(kXEFileModeRead, local_path_.c_str(), 0, 0); if (!mmap_) { XELOGE("Disc image could not be mapped"); return 1; diff --git a/src/xenia/kernel/fs/devices/disc_image_device.h b/src/xenia/kernel/fs/devices/disc_image_device.h index 05a65aabd..c625357b3 100644 --- a/src/xenia/kernel/fs/devices/disc_image_device.h +++ b/src/xenia/kernel/fs/devices/disc_image_device.h @@ -10,42 +10,40 @@ #ifndef XENIA_KERNEL_FS_DEVICES_DISC_IMAGE_DEVICE_H_ #define XENIA_KERNEL_FS_DEVICES_DISC_IMAGE_DEVICE_H_ +#include + #include #include #include - namespace xe { namespace kernel { namespace fs { - class GDFX; - class DiscImageDevice : public Device { -public: - DiscImageDevice(const char* path, const xechar_t* local_path); - virtual ~DiscImageDevice(); + public: + DiscImageDevice(const std::string& path, const std::wstring& local_path); + ~DiscImageDevice() override; int Init(); - virtual Entry* ResolvePath(const char* path); + Entry* ResolvePath(const char* path) override; - virtual X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length); - virtual X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info, size_t length); + X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length) override; + X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info, + size_t length) override; -private: - xechar_t* local_path_; + private: + std::wstring local_path_; xe_mmap_ref mmap_; - GDFX* gdfx_; + GDFX* gdfx_; }; - } // namespace fs } // namespace kernel } // namespace xe - #endif // XENIA_KERNEL_FS_DEVICES_DISC_IMAGE_DEVICE_H_ diff --git a/src/xenia/kernel/fs/devices/host_path_device.cc b/src/xenia/kernel/fs/devices/host_path_device.cc index 9eed206f0..c134071ba 100644 --- a/src/xenia/kernel/fs/devices/host_path_device.cc +++ b/src/xenia/kernel/fs/devices/host_path_device.cc @@ -17,15 +17,11 @@ using namespace xe; using namespace xe::kernel; using namespace xe::kernel::fs; +HostPathDevice::HostPathDevice(const std::string& path, + const std::wstring& local_path) + : Device(path), local_path_(local_path) {} -HostPathDevice::HostPathDevice(const char* path, const xechar_t* local_path) : - Device(path) { - local_path_ = xestrdup(local_path); -} - -HostPathDevice::~HostPathDevice() { - xe_free(local_path_); -} +HostPathDevice::~HostPathDevice() {} Entry* HostPathDevice::ResolvePath(const char* path) { // The filesystem will have stripped our prefix off already, so the path will @@ -42,7 +38,7 @@ Entry* HostPathDevice::ResolvePath(const char* path) { #endif xechar_t full_path[poly::max_path]; - xe_path_join(local_path_, rel_path, full_path, XECOUNT(full_path)); + xe_path_join(local_path_.c_str(), rel_path, full_path, XECOUNT(full_path)); // Swap around path separators. if (poly::path_separator != '\\') { diff --git a/src/xenia/kernel/fs/devices/host_path_device.h b/src/xenia/kernel/fs/devices/host_path_device.h index f249911d2..95b265ab7 100644 --- a/src/xenia/kernel/fs/devices/host_path_device.h +++ b/src/xenia/kernel/fs/devices/host_path_device.h @@ -10,35 +10,34 @@ #ifndef XENIA_KERNEL_FS_DEVICES_HOST_PATH_DEVICE_H_ #define XENIA_KERNEL_FS_DEVICES_HOST_PATH_DEVICE_H_ +#include + #include #include #include - namespace xe { namespace kernel { namespace fs { - class HostPathDevice : public Device { -public: - HostPathDevice(const char* path, const xechar_t* local_path); - virtual ~HostPathDevice(); + public: + HostPathDevice(const std::string& path, const std::wstring& local_path); + ~HostPathDevice() override; - virtual Entry* ResolvePath(const char* path); + Entry* ResolvePath(const char* path) override; - virtual X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length); - virtual X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info, size_t length); + X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length) override; + X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info, + size_t length) override; -private: - xechar_t* local_path_; + private: + std::wstring local_path_; }; - } // namespace fs } // namespace kernel } // namespace xe - #endif // XENIA_KERNEL_FS_DEVICES_HOST_PATH_DEVICE_H_ diff --git a/src/xenia/kernel/fs/devices/stfs_container_device.cc b/src/xenia/kernel/fs/devices/stfs_container_device.cc index 4d0d97127..da37bc4bf 100644 --- a/src/xenia/kernel/fs/devices/stfs_container_device.cc +++ b/src/xenia/kernel/fs/devices/stfs_container_device.cc @@ -17,24 +17,17 @@ using namespace xe; using namespace xe::kernel; using namespace xe::kernel::fs; - - -STFSContainerDevice::STFSContainerDevice( - const char* path, const xechar_t* local_path) : - Device(path) { - local_path_ = xestrdup(local_path); - mmap_ = NULL; - stfs_ = NULL; -} +STFSContainerDevice::STFSContainerDevice(const std::string& path, + const std::wstring& local_path) + : Device(path), local_path_(local_path), mmap_(nullptr), stfs_(nullptr) {} STFSContainerDevice::~STFSContainerDevice() { delete stfs_; xe_mmap_release(mmap_); - xe_free(local_path_); } int STFSContainerDevice::Init() { - mmap_ = xe_mmap_open(kXEFileModeRead, local_path_, 0, 0); + mmap_ = xe_mmap_open(kXEFileModeRead, local_path_.c_str(), 0, 0); if (!mmap_) { XELOGE("STFS container could not be mapped"); return 1; diff --git a/src/xenia/kernel/fs/devices/stfs_container_device.h b/src/xenia/kernel/fs/devices/stfs_container_device.h index 09e37fb36..e0a368d79 100644 --- a/src/xenia/kernel/fs/devices/stfs_container_device.h +++ b/src/xenia/kernel/fs/devices/stfs_container_device.h @@ -10,42 +10,40 @@ #ifndef XENIA_KERNEL_FS_DEVICES_STFS_CONTAINER_DEVICE_H_ #define XENIA_KERNEL_FS_DEVICES_STFS_CONTAINER_DEVICE_H_ +#include + #include #include #include - namespace xe { namespace kernel { namespace fs { - class STFS; - class STFSContainerDevice : public Device { -public: - STFSContainerDevice(const char* path, const xechar_t* local_path); - virtual ~STFSContainerDevice(); + public: + STFSContainerDevice(const std::string& path, const std::wstring& local_path); + ~STFSContainerDevice() override; int Init(); - virtual Entry* ResolvePath(const char* path); + Entry* ResolvePath(const char* path) override; - virtual X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length); - virtual X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info, size_t length); + X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length) override; + X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info, + size_t length) override; -private: - xechar_t* local_path_; + private: + std::wstring local_path_; xe_mmap_ref mmap_; - STFS* stfs_; + STFS* stfs_; }; - } // namespace fs } // namespace kernel } // namespace xe - #endif // XENIA_KERNEL_FS_DEVICES_STFS_CONTAINER_DEVICE_H_ diff --git a/src/xenia/kernel/fs/filesystem.cc b/src/xenia/kernel/fs/filesystem.cc index 2b53acd0e..538959329 100644 --- a/src/xenia/kernel/fs/filesystem.cc +++ b/src/xenia/kernel/fs/filesystem.cc @@ -33,19 +33,86 @@ FileSystem::~FileSystem() { symlinks_.clear(); } -int FileSystem::RegisterDevice(const char* path, Device* device) { +fs::FileSystemType FileSystem::InferType(const std::wstring& local_path) { + auto last_dot = local_path.find_last_of('.'); + if (last_dot == std::wstring::npos) { + // Likely an STFS container. + return FileSystemType::STFS_TITLE; + } else if (local_path.substr(last_dot) == L".xex") { + // Treat as a naked xex file. + return FileSystemType::XEX_FILE; + } else { + // Assume a disc image. + return FileSystemType::DISC_IMAGE; + } +} + +int FileSystem::InitializeFromPath(fs::FileSystemType type, + const std::wstring& local_path) { + switch (type) { + case FileSystemType::STFS_TITLE: { + // Register the container in the virtual filesystem. + int result_code = + RegisterSTFSContainerDevice("\\Device\\Cdrom0", local_path); + if (result_code) { + XELOGE("Unable to mount STFS container"); + return result_code; + } + + // TODO(benvanik): figure out paths. + // Create symlinks to the device. + CreateSymbolicLink("game:", "\\Device\\Cdrom0"); + CreateSymbolicLink("d:", "\\Device\\Cdrom0"); + break; + } + case FileSystemType::XEX_FILE: { + // Get the parent path of the file. + auto last_slash = local_path.find_last_of(poly::path_separator); + std::wstring parent_path = local_path.substr(0, last_slash); + + // Register the local directory in the virtual filesystem. + int result_code = RegisterHostPathDevice( + "\\Device\\Harddisk1\\Partition0", parent_path); + if (result_code) { + XELOGE("Unable to mount local directory"); + return result_code; + } + + // Create symlinks to the device. + CreateSymbolicLink("game:", "\\Device\\Harddisk1\\Partition0"); + CreateSymbolicLink("d:", "\\Device\\Harddisk1\\Partition0"); + break; + } + case FileSystemType::DISC_IMAGE: { + // Register the disc image in the virtual filesystem. + int result_code = RegisterDiscImageDevice("\\Device\\Cdrom0", local_path); + if (result_code) { + XELOGE("Unable to mount disc image"); + return result_code; + } + + // Create symlinks to the device. + CreateSymbolicLink("game:", "\\Device\\Cdrom0"); + CreateSymbolicLink("d:", "\\Device\\Cdrom0"); + break; + } + } + return 0; +} + +int FileSystem::RegisterDevice(const std::string& path, Device* device) { devices_.push_back(device); return 0; } int FileSystem::RegisterHostPathDevice( - const char* path, const xechar_t* local_path) { + const std::string& path, const std::wstring& local_path) { Device* device = new HostPathDevice(path, local_path); return RegisterDevice(path, device); } int FileSystem::RegisterDiscImageDevice( - const char* path, const xechar_t* local_path) { + const std::string& path, const std::wstring& local_path) { DiscImageDevice* device = new DiscImageDevice(path, local_path); if (device->Init()) { return 1; @@ -54,7 +121,7 @@ int FileSystem::RegisterDiscImageDevice( } int FileSystem::RegisterSTFSContainerDevice( - const char* path, const xechar_t* local_path) { + const std::string& path, const std::wstring& local_path) { STFSContainerDevice* device = new STFSContainerDevice(path, local_path); if (device->Init()) { return 1; diff --git a/src/xenia/kernel/fs/filesystem.h b/src/xenia/kernel/fs/filesystem.h index b252d5e61..76d6ab549 100644 --- a/src/xenia/kernel/fs/filesystem.h +++ b/src/xenia/kernel/fs/filesystem.h @@ -10,6 +10,7 @@ #ifndef XENIA_KERNEL_FS_FILESYSTEM_H_ #define XENIA_KERNEL_FS_FILESYSTEM_H_ +#include #include #include @@ -18,39 +19,46 @@ #include - namespace xe { namespace kernel { namespace fs { - class Device; +enum class FileSystemType { + STFS_TITLE, + DISC_IMAGE, + XEX_FILE, +}; class FileSystem { -public: + public: FileSystem(); ~FileSystem(); - int RegisterDevice(const char* path, Device* device); - int RegisterHostPathDevice(const char* path, const xechar_t* local_path); - int RegisterDiscImageDevice(const char* path, const xechar_t* local_path); - int RegisterSTFSContainerDevice(const char* path, const xechar_t* local_path); + FileSystemType InferType(const std::wstring& local_path); + int InitializeFromPath(FileSystemType type, const std::wstring& local_path); + + int RegisterDevice(const std::string& path, Device* device); + int RegisterHostPathDevice(const std::string& path, + const std::wstring& local_path); + int RegisterDiscImageDevice(const std::string& path, + const std::wstring& local_path); + int RegisterSTFSContainerDevice(const std::string& path, + const std::wstring& local_path); int CreateSymbolicLink(const char* path, const char* target); int DeleteSymbolicLink(const char* path); Entry* ResolvePath(const char* path); -private: - std::vector devices_; + private: + std::vector devices_; std::unordered_map symlinks_; }; - } // namespace fs } // namespace kernel } // namespace xe - #endif // XENIA_KERNEL_FS_FILESYSTEM_H_ diff --git a/src/xenia/kernel/xboxkrnl_rtl.cc b/src/xenia/kernel/xboxkrnl_rtl.cc index 3173229ca..6214e6f97 100644 --- a/src/xenia/kernel/xboxkrnl_rtl.cc +++ b/src/xenia/kernel/xboxkrnl_rtl.cc @@ -9,9 +9,7 @@ #include -#include -#include - +#include #include #include #include @@ -329,8 +327,7 @@ X_STATUS xeRtlUnicodeStringToAnsiString(uint32_t destination_ptr, std::wstring unicode_str = poly::load_and_swap( IMPL_MEM_ADDR(IMPL_MEM_32(source_ptr + 4))); - std::wstring_convert> converter; - std::string ansi_str = converter.to_bytes(unicode_str); + std::string ansi_str = poly::to_string(unicode_str); if (ansi_str.size() > 0xFFFF - 1) { return X_STATUS_INVALID_PARAMETER_2; } diff --git a/tools/xenia-debug/xenia-debug.cc b/tools/xenia-debug/xenia-debug.cc index 2adeaf618..05f0bf846 100644 --- a/tools/xenia-debug/xenia-debug.cc +++ b/tools/xenia-debug/xenia-debug.cc @@ -51,7 +51,10 @@ int main(int argc, xechar_t** argv) { if (!FLAGS_trace_file.empty()) { // Trace file specified on command line. - app->OpenTraceFile(FLAGS_trace_file, FLAGS_content_file); + if (!app->OpenTraceFile(FLAGS_trace_file, FLAGS_content_file)) { + XEFATAL("Failed to open trace file"); + return 1; + } } else { app->OpenEmpty(); } diff --git a/tools/xenia-run/xenia-run.cc b/tools/xenia-run/xenia-run.cc index f684d14eb..fed8c3ae7 100644 --- a/tools/xenia-run/xenia-run.cc +++ b/tools/xenia-run/xenia-run.cc @@ -55,10 +55,6 @@ int xenia_run(int argc, xechar_t** argv) { xechar_t abs_path[poly::max_path]; xe_path_get_absolute(path, abs_path, XECOUNT(abs_path)); - // Grab file extension. - // May be NULL if an STFS file. - const xechar_t* dot = xestrrchr(abs_path, '.'); - // Create the emulator. emulator = new Emulator(L""); XEEXPECTNOTNULL(emulator); @@ -70,16 +66,17 @@ int xenia_run(int argc, xechar_t** argv) { // Launch based on file type. // This is a silly guess based on file extension. - // NOTE: the runtime launch routine will wait until the module exits. - if (!dot) { - // Likely an STFS container. - result = emulator->LaunchSTFSTitle(abs_path); - } else if (xestrcmp(dot, L".xex") == 0) { - // Treat as a naked xex file. - result = emulator->LaunchXexFile(abs_path); - } else { - // Assume a disc image. - result = emulator->LaunchDiscImage(abs_path); + auto file_system_type = emulator->file_system()->InferType(abs_path); + switch (file_system_type) { + case kernel::fs::FileSystemType::STFS_TITLE: + result = emulator->LaunchSTFSTitle(abs_path); + break; + case kernel::fs::FileSystemType::XEX_FILE: + result = emulator->LaunchXexFile(abs_path); + break; + case kernel::fs::FileSystemType::DISC_IMAGE: + result = emulator->LaunchDiscImage(abs_path); + break; } if (XFAILED(result)) { XELOGE("Failed to launch target: %.8X", result);