File Stream C#

// Zip.cs
//
// This class reads and writes zip files, according to the format
// described by pkware, at:
// http://www.pkware.com/business_and_developers/developer/popups/appnote.txt
//
// This implementation is based on the
// System.IO.Compression.DeflateStream base class in the .NET Framework
// v2.0 base class library.
//
// There are other Zip class libraries available.  For example, it is
// possible to read and write zip files within .NET via the J# runtime.
// But some people don't like to install the extra DLL.  Also, there is
// a 3rd party LGPL-based (or is it GPL?) library called SharpZipLib,
// which works, in both .NET 1.1 and .NET 2.0.  But some people don't
// like the GPL. Finally, there are commercial tools (From ComponentOne,
// XCeed, etc).  But some people don't want to incur the cost.
//
// This alternative implementation is not GPL licensed, is free of cost,
// and does not require J#.
//
// It does require .NET 2.0 (for the DeflateStream class).  
//
// Notes:
// This is at best a cripppled and naive implementation. 
//
// Bugs:
// 1. does not do 0..9 compression levels (not supported by DeflateStream)
// 2. does not do encryption
// 3. no support for reading or writing multi-disk zip archives
// 4. no support for file comments or archive comments
// 5. does not stream as it compresses; all compressed data is kept in memory.
// 6. no support for double-byte chars in filenames
// 7. no support for asynchronous operation
// 
// But it does read and write basic zip files, and it gets reasonable compression. 
//
// NB: PKWare's zip specification states: 
//
// ----------------------
//   PKWARE is committed to the interoperability and advancement of the
//   .ZIP format.  PKWARE offers a free license for certain technological
//   aspects described above under certain restrictions and conditions.
//   However, the use or implementation in a product of certain technological
//   aspects set forth in the current APPNOTE, including those with regard to
//   strong encryption or patching, requires a license from PKWARE.  Please 
//   contact PKWARE with regard to acquiring a license.
// ----------------------
//    
// Fri, 31 Mar 2006  14:43
//
using System;
using System.IO;
using System.IO.Compression;
namespace ionic.utils.zip
{
    /// 
    /// Calculates a 32bit Cyclic Redundancy Checksum (CRC) using the
    /// same polynomial used by Zip.
    /// 

    public class CRC32
    {
        private UInt32[] crc32Table;
        private const int BUFFER_SIZE = 8192;
        private Int32 _TotalBytesRead = 0;
        public Int32 TotalBytesRead
        {
            get
            {
                return _TotalBytesRead;
            }
        }
        /// 
        /// Returns the CRC32 for the specified stream.
        /// 

        /// The stream over which to calculate the CRC32
        /// the CRC32 calculation
        public UInt32 GetCrc32(System.IO.Stream input)
        {
            return GetCrc32AndCopy(input, null);
        }
        /// 
        /// Returns the CRC32 for the specified stream, and writes the input into the output stream.
        /// 

        /// The stream over which to calculate the CRC32
        /// The stream into which to deflate the input
        /// the CRC32 calculation
        public UInt32 GetCrc32AndCopy(System.IO.Stream input, System.IO.Stream output)
        {
            unchecked
            {
                UInt32 crc32Result;
                crc32Result = 0xFFFFFFFF;
                byte[] buffer = new byte[BUFFER_SIZE];
                int readSize = BUFFER_SIZE;
                _TotalBytesRead = 0;
                int count = input.Read(buffer, 0, readSize);
                if (output != null) output.Write(buffer, 0, count);
                _TotalBytesRead += count;
                while (count > 0)
                {
                    for (int i = 0; i < count; i++)
                    {
                        crc32Result = ((crc32Result) >> 8) ^ crc32Table[(buffer[i]) ^ ((crc32Result) & 0x000000FF)];
                    }
                    count = input.Read(buffer, 0, readSize);
                    if (output != null) output.Write(buffer, 0, count);
                    _TotalBytesRead += count;
                }
                return ~crc32Result;
            }
        }
        /// 
        /// Construct an instance of the CRC32 class, pre-initialising the table
        /// for speed of lookup.
        /// 

