diff --git a/Library/DiscUtils.SquashFs/Directory.cs b/Library/DiscUtils.SquashFs/Directory.cs index c9aaaedeb..1a8f79971 100644 --- a/Library/DiscUtils.SquashFs/Directory.cs +++ b/Library/DiscUtils.SquashFs/Directory.cs @@ -54,7 +54,7 @@ public IReadOnlyDictionary AllEntries var reader = Context.DirectoryReader; reader.SetPosition(_dirInode.StartBlock, _dirInode.Offset); - // For some reason, always 3 greater than actual.. + // The 3 additional bytes are for ".." and "." while (reader.DistanceFrom(_dirInode.StartBlock, _dirInode.Offset) < _dirInode.FileSize - 3) { var header = DirectoryHeader.ReadFrom(reader); diff --git a/Library/DiscUtils.SquashFs/MetablockReader.cs b/Library/DiscUtils.SquashFs/MetablockReader.cs index 62c7869e5..55e973e4f 100644 --- a/Library/DiscUtils.SquashFs/MetablockReader.cs +++ b/Library/DiscUtils.SquashFs/MetablockReader.cs @@ -55,11 +55,57 @@ public void SetPosition(long blockStart, int blockOffset) _currentBlockStart = blockStart; _currentOffset = blockOffset; } - + + /// + /// Calculates the distance between the current position and the specified block position and offset. + /// + /// + /// * BlockStart is always related to the start of the metablock in the raw data stream + /// * BlockOffset is the offset within the uncompressed data stream! + /// + /// This means, that there can never be a distance calculation based on start and offset + /// across block boundaries + /// + /// Therefor to calculate the distance across block boundaries we need to iteratively read + /// through all blocks from the starting block to the current block. + /// + /// In most (standard) cases everything will be within the same block and the calculation is trivial. + /// + /// The start of the metadatablock with the raw data + /// The offset within the uncompressed block + /// The distance between + /// public long DistanceFrom(long blockStart, int blockOffset) { - return (_currentBlockStart - blockStart) * VfsSquashFileSystemReader.MetadataBufferSize - + (_currentOffset - blockOffset); + if (blockStart > _currentBlockStart) + { + throw new ArgumentOutOfRangeException("Block start needs to be less than or equal to current block start"); + } + + if (blockStart == _currentBlockStart) + { + return _currentOffset - blockOffset; + } + + var block = _context.ReadMetaBlock(_start + blockStart); + long distance = Metablock.SQUASHFS_METADATA_SIZE - blockOffset; + + do + { + block = _context.ReadMetaBlock(block.NextBlockStart); + blockStart = block.Position - _start; + + if (blockStart == _currentBlockStart) + { + distance += _currentOffset; + } + else + { + distance += block.Data.LongLength; + } + } while (blockStart != _currentBlockStart); + + return distance; } public void Skip(int count) diff --git a/Library/DiscUtils.SquashFs/VfsSquashFileSystemReader.cs b/Library/DiscUtils.SquashFs/VfsSquashFileSystemReader.cs index bd58ceec3..aac64b615 100644 --- a/Library/DiscUtils.SquashFs/VfsSquashFileSystemReader.cs +++ b/Library/DiscUtils.SquashFs/VfsSquashFileSystemReader.cs @@ -35,6 +35,7 @@ internal class VfsSquashFileSystemReader : VfsReadOnlyFileSystem true; public const int MetadataBufferSize = 8 * 1024; + public const short BlockPrefixSize = 2; private readonly BlockCache _blockCache; private readonly Context _context; @@ -272,7 +273,7 @@ private Metablock ReadMetaBlock(long pos) var stream = _context.RawStream; stream.Position = pos; - Span buffer = stackalloc byte[2]; + Span buffer = stackalloc byte[BlockPrefixSize]; stream.ReadExactly(buffer); int readLen = EndianUtilities.ToUInt16LittleEndian(buffer); @@ -283,7 +284,7 @@ private Metablock ReadMetaBlock(long pos) readLen = Metablock.SQUASHFS_COMPRESSED_BIT; } - block.NextBlockStart = pos + readLen + 2; + block.NextBlockStart = pos + BlockPrefixSize + readLen; if (isCompressed) {