private unsafe byte[] ApplyPatch(PatchInfoHeader patchInfoHeader, Stream baseStream)
{
PatchHeader patchHeader;
Read((byte*)&patchHeader, sizeof(PatchHeader));
if (!BitConverter.IsLittleEndian) CommonMethods.SwapBytes((uint*)&patchHeader, sizeof(PatchHeader) >> 2);
if (patchHeader.Signature != 0x48435450 /* 'PTCH' */) throw new InvalidDataException(ErrorMessages.GetString("PatchHeaderInvalidSignature"));
if (patchHeader.PatchedFileSize != file.Size) throw new InvalidDataException(ErrorMessages.GetString("PatchHeaderInvalidFileSize"));
if (baseStream.Length != patchHeader.OriginalFileSize) throw new InvalidDataException(ErrorMessages.GetString("PatchHeaderInvalidBaseFileSize"));
// Once the initial tests are passed, we can load the whole patch in memory.
// This will take a big amount of memory, but will avoid having to unpack the file twice…
var originalData = new byte[baseStream.Length];
if (baseStream.Read(originalData, 0, originalData.Length) != originalData.Length) throw new EndOfStreamException();
var md5 = CommonMethods.SharedMD5;
var originalHash = md5.ComputeHash(originalData);
PatchMD5ChunkData md5ChunkData;
bool hasMD5 = false;
while (true)
{
long chunkPosition = Position;
var chunkHeader = stackalloc uint[2];
if (Read((byte*)chunkHeader, 8) != 8) throw new EndOfStreamException();
if (!BitConverter.IsLittleEndian) CommonMethods.SwapBytes(chunkHeader, 2);
if (chunkHeader[0] == 0x5F35444D /* 'MD5_' */)
{
if (Read((byte*)&md5ChunkData, sizeof(PatchMD5ChunkData)) != sizeof(PatchMD5ChunkData)) throw new EndOfStreamException();
if (!CommonMethods.CompareData(originalHash, md5ChunkData.OrginialFileMD5)) throw new InvalidDataException(ErrorMessages.GetString("PatchBaseFileMD5Failed"));
hasMD5 = true;
}
else if (chunkHeader[0] == 0x4D524658 /* 'XFRM' */)
{
// This may not be a real problem, however, let's not handle this case for now… (May fail because of the stupid bogus patches…)
if (chunkPosition + chunkHeader[1] != Length) throw new InvalidDataException(ErrorMessages.GetString("PatchXfrmChunkError"));
uint patchType;
if (Read((byte*)&patchType, 4) != 4) throw new EndOfStreamException();
if (!BitConverter.IsLittleEndian) patchType = CommonMethods.SwapBytes(patchType);
uint patchLength = chunkHeader[1] - 12;
byte[] patchedData;
if (patchType == 0x59504F43 /* 'COPY' */) patchedData = ApplyCopyPatch(ref patchInfoHeader, ref patchHeader, patchLength, originalData);
if (patchType == 0x30445342 /* 'BSD0' */) patchedData = ApplyBsd0Patch(ref patchInfoHeader, ref patchHeader, patchLength, originalData);
else throw new NotSupportedException("Unsupported patch type: '" + CommonMethods.FourCCToString(chunkHeader[0]) + "'");
if (hasMD5)
{
var patchedHash = md5.ComputeHash(patchedData);
if (!CommonMethods.CompareData(patchedHash, md5ChunkData.PatchedFileMD5)) throw new InvalidDataException("PatchFinalFileMD5Failed");
}
return patchedData;
}
else throw new InvalidDataException(string.Format(ErrorMessages.GetString("PatchUnknownChunk"), CommonMethods.FourCCToString(chunkHeader[0])));
Seek(chunkPosition + chunkHeader[1], SeekOrigin.Begin);
}
}