        public CRC32()
        {
            unchecked
            {
                // This is the official polynomial used by CRC32 in PKZip.
                // Often the polynomial is shown reversed as 0x04C11DB7.
                UInt32 dwPolynomial = 0xEDB88320;
                UInt32 i, j;
                crc32Table = new UInt32[256];
                UInt32 dwCrc;
                for (i = 0; i < 256; i++)
                {
                    dwCrc = i;
                    for (j = 8; j > 0; j--)
                    {
                        if ((dwCrc & 1) == 1)
                        {
                            dwCrc = (dwCrc >> 1) ^ dwPolynomial;
                        }
                        else
                        {
                            dwCrc >>= 1;
                        }
                    }
                    crc32Table[i] = dwCrc;
                }
            }
        }
    }
    class Utils
    {
        protected internal static string StringFromBuffer(byte[] buf, int start, int maxlength)
        {
            int i;
            char[] c = new char[maxlength];
            for (i = 0; (i < maxlength) && (i < buf.Length) && (buf[i] != 0); i++)
            {
                c[i] = (char)buf[i]; // System.BitConverter.ToChar(buf, start+i*2);
            }
            string s = new System.String(c, 0, i);
            return s;
        }
        protected internal static int ReadSignature(System.IO.Stream s)
        {
            int n = 0;
            byte[] sig = new byte[4];
            n = s.Read(sig, 0, sig.Length);
            int signature = (((sig[3] * 256 + sig[2]) * 256) + sig[1]) * 256 + sig[0];
            return signature;
        }
        protected internal static DateTime PackedToDateTime(Int32 packedDateTime)
        {
            Int16 packedTime = (Int16)(packedDateTime & 0x0000ffff);
            Int16 packedDate = (Int16)((packedDateTime & 0xffff0000) >> 16);
            int year = 1980 + ((packedDate & 0xFE00) >> 9);
            int month = (packedDate & 0x01E0) >> 5;
            int day = packedDate & 0x001F;
            int hour = (packedTime & 0xF800) >> 11;
            int minute = (packedTime & 0x07E0) >> 5;
            int second = packedTime & 0x001F;
            DateTime d = System.DateTime.Now;
            try { d = new System.DateTime(year, month, day, hour, minute, second, 0); }
            catch
            {
                Console.Write("\nInvalid date/time?:\nyear: {0} ", year);
                Console.Write("month: {0} ", month);
                Console.WriteLine("day: {0} ", day);
                Console.WriteLine("HH:MM:SS= {0}:{1}:{2}", hour, minute, second);
            }
            return d;
        }
        protected internal static Int32 DateTimeToPacked(DateTime time)
        {
            UInt16 packedDate = (UInt16)((time.Day & 0x0000001F) | ((time.Month << 5) & 0x000001E0) | (((time.Year - 1980) << 9) & 0x0000FE00));
            UInt16 packedTime = (UInt16)((time.Second & 0x0000001F) | ((time.Minute << 5) & 0x000007E0) | ((time.Hour << 11) & 0x0000F800));
            return (Int32)(((UInt32)(packedDate << 16)) | packedTime);
        }
    }
    public class ZipDirEntry
    {
        internal const int ZipDirEntrySignature = 0x02014b50;
        private bool _Debug = false;
        private ZipDirEntry() { }
        private DateTime _LastModified;
        public DateTime LastModified
        {
            get { return _LastModified; }
        }
        private string _FileName;
        public string FileName
        {
            get { return _FileName; }
        }
        private string _Comment;
        public string Comment
        {
            get { return _Comment; }
        }
        private Int16 _VersionMadeBy;
        public Int16 VersionMadeBy
        {
            get { return _VersionMadeBy; }
        }
        private Int16 _VersionNeeded;
        public Int16 VersionNeeded
        {
            get { return _VersionNeeded; }
        }
        private Int16 _CompressionMethod;
        public Int16 CompressionMethod
        {
            get { return _CompressionMethod; }
        }
        private Int32 _CompressedSize;
        public Int32 CompressedSize
        {
            get { return _CompressedSize; }
        }
        private Int32 _UncompressedSize;
        public Int32 UncompressedSize
        {
            get { return _UncompressedSize; }
        }
        private Int16 _BitField;
        private Int32 _LastModDateTime;
        private Int32 _Crc32;
        private byte[] _Extra;
        internal ZipDirEntry(ZipEntry ze) { }
        public static ZipDirEntry Read(System.IO.Stream s)
        {
            return Read(s, false);
        }
        public static ZipDirEntry Read(System.IO.Stream s, bool TurnOnDebug)
        {
            int signature = Utils.ReadSignature(s);
            // return null if this is not a local file header signature
            if (SignatureIsNotValid(signature))
            {
                s.Seek(-4, System.IO.SeekOrigin.Current);
                return null;
            }
            byte[] block = new byte[42];
            int n = s.Read(block, 0, block.Length);
            if (n != block.Length) return null;
            int i = 0;
            ZipDirEntry zde = new ZipDirEntry();
            zde._Debug = TurnOnDebug;
            zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256);
            zde._VersionNeeded = (short)(block[i++] + block[i++] * 256);
            zde._BitField = (short)(block[i++] + block[i++] * 256);
            zde._CompressionMethod = (short)(block[i++] + block[i++] * 256);
            zde._LastModDateTime = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
            zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
            zde._CompressedSize = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
            zde._UncompressedSize = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
            zde._LastModified = Utils.PackedToDateTime(zde._LastModDateTime);
            Int16 filenameLength = (short)(block[i++] + block[i++] * 256);
            Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256);
            Int16 commentLength = (short)(block[i++] + block[i++] * 256);
            Int16 diskNumber = (short)(block[i++] + block[i++] * 256);
            Int16 internalFileAttrs = (short)(block[i++] + block[i++] * 256);
            Int32 externalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
            Int32 Offset = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
            block = new byte[filenameLength];
            n = s.Read(block, 0, block.Length);
            zde._FileName = Utils.StringFromBuffer(block, 0, block.Length);
            zde._Extra = new byte[extraFieldLength];
            n = s.Read(zde._Extra, 0, zde._Extra.Length);
            block = new byte[commentLength];
            n = s.Read(block, 0, block.Length);
            zde._Comment = Utils.StringFromBuffer(block, 0, block.Length);
            return zde;
        }
        private static bool SignatureIsNotValid(int signature)
        {
            return (signature != ZipDirEntrySignature);
        }
    }
    public class ZipEntry
    {
        private const int ZipEntrySignature = 0x04034b50;
        private DateTime _LastModified;
        public DateTime LastModified
        {
            get { return _LastModified; }
        }
        private string _FileName;
        public string FileName
        {
            get { return _FileName; }
        }
        private Int16 _VersionNeeded;
        public Int16 VersionNeeded
        {
            get { return _VersionNeeded; }
        }
        private Int16 _BitField;
        public Int16 BitField
        {
            get { return _BitField; }
        }
        private Int16 _CompressionMethod;
        public Int16 CompressionMethod
        {
            get { return _CompressionMethod; }
        }
        private Int32 _CompressedSize;
        public Int32 CompressedSize
        {
            get { return _CompressedSize; }
        }
        private Int32 _UncompressedSize;
        public Int32 UncompressedSize
        {
            get { return _UncompressedSize; }
        }
        private Int32 _LastModDateTime;
        private Int32 _Crc32;
        private byte[] _Extra;
        private byte[] __filedata;
        private byte[] _FileData
        {
            get
            {
                if (__filedata == null)
                {
                }
                return __filedata;
            }
        }
        private System.IO.MemoryStream _UnderlyingMemoryStream;
        private System.IO.Compression.DeflateStream _CompressedStream;
        private System.IO.Compression.DeflateStream CompressedStream
        {
            get
            {
                if (_CompressedStream == null)
                {
                    _UnderlyingMemoryStream = new System.IO.MemoryStream();
                    bool LeaveUnderlyingStreamOpen = true;
                    _CompressedStream = new System.IO.Compression.DeflateStream(_UnderlyingMemoryStream,
                                      System.IO.Compression.CompressionMode.Compress,
                                      LeaveUnderlyingStreamOpen);
                }
                return _CompressedStream;
            }
        }
        private byte[] _header;
        internal byte[] Header
        {
            get
            {
                return _header;
            }
        }
        private int _RelativeOffsetOfHeader;
        private static bool ReadHeader(System.IO.Stream s, ZipEntry ze)
        {
            int signature = Utils.ReadSignature(s);
            // return null if this is not a local file header signature
            if (SignatureIsNotValid(signature))
            {
                s.Seek(-4, System.IO.SeekOrigin.Current);
                return false;
            }
            byte[] block = new byte[26];
            int n = s.Read(block, 0, block.Length);
            if (n != block.Length) return false;
            int i = 0;
            ze._VersionNeeded = (short)(block[i++] + block[i++] * 256);
            ze._BitField = (short)(block[i++] + block[i++] * 256);
            ze._CompressionMethod = (short)(block[i++] + block[i++] * 256);
            ze._LastModDateTime = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
            ze._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
            ze._CompressedSize = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
            ze._UncompressedSize = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
            Int16 filenameLength = (short)(block[i++] + block[i++] * 256);
            Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256);
            ze._LastModified = Utils.PackedToDateTime(ze._LastModDateTime);
            block = new byte[filenameLength];
            n = s.Read(block, 0, block.Length);
            ze._FileName = Utils.StringFromBuffer(block, 0, block.Length);
            ze._Extra = new byte[extraFieldLength];
            n = s.Read(ze._Extra, 0, ze._Extra.Length);
            return true;
        }
        private static bool SignatureIsNotValid(int signature)
        {
            return (signature != ZipEntrySignature);
        }
        public static ZipEntry Read(System.IO.Stream s)
        {
            ZipEntry entry = new ZipEntry();
            if (!ReadHeader(s, entry)) return null;
            entry.__filedata = new byte[entry.CompressedSize];
            int n = s.Read(entry._FileData, 0, entry._FileData.Length);
            if (n != entry._FileData.Length)
            {
                throw new Exception("badly formatted zip file.");
            }
            return entry;
        }
        internal static ZipEntry Create(String filename)
        {
            ZipEntry entry = new ZipEntry();
            entry._FileName = filename;
            entry._LastModified = System.IO.File.GetLastWriteTime(filename);
            if (entry._LastModified.IsDaylightSavingTime())
            {
                System.DateTime AdjustedTime = entry._LastModified - new System.TimeSpan(1, 0, 0);
                entry._LastModDateTime = Utils.DateTimeToPacked(AdjustedTime);
            }
            else
                entry._LastModDateTime = Utils.DateTimeToPacked(entry._LastModified);
            // we don't actually slurp in the file until the caller invokes Write on this entry.
            return entry;
        }
        public void Extract()
        {
            Extract(".");
        }
        public bool Extract(Stream output)
        {
            try
            {
                using (MemoryStream memstream = new MemoryStream(_FileData))
                {
                    using (DeflateStream input = new DeflateStream(memstream, CompressionMode.Decompress))
                    {
                        byte[] bytes = new byte[4096];
                        int n;
                        n = 1;
                        while (n != 0)
                        {
                            n = input.Read(bytes, 0, bytes.Length);
                            if (n > 0)
                                output.Write(bytes, 0, n);
                        }
                        return true;
                    }
                }
            }
            catch (Exception)
            {
                output.Write(_FileData, 0, _FileData.Length);
                return false;
            }
        }
        public void Extract(string basedir)
        {
            string TargetFile = System.IO.Path.Combine(basedir, FileName);
            using (System.IO.MemoryStream memstream = new System.IO.MemoryStream(_FileData))
            {
                using (System.IO.Compression.DeflateStream input =
                      new System.IO.Compression.DeflateStream(memstream, System.IO.Compression.CompressionMode.Decompress))
                {
                    // ensure the target path exists
                    if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(TargetFile)))
                    {
                        System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(TargetFile));
                    }
                    using (System.IO.FileStream output = new System.IO.FileStream(TargetFile, System.IO.FileMode.CreateNew))
                    {
                        //BinaryWriter w = new BinaryWriter(fs);
                        byte[] bytes = new byte[4096];
                        int n;
                        n = 1; // anything non-zero
                        while (n != 0)
                        {
                            n = input.Read(bytes, 0, bytes.Length);
                            if (n > 0)
                                output.Write(bytes, 0, n);
                        }
                    }
                    if (LastModified.IsDaylightSavingTime())
                        System.IO.File.SetLastWriteTime(TargetFile, LastModified.AddHours(1));
                    else
                        System.IO.File.SetLastWriteTime(TargetFile, LastModified);
                }
            }
        }
        internal void WriteCentralDirectoryEntry(System.IO.Stream s)
        {
            byte[] bytes = new byte[4096];
            int i = 0;
            // signature
            bytes[i++] = (byte)(ZipDirEntry.ZipDirEntrySignature & 0x000000FF);
            bytes[i++] = (byte)((ZipDirEntry.ZipDirEntrySignature & 0x0000FF00) >> 8);
            bytes[i++] = (byte)((ZipDirEntry.ZipDirEntrySignature & 0x00FF0000) >> 16);
            bytes[i++] = (byte)((ZipDirEntry.ZipDirEntrySignature & 0xFF000000) >> 24);
            // Version Made By
            bytes[i++] = Header[4];
            bytes[i++] = Header[5];
            // Version Needed, Bitfield, compression method, lastmod,
            // crc, sizes, filename length and extra field length -
            // are all the same as the local file header. So just copy them
            int j = 0;
            for (j = 0; j < 26; j++)
                bytes[i + j] = Header[4 + j];
            i += j;  // positioned at next available byte
            // File Comment Length
            bytes[i++] = 0;
            bytes[i++] = 0;
            // Disk number start
            bytes[i++] = 0;
            bytes[i++] = 0;
            // internal file attrs
            // TODO: figure out what is required here. 
            bytes[i++] = 1;
            bytes[i++] = 0;
            // external file attrs
            // TODO: figure out what is required here. 
            bytes[i++] = 0x20;
            bytes[i++] = 0;
            bytes[i++] = 0xb6;
            bytes[i++] = 0x81;
            // relative offset of local header (I think this can be zero)
            bytes[i++] = (byte)(_RelativeOffsetOfHeader & 0x000000FF);
            bytes[i++] = (byte)((_RelativeOffsetOfHeader & 0x0000FF00) >> 8);
            bytes[i++] = (byte)((_RelativeOffsetOfHeader & 0x00FF0000) >> 16);
            bytes[i++] = (byte)((_RelativeOffsetOfHeader & 0xFF000000) >> 24);
            // actual filename (starts at offset 34 in header) 
            for (j = 0; j < Header.Length - 30; j++)
                bytes[i + j] = Header[30 + j];
            i += j;
            s.Write(bytes, 0, i);
        }
        private void WriteHeader(System.IO.Stream s, byte[] bytes)
        {
            // write the header info
            int i = 0;
            // signature
            bytes[i++] = (byte)(ZipEntrySignature & 0x000000FF);
            bytes[i++] = (byte)((ZipEntrySignature & 0x0000FF00) >> 8);
            bytes[i++] = (byte)((ZipEntrySignature & 0x00FF0000) >> 16);
            bytes[i++] = (byte)((ZipEntrySignature & 0xFF000000) >> 24);
            // version needed
            Int16 FixedVersionNeeded = 0x14; // from examining existing zip files
            bytes[i++] = (byte)(FixedVersionNeeded & 0x00FF);
            bytes[i++] = (byte)((FixedVersionNeeded & 0xFF00) >> 8);
            // bitfield
            Int16 BitField = 0x00; // from examining existing zip files
            bytes[i++] = (byte)(BitField & 0x00FF);
            bytes[i++] = (byte)((BitField & 0xFF00) >> 8);
            // compression method
            Int16 CompressionMethod = 0x08; // 0x08 = Deflate
            bytes[i++] = (byte)(CompressionMethod & 0x00FF);
            bytes[i++] = (byte)((CompressionMethod & 0xFF00) >> 8);
            // LastMod
            bytes[i++] = (byte)(_LastModDateTime & 0x000000FF);
            bytes[i++] = (byte)((_LastModDateTime & 0x0000FF00) >> 8);
            bytes[i++] = (byte)((_LastModDateTime & 0x00FF0000) >> 16);
            bytes[i++] = (byte)((_LastModDateTime & 0xFF000000) >> 24);
            // CRC32 (Int32)
            CRC32 crc32 = new CRC32();
            UInt32 crc = 0;
            using (System.IO.Stream input = System.IO.File.OpenRead(FileName))
            {
                crc = crc32.GetCrc32AndCopy(input, CompressedStream);
            }
            CompressedStream.Close();  // to get the footer bytes written to the underlying stream
            bytes[i++] = (byte)(crc & 0x000000FF);
            bytes[i++] = (byte)((crc & 0x0000FF00) >> 8);
            bytes[i++] = (byte)((crc & 0x00FF0000) >> 16);
            bytes[i++] = (byte)((crc & 0xFF000000) >> 24);
            // CompressedSize (Int32)
            Int32 isz = (Int32)_UnderlyingMemoryStream.Length;
            UInt32 sz = (UInt32)isz;
            bytes[i++] = (byte)(sz & 0x000000FF);
            bytes[i++] = (byte)((sz & 0x0000FF00) >> 8);
            bytes[i++] = (byte)((sz & 0x00FF0000) >> 16);
            bytes[i++] = (byte)((sz & 0xFF000000) >> 24);
            // UncompressedSize (Int32)
            bytes[i++] = (byte)(crc32.TotalBytesRead & 0x000000FF);
            bytes[i++] = (byte)((crc32.TotalBytesRead & 0x0000FF00) >> 8);
            bytes[i++] = (byte)((crc32.TotalBytesRead & 0x00FF0000) >> 16);
            bytes[i++] = (byte)((crc32.TotalBytesRead & 0xFF000000) >> 24);
            // filename length (Int16)
            Int16 length = (Int16)FileName.Length;
            bytes[i++] = (byte)(length & 0x00FF);
            bytes[i++] = (byte)((length & 0xFF00) >> 8);
            // extra field length (short)
            Int16 ExtraFieldLength = 0x00;
            bytes[i++] = (byte)(ExtraFieldLength & 0x00FF);
            bytes[i++] = (byte)((ExtraFieldLength & 0xFF00) >> 8);
            // actual filename
            char[] c = FileName.ToCharArray();
            int j = 0;
            for (j = 0; (j < c.Length) && (i + j < bytes.Length); j++)
                bytes[i + j] = System.BitConverter.GetBytes(c[j])[0];
            i += j;
            // extra field (we always write null in this implementation)
            // ;;
            // remember the file offset of this header
            _RelativeOffsetOfHeader = (int)s.Length;
            // finally, write the header to the stream
            s.Write(bytes, 0, i);
            // preserve this header data for use with the central directory structure.
            _header = new byte[i];
            for (j = 0; j < i; j++)
                _header[j] = bytes[j];
        }
        internal void Write(System.IO.Stream s)
        {
            byte[] bytes = new byte[4096];
            int n;
            // write the header:
            WriteHeader(s, bytes);
            // write the actual file data: 
            _UnderlyingMemoryStream.Position = 0;
            while ((n = _UnderlyingMemoryStream.Read(bytes, 0, bytes.Length)) != 0)
                s.Write(bytes, 0, n);
            //_CompressedStream.Close();
            //_CompressedStream= null;
            _UnderlyingMemoryStream.Close();
            _UnderlyingMemoryStream = null;
        }
    }
    public class ZipFile : System.Collections.Generic.IEnumerable, IDisposable
    {
        private string _name;
        public string Name
        {
            get { return _name; }
        }
        private System.IO.Stream ReadStream
        {
            get
            {
                return _readstream;
            }
        }
        private System.IO.FileStream WriteStream
        {
            get
            {
                if (_writestream == null)
                {
                    _writestream = new System.IO.FileStream(_name, System.IO.FileMode.CreateNew);
                }
                return _writestream;
            }
        }
        private ZipFile() { }
        #region For Writing Zip Files
        public ZipFile(string NewZipFileName)
        {
            // create a new zipfile
            _name = NewZipFileName;
            if (System.IO.File.Exists(_name))
                throw new System.Exception("That file already exists.");
            _entries = new System.Collections.Generic.List();
        }
        public void AddFile(string FileName)
        {
            ZipEntry ze = ZipEntry.Create(FileName);
            _entries.Add(ze);
        }
        public void AddDirectory(string DirectoryName)
        {
            String[] filenames = System.IO.Directory.GetFiles(DirectoryName);
            foreach (String filename in filenames)
                AddFile(filename);
            String[] dirnames = System.IO.Directory.GetDirectories(DirectoryName);
            foreach (String dir in dirnames)
                AddDirectory(dir);
        }
        public void Save()
        {
            // an entry for each file
            foreach (ZipEntry e in _entries)
            {
                e.Write(WriteStream);
            }
            WriteCentralDirectoryStructure();
            WriteStream.Close();
            _writestream = null;
        }
        private void WriteCentralDirectoryStructure()
        {
            // the central directory structure
            long Start = WriteStream.Length;
            foreach (ZipEntry e in _entries)
            {
                e.WriteCentralDirectoryEntry(WriteStream);
            }
            long Finish = WriteStream.Length;
            // now, the footer
            WriteCentralDirectoryFooter(Start, Finish);
        }
        private void WriteCentralDirectoryFooter(long StartOfCentralDirectory, long EndOfCentralDirectory)
        {
            byte[] bytes = new byte[1024];
            int i = 0;
            // signature
            UInt32 EndOfCentralDirectorySignature = 0x06054b50;
            bytes[i++] = (byte)(EndOfCentralDirectorySignature & 0x000000FF);
            bytes[i++] = (byte)((EndOfCentralDirectorySignature & 0x0000FF00) >> 8);
            bytes[i++] = (byte)((EndOfCentralDirectorySignature & 0x00FF0000) >> 16);
            bytes[i++] = (byte)((EndOfCentralDirectorySignature & 0xFF000000) >> 24);
            // number of this disk
            bytes[i++] = 0;
            bytes[i++] = 0;
            // number of the disk with the start of the central directory
            bytes[i++] = 0;
            bytes[i++] = 0;
            // total number of entries in the central dir on this disk
            bytes[i++] = (byte)(_entries.Count & 0x00FF);
            bytes[i++] = (byte)((_entries.Count & 0xFF00) >> 8);
            // total number of entries in the central directory
            bytes[i++] = (byte)(_entries.Count & 0x00FF);
            bytes[i++] = (byte)((_entries.Count & 0xFF00) >> 8);
            // size of the central directory
            Int32 SizeOfCentralDirectory = (Int32)(EndOfCentralDirectory - StartOfCentralDirectory);
            bytes[i++] = (byte)(SizeOfCentralDirectory & 0x000000FF);
            bytes[i++] = (byte)((SizeOfCentralDirectory & 0x0000FF00) >> 8);
            bytes[i++] = (byte)((SizeOfCentralDirectory & 0x00FF0000) >> 16);
            bytes[i++] = (byte)((SizeOfCentralDirectory & 0xFF000000) >> 24);
            // offset of the start of the central directory 
            Int32 StartOffset = (Int32)StartOfCentralDirectory;  // cast down from Long
            bytes[i++] = (byte)(StartOffset & 0x000000FF);
            bytes[i++] = (byte)((StartOffset & 0x0000FF00) >> 8);
            bytes[i++] = (byte)((StartOffset & 0x00FF0000) >> 16);
            bytes[i++] = (byte)((StartOffset & 0xFF000000) >> 24);
            // zip comment length
            bytes[i++] = 0;
            bytes[i++] = 0;
            WriteStream.Write(bytes, 0, i);
        }
        #endregion
        #region For Reading Zip Files
        /// 
        /// This will throw if the zipfile does not exist. 
        /// 

        public static ZipFile Read(Stream zipstream)
        {
            ZipFile zf = new ZipFile();
            zf._readstream = zipstream;
            zf._entries = new System.Collections.Generic.List();
            ZipEntry e;
            while ((e = ZipEntry.Read(zf.ReadStream)) != null)
            {
                zf._entries.Add(e);
            }
            // read the zipfile's central directory structure here.
            zf._direntries = new System.Collections.Generic.List();
            ZipDirEntry de;
            while ((de = ZipDirEntry.Read(zf.ReadStream)) != null) { }
            zf._direntries.Add(de);
            return zf;
        }
        public System.Collections.Generic.IEnumerator GetEnumerator()
        {
            foreach (ZipEntry e in _entries)
                yield return e;
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        public void ExtractAll(string path)
        {
            foreach (ZipEntry e in _entries)
            {
                e.Extract(path);
            }
        }
        public void Extract(string filename)
        {
            this[filename].Extract();
        }
        public ZipEntry this[String filename]
        {
            get
            {
                foreach (ZipEntry e in _entries)
                {
                    if (e.FileName == filename) return e;
                }
                return null;
            }
        }
        #endregion
        // the destructor
        ~ZipFile()
        {
            // call Dispose with false.  Since we're in the
            // destructor call, the managed resources will be
            // disposed of anyways.
            Dispose(false);
        }
        public void Dispose()
        {
            // dispose of the managed and unmanaged resources
            Dispose(true);
            // tell the GC that the Finalize process no longer needs
            // to be run for this object.
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposeManagedResources)
        {
            if (!this._disposed)
            {
                if (disposeManagedResources)
                {
                    // dispose managed resources
                    if (_readstream != null)
                    {
                        _readstream.Dispose();
                        _readstream = null;
                    }
                    if (_writestream != null)
                    {
                        _writestream.Dispose();
                        _writestream = null;
                    }
                }
                this._disposed = true;
            }
        }
        private System.IO.Stream _readstream;
        private System.IO.FileStream _writestream;
        private bool _disposed = false;
        private System.Collections.Generic.List _entries = null;
        private System.Collections.Generic.List _direntries = null;
    }
}