diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index ccdff4cba..3e794b374 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -20,6 +20,7 @@ #include "xenia/emulator.h" #include "xenia/ui/file_picker.h" #include "xenia/vfs/devices/host_path_device.h" +#include "xenia/vfs/devices/null_device.h" // Available audio systems: #include "xenia/apu/nop/nop_audio_system.h" @@ -263,6 +264,9 @@ int xenia_main(const std::vector& args) { } if (cvars::mount_cache) { + // NOTE: make sure to update null_file.cc if changing the local paths of + // cache! + auto cache0_device = std::make_unique("\\CACHE0", L"cache0", false); if (!cache0_device->Initialize()) { @@ -287,6 +291,17 @@ int xenia_main(const std::vector& args) { emulator->file_system()->RegisterSymbolicLink("cache1:", "\\CACHE1"); } } + + // Below are accessed directly by STFC/cache code baked into the game + // By using a NullDevice that just returns success to all IO requests, the + // cache code should hopefully progress without erroring out + auto null_files = {std::string("\\Partition0"), std::string("\\Cache0"), + std::string("\\Cache1")}; + auto null_device = std::make_unique( + "\\Device\\Harddisk0", null_files, emulator->file_system()); + if (null_device->Initialize()) { + emulator->file_system()->RegisterDevice(std::move(null_device)); + } } // Set a debug handler. diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 66b8831a6..73cf0dcee 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -41,7 +41,6 @@ #include "xenia/ui/imgui_dialog.h" #include "xenia/vfs/devices/disc_image_device.h" #include "xenia/vfs/devices/host_path_device.h" -#include "xenia/vfs/devices/null_device.h" #include "xenia/vfs/devices/stfs_container_device.h" #include "xenia/vfs/virtual_file_system.h" @@ -656,19 +655,6 @@ std::string Emulator::FindLaunchModule() { X_STATUS Emulator::CompleteLaunch(const std::wstring& path, const std::string& module_path) { - if (cvars::mount_cache) { - // Below are accessed directly by STFC/cache code baked into the game - // By using a NullDevice that just returns success to all IO requests, the - // cache code should hopefully progress without erroring out - auto null_files = {std::string("\\Partition0"), std::string("\\Cache0"), - std::string("\\Cache1")}; - auto null_device = - std::make_unique("\\Device\\Harddisk0", null_files); - if (null_device->Initialize()) { - file_system_->RegisterDevice(std::move(null_device)); - } - } - // Reset state. title_id_ = 0; game_title_ = L""; diff --git a/src/xenia/vfs/devices/null_device.cc b/src/xenia/vfs/devices/null_device.cc index a27bff29a..f11cdebb7 100644 --- a/src/xenia/vfs/devices/null_device.cc +++ b/src/xenia/vfs/devices/null_device.cc @@ -19,8 +19,9 @@ namespace xe { namespace vfs { NullDevice::NullDevice(const std::string& mount_path, - const std::initializer_list& null_paths) - : Device(mount_path), null_paths_(null_paths) {} + const std::initializer_list& null_paths, + VirtualFileSystem* vfs) + : Device(mount_path), null_paths_(null_paths), vfs_(vfs) {} NullDevice::~NullDevice() = default; diff --git a/src/xenia/vfs/devices/null_device.h b/src/xenia/vfs/devices/null_device.h index dd5719392..61113d327 100644 --- a/src/xenia/vfs/devices/null_device.h +++ b/src/xenia/vfs/devices/null_device.h @@ -13,6 +13,7 @@ #include #include "xenia/vfs/device.h" +#include "xenia/vfs/virtual_file_system.h" namespace xe { namespace vfs { @@ -22,7 +23,8 @@ class NullEntry; class NullDevice : public Device { public: NullDevice(const std::string& mount_path, - const std::initializer_list& null_paths); + const std::initializer_list& null_paths, + VirtualFileSystem* vfs); ~NullDevice() override; bool Initialize() override; @@ -46,11 +48,15 @@ class NullDevice : public Device { sectors_per_allocation_unit_ = value; } + VirtualFileSystem* vfs() { return vfs_; } + private: std::unique_ptr root_entry_; std::vector null_paths_; uint32_t sectors_per_allocation_unit_ = 0x80; + + VirtualFileSystem* vfs_ = nullptr; }; } // namespace vfs diff --git a/src/xenia/vfs/devices/null_file.cc b/src/xenia/vfs/devices/null_file.cc index 85e3b9381..fd718d1c6 100644 --- a/src/xenia/vfs/devices/null_file.cc +++ b/src/xenia/vfs/devices/null_file.cc @@ -9,6 +9,9 @@ #include "xenia/vfs/devices/null_file.h" +#include + +#include "xenia/kernel/kernel_state.h" #include "xenia/vfs/devices/null_device.h" #include "xenia/vfs/devices/null_entry.h" @@ -46,9 +49,37 @@ X_STATUS NullFile::WriteSync(const void* buffer, size_t buffer_length, // Game will try reading this back through NtQueryVolumeInformationFile // later on, if it doesn't match, cache partition mount won't succeed auto sectors_per_cluster = xe::byte_swap(header[2]); + // Update NullDevice with the SectorsPerCluster value auto* null_device = (NullDevice*)entry_->device(); null_device->sectors_per_allocation_unit(sectors_per_cluster); + + // Since the game is trying to remake the FATX header (in other words, + // formatting the partition), we'll clear out the folder for this + // partition + auto folder_name = entry_->name(); + std::transform(folder_name.begin(), folder_name.end(), + folder_name.begin(), tolower); + + // TODO: this works because atm cache0/cache1 folders are in same folder + // as xenia.exe, if that changes we should update this code too! + auto files = xe::filesystem::ListFiles(xe::to_wstring(folder_name)); + for (auto file : files) { + auto filepath = xe::join_paths(file.path, file.name); + if (file.type == xe::filesystem::FileInfo::Type::kDirectory) { + xe::filesystem::DeleteFolder(filepath); + } else { + xe::filesystem::DeleteFile(filepath); + } + } + + // Now re-init the device for this cache partition + // (otherwise device will think it has files that don't actually exist) + auto* device_root = + null_device->vfs()->ResolveDevice("\\" + folder_name + "\\"); + if (device_root) { + device_root->device()->Initialize(); + } } }