private bool WriteLocalFileHeader(bool isEmptyFile)
{
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();
bool zip64Used = false;
uint compressedSizeTruncated, uncompressedSizeTruncated;
//if we already know that we have an empty file don't worry about anything, just do a straight shot of the header
if (isEmptyFile)
{
CompressionMethod = CompressionMethodValues.Stored;
compressedSizeTruncated = 0;
uncompressedSizeTruncated = 0;
Debug.Assert(_compressedSize == 0);
Debug.Assert(_uncompressedSize == 0);
Debug.Assert(_crc32 == 0);
}
else
{
//if we have a non-seekable stream, don't worry about sizes at all, and just set the right bit
//if we are using the data descriptor, then sizes and crc should be set to 0 in the header
if (_archive.Mode == ZipArchiveMode.Create && _archive.ArchiveStream.CanSeek == false && !isEmptyFile)
{
_generalPurposeBitFlag |= BitFlagValues.DataDescriptor;
zip64Used = false;
compressedSizeTruncated = 0;
uncompressedSizeTruncated = 0;
//the crc should not have been set if we are in create mode, but clear it just to be sure
Debug.Assert(_crc32 == 0);
}
else //if we are not in streaming mode, we have to decide if we want to write zip64 headers
{
if (SizesTooLarge()
#if DEBUG_FORCE_ZIP64
|| (_archive._forceZip64 && _archive.Mode == ZipArchiveMode.Update)
#endif
)
{
zip64Used = true;
compressedSizeTruncated = ZipHelper.Mask32Bit;
uncompressedSizeTruncated = ZipHelper.Mask32Bit;
//prepare Zip64 extra field object. If we have one of the sizes, the other must go in there
zip64ExtraField.CompressedSize = _compressedSize;
zip64ExtraField.UncompressedSize = _uncompressedSize;
VersionToExtractAtLeast(ZipVersionNeededValues.Zip64);
}
else
{
zip64Used = false;
compressedSizeTruncated = (uint)_compressedSize;
uncompressedSizeTruncated = (uint)_uncompressedSize;
}
}
}
//save offset
_offsetOfLocalHeader = writer.BaseStream.Position;
//calculate extra field. if zip64 stuff + original extraField aren't going to fit, dump the original extraField, because this is more important
int bigExtraFieldLength = (zip64Used ? zip64ExtraField.TotalSize : 0)
+ (_lhUnknownExtraFields != null ? ZipGenericExtraField.TotalSize(_lhUnknownExtraFields) : 0);
ushort extraFieldLength;
if (bigExtraFieldLength > ushort.MaxValue)
{
extraFieldLength = (ushort)(zip64Used ? zip64ExtraField.TotalSize : 0);
_lhUnknownExtraFields = null;
}
else
{
extraFieldLength = (ushort)bigExtraFieldLength;
}
//write header
writer.Write(ZipLocalFileHeader.SignatureConstant);
writer.Write((ushort)_versionToExtract);
writer.Write((ushort)_generalPurposeBitFlag);
writer.Write((ushort)CompressionMethod);
writer.Write(ZipHelper.DateTimeToDosTime(_lastModified.DateTime)); //uint
writer.Write(_crc32); //uint
writer.Write(compressedSizeTruncated); //uint
writer.Write(uncompressedSizeTruncated); //uint
writer.Write((ushort)_storedEntryNameBytes.Length);
writer.Write(extraFieldLength); //ushort
writer.Write(_storedEntryNameBytes);
if (zip64Used)
zip64ExtraField.WriteBlock(_archive.ArchiveStream);
if (_lhUnknownExtraFields != null)
ZipGenericExtraField.WriteAllBlocks(_lhUnknownExtraFields, _archive.ArchiveStream);
return zip64Used;
}