private void ReadCentralDirectory() { try { // assume ReadEndOfCentralDirectory has been called and has populated _centralDirectoryStart ArchiveStream.Seek(_centralDirectoryStart, SeekOrigin.Begin); long numberOfEntries = 0; //read the central directory while (ZipCentralDirectoryFileHeader.TryReadBlock(ArchiveReader, out var currentHeader)) { AddEntry(new ZipArchiveEntry(this, currentHeader)); numberOfEntries++; } if (numberOfEntries != _expectedNumberOfEntries) { throw new InvalidDataException(SR.NumEntriesWrong); } } catch (EndOfStreamException ex) { throw new InvalidDataException(SR.Format(SR.CentralDirectoryInvalid, ex)); } }
public async Task <byte[]> ReadFile(FileRecord record) { var isCompressed = (Header.ArchiveFlags & BSA105ArchiveFlags.CompressedArchive) == BSA105ArchiveFlags.CompressedArchive; if ((isCompressed && !record.CompressionBit) || (!isCompressed && record.CompressionBit)) { return(await ReadCompressedFile(record)); } ArchiveStream.Seek(record.DataOffset, SeekOrigin.Begin); var fileBytes = new byte[record.Size]; await ArchiveStream.ReadAsync(fileBytes, 0, fileBytes.Length); return(fileBytes); }
public async Task <byte[]> ReadCompressedFile(FileRecord record) { ArchiveStream.Seek(record.DataOffset, SeekOrigin.Begin); var reader = new BinaryReader(ArchiveStream); var realSize = reader.ReadUInt32(); var source = new byte[record.Size - 4]; await ArchiveStream.ReadAsync(source, 0, source.Length); using (var memory = new MemoryStream(source)) { var output = LZ4Stream.Decode(memory); var targetBytes = new byte[realSize]; await output.ReadAsync(targetBytes, 0, targetBytes.Length); return(targetBytes); } }
// This function reads all the EOCD stuff it needs to find the offset to the start of the central directory // This offset gets put in _centralDirectoryStart and the number of this disk gets put in _numberOfThisDisk // Also does some verification that this isn't a split/spanned archive // Also checks that offset to CD isn't out of bounds private void ReadEndOfCentralDirectory() { try { // this seeks to the start of the end of central directory record ArchiveStream.Seek(-ZipEndOfCentralDirectoryBlock.SizeOfBlockWithoutSignature, SeekOrigin.End); if (!ZipHelper.SeekBackwardsToSignature(ArchiveStream, ZipEndOfCentralDirectoryBlock.SignatureConstant)) { throw new InvalidDataException(SR.EOCDNotFound); } long eocdStart = ArchiveStream.Position; // read the EOCD ZipEndOfCentralDirectoryBlock eocd; bool eocdProper = ZipEndOfCentralDirectoryBlock.TryReadBlock(ArchiveReader, out eocd); Debug.Assert(eocdProper); // we just found this using the signature finder, so it should be okay if (eocd.NumberOfThisDisk != eocd.NumberOfTheDiskWithTheStartOfTheCentralDirectory) { throw new InvalidDataException(SR.SplitSpanned); } NumberOfThisDisk = eocd.NumberOfThisDisk; _centralDirectoryStart = eocd.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; if (eocd.NumberOfEntriesInTheCentralDirectory != eocd.NumberOfEntriesInTheCentralDirectoryOnThisDisk) { throw new InvalidDataException(SR.SplitSpanned); } _expectedNumberOfEntries = eocd.NumberOfEntriesInTheCentralDirectory; // only bother looking for zip64 EOCD stuff if we suspect it is needed because some value is FFFFFFFFF // because these are the only two values we need, we only worry about these // if we don't find the zip64 EOCD, we just give up and try to use the original values if (eocd.NumberOfThisDisk == ZipHelper.Mask16Bit || eocd.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber == ZipHelper.Mask32Bit || eocd.NumberOfEntriesInTheCentralDirectory == ZipHelper.Mask16Bit) { // we need to look for zip 64 EOCD stuff // seek to the zip 64 EOCD locator ArchiveStream.Seek(eocdStart - Zip64EndOfCentralDirectoryLocator.SizeOfBlockWithoutSignature, SeekOrigin.Begin); // if we don't find it, assume it doesn't exist and use data from normal eocd if (ZipHelper.SeekBackwardsToSignature(ArchiveStream, Zip64EndOfCentralDirectoryLocator.SignatureConstant)) { // use locator to get to Zip64EOCD Zip64EndOfCentralDirectoryLocator locator; bool zip64EOCDLocatorProper = Zip64EndOfCentralDirectoryLocator.TryReadBlock(ArchiveReader, out locator); Debug.Assert(zip64EOCDLocatorProper); // we just found this using the signature finder, so it should be okay if (locator.OffsetOfZip64EOCD > long.MaxValue) { throw new InvalidDataException(SR.FieldTooBigOffsetToZip64EOCD); } long zip64EOCDOffset = (long)locator.OffsetOfZip64EOCD; ArchiveStream.Seek(zip64EOCDOffset, SeekOrigin.Begin); // read Zip64EOCD Zip64EndOfCentralDirectoryRecord record; if (!Zip64EndOfCentralDirectoryRecord.TryReadBlock(ArchiveReader, out record)) { throw new InvalidDataException(SR.Zip64EOCDNotWhereExpected); } NumberOfThisDisk = record.NumberOfThisDisk; if (record.NumberOfEntriesTotal > long.MaxValue) { throw new InvalidDataException(SR.FieldTooBigNumEntries); } if (record.OffsetOfCentralDirectory > long.MaxValue) { throw new InvalidDataException(SR.FieldTooBigOffsetToCD); } if (record.NumberOfEntriesTotal != record.NumberOfEntriesOnThisDisk) { throw new InvalidDataException(SR.SplitSpanned); } _expectedNumberOfEntries = (long)record.NumberOfEntriesTotal; _centralDirectoryStart = (long)record.OffsetOfCentralDirectory; } } if (_centralDirectoryStart > ArchiveStream.Length) { throw new InvalidDataException(SR.FieldTooBigOffsetToCD); } } catch (EndOfStreamException ex) { throw new InvalidDataException(SR.CDCorrupt, ex); } catch (IOException ex) { throw new InvalidDataException(SR.CDCorrupt, ex); } }