| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649 |
- // Better Streaming Assets, Piotr Gwiazdowski <gwiazdorrr+github at gmail.com>, 2017
- // Bits below are copied from or inspired by System.IO.Compression.dll; leaving comments from
- // original source code and attaching license
- // The MIT License(MIT)
- //
- // Copyright(c) .NET Foundation and Contributors
- //
- // All rights reserved.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in all
- // copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- // SOFTWARE.
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Text;
- using UnityEngine;
- namespace Better.StreamingAssets.ZipArchive
- {
- // All blocks.TryReadBlock do a check to see if signature is correct. Generic extra field is slightly different
- // all of the TryReadBlocks will throw if there are not enough bytes in the stream
- internal struct ZipGenericExtraField
- {
- private const int SizeOfHeader = 4;
- private ushort _tag;
- private ushort _size;
- private byte[] _data;
- public ushort Tag { get { return _tag; } }
- // returns size of data, not of the entire block
- public ushort Size { get { return _size; } }
- public byte[] Data { get { return _data; } }
- // shouldn't ever read the byte at position endExtraField
- // assumes we are positioned at the beginning of an extra field subfield
- public static bool TryReadBlock(BinaryReader reader, long endExtraField, out ZipGenericExtraField field)
- {
- field = new ZipGenericExtraField();
- // not enough bytes to read tag + size
- if ( endExtraField - reader.BaseStream.Position < 4 )
- return false;
- field._tag = reader.ReadUInt16();
- field._size = reader.ReadUInt16();
- // not enough bytes to read the data
- if ( endExtraField - reader.BaseStream.Position < field._size )
- return false;
- field._data = reader.ReadBytes(field._size);
- return true;
- }
- }
- internal struct Zip64ExtraField
- {
- // Size is size of the record not including the tag or size fields
- // If the extra field is going in the local header, it cannot include only
- // one of uncompressed/compressed size
- public const int OffsetToFirstField = 4;
- private const ushort TagConstant = 1;
- private ushort _size;
- private long? _uncompressedSize;
- private long? _compressedSize;
- private long? _localHeaderOffset;
- private int? _startDiskNumber;
- public long? UncompressedSize
- {
- get { return _uncompressedSize; }
- set { _uncompressedSize = value; UpdateSize(); }
- }
- public long? CompressedSize
- {
- get { return _compressedSize; }
- set { _compressedSize = value; UpdateSize(); }
- }
- public long? LocalHeaderOffset
- {
- get { return _localHeaderOffset; }
- set { _localHeaderOffset = value; UpdateSize(); }
- }
- public int? StartDiskNumber { get { return _startDiskNumber; } }
- private void UpdateSize()
- {
- _size = 0;
- if ( _uncompressedSize != null ) _size += 8;
- if ( _compressedSize != null ) _size += 8;
- if ( _localHeaderOffset != null ) _size += 8;
- if ( _startDiskNumber != null ) _size += 4;
- }
- // There is a small chance that something very weird could happen here. The code calling into this function
- // will ask for a value from the extra field if the field was masked with FF's. It's theoretically possible
- // that a field was FF's legitimately, and the writer didn't decide to write the corresponding extra field.
- // Also, at the same time, other fields were masked with FF's to indicate looking in the zip64 record.
- // Then, the search for the zip64 record will fail because the expected size is wrong,
- // and a nulled out Zip64ExtraField will be returned. Thus, even though there was Zip64 data,
- // it will not be used. It is questionable whether this situation is possible to detect
- // unlike the other functions that have try-pattern semantics, these functions always return a
- // Zip64ExtraField. If a Zip64 extra field actually doesn't exist, all of the fields in the
- // returned struct will be null
- //
- // If there are more than one Zip64 extra fields, we take the first one that has the expected size
- //
- public static Zip64ExtraField GetJustZip64Block(Stream extraFieldStream,
- bool readUncompressedSize, bool readCompressedSize,
- bool readLocalHeaderOffset, bool readStartDiskNumber)
- {
- Zip64ExtraField zip64Field;
- using ( BinaryReader reader = new BinaryReader(extraFieldStream) )
- {
- ZipGenericExtraField currentExtraField;
- while ( ZipGenericExtraField.TryReadBlock(reader, extraFieldStream.Length, out currentExtraField) )
- {
- if ( TryGetZip64BlockFromGenericExtraField(currentExtraField, readUncompressedSize,
- readCompressedSize, readLocalHeaderOffset, readStartDiskNumber, out zip64Field) )
- {
- return zip64Field;
- }
- }
- }
- zip64Field = new Zip64ExtraField();
- zip64Field._compressedSize = null;
- zip64Field._uncompressedSize = null;
- zip64Field._localHeaderOffset = null;
- zip64Field._startDiskNumber = null;
- return zip64Field;
- }
- private static bool TryGetZip64BlockFromGenericExtraField(ZipGenericExtraField extraField,
- bool readUncompressedSize, bool readCompressedSize,
- bool readLocalHeaderOffset, bool readStartDiskNumber,
- out Zip64ExtraField zip64Block)
- {
- zip64Block = new Zip64ExtraField();
- zip64Block._compressedSize = null;
- zip64Block._uncompressedSize = null;
- zip64Block._localHeaderOffset = null;
- zip64Block._startDiskNumber = null;
- if ( extraField.Tag != TagConstant )
- return false;
- // this pattern needed because nested using blocks trigger CA2202
- MemoryStream ms = null;
- try
- {
- ms = new MemoryStream(extraField.Data);
- using ( BinaryReader reader = new BinaryReader(ms) )
- {
- ms = null;
- zip64Block._size = extraField.Size;
- ushort expectedSize = 0;
- if ( readUncompressedSize ) expectedSize += 8;
- if ( readCompressedSize ) expectedSize += 8;
- if ( readLocalHeaderOffset ) expectedSize += 8;
- if ( readStartDiskNumber ) expectedSize += 4;
- // if it is not the expected size, perhaps there is another extra field that matches
- if ( expectedSize != zip64Block._size )
- return false;
- if ( readUncompressedSize ) zip64Block._uncompressedSize = reader.ReadInt64();
- if ( readCompressedSize ) zip64Block._compressedSize = reader.ReadInt64();
- if ( readLocalHeaderOffset ) zip64Block._localHeaderOffset = reader.ReadInt64();
- if ( readStartDiskNumber ) zip64Block._startDiskNumber = reader.ReadInt32();
- // original values are unsigned, so implies value is too big to fit in signed integer
- if ( zip64Block._uncompressedSize < 0 ) throw new ZipArchiveException("FieldTooBigUncompressedSize");
- if ( zip64Block._compressedSize < 0 ) throw new ZipArchiveException("FieldTooBigCompressedSize");
- if ( zip64Block._localHeaderOffset < 0 ) throw new ZipArchiveException("FieldTooBigLocalHeaderOffset");
- if ( zip64Block._startDiskNumber < 0 ) throw new ZipArchiveException("FieldTooBigStartDiskNumber");
- return true;
- }
- }
- finally
- {
- if ( ms != null )
- ms.Dispose();
- }
- }
-
- }
- internal struct Zip64EndOfCentralDirectoryLocator
- {
- public const uint SignatureConstant = 0x07064B50;
- public const int SizeOfBlockWithoutSignature = 16;
- public uint NumberOfDiskWithZip64EOCD;
- public ulong OffsetOfZip64EOCD;
- public uint TotalNumberOfDisks;
- public static bool TryReadBlock(BinaryReader reader, out Zip64EndOfCentralDirectoryLocator zip64EOCDLocator)
- {
- zip64EOCDLocator = new Zip64EndOfCentralDirectoryLocator();
- if ( reader.ReadUInt32() != SignatureConstant )
- return false;
- zip64EOCDLocator.NumberOfDiskWithZip64EOCD = reader.ReadUInt32();
- zip64EOCDLocator.OffsetOfZip64EOCD = reader.ReadUInt64();
- zip64EOCDLocator.TotalNumberOfDisks = reader.ReadUInt32();
- return true;
- }
- }
- internal struct Zip64EndOfCentralDirectoryRecord
- {
- private const uint SignatureConstant = 0x06064B50;
- private const ulong NormalSize = 0x2C; // the size of the data excluding the size/signature fields if no extra data included
- public ulong SizeOfThisRecord;
- public ushort VersionMadeBy;
- public ushort VersionNeededToExtract;
- public uint NumberOfThisDisk;
- public uint NumberOfDiskWithStartOfCD;
- public ulong NumberOfEntriesOnThisDisk;
- public ulong NumberOfEntriesTotal;
- public ulong SizeOfCentralDirectory;
- public ulong OffsetOfCentralDirectory;
- public static bool TryReadBlock(BinaryReader reader, out Zip64EndOfCentralDirectoryRecord zip64EOCDRecord)
- {
- zip64EOCDRecord = new Zip64EndOfCentralDirectoryRecord();
- if ( reader.ReadUInt32() != SignatureConstant )
- return false;
- zip64EOCDRecord.SizeOfThisRecord = reader.ReadUInt64();
- zip64EOCDRecord.VersionMadeBy = reader.ReadUInt16();
- zip64EOCDRecord.VersionNeededToExtract = reader.ReadUInt16();
- zip64EOCDRecord.NumberOfThisDisk = reader.ReadUInt32();
- zip64EOCDRecord.NumberOfDiskWithStartOfCD = reader.ReadUInt32();
- zip64EOCDRecord.NumberOfEntriesOnThisDisk = reader.ReadUInt64();
- zip64EOCDRecord.NumberOfEntriesTotal = reader.ReadUInt64();
- zip64EOCDRecord.SizeOfCentralDirectory = reader.ReadUInt64();
- zip64EOCDRecord.OffsetOfCentralDirectory = reader.ReadUInt64();
- return true;
- }
- }
- internal struct ZipLocalFileHeader
- {
- public const uint DataDescriptorSignature = 0x08074B50;
- public const uint SignatureConstant = 0x04034B50;
- public const int OffsetToCrcFromHeaderStart = 14;
- public const int OffsetToBitFlagFromHeaderStart = 6;
- public const int SizeOfLocalHeader = 30;
- // will not throw end of stream exception
- public static bool TrySkipBlock(BinaryReader reader)
- {
- const int OffsetToFilenameLength = 22; // from the point after the signature
- if ( reader.ReadUInt32() != SignatureConstant )
- return false;
- if ( reader.BaseStream.Length < reader.BaseStream.Position + OffsetToFilenameLength )
- return false;
- reader.BaseStream.Seek(OffsetToFilenameLength, SeekOrigin.Current);
- ushort filenameLength = reader.ReadUInt16();
- ushort extraFieldLength = reader.ReadUInt16();
- if ( reader.BaseStream.Length < reader.BaseStream.Position + filenameLength + extraFieldLength )
- return false;
- reader.BaseStream.Seek(filenameLength + extraFieldLength, SeekOrigin.Current);
- return true;
- }
- }
- internal struct ZipCentralDirectoryFileHeader
- {
- public const uint SignatureConstant = 0x02014B50;
- public byte VersionMadeByCompatibility;
- public byte VersionMadeBySpecification;
- public ushort VersionNeededToExtract;
- public ushort GeneralPurposeBitFlag;
- public ushort CompressionMethod;
- public uint LastModified; // convert this on the fly
- public uint Crc32;
- public long CompressedSize;
- public long UncompressedSize;
- public ushort FilenameLength;
- public ushort ExtraFieldLength;
- public ushort FileCommentLength;
- public int DiskNumberStart;
- public ushort InternalFileAttributes;
- public uint ExternalFileAttributes;
- public long RelativeOffsetOfLocalHeader;
- public byte[] Filename;
- public byte[] FileComment;
- public List<ZipGenericExtraField> ExtraFields;
- // if saveExtraFieldsAndComments is false, FileComment and ExtraFields will be null
- // in either case, the zip64 extra field info will be incorporated into other fields
- public static bool TryReadBlock(BinaryReader reader, out ZipCentralDirectoryFileHeader header)
- {
- header = new ZipCentralDirectoryFileHeader();
- if ( reader.ReadUInt32() != SignatureConstant )
- return false;
- header.VersionMadeBySpecification = reader.ReadByte();
- header.VersionMadeByCompatibility = reader.ReadByte();
- header.VersionNeededToExtract = reader.ReadUInt16();
- header.GeneralPurposeBitFlag = reader.ReadUInt16();
- header.CompressionMethod = reader.ReadUInt16();
- header.LastModified = reader.ReadUInt32();
- header.Crc32 = reader.ReadUInt32();
- uint compressedSizeSmall = reader.ReadUInt32();
- uint uncompressedSizeSmall = reader.ReadUInt32();
- header.FilenameLength = reader.ReadUInt16();
- header.ExtraFieldLength = reader.ReadUInt16();
- header.FileCommentLength = reader.ReadUInt16();
- ushort diskNumberStartSmall = reader.ReadUInt16();
- header.InternalFileAttributes = reader.ReadUInt16();
- header.ExternalFileAttributes = reader.ReadUInt32();
- uint relativeOffsetOfLocalHeaderSmall = reader.ReadUInt32();
- header.Filename = reader.ReadBytes(header.FilenameLength);
- bool uncompressedSizeInZip64 = uncompressedSizeSmall == ZipHelper.Mask32Bit;
- bool compressedSizeInZip64 = compressedSizeSmall == ZipHelper.Mask32Bit;
- bool relativeOffsetInZip64 = relativeOffsetOfLocalHeaderSmall == ZipHelper.Mask32Bit;
- bool diskNumberStartInZip64 = diskNumberStartSmall == ZipHelper.Mask16Bit;
- Zip64ExtraField zip64;
- long endExtraFields = reader.BaseStream.Position + header.ExtraFieldLength;
- using ( Stream str = new SubReadOnlyStream(reader.BaseStream, reader.BaseStream.Position, header.ExtraFieldLength, leaveOpen: true) )
- {
- header.ExtraFields = null;
- zip64 = Zip64ExtraField.GetJustZip64Block(str,
- uncompressedSizeInZip64, compressedSizeInZip64,
- relativeOffsetInZip64, diskNumberStartInZip64);
- }
- // There are zip files that have malformed ExtraField blocks in which GetJustZip64Block() silently bails out without reading all the way to the end
- // of the ExtraField block. Thus we must force the stream's position to the proper place.
- reader.BaseStream.AdvanceToPosition(endExtraFields);
- reader.BaseStream.Position += header.FileCommentLength;
- header.FileComment = null;
- header.UncompressedSize = zip64.UncompressedSize == null
- ? uncompressedSizeSmall
- : zip64.UncompressedSize.Value;
- header.CompressedSize = zip64.CompressedSize == null
- ? compressedSizeSmall
- : zip64.CompressedSize.Value;
- header.RelativeOffsetOfLocalHeader = zip64.LocalHeaderOffset == null
- ? relativeOffsetOfLocalHeaderSmall
- : zip64.LocalHeaderOffset.Value;
- header.DiskNumberStart = zip64.StartDiskNumber == null
- ? diskNumberStartSmall
- : zip64.StartDiskNumber.Value;
- return true;
- }
- }
- internal struct ZipEndOfCentralDirectoryBlock
- {
- public const uint SignatureConstant = 0x06054B50;
- public const int SizeOfBlockWithoutSignature = 18;
- public uint Signature;
- public ushort NumberOfThisDisk;
- public ushort NumberOfTheDiskWithTheStartOfTheCentralDirectory;
- public ushort NumberOfEntriesInTheCentralDirectoryOnThisDisk;
- public ushort NumberOfEntriesInTheCentralDirectory;
- public uint SizeOfCentralDirectory;
- public uint OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
- public byte[] ArchiveComment;
- public static bool TryReadBlock(BinaryReader reader, out ZipEndOfCentralDirectoryBlock eocdBlock)
- {
- eocdBlock = new ZipEndOfCentralDirectoryBlock();
- if ( reader.ReadUInt32() != SignatureConstant )
- return false;
- eocdBlock.Signature = SignatureConstant;
- eocdBlock.NumberOfThisDisk = reader.ReadUInt16();
- eocdBlock.NumberOfTheDiskWithTheStartOfTheCentralDirectory = reader.ReadUInt16();
- eocdBlock.NumberOfEntriesInTheCentralDirectoryOnThisDisk = reader.ReadUInt16();
- eocdBlock.NumberOfEntriesInTheCentralDirectory = reader.ReadUInt16();
- eocdBlock.SizeOfCentralDirectory = reader.ReadUInt32();
- eocdBlock.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = reader.ReadUInt32();
- ushort commentLength = reader.ReadUInt16();
- eocdBlock.ArchiveComment = reader.ReadBytes(commentLength);
- return true;
- }
- }
- internal static class ZipHelper
- {
- internal const uint Mask32Bit = 0xFFFFFFFF;
- internal const ushort Mask16Bit = 0xFFFF;
- private const int BackwardsSeekingBufferSize = 32;
- /// <summary>
- /// Reads exactly bytesToRead out of stream, unless it is out of bytes
- /// </summary>
- internal static void ReadBytes(Stream stream, byte[] buffer, int bytesToRead)
- {
- int bytesLeftToRead = bytesToRead;
- int totalBytesRead = 0;
- while (bytesLeftToRead > 0)
- {
- int bytesRead = stream.Read(buffer, totalBytesRead, bytesLeftToRead);
- if (bytesRead == 0) throw new IOException();
- totalBytesRead += bytesRead;
- bytesLeftToRead -= bytesRead;
- }
- }
- // assumes all bytes of signatureToFind are non zero, looks backwards from current position in stream,
- // if the signature is found then returns true and positions stream at first byte of signature
- // if the signature is not found, returns false
- internal static bool SeekBackwardsToSignature(Stream stream, uint signatureToFind)
- {
- int bufferPointer = 0;
- uint currentSignature = 0;
- byte[] buffer = new byte[BackwardsSeekingBufferSize];
- bool outOfBytes = false;
- bool signatureFound = false;
- while (!signatureFound && !outOfBytes)
- {
- outOfBytes = SeekBackwardsAndRead(stream, buffer, out bufferPointer);
- Debug.Assert(bufferPointer < buffer.Length);
- while (bufferPointer >= 0 && !signatureFound)
- {
- currentSignature = (currentSignature << 8) | ((uint)buffer[bufferPointer]);
- if (currentSignature == signatureToFind)
- {
- signatureFound = true;
- }
- else
- {
- bufferPointer--;
- }
- }
- }
- if (!signatureFound)
- {
- return false;
- }
- else
- {
- stream.Seek(bufferPointer, SeekOrigin.Current);
- return true;
- }
- }
- // Skip to a further position downstream (without relying on the stream being seekable)
- internal static void AdvanceToPosition(this Stream stream, long position)
- {
- long numBytesLeft = position - stream.Position;
- Debug.Assert(numBytesLeft >= 0);
- while (numBytesLeft != 0)
- {
- const int throwAwayBufferSize = 64;
- int numBytesToSkip = (numBytesLeft > throwAwayBufferSize) ? throwAwayBufferSize : (int)numBytesLeft;
- int numBytesActuallySkipped = stream.Read(new byte[throwAwayBufferSize], 0, numBytesToSkip);
- if (numBytesActuallySkipped == 0)
- throw new IOException();
- numBytesLeft -= numBytesActuallySkipped;
- }
- }
- // Returns true if we are out of bytes
- private static bool SeekBackwardsAndRead(Stream stream, byte[] buffer, out int bufferPointer)
- {
- if (stream.Position >= buffer.Length)
- {
- stream.Seek(-buffer.Length, SeekOrigin.Current);
- ReadBytes(stream, buffer, buffer.Length);
- stream.Seek(-buffer.Length, SeekOrigin.Current);
- bufferPointer = buffer.Length - 1;
- return false;
- }
- else
- {
- int bytesToRead = (int)stream.Position;
- stream.Seek(0, SeekOrigin.Begin);
- ReadBytes(stream, buffer, bytesToRead);
- stream.Seek(0, SeekOrigin.Begin);
- bufferPointer = bytesToRead - 1;
- return true;
- }
- }
- }
- public class ZipArchiveException : Exception
- {
- public ZipArchiveException(string msg) : base(msg)
- { }
- public ZipArchiveException(string msg, Exception inner)
- : base(msg, inner)
- {
- }
- }
- public static class ZipArchiveUtils
- {
- public static void ReadEndOfCentralDirectory(Stream stream, BinaryReader reader, out long expectedNumberOfEntries, out long centralDirectoryStart)
- {
- try
- {
- // this seeks to the start of the end of central directory record
- stream.Seek(-ZipEndOfCentralDirectoryBlock.SizeOfBlockWithoutSignature, SeekOrigin.End);
- if (!ZipHelper.SeekBackwardsToSignature(stream, ZipEndOfCentralDirectoryBlock.SignatureConstant))
- throw new ZipArchiveException("SignatureConstant");
- long eocdStart = stream.Position;
- // read the EOCD
- ZipEndOfCentralDirectoryBlock eocd;
- bool eocdProper = ZipEndOfCentralDirectoryBlock.TryReadBlock(reader, 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 ZipArchiveException("SplitSpanned");
- centralDirectoryStart = eocd.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
- if (eocd.NumberOfEntriesInTheCentralDirectory != eocd.NumberOfEntriesInTheCentralDirectoryOnThisDisk)
- throw new ZipArchiveException("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
- stream.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(stream, Zip64EndOfCentralDirectoryLocator.SignatureConstant))
- {
- // use locator to get to Zip64EOCD
- Zip64EndOfCentralDirectoryLocator locator;
- bool zip64eocdLocatorProper = Zip64EndOfCentralDirectoryLocator.TryReadBlock(reader, 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 ZipArchiveException("FieldTooBigOffsetToZip64EOCD");
- long zip64EOCDOffset = (long)locator.OffsetOfZip64EOCD;
- stream.Seek(zip64EOCDOffset, SeekOrigin.Begin);
- // read Zip64EOCD
- Zip64EndOfCentralDirectoryRecord record;
- if (!Zip64EndOfCentralDirectoryRecord.TryReadBlock(reader, out record))
- throw new ZipArchiveException("Zip64EOCDNotWhereExpected");
- if (record.NumberOfEntriesTotal > long.MaxValue)
- throw new ZipArchiveException("FieldTooBigNumEntries");
- if (record.OffsetOfCentralDirectory > long.MaxValue)
- throw new ZipArchiveException("FieldTooBigOffsetToCD");
- if (record.NumberOfEntriesTotal != record.NumberOfEntriesOnThisDisk)
- throw new ZipArchiveException("SplitSpanned");
- expectedNumberOfEntries = (long)record.NumberOfEntriesTotal;
- centralDirectoryStart = (long)record.OffsetOfCentralDirectory;
- }
- }
- if (centralDirectoryStart > stream.Length)
- {
- throw new ZipArchiveException("FieldTooBigOffsetToCD");
- }
- }
- catch (EndOfStreamException ex)
- {
- throw new ZipArchiveException("CDCorrupt", ex);
- }
- catch (IOException ex)
- {
- throw new ZipArchiveException("CDCorrupt", ex);
- }
- }
- }
- }
|