From ef6e0a32c380f981f0f316a6db584f4ef3a45014 Mon Sep 17 00:00:00 2001 From: emoose Date: Mon, 20 Jan 2020 19:51:43 +0000 Subject: [PATCH] [VFS] Clear out cache partition when game writes FATX header Writing FATX header likely means the game is trying to format it, so we'll help it along by deleting the contents too. HostPathDevice actually loads in all the file entries when its first inited though, so we also have to re-init the HostPathDevice for the cache partition too :/ --- src/xenia/app/xenia_main.cc | 15 ++++++++++++++ src/xenia/emulator.cc | 14 ------------- src/xenia/vfs/devices/null_device.cc | 5 +++-- src/xenia/vfs/devices/null_device.h | 8 ++++++- src/xenia/vfs/devices/null_file.cc | 31 ++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 17 deletions(-) diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index 56f94c253..76a62b0b8 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" @@ -273,6 +274,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()) { @@ -297,6 +301,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 35c1979cc..af3b5aa02 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" @@ -658,19 +657,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(); + } } }