From f78fdba9c3b50128fd5d19256848b406b499f63c Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Thu, 31 Jan 2013 16:52:50 -0800 Subject: [PATCH] Wiring up xex loading via the new virtual filesystem. File resolution is hacked, but works well enough for testing. --- include/xenia/config.h | 1 + include/xenia/kernel/fs/device.h | 8 +- include/xenia/kernel/fs/entry.h | 21 ++++- include/xenia/kernel/fs/filesystem.h | 6 +- include/xenia/kernel/runtime.h | 5 +- include/xenia/kernel/xbox.h | 1 + include/xenia/logging.h | 5 ++ include/xenia/string.h | 8 +- src/cpu/codegen/module_generator.cc | 2 +- src/cpu/exec_module.cc | 2 +- src/cpu/processor.cc | 4 +- src/kernel/fs/device.cc | 12 ++- src/kernel/fs/devices/disc_image_device.cc | 11 ++- src/kernel/fs/devices/disc_image_device.h | 2 +- .../fs/devices/local_directory_device.cc | 88 ++++++++++++++++++- .../fs/devices/local_directory_device.h | 3 +- src/kernel/fs/entry.cc | 16 ++++ src/kernel/fs/filesystem.cc | 66 +++++++++++++- src/kernel/modules/xboxkrnl/kernel_state.cc | 6 ++ src/kernel/modules/xboxkrnl/kernel_state.h | 3 + src/kernel/modules/xboxkrnl/module.cc | 36 +++----- src/kernel/modules/xboxkrnl/module.h | 2 +- .../modules/xboxkrnl/objects/xmodule.cc | 37 ++++++-- src/kernel/modules/xboxkrnl/objects/xmodule.h | 2 +- src/kernel/runtime.cc | 77 +++++++++++++++- tools/xenia-run/xenia-run.cc | 28 +++++- 26 files changed, 392 insertions(+), 60 deletions(-) diff --git a/include/xenia/config.h b/include/xenia/config.h index 10d26feb0..aaa05aca8 100644 --- a/include/xenia/config.h +++ b/include/xenia/config.h @@ -26,6 +26,7 @@ #define XE_OPTION_LOG_SDB 1 #define XE_OPTION_LOG_GPU 1 #define XE_OPTION_LOG_KERNEL 1 +#define XE_OPTION_LOG_FS 1 // TODO(benvanik): make this a runtime option diff --git a/include/xenia/kernel/fs/device.h b/include/xenia/kernel/fs/device.h index 9925a16e5..adeac566f 100644 --- a/include/xenia/kernel/fs/device.h +++ b/include/xenia/kernel/fs/device.h @@ -23,13 +23,17 @@ namespace fs { class Device { public: - Device(xe_pal_ref pal); + Device(xe_pal_ref pal, const char* path); virtual ~Device(); + xe_pal_ref pal(); + const char* path(); + virtual Entry* ResolvePath(const char* path) = 0; protected: - xe_pal_ref pal_; + xe_pal_ref pal_; + char* path_; }; diff --git a/include/xenia/kernel/fs/entry.h b/include/xenia/kernel/fs/entry.h index 3c4359e9b..a9e0e49a5 100644 --- a/include/xenia/kernel/fs/entry.h +++ b/include/xenia/kernel/fs/entry.h @@ -45,12 +45,29 @@ private: }; +class MemoryMapping { +public: + MemoryMapping(uint8_t* address, size_t length); + virtual ~MemoryMapping(); + + uint8_t* address(); + size_t length(); + +private: + uint8_t* address_; + size_t length_; +}; + + class FileEntry : public Entry { public: FileEntry(Device* device, const char* path); virtual ~FileEntry(); - //virtual void Query(); + //virtual void Query() = 0; + + virtual MemoryMapping* CreateMemoryMapping( + xe_file_mode file_mode, const size_t offset, const size_t length) = 0; }; @@ -59,7 +76,7 @@ public: DirectoryEntry(Device* device, const char* path); virtual ~DirectoryEntry(); - //virtual void Query(); + //virtual void Query() = 0; }; diff --git a/include/xenia/kernel/fs/filesystem.h b/include/xenia/kernel/fs/filesystem.h index fc85b059d..e9f6ea619 100644 --- a/include/xenia/kernel/fs/filesystem.h +++ b/include/xenia/kernel/fs/filesystem.h @@ -13,6 +13,8 @@ #include #include +#include + #include @@ -40,7 +42,9 @@ public: Entry* ResolvePath(const char* path); private: - xe_pal_ref pal_; + xe_pal_ref pal_; + std::vector devices_; + std::tr1::unordered_map symlinks_; }; diff --git a/include/xenia/kernel/runtime.h b/include/xenia/kernel/runtime.h index 30bca2910..9ffd820c4 100644 --- a/include/xenia/kernel/runtime.h +++ b/include/xenia/kernel/runtime.h @@ -54,10 +54,11 @@ public: shared_ptr export_resolver(); shared_ptr filesystem(); - int LaunchModule(const xechar_t* path); + int LaunchXexFile(const xechar_t* path); + int LaunchDiscImage(const xechar_t* path); private: - xechar_t command_line_[2048]; + xechar_t command_line_[XE_MAX_PATH]; xe_pal_ref pal_; xe_memory_ref memory_; diff --git a/include/xenia/kernel/xbox.h b/include/xenia/kernel/xbox.h index d0c3131f9..66d807f40 100644 --- a/include/xenia/kernel/xbox.h +++ b/include/xenia/kernel/xbox.h @@ -34,6 +34,7 @@ typedef uint32_t X_STATUS; #define X_STATUS_ACCESS_VIOLATION ((uint32_t)0xC0000005L) #define X_STATUS_INVALID_HANDLE ((uint32_t)0xC0000008L) #define X_STATUS_INVALID_PARAMETER ((uint32_t)0xC000000DL) +#define X_STATUS_NO_SUCH_FILE ((uint32_t)0xC000000FL) #define X_STATUS_NO_MEMORY ((uint32_t)0xC0000017L) #define X_STATUS_ALREADY_COMMITTED ((uint32_t)0xC0000021L) #define X_STATUS_ACCESS_DENIED ((uint32_t)0xC0000022L) diff --git a/include/xenia/logging.h b/include/xenia/logging.h index 5e26ee80e..72e32bc15 100644 --- a/include/xenia/logging.h +++ b/include/xenia/logging.h @@ -74,6 +74,11 @@ void xe_log_line(const xechar_t* file_path, const uint32_t line_number, #else #define XELOGKERNEL(fmt, ...) XE_EMPTY_MACRO #endif +#if XE_OPTION(LOG_FS) +#define XELOGFS(fmt, ...) XELOGCORE('F', fmt, ##__VA_ARGS__) +#else +#define XELOGFS(fmt, ...) XE_EMPTY_MACRO +#endif #endif // XENIA_LOGGING_H_ diff --git a/include/xenia/string.h b/include/xenia/string.h index 15f97e24d..1d91787e2 100644 --- a/include/xenia/string.h +++ b/include/xenia/string.h @@ -31,7 +31,7 @@ int strncpy_s(char* dest, size_t destLength, const char* source, size_t count); #define xestrdupa _strdup #define xestrtoullw _wcstoui64 #define xestrtoulla _strtoui64 -#endif +#endif // !WIN32 #define xestrlenw wcslen #define xestrcmpw wcscmp @@ -40,6 +40,7 @@ int strncpy_s(char* dest, size_t destLength, const char* source, size_t count); #define xestrchrw wcschr #define xestrrchrw wcsrchr #define xestrstrw wcsstr +#define xestrcasestrw ?? #define xestrcpyw(dest, destLength, source) (wcscpy_s(dest, destLength, source) == 0) #define xestrncpyw(dest, destLength, source, count) (wcsncpy_s(dest, destLength, source, count) == 0) #define xestrcatw(dest, destLength, source) (wcscat_s(dest, destLength, source) == 0) @@ -53,6 +54,7 @@ int strncpy_s(char* dest, size_t destLength, const char* source, size_t count); #define xestrchra strchr #define xestrrchra strrchr #define xestrstra strstr +#define xestrcasestra strcasestr #define xestrcpya(dest, destLength, source) (strcpy_s(dest, destLength, source) == 0) #define xestrncpya(dest, destLength, source, count) (strncpy_s(dest, destLength, source, count) == 0) #define xestrcata(dest, destLength, source) (strcat_s(dest, destLength, source) == 0) @@ -72,6 +74,7 @@ typedef wchar_t xechar_t; #define xestrchr xestrchrw #define xestrrchr xestrrchrw #define xestrstr xestrstrw +#define xestrcasestr xestrcasestrw #define xestrtoull xestrtoullw #define xestrcpy xestrcpyw #define xestrncpy xestrncpyw @@ -94,6 +97,7 @@ typedef char xechar_t; #define xestrchr xestrchra #define xestrrchr xestrrchra #define xestrstr xestrstra +#define xestrcasestr xestrcasestra #define xestrtoull xestrtoulla #define xestrcpy xestrcpya #define xestrncpy xestrncpya @@ -109,8 +113,10 @@ typedef char xechar_t; #if XE_LIKE(WIN32) #define XE_PATH_SEPARATOR ((xechar_t)'\\') +#define XE_MAX_PATH _MAX_PATH #else #define XE_PATH_SEPARATOR ((xechar_t)'/') +#define XE_MAX_PATH PATH_MAX #endif // WIN32 #endif // XENIA_STRING_H_ diff --git a/src/cpu/codegen/module_generator.cc b/src/cpu/codegen/module_generator.cc index f1a0343e5..07bf03ecd 100644 --- a/src/cpu/codegen/module_generator.cc +++ b/src/cpu/codegen/module_generator.cc @@ -71,7 +71,7 @@ int ModuleGenerator::Generate() { // Setup a debug info builder. // This is used when creating any debug info. We may want to go more // fine grained than this, but for now it's something. - char dir[2048]; + char dir[XE_MAX_PATH]; XEIGNORE(xestrcpya(dir, XECOUNT(dir), module_path_)); char* slash = xestrrchra(dir, '/'); if (slash) { diff --git a/src/cpu/exec_module.cc b/src/cpu/exec_module.cc index f4af6d0ad..60c9b471a 100644 --- a/src/cpu/exec_module.cc +++ b/src/cpu/exec_module.cc @@ -97,7 +97,7 @@ int ExecModule::Prepare() { int result_code = 1; std::string error_message; - char file_name[2048]; + char file_name[XE_MAX_PATH]; OwningPtr shared_module_buffer; auto_ptr shared_module; diff --git a/src/cpu/processor.cc b/src/cpu/processor.cc index 80f26fbd9..a1acd7981 100644 --- a/src/cpu/processor.cc +++ b/src/cpu/processor.cc @@ -133,9 +133,9 @@ int Processor::LoadBinary(const xechar_t* path, uint32_t start_address, addr, length)); // Prepare the module. - char name_a[2048]; + char name_a[XE_MAX_PATH]; XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name)); - char path_a[2048]; + char path_a[XE_MAX_PATH]; XEEXPECTTRUE(xestrnarrow(path_a, XECOUNT(path_a), path)); exec_module = new ExecModule( diff --git a/src/kernel/fs/device.cc b/src/kernel/fs/device.cc index 85d45a23b..094ecc986 100644 --- a/src/kernel/fs/device.cc +++ b/src/kernel/fs/device.cc @@ -15,10 +15,20 @@ using namespace xe::kernel; using namespace xe::kernel::fs; -Device::Device(xe_pal_ref pal) { +Device::Device(xe_pal_ref pal, const char* path) { pal_ = xe_pal_retain(pal); + path_ = xestrdupa(path); } Device::~Device() { + xe_free(path_); xe_pal_release(pal_); } + +xe_pal_ref Device::pal() { + return xe_pal_retain(pal_); +} + +const char* Device::path() { + return path_; +} diff --git a/src/kernel/fs/devices/disc_image_device.cc b/src/kernel/fs/devices/disc_image_device.cc index 68dfaf27f..9be525419 100644 --- a/src/kernel/fs/devices/disc_image_device.cc +++ b/src/kernel/fs/devices/disc_image_device.cc @@ -15,8 +15,9 @@ using namespace xe::kernel; using namespace xe::kernel::fs; -DiscImageDevice::DiscImageDevice(xe_pal_ref pal, const xechar_t* local_path) : - Device(pal) { +DiscImageDevice::DiscImageDevice(xe_pal_ref pal, const char* path, + const xechar_t* local_path) : + Device(pal, path) { local_path_ = xestrdup(local_path); } @@ -25,5 +26,11 @@ DiscImageDevice::~DiscImageDevice() { } Entry* DiscImageDevice::ResolvePath(const char* path) { + // The filesystem will have stripped our prefix off already, so the path will + // be in the form: + // some\PATH.foo + + XELOGFS(XT("DiscImageDevice::ResolvePath(%s)"), path); + return NULL; } diff --git a/src/kernel/fs/devices/disc_image_device.h b/src/kernel/fs/devices/disc_image_device.h index ffcc0a24f..bc443c3d1 100644 --- a/src/kernel/fs/devices/disc_image_device.h +++ b/src/kernel/fs/devices/disc_image_device.h @@ -23,7 +23,7 @@ namespace fs { class DiscImageDevice : public Device { public: - DiscImageDevice(xe_pal_ref pal, const xechar_t* local_path); + DiscImageDevice(xe_pal_ref pal, const char* path, const xechar_t* local_path); virtual ~DiscImageDevice(); virtual Entry* ResolvePath(const char* path); diff --git a/src/kernel/fs/devices/local_directory_device.cc b/src/kernel/fs/devices/local_directory_device.cc index ed0819c3f..deec5693f 100644 --- a/src/kernel/fs/devices/local_directory_device.cc +++ b/src/kernel/fs/devices/local_directory_device.cc @@ -15,9 +15,64 @@ using namespace xe::kernel; using namespace xe::kernel::fs; -LocalDirectoryDevice::LocalDirectoryDevice(xe_pal_ref pal, +namespace { + + +class LocalFileMemoryMapping : public MemoryMapping { +public: + LocalFileMemoryMapping(uint8_t* address, size_t length, xe_mmap_ref mmap) : + MemoryMapping(address, length) { + mmap_ = xe_mmap_retain(mmap); + } + virtual ~LocalFileMemoryMapping() { + xe_mmap_release(mmap_); + } +private: + xe_mmap_ref mmap_; +}; + + +class LocalFileEntry : public FileEntry { +public: + LocalFileEntry(Device* device, const char* path, const xechar_t* local_path) : + FileEntry(device, path) { + local_path_ = xestrdup(local_path); + } + virtual ~LocalFileEntry() { + xe_free(local_path_); + } + + const xechar_t* local_path() { return local_path_; } + + virtual MemoryMapping* CreateMemoryMapping( + xe_file_mode file_mode, const size_t offset, const size_t length) { + xe_pal_ref pal = device()->pal(); + xe_mmap_ref mmap = xe_mmap_open(pal, file_mode, local_path_, + offset, length); + xe_pal_release(pal); + if (!mmap) { + return NULL; + } + + LocalFileMemoryMapping* lfmm = new LocalFileMemoryMapping( + (uint8_t*)xe_mmap_get_addr(mmap), xe_mmap_get_length(mmap), + mmap); + xe_mmap_release(mmap); + + return lfmm; + } + +private: + xechar_t* local_path_; +}; + + +} + + +LocalDirectoryDevice::LocalDirectoryDevice(xe_pal_ref pal, const char* path, const xechar_t* local_path) : - Device(pal) { + Device(pal, path) { local_path_ = xestrdup(local_path); } @@ -26,5 +81,32 @@ LocalDirectoryDevice::~LocalDirectoryDevice() { } Entry* LocalDirectoryDevice::ResolvePath(const char* path) { - return NULL; + // The filesystem will have stripped our prefix off already, so the path will + // be in the form: + // some\PATH.foo + + XELOGFS(XT("LocalDirectoryDevice::ResolvePath(%s)"), path); + + xechar_t full_path[XE_MAX_PATH]; + xesnprintf(full_path, XECOUNT(full_path), XT("%s%c%s"), + local_path_, XE_PATH_SEPARATOR, path); + + // Swap around path separators. + if (XE_PATH_SEPARATOR != '\\') { + for (size_t n = 0; n < XECOUNT(full_path); n++) { + if (full_path[n] == 0) { + break; + } + if (full_path[n] == '\\') { + full_path[n] = XE_PATH_SEPARATOR; + } + } + } + + // TODO(benvanik): get file info + // TODO(benvanik): fail if does not exit + // TODO(benvanik): switch based on type + + LocalFileEntry* file_entry = new LocalFileEntry(this, path, full_path); + return file_entry; } diff --git a/src/kernel/fs/devices/local_directory_device.h b/src/kernel/fs/devices/local_directory_device.h index 4126183af..372fc464b 100644 --- a/src/kernel/fs/devices/local_directory_device.h +++ b/src/kernel/fs/devices/local_directory_device.h @@ -23,7 +23,8 @@ namespace fs { class LocalDirectoryDevice : public Device { public: - LocalDirectoryDevice(xe_pal_ref pal, const xechar_t* local_path); + LocalDirectoryDevice(xe_pal_ref pal, const char* path, + const xechar_t* local_path); virtual ~LocalDirectoryDevice(); virtual Entry* ResolvePath(const char* path); diff --git a/src/kernel/fs/entry.cc b/src/kernel/fs/entry.cc index 12ae28a2b..da8e5ed29 100644 --- a/src/kernel/fs/entry.cc +++ b/src/kernel/fs/entry.cc @@ -45,6 +45,22 @@ const char* Entry::name() { } +MemoryMapping::MemoryMapping(uint8_t* address, size_t length) : + address_(address), length_(length) { +} + +MemoryMapping::~MemoryMapping() { +} + +uint8_t* MemoryMapping::address() { + return address_; +} + +size_t MemoryMapping::length() { + return length_; +} + + FileEntry::FileEntry(Device* device, const char* path) : Entry(kTypeFile, device, path) { } diff --git a/src/kernel/fs/filesystem.cc b/src/kernel/fs/filesystem.cc index 80c949667..586ede1d8 100644 --- a/src/kernel/fs/filesystem.cc +++ b/src/kernel/fs/filesystem.cc @@ -23,33 +23,91 @@ FileSystem::FileSystem(xe_pal_ref pal) { } FileSystem::~FileSystem() { + // Delete all devices. + // This will explode if anyone is still using data from them. + for (std::vector::iterator it = devices_.begin(); + it != devices_.end(); ++it) { + delete *it; + } + devices_.clear(); + symlinks_.clear(); + xe_pal_release(pal_); } int FileSystem::RegisterDevice(const char* path, Device* device) { - return 1; + devices_.push_back(device); + return 0; } int FileSystem::RegisterLocalDirectoryDevice( const char* path, const xechar_t* local_path) { - Device* device = new LocalDirectoryDevice(pal_, local_path); + Device* device = new LocalDirectoryDevice(pal_, path, local_path); return RegisterDevice(path, device); } int FileSystem::RegisterDiscImageDevice( const char* path, const xechar_t* local_path) { - Device* device = new DiscImageDevice(pal_, local_path); + Device* device = new DiscImageDevice(pal_, path, local_path); return RegisterDevice(path, device); } int FileSystem::CreateSymbolicLink(const char* path, const char* target) { - return 1; + symlinks_.insert(std::pair( + xestrdupa(path), + xestrdupa(target))); + return 0; } int FileSystem::DeleteSymbolicLink(const char* path) { + std::tr1::unordered_map::iterator it = + symlinks_.find(std::string(path)); + if (it != symlinks_.end()) { + symlinks_.erase(it); + return 0; + } return 1; } Entry* FileSystem::ResolvePath(const char* path) { + // Strip off prefix and pass to device. + // e.g., d:\some\PATH.foo -> some\PATH.foo + // Support both symlinks and device specifiers, like: + // \\Device\Foo\some\PATH.foo, d:\some\PATH.foo, etc. + + // TODO(benvanik): normalize path/etc + // e.g., remove ..'s and such + + // Resolve symlinks. + // TODO(benvanik): more robust symlink handling - right now we assume simple + // drive path -> device mappings with nothing nested. + char full_path[XE_MAX_PATH]; + XEIGNORE(xestrcpya(full_path, XECOUNT(full_path), path)); + for (std::tr1::unordered_map::iterator it = + symlinks_.begin(); it != symlinks_.end(); ++it) { + if (xestrcasestra(path, it->first.c_str()) == path) { + // Found symlink, fixup. + const char* after_path = path + it->first.size(); + XEIGNORE(xesnprintf(full_path, XECOUNT(full_path), "%s%s", + it->second.c_str(), after_path)); + break; + } + } + + // Scan all devices. + for (std::vector::iterator it = devices_.begin(); + it != devices_.end(); ++it) { + Device* device = *it; + if (xestrcasestra(full_path, device->path()) == full_path) { + // Found! + // Trim the device prefix off and pass down. + char device_path[XE_MAX_PATH]; + XEIGNORE(xestrcpya(device_path, XECOUNT(device_path), + full_path + xestrlena(device->path()))); + return device->ResolvePath(device_path); + } + } + + XELOGE(XT("ResolvePath(%s) failed - no root found"), path); return NULL; } diff --git a/src/kernel/modules/xboxkrnl/kernel_state.cc b/src/kernel/modules/xboxkrnl/kernel_state.cc index d9c7facde..374f7eb71 100644 --- a/src/kernel/modules/xboxkrnl/kernel_state.cc +++ b/src/kernel/modules/xboxkrnl/kernel_state.cc @@ -31,6 +31,7 @@ KernelState::KernelState(Runtime* runtime) : pal_ = runtime->pal(); memory_ = runtime->memory(); processor_ = runtime->processor(); + filesystem_ = runtime->filesystem(); objects_mutex_ = xe_mutex_alloc(0); XEASSERTNOTNULL(objects_mutex_); @@ -65,6 +66,7 @@ KernelState::~KernelState() { xe_mutex_free(objects_mutex_); objects_mutex_ = NULL; + filesystem_.reset(); processor_.reset(); xe_memory_release(memory_); xe_pal_release(pal_); @@ -86,6 +88,10 @@ cpu::Processor* KernelState::processor() { return processor_.get(); } +fs::FileSystem* KernelState::filesystem() { + return filesystem_.get(); +} + // TODO(benvanik): invesitgate better handle storage/structure. // A much better way of doing handles, if performance becomes an issue, would // be to try to make the pointers 32bit. Then we could round-trip them through diff --git a/src/kernel/modules/xboxkrnl/kernel_state.h b/src/kernel/modules/xboxkrnl/kernel_state.h index 475b6e4f1..38e580fa3 100644 --- a/src/kernel/modules/xboxkrnl/kernel_state.h +++ b/src/kernel/modules/xboxkrnl/kernel_state.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace xe { @@ -37,6 +38,7 @@ public: xe_pal_ref pal(); xe_memory_ref memory(); cpu::Processor* processor(); + fs::FileSystem* filesystem(); XObject* GetObject(X_HANDLE handle); @@ -52,6 +54,7 @@ private: xe_pal_ref pal_; xe_memory_ref memory_; shared_ptr processor_; + shared_ptr filesystem_; XModule* executable_module_; diff --git a/src/kernel/modules/xboxkrnl/module.cc b/src/kernel/modules/xboxkrnl/module.cc index fa2850095..b764f8a86 100644 --- a/src/kernel/modules/xboxkrnl/module.cc +++ b/src/kernel/modules/xboxkrnl/module.cc @@ -104,36 +104,24 @@ XboxkrnlModule::XboxkrnlModule(Runtime* runtime) : XboxkrnlModule::~XboxkrnlModule() { } -int XboxkrnlModule::LaunchModule(const xechar_t* path) { - // TODO(benvanik): setup the virtual filesystem map and use LoadFromFile. +int XboxkrnlModule::LaunchModule(const char* path) { + // Create and register the module. We keep it local to this function and + // dispose it on exit. + XModule* module = new XModule(kernel_state_.get(), path); - xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0); - if (!mmap) { - return NULL; - } - void* addr = xe_mmap_get_addr(mmap); - size_t length = xe_mmap_get_length(mmap); - - char path_a[2048]; - XEIGNORE(xestrnarrow(path_a, XECOUNT(path_a), path)); - - // Create and load the module. - XModule* module = new XModule(kernel_state_.get(), path_a); - X_STATUS status = module->LoadFromMemory(addr, length); - - // TODO(benvanik): retain memory somehow? is it needed? - xe_mmap_release(mmap); - - if (XFAILED(status)) { - XELOGE(XT("Failed to load module")); + // Load the module into memory from the filesystem. + X_STATUS result_code = module->LoadFromFile(path); + if (XFAILED(result_code)) { + XELOGE(XT("Failed to load module %s: %.8X"), path, result_code); module->Release(); return 1; } // Launch the module. - status = module->Launch(0); - if (XFAILED(status)) { - XELOGE(XT("Failed to launch module")); + // NOTE: this won't return until the module exits. + result_code = module->Launch(0); + if (XFAILED(result_code)) { + XELOGE(XT("Failed to launch module %s: %.8X"), path, result_code); module->Release(); return 2; } diff --git a/src/kernel/modules/xboxkrnl/module.h b/src/kernel/modules/xboxkrnl/module.h index 86562b3c9..627d30fcc 100644 --- a/src/kernel/modules/xboxkrnl/module.h +++ b/src/kernel/modules/xboxkrnl/module.h @@ -29,7 +29,7 @@ public: XboxkrnlModule(Runtime* runtime); virtual ~XboxkrnlModule(); - int LaunchModule(const xechar_t* path); + int LaunchModule(const char* path); private: auto_ptr kernel_state_; diff --git a/src/kernel/modules/xboxkrnl/objects/xmodule.cc b/src/kernel/modules/xboxkrnl/objects/xmodule.cc index 5809fca11..7246ba813 100644 --- a/src/kernel/modules/xboxkrnl/objects/xmodule.cc +++ b/src/kernel/modules/xboxkrnl/objects/xmodule.cc @@ -24,10 +24,13 @@ namespace { XModule::XModule(KernelState* kernel_state, const char* path) : XObject(kernel_state, kTypeModule), xex_(NULL) { - XEIGNORE(xestrcpy(path_, XECOUNT(path_), path)); + XEIGNORE(xestrcpya(path_, XECOUNT(path_), path)); const xechar_t *slash = xestrrchr(path, '/'); + if (!slash) { + slash = xestrrchr(path, '\\'); + } if (slash) { - XEIGNORE(xestrcpy(name_, XECOUNT(name_), slash + 1)); + XEIGNORE(xestrcpya(name_, XECOUNT(name_), slash + 1)); } } @@ -52,9 +55,33 @@ const xe_xex2_header_t* XModule::xex_header() { } X_STATUS XModule::LoadFromFile(const char* path) { - // TODO(benvanik): load from virtual filesystem. - XEASSERTALWAYS(); - return X_STATUS_UNSUCCESSFUL; + // Resolve the file to open. + // TODO(benvanik): make this code shared? + fs::Entry* fs_entry = kernel_state()->filesystem()->ResolvePath(path); + if (!fs_entry) { + XELOGE(XT("File not found: %s"), path); + return X_STATUS_NO_SUCH_FILE; + } + if (fs_entry->type() != fs::Entry::kTypeFile) { + XELOGE(XT("Invalid file type: %s"), path); + return X_STATUS_NO_SUCH_FILE; + } + fs::FileEntry* fs_file = static_cast(fs_entry); + + // Map into memory. + fs::MemoryMapping* mmap = fs_file->CreateMemoryMapping(kXEFileModeRead, 0, 0); + if (!mmap) { + return X_STATUS_UNSUCCESSFUL; + } + + // Load the module. + X_STATUS return_code = LoadFromMemory(mmap->address(), mmap->length()); + + // Unmap memory and cleanup. + delete mmap; + delete fs_entry; + + return return_code; } X_STATUS XModule::LoadFromMemory(const void* addr, const size_t length) { diff --git a/src/kernel/modules/xboxkrnl/objects/xmodule.h b/src/kernel/modules/xboxkrnl/objects/xmodule.h index fc196e630..94d2f5f64 100644 --- a/src/kernel/modules/xboxkrnl/objects/xmodule.h +++ b/src/kernel/modules/xboxkrnl/objects/xmodule.h @@ -48,7 +48,7 @@ private: int LoadPE(); char name_[256]; - char path_[2048]; + char path_[XE_MAX_PATH]; xe_xex2_ref xex_; }; diff --git a/src/kernel/runtime.cc b/src/kernel/runtime.cc index cac8a9c2b..298a7eeef 100644 --- a/src/kernel/runtime.cc +++ b/src/kernel/runtime.cc @@ -63,6 +63,79 @@ shared_ptr Runtime::filesystem() { return filesystem_; } -int Runtime::LaunchModule(const xechar_t* path) { - return xboxkrnl_->LaunchModule(path); +int Runtime::LaunchXexFile(const xechar_t* 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: + // \\Device\\Harddisk0\\Partition1 + // and then get that symlinked to game:\, so + // -> game:\foo.xex + + int result_code = 0; + + // Get just the filename (foo.xex). + const xechar_t* file_name = xestrrchr(path, XE_PATH_SEPARATOR); + if (file_name) { + // Skip slash. + file_name++; + } else { + // No slash found, whole thing is a file. + file_name = path; + } + + // Get the parent path of the file. + xechar_t parent_path[XE_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 = filesystem_->RegisterLocalDirectoryDevice( + "\\Device\\Harddisk1\\Partition0", parent_path); + if (result_code) { + XELOGE(XT("Unable to mount local directory %s"), parent_path); + return result_code; + } + + // Create symlinks to the device. + filesystem_->CreateSymbolicLink( + "game:", "\\Device\\Harddisk1\\Partition0"); + filesystem_->CreateSymbolicLink( + "d:", "\\Device\\Harddisk1\\Partition0"); + + // Get the file name of the module to load from the filesystem. + char fs_path[XE_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); +} + +int Runtime::LaunchDiscImage(const xechar_t* path) { + int result_code = 0; + + // Register the disc image in the virtual filesystem. + result_code = filesystem_->RegisterDiscImageDevice( + "\\Device\\Cdrom0", path); + if (result_code) { + XELOGE(XT("Unable to mount disc image %s"), path); + return result_code; + } + + // Create symlinks to the device. + filesystem_->CreateSymbolicLink( + "game:", + "\\Device\\Cdrom0"); + filesystem_->CreateSymbolicLink( + "d:", + "\\Device\\Cdrom0"); + + // Launch the game. + return xboxkrnl_->LaunchModule("game:\\default.xex"); } diff --git a/tools/xenia-run/xenia-run.cc b/tools/xenia-run/xenia-run.cc index 8fb3a643b..e2df701f4 100644 --- a/tools/xenia-run/xenia-run.cc +++ b/tools/xenia-run/xenia-run.cc @@ -70,10 +70,32 @@ int Run::Launch(const xechar_t* path) { return 0; } - // TODO(benvanik): wait until the module thread exits - runtime_->LaunchModule(path); + // Normalize the path and make absolute. + // TODO(benvanik): move this someplace common. + xechar_t abs_path[XE_MAX_PATH]; +#if XE_PLATFORM(WIN32) + _wfullpath(abs_path, path, XECOUNT(abs_path)); +#else + realpath(path, abs_path); +#endif // WIN32 - return 0; + // Grab file extension. + const xechar_t* dot = xestrrchr(abs_path, '.'); + if (!dot) { + XELOGE(XT("Invalid input path; no extension found")); + return 1; + } + + // 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 (xestrcmp(dot, XT(".xex")) == 0) { + // Treat as a naked xex file. + return runtime_->LaunchXexFile(abs_path); + } else { + // Assume a disc image. + return runtime_->LaunchDiscImage(abs_path); + } } int xenia_run(int argc, xechar_t **argv) {