initial commit
[rofl0r-KOL.git] / FileGuard / UpdatesUnit.pas
blob962ff5618da618c4a6c9a4b54197e58de2e1189b
2 MakeUpdates
3 ApplyUpdates
4 (C) by Vladimir Kladov, 2002-2004
6 These functions allows to create (MakeUpdates) a command stream
7 on base of two data streams: Stream1 and Stream2,
8 which later can be used to restore (ApplyUpdates) Stream2 on base of
9 the same source Stream1 and created by MakeUpdates command stream.
11 This technique used in my UpdateMaker and Updater applications
12 to provide patches to a set of (big) files which otherwise could
13 require a lot of files to download to get new version. This is great
14 for developers providing sources rather then compiled stuff.
16 History:
18 5-sep-2004
19 first release of this unit: functions MakeUpdates and ApplyUpdates
20 extracted from UpdateMaker / Updater sources, with some sumplifications
21 in order to make possible to use streams rather then files, and to use
22 these two functions separately from UpdateMaker and Updater utilities.
24 7-Sep-2004
25 version 2 of this unit, algorithm improved in comparison to current
26 version of UpdateMaker program and errors fixed (though new verion of
27 MakeUpdates creates command file still compatible with old version
28 of ApplyUpdates been corrected). Now plan to re-use these functions in
29 newer versions of Updater / UpdateMaker.
31 8-Sep-2004
32 version 3 of this unit. Some errors fixed. Parameter added: OnProgress.
35 unit UpdatesUnit;
37 interface
39 uses Windows, KOL;
41 //{$DEFINE WRITE_LEN_CC}
43 type
44 TOnUpdatesProgress = procedure( Percents, TotalSize, CurrentPosition: Integer;
45 var Cancel: Boolean ) of object;
47 function ApplyUpdates( OutStrm, SourceStream, CmdStrm: PStream;
48 OnProgress: TOnUpdatesProgress ): Boolean;
49 function MakeUpdates( DestCommandStream, LastVersion, PrevVersion: PStream;
50 OnProgress: TOnUpdatesProgress ): Boolean;
52 implementation
54 type
55 TCommand = (cmdFWDandCOPY, cmdBCKandCOPY, cmdINSERT, cmdNone);
56 TDwordArray = array[ 0..1000000 ] of DWORD;
57 PDwordArray = ^TDwordArray;
59 function Compare4Bytes( A: PDwordArray; e1, e2: DWORD ): Integer;
60 {$IFDEF PAS_CODE}
61 var X, Y: DWORD;
62 begin
63 X := A[ e1 ];
64 Y := A[ e2 ];
65 asm
66 MOV EAX, [X]
67 MOV EDX, [Y]
68 MOV EAX, [EAX]
69 MOV EDX, [EDX]
70 BSWAP EAX
71 BSWAP EDX
72 SUB EAX, EDX
73 MOV Result, EAX
74 end;
75 end;
76 {$ELSE}
77 asm
78 MOV ECX, [EAX+ECX*4]
79 MOV EAX, [EAX+EDX*4]
80 MOV ECX, [ECX]
81 MOV EAX, [EAX]
82 BSWAP ECX
83 BSWAP EAX
84 SUB EAX, ECX
85 end;
86 {$ENDIF}
88 procedure SwapIndxes( A: PDwordArray; e1, e2: DWORD );
89 {$IFDEF PAS_CODE}
90 var X, Y: Integer;
91 begin
92 X := A[ e1 ];
93 Y := A[ e2 ];
94 A[ e1 ] := Y;
95 A[ e2 ] := X;
96 end;
97 {$ELSE}
98 asm
99 PUSH EBX
100 MOV EBX, [EAX+ECX*4]
101 XCHG EBX, [EAX+EDX*4]
102 MOV [EAX+ECX*4], EBX
103 POP EBX
104 end;
105 {$ENDIF}
108 function MakePattern2(Data: PStream; Old, New: PChar; OldLen, NewLen: Integer;
109 MaxSize: Integer; var Equal: Boolean; OnProgress: TOnUpdatesProgress): Boolean;
110 var HashTable1: PChar;
111 SortTable1, AccTable1: PDwordArray;
113 function BytesEqual( OldPos, NewPos: Integer ): Integer;
114 var I: Integer;
115 begin
116 Result := Min( OldLen - OldPos, NewLen - NewPos );
117 for I := 1 to Result do
118 begin
119 if Old[ OldPos ] <> New[ NewPos ] then
120 begin
121 Result := I - 1;
122 Exit;
123 end;
124 Inc( OldPos );
125 Inc( NewPos );
126 end;
127 end;
129 // çàïèñü îäíîãî áàéòà â âûõîäíîé ïîòîê
130 procedure WriteByte( B: Byte );
131 begin
132 Data.Write( B, 1 );
133 end;
135 // âîçâðàòèò Flag, åñëè âûïîëíåíî óñëîâèå Cond, èíà÷å 0.
136 function MakeFlag( Flag: Byte; Cond: Boolean ): Byte;
137 begin
138 if Cond then Result := Flag
139 else Result := 0;
140 end;
142 // çàïèñü ÷èñëà â ôîðìàòå ñ ïåðåìåííîé äëèíîé. Ñòàðøèé áèò áàéòà îïðåäåëÿåò
143 // äëÿ ÷èòàòåëÿ ÷èñëà, ÷èòàòü ëè ñëåäóþùèé áàéò. 7 ðàçðÿäîâ õðàíÿò î÷åðåäíûå
144 // 7 áèò ÷èñëà. Äëÿ õðàíåíèÿ ÷èñëà îò 0 äî 127 äîñòàòî÷íî 1 áàéòà, îò 128 äî
145 // 16383 (14 áèò åäèíèö) - äâóõ áàéò, è ò.ä.
146 procedure WriteNum( N: DWORD );
147 begin
148 REPEAT
149 WriteByte( N and $7F or MakeFlag( $80, N > 127 ) );
150 N := N shr 7;
151 UNTIL N = 0;
152 end;
154 // Ïðåäâàðèòåëüíûé ïîäñ÷åò ÷èñëà â áàéòàõ
155 function CalcNumLen( N: DWORD ): Integer;
156 begin
157 Result := 1;
158 while N <> 0 do
159 begin
160 N := N shr 7;
161 Inc( Result );
162 end;
163 end;
165 var PrevCmd: TCommand;
167 // ôîðìèðóåò êîìàíäó ñ ïàðàìåòðîì "äëèíà" ïåðåìåííîé ðàçðÿäíîñòè. Ñì. îïèñàíèå
168 // âûøå. Ôîðìàò êîìàíäû çàâèñèò îò PrevCmd.
169 procedure WriteCommand( Cmd: TCommand; Len: Integer );
170 begin
171 Dec( Len );
172 CASE PrevCmd OF
173 cmdNONE:
174 CASE Cmd OF
175 cmdFWDandCOPY:
176 begin
177 WriteByte( Len and $3F or $00 or MakeFlag( $40, Len > 63 ) );
178 if Len > 63 then WriteNum( Len shr 6 );
179 end;
180 cmdINSERT:
181 begin
182 WriteByte( Len and $3F or $80 or MakeFlag( $40, Len > 63 ) );
183 if Len > 63 then WriteNum( Len shr 6 );
184 end;
185 else
186 begin
187 ShowMessage( 'Error! BCKandCOPY at the beginning' );
188 Halt;
189 end;
190 END;
191 cmdINSERT:
192 begin
193 CASE Cmd OF
194 cmdFWDandCOPY:
195 begin
196 WriteByte( Len and $3F or $00 or MakeFlag( $40, Len > 63 ) );
197 if Len > 63 then WriteNum( Len shr 6 );
198 end;
199 cmdBCKandCOPY:
200 begin
201 WriteByte( Len and $3F or $80 or MakeFlag( $40, Len > 63 ) );
202 if Len > 63 then WriteNum( Len shr 6 );
203 end;
204 else
205 begin
206 ShowMessage( 'Error! INSERT after INSERT' );
207 Halt;
208 end;
209 END;
210 end;
211 cmdBCKandCOPY, cmdFWDandCOPY:
212 begin
213 CASE Cmd OF
214 cmdINSERT:
215 begin
216 WriteByte( Len and $3F or $80 or MakeFlag( $40, Len > 63 ) );
217 if Len > 63 then WriteNum( Len shr 6 );
218 end;
219 cmdFWDandCOPY:
220 begin
221 WriteByte( Len and $1F or $00 or MakeFlag( $20, Len > 31 ) );
222 if Len > 31 then WriteNum( Len shr 5 );
223 end;
224 cmdBCKandCOPY:
225 begin
226 WriteByte( Len and $1F or $40 or MakeFlag( $20, Len > 31 ) );
227 if Len > 31 then WriteNum( Len shr 5 );
228 end;
229 END;
230 end;
231 END;
232 PrevCmd := Cmd;
233 end;
235 var OldPos, NewPos: Integer;
237 // Âû÷èñëÿåò ïðåäïîëîæèòåëüíûé ðàçìåð êîìàíäû BCKandCOPY èëè FWDandCOPY.
238 function CalcCmdLen( OldIdx: Integer; LCopy: Integer ): Integer;
239 var //Cmd: TCommand;
240 Off: Integer;
241 begin
242 if OldIdx < OldPos then
243 begin
244 //Cmd := cmdBCKandCOPY;
245 Off := OldPos-OldIdx-1-LCopy;
247 else
248 begin
249 //Cmd := cmdFWDandCOPY;
250 Off := OldIdx - OldPos;
251 end;
252 Dec( LCopy );
253 Result := 1 + CalcNumLen( Off );
254 CASE PrevCmd OF
255 cmdNONE, cmdINSERT:
256 if LCopy > 63 then Inc( Result, CalcNumLen( LCopy shr 6 ) );
257 cmdBCKandCOPY, cmdFWDandCOPY:
258 if LCopy > 31 then Inc( Result, CalcNumLen( LCopy shr 5 ) );
259 END;
260 end;
262 // Âû÷èñëÿåò ïðåäïîëîæèòåëüíûé ðàçìåð êîìàíäû BCKandCOPY èëè FWDandCOPY.
263 function CalcCmdLen2( NewIdx: Integer; LCopy: Integer ): Integer;
264 var //Cmd: TCommand;
265 Off: Integer;
266 begin
267 if NewIdx < NewPos then
268 begin
269 //Cmd := cmdBCKandCOPY;
270 Off := NewPos-NewIdx-1-LCopy;
272 else
273 begin
274 //Cmd := cmdFWDandCOPY;
275 Off := NewIdx - NewPos;
276 end;
277 Dec( LCopy );
278 Result := 1 + CalcNumLen( Off );
279 CASE PrevCmd OF
280 cmdNONE, cmdINSERT:
281 if LCopy > 63 then Inc( Result, CalcNumLen( LCopy shr 6 ) );
282 cmdBCKandCOPY, cmdFWDandCOPY:
283 if LCopy > 31 then Inc( Result, CalcNumLen( LCopy shr 5 ) );
284 END;
285 end;
287 // èçîáðàæåíèå ïðîãðåññà ïðîñìîòðà íîâîé âåðñèè ôàéëà.
288 function ShowProgress_Cancel: Boolean;
289 var Pr: Integer;
290 begin
291 Pr := NewPos * 100 div NewLen;
292 Result := FALSE;
293 if Assigned( OnProgress ) then
294 OnProgress( Pr, NewPos, NewLen, Result );
295 end;
297 // ïîèñê â ñòàðîì ôàéëå ïîñëåäîâàòåëüíîñòè áàéòîâ, ïî âîçìîæíîñòè áîëüøåé äëèíû,
298 // ñîâïàäàþùåé ñ áàéòàìè â íîâîé âåðñèè ôàéëà â ïîçèöèè NewIdx.
299 // Åñëè óäàåòñÿ íàéòè òàêóþ, âîçâðàùàåòñÿ ïîçèöèÿ â ñòàðîé âåðñèè ôàéëà
300 // è LenFound = äëèíà íàéäåííîé ïîñë-ñòè.
301 function SearchSimilar( NewIdx: Integer; var LenFound: Integer ): Integer;
302 function LexicographicCompare( X, Y: DWORD ): Integer;
304 BSWAP EAX
305 BSWAP EDX
306 SUB EAX, EDX
307 end;
308 var I, L: Integer;
309 Hash: DWORD;
310 Ptr: PDWORD;
311 Pos, CmdLenFound, CmdLen: Integer;
312 begin
313 Result := -1;
314 LenFound := 0;
315 CmdLenFound := 0;
316 Hash := PDWORD( @ New[ NewIdx ] )^ and $FFFFFF;
317 I := AccTable1[ Hash and $FFFF ]; // èíäåêñ ïåðâîãî ýëåìåíòà â SortTable1,
318 // óêàçûâàþùåãî íà ïîñëåäîâàòåëüíîñòü â Old[],
319 // íà÷èíàþùóþñÿ ñ áàéòîâ New[ NewIdx ], New[ NewIdx + 1 ]
320 if I = 0 then
321 Exit; // íåò òàêèõ ïîñëåäîâàòåëüíîñòåé èç 2õ áàéò â Old[]
322 for I := I to OldLen-4 do
323 begin
324 Ptr := Pointer( SortTable1[ I ] );
325 if (Ptr^ and $FFFFFF) <> Hash then
326 begin
327 if LexicographicCompare( Ptr^ and $FFFFFF, Hash ) > 0 then
328 begin
329 Exit; // âñ¸, âñå òàêèå ïîñëåäîâàòåëüíîñòè èç ïî êðàéíåé ìåðå òðåõ áàéò êîí÷èëèñü
330 end;
332 else
333 begin
334 Pos := Integer( Ptr ) - Integer( @ Old[ 0 ] );
335 L := BytesEqual( Pos, NewIdx );
336 if L < 3 then Exit;
337 //Assert( L >= 3, '÷òî-òî íå òî' );
338 if L >= 3 then
339 begin
340 // ïðåäîòâðàòèì ïîÿâëåíèå îòðèöàòåëüíîãî ñìåùåíèÿ:
341 if (Pos < OldPos) and (Pos + L >= OldPos - 1) then
342 L := OldPos - 1 - Pos;
343 if L >= 2 then
344 begin
345 CmdLen := CalcCmdLen( Pos, L );
346 if L + CmdLen > LenFound + CmdLenFound then
347 begin
348 CmdLenFound := CmdLen;
349 LenFound := L;
350 Result := Pos;
351 end;
352 end;
353 end;
354 end;
355 end;
356 end;
359 var I, J, L: Integer;
360 Found: Boolean;
361 {$IFDEF WRITE_LEN_CC}
362 CC: DWORD;
363 {$ENDIF}
364 begin
365 Result := FALSE;
366 Equal := FALSE;
368 if (OldLen = NewLen) and (OldLen <> 0) and (CompareMem( Old, New, OldLen )) then
369 begin
370 Equal := TRUE;
371 Exit;
372 end;
374 HashTable1 := AllocMem( 1 shl 21 );
376 GetMem( SortTable1, (OldLen-3) * Sizeof( DWORD ) );
377 AccTable1 := AllocMem( 65536 * Sizeof( DWORD ) );
379 if (HashTable1 = nil) or
380 (SortTable1 = nil) or (AccTable1 = nil) then
381 begin
382 //ShowMessage( 'No memory to process (' + OldFile + '->' + NewFile + ').' );
383 Exit;
384 end;
385 // Èíèöèàëèçèðóåì õýø-òàáëèöû:
386 for I := 0 to OldLen-3 do
387 begin
388 J := PDWORD( @ Old[ I ] )^ and $FFFFFF;
389 Byte( HashTable1[ J shr 3 ] ) := Byte( HashTable1[ J shr 3 ] ) or (1 shl (J and 7));
390 end;
391 // Ñòðîèì òàáëèöó áûñòðîãî ïîèñêà ïîñëåäîâàòåëüíîñòåé:
392 for I := 0 to OldLen-4 do
393 SortTable1[ I ] := DWORD( @ Old[ I ] );
394 SortData( SortTable1, OldLen-3, @ Compare4Bytes, @ SwapIndxes );
395 for I := 0 to OldLen-4 do
396 begin
397 J := SortTable1[ I ];
398 J := PDWORD( J )^ and $FFFF;
399 if (AccTable1[ J ] = 0) or (AccTable1[ J ] > DWORD( I )) then
400 AccTable1[ J ] := I; // AccTable1[ I ] = èíäåêñó ïåðâîãî âõîæäåíèÿ ïîñëåäîâàòåëüíîñòè
401 // íà÷èíàþùåéñÿ ñ 2õ áàéòîâ (J and $FF), (J shr 8)and $FF
402 end;
403 // Ñòðîèì ôàéë ðàçëè÷èé:
404 OldPos := 0;
405 NewPos := 0;
407 {$IFDEF WRITE_LEN_CC}
408 WriteNum( OldLen );
409 // Ïîñ÷èòàåì êîíòðîëüíóþ ñóììó:
410 I := 0;
411 CC := 0;
412 while I < OldLen do
413 begin
414 CC := ((CC shl 1) or (CC shr 31)) xor PDWORD(@Old[ I ])^;
415 Inc( I, 4 );
416 end;
417 Data.Write( CC, 4 );
418 {$ENDIF}
420 PrevCmd := cmdNone;
421 while (NewPos < NewLen) do
422 begin
423 if ShowProgress_Cancel then Exit;
424 L := BytesEqual( OldPos, NewPos );
425 if L >= 2 then
426 begin
427 // ñîâïàäàåò ó÷àñòîê äîñòàòî÷íî õîðîøåé äëèíû, êîïèðóåì åãî:
428 WriteCommand( cmdFWDandCOPY, L );
429 WriteByte( 0 ); // ñìåùåíèå = 0
430 Inc( OldPos, L );
431 Inc( NewPos, L );
433 else
434 begin
435 // èíà÷å èùåì ñëåäóþùèé ó÷àñòîê, êîòîðûé ìîæíî ñêîïèðîâàòü:
436 Found := FALSE;
437 for I := NewPos to NewLen-6 do
438 begin
439 //if (I and $1F) = 0 then
440 begin
441 if ShowProgress_Cancel then Exit;
442 end;
443 J := PDWORD( @ New[ I ] )^ and $FFFFFF;
444 if ( Byte( HashTable1[ J shr 3 ] ) and (1 shl (J and 7))) = 0 then continue;
445 J := SearchSimilar( I, L );
446 if L > 0 then
447 begin
448 Found := TRUE;
449 if I > NewPos then
450 begin
451 WriteCommand( cmdINSERT, I-NewPos );
452 Data.Write( New[ NewPos ], I-NewPos );
453 end;
454 if J < OldPos then begin
455 WriteCommand( cmdBCKandCOPY, L );
456 WriteNum( OldPos-J-1-L );
457 end else begin
458 WriteCommand( cmdFWDandCOPY, L );
459 WriteNum( J-OldPos );
460 end;
461 NewPos := I + L;
462 OldPos := J + L;
463 break;
464 end;
465 end;
466 if not Found then
467 begin
468 for I := NewPos to NewLen-6 do
469 begin
470 //if (I and $1F) = 0 then
471 begin
472 if ShowProgress_Cancel then Exit;
473 end;
474 J := PDWORD( @ New[ I ] )^ and $FFFFFF;
475 if ( Byte( HashTable1[ J shr 3 ] ) and (1 shl (J and 7))) = 0 then continue;
476 J := SearchSimilar( I, L );
477 if L > 0 then
478 begin
479 Found := TRUE;
480 WriteCommand( cmdINSERT, I-NewPos );
481 Data.Write( New[ NewPos ], I-NewPos );
482 if J < OldPos then begin
483 WriteCommand( cmdBCKandCOPY, L );
484 WriteNum( OldPos-J-1-L );
485 end else begin
486 WriteCommand( cmdFWDandCOPY, L );
487 WriteNum( J-OldPos );
488 end;
489 NewPos := I + L;
490 OldPos := J + L;
491 break;
492 end;
493 end;
495 if not Found then
496 begin
497 // Âîîáùå íåò áîëüøå ñîâïàäåíèé, îñòàòîê êîïèðóåì âñòàâêîé.
498 WriteCommand( cmdINSERT, NewLen-NewPos );
499 Data.Write( New[ NewPos ], NewLen-NewPos );
500 NewPos := NewLen;
501 end;
502 end;
503 end;
504 end;
505 Result := TRUE;
506 FINALLY
507 FreeMem( HashTable1 );
508 //FreeMem( HashTable2 );
509 FreeMem( SortTable1 );
510 FreeMem( AccTable1 );
511 //FreeMem( SortTable2 );
512 //FreeMem( AccTable2 );
513 END;
514 end;
517 function ApplyUpdates( OutStrm, SourceStream, CmdStrm: PStream;
518 OnProgress: TOnUpdatesProgress ): Boolean;
519 var DataLen: DWORD;
520 L: Integer;
521 Pos0: DWORD;
522 SrcPos: Integer;
523 Src: PChar;
525 function ShowProgress_Cancel: Boolean;
526 var Pr: Integer;
527 begin
528 Pr := CmdStrm.Position * 100 div CmdStrm.Size;
529 Result := FALSE;
530 if Assigned( OnProgress ) then
531 OnProgress( Pr, CmdStrm.Position, CmdStrm.Size, Result );
532 end;
534 function ReadNum( Shft: Integer = 0 ): Integer;
535 var B: Byte;
536 begin
537 Result := 0;
538 while CmdStrm.Position < CmdStrm.Size do
539 begin
540 CmdStrm.Read( B, 1 );
541 Result := Result or ((B and $7F) shl Shft);
542 Shft := Shft + 7;
543 if (B and $80) = 0 then break;
544 end;
545 end;
547 var I: Integer;
548 B: Byte;
549 Cmd: TCommand;
550 PrevCmd: TCommand;
551 begin
552 Result := FALSE;
553 // íà÷èíàåì ïîñòðîåíèå âûõîäíîãî ôàéëà
554 SrcPos := 0;
555 GetMem( Src, SourceStream.Size );
557 SourceStream.Position := 0;
558 DataLen := CmdStrm.Size;
559 SourceStream.Read( Src^, SourceStream.Size );
560 PrevCmd := cmdNONE;
561 Pos0 := CmdStrm.Position;
562 while CmdStrm.Position < DWORD( Pos0 + DataLen ) do
563 begin
564 if ShowProgress_Cancel then Exit;
565 // ÷èòàåì êîìàíäó:
566 CmdStrm.Read( B, 1 );
567 CASE PrevCmd OF
568 cmdNONE:
569 begin
570 if (B and $80) = 0 then
571 Cmd := cmdFWDandCOPY
572 else
573 Cmd := cmdINSERT;
574 L := B and $3F;
575 if (B and $40) <> 0 then
576 L := L or ReadNum( 6 );
577 end;
578 cmdINSERT:
579 begin
580 if (B and $80) = 0 then
581 Cmd := cmdFWDandCOPY
582 else
583 Cmd := cmdBCKandCOPY;
584 L := B and $3F;
585 if (B and $40) <> 0 then
586 L := L or ReadNum( 6 );
587 end;
588 //cmdFWDandCOPY, cmdBCKandCOPY:
589 else
590 begin
591 if (B and $80) = 0 then
592 begin
593 if (B and $40) = 0 then
594 Cmd := cmdFWDandCOPY
595 else
596 Cmd := cmdBCKandCOPY;
597 L := B and $1F;
598 if (B and $20) <> 0 then
599 L := L or ReadNum( 5 );
601 else
602 begin
603 Cmd := cmdINSERT;
604 L := B and $3F;
605 if (B and $40) <> 0 then
606 L := L or ReadNum( 6 );
607 end;
608 end;
609 END;
610 {$IFDEF DEBUG}
611 CASE PrevCmd OF
612 cmdNONE: Exit;//Assert( Cmd in [cmdFWDandCOPY, cmdINSERT], '!' );
613 cmdINSERT: Exit; //Assert( Cmd in [cmdFWDandCOPY, cmdBCKandCOPY], '!' );
614 else ;
615 END;
616 {$ENDIF}
617 PrevCmd := Cmd;
618 Inc( L );
619 {$IFDEF DEBUG}
620 CASE Cmd OF
621 cmdBCKandCOPY: Protocol.Add( 'BCKandCOPY ' + Int2Str( L ) );
622 cmdFWDandCOPY: Protocol.Add( 'FWDandCOPY ' + Int2Str( L ) );
623 cmdINSERT: Protocol.Add( 'INSERT ' + Int2Str( L ) );
624 END;
625 {$ENDIF}
626 // âûïîëíÿåì êîìàíäó:
627 CASE Cmd OF
628 cmdBCKandCOPY, // ñìåñòèòüñÿ íà N + L + 1 áàéò íàçàä
629 cmdFWDandCOPY: // èëè âïåðåä è ñêîïèðîâàòü L áàéò èç èñõîäíîãî ôàéëà:
630 begin
631 I := ReadNum( 0 );
632 {$IFDEF DEBUG}
633 Protocol.Add( 'OFFSET ' + Int2Str( I ) );
634 {$ENDIF}
635 if Cmd = cmdBCKandCOPY then SrcPos := SrcPos - 1 - I - L
636 else SrcPos := SrcPos + I;
637 {$IFDEF DEBUG}
638 Protocol.Add( 'SRCPOS ' + Int2Str( SrcPos ) );
639 if (SrcPos < 0) or (SrcPos >= InLen) then
640 begin
641 //MsgBox( 'out of bounds', MB_OK );
642 Result := FALSE;
643 Exit;
644 end;
645 {$ENDIF}
646 OutStrm.Write( Src[ SrcPos ], L );
647 {$IFDEF DEBUG}
648 I := Min( 512, L );
649 SetLength( S, I );
650 Move( Src[ SrcPos + L - I ], S[ 1 ], I );
651 Protocol.Add( 'LAST WRITTEN ARE: <' + S + '>' );
652 {$ENDIF}
653 Inc( SrcPos, L );
654 end;
655 cmdINSERT: // âñòàâèòü L áàéòîâ ïðÿìî èç êîìàíäíîãî ôàéëà:
656 begin
657 Stream2Stream( OutStrm, CmdStrm, L );
658 {$IFDEF DEBUG}
659 CmdStrm.Position := CmdStrm.Position - DWORD( L );
660 SetLength( S, L );
661 CmdStrm.Read( S[ 1 ], L );
662 Protocol.Add( 'DATA: ' + Copy( S, 1, 100 ) );
663 {$ENDIF}
664 end;
665 END;
666 end;
667 FINALLY
668 FreeMem( Src );
669 END;
670 Result := TRUE;
671 end;
673 function MakeUpdates( DestCommandStream, LastVersion, PrevVersion: PStream;
674 OnProgress: TOnUpdatesProgress ): Boolean;
675 var Old, New: PChar;
676 Eq: Boolean;
677 begin
678 Result := FALSE;
679 GetMem( Old, PrevVersion.Size );
680 GetMem( New, LastVersion.Size );
682 if LastVersion.Size > 0 then
683 begin
684 LastVersion.Position := 0;
685 LastVersion.Read( New^, LastVersion.Size );
686 end;
687 if PrevVersion.Size > 0 then
688 begin
689 PrevVersion.Position := 0;
690 PrevVersion.Read( Old^, PrevVersion.Size );
691 end;
692 if not MakePattern2( DestCommandStream, Old, New, PrevVersion.Size,
693 LastVersion.Size, max( PrevVersion.Size, LastVersion.Size ), Eq,
694 OnProgress ) then Exit;
695 if Eq then Exit;
696 Result := TRUE;
697 FINALLY
698 if Old <> nil then FreeMem( Old );
699 if New <> nil then FreeMem( New );
700 END;
701 end;
703 end.