in Core/src/Impl/CabCompressionUtil.cs [39:86]
private static void PatchCompressed(DateTime writeSourceFileTime, Stream stream)
{
// Bug: The C# library randomizes CCAB::setID in CabPacker::CreateFci(): `pccab.setID = checked ((short) new Random().Next((int) short.MinValue, 32768));`.
// See https://docs.microsoft.com/en-us/windows/win32/api/fci/ns-fci-ccab for details
var buffer = new byte[0x40];
var pos = stream.Position;
if (stream.Read(buffer, 0, buffer.Length) != buffer.Length)
throw new FormatException("Too short Microsoft CAB file");
if (buffer[0] != 'M' || buffer[1] != 'S' || buffer[2] != 'C' || buffer[3] != 'F')
throw new FormatException("Microsoft CAB file is expected");
// Additional time correctness validation
var span = TimeSpan.FromSeconds(2);
var ceil = writeSourceFileTime.ToCeil(span);
var floor = writeSourceFileTime.ToFloor(span);
if (floor.Year >= 1980)
{
var cab = DateTimeUtil.ToDateTime(
ToUInt16(buffer[0x37], buffer[0x36]),
ToUInt16(buffer[0x39], buffer[0x38]));
if (cab < floor || ceil < cab)
throw new FormatException("The time in the CAB-file record is out of 2 seconds range which can be patched");
}
else
{
throw new FormatException("The source time after rounding to floor is early then 1.1.1980");
}
// setID
const ushort id = 0xFFFF;
buffer[0x20] = id & 0xFF;
buffer[0x21] = id >> 8;
// DOS date + DOS time, see https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-filetimetodosdatetime
var dateTime = new DateTime(1980, 1, 1, 0, 0, 0);
var date = dateTime.ToDosDate();
var time = dateTime.ToDosTime();
buffer[0x36] = (byte) date;
buffer[0x37] = (byte) (date >> 8);
buffer[0x38] = (byte) time;
buffer[0x39] = (byte) (time >> 8);
stream.Seek(pos, SeekOrigin.Begin);
stream.Write(buffer, 0, buffer.Length);
}