/// <summary> /// Opens the entry in Read mode. The returned stream will be readable, and it may or may not be seekable. /// </summary> /// <returns>A Stream that represents the contents of the entry.</returns> /// <exception cref="InvalidDataException"> /// The entry is missing from the archive or is corrupt and cannot be read. /// -or- /// The entry has been compressed using a compression method that is not supported. /// </exception> /// <exception cref="ObjectDisposedException"> /// The ZipArchive that this entry belongs to has been disposed. /// </exception> internal Stream Open() { Archive.ThrowIfDisposed(); if (!IsOpenable(out var message)) { throw new InvalidDataException(message); } // _storedOffsetOfCompressedData will never be null, since we know IsOpenable is true Debug.Assert(_storedOffsetOfCompressedData != null, nameof(_storedOffsetOfCompressedData) + " != null"); Stream compressedStream = new SubReadStream(Archive.ArchiveStream, (long)_storedOffsetOfCompressedData, CompressedLength); return(GetDataDecompressor(compressedStream)); }
// if saveExtraFieldsAndComments is false, FileComment and ExtraFields will be null // in either case, the zip64 extra field info will be incorporated into other fields internal static bool TryReadBlock(BinaryReader reader, out ZipCentralDirectoryFileHeader header) { header = new ZipCentralDirectoryFileHeader(); if (reader.ReadUInt32() != SignatureConstant) { return(false); } reader.ReadByte(); // VersionMadeBySpecification header.VersionMadeByCompatibility = reader.ReadByte(); reader.ReadUInt16(); // VersionNeededToExtract header.GeneralPurposeBitFlag = reader.ReadUInt16(); header.CompressionMethod = reader.ReadUInt16(); header.LastModified = reader.ReadUInt32(); reader.ReadUInt32(); // Crc32 uint compressedSizeSmall = reader.ReadUInt32(); uint uncompressedSizeSmall = reader.ReadUInt32(); header.FilenameLength = reader.ReadUInt16(); header.ExtraFieldLength = reader.ReadUInt16(); header.FileCommentLength = reader.ReadUInt16(); ushort diskNumberStartSmall = reader.ReadUInt16(); reader.ReadUInt16(); // InternalFileAttributes reader.ReadUInt32(); // ExternalFileAttributes 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 SubReadStream(reader.BaseStream, reader.BaseStream.Position, header.ExtraFieldLength)) { 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. // Fen's note: Original did a seek here, which for some reason is like 300x slower than a read, and // also inexplicably causes ReadUInt32() to be 4x as slow and/or occur 4x as often(?!) // Buffer alignments...? I dunno. Anyway. Speed. // Also maybe not a good idea to use something that's faster when I don't know why it's faster. // But my results are the same as the old method, so herpaderp. reader.BaseStream.AdvanceToPosition(endExtraFields + header.FileCommentLength); header.UncompressedSize = zip64.UncompressedSize ?? uncompressedSizeSmall; header.CompressedSize = zip64.CompressedSize ?? compressedSizeSmall; header.RelativeOffsetOfLocalHeader = zip64.LocalHeaderOffset ?? relativeOffsetOfLocalHeaderSmall; header.DiskNumberStart = zip64.StartDiskNumber ?? diskNumberStartSmall; return(true); }