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);
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)
{
// 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
int keystrength = GetKeyStrengthInBits(Encryption);
if (keystrength == 128)
block[i] = 1;
else if (keystrength == 256)
block[i] = 3;
else
block[i] = 0xFF;
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;
}