From dc25bf59618bb06f1bb893d6f54513cb27467878 Mon Sep 17 00:00:00 2001 From: emoose Date: Mon, 20 Jan 2020 01:10:43 +0000 Subject: [PATCH] [VFS] Use SectorsPerCluster provided by game for cache drive. This fixes some games (eg. Halo: CEA) not mounting cache. It seems some games use different SectorsPerClusters values, which then changes the value it expects from NtQueryVolumeInformationFile. Since we'd always respond with a static value this would make the game think that mounting failed, and it'd bail out of the cache-mounting code. Now we capture the SectorsPerCluster value when the game writes it to the cache-partition header, and update the NullDevice's sectors_per_allocation_unit with it. --- src/xenia/vfs/devices/null_device.h | 16 +++++++++++++--- src/xenia/vfs/devices/null_file.cc | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/xenia/vfs/devices/null_device.h b/src/xenia/vfs/devices/null_device.h index 0a87e739f..dd5719392 100644 --- a/src/xenia/vfs/devices/null_device.h +++ b/src/xenia/vfs/devices/null_device.h @@ -34,13 +34,23 @@ class NullDevice : public Device { uint32_t total_allocation_units() const override { return 128 * 1024; } uint32_t available_allocation_units() const override { return 128 * 1024; } - // STFC/cache code seems to require the product of these two to equal 0x10000! - uint32_t sectors_per_allocation_unit() const override { return 1; } - uint32_t bytes_per_sector() const override { return 0x10000; } + // STFC/cache code seems to require the product of these two to equal 0x10000 + // or 0x8000! (depending on SectorsPerCluster value written to partition + // header) + uint32_t sectors_per_allocation_unit() const override { + return sectors_per_allocation_unit_; + } + uint32_t bytes_per_sector() const override { return 512; } + + void sectors_per_allocation_unit(uint32_t value) { + sectors_per_allocation_unit_ = value; + } private: std::unique_ptr root_entry_; std::vector null_paths_; + + uint32_t sectors_per_allocation_unit_ = 0x80; }; } // namespace vfs diff --git a/src/xenia/vfs/devices/null_file.cc b/src/xenia/vfs/devices/null_file.cc index b26a4b6cf..85e3b9381 100644 --- a/src/xenia/vfs/devices/null_file.cc +++ b/src/xenia/vfs/devices/null_file.cc @@ -9,6 +9,7 @@ #include "xenia/vfs/devices/null_file.h" +#include "xenia/vfs/devices/null_device.h" #include "xenia/vfs/devices/null_entry.h" namespace xe { @@ -37,6 +38,20 @@ X_STATUS NullFile::WriteSync(const void* buffer, size_t buffer_length, return X_STATUS_ACCESS_DENIED; } + // Check if game is writing a FATX header... + if (byte_offset == 0 && buffer_length >= (4 * 3)) { + auto* header = (uint32_t*)buffer; + if (xe::load_and_swap(header) == 0x58544146) { + // This is a FATX header - read the SectorsPerCluster value from it + // 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); + } + } + return X_STATUS_SUCCESS; }