Postmortem debug target now loads/scans the trace and inits the filesystem.
This commit is contained in:
parent
4768f2fc0b
commit
3de39aaf10
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <poly/assert.h>
|
||||||
#include <poly/byte_order.h>
|
#include <poly/byte_order.h>
|
||||||
|
|
||||||
namespace poly {
|
namespace poly {
|
||||||
|
@ -60,6 +61,20 @@ template <>
|
||||||
inline double load<double>(const void* mem) {
|
inline double load<double>(const void* mem) {
|
||||||
return *reinterpret_cast<const double*>(mem);
|
return *reinterpret_cast<const double*>(mem);
|
||||||
}
|
}
|
||||||
|
template <typename T>
|
||||||
|
inline T load(const void* mem) {
|
||||||
|
if (sizeof(T) == 1) {
|
||||||
|
return static_cast<T>(load<uint8_t>(mem));
|
||||||
|
} else if (sizeof(T) == 2) {
|
||||||
|
return static_cast<T>(load<uint16_t>(mem));
|
||||||
|
} else if (sizeof(T) == 4) {
|
||||||
|
return static_cast<T>(load<uint32_t>(mem));
|
||||||
|
} else if (sizeof(T) == 8) {
|
||||||
|
return static_cast<T>(load<uint64_t>(mem));
|
||||||
|
} else {
|
||||||
|
assert_always("Invalid poly::load size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T load_and_swap(const void* mem);
|
T load_and_swap(const void* mem);
|
||||||
|
@ -172,6 +187,20 @@ template <>
|
||||||
inline void store<double>(void* mem, double value) {
|
inline void store<double>(void* mem, double value) {
|
||||||
*reinterpret_cast<double*>(mem) = value;
|
*reinterpret_cast<double*>(mem) = value;
|
||||||
}
|
}
|
||||||
|
template <typename T>
|
||||||
|
inline void store(const void* mem, T value) {
|
||||||
|
if (sizeof(T) == 1) {
|
||||||
|
store<uint8_t>(mem, static_cast<uint8_t>(value));
|
||||||
|
} else if (sizeof(T) == 2) {
|
||||||
|
store<uint8_t>(mem, static_cast<uint16_t>(value));
|
||||||
|
} else if (sizeof(T) == 4) {
|
||||||
|
store<uint8_t>(mem, static_cast<uint32_t>(value));
|
||||||
|
} else if (sizeof(T) == 8) {
|
||||||
|
store<uint8_t>(mem, static_cast<uint64_t>(value));
|
||||||
|
} else {
|
||||||
|
assert_always("Invalid poly::store size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void store_and_swap(void* mem, T value);
|
void store_and_swap(void* mem, T value);
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <poly/math.h>
|
#include <poly/math.h>
|
||||||
#include <poly/memory.h>
|
#include <poly/memory.h>
|
||||||
#include <poly/platform.h>
|
#include <poly/platform.h>
|
||||||
|
#include <poly/string.h>
|
||||||
#include <poly/threading.h>
|
#include <poly/threading.h>
|
||||||
|
|
||||||
namespace poly {} // namespace poly
|
namespace poly {} // namespace poly
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
'poly-private.h',
|
'poly-private.h',
|
||||||
'poly.cc',
|
'poly.cc',
|
||||||
'poly.h',
|
'poly.h',
|
||||||
|
'string.cc',
|
||||||
|
'string.h',
|
||||||
'threading.h',
|
'threading.h',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -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 <poly/string.h>
|
||||||
|
|
||||||
|
#include <codecvt>
|
||||||
|
|
||||||
|
namespace poly {
|
||||||
|
|
||||||
|
std::string to_string(const std::wstring& source) {
|
||||||
|
static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||||
|
return converter.to_bytes(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring to_wstring(const std::string& source) {
|
||||||
|
static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||||
|
return converter.from_bytes(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace poly
|
|
@ -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 <string>
|
||||||
|
|
||||||
|
#include <poly/config.h>
|
||||||
|
|
||||||
|
namespace poly {
|
||||||
|
|
||||||
|
std::string to_string(const std::wstring& source);
|
||||||
|
std::wstring to_wstring(const std::string& source);
|
||||||
|
|
||||||
|
} // namespace poly
|
||||||
|
|
||||||
|
#endif // POLY_STRING_H_
|
|
@ -11,6 +11,20 @@
|
||||||
|
|
||||||
namespace xdb {
|
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
|
} // namespace xdb
|
||||||
|
|
|
@ -10,12 +10,22 @@
|
||||||
#ifndef XDB_DEBUG_TARGET_H_
|
#ifndef XDB_DEBUG_TARGET_H_
|
||||||
#define XDB_DEBUG_TARGET_H_
|
#define XDB_DEBUG_TARGET_H_
|
||||||
|
|
||||||
|
#include <xenia/kernel/fs/filesystem.h>
|
||||||
|
|
||||||
namespace xdb {
|
namespace xdb {
|
||||||
|
|
||||||
class DebugTarget {
|
class DebugTarget {
|
||||||
public:
|
public:
|
||||||
|
virtual ~DebugTarget() = default;
|
||||||
|
|
||||||
|
xe::kernel::fs::FileSystem* file_system() const { return file_system_.get(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DebugTarget() = default;
|
DebugTarget() = default;
|
||||||
|
|
||||||
|
bool InitializeFileSystem(const std::wstring& path);
|
||||||
|
|
||||||
|
std::unique_ptr<xe::kernel::fs::FileSystem> file_system_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xdb
|
} // namespace xdb
|
||||||
|
|
|
@ -9,19 +9,109 @@
|
||||||
|
|
||||||
#include <xdb/postmortem_debug_target.h>
|
#include <xdb/postmortem_debug_target.h>
|
||||||
|
|
||||||
|
#include <poly/poly.h>
|
||||||
|
#include <xenia/logging.h>
|
||||||
|
|
||||||
namespace xdb {
|
namespace xdb {
|
||||||
|
|
||||||
bool PostmortemDebugTarget::LoadTrace(const std::wstring& path) {
|
using xdb::protocol::EventType;
|
||||||
// TODO(benvanik): memory map trace.
|
|
||||||
return true;
|
// 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) {
|
bool PostmortemDebugTarget::LoadTrace(const std::wstring& path,
|
||||||
// If no path is provided attempt to infer from the trace.
|
const std::wstring& content_path) {
|
||||||
if (path.empty()) {
|
file_ = CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||||
// TODO(benvanik): find process info block and read source path.
|
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<const uint8_t*>(
|
||||||
|
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<EventType>(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<uint8_t>(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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +122,96 @@ bool PostmortemDebugTarget::Prepare() {
|
||||||
|
|
||||||
bool PostmortemDebugTarget::Prepare(std::atomic<bool>& cancelled) {
|
bool PostmortemDebugTarget::Prepare(std::atomic<bool>& cancelled) {
|
||||||
// TODO(benvanik): scan file, build indicies, etc.
|
// TODO(benvanik): scan file, build indicies, etc.
|
||||||
|
|
||||||
|
const uint8_t* ptr = trace_base_ + 8;
|
||||||
|
EventType event_type;
|
||||||
|
while ((event_type = poly::load<EventType>(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<uint8_t>(event_type)];
|
||||||
|
};
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,22 +10,34 @@
|
||||||
#ifndef XDB_POSTMORTEM_DEBUG_TARGET_H_
|
#ifndef XDB_POSTMORTEM_DEBUG_TARGET_H_
|
||||||
#define XDB_POSTMORTEM_DEBUG_TARGET_H_
|
#define XDB_POSTMORTEM_DEBUG_TARGET_H_
|
||||||
|
|
||||||
|
// TODO(benvanik): abstract mapping type.
|
||||||
|
#include <Windows.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <xdb/debug_target.h>
|
#include <xdb/debug_target.h>
|
||||||
|
#include <xdb/protocol.h>
|
||||||
|
|
||||||
namespace xdb {
|
namespace xdb {
|
||||||
|
|
||||||
class PostmortemDebugTarget : public DebugTarget {
|
class PostmortemDebugTarget : public DebugTarget {
|
||||||
public:
|
public:
|
||||||
PostmortemDebugTarget() = default;
|
PostmortemDebugTarget();
|
||||||
|
~PostmortemDebugTarget() override;
|
||||||
|
|
||||||
bool LoadTrace(const std::wstring& path);
|
bool LoadTrace(const std::wstring& path,
|
||||||
bool LoadContent(const std::wstring& path = L"");
|
const std::wstring& content_path = L"");
|
||||||
|
|
||||||
bool Prepare();
|
bool Prepare();
|
||||||
bool Prepare(std::atomic<bool>& cancelled);
|
bool Prepare(std::atomic<bool>& 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
|
} // namespace xdb
|
||||||
|
|
|
@ -23,7 +23,7 @@ using vec128_t = alloy::vec128_t;
|
||||||
#pragma pack(push, 4)
|
#pragma pack(push, 4)
|
||||||
|
|
||||||
enum class EventType : uint8_t {
|
enum class EventType : uint8_t {
|
||||||
SETUP,
|
END_OF_STREAM = 0,
|
||||||
|
|
||||||
PROCESS_START,
|
PROCESS_START,
|
||||||
PROCESS_EXIT,
|
PROCESS_EXIT,
|
||||||
|
@ -61,15 +61,16 @@ struct Event {
|
||||||
sizeof(T), reinterpret_cast<uint64_t*>(trace_base));
|
sizeof(T), reinterpret_cast<uint64_t*>(trace_base));
|
||||||
return reinterpret_cast<T*>(ptr);
|
return reinterpret_cast<T*>(ptr);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
struct SetupEvent : public Event<SetupEvent> {
|
static const T* Get(const void* ptr) {
|
||||||
EventType type;
|
return reinterpret_cast<const T*>(ptr);
|
||||||
uint64_t membase;
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ProcessStartEvent : public Event<ProcessStartEvent> {
|
struct ProcessStartEvent : public Event<ProcessStartEvent> {
|
||||||
EventType type;
|
EventType type;
|
||||||
|
uint64_t membase;
|
||||||
|
char launch_path[256];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ProcessExitEvent : public Event<ProcessExitEvent> {
|
struct ProcessExitEvent : public Event<ProcessExitEvent> {
|
||||||
|
|
|
@ -10,10 +10,9 @@
|
||||||
#include <xdb/ui/xdb_app.h>
|
#include <xdb/ui/xdb_app.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <codecvt>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include <poly/assert.h>
|
#include <poly/poly.h>
|
||||||
#include <xdb/postmortem_debug_target.h>
|
#include <xdb/postmortem_debug_target.h>
|
||||||
#include <xdb/ui/main_frame.h>
|
#include <xdb/ui/main_frame.h>
|
||||||
#include <xdb/ui/open_postmortem_trace_dialog.h>
|
#include <xdb/ui/open_postmortem_trace_dialog.h>
|
||||||
|
@ -46,25 +45,19 @@ void XdbApp::OpenEmpty() {
|
||||||
|
|
||||||
bool XdbApp::OpenTraceFile(const std::string& trace_file_path,
|
bool XdbApp::OpenTraceFile(const std::string& trace_file_path,
|
||||||
const std::string& content_file_path) {
|
const std::string& content_file_path) {
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
return OpenTraceFile(poly::to_wstring(trace_file_path),
|
||||||
return OpenTraceFile(converter.from_bytes(trace_file_path),
|
poly::to_wstring(content_file_path));
|
||||||
converter.from_bytes(content_file_path));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XdbApp::OpenTraceFile(const std::wstring& trace_file_path,
|
bool XdbApp::OpenTraceFile(const std::wstring& trace_file_path,
|
||||||
const std::wstring& content_file_path) {
|
const std::wstring& content_file_path) {
|
||||||
std::unique_ptr<PostmortemDebugTarget> target(new PostmortemDebugTarget());
|
std::unique_ptr<PostmortemDebugTarget> target(new PostmortemDebugTarget());
|
||||||
|
|
||||||
if (!target->LoadTrace(trace_file_path)) {
|
if (!target->LoadTrace(trace_file_path, content_file_path)) {
|
||||||
HandleOpenError("Unable to load trace file.");
|
HandleOpenError("Unable to load trace file.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!target->LoadContent(content_file_path)) {
|
|
||||||
HandleOpenError("Unable to load source game content module.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxProgressDialog progress_dialog(
|
wxProgressDialog progress_dialog(
|
||||||
"Preparing trace...", "This may take some time.", 100, nullptr,
|
"Preparing trace...", "This may take some time.", 100, nullptr,
|
||||||
wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_CAN_ABORT |
|
wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_CAN_ABORT |
|
||||||
|
|
|
@ -9,8 +9,7 @@
|
||||||
|
|
||||||
#include <xenia/debug_agent.h>
|
#include <xenia/debug_agent.h>
|
||||||
|
|
||||||
#include <codecvt>
|
#include <poly/string.h>
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
DEFINE_string(trace_file, "", "Trace to the given file.");
|
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) {
|
int DebugAgent::SetupTracing(const std::string& trace_file, uint64_t capacity) {
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cvt;
|
auto file_path = poly::to_wstring(trace_file);
|
||||||
auto file_path = cvt.from_bytes(trace_file);
|
|
||||||
file_ = CreateFile(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
file_ = CreateFile(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS,
|
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS,
|
||||||
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY,
|
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY,
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <xenia/emulator.h>
|
#include <xenia/emulator.h>
|
||||||
|
|
||||||
|
#include <poly/poly.h>
|
||||||
#include <xdb/protocol.h>
|
#include <xdb/protocol.h>
|
||||||
#include <xenia/apu/apu.h>
|
#include <xenia/apu/apu.h>
|
||||||
#include <xenia/cpu/cpu.h>
|
#include <xenia/cpu/cpu.h>
|
||||||
|
@ -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
|
// We create a virtual filesystem pointing to its directory and symlink
|
||||||
// that to the game filesystem.
|
// that to the game filesystem.
|
||||||
// e.g., /my/files/foo.xex will get a local fs at:
|
// 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
|
// and then get that symlinked to game:\, so
|
||||||
// -> game:\foo.xex
|
// -> game:\foo.xex
|
||||||
|
|
||||||
auto ev = xdb::protocol::ProcessStartEvent::Append(memory()->trace_base());
|
int result_code =
|
||||||
if (ev) {
|
file_system_->InitializeFromPath(FileSystemType::XEX_FILE, path);
|
||||||
ev->type = xdb::protocol::EventType::PROCESS_START;
|
if (result_code) {
|
||||||
|
return X_STATUS_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
int result_code = 0;
|
|
||||||
|
|
||||||
// Get just the filename (foo.xex).
|
// Get just the filename (foo.xex).
|
||||||
const xechar_t* file_name = xestrrchr(path, poly::path_separator);
|
std::wstring file_name;
|
||||||
if (file_name) {
|
auto last_slash = path.find_last_of(poly::path_separator);
|
||||||
// Skip slash.
|
if (last_slash == std::string::npos) {
|
||||||
file_name++;
|
|
||||||
} else {
|
|
||||||
// No slash found, whole thing is a file.
|
// No slash found, whole thing is a file.
|
||||||
file_name = path;
|
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.
|
// 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) {
|
X_STATUS Emulator::LaunchDiscImage(const std::wstring& path) {
|
||||||
int result_code = 0;
|
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());
|
auto ev = xdb::protocol::ProcessStartEvent::Append(memory()->trace_base());
|
||||||
if (ev) {
|
if (ev) {
|
||||||
ev->type = xdb::protocol::EventType::PROCESS_START;
|
ev->type = xdb::protocol::EventType::PROCESS_START;
|
||||||
|
ev->membase = reinterpret_cast<uint64_t>(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.
|
return xboxkrnl_->LaunchModule(module_path.c_str());
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#ifndef XENIA_EMULATOR_H_
|
#ifndef XENIA_EMULATOR_H_
|
||||||
#define XENIA_EMULATOR_H_
|
#define XENIA_EMULATOR_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <xenia/common.h>
|
#include <xenia/common.h>
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
#include <xenia/debug_agent.h>
|
#include <xenia/debug_agent.h>
|
||||||
|
@ -32,7 +34,7 @@ XEDECLARECLASS2(xe, ui, Window);
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
||||||
class Emulator {
|
class Emulator {
|
||||||
public:
|
public:
|
||||||
Emulator(const xechar_t* command_line);
|
Emulator(const xechar_t* command_line);
|
||||||
~Emulator();
|
~Emulator();
|
||||||
|
|
||||||
|
@ -59,11 +61,14 @@ public:
|
||||||
X_STATUS Setup();
|
X_STATUS Setup();
|
||||||
|
|
||||||
// TODO(benvanik): raw binary.
|
// TODO(benvanik): raw binary.
|
||||||
X_STATUS LaunchXexFile(const xechar_t* path);
|
X_STATUS LaunchXexFile(const std::wstring& path);
|
||||||
X_STATUS LaunchDiscImage(const xechar_t* path);
|
X_STATUS LaunchDiscImage(const std::wstring& path);
|
||||||
X_STATUS LaunchSTFSTitle(const xechar_t* 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];
|
xechar_t command_line_[poly::max_path];
|
||||||
|
|
||||||
ui::Window* main_window_;
|
ui::Window* main_window_;
|
||||||
|
|
|
@ -9,20 +9,12 @@
|
||||||
|
|
||||||
#include <xenia/kernel/fs/device.h>
|
#include <xenia/kernel/fs/device.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
using namespace xe::kernel::fs;
|
using namespace xe::kernel::fs;
|
||||||
|
|
||||||
|
Device::Device(const std::string& path) : path_(path) {}
|
||||||
|
|
||||||
Device::Device(const char* path) {
|
Device::~Device() {}
|
||||||
path_ = xestrdupa(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Device::~Device() {
|
const char* Device::path() const { return path_.c_str(); }
|
||||||
xe_free(path_);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* Device::path() {
|
|
||||||
return path_;
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,37 +10,36 @@
|
||||||
#ifndef XENIA_KERNEL_FS_DEVICE_H_
|
#ifndef XENIA_KERNEL_FS_DEVICE_H_
|
||||||
#define XENIA_KERNEL_FS_DEVICE_H_
|
#define XENIA_KERNEL_FS_DEVICE_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <xenia/common.h>
|
#include <xenia/common.h>
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
|
|
||||||
#include <xenia/kernel/fs/entry.h>
|
#include <xenia/kernel/fs/entry.h>
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace fs {
|
namespace fs {
|
||||||
|
|
||||||
|
|
||||||
class Device {
|
class Device {
|
||||||
public:
|
public:
|
||||||
Device(const char* path);
|
Device(const std::string& path);
|
||||||
virtual ~Device();
|
virtual ~Device();
|
||||||
|
|
||||||
const char* path();
|
const char* path() const;
|
||||||
|
|
||||||
virtual Entry* ResolvePath(const char* path) = 0;
|
virtual Entry* ResolvePath(const char* path) = 0;
|
||||||
|
|
||||||
virtual X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length) = 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:
|
protected:
|
||||||
char* path_;
|
std::string path_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace fs
|
} // namespace fs
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
#endif // XENIA_KERNEL_FS_DEVICE_H_
|
#endif // XENIA_KERNEL_FS_DEVICE_H_
|
||||||
|
|
|
@ -17,23 +17,17 @@ using namespace xe;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
using namespace xe::kernel::fs;
|
using namespace xe::kernel::fs;
|
||||||
|
|
||||||
|
DiscImageDevice::DiscImageDevice(const std::string& path,
|
||||||
|
const std::wstring& local_path)
|
||||||
DiscImageDevice::DiscImageDevice(const char* path, const xechar_t* local_path) :
|
: Device(path), local_path_(local_path), mmap_(nullptr), gdfx_(nullptr) {}
|
||||||
Device(path) {
|
|
||||||
local_path_ = xestrdup(local_path);
|
|
||||||
mmap_ = NULL;
|
|
||||||
gdfx_ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
DiscImageDevice::~DiscImageDevice() {
|
DiscImageDevice::~DiscImageDevice() {
|
||||||
delete gdfx_;
|
delete gdfx_;
|
||||||
xe_mmap_release(mmap_);
|
xe_mmap_release(mmap_);
|
||||||
xe_free(local_path_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int DiscImageDevice::Init() {
|
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_) {
|
if (!mmap_) {
|
||||||
XELOGE("Disc image could not be mapped");
|
XELOGE("Disc image could not be mapped");
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -10,42 +10,40 @@
|
||||||
#ifndef XENIA_KERNEL_FS_DEVICES_DISC_IMAGE_DEVICE_H_
|
#ifndef XENIA_KERNEL_FS_DEVICES_DISC_IMAGE_DEVICE_H_
|
||||||
#define XENIA_KERNEL_FS_DEVICES_DISC_IMAGE_DEVICE_H_
|
#define XENIA_KERNEL_FS_DEVICES_DISC_IMAGE_DEVICE_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <xenia/common.h>
|
#include <xenia/common.h>
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
|
|
||||||
#include <xenia/kernel/fs/device.h>
|
#include <xenia/kernel/fs/device.h>
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace fs {
|
namespace fs {
|
||||||
|
|
||||||
|
|
||||||
class GDFX;
|
class GDFX;
|
||||||
|
|
||||||
|
|
||||||
class DiscImageDevice : public Device {
|
class DiscImageDevice : public Device {
|
||||||
public:
|
public:
|
||||||
DiscImageDevice(const char* path, const xechar_t* local_path);
|
DiscImageDevice(const std::string& path, const std::wstring& local_path);
|
||||||
virtual ~DiscImageDevice();
|
~DiscImageDevice() override;
|
||||||
|
|
||||||
int Init();
|
int Init();
|
||||||
|
|
||||||
virtual Entry* ResolvePath(const char* path);
|
Entry* ResolvePath(const char* path) override;
|
||||||
|
|
||||||
virtual X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length);
|
X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length) override;
|
||||||
virtual X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info, size_t length);
|
X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info,
|
||||||
|
size_t length) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
xechar_t* local_path_;
|
std::wstring local_path_;
|
||||||
xe_mmap_ref mmap_;
|
xe_mmap_ref mmap_;
|
||||||
GDFX* gdfx_;
|
GDFX* gdfx_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace fs
|
} // namespace fs
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
#endif // XENIA_KERNEL_FS_DEVICES_DISC_IMAGE_DEVICE_H_
|
#endif // XENIA_KERNEL_FS_DEVICES_DISC_IMAGE_DEVICE_H_
|
||||||
|
|
|
@ -17,15 +17,11 @@ using namespace xe;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
using namespace xe::kernel::fs;
|
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) :
|
HostPathDevice::~HostPathDevice() {}
|
||||||
Device(path) {
|
|
||||||
local_path_ = xestrdup(local_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
HostPathDevice::~HostPathDevice() {
|
|
||||||
xe_free(local_path_);
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry* HostPathDevice::ResolvePath(const char* path) {
|
Entry* HostPathDevice::ResolvePath(const char* path) {
|
||||||
// The filesystem will have stripped our prefix off already, so the path will
|
// The filesystem will have stripped our prefix off already, so the path will
|
||||||
|
@ -42,7 +38,7 @@ Entry* HostPathDevice::ResolvePath(const char* path) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
xechar_t full_path[poly::max_path];
|
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.
|
// Swap around path separators.
|
||||||
if (poly::path_separator != '\\') {
|
if (poly::path_separator != '\\') {
|
||||||
|
|
|
@ -10,35 +10,34 @@
|
||||||
#ifndef XENIA_KERNEL_FS_DEVICES_HOST_PATH_DEVICE_H_
|
#ifndef XENIA_KERNEL_FS_DEVICES_HOST_PATH_DEVICE_H_
|
||||||
#define XENIA_KERNEL_FS_DEVICES_HOST_PATH_DEVICE_H_
|
#define XENIA_KERNEL_FS_DEVICES_HOST_PATH_DEVICE_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <xenia/common.h>
|
#include <xenia/common.h>
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
|
|
||||||
#include <xenia/kernel/fs/device.h>
|
#include <xenia/kernel/fs/device.h>
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace fs {
|
namespace fs {
|
||||||
|
|
||||||
|
|
||||||
class HostPathDevice : public Device {
|
class HostPathDevice : public Device {
|
||||||
public:
|
public:
|
||||||
HostPathDevice(const char* path, const xechar_t* local_path);
|
HostPathDevice(const std::string& path, const std::wstring& local_path);
|
||||||
virtual ~HostPathDevice();
|
~HostPathDevice() override;
|
||||||
|
|
||||||
virtual Entry* ResolvePath(const char* path);
|
Entry* ResolvePath(const char* path) override;
|
||||||
|
|
||||||
virtual X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length);
|
X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length) override;
|
||||||
virtual X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info, size_t length);
|
X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info,
|
||||||
|
size_t length) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
xechar_t* local_path_;
|
std::wstring local_path_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace fs
|
} // namespace fs
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
#endif // XENIA_KERNEL_FS_DEVICES_HOST_PATH_DEVICE_H_
|
#endif // XENIA_KERNEL_FS_DEVICES_HOST_PATH_DEVICE_H_
|
||||||
|
|
|
@ -17,24 +17,17 @@ using namespace xe;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
using namespace xe::kernel::fs;
|
using namespace xe::kernel::fs;
|
||||||
|
|
||||||
|
STFSContainerDevice::STFSContainerDevice(const std::string& path,
|
||||||
|
const std::wstring& local_path)
|
||||||
STFSContainerDevice::STFSContainerDevice(
|
: Device(path), local_path_(local_path), mmap_(nullptr), stfs_(nullptr) {}
|
||||||
const char* path, const xechar_t* local_path) :
|
|
||||||
Device(path) {
|
|
||||||
local_path_ = xestrdup(local_path);
|
|
||||||
mmap_ = NULL;
|
|
||||||
stfs_ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
STFSContainerDevice::~STFSContainerDevice() {
|
STFSContainerDevice::~STFSContainerDevice() {
|
||||||
delete stfs_;
|
delete stfs_;
|
||||||
xe_mmap_release(mmap_);
|
xe_mmap_release(mmap_);
|
||||||
xe_free(local_path_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int STFSContainerDevice::Init() {
|
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_) {
|
if (!mmap_) {
|
||||||
XELOGE("STFS container could not be mapped");
|
XELOGE("STFS container could not be mapped");
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -10,42 +10,40 @@
|
||||||
#ifndef XENIA_KERNEL_FS_DEVICES_STFS_CONTAINER_DEVICE_H_
|
#ifndef XENIA_KERNEL_FS_DEVICES_STFS_CONTAINER_DEVICE_H_
|
||||||
#define XENIA_KERNEL_FS_DEVICES_STFS_CONTAINER_DEVICE_H_
|
#define XENIA_KERNEL_FS_DEVICES_STFS_CONTAINER_DEVICE_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <xenia/common.h>
|
#include <xenia/common.h>
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
|
|
||||||
#include <xenia/kernel/fs/device.h>
|
#include <xenia/kernel/fs/device.h>
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace fs {
|
namespace fs {
|
||||||
|
|
||||||
|
|
||||||
class STFS;
|
class STFS;
|
||||||
|
|
||||||
|
|
||||||
class STFSContainerDevice : public Device {
|
class STFSContainerDevice : public Device {
|
||||||
public:
|
public:
|
||||||
STFSContainerDevice(const char* path, const xechar_t* local_path);
|
STFSContainerDevice(const std::string& path, const std::wstring& local_path);
|
||||||
virtual ~STFSContainerDevice();
|
~STFSContainerDevice() override;
|
||||||
|
|
||||||
int Init();
|
int Init();
|
||||||
|
|
||||||
virtual Entry* ResolvePath(const char* path);
|
Entry* ResolvePath(const char* path) override;
|
||||||
|
|
||||||
virtual X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length);
|
X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length) override;
|
||||||
virtual X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info, size_t length);
|
X_STATUS QueryFileSystemAttributes(XFileSystemAttributeInfo* out_info,
|
||||||
|
size_t length) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
xechar_t* local_path_;
|
std::wstring local_path_;
|
||||||
xe_mmap_ref mmap_;
|
xe_mmap_ref mmap_;
|
||||||
STFS* stfs_;
|
STFS* stfs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace fs
|
} // namespace fs
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
#endif // XENIA_KERNEL_FS_DEVICES_STFS_CONTAINER_DEVICE_H_
|
#endif // XENIA_KERNEL_FS_DEVICES_STFS_CONTAINER_DEVICE_H_
|
||||||
|
|
|
@ -33,19 +33,86 @@ FileSystem::~FileSystem() {
|
||||||
symlinks_.clear();
|
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);
|
devices_.push_back(device);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileSystem::RegisterHostPathDevice(
|
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);
|
Device* device = new HostPathDevice(path, local_path);
|
||||||
return RegisterDevice(path, device);
|
return RegisterDevice(path, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileSystem::RegisterDiscImageDevice(
|
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);
|
DiscImageDevice* device = new DiscImageDevice(path, local_path);
|
||||||
if (device->Init()) {
|
if (device->Init()) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -54,7 +121,7 @@ int FileSystem::RegisterDiscImageDevice(
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileSystem::RegisterSTFSContainerDevice(
|
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);
|
STFSContainerDevice* device = new STFSContainerDevice(path, local_path);
|
||||||
if (device->Init()) {
|
if (device->Init()) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#ifndef XENIA_KERNEL_FS_FILESYSTEM_H_
|
#ifndef XENIA_KERNEL_FS_FILESYSTEM_H_
|
||||||
#define XENIA_KERNEL_FS_FILESYSTEM_H_
|
#define XENIA_KERNEL_FS_FILESYSTEM_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -18,39 +19,46 @@
|
||||||
|
|
||||||
#include <xenia/kernel/fs/entry.h>
|
#include <xenia/kernel/fs/entry.h>
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace fs {
|
namespace fs {
|
||||||
|
|
||||||
|
|
||||||
class Device;
|
class Device;
|
||||||
|
|
||||||
|
enum class FileSystemType {
|
||||||
|
STFS_TITLE,
|
||||||
|
DISC_IMAGE,
|
||||||
|
XEX_FILE,
|
||||||
|
};
|
||||||
|
|
||||||
class FileSystem {
|
class FileSystem {
|
||||||
public:
|
public:
|
||||||
FileSystem();
|
FileSystem();
|
||||||
~FileSystem();
|
~FileSystem();
|
||||||
|
|
||||||
int RegisterDevice(const char* path, Device* device);
|
FileSystemType InferType(const std::wstring& local_path);
|
||||||
int RegisterHostPathDevice(const char* path, const xechar_t* local_path);
|
int InitializeFromPath(FileSystemType type, const std::wstring& local_path);
|
||||||
int RegisterDiscImageDevice(const char* path, const xechar_t* local_path);
|
|
||||||
int RegisterSTFSContainerDevice(const char* path, const xechar_t* 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 CreateSymbolicLink(const char* path, const char* target);
|
||||||
int DeleteSymbolicLink(const char* path);
|
int DeleteSymbolicLink(const char* path);
|
||||||
|
|
||||||
Entry* ResolvePath(const char* path);
|
Entry* ResolvePath(const char* path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Device*> devices_;
|
std::vector<Device*> devices_;
|
||||||
std::unordered_map<std::string, std::string> symlinks_;
|
std::unordered_map<std::string, std::string> symlinks_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace fs
|
} // namespace fs
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
#endif // XENIA_KERNEL_FS_FILESYSTEM_H_
|
#endif // XENIA_KERNEL_FS_FILESYSTEM_H_
|
||||||
|
|
|
@ -9,9 +9,7 @@
|
||||||
|
|
||||||
#include <xenia/kernel/xboxkrnl_rtl.h>
|
#include <xenia/kernel/xboxkrnl_rtl.h>
|
||||||
|
|
||||||
#include <locale>
|
#include <poly/string.h>
|
||||||
#include <codecvt>
|
|
||||||
|
|
||||||
#include <xenia/kernel/kernel_state.h>
|
#include <xenia/kernel/kernel_state.h>
|
||||||
#include <xenia/kernel/xboxkrnl_private.h>
|
#include <xenia/kernel/xboxkrnl_private.h>
|
||||||
#include <xenia/kernel/objects/xthread.h>
|
#include <xenia/kernel/objects/xthread.h>
|
||||||
|
@ -329,8 +327,7 @@ X_STATUS xeRtlUnicodeStringToAnsiString(uint32_t destination_ptr,
|
||||||
|
|
||||||
std::wstring unicode_str = poly::load_and_swap<std::wstring>(
|
std::wstring unicode_str = poly::load_and_swap<std::wstring>(
|
||||||
IMPL_MEM_ADDR(IMPL_MEM_32(source_ptr + 4)));
|
IMPL_MEM_ADDR(IMPL_MEM_32(source_ptr + 4)));
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
std::string ansi_str = poly::to_string(unicode_str);
|
||||||
std::string ansi_str = converter.to_bytes(unicode_str);
|
|
||||||
if (ansi_str.size() > 0xFFFF - 1) {
|
if (ansi_str.size() > 0xFFFF - 1) {
|
||||||
return X_STATUS_INVALID_PARAMETER_2;
|
return X_STATUS_INVALID_PARAMETER_2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,10 @@ int main(int argc, xechar_t** argv) {
|
||||||
|
|
||||||
if (!FLAGS_trace_file.empty()) {
|
if (!FLAGS_trace_file.empty()) {
|
||||||
// Trace file specified on command line.
|
// 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 {
|
} else {
|
||||||
app->OpenEmpty();
|
app->OpenEmpty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,10 +55,6 @@ int xenia_run(int argc, xechar_t** argv) {
|
||||||
xechar_t abs_path[poly::max_path];
|
xechar_t abs_path[poly::max_path];
|
||||||
xe_path_get_absolute(path, abs_path, XECOUNT(abs_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.
|
// Create the emulator.
|
||||||
emulator = new Emulator(L"");
|
emulator = new Emulator(L"");
|
||||||
XEEXPECTNOTNULL(emulator);
|
XEEXPECTNOTNULL(emulator);
|
||||||
|
@ -70,16 +66,17 @@ int xenia_run(int argc, xechar_t** argv) {
|
||||||
|
|
||||||
// Launch based on file type.
|
// Launch based on file type.
|
||||||
// This is a silly guess based on file extension.
|
// This is a silly guess based on file extension.
|
||||||
// NOTE: the runtime launch routine will wait until the module exits.
|
auto file_system_type = emulator->file_system()->InferType(abs_path);
|
||||||
if (!dot) {
|
switch (file_system_type) {
|
||||||
// Likely an STFS container.
|
case kernel::fs::FileSystemType::STFS_TITLE:
|
||||||
result = emulator->LaunchSTFSTitle(abs_path);
|
result = emulator->LaunchSTFSTitle(abs_path);
|
||||||
} else if (xestrcmp(dot, L".xex") == 0) {
|
break;
|
||||||
// Treat as a naked xex file.
|
case kernel::fs::FileSystemType::XEX_FILE:
|
||||||
result = emulator->LaunchXexFile(abs_path);
|
result = emulator->LaunchXexFile(abs_path);
|
||||||
} else {
|
break;
|
||||||
// Assume a disc image.
|
case kernel::fs::FileSystemType::DISC_IMAGE:
|
||||||
result = emulator->LaunchDiscImage(abs_path);
|
result = emulator->LaunchDiscImage(abs_path);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (XFAILED(result)) {
|
if (XFAILED(result)) {
|
||||||
XELOGE("Failed to launch target: %.8X", result);
|
XELOGE("Failed to launch target: %.8X", result);
|
||||||
|
|
Loading…
Reference in New Issue