[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.
This commit is contained in:
emoose 2020-01-20 01:10:43 +00:00 committed by illusion98
parent 7446d74ac4
commit 55ce179ff2
2 changed files with 28 additions and 3 deletions

View File

@ -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<Entry> root_entry_;
std::vector<std::string> null_paths_;
uint32_t sectors_per_allocation_unit_ = 0x80;
};
} // namespace vfs

View File

@ -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<uint32_t>(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;
}