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];
}