diff --git a/GMap.NET.Core/GMap.NET/ZipStorer.cs b/GMap.NET.Core/GMap.NET/ZipStorer.cs new file mode 100644 index 00000000..93ff22a0 --- /dev/null +++ b/GMap.NET.Core/GMap.NET/ZipStorer.cs @@ -0,0 +1,748 @@ +// ZipStorer, by Jaime Olivares +// Website: zipstorer.codeplex.com +// Version: 2.35 (March 14, 2010) + +using System.Collections.Generic; +using System.Text; + +namespace System.IO.Compression +{ + /// + /// Unique class for compression/decompression file. Represents a Zip file. + /// + public class ZipStorer : IDisposable + { + /// + /// Compression method enumeration + /// + public enum Compression : ushort { + /// Uncompressed storage + Store = 0, + /// Deflate compression method + Deflate = 8 } + + /// + /// Represents an entry in Zip file directory + /// + public struct ZipFileEntry + { + /// Compression method + public Compression Method; + /// Full path and filename as stored in Zip + public string FilenameInZip; + /// Original file size + public uint FileSize; + /// Compressed file size + public uint CompressedSize; + /// Offset of header information inside Zip storage + public uint HeaderOffset; + /// Offset of file inside Zip storage + public uint FileOffset; + /// Size of header information + public uint HeaderSize; + /// 32-bit checksum of entire file + public uint Crc32; + /// Last modification time of file + public DateTime ModifyTime; + /// User comment for file + public string Comment; + /// True if UTF8 encoding for filename and comments, false if default (CP 437) + public bool EncodeUTF8; + + /// Overriden method + /// Filename in Zip + public override string ToString() + { + return this.FilenameInZip; + } + } + + #region Public fields + /// True if UTF8 encoding for filename and comments, false if default (CP 437) + public bool EncodeUTF8 = false; + /// Force deflate algotithm even if it inflates the stored file. Off by default. + public bool ForceDeflating = false; + #endregion + + #region Private fields + // List of files to store + private List Files = new List(); + // Filename of storage file + private string FileName; + // Stream object of storage file + private Stream ZipFileStream; + // General comment + private string Comment = ""; + // Central dir image + private byte[] CentralDirImage = null; + // Existing files in zip + private ushort ExistingFiles = 0; + // File access for Open method + private FileAccess Access; + // Static CRC32 Table + private static UInt32[] CrcTable = null; + // Default filename encoder + private static Encoding DefaultEncoding = Encoding.GetEncoding(437); + #endregion + + #region Public methods + // Static constructor. Just invoked once in order to create the CRC32 lookup table. + static ZipStorer() + { + // Generate CRC32 table + CrcTable = new UInt32[256]; + for (int i = 0; i < CrcTable.Length; i++) + { + UInt32 c = (UInt32)i; + for (int j = 0; j < 8; j++) + { + if ((c & 1) != 0) + c = 3988292384 ^ (c >> 1); + else + c >>= 1; + } + CrcTable[i] = c; + } + } + /// + /// Method to create a new storage file + /// + /// Full path of Zip file to create + /// General comment for Zip file + /// A valid ZipStorer object + public static ZipStorer Create(string _filename, string _comment) + { + Stream stream = new FileStream(_filename, FileMode.Create, FileAccess.ReadWrite); + + ZipStorer zip = Create(stream, _comment); + zip.Comment = _comment; + zip.FileName = _filename; + + return zip; + } + /// + /// Method to create a new zip storage in a stream + /// + /// + /// + /// A valid ZipStorer object + public static ZipStorer Create(Stream _stream, string _comment) + { + ZipStorer zip = new ZipStorer(); + zip.Comment = _comment; + zip.ZipFileStream = _stream; + zip.Access = FileAccess.Write; + + return zip; + } + /// + /// Method to open an existing storage file + /// + /// Full path of Zip file to open + /// File access mode as used in FileStream constructor + /// A valid ZipStorer object + public static ZipStorer Open(string _filename, FileAccess _access) + { + Stream stream = (Stream)new FileStream(_filename, FileMode.Open, _access == FileAccess.Read ? FileAccess.Read : FileAccess.ReadWrite); + + ZipStorer zip = Open(stream, _access); + zip.FileName = _filename; + + return zip; + } + /// + /// Method to open an existing storage from stream + /// + /// Already opened stream with zip contents + /// File access mode for stream operations + /// A valid ZipStorer object + public static ZipStorer Open(Stream _stream, FileAccess _access) + { + if (!_stream.CanSeek && _access != FileAccess.Read) + throw new InvalidOperationException("Stream cannot seek"); + + ZipStorer zip = new ZipStorer(); + //zip.FileName = _filename; + zip.ZipFileStream = _stream; + zip.Access = _access; + + if (zip.ReadFileInfo()) + return zip; + + throw new System.IO.InvalidDataException(); + } + /// + /// Add full contents of a file into the Zip storage + /// + /// Compression method + /// Full path of file to add to Zip storage + /// Filename and path as desired in Zip directory + /// Comment for stored file + public void AddFile(Compression _method, string _pathname, string _filenameInZip, string _comment) + { + if (Access == FileAccess.Read) + throw new InvalidOperationException("Writing is not alowed"); + + FileStream stream = new FileStream(_pathname, FileMode.Open, FileAccess.Read); + AddStream(_method, _filenameInZip, stream, File.GetLastWriteTime(_pathname), _comment); + stream.Close(); + } + /// + /// Add full contents of a stream into the Zip storage + /// + /// Compression method + /// Filename and path as desired in Zip directory + /// Stream object containing the data to store in Zip + /// Modification time of the data to store + /// Comment for stored file + public void AddStream(Compression _method, string _filenameInZip, Stream _source, DateTime _modTime, string _comment) + { + if (Access == FileAccess.Read) + throw new InvalidOperationException("Writing is not alowed"); + + long offset; + if (this.Files.Count==0) + offset = 0; + else + { + ZipFileEntry last = this.Files[this.Files.Count-1]; + offset = last.HeaderOffset + last.HeaderSize; + } + + // Prepare the fileinfo + ZipFileEntry zfe = new ZipFileEntry(); + zfe.Method = _method; + zfe.EncodeUTF8 = this.EncodeUTF8; + zfe.FilenameInZip = NormalizedFilename(_filenameInZip); + zfe.Comment = (_comment == null ? "" : _comment); + + // Even though we write the header now, it will have to be rewritten, since we don't know compressed size or crc. + zfe.Crc32 = 0; // to be updated later + zfe.HeaderOffset = (uint)this.ZipFileStream.Position; // offset within file of the start of this local record + zfe.ModifyTime = _modTime; + + // Write local header + WriteLocalHeader(ref zfe); + zfe.FileOffset = (uint)this.ZipFileStream.Position; + + // Write file to zip (store) + Store(ref zfe, _source); + _source.Close(); + + this.UpdateCrcAndSizes(ref zfe); + + Files.Add(zfe); + } + /// + /// Updates central directory (if pertinent) and close the Zip storage + /// + /// This is a required step, unless automatic dispose is used + public void Close() + { + if(this.ZipFileStream == null) + return; + + if (this.Access != FileAccess.Read) + { + uint centralOffset = (uint)this.ZipFileStream.Position; + uint centralSize = 0; + + if (this.CentralDirImage != null) + this.ZipFileStream.Write(CentralDirImage, 0, CentralDirImage.Length); + + for (int i = 0; i < Files.Count; i++) + { + long pos = this.ZipFileStream.Position; + this.WriteCentralDirRecord(Files[i]); + centralSize += (uint)(this.ZipFileStream.Position - pos); + } + + if (this.CentralDirImage != null) + this.WriteEndRecord(centralSize + (uint)CentralDirImage.Length, centralOffset); + else + this.WriteEndRecord(centralSize, centralOffset); + } + + if (this.ZipFileStream != null) + { + this.ZipFileStream.Flush(); + this.ZipFileStream.Dispose(); + this.ZipFileStream = null; + } + } + /// + /// Read all the file records in the central directory + /// + /// List of all entries in directory + public List ReadCentralDir() + { + if (this.CentralDirImage == null) + throw new InvalidOperationException("Central directory currently does not exist"); + + List result = new List(); + + for (int pointer = 0; pointer < this.CentralDirImage.Length; ) + { + uint signature = BitConverter.ToUInt32(CentralDirImage, pointer); + if (signature != 0x02014b50) + break; + + bool encodeUTF8 = (BitConverter.ToUInt16(CentralDirImage, pointer + 8) & 0x0800) != 0; + ushort method = BitConverter.ToUInt16(CentralDirImage, pointer + 10); + uint modifyTime = BitConverter.ToUInt32(CentralDirImage, pointer + 12); + uint crc32 = BitConverter.ToUInt32(CentralDirImage, pointer + 16); + uint comprSize = BitConverter.ToUInt32(CentralDirImage, pointer + 20); + uint fileSize = BitConverter.ToUInt32(CentralDirImage, pointer + 24); + ushort filenameSize = BitConverter.ToUInt16(CentralDirImage, pointer + 28); + ushort extraSize = BitConverter.ToUInt16(CentralDirImage, pointer + 30); + ushort commentSize = BitConverter.ToUInt16(CentralDirImage, pointer + 32); + uint headerOffset = BitConverter.ToUInt32(CentralDirImage, pointer + 42); + uint headerSize = (uint)( 46 + filenameSize + extraSize + commentSize); + + Encoding encoder = encodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + + ZipFileEntry zfe = new ZipFileEntry(); + zfe.Method = (Compression)method; + zfe.FilenameInZip = encoder.GetString(CentralDirImage, pointer + 46, filenameSize); + zfe.FileOffset = GetFileOffset(headerOffset); + zfe.FileSize = fileSize; + zfe.CompressedSize = comprSize; + zfe.HeaderOffset = headerOffset; + zfe.HeaderSize = headerSize; + zfe.Crc32 = crc32; + zfe.ModifyTime = DosTimeToDateTime(modifyTime); + if (commentSize > 0) + zfe.Comment = encoder.GetString(CentralDirImage, pointer + 46 + filenameSize + extraSize, commentSize); + + result.Add(zfe); + pointer += (46 + filenameSize + extraSize + commentSize); + } + + return result; + } + /// + /// Copy the contents of a stored file into a physical file + /// + /// Entry information of file to extract + /// Name of file to store uncompressed data + /// True if success, false if not. + /// Unique compression methods are Store and Deflate + public bool ExtractFile(ZipFileEntry _zfe, string _filename) + { + // Make sure the parent directory exist + string path = System.IO.Path.GetDirectoryName(_filename); + + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + // Check it is directory. If so, do nothing + if (Directory.Exists(_filename)) + return true; + + Stream output = new FileStream(_filename, FileMode.Create, FileAccess.Write); + bool result = ExtractFile(_zfe, output); + if (result) + output.Close(); + + File.SetCreationTime(_filename, _zfe.ModifyTime); + File.SetLastWriteTime(_filename, _zfe.ModifyTime); + + return result; + } + /// + /// Copy the contents of a stored file into an opened stream + /// + /// Entry information of file to extract + /// Stream to store the uncompressed data + /// True if success, false if not. + /// Unique compression methods are Store and Deflate + public bool ExtractFile(ZipFileEntry _zfe, Stream _stream) + { + if (!_stream.CanWrite) + throw new InvalidOperationException("Stream cannot be written"); + + // check signature + byte[] signature = new byte[4]; + this.ZipFileStream.Seek(_zfe.HeaderOffset, SeekOrigin.Begin); + this.ZipFileStream.Read(signature, 0, 4); + if (BitConverter.ToUInt32(signature, 0) != 0x04034b50) + return false; + + // Select input stream for inflating or just reading + Stream inStream; + if (_zfe.Method == Compression.Store) + inStream = this.ZipFileStream; + else if (_zfe.Method == Compression.Deflate) + inStream = new DeflateStream(this.ZipFileStream, CompressionMode.Decompress, true); + else + return false; + + // Buffered copy + byte[] buffer = new byte[16384]; + this.ZipFileStream.Seek(_zfe.FileOffset, SeekOrigin.Begin); + uint bytesPending = _zfe.FileSize; + while (bytesPending > 0) + { + int bytesRead = inStream.Read(buffer, 0, (int)Math.Min(bytesPending, buffer.Length)); + _stream.Write(buffer, 0, bytesRead); + bytesPending -= (uint)bytesRead; + } + _stream.Flush(); + + if (_zfe.Method == Compression.Deflate) + inStream.Dispose(); + return true; + } + /// + /// Removes one of many files in storage. It creates a new Zip file. + /// + /// Reference to the current Zip object + /// List of Entries to remove from storage + /// True if success, false if not + /// This method only works for storage of type FileStream + public static bool RemoveEntries(ref ZipStorer _zip, List _zfes) + { + if (!(_zip.ZipFileStream is FileStream)) + throw new InvalidOperationException("RemoveEntries is allowed just over streams of type FileStream"); + + + //Get full list of entries + List fullList = _zip.ReadCentralDir(); + + //In order to delete we need to create a copy of the zip file excluding the selected items + string tempZipName = Path.GetTempFileName(); + string tempEntryName = Path.GetTempFileName(); + + try + { + ZipStorer tempZip = ZipStorer.Create(tempZipName, string.Empty); + + foreach (ZipFileEntry zfe in fullList) + { + if (!_zfes.Contains(zfe)) + { + if (_zip.ExtractFile(zfe, tempEntryName)) + { + tempZip.AddFile(zfe.Method, tempEntryName, zfe.FilenameInZip, zfe.Comment); + } + } + } + _zip.Close(); + tempZip.Close(); + + File.Delete(_zip.FileName); + File.Move(tempZipName, _zip.FileName); + + _zip = ZipStorer.Open(_zip.FileName, _zip.Access); + } + catch + { + return false; + } + finally + { + if (File.Exists(tempZipName)) + File.Delete(tempZipName); + if (File.Exists(tempEntryName)) + File.Delete(tempEntryName); + } + return true; + } + #endregion + + #region Private methods + // Calculate the file offset by reading the corresponding local header + private uint GetFileOffset(uint _headerOffset) + { + byte[] buffer = new byte[2]; + + this.ZipFileStream.Seek(_headerOffset + 26, SeekOrigin.Begin); + this.ZipFileStream.Read(buffer, 0, 2); + ushort filenameSize = BitConverter.ToUInt16(buffer, 0); + this.ZipFileStream.Read(buffer, 0, 2); + ushort extraSize = BitConverter.ToUInt16(buffer, 0); + + return (uint)(30 + filenameSize + extraSize + _headerOffset); + } + /* Local file header: + local file header signature 4 bytes (0x04034b50) + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + filename length 2 bytes + extra field length 2 bytes + + filename (variable size) + extra field (variable size) + */ + private void WriteLocalHeader(ref ZipFileEntry _zfe) + { + long pos = this.ZipFileStream.Position; + Encoding encoder = _zfe.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + byte[] encodedFilename = encoder.GetBytes(_zfe.FilenameInZip); + + this.ZipFileStream.Write(new byte[] { 80, 75, 3, 4, 20, 0}, 0, 6); // No extra header + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)(_zfe.EncodeUTF8 ? 0x0800 : 0)), 0, 2); // filename and comment encoding + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)_zfe.Method), 0, 2); // zipping method + this.ZipFileStream.Write(BitConverter.GetBytes(DateTimeToDosTime(_zfe.ModifyTime)), 0, 4); // zipping date and time + this.ZipFileStream.Write(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 12); // unused CRC, un/compressed size, updated later + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)encodedFilename.Length), 0, 2); // filename length + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0), 0, 2); // extra length + + this.ZipFileStream.Write(encodedFilename, 0, encodedFilename.Length); + _zfe.HeaderSize = (uint)(this.ZipFileStream.Position - pos); + } + /* Central directory's File header: + central file header signature 4 bytes (0x02014b50) + version made by 2 bytes + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + filename length 2 bytes + extra field length 2 bytes + file comment length 2 bytes + disk number start 2 bytes + internal file attributes 2 bytes + external file attributes 4 bytes + relative offset of local header 4 bytes + + filename (variable size) + extra field (variable size) + file comment (variable size) + */ + private void WriteCentralDirRecord(ZipFileEntry _zfe) + { + Encoding encoder = _zfe.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + byte[] encodedFilename = encoder.GetBytes(_zfe.FilenameInZip); + byte[] encodedComment = encoder.GetBytes(_zfe.Comment); + + this.ZipFileStream.Write(new byte[] { 80, 75, 1, 2, 23, 0xB, 20, 0 }, 0, 8); + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)(_zfe.EncodeUTF8 ? 0x0800 : 0)), 0, 2); // filename and comment encoding + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)_zfe.Method), 0, 2); // zipping method + this.ZipFileStream.Write(BitConverter.GetBytes(DateTimeToDosTime(_zfe.ModifyTime)), 0, 4); // zipping date and time + this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.Crc32), 0, 4); // file CRC + this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.CompressedSize), 0, 4); // compressed file size + this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.FileSize), 0, 4); // uncompressed file size + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)encodedFilename.Length), 0, 2); // Filename in zip + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0), 0, 2); // extra length + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)encodedComment.Length), 0, 2); + + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0), 0, 2); // disk=0 + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0), 0, 2); // file type: binary + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0), 0, 2); // Internal file attributes + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0x8100), 0, 2); // External file attributes (normal/readable) + this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.HeaderOffset), 0, 4); // Offset of header + + this.ZipFileStream.Write(encodedFilename, 0, encodedFilename.Length); + this.ZipFileStream.Write(encodedComment, 0, encodedComment.Length); + } + /* End of central dir record: + end of central dir signature 4 bytes (0x06054b50) + number of this disk 2 bytes + number of the disk with the + start of the central directory 2 bytes + total number of entries in + the central dir on this disk 2 bytes + total number of entries in + the central dir 2 bytes + size of the central directory 4 bytes + offset of start of central + directory with respect to + the starting disk number 4 bytes + zipfile comment length 2 bytes + zipfile comment (variable size) + */ + private void WriteEndRecord(uint _size, uint _offset) + { + Encoding encoder = this.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + byte[] encodedComment = encoder.GetBytes(this.Comment); + + this.ZipFileStream.Write(new byte[] { 80, 75, 5, 6, 0, 0, 0, 0 }, 0, 8); + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)Files.Count+ExistingFiles), 0, 2); + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)Files.Count+ExistingFiles), 0, 2); + this.ZipFileStream.Write(BitConverter.GetBytes(_size), 0, 4); + this.ZipFileStream.Write(BitConverter.GetBytes(_offset), 0, 4); + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)encodedComment.Length), 0, 2); + this.ZipFileStream.Write(encodedComment, 0, encodedComment.Length); + } + // Copies all source file into storage file + private void Store(ref ZipFileEntry _zfe, Stream _source) + { + byte[] buffer = new byte[16384]; + int bytesRead; + uint totalRead = 0; + Stream outStream; + + long posStart = this.ZipFileStream.Position; + long sourceStart = _source.Position; + + if (_zfe.Method == Compression.Store) + outStream = this.ZipFileStream; + else + outStream = new DeflateStream(this.ZipFileStream, CompressionMode.Compress, true); + + _zfe.Crc32 = 0 ^ 0xffffffff; + + do + { + bytesRead = _source.Read(buffer, 0, buffer.Length); + totalRead += (uint)bytesRead; + if (bytesRead > 0) + { + outStream.Write(buffer, 0, bytesRead); + + for (uint i = 0; i < bytesRead; i++) + { + _zfe.Crc32 = ZipStorer.CrcTable[(_zfe.Crc32 ^ buffer[i]) & 0xFF] ^ (_zfe.Crc32 >> 8); + } + } + } while (bytesRead == buffer.Length); + outStream.Flush(); + + if (_zfe.Method == Compression.Deflate) + outStream.Dispose(); + + _zfe.Crc32 ^= 0xffffffff; + _zfe.FileSize = totalRead; + _zfe.CompressedSize = (uint)(this.ZipFileStream.Position - posStart); + + // Verify for real compression + if (_zfe.Method == Compression.Deflate && !this.ForceDeflating && _source.CanSeek && _zfe.CompressedSize > _zfe.FileSize) + { + // Start operation again with Store algorithm + _zfe.Method = Compression.Store; + this.ZipFileStream.Position = posStart; + this.ZipFileStream.SetLength(posStart); + _source.Position = sourceStart; + this.Store(ref _zfe, _source); + } + } + /* DOS Date and time: + MS-DOS date. The date is a packed value with the following format. Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + MS-DOS time. The time is a packed value with the following format. Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) + */ + private uint DateTimeToDosTime(DateTime _dt) + { + return (uint)( + (_dt.Second / 2) | (_dt.Minute << 5) | (_dt.Hour << 11) | + (_dt.Day<<16) | (_dt.Month << 21) | ((_dt.Year - 1980) << 25)); + } + private DateTime DosTimeToDateTime(uint _dt) + { + return new DateTime( + (int)(_dt >> 25) + 1980, + (int)(_dt >> 21) & 15, + (int)(_dt >> 16) & 31, + (int)(_dt >> 11) & 31, + (int)(_dt >> 5) & 63, + (int)(_dt & 31) * 2); + } + + /* CRC32 algorithm + The 'magic number' for the CRC is 0xdebb20e3. + The proper CRC pre and post conditioning + is used, meaning that the CRC register is + pre-conditioned with all ones (a starting value + of 0xffffffff) and the value is post-conditioned by + taking the one's complement of the CRC residual. + If bit 3 of the general purpose flag is set, this + field is set to zero in the local header and the correct + value is put in the data descriptor and in the central + directory. + */ + private void UpdateCrcAndSizes(ref ZipFileEntry _zfe) + { + long lastPos = this.ZipFileStream.Position; // remember position + + this.ZipFileStream.Position = _zfe.HeaderOffset + 8; + this.ZipFileStream.Write(BitConverter.GetBytes((ushort)_zfe.Method), 0, 2); // zipping method + + this.ZipFileStream.Position = _zfe.HeaderOffset + 14; + this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.Crc32), 0, 4); // Update CRC + this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.CompressedSize), 0, 4); // Compressed size + this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.FileSize), 0, 4); // Uncompressed size + + this.ZipFileStream.Position = lastPos; // restore position + } + // Replaces backslashes with slashes to store in zip header + private string NormalizedFilename(string _filename) + { + string filename = _filename.Replace('\\', '/'); + + int pos = filename.IndexOf(':'); + if (pos >= 0) + filename = filename.Remove(0, pos + 1); + + return filename.Trim('/'); + } + // Reads the end-of-central-directory record + private bool ReadFileInfo() + { + if (this.ZipFileStream.Length < 22) + return false; + + try + { + this.ZipFileStream.Seek(-17, SeekOrigin.End); + BinaryReader br = new BinaryReader(this.ZipFileStream); + do + { + this.ZipFileStream.Seek(-5, SeekOrigin.Current); + UInt32 sig = br.ReadUInt32(); + if (sig == 0x06054b50) + { + this.ZipFileStream.Seek(6, SeekOrigin.Current); + + UInt16 entries = br.ReadUInt16(); + Int32 centralSize = br.ReadInt32(); + UInt32 centralDirOffset = br.ReadUInt32(); + UInt16 commentSize = br.ReadUInt16(); + + // check if comment field is the very last data in file + if (this.ZipFileStream.Position + commentSize != this.ZipFileStream.Length) + return false; + + // Copy entire central directory to a memory buffer + this.ExistingFiles = entries; + this.CentralDirImage = new byte[centralSize]; + this.ZipFileStream.Seek(centralDirOffset, SeekOrigin.Begin); + this.ZipFileStream.Read(this.CentralDirImage, 0, centralSize); + + // Leave the pointer at the begining of central dir, to append new files + this.ZipFileStream.Seek(centralDirOffset, SeekOrigin.Begin); + return true; + } + } while (this.ZipFileStream.Position > 0); + } + catch { } + + return false; + } + #endregion + + #region IDisposable Members + /// + /// Closes the Zip file stream + /// + public void Dispose() + { + this.Close(); + } + #endregion + } +}