private void WriteHeader(System.IO.Stream s, byte[] bytes) { // write the header info int i = 0; // signature bytes[i++] = (byte)(ZipConstants.ZipEntrySignature & 0x000000FF); bytes[i++] = (byte)((ZipConstants.ZipEntrySignature & 0x0000FF00) >> 8); bytes[i++] = (byte)((ZipConstants.ZipEntrySignature & 0x00FF0000) >> 16); bytes[i++] = (byte)((ZipConstants.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); Int16 CompressionMethod = 0x08; // 0x08 = Deflate, 0x00 == No Compression // CRC32 (Int32) if (_FileData != null) { // if we have FileData, that means we've read this entry from an // existing zip archive. We must just copy the existing file data, // CRC, compressed size, and uncompressed size // over to the new (updated) archive. } else { // Read in the data from the file in the filesystem, comress it, and // calculate a CRC on it as we read. CRC32 crc32 = new CRC32(); using (System.IO.Stream input = System.IO.File.OpenRead(LocalFileName)) { UInt32 crc = crc32.GetCrc32AndCopy(input, CompressedStream); _Crc32 = (Int32)crc; } CompressedStream.Close(); // to get the footer bytes written to the underlying stream _UncompressedSize = crc32.TotalBytesRead; _CompressedSize = (Int32)_UnderlyingMemoryStream.Length; // It is possible that applying this stream compression on a previously compressed // file will actually increase the size of the data. In that case, we back-off // and just store the uncompressed (really, already compressed) data. // We need to recompute the CRC, and point to the right data. if (_CompressedSize > _UncompressedSize) { using (System.IO.Stream input = System.IO.File.OpenRead(LocalFileName)) { _UnderlyingMemoryStream = new System.IO.MemoryStream(); UInt32 crc = crc32.GetCrc32AndCopy(input, _UnderlyingMemoryStream); _Crc32 = (Int32)crc; } _UncompressedSize = crc32.TotalBytesRead; _CompressedSize = (Int32)_UnderlyingMemoryStream.Length; if (_CompressedSize != _UncompressedSize) throw new Exception("No compression but unequal stream lengths!"); CompressionMethod = 0x00; } } // compression method 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); // calculated above bytes[i++] = (byte)(_Crc32 & 0x000000FF); bytes[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8); bytes[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16); bytes[i++] = (byte)((_Crc32 & 0xFF000000) >> 24); // CompressedSize (Int32) bytes[i++] = (byte)(_CompressedSize & 0x000000FF); bytes[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8); bytes[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16); bytes[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24); // UncompressedSize (Int32) if (_Debug) System.Console.WriteLine("Uncompressed Size: {0}", _UncompressedSize); bytes[i++] = (byte)(_UncompressedSize & 0x000000FF); bytes[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8); bytes[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16); bytes[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24); // filename length (Int16) Int16 length = (Int16)FileName.Length; // see note below about TrimVolumeFromFullyQualifiedPaths. if ((TrimVolumeFromFullyQualifiedPaths) && (FileName[1] == ':') && (FileName[2] == '\\')) length -= 3; 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); // Tue, 27 Mar 2007 16:35 // Creating a zip that contains entries with "fully qualified" pathnames // can result in a zip archive that is unreadable by Windows Explorer. // Such archives are valid according to other tools but not to explorer. // To avoid this, we can trim off the leading volume name and slash (eg // c:\) when creating (writing) a zip file. We do this by default and we // leave the old behavior available with the // TrimVolumeFromFullyQualifiedPaths flag - set it to false to get the old // behavior. It only affects zip creation. // actual filename char[] c = ((TrimVolumeFromFullyQualifiedPaths) && (FileName[1] == ':') && (FileName[2] == '\\')) ? FileName.Substring(3).ToCharArray() : // trim off volume letter, colon, and slash FileName.ToCharArray(); int j = 0; if (_Debug) { System.Console.WriteLine("local header: writing filename, {0} chars", c.Length); System.Console.WriteLine("starting offset={0}", i); } for (j = 0; (j < c.Length) && (i + j < bytes.Length); j++) { bytes[i + j] = System.BitConverter.GetBytes(c[j])[0]; if (_Debug) System.Console.Write(" {0:X2}", bytes[i + j]); } if (_Debug) System.Console.WriteLine(); i += j; // extra field (we always write nothing in this implementation) // ;; // remember the file offset of this header _RelativeOffsetOfHeader = (int)s.Length; if (_Debug) { System.Console.WriteLine("\nAll header data:"); for (j = 0; j < i; j++) System.Console.Write(" {0:X2}", bytes[j]); System.Console.WriteLine(); } // 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]; if (_Debug) System.Console.WriteLine("preserving header of {0} bytes", _header.Length); for (j = 0; j < i; j++) _header[j] = bytes[j]; }