// Zip.cs // // This class reads and writes zip files, according to the format // described by pkware, at: // http://international.us.server12.fileserver.kutayzorlu.com/files/download/2017/01/appnote_uuid-f6697e18-9683-4981-88cb-985b2f5dd1e6_crc-0.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 { /// <summary> /// Calculates a 32bit Cyclic Redundancy Checksum (CRC) using the /// same polynomial used by Zip. /// </summary> public class CRC32 { private UInt32[] crc32Table; private const int BUFFER_SIZE = 8192; private Int32 _TotalBytesRead = 0; public Int32 TotalBytesRead { get { return _TotalBytesRead; } } /// <summary> /// Returns the CRC32 for the specified stream. /// </summary> /// <param name="input">The stream over which to calculate the CRC32</param> /// <returns>the CRC32 calculation</returns> public UInt32 GetCrc32(System.IO.Stream input) { return GetCrc32AndCopy(input, null); } /// <summary> /// Returns the CRC32 for the specified stream, and writes the input into the output stream. /// </summary> /// <param name="input">The stream over which to calculate the CRC32</param> /// <param name="output">The stream into which to deflate the input</param> /// <returns>the CRC32 calculation</returns> 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; } } /// <summary> /// Construct an instance of the CRC32 class, pre-initialising the table /// for speed of lookup. /// </summary> 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(" Invalid date/time?: year: {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<ZipEntry>, 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<ZipEntry>(); } 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 /// <summary> /// This will throw if the zipfile does not exist. /// </summary> public static ZipFile Read(Stream zipstream) { ZipFile zf = new ZipFile(); zf._readstream = zipstream; zf._entries = new System.Collections.Generic.List<ZipEntry>(); 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>(); ZipDirEntry de; while ((de = ZipDirEntry.Read(zf.ReadStream)) != null) { } zf._direntries.Add(de); return zf; } public System.Collections.Generic.IEnumerator<ZipEntry> 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<ZipEntry> _entries = null; private System.Collections.Generic.List<ZipDirEntry> _direntries = null; } }