internal void WriteCentralDirectoryFileHeader()
{
//This part is simple, because we should definitely know the sizes by this time
BinaryWriter writer = new BinaryWriter(_archive.ArchiveStream);
//_entryname only gets set when we read in or call moveTo. MoveTo does a check, and
//reading in should not be able to produce a entryname longer than ushort.MaxValue
Debug.Assert(_storedEntryNameBytes.Length <= ushort.MaxValue);
//decide if we need the Zip64 extra field:
Zip64ExtraField zip64ExtraField = new Zip64ExtraField();
uint compressedSizeTruncated, uncompressedSizeTruncated, offsetOfLocalHeaderTruncated;
bool zip64Needed = false;
if (SizesTooLarge()
#if DEBUG_FORCE_ZIP64
|| _archive._forceZip64
#endif
)
{
zip64Needed = true;
compressedSizeTruncated = ZipHelper.Mask32Bit;
uncompressedSizeTruncated = ZipHelper.Mask32Bit;
//If we have one of the sizes, the other must go in there as speced for LH, but not necessarily for CH, but we do it anyways
zip64ExtraField.CompressedSize = _compressedSize;
zip64ExtraField.UncompressedSize = _uncompressedSize;
}
else
{
compressedSizeTruncated = (uint)_compressedSize;
uncompressedSizeTruncated = (uint)_uncompressedSize;
}
if (_offsetOfLocalHeader > uint.MaxValue
#if DEBUG_FORCE_ZIP64
|| _archive._forceZip64
#endif
)
{
zip64Needed = true;
offsetOfLocalHeaderTruncated = ZipHelper.Mask32Bit;
//If we have one of the sizes, the other must go in there as speced for LH, but not necessarily for CH, but we do it anyways
zip64ExtraField.LocalHeaderOffset = _offsetOfLocalHeader;
}
else
{
offsetOfLocalHeaderTruncated = (uint)_offsetOfLocalHeader;
}
if (zip64Needed)
VersionToExtractAtLeast(ZipVersionNeededValues.Zip64);
//determine if we can fit zip64 extra field and original extra fields all in
int bigExtraFieldLength = (zip64Needed ? zip64ExtraField.TotalSize : 0)
+ (_cdUnknownExtraFields != null ? ZipGenericExtraField.TotalSize(_cdUnknownExtraFields) : 0);
ushort extraFieldLength;
if (bigExtraFieldLength > ushort.MaxValue)
{
extraFieldLength = (ushort)(zip64Needed ? zip64ExtraField.TotalSize : 0);
_cdUnknownExtraFields = null;
}
else
{
extraFieldLength = (ushort)bigExtraFieldLength;
}
writer.Write(ZipCentralDirectoryFileHeader.SignatureConstant); // Central directory file header signature (4 bytes)
writer.Write((byte)_versionMadeBySpecification); // Version made by Specification (version) (1 byte)
writer.Write((byte)CurrentZipPlatform); // Version made by Compatibility (type) (1 byte)
writer.Write((ushort)_versionToExtract); // Minimum version needed to extract (2 bytes)
writer.Write((ushort)_generalPurposeBitFlag); // General Purpose bit flag (2 bytes)
writer.Write((ushort)CompressionMethod); // The Compression method (2 bytes)
writer.Write(ZipHelper.DateTimeToDosTime(_lastModified.DateTime)); // File last modification time and date (4 bytes)
writer.Write(_crc32); // CRC-32 (4 bytes)
writer.Write(compressedSizeTruncated); // Compressed Size (4 bytes)
writer.Write(uncompressedSizeTruncated); // Uncompressed Size (4 bytes)
writer.Write((ushort)_storedEntryNameBytes.Length); // File Name Length (2 bytes)
writer.Write(extraFieldLength); // Extra Field Length (2 bytes)
// This should hold because of how we read it originally in ZipCentralDirectoryFileHeader:
Debug.Assert((_fileComment == null) || (_fileComment.Length <= ushort.MaxValue));
writer.Write(_fileComment != null ? (ushort)_fileComment.Length : (ushort)0); //file comment length
writer.Write((ushort)0); //disk number start
writer.Write((ushort)0); //internal file attributes
writer.Write((uint)0); //external file attributes
writer.Write(offsetOfLocalHeaderTruncated); //offset of local header
writer.Write(_storedEntryNameBytes);
//write extra fields
if (zip64Needed)
zip64ExtraField.WriteBlock(_archive.ArchiveStream);
if (_cdUnknownExtraFields != null)
ZipGenericExtraField.WriteAllBlocks(_cdUnknownExtraFields, _archive.ArchiveStream);
if (_fileComment != null)
writer.Write(_fileComment);
}