CrystalMpq.MpqFileStream.ApplyPatch C# (CSharp) Method

ApplyPatch() private method

private ApplyPatch ( PatchInfoHeader patchInfoHeader, Stream baseStream ) : byte[]
patchInfoHeader PatchInfoHeader
baseStream Stream
return byte[]
        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);
            }
        }