// ZipInputStream.cs 
 | 
// 
 | 
// Copyright (C) 2001 Mike Krueger 
 | 
// Copyright (C) 2004 John Reilly 
 | 
// 
 | 
// This file was translated from java, it was part of the GNU Classpath 
 | 
// Copyright (C) 2001 Free Software Foundation, Inc. 
 | 
// 
 | 
// This program is free software; you can redistribute it and/or 
 | 
// modify it under the terms of the GNU General Public License 
 | 
// as published by the Free Software Foundation; either version 2 
 | 
// of the License, or (at your option) any later version. 
 | 
// 
 | 
// This program is distributed in the hope that it will be useful, 
 | 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
 | 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 | 
// GNU General Public License for more details. 
 | 
// 
 | 
// You should have received a copy of the GNU General Public License 
 | 
// along with this program; if not, write to the Free Software 
 | 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
 | 
// 
 | 
// Linking this library statically or dynamically with other modules is 
 | 
// making a combined work based on this library.  Thus, the terms and 
 | 
// conditions of the GNU General Public License cover the whole 
 | 
// combination. 
 | 
//  
 | 
// As a special exception, the copyright holders of this library give you 
 | 
// permission to link this library with independent modules to produce an 
 | 
// executable, regardless of the license terms of these independent 
 | 
// modules, and to copy and distribute the resulting executable under 
 | 
// terms of your choice, provided that you also meet, for each linked 
 | 
// independent module, the terms and conditions of the license of that 
 | 
// module.  An independent module is a module which is not derived from 
 | 
// or based on this library.  If you modify this library, you may extend 
 | 
// this exception to your version of the library, but you are not 
 | 
// obligated to do so.  If you do not wish to do so, delete this 
 | 
// exception statement from your version. 
 | 
  
 | 
// HISTORY 
 | 
//    2010-05-25    Z-1663    Fixed exception when testing local header compressed size of -1 
 | 
  
 | 
using System; 
 | 
using System.IO; 
 | 
  
 | 
using ICSharpCode.SharpZipLib.Checksums; 
 | 
using ICSharpCode.SharpZipLib.Zip.Compression; 
 | 
using ICSharpCode.SharpZipLib.Zip.Compression.Streams; 
 | 
  
 | 
#if !NETCF_1_0 
 | 
using ICSharpCode.SharpZipLib.Encryption; 
 | 
#endif 
 | 
  
 | 
