home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 March / CMCD0304.ISO / Software / Freeware / Programare / nullsoft / nsis20.exe / Contrib / VPatch / Source / GenPat / PatchGenerator.pas < prev    next >
Pascal/Delphi Source File  |  2003-12-27  |  19KB  |  616 lines

  1. unit PatchGenerator;
  2.  
  3. {
  4.   VPatch 2 - Patch Generator
  5.   ==========================
  6.  
  7.   (c) 2002-2003 Van de Sande Productions
  8.  
  9.   This unit contains the 'core' functionality of VPatch. TPatchGenerator can
  10.   load/create/save .PAT files and supports CreatePatch(Old, New) to generate
  11.   new patches. The only configurable parameter is StartBlockSize.
  12.   Though I cleaned up the code a little bit, there is very little documentation.
  13.   That's why I will briefly explain the general workings of the current VPatch
  14.   algoritm.
  15.   There is a source file, which is divided into blocks of BlockSize. Block 1
  16.   spans bytes 0-15, block 2 16-31, etc if blocksize = 16. For every block, a
  17.   checksum is calculated and then the block is inserted into a binary search
  18.   tree, which is sorted on this checksum.
  19.   Now, the target file (new version) is traversed linearly. For a block at a
  20.   certain position, the checksum is calculated. Then, a lookup is performed in
  21.   the binary search tree to find all blocks in the source file which match this
  22.   checksum. For every occurence, it is checked how many consecutive bytes match
  23.   with this block (note: since the checksum is not unique, this can be 0 as well,
  24.   but since all occurences are checked, the largest match is selected). Note also
  25.   that this match length is not limited to BlockSize but can be larger as well;
  26.   everything beyond the block end is checked as well (the block is merely used
  27.   as a starting position for checking the match).
  28.   For those biggest block matches between source/target files, a copy instruction
  29.   will be generated in the patch file. For 'inbetween' or unmatchable blocks, the
  30.   data of the new file is placed in the patch file. This involves some
  31.   housekeeping, which is what most of the other code does.
  32.  
  33.   What's new
  34.   ----------
  35.   2.1   20031219    Koen            Added error checking to CreatePatch, returns
  36.                                     negative numbers when there are errors.
  37.   2.0   20030811    Koen            Initial documentation
  38. }
  39.  
  40. interface
  41.  
  42. uses
  43.   Classes,
  44.   Sysutils,
  45.   TreeCode,
  46.   VDSP_CRC;
  47.  
  48. type
  49.   TStatusNotifyEvent = procedure(S: String; Current, Total, Savings: Integer) of object;
  50.   TDebugNotifyEvent = procedure(S: String) of object;
  51.   PDataBlock = ^TDataBlock;
  52.   TDataBlock = record
  53.     SourceOffset: Integer;
  54.     TargetOffset: Integer;
  55.     Size: Integer;
  56.     Next: PDataBlock;
  57.   end;
  58.   //internal structure for FindBlock
  59.   TBlock = record
  60.     Offset: Integer;
  61.     Size: Integer;
  62.   end;
  63.   TPatchGenerator = class
  64.   private
  65.     noPat: Integer;
  66.     PRay: Array of TDataBlock;
  67.     NRay: Array of TDataBlock;
  68.  
  69.     FPatchData: TMemoryStream;
  70.     FStartBlockSize: Integer;  //initial block size
  71.     FBlockDivider: Integer;    //... block size is divided by this
  72.     FMinimumBlockSize: Integer;//until this minimum is reached
  73.     FStepSize: Integer;
  74.  
  75.     //input: ASubBlock, which is a pointer to the start of the block to look
  76.     //for in ABlock. The entire ABlock is searched. The function returns the
  77.     //offset of the block, when it is found. The ASize parameter contains the
  78.     //size of this block
  79.     procedure ShowDebug(S: String);
  80.  
  81.     function FindBlock(ASubBlock, ABlock, ABlockTree: Pointer;
  82.       var ASubBlockStart: Integer; ASubBlockSize, ABlockSize,
  83.       AMatchSize, ABlockTreeNodeCount: Integer; var ASize: Integer): Integer;
  84.  
  85.     procedure FindBlockSize(ASubBlock, ABlock: Pointer; ASubBlockSize,
  86.       ABlockSize: Integer; var ASubStart, AStart, AFoundSize: Integer);
  87.     function WritePatchToStream(Target: Pointer; SourceCRC,
  88.       TargetCRC: Integer): Integer;
  89.  
  90.     procedure RemoveExistingPatch(ACRC: Integer);
  91.   public
  92.     constructor Create;
  93.     destructor Destroy; override;
  94.     procedure Clear;
  95.     function CreatePatch(SourceFileName, TargetFileName: String): Integer;
  96.     property StartBlockSize: Integer read FStartBlockSize write FStartBlockSize;
  97.     property BlockDivider: Integer read FBlockDivider write FBlockDivider;
  98.     property MinimumBlockSize: Integer read FMinimumBlockSize write FMinimumBlockSize;
  99.     property StepSize: Integer read FStepSize write FStepSize;
  100.     function Size: Integer;
  101.     procedure WriteToFile(AFileName: String);
  102.     procedure WriteToStream(AStream: TStream);
  103.     procedure LoadFromFile(AFileName: String);
  104.   end;
  105.  
  106.  
  107. const
  108.   BUF_BLOCK_SIZE = 4096;
  109.   INIT_BLOCK_COUNT=10000;
  110.  
  111. var
  112.   DebugEvent: TDebugNotifyEvent = nil;
  113.  
  114. implementation
  115.  
  116. { TPatchGenerator }
  117.  
  118. procedure TPatchGenerator.Clear;
  119. begin
  120.   FPatchData.Clear;
  121. end;
  122.  
  123. constructor TPatchGenerator.Create;
  124. begin
  125.   inherited;
  126.   FPatchData:=TMemoryStream.Create;
  127. end;
  128.  
  129. function TPatchGenerator.CreatePatch(SourceFileName,
  130.   TargetFileName: String): Integer;
  131. var
  132.   fsSource, fsTarget: TFileStream;
  133.   fm: TMemoryStream;
  134.   Source, Target: Pointer;
  135.   SourceSize, TargetSize: Integer;
  136.   SourceCRC, TargetCRC: Integer;
  137.   SourceTree: Pointer;
  138.   SourceTreeNodeCount: Cardinal;
  139.   cBlockSize: Integer;
  140.  
  141.   o,i,lastO: Integer;
  142.   Start,Siz,BetweenSiz: Integer;
  143.   retTO: Integer;
  144.   noN: Integer;
  145. begin
  146.   fsSource:=TFileStream.Create(SourceFileName,fmOpenRead+fmShareDenyNone);
  147.   fsTarget:=TFileStream.Create(TargetFileName,fmOpenRead+fmShareDenyNone);
  148.   fm:=TMemoryStream.Create;
  149.  
  150.   SetLength(PRay,INIT_BLOCK_COUNT);
  151.   SetLength(NRay,INIT_BLOCK_COUNT);
  152.  
  153.   //Load those files into memory!
  154.   SourceSize:=fsSource.Size;
  155.   try
  156.     GetMem(Source,SourceSize);
  157.   except
  158.     on EOutOfMemory do begin
  159.       Result:=-2;           // not enough memory for source file
  160.       Exit;
  161.     end;
  162.   end;
  163.   fm.CopyFrom(fsSource,SourceSize);
  164.   Move(fm.Memory^,Source^,SourceSize);
  165.   SourceCRC:=FileCRC(fsSource);
  166.   fsSource.Free;
  167.  
  168.   fm.Clear;
  169.   TargetSize:=fsTarget.Size;
  170.   try
  171.     GetMem(Target,TargetSize);
  172.   except
  173.     on EOutOfMemory do begin
  174.       FreeMem(Source,SourceSize);
  175.       Result:=-3;           // not enough memory for target file
  176.       Exit;
  177.     end;
  178.   end;
  179.   fm.CopyFrom(fsTarget,TargetSize);
  180.   Move(fm.Memory^,Target^,TargetSize);
  181.   TargetCRC:=FileCRC(fsTarget);
  182.   fsTarget.Free;
  183.   fm.Free;
  184.  
  185.   if(SourceCRC = TargetCRC) then begin
  186.     FreeMem(Source,SourceSize);
  187.     FreeMem(Target,TargetSize);
  188.     Result:=-1;
  189.     Exit;
  190.   end;
  191.  
  192.   PRay[0].TargetOffset:=0;
  193.   PRay[0].SourceOffset:=0;
  194.   PRay[0].Size:=0;
  195.   noPat:=1;
  196.  
  197.   //termination block
  198.  
  199.   PRay[noPat].SourceOffset:=0;
  200.   PRay[noPat].TargetOffset:=TargetSize;
  201.   PRay[noPat].Size:=0;
  202.  
  203.   //we only have one pass in this mode
  204. //  StartBlockSize:=16;
  205.   MinimumBlockSize:=StartBlockSize;
  206.   StepSize:=1;
  207.   BlockDivider:=2;
  208.  
  209.   //because we are dividing first inside.
  210.   cBlockSize:=StartBlockSize*BlockDivider;
  211.  
  212.   SourceTree:=nil;
  213.   SourceTreeNodeCount:=BuildTree(Source,SourceTree,SourceSize,cBlockSize div BlockDivider);
  214.   SortTree(SourceTree,SourceTreeNodeCount);
  215.  
  216.   //now, we must do the above again - with a smaller block size
  217.   repeat
  218.     if cBlockSize<=MinimumBlockSize then break;
  219.     cBlockSize:=cBlockSize div BlockDivider;
  220.     noN:=0;
  221.     for i:=1 to noPat do begin
  222.       //calculate location of the inbetween parts
  223.       Start:=PRay[i-1].TargetOffset+PRay[i-1].Size;
  224.       BetweenSiz:=PRay[i].TargetOffset-Start;
  225.  
  226.       NRay[noN].SourceOffset:=PRay[i-1].SourceOffset;
  227.       NRay[noN].TargetOffset:=PRay[i-1].TargetOffset;
  228.       NRay[noN].Size:=PRay[i-1].Size;
  229.       Inc(noN);
  230.       if BetweenSiz>0 then begin
  231.  
  232.         o:=Start;
  233.         repeat
  234.           //ShowDebug(PChar('DoFind '+IntToStr(o)));
  235.           LastO:=o;
  236.           retTO:=FindBlock(Target,Source,SourceTree,o,TargetSize,SourceSize,cBlockSize,SourceTreeNodeCount,Siz);
  237.           if not (Siz=0) then
  238.             ShowDebug(IntToStr(LastO)+'  ->  Source='+IntToStr(retTO)+' Target='+IntToStr(o)+' Size='+IntToStr(Siz));
  239.  
  240.           if Siz=0 then begin
  241.             o:=LastO+StepSize;
  242.           end else begin
  243.             //we have found a block, let's add it!
  244.             NRay[noN].SourceOffset:=retTO;
  245.             NRay[noN].TargetOffset:=o;
  246.             NRay[noN].Size:=Siz;
  247.             Inc(noN);
  248.             if noN>=Length(NRay) then begin
  249.               SetLength(NRay,Length(NRay)*2);
  250.               SetLength(PRay,Length(PRay)*2);
  251.             end;
  252.             Inc(o,Siz);
  253.           end;
  254.  
  255.           //check to see if we're not inside another one.
  256.           Siz:=NRay[noN].TargetOffset-NRay[noN-1].TargetOffset-NRay[noN-1].Size;
  257.           If Siz<0 then begin  //that's impossible! (overlapping should be eliminated)
  258.             NRay[noN].TargetOffset:=NRay[noN].TargetOffset-Siz;
  259.             NRay[noN].Size:=NRay[noN].Size+Siz;
  260.             NRay[noN].SourceOffset:=NRay[noN].SourceOffset-Siz;
  261.           end;
  262.         until o>Start+BetweenSiz;
  263.  
  264.       end;
  265.     end;
  266.     //I think the last termination block isn't copied: do so now.
  267.     NRay[noN].SourceOffset:=PRay[noPat].SourceOffset;
  268.     NRay[noN].TargetOffset:=PRay[noPat].TargetOffset;
  269.     NRay[noN].Size:=PRay[noPat].Size;
  270.     //copy back into PRay
  271.     for i:=0 to noN do begin
  272.       PRay[i].SourceOffset:=NRay[i].SourceOffset;
  273.       PRay[i].TargetOffset:=NRay[i].TargetOffset;
  274.       PRay[i].Size:=NRay[i].Size;
  275.     end;
  276.     noPat:=noN;
  277.   until false;
  278.  
  279.   //writing is next!
  280.   ShowDebug('Writing patch');
  281.  
  282.   Result:=WritePatchToStream(Target, SourceCRC, TargetCRC);
  283.  
  284.   ClearTree(SourceTree,SourceTreeNodeCount);
  285.   FreeMem(Source,SourceSize);
  286.   FreeMem(Target,TargetSize);
  287.   ShowDebug('Done');
  288. end;
  289.  
  290. destructor TPatchGenerator.Destroy;
  291. begin
  292.   FPatchData.Free;
  293.   inherited;
  294. end;
  295.  
  296. function TPatchGenerator.FindBlock(ASubBlock, ABlock, ABlockTree: Pointer; var ASubBlockStart: Integer;
  297.   ASubBlockSize, ABlockSize, AMatchSize, ABlockTreeNodeCount: Integer; var ASize: Integer): Integer;
  298. //This procedure locates location of a block in the target file
  299. //Then, it calls FindBlockSize to determine size of this block
  300. var
  301.   MatchSize, FoundSize: Integer;
  302.   q,r,i: Integer;
  303.   FoundCache_SubOffset, FoundCache_Size, FoundCache_Offset: Integer;
  304.   Checksum: Cardinal;
  305.   PFound: PTreeNode;
  306.   FoundCount: Integer;
  307. begin
  308.   //if we find nothing...
  309.  
  310.   FoundCache_Size:=0;
  311.   FoundCache_Offset:=0;
  312.   FoundCache_SubOffset:=ASubBlockStart;
  313.  
  314.   FindBlock:=0;
  315.   ASize:=0;
  316.  
  317.   MatchSize:=AMatchSize;
  318.  
  319.   //we can only find MatchSize sized blocks in the tree!
  320.   if MatchSize > ASubBlockSize - ASubBlockStart then Exit;
  321.   if MatchSize = 0 then Exit;
  322.  
  323.   Checksum:=0;
  324.   calculateChecksum(ASubBlock,ASubBlockStart,MatchSize,Checksum);
  325.   PFound:=TreeFind(Checksum,ABlockTree,ABlockTreeNodeCount,FoundCount);
  326.  
  327.   for i:=0 to Pred(FoundCount) do begin
  328.     FoundSize:=MatchSize;
  329.     //q = offset in Block
  330.     q:=PFound^.Offset;
  331.     //r = offset in SubBlock
  332.     r:=ASubBlockStart;
  333.     FindBlockSize(ASubBlock, ABlock, ASubBlockSize, ABlockSize, r, q, FoundSize);
  334.     if FoundSize>FoundCache_Size then begin
  335.       FoundCache_SubOffset:=r;
  336.       FoundCache_Offset:=q;
  337.       FoundCache_Size:=FoundSize;
  338.     end;
  339.     ShowDebug('   Block Size   Start='+IntToStr(r)+' tarStart='+IntToStr(q)+' Size='+IntToStr(FoundSize));
  340.     PFound:=PTreeNode(Integer(PFound)+SizeOf(TTreeNode));
  341.   end;
  342.  
  343.   FindBlock:=FoundCache_Offset;
  344.   ASize:=FoundCache_Size;
  345.   ASubBlockStart:=FoundCache_SubOffset;
  346. end;
  347.  
  348. procedure TPatchGenerator.FindBlockSize(ASubBlock, ABlock: Pointer; ASubBlockSize, ABlockSize: Integer; var ASubStart,AStart,AFoundSize: Integer);
  349. var
  350.   FoundSize: Integer;
  351.   a,c,d,i: Integer;
  352.   f1p,f2p,f1Size,f2Size: Integer;
  353.   beforeSize: Integer;
  354.   CurBufSize: Integer;
  355. begin
  356.   //OK, now let's go...
  357.   //Trace after -> how long does this go on?
  358.   f1p:=Integer(ASubBlock)+ASubStart;
  359.   f2p:=Integer(ABlock)+AStart;
  360.   f1Size:=ASubBlockSize-ASubStart;
  361.   f2Size:=ABlockSize-AStart;
  362.   FoundSize:=0;
  363.   CurBufSize := BUF_BLOCK_SIZE; //size of the block we're checking
  364.   while not (CurBufSize = 0) do begin
  365.     //we need equal bytes from both... so if one of them EOF, it's the end.
  366.     if FoundSize+CurBufSize>f1Size then CurBufSize:=f1Size - FoundSize;
  367.     if FoundSize+CurBufSize>f2Size then CurBufSize:=f2Size - FoundSize;
  368.     if CompareMem(Pointer(f1p),Pointer(f2p),CurBufSize) then begin
  369.       Inc(FoundSize,CurBufSize);
  370.       Inc(f1p,CurBufSize);
  371.       Inc(f2p,CurBufSize);
  372.     end
  373.     else begin
  374.       CurBufSize:=CurBufSize div 2;
  375.     end;
  376.   end;
  377.  
  378.   if FoundSize = 0 then begin AFoundSize:=0; Exit; end;
  379.  
  380.   //Trace before -> how much bytes are still the same before the block?
  381.   //First, read 1 block from source and 1 block from target, start from back to compare how much they differ
  382.   //just take BUF_BLOCK_SIZE as maximum size for the block before - that's surely
  383.   //big enough!
  384.   beforeSize:=BUF_BLOCK_SIZE;
  385.   a:=ASubStart-beforeSize;
  386.   if a<0 then begin
  387.     a:=0;
  388.     beforeSize:=ASubStart;
  389.   end;
  390.   //b is the current before block size
  391.   c:=AStart-beforeSize;
  392.   if c<0 then begin
  393.     c:=0;
  394.     beforeSize:=AStart;
  395.     a:=ASubStart-beforeSize;
  396.   end;
  397.   //a=Offset in source
  398.   //b=Size of beforeblock
  399.   //c=offset in target
  400.  
  401.   d:=0;
  402.   for i:=beforeSize-1 downto 0 do begin
  403.     //if not (f1^[a+i]=f2^[c+i]) then begin
  404.     if not (PByte(Integer(ASubBlock)+a+i)^=PByte(Integer(ABlock)+c+i)^) then begin
  405.       //d=how many bytes before are the same?
  406.       Break;
  407.     end;
  408.     Inc(d);
  409.   end;
  410.   Inc(FoundSize,d);
  411.   Dec(ASubStart,d);
  412.   Dec(AStart,d);
  413.   AFoundSize:=FoundSize;
  414. end;
  415.  
  416. function TPatchGenerator.Size: Integer;
  417. begin
  418.   Result:=FPatchData.Size;
  419. end;
  420.  
  421. procedure TPatchGenerator.ShowDebug(S: String);
  422. begin
  423.   if Assigned(DebugEvent) then DebugEvent(S);
  424. end;
  425.  
  426. function TPatchGenerator.WritePatchToStream(Target: Pointer; SourceCRC, TargetCRC: Integer): Integer;
  427. var
  428.   HeadID: Array[0..3] of Char;
  429.   NoBlocks, NoBlocksOffset, BodySize, BodySizeOffset: Integer;
  430.   b: Byte;
  431.   w: Word;
  432.   i, j: Integer;
  433.   l: LongWord;
  434.   Start, Siz: Integer;
  435.   PTarget: Pointer;
  436. begin
  437.   RemoveExistingPatch(SourceCRC);
  438.   with FPatchData do begin
  439.     Seek(0,soFromEnd);
  440.     if Size = 0 then begin
  441.       HeadID:='VPAT';
  442.       Write(HeadID,SizeOf(HeadID));
  443.       l:=0;
  444.       Write(l,SizeOf(l)); //NoFiles
  445.     end;
  446.     l:=0;
  447.     NoBlocksOffset:=Position;
  448.     Write(l,SizeOf(l)); //should become NoBlocks later
  449.     Write(SourceCRC,SizeOf(SourceCRC)); //source CRC
  450.     Write(TargetCRC,SizeOf(TargetCRC)); //target CRC
  451.     BodySizeOffset:=Position;
  452.     Write(l,SizeOf(l)); //should become BodySize (of this patch)
  453.  
  454.     NoBlocks:=0;
  455.     BodySize:=0;
  456.     //Write the patch...
  457.     for i:=0 to noPat - 1 do begin
  458.       //write char 1 - integer/copysource
  459.       //write char 2 - long/copysource
  460.       //write char 5 - integer/insidepatch
  461.       //write char 6 - long/insidepatch
  462.  
  463.       Start:=PRay[i].TargetOffset+PRay[i].Size;
  464.       Siz:= PRay[i+1].TargetOffset-Start;
  465.  
  466.       If Siz<0 then begin  //that's impossible! (overlapping should be eliminated)
  467.         PRay[i+1].TargetOffset:=PRay[i+1].TargetOffset-Siz;
  468.         PRay[i+1].Size:=PRay[i+1].Size+Siz;
  469.         PRay[i+1].SourceOffset:=PRay[i+1].SourceOffset-Siz;
  470.         Siz:=0;
  471.       end;
  472.  
  473.       if not (PRay[i].Size=0) then begin
  474.         if (PRay[i].Size<=255) then begin
  475.           b:=1;
  476.           Write(b,SizeOf(b));
  477.           b:=PRay[i].Size;
  478.           Write(b,SizeOf(b));
  479.           Inc(BodySize,2);
  480.         end else if PRay[i].Size<=65535 then begin
  481.           b:=2;
  482.           Write(b,SizeOf(b));
  483.           w:=PRay[i].Size;
  484.           Write(w,SizeOf(w));
  485.           Inc(BodySize,3);
  486.         end else begin
  487.           b:=3;
  488.           Write(b,SizeOf(b));
  489.           Write(PRay[i].Size,SizeOf(Integer));
  490.           Inc(BodySize,5);
  491.         end;
  492.         Write(PRay[i].SourceOffset,SizeOf(Integer));
  493.         Inc(BodySize,SizeOf(Integer));
  494.         Inc(NoBlocks);
  495.       end;
  496.       //Now write the writeblock
  497.       If Not (Siz = 0) Then begin
  498.         if Siz<=255 then begin
  499.           b:=5;
  500.           Write(b,SizeOf(b));
  501.           b:=Siz;
  502.           Write(b,SizeOf(b));
  503.           Inc(BodySize,2);
  504.         end else if Siz<=65535 then begin
  505.           b:=6;
  506.           Write(b,1);
  507.           w:=Siz;
  508.           Write(w,2);
  509.           Inc(BodySize,3);
  510.         end else begin
  511.           b:=7;
  512.           Write(b,1);
  513.           Write(Siz,4);
  514.           Inc(BodySize,5);
  515.         end;
  516.         PTarget:=Pointer(Integer(Target)+Start);
  517.         j:=Start;
  518.         repeat
  519.           //read
  520.           if (j+4096>Start+Siz) then begin
  521.             Write(PTarget^,Start+Siz-j);
  522.             break;
  523.           end;
  524.           Write(PTarget^,4096);
  525.           Inc(j,4096);
  526.           PTarget:=Pointer(Integer(PTarget)+4096);
  527.         until false;
  528.         Inc(BodySize,Siz);
  529.         Inc(NoBlocks);
  530.       end;
  531.     end;
  532.     Seek(NoBlocksOffset,soFromBeginning);
  533.     Write(NoBlocks,SizeOf(NoBlocks));
  534.     Seek(BodySizeOffset,soFromBeginning);
  535.     Write(BodySize,SizeOf(BodySize));
  536.     ShowDebug('Patch body size: '+IntToStr(BodySize));
  537.     ShowDebug('Total patch size:'+IntToStr(Size));
  538.     //now increase file count
  539.     Seek(4,soFromBeginning);
  540.     Read(i,SizeOf(i));
  541.     Inc(i);
  542.     Seek(4,soFromBeginning);
  543.     Write(i,SizeOf(i));
  544.     Seek(0,soFromEnd);
  545.     Result:=BodySize;
  546.   end;
  547. end;
  548.  
  549. procedure TPatchGenerator.WriteToFile(AFileName: String);
  550. var
  551.   fs: TFileStream;
  552. begin
  553.   fs:=TFileStream.Create(AFileName,fmCreate);
  554.   FPatchData.Seek(0,soFromBeginning);
  555.   fs.CopyFrom(FPatchData,FPatchData.Size);
  556.   fs.Free;
  557. end;
  558.  
  559. procedure TPatchGenerator.LoadFromFile(AFileName: String);
  560. var
  561.   fs: TFileStream;
  562. begin
  563.   fs:=TFileStream.Create(AFileName,fmOpenRead);
  564.   FPatchData.Clear;
  565.   FPatchData.CopyFrom(fs,fs.Size);
  566.   fs.Free;
  567. end;
  568.  
  569. procedure TPatchGenerator.RemoveExistingPatch(ACRC: Integer);
  570. var
  571.   HeadID: Array[0..3] of Char;
  572.   NoFiles, i, j, SourceCRC, MSize: Integer;
  573.   StartPos: Integer;
  574.   ms: TMemoryStream;
  575. begin
  576.   with FPatchData do begin
  577.     if Size = 0 then Exit;
  578.     Seek(0,soFromBeginning);
  579.     Read(HeadID,SizeOf(HeadID));
  580.     if HeadID = 'VPAT' then begin
  581.       Read(NoFiles,SizeOf(NoFiles));
  582.       for i:=0 to Pred(NoFiles) do begin
  583.         if Position >= Size then Break;
  584.         StartPos:=Position;
  585.         Read(j,SizeOf(j));                 //NoBlocks
  586.         Read(SourceCRC,SizeOf(SourceCRC)); //SourceCRC
  587.         Read(j,SizeOf(j));                 //TargetCRC
  588.         Read(j,SizeOf(j));                 //BodySize
  589.         Seek(j,soFromCurrent);
  590.         if SourceCRC = ACRC then begin
  591.           ms:=TMemoryStream.Create;
  592.           MSize:=Size-Position;
  593.           if MSize > 0 then ms.CopyFrom(FPatchData,MSize);
  594.           ms.Seek(0, soFromBeginning);
  595.           FPatchData.Seek(StartPos,soFromBeginning);
  596.           FPatchData.SetSize(Size - j - SizeOf(Integer) * 4);
  597.           FPatchData.CopyFrom(ms,ms.Size);
  598.           ms.Free;
  599.           Dec(NoFiles);
  600.           Seek(4,soFromBeginning);
  601.           Write(NoFiles,SizeOf(NoFiles));
  602.           Break;
  603.         end;
  604.       end;
  605.     end;
  606.   end;
  607. end;
  608.  
  609. procedure TPatchGenerator.WriteToStream(AStream: TStream);
  610. begin
  611.   FPatchData.Seek(0,soFromBeginning);
  612.   AStream.CopyFrom(FPatchData,FPatchData.Size);
  613. end;
  614.  
  615. end.
  616.