// InflaterInputStream.cs // Copyright (C) 2001 Mike Krueger // // 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. using System; using System.IO; using ICSharpCode.SharpZipLib.Zip.Compression; using ICSharpCode.SharpZipLib.Checksums; namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams { /// /// This filter stream is used to decompress data compressed baseInputStream the "deflate" /// format. The "deflate" format is described baseInputStream RFC 1951. /// /// This stream may form the basis for other decompression filters, such /// as the GzipInputStream. /// /// author of the original java version : John Leuner /// public class InflaterInputStream : Stream { //Variables /// /// Decompressor for this filter /// protected Inflater inf; /// /// Byte array used as a buffer /// protected byte[] buf; /// /// Size of buffer /// protected int len; //We just use this if we are decoding one byte at a time with the read() call private byte[] onebytebuffer = new byte[1]; /// /// base stream the inflater depends on. /// protected Stream baseInputStream; protected long csize; /// /// I needed to implement the abstract member. /// public override bool CanRead { get { return baseInputStream.CanRead; } } /// /// I needed to implement the abstract member. /// public override bool CanSeek { get { return false; // return baseInputStream.CanSeek; } } /// /// I needed to implement the abstract member. /// public override bool CanWrite { get { return baseInputStream.CanWrite; } } /// /// I needed to implement the abstract member. /// public override long Length { get { return len; } } /// /// I needed to implement the abstract member. /// public override long Position { get { return baseInputStream.Position; } set { baseInputStream.Position = value; } } /// /// Flushes the baseInputStream /// public override void Flush() { baseInputStream.Flush(); } /// /// I needed to implement the abstract member. /// public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException("Seek not supported"); // -jr- 01-Dec-2003 } /// /// I needed to implement the abstract member. /// public override void SetLength(long val) { baseInputStream.SetLength(val); } /// /// I needed to implement the abstract member. /// public override void Write(byte[] array, int offset, int count) { baseInputStream.Write(array, offset, count); } /// /// I needed to implement the abstract member. /// public override void WriteByte(byte val) { baseInputStream.WriteByte(val); } // -jr- 01-Dec-2003 This may be flawed for some base streams? Depends on implementation of BeginWrite public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw new NotSupportedException("Asynch write not currently supported"); } //Constructors /// /// Create an InflaterInputStream with the default decompresseor /// and a default buffer size. /// /// /// the InputStream to read bytes from /// public InflaterInputStream(Stream baseInputStream) : this(baseInputStream, new Inflater(), 4096) { } /// /// Create an InflaterInputStream with the specified decompresseor /// and a default buffer size. /// /// /// the InputStream to read bytes from /// /// /// the decompressor used to decompress data read from baseInputStream /// public InflaterInputStream(Stream baseInputStream, Inflater inf) : this(baseInputStream, inf, 4096) { } /// /// Create an InflaterInputStream with the specified decompresseor /// and a specified buffer size. /// /// /// the InputStream to read bytes from /// /// /// the decompressor used to decompress data read from baseInputStream /// /// /// size of the buffer to use /// public InflaterInputStream(Stream baseInputStream, Inflater inf, int size) { this.baseInputStream = baseInputStream; this.inf = inf; try { this.len = (int)baseInputStream.Length; } catch (Exception) { // the stream may not support .Length this.len = 0; } if (size <= 0) { throw new ArgumentOutOfRangeException("size <= 0"); } buf = new byte[size]; //Create the buffer } //Methods /// /// Returns 0 once the end of the stream (EOF) has been reached. /// Otherwise returns 1. /// public virtual int Available { get { return inf.IsFinished ? 0 : 1; } } /// /// Closes the input stream /// public override void Close() { baseInputStream.Close(); } /// /// Fills the buffer with more data to decompress. /// protected void Fill() { len = baseInputStream.Read(buf, 0, buf.Length); // decrypting crypted data if (cryptbuffer != null) { DecryptBlock(buf, 0, System.Math.Min((int)(csize - inf.TotalIn), buf.Length)); } if (len <= 0) { throw new ApplicationException("Deflated stream ends early."); } inf.SetInput(buf, 0, len); } /// /// Reads one byte of decompressed data. /// /// The byte is baseInputStream the lower 8 bits of the int. /// public override int ReadByte() { int nread = Read(onebytebuffer, 0, 1); //read one byte if (nread > 0) { return onebytebuffer[0] & 0xff; } return -1; // ok } /// /// Decompresses data into the byte array /// /// /// the array to read and decompress data into /// /// /// the offset indicating where the data should be placed /// /// /// the number of bytes to decompress /// public override int Read(byte[] b, int off, int len) { for (;;) { int count; try { count = inf.Inflate(b, off, len); } catch (Exception e) { throw new ZipException(e.ToString()); } if (count > 0) { return count; } if (inf.IsNeedingDictionary) { throw new ZipException("Need a dictionary"); } else if (inf.IsFinished) { return 0; } else if (inf.IsNeedingInput) { Fill(); } else { throw new InvalidOperationException("Don't know what to do"); } } } /// /// Skip specified number of bytes of uncompressed data /// /// /// number of bytes to skip /// public long Skip(long n) { if (n < 0) { throw new ArgumentOutOfRangeException("n"); } int len = 2048; if (n < len) { len = (int) n; } byte[] tmp = new byte[len]; return (long)baseInputStream.Read(tmp, 0, tmp.Length); } #region Encryption stuff protected byte[] cryptbuffer = null; uint[] keys = null; protected byte DecryptByte() { uint temp = ((keys[2] & 0xFFFF) | 2); return (byte)((temp * (temp ^ 1)) >> 8); } protected void DecryptBlock(byte[] buf, int off, int len) { for (int i = off; i < off + len; ++i) { buf[i] ^= DecryptByte(); UpdateKeys(buf[i]); } } protected void InitializePassword(string password) { keys = new uint[] { 0x12345678, 0x23456789, 0x34567890 }; for (int i = 0; i < password.Length; ++i) { UpdateKeys((byte)password[i]); } } protected void UpdateKeys(byte ch) { keys[0] = Crc32.ComputeCrc32(keys[0], ch); keys[1] = keys[1] + (byte)keys[0]; keys[1] = keys[1] * 134775813 + 1; keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24)); } #endregion } }