diff --git a/src/xenia/vfs/devices/stfs_container_device.cc b/src/xenia/vfs/devices/stfs_container_device.cc index e3ecdedff..5165498ea 100644 --- a/src/xenia/vfs/devices/stfs_container_device.cc +++ b/src/xenia/vfs/devices/stfs_container_device.cc @@ -586,19 +586,60 @@ size_t StfsContainerDevice::BlockToOffsetSTFS(uint64_t block_index) const { return xe::round_up(header_.header.header_size, kSectorSize) + (block << 12); } +uint32_t StfsContainerDevice::BlockToHashBlockNumberSTFS( + uint32_t block_index, uint32_t hash_level) const { + uint32_t blocks_per_hash_table = 1; + if (!header_.metadata.stfs_volume_descriptor.flags.read_only_format) { + blocks_per_hash_table = 2; + } + + uint32_t blockStep0 = kBlocksPerHashLevel[0] + blocks_per_hash_table; + uint32_t blockStep1 = kBlocksPerHashLevel[1] + + ((kBlocksPerHashLevel[0] + 1) * blocks_per_hash_table); + + uint32_t block = 0; + if (hash_level == 0) { + if (block_index < kBlocksPerHashLevel[0]) { + return 0; + } + + block = (block_index / kBlocksPerHashLevel[0]) * blockStep0; + block += + ((block_index / kBlocksPerHashLevel[1]) + 1) * blocks_per_hash_table; + + if (block_index < kBlocksPerHashLevel[1]) { + return block; + } + + return block + blocks_per_hash_table; + } + + if (hash_level == 1) { + if (block_index < kBlocksPerHashLevel[1]) { + return blockStep0; + } + + block = (block_index / kBlocksPerHashLevel[1]) * blockStep1; + return block + blocks_per_hash_table; + } + + // Level 2 is always at blockStep1 + return blockStep1; +} + +size_t StfsContainerDevice::BlockToHashBlockOffsetSTFS( + uint32_t block_index, uint32_t hash_level) const { + uint64_t block = BlockToHashBlockNumberSTFS(block_index, hash_level); + return xe::round_up(header_.header.header_size, kSectorSize) + (block << 12); +} + StfsHashEntry StfsContainerDevice::GetBlockHash(const uint8_t* map_ptr, uint32_t block_index, uint32_t table_offset) { - uint32_t record = block_index % 0xAA; - - // This is a bit hacky, but we'll get a pointer to the first block after the - // table and then subtract one sector to land on the table itself. - size_t hash_offset = - BlockToOffsetSTFS(xe::round_up(block_index + 1, kBlocksPerHashLevel[0]) - - kBlocksPerHashLevel[0]); - hash_offset -= kSectorSize; + size_t hash_offset = BlockToHashBlockOffsetSTFS(block_index, 0); const uint8_t* hash_data = map_ptr + hash_offset; + uint32_t record = block_index % kBlocksPerHashLevel[0]; // table_index += table_offset - (1 << table_size_shift_); const StfsHashEntry* record_data = reinterpret_cast(hash_data + record * 0x18); diff --git a/src/xenia/vfs/devices/stfs_container_device.h b/src/xenia/vfs/devices/stfs_container_device.h index 29d12cfa0..1af92b27f 100644 --- a/src/xenia/vfs/devices/stfs_container_device.h +++ b/src/xenia/vfs/devices/stfs_container_device.h @@ -463,6 +463,10 @@ class StfsContainerDevice : public Device { Error ReadSTFS(); size_t BlockToOffsetSTFS(uint64_t block_index) const; + uint32_t BlockToHashBlockNumberSTFS(uint32_t block_index, + uint32_t hash_level) const; + size_t BlockToHashBlockOffsetSTFS(uint32_t block_index, + uint32_t hash_level) const; StfsHashEntry GetBlockHash(const uint8_t* map_ptr, uint32_t block_index, uint32_t table_offset);