context: //#define Trace
  
  // ZipEntry.Write.cs
  // ------------------------------------------------------------------
  //
  // Copyright (c)  2009 Dino Chiesa
  // All rights reserved.
  //
  // This code module is part of DotNetZip, a zipfile class library.
  //
  // ------------------------------------------------------------------
  //
  // This code is licensed under the Microsoft Public License.
  // See the file License.txt for the license details.
  // More info on: http://dotnetzip.codeplex.com
  //
  // ------------------------------------------------------------------
  //
  // last saved (in emacs):
  // Time-stamp: <2010-January-07 01:34:03>
  //
  // ------------------------------------------------------------------
  //
  // This module defines logic for writing (saving) the ZipEntry into a
  // zip file.
  //
  //
  // ------------------------------------------------------------------
  
  
  using System;
  using System.IO;
  using RE = System.Text.RegularExpressions;
  
  namespace Ionic.Zip
  {
  
      public partial class ZipEntry
      {
  
          internal void WriteCentralDirectoryEntry(Stream s)
          {
              _ConsAndWriteCentralDirectoryEntry(s);
          }
  
  
          private void _ConsAndWriteCentralDirectoryEntry(Stream s)
          {
              byte[] bytes = new byte[4096];
              int i = 0;
              // signature
              bytes[i++] = (byte)(ZipConstants.ZipDirEntrySignature & 0x000000FF);
              bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0x0000FF00) >> 8);
              bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0x00FF0000) >> 16);
              bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0xFF000000) >> 24);
  
              // Version Made By
              // workitem 7071
              // We must not overwrite the VersionMadeBy field when writing out a zip archive.
              // The VersionMadeBy tells the zip reader the meaning of the File attributes.
              // Overwriting the VersionMadeBy will result in inconsistent metadata.
              // Consider the scenario where the application opens and reads a zip file that had been created
              // on Linux. Then the app adds one file to the Zip archive, and saves it.
              // The file attributes for all the entries added on Linux will be significant
              // for Linux.  Therefore the VersionMadeBy for those entries must not be changed.
              // Only the entries that are actually created on Windows NTFS should get the
              // VersionMadeBy indicating Windows/NTFS.
              bytes[i++] = (byte)(_VersionMadeBy & 0x00FF);
              bytes[i++] = (byte)((_VersionMadeBy & 0xFF00) >> 8);
  
              // Apparently we want to duplicate the extra field here; we cannot
              // simply zero it out and assume tools and apps will use the right one.
  
              ////Int16 extraFieldLengthSave = (short)(_EntryHeader[28] + _EntryHeader[29] * 256);
              ////_EntryHeader[28] = 0;
              ////_EntryHeader[29] = 0;
  
              // Version Needed, Bitfield, compression method, lastmod,
              // crc, compressed and uncompressed sizes, filename length and extra field length.
              // These are all present in the local file header, but they may be zero values there.
              // So we cannot just copy them.
  
              Int16 versionNeededToExtract = (Int16)(_OutputUsesZip64.Value ? 45 : 20);
  
              bytes[i++] = (byte)(versionNeededToExtract & 0x00FF);
              bytes[i++] = (byte)((versionNeededToExtract & 0xFF00) >> 8);
  
              bytes[i++] = (byte)(_BitField & 0x00FF);
              bytes[i++] = (byte)((_BitField & 0xFF00) >> 8);
  
              bytes[i++] = (byte)(_CompressionMethod & 0x00FF);
              bytes[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8);
  
  #if AESCRYPTO
              if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
              Encryption == EncryptionAlgorithm.WinZipAes256)
              {
                  i -= 2;
                  bytes[i++] = 0x63;
                  bytes[i++] = 0;
              }
  #endif
  
              bytes[i++] = (byte)(_TimeBlob & 0x000000FF);
              bytes[i++] = (byte)((_TimeBlob & 0x0000FF00) >> 8);
              bytes[i++] = (byte)((_TimeBlob & 0x00FF0000) >> 16);
              bytes[i++] = (byte)((_TimeBlob & 0xFF000000) >> 24);
              bytes[i++] = (byte)(_Crc32 & 0x000000FF);
              bytes[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8);
              bytes[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16);
              bytes[i++] = (byte)((_Crc32 & 0xFF000000) >> 24);
  
              int j = 0;
              if (_OutputUsesZip64.Value)
              {
                  // CompressedSize (Int32) and UncompressedSize - all 0xFF
                  for (j = 0; j < 8; j++)
                      bytes[i++] = 0xFF;
              }
              else
              {
                  bytes[i++] = (byte)(_CompressedSize & 0x000000FF);
                  bytes[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8);
                  bytes[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16);
                  bytes[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24);
  
                  bytes[i++] = (byte)(_UncompressedSize & 0x000000FF);
                  bytes[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8);
                  bytes[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16);
                  bytes[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24);
              }
  
              byte[] FileNameBytes = _GetEncodedFileNameBytes();
              Int16 filenameLength = (Int16)FileNameBytes.Length;
              bytes[i++] = (byte)(filenameLength & 0x00FF);
              bytes[i++] = (byte)((filenameLength & 0xFF00) >> 8);
  
  
              // do this again because now we have real data
              _presumeZip64 = _OutputUsesZip64.Value;
              _Extra = ConstructExtraField(true);
  
              Int16 extraFieldLength = (Int16)((_Extra == null) ? 0 : _Extra.Length);
              bytes[i++] = (byte)(extraFieldLength & 0x00FF);
              bytes[i++] = (byte)((extraFieldLength & 0xFF00) >> 8);
  
              // File (entry) Comment Length
              // the _CommentBytes private field was set during WriteHeader()
              int commentLength = (_CommentBytes == null) ? 0 : _CommentBytes.Length;
  
              // the size of our buffer defines the max length of the comment we can write
              if (commentLength + i > bytes.Length) commentLength = bytes.Length - i;
              bytes[i++] = (byte)(commentLength & 0x00FF);
              bytes[i++] = (byte)((commentLength & 0xFF00) >> 8);
  
              // Disk number start
              bytes[i++] = (byte)(_diskNumber & 0x00FF);
              bytes[i++] = (byte)((_diskNumber & 0xFF00) >> 8);
  
              // internal file attrs
              // workitem 7801
              bytes[i++] = (byte)((_IsText) ? 1 : 0); // lo bit: filetype hint.  0=bin, 1=txt.
              bytes[i++] = 0;
  
              // external file attrs
              // workitem 7071
              bytes[i++] = (byte)(_ExternalFileAttrs & 0x000000FF);
              bytes[i++] = (byte)((_ExternalFileAttrs & 0x0000FF00) >> 8);
              bytes[i++] = (byte)((_ExternalFileAttrs & 0x00FF0000) >> 16);
              bytes[i++] = (byte)((_ExternalFileAttrs & 0xFF000000) >> 24);
  
              // relative offset of local header
              if (_OutputUsesZip64.Value)
              {
                  // Value==true means it used Zip64.
                  for (j = 0; j < 4; j++) bytes[i++] = 0xFF;
              }
              else
              {
                  bytes[i++] = (byte)(_RelativeOffsetOfLocalHeader & 0x000000FF);
                  bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0x0000FF00) >> 8);
                  bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0x00FF0000) >> 16);
                  bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0xFF000000) >> 24);
              }
  
              // actual filename
              for (j = 0; j < filenameLength; j++)
                  bytes[i + j] = FileNameBytes[j];
              i += j;
  
              // "Extra field"
              if (_Extra != null)
              {
                  for (j = 0; j < extraFieldLength; j++)
                      bytes[i + j] = _Extra[j];
                  i += j;
              }
  
              // file (entry) comment
              if (commentLength != 0)
              {
                  // now actually write the comment itself into the byte buffer
                  for (j = 0; (j < commentLength) && (i + j < bytes.Length); j++)
                      bytes[i + j] = _CommentBytes[j];
                  i += j;
              }
  
              s.Write(bytes, 0, i);
          }
  
  
  #if INFOZIP_UTF8
          static private bool FileNameIsUtf8(char[] FileNameChars)
          {
              bool isUTF8 = false;
              bool isUnicode = false;
              for (int j = 0; j < FileNameChars.Length; j++)
              {
                  byte[] b = System.BitConverter.GetBytes(FileNameChars[j]);
                  isUnicode |= (b.Length != 2);
                  isUnicode |= (b[1] != 0);
                  isUTF8 |= ((b[0] & 0x80) != 0);
              }
  
              return isUTF8;
          }
  #endif
  
  
          private byte[] ConstructExtraField(bool forCentralDirectory)
          {
              var listOfBlocks = new System.Collections.Generic.List<byte[]>();
              byte[] block;
  
              // Conditionally emit an extra field with Zip64 information.
              // If the Zip64 option is Always, we emit the field, before knowing that it's necessary.
              // Later, if it turns out this entry does not need zip64, we'll set the header ID to rubbish and
              // the data will be ignored.  This results in additional overhead metadata
              // in the zip file, but it will be small in comparison to the entry data.
              //
              // On the other hand if the Zip64 option is AsNecessary and it's NOT for the central
              // directory, then we do the same thing.  Or, if the Zip64 option is AsNecessary and
              // it IS for the central directory, and the entry requires zip64, then emit the
              // header.
              if (_container.Zip64 == Zip64Option.Always ||
                  (_container.Zip64 == Zip64Option.AsNecessary &&
                   (!forCentralDirectory || _entryRequiresZip64.Value)))
              {
                  // add extra field for zip64 here
                  // workitem 7924
                  int sz = 4 + (forCentralDirectory ? 28 : 16);
                  block = new byte[sz];
                  int i = 0;
  
                  if (_presumeZip64 || forCentralDirectory)
                  {
                      // HeaderId = always use zip64 extensions.
                      block[i++] = 0x01;
                      block[i++] = 0x00;
                  }
                  else
                  {
                      // HeaderId = dummy data now, maybe set to 0x0001 (ZIP64) later.
                      block[i++] = 0x99;
                      block[i++] = 0x99;
                  }
  
                  // DataSize
                  block[i++] = (byte)(sz - 4);  // decimal 28 or 16  (workitem 7924)
                  block[i++] = 0x00;
  
                  // The actual metadata - we may or may not have real values yet...
  
                  // uncompressed size
                  Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, block, i, 8);
                  i += 8;
                  // compressed size
                  Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, block, i, 8);
  
                  // workitem 7924 - only include this if the "extra" field is for use in the central directory.
                  // It is unnecessary and not useful for local header; makes WinZip choke.
                  if (forCentralDirectory)
                  {
                      i += 8;
                      // relative offset
                      Array.Copy(BitConverter.GetBytes(_RelativeOffsetOfLocalHeader), 0, block, i, 8);
                      i += 8;
                      // starting disk number
                      Array.Copy(BitConverter.GetBytes(0), 0, block, i, 4);
                  }
                  listOfBlocks.Add(block);
              }
  
  
  #if AESCRYPTO
              if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
                  Encryption == EncryptionAlgorithm.WinZipAes256)
              {
                  block = new byte[4 + 7];
                  int i = 0;
                  // extra field for WinZip AES
                  // header id
                  block[i++] = 0x01;
                  block[i++] = 0x99;
  
                  // data size
                  block[i++] = 0x07;
                  block[i++] = 0x00;
  
                  // vendor number
                  block[i++] = 0x01;  // AE-1 - means "Verify CRC"
                  block[i++] = 0x00;
  
                  // vendor id "AE"
                  block[i++] = 0x41;
                  block[i++] = 0x45;
  
                  // key strength
                  block[i] = 0xFF;
                  int keystrength = GetKeyStrengthInBits(Encryption);
                  if (keystrength == 128)
                      block[i] = 1;
                  if (keystrength == 256)
                      block[i] = 3;
                  i++;
  
                  // actual compression method
                  block[i++] = (byte)(_CompressionMethod & 0x00FF);
                  block[i++] = (byte)(_CompressionMethod & 0xFF00);
  
                  listOfBlocks.Add(block);
              }
  #endif
  
              if (_ntfsTimesAreSet && _emitNtfsTimes)
              {
                  block = new byte[32 + 4];
                  // HeaderId   2 bytes    0x000a == NTFS times
                  // Datasize   2 bytes    32
                  // reserved   4 bytes    ?? don't care
                  // timetag    2 bytes    0x0001 == NTFS time
                  // size       2 bytes    24 == 8 bytes each for ctime, mtime, atime
                  // mtime      8 bytes    win32 ticks since win32epoch
                  // atime      8 bytes    win32 ticks since win32epoch
                  // ctime      8 bytes    win32 ticks since win32epoch
                  int i = 0;
                  // extra field for NTFS times
                  // header id
                  block[i++] = 0x0a;
                  block[i++] = 0x00;
  
                  // data size
                  block[i++] = 32;
                  block[i++] = 0;
  
                  i += 4; // reserved
  
                  // time tag
                  block[i++] = 0x01;
                  block[i++] = 0x00;
  
                  // data size (again)
                  block[i++] = 24;
                  block[i++] = 0;
  
                  Int64 z = _Mtime.ToFileTime();
                  Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8);
                  i += 8;
                  z = _Atime.ToFileTime();
                  Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8);
                  i += 8;
                  z = _Ctime.ToFileTime();
                  Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8);
                  i += 8;
  
                  listOfBlocks.Add(block);
              }
  
              if (_ntfsTimesAreSet && _emitUnixTimes)
              {
                  int len = 5 + 4;
                  if (!forCentralDirectory) len += 8;
  
                  block = new byte[len];
                  // local form:
                  // --------------
                  // HeaderId   2 bytes    0x5455 == unix timestamp
                  // Datasize   2 bytes    13
                  // flags      1 byte     7 (low three bits all set)
                  // mtime      4 bytes    seconds since unix epoch
                  // atime      4 bytes    seconds since unix epoch
                  // ctime      4 bytes    seconds since unix epoch
                  //
                  // central directory form:
                  //---------------------------------
                  // HeaderId   2 bytes    0x5455 == unix timestamp
                  // Datasize   2 bytes    5
                  // flags      1 byte     7 (low three bits all set)
                  // mtime      4 bytes    seconds since unix epoch
                  //
                  int i = 0;
                  // extra field for "unix" times
                  // header id
                  block[i++] = 0x55;
                  block[i++] = 0x54;
  
                  // data size
                  block[i++] = unchecked((byte)(len - 4));
                  block[i++] = 0;
  
                  // flags
                  block[i++] = 0x07;
  
                  Int32 z = unchecked((int)((_Mtime - _unixEpoch).TotalSeconds));
                  Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4);
                  i += 4;
                  if (!forCentralDirectory)
                  {
                      z = unchecked((int)((_Atime - _unixEpoch).TotalSeconds));
                      Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4);
                      i += 4;
                      z = unchecked((int)((_Ctime - _unixEpoch).TotalSeconds));
                      Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4);
                      i += 4;
                  }
                  listOfBlocks.Add(block);
              }
  
  
              // inject other blocks here...
  
  
              // concatenate any blocks we've got:
              byte[] aggregateBlock = null;
              if (listOfBlocks.Count > 0)
              {
                  int totalLength = 0;
                  int i, current = 0;
                  for (i = 0; i < listOfBlocks.Count; i++)
                      totalLength += listOfBlocks[i].Length;
                  aggregateBlock = new byte[totalLength];
                  for (i = 0; i < listOfBlocks.Count; i++)
                  {
                      System.Array.Copy(listOfBlocks[i], 0, aggregateBlock, current, listOfBlocks[i].Length);
                      current += listOfBlocks[i].Length;
                  }
              }
  
              return aggregateBlock;
          }
  
  
  
          // workitem 6513: when writing, use alt encoding only when ibm437 will not do
          private System.Text.Encoding GenerateCommentBytes()
          {
              _CommentBytes = ibm437.GetBytes(_Comment);
              // need to use this form of GetString() for .NET CF
              string s1 = ibm437.GetString(_CommentBytes, 0, _CommentBytes.Length);
              if (s1 == _Comment)
                  return ibm437;
              else
              {
                  _CommentBytes = _provisionalAlternateEncoding.GetBytes(_Comment);
                  return _provisionalAlternateEncoding;
              }
          }
  
  
          // workitem 6513
          private byte[] _GetEncodedFileNameBytes()
          {
              // here, we need to flip the backslashes to forward-slashes,
              // also, we need to trim the \\server\share syntax from any UNC path.
              // and finally, we need to remove any leading .\
  
              string SlashFixed = FileName.Replace("\\", "/");
              string s1 = null;
              if ((_TrimVolumeFromFullyQualifiedPaths) && (FileName.Length >= 3)
                  && (FileName[1] == ':') && (SlashFixed[2] == '/'))
              {
                  // trim off volume letter, colon, and slash
                  s1 = SlashFixed.Substring(3);
              }
              else if ((FileName.Length >= 4)
                       && ((SlashFixed[0] == '/') && (SlashFixed[1] == '/')))
              {
                  int n = SlashFixed.IndexOf('/', 2);
                  if (n == -1)
                      throw new ArgumentException("The path for that entry appears to be badly formatted");
                  s1 = SlashFixed.Substring(n + 1);
              }
              else if ((FileName.Length >= 3)
                       && ((SlashFixed[0] == '.') && (SlashFixed[1] == '/')))
              {
                  // trim off dot and slash
                  s1 = SlashFixed.Substring(2);
              }
              else
              {
                  s1 = SlashFixed;
              }
  
              // workitem 6513: when writing, use the alternative encoding only when ibm437 will not do.
              byte[] result = ibm437.GetBytes(s1);
              // need to use this form of GetString() for .NET CF
              string s2 = ibm437.GetString(result, 0, result.Length);
              _CommentBytes = null;
              if (s2 == s1)
              {
                  // file can be encoded with ibm437, now try comment
  
                  // case 1: no comment.  use ibm437
                  if (_Comment == null || _Comment.Length == 0)
                  {
                      _actualEncoding = ibm437;
                      return result;
                  }
  
                  // there is a comment.  Get the encoded form.
                  System.Text.Encoding commentEncoding = GenerateCommentBytes();
  
                  // case 2: if the comment also uses 437, we're good.
                  if (commentEncoding.CodePage == 437)
                  {
                      _actualEncoding = ibm437;
                      return result;
                  }
  
                  // case 3: comment requires non-437 code page.  Use the same
                  // code page for the filename.
                  _actualEncoding = commentEncoding;
                  result = commentEncoding.GetBytes(s1);
                  return result;
              }
              else
              {
                  // Cannot encode with ibm437 safely.
                  // Therefore, use the provisional encoding
                  result = _provisionalAlternateEncoding.GetBytes(s1);
                  if (_Comment != null && _Comment.Length != 0)
                  {
                      _CommentBytes = _provisionalAlternateEncoding.GetBytes(_Comment);
                  }
  
                  _actualEncoding = _provisionalAlternateEncoding;
                  return result;
              }
          }
  
  
          private bool WantReadAgain()
          {
              if (_UncompressedSize < 0x10) return false;
              if (_CompressionMethod == 0x00) return false;
              if (CompressionLevel == Ionic.Zlib.CompressionLevel.None) return false;
              if (_CompressedSize < _UncompressedSize) return false;
  
              if (this._Source == ZipEntrySource.Stream && !this._sourceStream.CanSeek) return false;
  
  #if AESCRYPTO
              if (_aesCrypto_forWrite != null && (CompressedSize - _aesCrypto_forWrite.SizeOfEncryptionMetadata) <= UncompressedSize + 0x10) return false;
  #endif
  
              if (_zipCrypto_forWrite != null && (CompressedSize - 12) <= UncompressedSize) return false;
  
              return true;
          }
  
  
  
          private void FigureCompressionMethodForWriting(int cycle)
          {
              // if we've already tried with compression... turn it off this time
              if (cycle > 1)
              {
                  _CompressionMethod = 0x0;
                  return;
              }
              // compression for directories = 0x00 (No Compression)
              if (IsDirectory)
              {
                  _CompressionMethod = 0x0;
                  return;
              }
  
              //             if (__FileDataPosition != -1)
              //             {
              //                 // If at this point, __FileDataPosition is non-zero, that means we've read this
              //                 // entry from an existing zip archive.
              //                 //
              //                 // In this case, we just keep the existing file data and metadata (including
              //                 // CompressionMethod, CRC, compressed size, uncompressed size, etc).
              //                 //
              //                 // All those member variables have been set during read!
              //                 //
              //                 return;
              //             }
  
              if (this._Source == ZipEntrySource.ZipFile)
              {
                  return;
              }
  
              // If __FileDataPosition is zero, then that means we will get the data
              // from a file or stream.
  
              // It is never possible to compress a zero-length file, so we check for
              // this condition.
  
  
              if (this._Source == ZipEntrySource.Stream)
              {
                  // workitem 7742
                  if (_sourceStream != null && _sourceStream.CanSeek)
                  {
                      // Length prop will throw if CanSeek is false
                      long fileLength = _sourceStream.Length;
                      if (fileLength == 0)
                      {
                          _CompressionMethod = 0x00;
                          return;
                      }
                  }
              }
              else if ((this._Source == ZipEntrySource.FileSystem) && (SharedUtilities.GetFileLength(LocalFileName) == 0L))
              {
                  _CompressionMethod = 0x00;
                  return;
              }
  
              // Ok, we're getting the data to be compressed from a
              // non-zero-length file or stream, or a file or stream of
              // unknown length, and we presume that it is non-zero.  In
              // that case we check the callback to see if the app wants
              // to tell us whether to compress or not.
              if (SetCompression != null)
                  CompressionLevel = SetCompression(LocalFileName, _FileNameInArchive);
  
              _CompressionMethod = (short)((CompressionLevel == Ionic.Zlib.CompressionLevel.None)
                                            ? 0x00
                                            : 0x08);
              return;
  
          }
  
  
  
          // write the header info for an entry
          internal void WriteHeader(Stream s, int cycle)
          {
              // Must remember the offset, within the output stream, of this particular
              // entry header.
              //
              // This is for 2 reasons:
              //
              //  1. so we can determine the RelativeOffsetOfLocalHeader (ROLH) for use in the
              //     central directory.
              //  2. so we can seek backward in case there is an error opening or reading
              //     the file, and the application decides to skip the file. In this case,
              //     we need to seek backward in the output stream to allow the next entry
              //     to be added to the zipfile output stream.
              //
              // Normally you would just store the offset before writing to the output
              // stream and be done with it.  But the possibility to use split archives
              // makes this approach ineffective.  In split archives, each file or segment
              // is bound to a max size limit, and each local file header must not span a
              // segment boundary; it must be written contiguously.  If it will fit in the
              // current segment, then the ROLH is just the current Position in the output
              // stream.  If it won't fit, then we need a new file (segment) and the ROLH
              // is zero.
              //
              // But we only can know if it is possible to write a header contiguously
              // after we know the size of the local header, a size that varies with
              // things like filename length, comments, and extra fields.  We have to
              // compute the header fully before knowing whether it will fit.
              //
              // That takes care of item #1 above.  Now, regarding #2.  If an error occurs
              // while computing the local header, we want to just seek backward. The
              // exception handling logic (in the caller of WriteHeader) uses ROLH to
              // scroll back.
              //
              // All this means we have to preserve the starting offset before computing
              // the header, and also we have to compute the offset later, to handle the
              // case of split archives.
  
              var counter = s as CountingStream;
  
              // workitem 8098: ok (output)
              // This may change later, for split archives
  
              // Don't set _RelativeOffsetOfLocalHeader. Instead, set a temp variable.
              // This allows for re-streaming, where a zip entry might be read from a
              // zip archive (and maybe decrypted, and maybe decompressed) and then
              // written to another zip archive, with different settings for
              // compression method, compression level, or encryption algorithm.
              _future_ROLH = (counter != null)
                  ? counter.ComputedPosition
                  : s.Position;
  
              int j = 0;
              int i = 0;
              byte[] bytes = new byte[512];  // large enough for looooong filenames (MAX_PATH == 260)
  
              // 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);
  
              // Design notes for ZIP64:
  
              // The specification says that the header must include the Compressed and
              // Uncompressed sizes, as well as the CRC32 value.  When creating a zip via
              // streamed processing, these quantities are not known until after the compression
              // is done.  Thus, a typical way to do it is to insert zeroes for these quantities,
              // then do the compression, then seek back to insert the appropriate values, then
              // seek forward to the end of the file data.
  
              // There is also the option of using bit 3 in the GP bitfield - to specify that
              // there is a data descriptor after the file data containing these three
              // quantities.
  
              // This works when the size of the quantities is known, either 32-bits or 64 bits as
              // with the ZIP64 extensions.
  
              // With Zip64, the 4-byte fields are set to 0xffffffff, and there is a
              // corresponding data block in the "extra field" that contains the actual
              // Compressed, uncompressed sizes.  (As well as an additional field, the "Relative
              // Offset of Local Header")
  
              // The problem is when the app desires to use ZIP64 extensions optionally, only
              // when necessary.  Suppose the library assumes no zip64 extensions when writing
              // the header, then after compression finds that the size of the data requires
              // zip64.  At this point, the header, already written to the file, won't have the
              // necessary data block in the "extra field".  The size of the entry header is
              // fixed, so it is not possible to just "add on" the zip64 data block after
              // compressing the file.  On the other hand, always using zip64 will break
              // interoperability with many other systems and apps.
  
              // The approach we take is to insert a 32-byte dummy data block in the extra field,
              // whenever zip64 is to be used "as necessary". This data block will get the actual
              // zip64 HeaderId and zip64 metadata if necessary.  If not necessary, the data
              // block will get a meaningless HeaderId (0x1111), and will be filled with zeroes.
  
              // When zip64 is actually in use, we also need to set the VersionNeededToExtract
              // field to 45.
  
              // There is one additional wrinkle: using zip64 as necessary conflicts with output
              // to non-seekable devices.  The header is emitted and must indicate whether zip64
              // is in use, before we know if zip64 is necessary.  Because there is no seeking,
              // the header can never be changed.  Therefore, on non-seekable devices,
              // Zip64Option.AsNecessary is the same as Zip64Option.Always.
  
              // version needed- see AppNote.txt.
              // need v5.1 for PKZIP strong encryption, or v2.0 for no encryption or for PK
              // encryption, 4.5 for zip64.  We may reset this later, as necessary or zip64.
  
              _presumeZip64 = (_container.Zip64 == Zip64Option.Always || (_container.Zip64 == Zip64Option.AsNecessary && !s.CanSeek));
              Int16 VersionNeededToExtract = (Int16)(_presumeZip64 ? 45 : 20);
  
              // (i==4)
              bytes[i++] = (byte)(VersionNeededToExtract & 0x00FF);
              bytes[i++] = (byte)((VersionNeededToExtract & 0xFF00) >> 8);
  
              // get byte array including any encoding
              // workitem 6513
              byte[] FileNameBytes = _GetEncodedFileNameBytes();
              Int16 filenameLength = (Int16)FileNameBytes.Length;
  
              // general purpose bitfield
              // In the current implementation, this library uses only these bits
              // in the GP bitfield:
              //  bit 0 = if set, indicates the entry is encrypted
              //  bit 3 = if set, indicates the CRC, C and UC sizes follow the file data.
              //  bit 6 = strong encryption - for pkware's meaning of strong encryption
              //  bit 11 = UTF-8 encoding is used in the comment and filename
  
  
              // Here we set or unset the encryption bit.
              // _BitField may already be set, as with a ZipEntry added into ZipOutputStream, which
              // has bit 3 always set. We only want to set one bit
              if (_Encryption == EncryptionAlgorithm.None)
                  _BitField &= ~1;  // encryption bit OFF
              else
                  _BitField |= 1;   // encryption bit ON
  
  
              // workitem 7941: WinZip does not the "strong encryption" bit  when using AES.
              // This "Strong Encryption" is a PKWare Strong encryption thing.
              //                 _BitField |= 0x0020;
  
              // set the UTF8 bit if necessary
              if (ActualEncoding.CodePage == System.Text.Encoding.UTF8.CodePage) _BitField |= 0x0800;
  
              // The PKZIP spec says that if bit 3 is set (0x0008) in the General Purpose BitField,
              // then the CRC, Compressed size, and uncompressed size are written directly after the
              // file data.
              //
              // These 3 quantities are normally present in the regular zip entry header. But, they
              // are not knowable until after the compression is done. So, in the normal case, we
              //  - write the header, using zeros for these quantities
              //  - compress the data, and incidentally compute these quantities.
              //  - seek back and write the correct values them into the header.
              //
              // This is nice because, while it is more complicated to write the zip file, it is
              // simpler and less error prone to read the zip file, and as a result more
              // applications can read zip files produced this way, with those 3 quantities in the
              // header.
              //
              // But if seeking in the output stream is not possible, then we need to set the
              // appropriate bitfield and emit these quantities after the compressed file data in
              // the output.
              //
              // workitem 7216 - having trouble formatting a zip64 file that is readable by WinZip.
              // not sure why!  What I found is that setting bit 3 and following all the implications,
              // the zip64 file is readable by WinZip 12. and Perl's  IO::Compress::Zip .
              // Perl takes an interesting approach - it always sets bit 3 if ZIP64 in use.
              // DotNetZip now does the same; this gives better compatibility with WinZip 12.
  
              if (IsDirectory || cycle == 99)
              {
                  // (cycle == 99) indicates a zero-length entry written by ZipOutputStream
  
                  _BitField &= ~0x0008;  // unset bit 3 - no "data descriptor" - ever
                  _BitField &= ~0x0001;  // unset bit 1 - no encryption - ever
                  Encryption = EncryptionAlgorithm.None;
                  Password = null;
              }
              else if (!s.CanSeek)
                  _BitField |= 0x0008;
  
  #if DONT_GO_THERE
              else if (this.Encryption == EncryptionAlgorithm.PkzipWeak  &&
                       this._Source != ZipEntrySource.ZipFile)
              {
                  // Set bit 3 to avoid the double-read perf issue.
                  //
                  // When PKZIP encryption is used, byte 11 of the encryption header is
                  // used as a consistency check. It is normally set to the MSByte of the
                  // CRC.  But this means the cRC must be known ebfore compression and
                  // encryption, which means the entire stream has to be read twice.  To
                  // avoid that, the high-byte of the time blob (when in DOS format) can
                  // be used for the consistency check (byte 11 in the encryption header).
                  // But this means the entry must have bit 3 set.
                  //
                  // Previously I used a more complex arrangement - using the methods like
                  // FigureCrc32(), PrepOutputStream() and others, in order to manage the
                  // seek-back in the source stream.  Why?  Because bit 3 is not always
                  // friendly with third-party zip tools, like those on the Mac.
                  //
                  // This is why this code is still ifdef'd  out.
                  //
                  // Might consider making this yet another programmable option -
                  // AlwaysUseBit3ForPkzip.  But that's for another day.
                  //
                  _BitField |= 0x0008;
              }
  #endif
  
              // (i==6)
              bytes[i++] = (byte)(_BitField & 0x00FF);
              bytes[i++] = (byte)((_BitField & 0xFF00) >> 8);
  
              // Here, we want to set values for Compressed Size, Uncompressed Size, and CRC.  If
              // we have __FileDataPosition as not -1 (zero is a valid FDP), then that means we
              // are reading this zip entry from a zip file, and we have good values for those
              // quantities.
              //
              // If _FileDataPosition is -1, then we are constructing this Entry from nothing.  We
              // zero those quantities now, and we will compute actual values for the three
              // quantities later, when we do the compression, and then seek back to write them
              // into the appropriate place in the header.
              if (this.__FileDataPosition == -1)
              {
                  //_UncompressedSize = 0; // do not unset - may need this value for restream
                  // _Crc32 = 0;           // ditto
                  _CompressedSize = 0;
                  _crcCalculated = false;
              }
  
              // set compression method here
              FigureCompressionMethodForWriting(cycle);
  
              // (i==8) compression method
              bytes[i++] = (byte)(_CompressionMethod & 0x00FF);
  
gt:             bytes[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8);