/// <summary> /// Gets the offset of an archive that is positioned 0 or more bytes /// from the start of the Stream. /// </summary> /// <param name="stream">A stream for reading the archive.</param> /// <returns>The offset in bytes of the archive, /// or -1 if no archive is found in the Stream.</returns> /// <remarks>The archive must begin on a 4-byte boundary.</remarks> public override long FindArchiveOffset(Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } long offset = base.FindArchiveOffset(stream); if (offset > 0) { // Some self-extract packages include the exe stub in file offset calculations. // Check the first header directory offset to decide whether the entire // archive needs to be offset or not. ZipEndOfCentralDirectory eocd = this.GetEOCD(null, stream); if (eocd != null && eocd.totalEntries > 0) { stream.Seek(eocd.dirOffset, SeekOrigin.Begin); ZipFileHeader header = new ZipFileHeader(); if (header.Read(stream, true) && header.localHeaderOffset < stream.Length) { stream.Seek(header.localHeaderOffset, SeekOrigin.Begin); if (header.Read(stream, false)) { return(0); } } } } return(offset); }
/// <summary> /// Unpacks a single file from an archive or archive chain. /// </summary> private void UnpackOneFile( IUnpackStreamContext streamContext, ZipFileHeader header, ref Stream archiveStream) { ZipFileInfo fileInfo = null; Stream fileStream = null; try { Converter <Stream, Stream> compressionStreamCreator; if (!ZipEngine.decompressionStreamCreators.TryGetValue( header.compressionMethod, out compressionStreamCreator)) { // Silently skip files of an unsupported compression method. return; } long compressedSize; long uncompressedSize; long localHeaderOffset; int archiveNumber; uint crc; header.GetZip64Fields( out compressedSize, out uncompressedSize, out localHeaderOffset, out archiveNumber, out crc); if (this.currentArchiveNumber != archiveNumber + 1) { if (archiveStream != null) { streamContext.CloseArchiveReadStream( this.currentArchiveNumber, String.Empty, archiveStream); archiveStream = null; this.OnProgress(ArchiveProgressType.FinishArchive); this.currentArchiveName = null; } this.currentArchiveNumber = (short)(archiveNumber + 1); this.currentArchiveBytesProcessed = 0; this.currentArchiveTotalBytes = 0; archiveStream = this.OpenArchive( streamContext, this.currentArchiveNumber); FileStream archiveFileStream = archiveStream as FileStream; this.currentArchiveName = (archiveFileStream != null ? Path.GetFileName(archiveFileStream.Name) : null); this.currentArchiveTotalBytes = archiveStream.Length; this.currentArchiveNumber--; this.OnProgress(ArchiveProgressType.StartArchive); this.currentArchiveNumber++; } archiveStream.Seek(localHeaderOffset, SeekOrigin.Begin); ZipFileHeader localHeader = new ZipFileHeader(); if (!localHeader.Read(archiveStream, false) || !ZipEngine.AreFilePathsEqual(localHeader.fileName, header.fileName)) { string msg = "Could not read file: " + header.fileName; throw new ZipException(msg); } fileInfo = header.ToZipFileInfo(); fileStream = streamContext.OpenFileWriteStream( fileInfo.FullName, fileInfo.Length, fileInfo.LastWriteTime); if (fileStream != null) { this.currentFileName = header.fileName; this.currentFileBytesProcessed = 0; this.currentFileTotalBytes = fileInfo.Length; this.currentArchiveNumber--; this.OnProgress(ArchiveProgressType.StartFile); this.currentArchiveNumber++; this.UnpackFileBytes( streamContext, fileInfo.FullName, fileInfo.CompressedLength, fileInfo.Length, header.crc32, fileStream, compressionStreamCreator, ref archiveStream); } } finally { if (fileStream != null) { streamContext.CloseFileWriteStream( fileInfo.FullName, fileStream, fileInfo.Attributes, fileInfo.LastWriteTime); this.currentArchiveNumber--; this.OnProgress(ArchiveProgressType.FinishFile); this.currentArchiveNumber++; } } }
/// <summary> /// Reads all the file headers from the central directory in the main archive. /// </summary> private IList <ZipFileHeader> GetCentralDirectory(IUnpackStreamContext streamContext) { Stream archiveStream = null; this.currentArchiveNumber = 0; try { List <ZipFileHeader> headers = new List <ZipFileHeader>(); archiveStream = this.OpenArchive(streamContext, 0); ZipEndOfCentralDirectory eocd = this.GetEOCD(streamContext, archiveStream); if (eocd == null) { return(null); } else if (eocd.totalEntries == 0) { return(headers); } headers.Capacity = (int)eocd.totalEntries; if (eocd.dirOffset > archiveStream.Length - ZipFileHeader.CFH_FIXEDSIZE) { streamContext.CloseArchiveReadStream( this.currentArchiveNumber, String.Empty, archiveStream); archiveStream = null; } else { archiveStream.Seek(eocd.dirOffset, SeekOrigin.Begin); uint sig = new BinaryReader(archiveStream).ReadUInt32(); if (sig != ZipFileHeader.CFHSIG) { streamContext.CloseArchiveReadStream( this.currentArchiveNumber, String.Empty, archiveStream); archiveStream = null; } } if (archiveStream == null) { this.currentArchiveNumber = (short)(eocd.dirStartDiskNumber + 1); archiveStream = streamContext.OpenArchiveReadStream( this.currentArchiveNumber, String.Empty, this); if (archiveStream == null) { return(null); } } archiveStream.Seek(eocd.dirOffset, SeekOrigin.Begin); while (headers.Count < eocd.totalEntries) { ZipFileHeader header = new ZipFileHeader(); if (!header.Read(archiveStream, true)) { throw new ZipException( "Missing or invalid central directory file header"); } headers.Add(header); if (headers.Count < eocd.totalEntries && archiveStream.Position == archiveStream.Length) { streamContext.CloseArchiveReadStream( this.currentArchiveNumber, String.Empty, archiveStream); this.currentArchiveNumber++; archiveStream = streamContext.OpenArchiveReadStream( this.currentArchiveNumber, String.Empty, this); if (archiveStream == null) { this.currentArchiveNumber = 0; archiveStream = streamContext.OpenArchiveReadStream( this.currentArchiveNumber, String.Empty, this); } } } return(headers); } finally { if (archiveStream != null) { streamContext.CloseArchiveReadStream( this.currentArchiveNumber, String.Empty, archiveStream); } } }