diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index de404e025..07cb8ecc3 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -15,6 +15,7 @@ #include "xenia/apu/xma_decoder.h" #include "xenia/base/assert.h" #include "xenia/base/clock.h" +#include "xenia/base/logging.h" #include "xenia/base/string.h" #include "xenia/gpu/graphics_system.h" #include "xenia/hid/input_system.h" @@ -24,6 +25,9 @@ #include "xenia/memory.h" #include "xenia/ui/main_window.h" #include "xenia/vfs/virtual_file_system.h" +#include "xenia/vfs/devices/disc_image_device.h" +#include "xenia/vfs/devices/host_path_device.h" +#include "xenia/vfs/devices/stfs_container_device.h" DEFINE_double(time_scalar, 1.0, "Scalar used to speed or slow time (1x, 2x, 1/2x, etc)."); @@ -171,7 +175,27 @@ X_STATUS Emulator::Setup() { return result; } -X_STATUS Emulator::LaunchXexFile(const std::wstring& path) { +X_STATUS Emulator::LaunchPath(std::wstring path) { + // Launch based on file type. + // This is a silly guess based on file extension. + auto last_slash = path.find_last_of(xe::path_separator); + auto last_dot = path.find_last_of('.'); + if (last_dot < last_slash) { + last_dot = std::wstring::npos; + } + if (last_dot == std::wstring::npos) { + // Likely an STFS container. + return LaunchStfsContainer(path); + } else if (path.substr(last_dot) == L".xex") { + // Treat as a naked xex file. + return LaunchXexFile(path); + } else { + // Assume a disc image. + return LaunchDiscImage(path); + } +} + +X_STATUS Emulator::LaunchXexFile(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: @@ -179,45 +203,67 @@ X_STATUS Emulator::LaunchXexFile(const std::wstring& path) { // and then get that symlinked to game:\, so // -> game:\foo.xex - int result_code = - file_system_->InitializeFromPath(FileSystemType::XEX_FILE, path); - if (result_code) { - return X_STATUS_INVALID_PARAMETER; + auto mount_path = "\\Device\\Harddisk0\\Partition0"; + + // Register the local directory in the virtual filesystem. + auto parent_path = xe::find_base_path(path); + auto device = + std::make_unique(mount_path, parent_path, true); + if (!file_system_->RegisterDevice(std::move(device))) { + XELOGE("Unable to register host path"); + return X_STATUS_NO_SUCH_FILE; } + // Create symlinks to the device. + file_system_->RegisterSymbolicLink("game:", mount_path); + file_system_->RegisterSymbolicLink("d:", mount_path); + // Get just the filename (foo.xex). - std::wstring file_name; - auto last_slash = path.find_last_of(xe::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); - } + auto file_name = xe::find_name_from_path(path); // Launch the game. std::string fs_path = "game:\\" + xe::to_string(file_name); return CompleteLaunch(path, fs_path); } -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; +X_STATUS Emulator::LaunchDiscImage(std::wstring path) { + auto mount_path = "\\Device\\Cdrom0"; + + // Register the disc image in the virtual filesystem. + auto device = std::make_unique(mount_path, path); + if (!device->Initialize()) { + XELOGE("Unable to mount disc image"); + return X_STATUS_NO_SUCH_FILE; } + if (!file_system_->RegisterDevice(std::move(device))) { + XELOGE("Unable to register disc image"); + return X_STATUS_NO_SUCH_FILE; + } + + // Create symlinks to the device. + file_system_->RegisterSymbolicLink("game:", mount_path); + file_system_->RegisterSymbolicLink("d:", mount_path); // 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; +X_STATUS Emulator::LaunchStfsContainer(std::wstring path) { + auto mount_path = "\\Device\\Cdrom0"; + + // Register the container in the virtual filesystem. + auto device = std::make_unique(mount_path, path); + if (!device->Initialize()) { + XELOGE("Unable to mount STFS container"); + return X_STATUS_NO_SUCH_FILE; } + if (!file_system_->RegisterDevice(std::move(device))) { + XELOGE("Unable to register STFS container"); + return X_STATUS_NO_SUCH_FILE; + } + + file_system_->RegisterSymbolicLink("game:", mount_path); + file_system_->RegisterSymbolicLink("d:", mount_path); // Launch the game. return CompleteLaunch(path, "game:\\default.xex"); diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index 3d43c40d0..ccddfc52b 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -72,10 +72,10 @@ class Emulator { X_STATUS Setup(); - // TODO(benvanik): raw binary. - X_STATUS LaunchXexFile(const std::wstring& path); - X_STATUS LaunchDiscImage(const std::wstring& path); - X_STATUS LaunchSTFSTitle(const std::wstring& path); + X_STATUS LaunchPath(std::wstring path); + X_STATUS LaunchXexFile(std::wstring path); + X_STATUS LaunchDiscImage(std::wstring path); + X_STATUS LaunchStfsContainer(std::wstring path); private: X_STATUS CompleteLaunch(const std::wstring& path, diff --git a/src/xenia/kernel/content_manager.cc b/src/xenia/kernel/content_manager.cc index f48bca2f4..0e65b9430 100644 --- a/src/xenia/kernel/content_manager.cc +++ b/src/xenia/kernel/content_manager.cc @@ -14,6 +14,7 @@ #include "xenia/base/filesystem.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/xobject.h" +#include "xenia/vfs/devices/host_path_device.h" namespace xe { namespace kernel { @@ -28,14 +29,16 @@ ContentPackage::ContentPackage(KernelState* kernel_state, std::string root_name, : kernel_state_(kernel_state), root_name_(std::move(root_name)) { device_path_ = std::string("\\Device\\Content\\") + std::to_string(++content_device_id_) + "\\"; - kernel_state_->file_system()->RegisterHostPathDevice(device_path_, - package_path, false); - kernel_state_->file_system()->CreateSymbolicLink(root_name_ + ":", - device_path_); + + auto fs = kernel_state_->file_system(); + auto device = + std::make_unique(device_path_, package_path, false); + fs->RegisterDevice(std::move(device)); + fs->RegisterSymbolicLink(root_name_ + ":", device_path_); } ContentPackage::~ContentPackage() { - kernel_state_->file_system()->DeleteSymbolicLink(root_name_ + ":"); + kernel_state_->file_system()->UnregisterSymbolicLink(root_name_ + ":"); // TODO(benvanik): unregister device. } diff --git a/src/xenia/ui/main_window.cc b/src/xenia/ui/main_window.cc index 2b60828ae..f2b40276e 100644 --- a/src/xenia/ui/main_window.cc +++ b/src/xenia/ui/main_window.cc @@ -262,27 +262,5 @@ void MainWindow::OnCommand(int id) { } } -X_STATUS MainWindow::LaunchPath(std::wstring path) { - X_STATUS result; - - // Launch based on file type. - // This is a silly guess based on file extension. - // NOTE: this blocks! - auto file_system_type = emulator_->file_system()->InferType(path); - switch (file_system_type) { - case vfs::FileSystemType::STFS_TITLE: - result = emulator_->LaunchSTFSTitle(path); - break; - case vfs::FileSystemType::XEX_FILE: - result = emulator_->LaunchXexFile(path); - break; - case vfs::FileSystemType::DISC_IMAGE: - result = emulator_->LaunchDiscImage(path); - break; - } - - return result; -} - } // namespace ui } // namespace xe diff --git a/src/xenia/ui/main_window.h b/src/xenia/ui/main_window.h index 2c75e4953..7db737180 100644 --- a/src/xenia/ui/main_window.h +++ b/src/xenia/ui/main_window.h @@ -41,8 +41,6 @@ class MainWindow : public PlatformWindow { void Start(); - X_STATUS LaunchPath(std::wstring path); - private: bool Initialize(); diff --git a/src/xenia/vfs/device.cc b/src/xenia/vfs/device.cc index ec5e734f1..a1e3a44b7 100644 --- a/src/xenia/vfs/device.cc +++ b/src/xenia/vfs/device.cc @@ -14,7 +14,7 @@ namespace xe { namespace vfs { -Device::Device(const std::string& path) : path_(path) {} +Device::Device(const std::string& mount_path) : mount_path_(mount_path) {} Device::~Device() = default; diff --git a/src/xenia/vfs/device.h b/src/xenia/vfs/device.h index 0a651268a..3108d79ea 100644 --- a/src/xenia/vfs/device.h +++ b/src/xenia/vfs/device.h @@ -23,7 +23,7 @@ class Device { Device(const std::string& path); virtual ~Device(); - const std::string& path() const { return path_; } + const std::string& mount_path() const { return mount_path_; } virtual bool is_read_only() const { return true; } @@ -37,7 +37,7 @@ class Device { size_t length); protected: - std::string path_; + std::string mount_path_; }; } // namespace vfs diff --git a/src/xenia/vfs/devices/disc_image_device.cc b/src/xenia/vfs/devices/disc_image_device.cc index 94bca5e2f..1e57b2baa 100644 --- a/src/xenia/vfs/devices/disc_image_device.cc +++ b/src/xenia/vfs/devices/disc_image_device.cc @@ -17,29 +17,29 @@ namespace xe { namespace vfs { -DiscImageDevice::DiscImageDevice(const std::string& path, +DiscImageDevice::DiscImageDevice(const std::string& mount_path, const std::wstring& local_path) - : Device(path), local_path_(local_path), gdfx_(nullptr) {} + : Device(mount_path), local_path_(local_path), gdfx_(nullptr) {} DiscImageDevice::~DiscImageDevice() { delete gdfx_; } -int DiscImageDevice::Init() { +bool DiscImageDevice::Initialize() { mmap_ = MappedMemory::Open(local_path_, MappedMemory::Mode::kRead); if (!mmap_) { XELOGE("Disc image could not be mapped"); - return 1; + return false; } gdfx_ = new GDFX(mmap_.get()); GDFX::Error error = gdfx_->Load(); if (error != GDFX::kSuccess) { XELOGE("GDFX init failed: %d", error); - return 1; + return false; } // gdfx_->Dump(); - return 0; + return true; } std::unique_ptr DiscImageDevice::ResolvePath(const char* path) { diff --git a/src/xenia/vfs/devices/disc_image_device.h b/src/xenia/vfs/devices/disc_image_device.h index b24d6f0d6..0884c1d54 100644 --- a/src/xenia/vfs/devices/disc_image_device.h +++ b/src/xenia/vfs/devices/disc_image_device.h @@ -23,10 +23,11 @@ class GDFX; class DiscImageDevice : public Device { public: - DiscImageDevice(const std::string& path, const std::wstring& local_path); + DiscImageDevice(const std::string& mount_path, + const std::wstring& local_path); ~DiscImageDevice() override; - int Init(); + bool Initialize(); std::unique_ptr ResolvePath(const char* path) override; diff --git a/src/xenia/vfs/devices/host_path_device.cc b/src/xenia/vfs/devices/host_path_device.cc index 3731670fa..a51d3232f 100644 --- a/src/xenia/vfs/devices/host_path_device.cc +++ b/src/xenia/vfs/devices/host_path_device.cc @@ -17,9 +17,9 @@ namespace xe { namespace vfs { -HostPathDevice::HostPathDevice(const std::string& path, +HostPathDevice::HostPathDevice(const std::string& mount_path, const std::wstring& local_path, bool read_only) - : Device(path), local_path_(local_path), read_only_(read_only) {} + : Device(mount_path), local_path_(local_path), read_only_(read_only) {} HostPathDevice::~HostPathDevice() {} diff --git a/src/xenia/vfs/devices/host_path_device.h b/src/xenia/vfs/devices/host_path_device.h index b801008d6..fc4d3ee49 100644 --- a/src/xenia/vfs/devices/host_path_device.h +++ b/src/xenia/vfs/devices/host_path_device.h @@ -19,7 +19,7 @@ namespace vfs { class HostPathDevice : public Device { public: - HostPathDevice(const std::string& path, const std::wstring& local_path, + HostPathDevice(const std::string& mount_path, const std::wstring& local_path, bool read_only); ~HostPathDevice() override; diff --git a/src/xenia/vfs/devices/stfs_container_device.cc b/src/xenia/vfs/devices/stfs_container_device.cc index eae3c6131..f2c4a33be 100644 --- a/src/xenia/vfs/devices/stfs_container_device.cc +++ b/src/xenia/vfs/devices/stfs_container_device.cc @@ -18,29 +18,29 @@ namespace xe { namespace vfs { -STFSContainerDevice::STFSContainerDevice(const std::string& path, +STFSContainerDevice::STFSContainerDevice(const std::string& mount_path, const std::wstring& local_path) - : Device(path), local_path_(local_path), stfs_(nullptr) {} + : Device(mount_path), local_path_(local_path), stfs_(nullptr) {} STFSContainerDevice::~STFSContainerDevice() { delete stfs_; } -int STFSContainerDevice::Init() { +bool STFSContainerDevice::Initialize() { mmap_ = MappedMemory::Open(local_path_, MappedMemory::Mode::kRead); if (!mmap_) { XELOGE("STFS container could not be mapped"); - return 1; + return false; } stfs_ = new STFS(mmap_.get()); STFS::Error error = stfs_->Load(); if (error != STFS::kSuccess) { XELOGE("STFS init failed: %d", error); - return 1; + return false; } // stfs_->Dump(); - return 0; + return true; } std::unique_ptr STFSContainerDevice::ResolvePath(const char* path) { diff --git a/src/xenia/vfs/devices/stfs_container_device.h b/src/xenia/vfs/devices/stfs_container_device.h index 219e22f06..b52fcbdf9 100644 --- a/src/xenia/vfs/devices/stfs_container_device.h +++ b/src/xenia/vfs/devices/stfs_container_device.h @@ -23,10 +23,11 @@ class STFS; class STFSContainerDevice : public Device { public: - STFSContainerDevice(const std::string& path, const std::wstring& local_path); + STFSContainerDevice(const std::string& mount_path, + const std::wstring& local_path); ~STFSContainerDevice() override; - int Init(); + bool Initialize(); std::unique_ptr ResolvePath(const char* path) override; diff --git a/src/xenia/vfs/entry.cc b/src/xenia/vfs/entry.cc index d944d2e5c..99cef3d68 100644 --- a/src/xenia/vfs/entry.cc +++ b/src/xenia/vfs/entry.cc @@ -23,7 +23,7 @@ MemoryMapping::~MemoryMapping() {} Entry::Entry(Device* device, const std::string& path) : device_(device), path_(path) { assert_not_null(device); - absolute_path_ = device->path() + path; + absolute_path_ = xe::join_paths(device->mount_path(), path); name_ = xe::find_name_from_path(path); } diff --git a/src/xenia/vfs/virtual_file_system.cc b/src/xenia/vfs/virtual_file_system.cc index 614eaaa2b..c5ab0c39f 100644 --- a/src/xenia/vfs/virtual_file_system.cc +++ b/src/xenia/vfs/virtual_file_system.cc @@ -12,9 +12,6 @@ #include "xenia/base/filesystem.h" #include "xenia/base/logging.h" #include "xenia/base/string.h" -#include "xenia/vfs/devices/disc_image_device.h" -#include "xenia/vfs/devices/host_path_device.h" -#include "xenia/vfs/devices/stfs_container_device.h" namespace xe { namespace vfs { @@ -24,128 +21,28 @@ VirtualFileSystem::VirtualFileSystem() {} VirtualFileSystem::~VirtualFileSystem() { // 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(); } -FileSystemType VirtualFileSystem::InferType(const std::wstring& local_path) { - auto last_slash = local_path.find_last_of(xe::path_separator); - auto last_dot = local_path.find_last_of('.'); - if (last_dot < last_slash) { - last_dot = std::wstring::npos; - } - 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; - } +bool VirtualFileSystem::RegisterDevice(std::unique_ptr device) { + devices_.emplace_back(std::move(device)); + return true; } -int VirtualFileSystem::InitializeFromPath(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(xe::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\\Harddisk0\\Partition0", parent_path, true); - if (result_code) { - XELOGE("Unable to mount local directory"); - return result_code; - } - - // Create symlinks to the device. - CreateSymbolicLink("game:", "\\Device\\Harddisk0\\Partition0"); - CreateSymbolicLink("d:", "\\Device\\Harddisk0\\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 VirtualFileSystem::RegisterDevice(const std::string& path, Device* device) { - devices_.push_back(device); - return 0; -} - -int VirtualFileSystem::RegisterHostPathDevice(const std::string& path, - const std::wstring& local_path, - bool read_only) { - Device* device = new HostPathDevice(path, local_path, read_only); - return RegisterDevice(path, device); -} - -int VirtualFileSystem::RegisterDiscImageDevice(const std::string& path, - const std::wstring& local_path) { - DiscImageDevice* device = new DiscImageDevice(path, local_path); - if (device->Init()) { - return 1; - } - return RegisterDevice(path, device); -} - -int VirtualFileSystem::RegisterSTFSContainerDevice( - const std::string& path, const std::wstring& local_path) { - STFSContainerDevice* device = new STFSContainerDevice(path, local_path); - if (device->Init()) { - return 1; - } - return RegisterDevice(path, device); -} - -int VirtualFileSystem::CreateSymbolicLink(const std::string& path, - const std::string& target) { +bool VirtualFileSystem::RegisterSymbolicLink(std::string path, + std::string target) { symlinks_.insert({path, target}); - return 0; + return true; } -int VirtualFileSystem::DeleteSymbolicLink(const std::string& path) { +bool VirtualFileSystem::UnregisterSymbolicLink(std::string path) { auto& it = symlinks_.find(path); if (it == symlinks_.end()) { - return 1; + return false; } symlinks_.erase(it); - return 0; + return true; } std::unique_ptr VirtualFileSystem::ResolvePath(const std::string& path) { @@ -167,8 +64,8 @@ std::unique_ptr VirtualFileSystem::ResolvePath(const std::string& path) { // Not to fret, check to see if the path is fully qualified. if (device_path.empty()) { for (auto& device : devices_) { - if (xe::find_first_of_case(normalized_path, device->path()) == 0) { - device_path = device->path(); + if (xe::find_first_of_case(normalized_path, device->mount_path()) == 0) { + device_path = device->mount_path(); relative_path = normalized_path.substr(device_path.size()); } } @@ -181,7 +78,7 @@ std::unique_ptr VirtualFileSystem::ResolvePath(const std::string& path) { // Scan all devices. for (auto& device : devices_) { - if (strcasecmp(device_path.c_str(), device->path().c_str()) == 0) { + if (strcasecmp(device_path.c_str(), device->mount_path().c_str()) == 0) { return device->ResolvePath(relative_path.c_str()); } } diff --git a/src/xenia/vfs/virtual_file_system.h b/src/xenia/vfs/virtual_file_system.h index c324c1d33..e8c50af1c 100644 --- a/src/xenia/vfs/virtual_file_system.h +++ b/src/xenia/vfs/virtual_file_system.h @@ -15,44 +15,28 @@ #include #include +#include "xenia/vfs/device.h" #include "xenia/vfs/entry.h" namespace xe { namespace vfs { -class Device; - -enum class FileSystemType { - STFS_TITLE, - DISC_IMAGE, - XEX_FILE, -}; - class VirtualFileSystem { public: VirtualFileSystem(); ~VirtualFileSystem(); - FileSystemType InferType(const std::wstring& local_path); - int InitializeFromPath(FileSystemType type, const std::wstring& local_path); + bool RegisterDevice(std::unique_ptr device); - int RegisterDevice(const std::string& path, Device* device); - int RegisterHostPathDevice(const std::string& path, - const std::wstring& local_path, bool read_only); - 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 std::string& path, const std::string& target); - int DeleteSymbolicLink(const std::string& path); + bool RegisterSymbolicLink(std::string path, std::string target); + bool UnregisterSymbolicLink(std::string path); std::unique_ptr ResolvePath(const std::string& path); X_STATUS Open(std::unique_ptr entry, KernelState* kernel_state, Mode mode, bool async, XFile** out_file); private: - std::vector devices_; + std::vector> devices_; std::unordered_map symlinks_; }; diff --git a/src/xenia/xenia_main.cc b/src/xenia/xenia_main.cc index 621b94e3a..22c6f8934 100644 --- a/src/xenia/xenia_main.cc +++ b/src/xenia/xenia_main.cc @@ -73,7 +73,7 @@ int xenia_main(std::vector& args) { // Normalize the path and make absolute. std::wstring abs_path = xe::to_absolute_path(path); - result = emulator->main_window()->LaunchPath(abs_path); + result = emulator->LaunchPath(abs_path); if (XFAILED(result)) { XELOGE("Failed to launch target: %.8X", result); return 1;