namespace ICSharpCode.SharpZipLib.Zip 
 | 
{ 
 | 
    /// <summary> 
 | 
    /// This is an InflaterInputStream that reads the files baseInputStream an zip archive 
 | 
    /// one after another.  It has a special method to get the zip entry of 
 | 
    /// the next file.  The zip entry contains information about the file name 
 | 
    /// size, compressed size, Crc, etc. 
 | 
    /// It includes support for Stored and Deflated entries. 
 | 
    /// <br/> 
 | 
    /// <br/>Author of the original java version : Jochen Hoenicke 
 | 
    /// </summary> 
 | 
    ///  
 | 
    /// <example> This sample shows how to read a zip file 
 | 
    /// <code lang="C#"> 
 | 
    /// using System; 
 | 
    /// using System.Text; 
 | 
    /// using System.IO; 
 | 
    ///  
 | 
    /// using ICSharpCode.SharpZipLib.Zip; 
 | 
    ///  
 | 
    /// class MainClass 
 | 
    /// { 
 | 
    ///     public static void Main(string[] args) 
 | 
    ///     { 
 | 
    ///         using ( ZipInputStream s = new ZipInputStream(File.OpenRead(args[0]))) { 
 | 
    /// 
 | 
    ///             ZipEntry theEntry; 
 | 
    ///             const int size = 2048; 
 | 
    ///             byte[] data = new byte[2048]; 
 | 
    ///              
 | 
    ///             while ((theEntry = s.GetNextEntry()) != null) { 
 | 
    ///                 if ( entry.IsFile ) { 
 | 
    ///                     Console.Write("Show contents (y/n) ?"); 
 | 
    ///                     if (Console.ReadLine() == "y") { 
 | 
    ///                         while (true) { 
 | 
    ///                             size = s.Read(data, 0, data.Length); 
 | 
    ///                             if (size > 0) { 
 | 
    ///                                 Console.Write(new ASCIIEncoding().GetString(data, 0, size)); 
 | 
    ///                             } else { 
 | 
    ///                                 break; 
 | 
    ///                             } 
 | 
    ///                         } 
 | 
    ///                     } 
 | 
    ///                 } 
 | 
    ///             } 
 | 
    ///         } 
 | 
    ///     } 
 | 
    /// } 
 | 
    /// </code> 
 | 
    /// </example> 
 | 
    public class ZipInputStream : InflaterInputStream 
 | 
    { 
 | 
        #region Instance Fields 
 | 
  
 | 
        /// <summary> 
 | 
        /// Delegate for reading bytes from a stream.  
 | 
        /// </summary> 
 | 
        delegate int ReadDataHandler(byte[] b, int offset, int length); 
 | 
         
 | 
        /// <summary> 
 | 
        /// The current reader this instance. 
 | 
        /// </summary> 
 | 
        ReadDataHandler internalReader; 
 | 
         
 | 
        Crc32 crc = new Crc32(); 
 | 
        ZipEntry entry; 
 | 
         
 | 
        long size; 
 | 
        int method; 
 | 
        int flags; 
 | 
        string password; 
 | 
        #endregion 
 | 
  
 | 
        #region Constructors 
 | 
        /// <summary> 
 | 
        /// Creates a new Zip input stream, for reading a zip archive. 
 | 
        /// </summary> 
 | 
        /// <param name="baseInputStream">The underlying <see cref="Stream"/> providing data.</param> 
 | 
        public ZipInputStream(Stream baseInputStream) 
 | 
            : base(baseInputStream, new Inflater(true)) 
 | 
        { 
 | 
            internalReader = new ReadDataHandler(ReadingNotAvailable); 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Creates a new Zip input stream, for reading a zip archive. 
 | 
        /// </summary> 
 | 
        /// <param name="baseInputStream">The underlying <see cref="Stream"/> providing data.</param> 
 | 
        /// <param name="bufferSize">Size of the buffer.</param> 
 | 
        public ZipInputStream( Stream baseInputStream, int bufferSize ) 
 | 
            : base(baseInputStream, new Inflater(true), bufferSize) 
 | 
        { 
 | 
            internalReader = new ReadDataHandler(ReadingNotAvailable); 
 | 
        } 
 | 
        #endregion 
 | 
         
 | 
        /// <summary> 
 | 
        /// Optional password used for encryption when non-null 
 | 
        /// </summary> 
 | 
        /// <value>A password for all encrypted <see cref="ZipEntry">entries </see> in this <see cref="ZipInputStream"/></value> 
 | 
        public string Password 
 | 
        { 
 | 
            get { 
 | 
                return password; 
 | 
            } 
 | 
            set { 
 | 
                password = value; 
 | 
            } 
 | 
        } 
 | 
         
 | 
         
 | 
        /// <summary> 
 | 
        /// Gets a value indicating if there is a current entry and it can be decompressed 
 | 
        /// </summary> 
 | 
        /// <remarks> 
 | 
        /// The entry can only be decompressed if the library supports the zip features required to extract it. 
 | 
        /// See the <see cref="ZipEntry.Version">ZipEntry Version</see> property for more details. 
 | 
        /// </remarks> 
 | 
        public bool CanDecompressEntry { 
 | 
            get { 
 | 
                return (entry != null) && entry.CanDecompress; 
 | 
            } 
 | 
        } 
 | 
         
 | 
        /// <summary> 
 | 
        /// Advances to the next entry in the archive 
 | 
        /// </summary> 
 | 
        /// <returns> 
 | 
        /// The next <see cref="ZipEntry">entry</see> in the archive or null if there are no more entries. 
 | 
        /// </returns> 
 | 
        /// <remarks> 
 | 
        /// If the previous entry is still open <see cref="CloseEntry">CloseEntry</see> is called. 
 | 
        /// </remarks> 
 | 
        /// <exception cref="InvalidOperationException"> 
 | 
        /// Input stream is closed 
 | 
        /// </exception> 
 | 
        /// <exception cref="ZipException"> 
 | 
        /// Password is not set, password is invalid, compression method is invalid, 
 | 
        /// version required to extract is not supported 
 | 
        /// </exception> 
 | 
        public ZipEntry GetNextEntry() 
 | 
        { 
 | 
            if (crc == null) { 
 | 
                throw new InvalidOperationException("Closed."); 
 | 
            } 
 | 
             
 | 
            if (entry != null) { 
 | 
                CloseEntry(); 
 | 
            } 
 | 
             
 | 
            int header = inputBuffer.ReadLeInt(); 
 | 
             
 | 
            if (header == ZipConstants.CentralHeaderSignature || 
 | 
                header == ZipConstants.EndOfCentralDirectorySignature || 
 | 
                header == ZipConstants.CentralHeaderDigitalSignature || 
 | 
                header == ZipConstants.ArchiveExtraDataSignature || 
 | 
                header == ZipConstants.Zip64CentralFileHeaderSignature) { 
 | 
                // No more individual entries exist 
 | 
                Close(); 
 | 
                return null; 
 | 
            } 
 | 
             
 | 
            // -jr- 07-Dec-2003 Ignore spanning temporary signatures if found 
 | 
            // Spanning signature is same as descriptor signature and is untested as yet. 
 | 
            if ( (header == ZipConstants.SpanningTempSignature) || (header == ZipConstants.SpanningSignature) ) { 
 | 
                header = inputBuffer.ReadLeInt(); 
 | 
            } 
 | 
             
 | 
            if (header != ZipConstants.LocalHeaderSignature) { 
 | 
                throw new ZipException("Wrong Local header signature: 0x" + String.Format("{0:X}", header)); 
 | 
            } 
 | 
             
 | 
            short versionRequiredToExtract = (short)inputBuffer.ReadLeShort(); 
 | 
             
 | 
            flags          = inputBuffer.ReadLeShort(); 
 | 
            method         = inputBuffer.ReadLeShort(); 
 | 
            uint dostime   = (uint)inputBuffer.ReadLeInt(); 
 | 
            int crc2       = inputBuffer.ReadLeInt(); 
 | 
            csize          = inputBuffer.ReadLeInt(); 
 | 
            size           = inputBuffer.ReadLeInt(); 
 | 
            int nameLen    = inputBuffer.ReadLeShort(); 
 | 
            int extraLen   = inputBuffer.ReadLeShort(); 
 | 
             
 | 
            bool isCrypted = (flags & 1) == 1; 
 | 
             
 | 
            byte[] buffer = new byte[nameLen]; 
 | 
            inputBuffer.ReadRawBuffer(buffer); 
 | 
             
 | 
            string name = ZipConstants.ConvertToStringExt(flags, buffer); 
 | 
             
 | 
            entry = new ZipEntry(name, versionRequiredToExtract); 
 | 
            entry.Flags = flags; 
 | 
             
 | 
            entry.CompressionMethod = (CompressionMethod)method; 
 | 
             
 | 
            if ((flags & 8) == 0) { 
 | 
                entry.Crc  = crc2 & 0xFFFFFFFFL; 
 | 
                entry.Size = size & 0xFFFFFFFFL; 
 | 
                entry.CompressedSize = csize & 0xFFFFFFFFL; 
 | 
  
 | 
                entry.CryptoCheckValue = (byte)((crc2 >> 24) & 0xff); 
 | 
  
 | 
            } else { 
 | 
                 
 | 
                // This allows for GNU, WinZip and possibly other archives, the PKZIP spec 
 | 
                // says these values are zero under these circumstances. 
 | 
                if (crc2 != 0) { 
 | 
                    entry.Crc = crc2 & 0xFFFFFFFFL; 
 | 
                } 
 | 
                 
 | 
                if (size != 0) { 
 | 
                    entry.Size = size & 0xFFFFFFFFL; 
 | 
                } 
 | 
  
 | 
                if (csize != 0) { 
 | 
                    entry.CompressedSize = csize & 0xFFFFFFFFL; 
 | 
                } 
 | 
  
 | 
                entry.CryptoCheckValue = (byte)((dostime >> 8) & 0xff); 
 | 
            } 
 | 
             
 | 
            entry.DosTime = dostime; 
 | 
  
 | 
            // If local header requires Zip64 is true then the extended header should contain 
 | 
            // both values. 
 | 
  
 | 
            // Handle extra data if present.  This can set/alter some fields of the entry. 
 | 
            if (extraLen > 0) { 
 | 
                byte[] extra = new byte[extraLen]; 
 | 
                inputBuffer.ReadRawBuffer(extra); 
 | 
                entry.ExtraData = extra; 
 | 
            } 
 | 
  
 | 
            entry.ProcessExtraData(true); 
 | 
            if ( entry.CompressedSize >= 0 ) { 
 | 
                csize = entry.CompressedSize; 
 | 
            } 
 | 
  
 | 
            if ( entry.Size >= 0 ) { 
 | 
                size = entry.Size; 
 | 
            } 
 | 
             
 | 
            if (method == (int)CompressionMethod.Stored && (!isCrypted && csize != size || (isCrypted && csize - ZipConstants.CryptoHeaderSize != size))) { 
 | 
                throw new ZipException("Stored, but compressed != uncompressed"); 
 | 
            } 
 | 
  
 | 
            // Determine how to handle reading of data if this is attempted. 
 | 
            if (entry.IsCompressionMethodSupported()) { 
 | 
                internalReader = new ReadDataHandler(InitialRead); 
 | 
            } else { 
 | 
                internalReader = new ReadDataHandler(ReadingNotSupported); 
 | 
            } 
 | 
             
 | 
            return entry; 
 | 
        } 
 | 
         
 | 
        /// <summary> 
 | 
        /// Read data descriptor at the end of compressed data.  
 | 
        /// </summary> 
 | 
        void ReadDataDescriptor() 
 | 
        { 
 | 
            if (inputBuffer.ReadLeInt() != ZipConstants.DataDescriptorSignature) { 
 | 
                throw new ZipException("Data descriptor signature not found"); 
 | 
            } 
 | 
             
 | 
            entry.Crc = inputBuffer.ReadLeInt() & 0xFFFFFFFFL; 
 | 
             
 | 
            if ( entry.LocalHeaderRequiresZip64 ) { 
 | 
                csize = inputBuffer.ReadLeLong(); 
 | 
                size = inputBuffer.ReadLeLong(); 
 | 
            } else { 
 | 
                csize = inputBuffer.ReadLeInt(); 
 | 
                size = inputBuffer.ReadLeInt(); 
 | 
            } 
 | 
            entry.CompressedSize = csize; 
 | 
            entry.Size = size; 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Complete cleanup as the final part of closing. 
 | 
        /// </summary> 
 | 
        /// <param name="testCrc">True if the crc value should be tested</param> 
 | 
        void CompleteCloseEntry(bool testCrc) 
 | 
        { 
 | 
            StopDecrypting(); 
 | 
                 
 | 
            if ((flags & 8) != 0) { 
 | 
                ReadDataDescriptor(); 
 | 
            } 
 | 
                 
 | 
            size = 0; 
 | 
  
 | 
            if ( testCrc && 
 | 
                ((crc.Value & 0xFFFFFFFFL) != entry.Crc) && (entry.Crc != -1)) { 
 | 
                throw new ZipException("CRC mismatch"); 
 | 
            } 
 | 
  
 | 
            crc.Reset(); 
 | 
  
 | 
            if (method == (int)CompressionMethod.Deflated) { 
 | 
                inf.Reset(); 
 | 
            } 
 | 
            entry = null; 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Closes the current zip entry and moves to the next one. 
 | 
        /// </summary> 
 | 
        /// <exception cref="InvalidOperationException"> 
 | 
        /// The stream is closed 
 | 
        /// </exception> 
 | 
        /// <exception cref="ZipException"> 
 | 
        /// The Zip stream ends early 
 | 
        /// </exception> 
 | 
        public void CloseEntry() 
 | 
        { 
 | 
            if (crc == null) { 
 | 
                throw new InvalidOperationException("Closed"); 
 | 
            } 
 | 
             
 | 
            if (entry == null) { 
 | 
                return; 
 | 
            } 
 | 
             
 | 
            if (method == (int)CompressionMethod.Deflated) { 
 | 
                if ((flags & 8) != 0) { 
 | 
                    // We don't know how much we must skip, read until end. 
 | 
                    byte[] tmp = new byte[4096]; 
 | 
  
 | 
                    // Read will close this entry 
 | 
                    while (Read(tmp, 0, tmp.Length) > 0) { 
 | 
                    } 
 | 
                    return; 
 | 
                } 
 | 
  
 | 
                csize -= inf.TotalIn; 
 | 
                inputBuffer.Available += inf.RemainingInput; 
 | 
            } 
 | 
         
 | 
            if ( (inputBuffer.Available > csize) && (csize >= 0) ) { 
 | 
                inputBuffer.Available = (int)((long)inputBuffer.Available - csize); 
 | 
            } else { 
 | 
                csize -= inputBuffer.Available; 
 | 
                inputBuffer.Available = 0; 
 | 
                while (csize != 0) { 
 | 
                    long skipped = base.Skip(csize); 
 | 
                 
 | 
                    if (skipped <= 0) { 
 | 
                        throw new ZipException("Zip archive ends early."); 
 | 
                    } 
 | 
                 
 | 
                    csize -= skipped; 
 | 
                } 
 | 
            } 
 | 
  
 | 
            CompleteCloseEntry(false); 
 | 
        } 
 | 
         
 | 
        /// <summary> 
 | 
        /// Returns 1 if there is an entry available 
 | 
        /// Otherwise returns 0. 
 | 
        /// </summary> 
 | 
        public override int Available { 
 | 
            get { 
 | 
                return entry != null ? 1 : 0; 
 | 
            } 
 | 
        } 
 | 
         
 | 
        /// <summary> 
 | 
        /// Returns the current size that can be read from the current entry if available 
 | 
        /// </summary> 
 | 
        /// <exception cref="ZipException">Thrown if the entry size is not known.</exception> 
 | 
        /// <exception cref="InvalidOperationException">Thrown if no entry is currently available.</exception> 
 | 
        public override long Length 
 | 
        { 
 | 
            get { 
 | 
                if ( entry != null ) { 
 | 
                    if ( entry.Size >= 0 ) { 
 | 
                        return entry.Size; 
 | 
                    } else { 
 | 
                        throw new ZipException("Length not available for the current entry"); 
 | 
                    } 
 | 
                } 
 | 
                else { 
 | 
                    throw new InvalidOperationException("No current entry"); 
 | 
                } 
 | 
            } 
 | 
                 
 | 
        } 
 | 
         
 | 
        /// <summary> 
 | 
        /// Reads a byte from the current zip entry. 
 | 
        /// </summary> 
 | 
        /// <returns> 
 | 
        /// The byte or -1 if end of stream is reached. 
 | 
        /// </returns> 
 | 
        public override int ReadByte() 
 | 
        { 
 | 
            byte[] b = new byte[1]; 
 | 
            if (Read(b, 0, 1) <= 0) { 
 | 
                return -1; 
 | 
            } 
 | 
            return b[0] & 0xff; 
 | 
        } 
 | 
         
 | 
        /// <summary> 
 | 
        /// Handle attempts to read by throwing an <see cref="InvalidOperationException"/>. 
 | 
        /// </summary> 
 | 
        /// <param name="destination">The destination array to store data in.</param> 
 | 
        /// <param name="offset">The offset at which data read should be stored.</param> 
 | 
        /// <param name="count">The maximum number of bytes to read.</param> 
 | 
        /// <returns>Returns the number of bytes actually read.</returns> 
 | 
        int ReadingNotAvailable(byte[] destination, int offset, int count) 
 | 
        { 
 | 
            throw new InvalidOperationException("Unable to read from this stream"); 
 | 
        } 
 | 
         
 | 
        /// <summary> 
 | 
        /// Handle attempts to read from this entry by throwing an exception 
 | 
        /// </summary> 
 | 
        int ReadingNotSupported(byte[] destination, int offset, int count) 
 | 
        { 
 | 
            throw new ZipException("The compression method for this entry is not supported"); 
 | 
        } 
 | 
         
 | 
        /// <summary> 
 | 
        /// Perform the initial read on an entry which may include  
 | 
        /// reading encryption headers and setting up inflation. 
 | 
        /// </summary> 
 | 
        /// <param name="destination">The destination to fill with data read.</param> 
 | 
        /// <param name="offset">The offset to start reading at.</param> 
 | 
        /// <param name="count">The maximum number of bytes to read.</param> 
 | 
        /// <returns>The actual number of bytes read.</returns> 
 | 
        int InitialRead(byte[] destination, int offset, int count) 
 | 
        { 
 | 
            if ( !CanDecompressEntry ) { 
 | 
                throw new ZipException("Library cannot extract this entry. Version required is (" + entry.Version.ToString() + ")"); 
 | 
            } 
 | 
             
 | 
            // Handle encryption if required. 
 | 
            if (entry.IsCrypted) { 
 | 
#if NETCF_1_0 
 | 
                throw new ZipException("Encryption not supported for Compact Framework 1.0"); 
 | 
#else 
 | 
                if (password == null) { 
 | 
                    throw new ZipException("No password set."); 
 | 
                } 
 | 
                 
 | 
                // Generate and set crypto transform... 
 | 
                PkzipClassicManaged managed = new PkzipClassicManaged(); 
 | 
                byte[] key = PkzipClassic.GenerateKeys(ZipConstants.ConvertToArray(password)); 
 | 
                 
 | 
                inputBuffer.CryptoTransform = managed.CreateDecryptor(key, null); 
 | 
                 
 | 
                byte[] cryptbuffer = new byte[ZipConstants.CryptoHeaderSize]; 
 | 
                inputBuffer.ReadClearTextBuffer(cryptbuffer, 0, ZipConstants.CryptoHeaderSize); 
 | 
  
 | 
                if (cryptbuffer[ZipConstants.CryptoHeaderSize - 1] != entry.CryptoCheckValue) { 
 | 
                    throw new ZipException("Invalid password"); 
 | 
                } 
 | 
  
 | 
                if (csize >= ZipConstants.CryptoHeaderSize) { 
 | 
                    csize -= ZipConstants.CryptoHeaderSize; 
 | 
                } 
 | 
                else if ( (entry.Flags & (int)GeneralBitFlags.Descriptor) == 0 ) { 
 | 
                    throw new ZipException(string.Format("Entry compressed size {0} too small for encryption", csize)); 
 | 
                } 
 | 
#endif                 
 | 
            } else { 
 | 
#if !NETCF_1_0 
 | 
                inputBuffer.CryptoTransform = null; 
 | 
#endif                 
 | 
            } 
 | 
  
 | 
            if ((csize > 0) || ((flags & (int)GeneralBitFlags.Descriptor) != 0)) { 
 | 
                if ((method == (int)CompressionMethod.Deflated) && (inputBuffer.Available > 0)) { 
 | 
                    inputBuffer.SetInflaterInput(inf); 
 | 
                } 
 | 
  
 | 
                internalReader = new ReadDataHandler(BodyRead); 
 | 
                return BodyRead(destination, offset, count); 
 | 
            } 
 | 
            else { 
 | 
                internalReader = new ReadDataHandler(ReadingNotAvailable); 
 | 
                return 0; 
 | 
            } 
 | 
        } 
 | 
         
 | 
        /// <summary> 
 | 
        /// Read a block of bytes from the stream. 
 | 
        /// </summary> 
 | 
        /// <param name="buffer">The destination for the bytes.</param> 
 | 
        /// <param name="offset">The index to start storing data.</param> 
 | 
        /// <param name="count">The number of bytes to attempt to read.</param> 
 | 
        /// <returns>Returns the number of bytes read.</returns> 
 | 
        /// <remarks>Zero bytes read means end of stream.</remarks> 
 | 
        public override int Read(byte[] buffer, int offset, int count) 
 | 
        { 
 | 
            if ( buffer == null ) { 
 | 
                throw new ArgumentNullException("buffer"); 
 | 
            } 
 | 
  
 | 
            if ( offset < 0 ) { 
 | 
#if NETCF_1_0 
 | 
                throw new ArgumentOutOfRangeException("offset"); 
 | 
#else 
 | 
                throw new ArgumentOutOfRangeException("offset", "Cannot be negative"); 
 | 
#endif                 
 | 
            } 
 | 
  
 | 
            if ( count < 0 ) { 
 | 
#if NETCF_1_0 
 | 
                throw new ArgumentOutOfRangeException("count"); 
 | 
#else 
 | 
                throw new ArgumentOutOfRangeException("count", "Cannot be negative"); 
 | 
#endif 
 | 
            } 
 | 
  
 | 
            if ( (buffer.Length - offset) < count ) { 
 | 
                throw new ArgumentException("Invalid offset/count combination"); 
 | 
            } 
 | 
  
 | 
            return internalReader(buffer, offset, count); 
 | 
        } 
 | 
         
 | 
        /// <summary> 
 | 
        /// Reads a block of bytes from the current zip entry. 
 | 
        /// </summary> 
 | 
        /// <returns> 
 | 
        /// The number of bytes read (this may be less than the length requested, even before the end of stream), or 0 on end of stream. 
 | 
        /// </returns> 
 | 
        /// <exception name="IOException"> 
 | 
        /// An i/o error occured. 
 | 
        /// </exception> 
 | 
        /// <exception cref="ZipException"> 
 | 
        /// The deflated stream is corrupted. 
 | 
        /// </exception> 
 | 
        /// <exception cref="InvalidOperationException"> 
 | 
        /// The stream is not open. 
 | 
        /// </exception> 
 | 
        int BodyRead(byte[] buffer, int offset, int count) 
 | 
        { 
 | 
            if ( crc == null ) { 
 | 
                throw new InvalidOperationException("Closed"); 
 | 
            } 
 | 
             
 | 
            if ( (entry == null) || (count <= 0) ) { 
 | 
                return 0; 
 | 
            } 
 | 
  
 | 
            if ( offset + count > buffer.Length ) { 
 | 
                throw new ArgumentException("Offset + count exceeds buffer size"); 
 | 
            } 
 | 
             
 | 
            bool finished = false; 
 | 
             
 | 
            switch (method) { 
 | 
                case (int)CompressionMethod.Deflated: 
 | 
                    count = base.Read(buffer, offset, count); 
 | 
                    if (count <= 0) { 
 | 
                        if (!inf.IsFinished) { 
 | 
                            throw new ZipException("Inflater not finished!"); 
 | 
                        } 
 | 
                        inputBuffer.Available = inf.RemainingInput; 
 | 
  
 | 
                        // A csize of -1 is from an unpatched local header 
 | 
                        if ((flags & 8) == 0 && 
 | 
                            (inf.TotalIn != csize && csize != 0xFFFFFFFF && csize != -1 || inf.TotalOut != size)) { 
 | 
                            throw new ZipException("Size mismatch: " + csize + ";" + size + " <-> " + inf.TotalIn + ";" + inf.TotalOut); 
 | 
                        } 
 | 
                        inf.Reset(); 
 | 
                        finished = true; 
 | 
                    } 
 | 
                    break; 
 | 
                     
 | 
                case (int)CompressionMethod.Stored: 
 | 
                    if ( (count > csize) && (csize >= 0) ) { 
 | 
                        count = (int)csize; 
 | 
                    } 
 | 
                     
 | 
                    if ( count > 0 ) { 
 | 
                        count = inputBuffer.ReadClearTextBuffer(buffer, offset, count); 
 | 
                        if (count > 0) { 
 | 
                            csize -= count; 
 | 
                            size -= count; 
 | 
                        } 
 | 
                    } 
 | 
                     
 | 
                    if (csize == 0) { 
 | 
                        finished = true; 
 | 
                    } else { 
 | 
                        if (count < 0) { 
 | 
                            throw new ZipException("EOF in stored block"); 
 | 
                        } 
 | 
                    } 
 | 
                    break; 
 | 
            } 
 | 
             
 | 
            if (count > 0) { 
 | 
                crc.Update(buffer, offset, count); 
 | 
            } 
 | 
             
 | 
            if (finished) { 
 | 
                CompleteCloseEntry(true); 
 | 
            } 
 | 
  
 | 
            return count; 
 | 
        } 
 | 
         
 | 
        /// <summary> 
 | 
        /// Closes the zip input stream 
 | 
        /// </summary> 
 | 
        public override void Close() 
 | 
        { 
 | 
            internalReader = new ReadDataHandler(ReadingNotAvailable); 
 | 
            crc = null; 
 | 
            entry = null; 
 | 
  
 | 
            base.Close(); 
 | 
        } 
 | 
    } 
 | 
} 
 |