Crisis.Ionic.Zip.ZipEntry.WriteCentralDirectoryEntry C# (CSharp) Method

WriteCentralDirectoryEntry() private method

private WriteCentralDirectoryEntry ( Stream s ) : void
s Stream
return void
        internal void WriteCentralDirectoryEntry(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.

            // workitem 11969: Version Needed To Extract in central directory must be
            // the same as the local entry or MS .NET System.IO.Zip fails read.
            Int16 vNeeded = (Int16)(VersionNeeded != 0 ? VersionNeeded : 20);
            // workitem 12964
            if (_OutputUsesZip64==null)
            {
                // a zipentry in a zipoutputstream, with zero bytes written
                _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always);
            }

            Int16 versionNeededToExtract = (Int16)(_OutputUsesZip64.Value ? 45 : vNeeded);
#if BZIP
            if (this.CompressionMethod == Ionic.Zip.CompressionMethod.BZip2)
                versionNeededToExtract = 46;
#endif

            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;

            // workitem 11131
            //
            // cannot generate the extra field again, here's why: In the case of a
            // zero-byte entry, which uses encryption, DotNetZip will "remove" the
            // encryption from the entry.  It does this in PostProcessOutput; it
            // modifies the entry header, and rewrites it, resetting the Bitfield
            // (one bit indicates encryption), and potentially resetting the
            // compression method - for AES the Compression method is 0x63, and it
            // would get reset to zero (no compression).  It then calls SetLength()
            // to truncate the stream to remove the encryption header (12 bytes for
            // AES256).  But, it leaves the previously-generated "Extra Field"
            // metadata (11 bytes) for AES in the entry header. This extra field
            // data is now "orphaned" - it refers to AES encryption when in fact no
            // AES encryption is used. But no problem, the PKWARE spec says that
            // unrecognized extra fields can just be ignored. ok.  After "removal"
            // of AES encryption, the length of the Extra Field can remains the
            // same; it's just that there will be 11 bytes in there that previously
            // pertained to AES which are now unused. Even the field code is still
            // there, but it will be unused by readers, as the encryption bit is not
            // set.
            //
            // Re-calculating the Extra field now would produce a block that is 11
            // bytes shorter, and that mismatch - between the extra field in the
            // local header and the extra field in the Central Directory - would
            // cause problems. (where? why? what problems?)  So we can't do
            // that. It's all good though, because though the content may have
            // changed, the length definitely has not. Also, the _EntryHeader
            // contains the "updated" extra field (after PostProcessOutput) at
            // offset (30 + filenameLength).

            _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
            bool segmented = (this._container.ZipFile != null) &&
                (this._container.ZipFile.MaxOutputSegmentSize != 0);
            if (segmented) // workitem 13915
            {
                // Emit nonzero disknumber only if saving segmented archive.
                bytes[i++] = (byte)(_diskNumber & 0x00FF);
                bytes[i++] = (byte)((_diskNumber & 0xFF00) >> 8);
            }
            else
            {
                // If reading a segmneted archive and saving to a regular archive,
                // ZipEntry._diskNumber will be non-zero but it should be saved as
                // zero.
                bytes[i++] = 0;
                bytes[i++] = 0;
            }

            // 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);

            // workitem 11131
            // relative offset of local header.
            //
            // If necessary to go to 64-bit value, then emit 0xFFFFFFFF,
            // else write out the value.
            //
            // Even if zip64 is required for other reasons - number of the entry
            // > 65534, or uncompressed size of the entry > MAX_INT32, the ROLH
            // need not be stored in a 64-bit field .
            if (_RelativeOffsetOfLocalHeader > 0xFFFFFFFFL) // _OutputUsesZip64.Value
            {
                bytes[i++] = 0xFF;
                bytes[i++] = 0xFF;
                bytes[i++] = 0xFF;
                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
            Buffer.BlockCopy(fileNameBytes, 0, bytes, i, filenameLength);
            i += filenameLength;

            // "Extra field"
            if (_Extra != null)
            {
                // workitem 11131
                //
                // copy from EntryHeader if available - it may have been updated.
                // if not, copy from Extra. This would be unnecessary if I just
                // updated the Extra field when updating EntryHeader, in
                // PostProcessOutput.

                //?? I don't understand why I wouldn't want to just use
                // the recalculated Extra field. ??

                // byte[] h = _EntryHeader ?? _Extra;
                // int offx = (h == _EntryHeader) ? 30 + filenameLength : 0;
                // Buffer.BlockCopy(h, offx, bytes, i, extraFieldLength);
                // i += extraFieldLength;

                byte[] h = _Extra;
                int offx = 0;
                Buffer.BlockCopy(h, offx, bytes, i, extraFieldLength);
                i += extraFieldLength;
            }

            // file (entry) comment
            if (commentLength != 0)
            {
                // now actually write the comment itself into the byte buffer
                Buffer.BlockCopy(_CommentBytes, 0, bytes, i, commentLength);
                // for (j = 0; (j < commentLength) && (i + j < bytes.Length); j++)
                //     bytes[i + j] = _CommentBytes[j];
                i += commentLength;
            }

            s.Write(bytes, 0, i);
        }
ZipEntry