public void FormatPatch(PatchFileInformation fileInfo, IList<SameBlock> sameBlocks, Stream target, Stream output) { using (var bw = new BinaryWriter(output)) { #region Patch File Preface bw.Write((UInt32)0x54415056); // Write magic header UInt32 fileCount = 0x80000000; // MD5 mode. Top byte is for extensions. fileCount += 1; // We're only packing one file in. bw.Write((UInt32)fileCount); // Go ahead and put the file count in. #endregion #region Current File Preface long bodySize = 0; long noBlocks = 0; long noBlocksOffset = output.Position; bw.Write((UInt32)noBlocks); bw.Write(fileInfo.SourceChecksum, 0, 16); bw.Write(fileInfo.TargetChecksum, 0, 16); long bodySizeOffset = output.Position; bw.Write((UInt32)bodySize); #endregion byte[] copyBuffer = new byte[COPY_BUF_SIZE]; for(int iter = 0; iter < sameBlocks.Count; iter++) { SameBlock current = sameBlocks[iter]; // store current block if(current.Size > 0) { // copy block from sourceFile if(current.Size < 256) { bw.Write((byte)1); bw.Write((byte)current.Size); bodySize += 2; } else if(current.Size < 65536) { bw.Write((byte)2); bw.Write((UInt16)current.Size); bodySize += 3; } else { bw.Write((byte)3); bw.Write((UInt32)current.Size); bodySize += 5; } bw.Write((UInt32)current.SourceOffset); bodySize += 4; noBlocks++; } iter++; if(iter >= sameBlocks.Count) break; SameBlock next = sameBlocks[iter]; iter--; // calculate area inbetween this block and the next long notFoundStart = current.TargetOffset+current.Size; if(notFoundStart > next.TargetOffset) { throw new InvalidOperationException("makeBinaryPatch input problem: there was overlap"); } long notFoundSize = next.TargetOffset - notFoundStart; if(notFoundSize > 0) { // we need to include this area in the patch directly if(notFoundSize < 256) { bw.Write((byte)5); bw.Write((byte)notFoundSize); bodySize += 2; } else if(notFoundSize < 65536) { bw.Write((byte)6); bw.Write((UInt16)notFoundSize); bodySize += 3; } else { bw.Write((byte)7); bw.Write((UInt32)notFoundSize); bodySize += 5; } // copy from target... target.Seek(notFoundStart, SeekOrigin.Begin); for(long i = 0; i < notFoundSize; i += COPY_BUF_SIZE) { long j = notFoundSize - i; if(j > COPY_BUF_SIZE) j = COPY_BUF_SIZE; target.Read(copyBuffer, 0, (int)j); output.Write(copyBuffer, 0, (int)j); } bodySize += notFoundSize; noBlocks++; } } // we are done, now add just one extra block with the target file time bw.Write((byte)255); long time = fileInfo.TargetDateTime.ToBinary(); bw.Write((Int64)time); noBlocks++; bodySize += 9; long curPos = output.Position; output.Seek(noBlocksOffset, SeekOrigin.Begin); bw.Write((UInt32)noBlocks); output.Seek(bodySizeOffset, SeekOrigin.Begin); bw.Write((UInt32)bodySize); output.Seek(curPos, SeekOrigin.Begin); } }
public void Clear() { mPatFileInfo = new PatchFileInformation(); }
/// <summary> /// Takes a stream for an old version of a file, preparing a /// block-by-block patch to transform it in to the new version of a /// file. The given formatter is used to output the prepared data, /// which is finally written to the output stream. /// </summary> /// <param name="oldVersionFile"> /// Seekable and readable stream for the old version of data. /// </param> /// <param name="newVersionFile"> /// Seekable and readable stream for the new version of data. /// </param> /// <param name="formatter"> /// Formatter to transform prepared patch data to a useful output. /// </param> /// <param name="output"></param> public void CreatePatch(Stream oldVersionFile, Stream newVersionFile, IPatchFormatter formatter, IPatchProgress prog, Stream output) { if (oldVersionFile == null) throw new NullReferenceException(); if (newVersionFile == null) throw new NullReferenceException(); if (output == null) throw new NullReferenceException(); if (oldVersionFile.CanSeek == false) throw new NotSupportedException(); if (newVersionFile.CanSeek == false) throw new NotSupportedException(); oldVersionFile.Seek(0, SeekOrigin.Begin); newVersionFile.Seek(0, SeekOrigin.Begin); var fileInfo = new PatchFileInformation(); fileInfo.SourceChecksum = MD5.Check(oldVersionFile); oldVersionFile.Seek(0, SeekOrigin.Begin); fileInfo.TargetChecksum = MD5.Check(newVersionFile); newVersionFile.Seek(0, SeekOrigin.Begin); var patchGenerator = new PatchGenerator(oldVersionFile, oldVersionFile.Length, newVersionFile, newVersionFile.Length); patchGenerator.BlockSize = BlockSize; patchGenerator.MaximumMatches = MaximumMatches; List<SameBlock> sameBlocks = new List<SameBlock>(); patchGenerator.Execute(sameBlocks, prog); if (formatter != null) formatter.FormatPatch(fileInfo, sameBlocks, newVersionFile, output); sameBlocks.Clear(); }