[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 :/
This commit is contained in:
emoose 2020-01-20 19:51:43 +00:00 committed by illusion
parent e62ac39d4b
commit ef6e0a32c3
5 changed files with 56 additions and 17 deletions

View File

@ -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<std::wstring>& 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<xe::vfs::HostPathDevice>("\\CACHE0", L"cache0", false);
if (!cache0_device->Initialize()) {
@ -297,6 +301,17 @@ int xenia_main(const std::vector<std::wstring>& 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<vfs::NullDevice>(
"\\Device\\Harddisk0", null_files, emulator->file_system());
if (null_device->Initialize()) {
emulator->file_system()->RegisterDevice(std::move(null_device));
}
}
// Set a debug handler.

View File

@ -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<vfs::NullDevice>("\\Device\\Harddisk0", null_files);
if (null_device->Initialize()) {
file_system_->RegisterDevice(std::move(null_device));
}
}
// Reset state.
title_id_ = 0;
game_title_ = L"";

View File

@ -19,8 +19,9 @@ namespace xe {
namespace vfs {
NullDevice::NullDevice(const std::string& mount_path,
const std::initializer_list<std::string>& null_paths)
: Device(mount_path), null_paths_(null_paths) {}
const std::initializer_list<std::string>& null_paths,
VirtualFileSystem* vfs)
: Device(mount_path), null_paths_(null_paths), vfs_(vfs) {}
NullDevice::~NullDevice() = default;

View File

@ -13,6 +13,7 @@
#include <string>
#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<std::string>& null_paths);
const std::initializer_list<std::string>& 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<Entry> root_entry_;
std::vector<std::string> null_paths_;
uint32_t sectors_per_allocation_unit_ = 0x80;
VirtualFileSystem* vfs_ = nullptr;
};
} // namespace vfs

View File

@ -9,6 +9,9 @@
#include "xenia/vfs/devices/null_file.h"
#include <algorithm>
#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();
}
}
}