Example #1
0
        public PatchApplyResponse Apply(Stream oldVersion, Stream patchStream, Stream output, IPatchProgress prog)
        {
            // Checksum our input to make sure things are okay
            byte[] oldVersionHash = MD5.Check(oldVersion);
            oldVersion.Seek(0, SeekOrigin.Begin);

            // Check if our signature is the same as the output
            bool isRequired = false;
            for (int i = 0; i < 16; i++) {
                if (oldVersionHash[i] != mPatFileInfo.TargetChecksum[i]) {
                    isRequired = true;
                    break;
                }
            }

            if (!isRequired) {
                return PatchApplyResponse.NotRequired;
            }

            // Make sure our file signatures match up
            for (int i = 0; i < 16; i++) {
                if (oldVersionHash[i] != mPatFileInfo.SourceChecksum[i]) return PatchApplyResponse.WrongFile;
            }

            byte[] copyBuffer = new byte[4096];

            BinaryReader br = new BinaryReader(patchStream);
            for (int currentBlock = 0; currentBlock < (int)mPatFileInfo.BlockCount; currentBlock++) {
                ulong blockSize = 0;
                long derp = patchStream.Position;
                byte blockType = br.ReadByte();
                switch (blockType) {
                    // Identical blocks
                    // ================
                    // Copy an amount of data from the original file in to the new one.
                    case 1:
                    case 2:
                    case 3:
                        // Decode the block length
                        switch (blockType) {
                            case 1:
                                blockSize = (ulong)br.ReadByte();
                                break;
                            case 2:
                                blockSize = (ulong)br.ReadUInt16();
                                break;
                            case 3:
                                blockSize = (ulong)br.ReadUInt32();
                                break;
                        }

                        long sourceOffset = br.ReadUInt32();
                        oldVersion.Seek(sourceOffset, SeekOrigin.Begin);

                        // If we have a derpyblock or couldn't read it, count it as a failure.
                        if (blockSize < 1) {
                            return PatchApplyResponse.Failed;
                        }

                        // Copy from the source to the output.
                        while (blockSize > 0) {
                            int read = oldVersion.Read(copyBuffer, 0, (int)Math.Min(4096, blockSize));
                            if (read <= 0) {
                                throw new IOException();
                            }
                            output.Write(copyBuffer, 0, read);
                            blockSize -= (ulong)read;
                        }
                        break;

                    // Payload delivery blocks
                    // =======================
                    // Copy an amount of data from our patch file in to the new one.
                    case 5:
                    case 6:
                    case 7:
                        switch (blockType) {
                            case 5:
                                blockSize = (ulong)br.ReadByte();
                                break;
                            case 6:
                                blockSize = (ulong)br.ReadUInt16();
                                break;
                            case 7:
                                blockSize = (ulong)br.ReadUInt32();
                                break;
                        }

                        while (blockSize > 0) {
                            int read = br.Read(copyBuffer, 0, (int)Math.Min(4096, blockSize));
                            if (read <= 0) {
                                throw new IOException();
                            }
                            output.Write(copyBuffer, 0, read);
                            blockSize -= (ulong)read;
                        }

                        break;

                    // Its the end of the taco stand, taco taco stand.
                    case 255:
                        // TODO: Should we really care about the timestamp?
                        br.ReadInt64();
                        break;

                    default:
                        return PatchApplyResponse.Failed;
                }
                // Issue any progress updates before moving to the next block
                if (prog != null) {
                    prog.OnPatchProgress(currentBlock, mPatFileInfo.BlockCount);
                }
            }

            // Make sure we applied the patch correctly
            output.Seek(0, SeekOrigin.Begin);
            byte[] patchedFileChecksum = MD5.Check(output);
            for (int i = 0; i < 16; i++) {
                if (patchedFileChecksum[i] != mPatFileInfo.TargetChecksum[i]) return PatchApplyResponse.Failed;
            }

            // We're done!
            return PatchApplyResponse.Ok;
        }
Example #2
0
        /// <param name="sameBlocks">
        /// This list will store blocks that have been found to have remained
        /// the same between files.
        /// </param>
        public void Execute(IList<SameBlock> sameBlocks, IPatchProgress prog)
        {
            if (sameBlocks == null)
                throw new ArgumentNullException();

            ChunkedFile sourceTree = new ChunkedFile(mSource, mSourceSize, mBlockSize);

            // the vector needs an 'empty' first block so checking for overlap with the 'previous' block never fails.
            sameBlocks.Add(new SameBlock());

            mTargetCDataBaseOffset = 0;
            mTargetCDataSize = 0;
            bool firstRun = true;

            // currentOffset is in the target file
            for (long currentOffset = 0; currentOffset < mTargetSize;) {
                bool reloadTargetCData = true;

                if ((currentOffset >= mTargetCDataBaseOffset) &&
                    (currentOffset + TargetLookaheadSize < mTargetCDataBaseOffset + TargetBufferSize))
                {
                    if (firstRun) {
                        firstRun = false;
                    } else {
                        reloadTargetCData = false;
                    }
                }

                if (reloadTargetCData) {
                    // at least support looking back blockSize, if possible (findBlock relies on this!)
                    mTargetCDataBaseOffset = currentOffset - mBlockSize;
                    // handle start of file correctly
                    if (currentOffset < BlockSize) mTargetCDataBaseOffset = 0;

                    mTargetCDataSize = TargetBufferSize;

                    // check if this does not extend beyond EOF
                    if (mTargetCDataBaseOffset + mTargetCDataSize > mTargetSize) {
                        mTargetCDataSize = mTargetSize - mTargetCDataBaseOffset;
                    }

                    // we need to update the memory cache of target
                    // TODO: Emit debug info here, if verbose is enabled.
                    // cout << "[CacheReload] File position = " << static_cast<unsigned long>(targetCDataBaseOffset) << "\n";

                    if (prog != null) {
                        prog.OnPatchProgress(mTargetCDataBaseOffset, mTargetSize);
                    }

                    mTarget.Seek(mTargetCDataBaseOffset, SeekOrigin.Begin);
                    mTarget.Read(mTargetCData, 0, (int)mTargetCDataSize);
                }

                SameBlock currentSameBlock = FindBlock(sourceTree, currentOffset);
                if (currentSameBlock != null) {
                    // We have a match.
                    SameBlock previousBlock = sameBlocks[sameBlocks.Count-1];
                    if (previousBlock.TargetOffset + previousBlock.Size > currentSameBlock.TargetOffset) {
                        // There is overlap, resolve it.
                        long difference = previousBlock.TargetOffset + previousBlock.Size - currentSameBlock.TargetOffset;
                        currentSameBlock.SourceOffset += difference;
                        currentSameBlock.TargetOffset += difference;
                        currentSameBlock.Size -= difference;
                    }
                    Console.WriteLine(currentSameBlock.ToString());
                    sameBlocks.Add(currentSameBlock);

                    // TODO: Emit debug info here, if verbose is enabled.

                    currentOffset = currentSameBlock.TargetOffset + currentSameBlock.Size;
                } else {
                    // No match, advance to the next byte.
                    currentOffset++;
                }
            }

            // Add a block at the end to prevent bounds checking hassles.
            SameBlock lastBlock = new SameBlock();
            lastBlock.SourceOffset = 0;
            lastBlock.TargetOffset = mTargetSize;
            lastBlock.Size = 0;
            sameBlocks.Add(lastBlock);
        }