VPatch 2.0 final
git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@2798 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
parent
2449fbc6bd
commit
6dd280b24f
22 changed files with 3566 additions and 14 deletions
125
Contrib/VPatch/Source/GenPat/GenPat2.dpr
Normal file
125
Contrib/VPatch/Source/GenPat/GenPat2.dpr
Normal file
|
@ -0,0 +1,125 @@
|
|||
program GenPat2;
|
||||
|
||||
{
|
||||
VPatch 2 - Patch Generator
|
||||
===============================
|
||||
|
||||
(c) 2001-2003 Van de Sande Productions
|
||||
|
||||
This is the main program unit for the commandline version. It implements
|
||||
commandline options (like /b=) and displays help if no options are given.
|
||||
|
||||
What's new
|
||||
----------
|
||||
2.0 20030811 Koen Initial documentation
|
||||
}
|
||||
|
||||
{$APPTYPE CONSOLE}
|
||||
uses
|
||||
PatchGenerator in 'PatchGenerator.pas',
|
||||
VDSP_CRC in 'VDSP_CRC.pas',
|
||||
Sysutils,
|
||||
TreeCode in 'TreeCode.pas';
|
||||
|
||||
type
|
||||
TEventHandler = class
|
||||
procedure PrintDebug(S: String);
|
||||
end;
|
||||
|
||||
procedure TEventhandler.PrintDebug(S: String);
|
||||
begin
|
||||
WriteLn(S);
|
||||
end;
|
||||
|
||||
{$DEFINE READCONFIG} //try to read genpat.ini?
|
||||
{.$DEFINE AUTOWAIT} //have /wait command line switch on by default?
|
||||
//useful when debugging
|
||||
|
||||
var
|
||||
Config: TextFile;
|
||||
T1,T2: TDateTime;
|
||||
d: Integer;
|
||||
S,Key: String;
|
||||
ShowDebug: Boolean;
|
||||
PG: TPatchGenerator;
|
||||
EV: TEventHandler;
|
||||
begin
|
||||
EV:=TEventHandler.Create;
|
||||
PG:=TPatchGenerator.Create;
|
||||
PG.StartBlockSize:=64;
|
||||
|
||||
WriteLn('GenPat v2.0 final');
|
||||
WriteLn('=================');
|
||||
WriteLn;
|
||||
WriteLn('(c) 2001-2003 Van de Sande Productions');
|
||||
WriteLn('Website: http://www.tibed.net/vpatch');
|
||||
WriteLn('E-mail: koen@tibed.net');
|
||||
WriteLn;
|
||||
ShowDebug:=FindCmdLineSwitch('debug',['/'],True);
|
||||
if ShowDebug then
|
||||
DebugEvent:=EV.PrintDebug;
|
||||
|
||||
{$IFDEF READCONFIG}
|
||||
if FileExists('genpat.ini') then begin
|
||||
AssignFile(Config,'genpat.ini');
|
||||
Reset(Config);
|
||||
while not eof(Config) do begin
|
||||
ReadLn(Config,S);
|
||||
d:=Pos('=',S);
|
||||
if not (d=0) then begin
|
||||
Key:=LowerCase(Copy(S,1,d-1));
|
||||
S:=Copy(S,d+1,Length(S));
|
||||
if CompareStr(Key,'startblocksize')=0 then PG.StartBlockSize:=StrToInt(S);
|
||||
end;
|
||||
end;
|
||||
CloseFile(Config);
|
||||
end;
|
||||
{$ENDIF}
|
||||
for d:=1 to ParamCount do begin
|
||||
if CompareStr(LowerCase(Copy(ParamStr(d),1,3)),'/b=')=0 then begin
|
||||
PG.StartBlockSize:=StrToInt(Copy(ParamStr(d),4,10));
|
||||
end;
|
||||
end;
|
||||
|
||||
if (CompareStr(ParamStr(1),'')=0) or (CompareStr(ParamStr(2),'')=0) or (CompareStr(ParamStr(3),'')=0) then begin
|
||||
WriteLn('This program will take (sourcefile) as input and create a (patchfile).');
|
||||
WriteLn('With this patchfile, you can convert a (sourcefile) into (targetfile).');
|
||||
WriteLn;
|
||||
WriteLn('Command line info:');
|
||||
WriteLn(' GENPAT (sourcefile) (targetfile) (patchfile)');
|
||||
WriteLn;
|
||||
WriteLn('Command line options (you do not need them):');
|
||||
WriteLn('/B=(BlockSize) Set blocksize (def=64), multiple of 2');
|
||||
WriteLn('/WAIT Wait for a keypress after program complete');
|
||||
WriteLn('/DEBUG Show runtime debug information');
|
||||
WriteLn('Note: all these parameters must be *after* the filenames!');
|
||||
WriteLn;
|
||||
Write('Press a enter to exit ');
|
||||
ReadLn(S);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if FileExists(ParamStr(3)) then begin
|
||||
WriteLn('Using existing file to include patches in: '+ParamStr(3));
|
||||
PG.LoadFromFile(ParamStr(3));
|
||||
end;
|
||||
|
||||
T1:=Now;
|
||||
WriteLn('Patch body size: '+IntToStr(PG.CreatePatch(ParamStr(1),ParamStr(2))));
|
||||
PG.WriteToFile(ParamStr(3));
|
||||
|
||||
T2:=Now;
|
||||
Write('GenPat.exe finished execution in: ');
|
||||
WriteLn(FloatToStr((T2-T1)*24*60*60),'s');
|
||||
WriteLn;
|
||||
{$IFNDEF AUTOWAIT}
|
||||
if FindCmdLineSwitch('wait',['/'],True) then begin
|
||||
{$ENDIF}
|
||||
WriteLn;
|
||||
WriteLn('Press a key');
|
||||
ReadLn(S);
|
||||
{$IFNDEF AUTOWAIT}
|
||||
end;
|
||||
{$ENDIF}
|
||||
PG.Free;
|
||||
end.
|
591
Contrib/VPatch/Source/GenPat/PatchGenerator.pas
Normal file
591
Contrib/VPatch/Source/GenPat/PatchGenerator.pas
Normal file
|
@ -0,0 +1,591 @@
|
|||
unit PatchGenerator;
|
||||
|
||||
{
|
||||
VPatch 2 - Patch Generator
|
||||
==========================
|
||||
|
||||
(c) 2002-2003 Van de Sande Productions
|
||||
|
||||
This unit contains the 'core' functionality of VPatch. TPatchGenerator can
|
||||
load/create/save .PAT files and supports CreatePatch(Old, New) to generate
|
||||
new patches. The only configurable parameter is StartBlockSize.
|
||||
Though I cleaned up the code a little bit, there is very little documentation.
|
||||
That's why I will briefly explain the general workings of the current VPatch
|
||||
algoritm.
|
||||
There is a source file, which is divided into blocks of BlockSize. Block 1
|
||||
spans bytes 0-15, block 2 16-31, etc if blocksize = 16. For every block, a
|
||||
checksum is calculated and then the block is inserted into a binary search
|
||||
tree, which is sorted on this checksum.
|
||||
Now, the target file (new version) is traversed linearly. For a block at a
|
||||
certain position, the checksum is calculated. Then, a lookup is performed in
|
||||
the binary search tree to find all blocks in the source file which match this
|
||||
checksum. For every occurence, it is checked how many consecutive bytes match
|
||||
with this block (note: since the checksum is not unique, this can be 0 as well,
|
||||
but since all occurences are checked, the largest match is selected). Note also
|
||||
that this match length is not limited to BlockSize but can be larger as well;
|
||||
everything beyond the block end is checked as well (the block is merely used
|
||||
as a starting position for checking the match).
|
||||
For those biggest block matches between source/target files, a copy instruction
|
||||
will be generated in the patch file. For 'inbetween' or unmatchable blocks, the
|
||||
data of the new file is placed in the patch file. This involves some
|
||||
housekeeping, which is what most of the other code does.
|
||||
|
||||
What's new
|
||||
----------
|
||||
2.0 20030811 Koen Initial documentation
|
||||
}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes,
|
||||
Sysutils,
|
||||
TreeCode,
|
||||
VDSP_CRC;
|
||||
|
||||
type
|
||||
TStatusNotifyEvent = procedure(S: String; Current, Total, Savings: Integer) of object;
|
||||
TDebugNotifyEvent = procedure(S: String) of object;
|
||||
PDataBlock = ^TDataBlock;
|
||||
TDataBlock = record
|
||||
SourceOffset: Integer;
|
||||
TargetOffset: Integer;
|
||||
Size: Integer;
|
||||
Next: PDataBlock;
|
||||
end;
|
||||
//internal structure for FindBlock
|
||||
TBlock = record
|
||||
Offset: Integer;
|
||||
Size: Integer;
|
||||
end;
|
||||
TPatchGenerator = class
|
||||
private
|
||||
noPat: Integer;
|
||||
PRay: Array of TDataBlock;
|
||||
NRay: Array of TDataBlock;
|
||||
|
||||
FPatchData: TMemoryStream;
|
||||
FStartBlockSize: Integer; //initial block size
|
||||
FBlockDivider: Integer; //... block size is divided by this
|
||||
FMinimumBlockSize: Integer;//until this minimum is reached
|
||||
FStepSize: Integer;
|
||||
|
||||
//input: ASubBlock, which is a pointer to the start of the block to look
|
||||
//for in ABlock. The entire ABlock is searched. The function returns the
|
||||
//offset of the block, when it is found. The ASize parameter contains the
|
||||
//size of this block
|
||||
procedure ShowDebug(S: String);
|
||||
|
||||
function FindBlock(ASubBlock, ABlock, ABlockTree: Pointer;
|
||||
var ASubBlockStart: Integer; ASubBlockSize, ABlockSize,
|
||||
AMatchSize, ABlockTreeNodeCount: Integer; var ASize: Integer): Integer;
|
||||
|
||||
procedure FindBlockSize(ASubBlock, ABlock: Pointer; ASubBlockSize,
|
||||
ABlockSize: Integer; var ASubStart, AStart, AFoundSize: Integer);
|
||||
function WritePatchToStream(Target: Pointer; SourceCRC,
|
||||
TargetCRC: Integer): Integer;
|
||||
|
||||
procedure RemoveExistingPatch(ACRC: Integer);
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure Clear;
|
||||
function CreatePatch(SourceFileName, TargetFileName: String): Integer;
|
||||
property StartBlockSize: Integer read FStartBlockSize write FStartBlockSize;
|
||||
property BlockDivider: Integer read FBlockDivider write FBlockDivider;
|
||||
property MinimumBlockSize: Integer read FMinimumBlockSize write FMinimumBlockSize;
|
||||
property StepSize: Integer read FStepSize write FStepSize;
|
||||
function Size: Integer;
|
||||
procedure WriteToFile(AFileName: String);
|
||||
procedure WriteToStream(AStream: TStream);
|
||||
procedure LoadFromFile(AFileName: String);
|
||||
end;
|
||||
|
||||
|
||||
const
|
||||
BUF_BLOCK_SIZE = 4096;
|
||||
INIT_BLOCK_COUNT=10000;
|
||||
|
||||
var
|
||||
DebugEvent: TDebugNotifyEvent = nil;
|
||||
|
||||
implementation
|
||||
|
||||
{ TPatchGenerator }
|
||||
|
||||
procedure TPatchGenerator.Clear;
|
||||
begin
|
||||
FPatchData.Clear;
|
||||
end;
|
||||
|
||||
constructor TPatchGenerator.Create;
|
||||
begin
|
||||
inherited;
|
||||
FPatchData:=TMemoryStream.Create;
|
||||
end;
|
||||
|
||||
function TPatchGenerator.CreatePatch(SourceFileName,
|
||||
TargetFileName: String): Integer;
|
||||
var
|
||||
fsSource, fsTarget: TFileStream;
|
||||
fm: TMemoryStream;
|
||||
Source, Target: Pointer;
|
||||
SourceSize, TargetSize: Integer;
|
||||
SourceCRC, TargetCRC: Integer;
|
||||
SourceTree: Pointer;
|
||||
SourceTreeNodeCount: Cardinal;
|
||||
cBlockSize: Integer;
|
||||
|
||||
o,i,lastO: Integer;
|
||||
Start,Siz,BetweenSiz: Integer;
|
||||
retTO: Integer;
|
||||
noN: Integer;
|
||||
begin
|
||||
fsSource:=TFileStream.Create(SourceFileName,fmOpenRead);
|
||||
fsTarget:=TFileStream.Create(TargetFileName,fmOpenRead);
|
||||
fm:=TMemoryStream.Create;
|
||||
|
||||
SetLength(PRay,INIT_BLOCK_COUNT);
|
||||
SetLength(NRay,INIT_BLOCK_COUNT);
|
||||
|
||||
//Load those files into memory!
|
||||
SourceSize:=fsSource.Size;
|
||||
GetMem(Source,SourceSize);
|
||||
fm.CopyFrom(fsSource,SourceSize);
|
||||
Move(fm.Memory^,Source^,SourceSize);
|
||||
SourceCRC:=FileCRC(fsSource);
|
||||
fsSource.Free;
|
||||
|
||||
fm.Clear;
|
||||
TargetSize:=fsTarget.Size;
|
||||
GetMem(Target,TargetSize);
|
||||
fm.CopyFrom(fsTarget,TargetSize);
|
||||
Move(fm.Memory^,Target^,TargetSize);
|
||||
TargetCRC:=FileCRC(fsTarget);
|
||||
fsTarget.Free;
|
||||
fm.Free;
|
||||
|
||||
PRay[0].TargetOffset:=0;
|
||||
PRay[0].SourceOffset:=0;
|
||||
PRay[0].Size:=0;
|
||||
noPat:=1;
|
||||
|
||||
//termination block
|
||||
|
||||
PRay[noPat].SourceOffset:=0;
|
||||
PRay[noPat].TargetOffset:=TargetSize;
|
||||
PRay[noPat].Size:=0;
|
||||
|
||||
//we only have one pass in this mode
|
||||
// StartBlockSize:=16;
|
||||
MinimumBlockSize:=StartBlockSize;
|
||||
StepSize:=1;
|
||||
BlockDivider:=2;
|
||||
|
||||
//because we are dividing first inside.
|
||||
cBlockSize:=StartBlockSize*BlockDivider;
|
||||
|
||||
SourceTree:=nil;
|
||||
SourceTreeNodeCount:=BuildTree(Source,SourceTree,SourceSize,cBlockSize div BlockDivider);
|
||||
SortTree(SourceTree,SourceTreeNodeCount);
|
||||
|
||||
//now, we must do the above again - with a smaller block size
|
||||
repeat
|
||||
if cBlockSize<=MinimumBlockSize then break;
|
||||
cBlockSize:=cBlockSize div BlockDivider;
|
||||
noN:=0;
|
||||
for i:=1 to noPat do begin
|
||||
//calculate location of the inbetween parts
|
||||
Start:=PRay[i-1].TargetOffset+PRay[i-1].Size;
|
||||
BetweenSiz:=PRay[i].TargetOffset-Start;
|
||||
|
||||
NRay[noN].SourceOffset:=PRay[i-1].SourceOffset;
|
||||
NRay[noN].TargetOffset:=PRay[i-1].TargetOffset;
|
||||
NRay[noN].Size:=PRay[i-1].Size;
|
||||
Inc(noN);
|
||||
if BetweenSiz>0 then begin
|
||||
|
||||
o:=Start;
|
||||
repeat
|
||||
//ShowDebug(PChar('DoFind '+IntToStr(o)));
|
||||
LastO:=o;
|
||||
retTO:=FindBlock(Target,Source,SourceTree,o,TargetSize,SourceSize,cBlockSize,SourceTreeNodeCount,Siz);
|
||||
if not (Siz=0) then
|
||||
ShowDebug(IntToStr(LastO)+' -> Source='+IntToStr(retTO)+' Target='+IntToStr(o)+' Size='+IntToStr(Siz));
|
||||
|
||||
if Siz=0 then begin
|
||||
o:=LastO+StepSize;
|
||||
end else begin
|
||||
//we have found a block, let's add it!
|
||||
NRay[noN].SourceOffset:=retTO;
|
||||
NRay[noN].TargetOffset:=o;
|
||||
NRay[noN].Size:=Siz;
|
||||
Inc(noN);
|
||||
if noN>=Length(NRay) then begin
|
||||
SetLength(NRay,Length(NRay)*2);
|
||||
SetLength(PRay,Length(PRay)*2);
|
||||
end;
|
||||
Inc(o,Siz);
|
||||
end;
|
||||
|
||||
//check to see if we're not inside another one.
|
||||
Siz:=NRay[noN].TargetOffset-NRay[noN-1].TargetOffset-NRay[noN-1].Size;
|
||||
If Siz<0 then begin //that's impossible! (overlapping should be eliminated)
|
||||
NRay[noN].TargetOffset:=NRay[noN].TargetOffset-Siz;
|
||||
NRay[noN].Size:=NRay[noN].Size+Siz;
|
||||
NRay[noN].SourceOffset:=NRay[noN].SourceOffset-Siz;
|
||||
end;
|
||||
until o>Start+BetweenSiz;
|
||||
|
||||
end;
|
||||
end;
|
||||
//I think the last termination block isn't copied: do so now.
|
||||
NRay[noN].SourceOffset:=PRay[noPat].SourceOffset;
|
||||
NRay[noN].TargetOffset:=PRay[noPat].TargetOffset;
|
||||
NRay[noN].Size:=PRay[noPat].Size;
|
||||
//copy back into PRay
|
||||
for i:=0 to noN do begin
|
||||
PRay[i].SourceOffset:=NRay[i].SourceOffset;
|
||||
PRay[i].TargetOffset:=NRay[i].TargetOffset;
|
||||
PRay[i].Size:=NRay[i].Size;
|
||||
end;
|
||||
noPat:=noN;
|
||||
until false;
|
||||
|
||||
//writing is next!
|
||||
ShowDebug('Writing patch');
|
||||
|
||||
Result:=WritePatchToStream(Target, SourceCRC, TargetCRC);
|
||||
|
||||
ClearTree(SourceTree,SourceTreeNodeCount);
|
||||
FreeMem(Source,SourceSize);
|
||||
FreeMem(Target,TargetSize);
|
||||
ShowDebug('Done');
|
||||
end;
|
||||
|
||||
destructor TPatchGenerator.Destroy;
|
||||
begin
|
||||
FPatchData.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TPatchGenerator.FindBlock(ASubBlock, ABlock, ABlockTree: Pointer; var ASubBlockStart: Integer;
|
||||
ASubBlockSize, ABlockSize, AMatchSize, ABlockTreeNodeCount: Integer; var ASize: Integer): Integer;
|
||||
//This procedure locates location of a block in the target file
|
||||
//Then, it calls FindBlockSize to determine size of this block
|
||||
var
|
||||
MatchSize, FoundSize: Integer;
|
||||
q,r,i: Integer;
|
||||
FoundCache_SubOffset, FoundCache_Size, FoundCache_Offset: Integer;
|
||||
Checksum: Cardinal;
|
||||
PFound: PTreeNode;
|
||||
FoundCount: Integer;
|
||||
begin
|
||||
//if we find nothing...
|
||||
|
||||
FoundCache_Size:=0;
|
||||
FoundCache_Offset:=0;
|
||||
FoundCache_SubOffset:=ASubBlockStart;
|
||||
|
||||
FindBlock:=0;
|
||||
ASize:=0;
|
||||
|
||||
MatchSize:=AMatchSize;
|
||||
|
||||
//we can only find MatchSize sized blocks in the tree!
|
||||
if MatchSize > ASubBlockSize - ASubBlockStart then Exit;
|
||||
if MatchSize = 0 then Exit;
|
||||
|
||||
Checksum:=0;
|
||||
calculateChecksum(ASubBlock,ASubBlockStart,MatchSize,Checksum);
|
||||
PFound:=TreeFind(Checksum,ABlockTree,ABlockTreeNodeCount,FoundCount);
|
||||
|
||||
for i:=0 to Pred(FoundCount) do begin
|
||||
FoundSize:=MatchSize;
|
||||
//q = offset in Block
|
||||
q:=PFound^.Offset;
|
||||
//r = offset in SubBlock
|
||||
r:=ASubBlockStart;
|
||||
FindBlockSize(ASubBlock, ABlock, ASubBlockSize, ABlockSize, r, q, FoundSize);
|
||||
if FoundSize>FoundCache_Size then begin
|
||||
FoundCache_SubOffset:=r;
|
||||
FoundCache_Offset:=q;
|
||||
FoundCache_Size:=FoundSize;
|
||||
end;
|
||||
ShowDebug(' Block Size Start='+IntToStr(r)+' tarStart='+IntToStr(q)+' Size='+IntToStr(FoundSize));
|
||||
PFound:=PTreeNode(Integer(PFound)+SizeOf(TTreeNode));
|
||||
end;
|
||||
|
||||
FindBlock:=FoundCache_Offset;
|
||||
ASize:=FoundCache_Size;
|
||||
ASubBlockStart:=FoundCache_SubOffset;
|
||||
end;
|
||||
|
||||
procedure TPatchGenerator.FindBlockSize(ASubBlock, ABlock: Pointer; ASubBlockSize, ABlockSize: Integer; var ASubStart,AStart,AFoundSize: Integer);
|
||||
var
|
||||
FoundSize: Integer;
|
||||
a,c,d,i: Integer;
|
||||
f1p,f2p,f1Size,f2Size: Integer;
|
||||
beforeSize: Integer;
|
||||
CurBufSize: Integer;
|
||||
begin
|
||||
//OK, now let's go...
|
||||
//Trace after -> how long does this go on?
|
||||
f1p:=Integer(ASubBlock)+ASubStart;
|
||||
f2p:=Integer(ABlock)+AStart;
|
||||
f1Size:=ASubBlockSize-ASubStart;
|
||||
f2Size:=ABlockSize-AStart;
|
||||
FoundSize:=0;
|
||||
CurBufSize := BUF_BLOCK_SIZE; //size of the block we're checking
|
||||
while not (CurBufSize = 0) do begin
|
||||
//we need equal bytes from both... so if one of them EOF, it's the end.
|
||||
if FoundSize+CurBufSize>f1Size then CurBufSize:=f1Size - FoundSize;
|
||||
if FoundSize+CurBufSize>f2Size then CurBufSize:=f2Size - FoundSize;
|
||||
if CompareMem(Pointer(f1p),Pointer(f2p),CurBufSize) then begin
|
||||
Inc(FoundSize,CurBufSize);
|
||||
Inc(f1p,CurBufSize);
|
||||
Inc(f2p,CurBufSize);
|
||||
end
|
||||
else begin
|
||||
CurBufSize:=CurBufSize div 2;
|
||||
end;
|
||||
end;
|
||||
|
||||
if FoundSize = 0 then begin AFoundSize:=0; Exit; end;
|
||||
|
||||
//Trace before -> how much bytes are still the same before the block?
|
||||
//First, read 1 block from source and 1 block from target, start from back to compare how much they differ
|
||||
//just take BUF_BLOCK_SIZE as maximum size for the block before - that's surely
|
||||
//big enough!
|
||||
beforeSize:=BUF_BLOCK_SIZE;
|
||||
a:=ASubStart-beforeSize;
|
||||
if a<0 then begin
|
||||
a:=0;
|
||||
beforeSize:=ASubStart;
|
||||
end;
|
||||
//b is the current before block size
|
||||
c:=AStart-beforeSize;
|
||||
if c<0 then begin
|
||||
c:=0;
|
||||
beforeSize:=AStart;
|
||||
a:=ASubStart-beforeSize;
|
||||
end;
|
||||
//a=Offset in source
|
||||
//b=Size of beforeblock
|
||||
//c=offset in target
|
||||
|
||||
d:=0;
|
||||
for i:=beforeSize-1 downto 0 do begin
|
||||
//if not (f1^[a+i]=f2^[c+i]) then begin
|
||||
if not (PByte(Integer(ASubBlock)+a+i)^=PByte(Integer(ABlock)+c+i)^) then begin
|
||||
//d=how many bytes before are the same?
|
||||
Break;
|
||||
end;
|
||||
Inc(d);
|
||||
end;
|
||||
Inc(FoundSize,d);
|
||||
Dec(ASubStart,d);
|
||||
Dec(AStart,d);
|
||||
AFoundSize:=FoundSize;
|
||||
end;
|
||||
|
||||
function TPatchGenerator.Size: Integer;
|
||||
begin
|
||||
Result:=FPatchData.Size;
|
||||
end;
|
||||
|
||||
procedure TPatchGenerator.ShowDebug(S: String);
|
||||
begin
|
||||
if Assigned(DebugEvent) then DebugEvent(S);
|
||||
end;
|
||||
|
||||
function TPatchGenerator.WritePatchToStream(Target: Pointer; SourceCRC, TargetCRC: Integer): Integer;
|
||||
var
|
||||
HeadID: Array[0..3] of Char;
|
||||
NoBlocks, NoBlocksOffset, BodySize, BodySizeOffset: Integer;
|
||||
b: Byte;
|
||||
w: Word;
|
||||
i, j: Integer;
|
||||
l: LongWord;
|
||||
Start, Siz: Integer;
|
||||
PTarget: Pointer;
|
||||
begin
|
||||
RemoveExistingPatch(SourceCRC);
|
||||
with FPatchData do begin
|
||||
Seek(0,soFromEnd);
|
||||
if Size = 0 then begin
|
||||
HeadID:='VPAT';
|
||||
Write(HeadID,SizeOf(HeadID));
|
||||
l:=0;
|
||||
Write(l,SizeOf(l)); //NoFiles
|
||||
end;
|
||||
l:=0;
|
||||
NoBlocksOffset:=Position;
|
||||
Write(l,SizeOf(l)); //should become NoBlocks later
|
||||
Write(SourceCRC,SizeOf(SourceCRC)); //source CRC
|
||||
Write(TargetCRC,SizeOf(TargetCRC)); //target CRC
|
||||
BodySizeOffset:=Position;
|
||||
Write(l,SizeOf(l)); //should become BodySize (of this patch)
|
||||
|
||||
NoBlocks:=0;
|
||||
BodySize:=0;
|
||||
//Write the patch...
|
||||
for i:=0 to noPat - 1 do begin
|
||||
//write char 1 - integer/copysource
|
||||
//write char 2 - long/copysource
|
||||
//write char 5 - integer/insidepatch
|
||||
//write char 6 - long/insidepatch
|
||||
|
||||
Start:=PRay[i].TargetOffset+PRay[i].Size;
|
||||
Siz:= PRay[i+1].TargetOffset-Start;
|
||||
|
||||
If Siz<0 then begin //that's impossible! (overlapping should be eliminated)
|
||||
PRay[i+1].TargetOffset:=PRay[i+1].TargetOffset-Siz;
|
||||
PRay[i+1].Size:=PRay[i+1].Size+Siz;
|
||||
PRay[i+1].SourceOffset:=PRay[i+1].SourceOffset-Siz;
|
||||
Siz:=0;
|
||||
end;
|
||||
|
||||
if not (PRay[i].Size=0) then begin
|
||||
if (PRay[i].Size<=255) then begin
|
||||
b:=1;
|
||||
Write(b,SizeOf(b));
|
||||
b:=PRay[i].Size;
|
||||
Write(b,SizeOf(b));
|
||||
Inc(BodySize,2);
|
||||
end else if PRay[i].Size<=65535 then begin
|
||||
b:=2;
|
||||
Write(b,SizeOf(b));
|
||||
w:=PRay[i].Size;
|
||||
Write(w,SizeOf(w));
|
||||
Inc(BodySize,3);
|
||||
end else begin
|
||||
b:=3;
|
||||
Write(b,SizeOf(b));
|
||||
Write(PRay[i].Size,SizeOf(Integer));
|
||||
Inc(BodySize,5);
|
||||
end;
|
||||
Write(PRay[i].SourceOffset,SizeOf(Integer));
|
||||
Inc(BodySize,SizeOf(Integer));
|
||||
Inc(NoBlocks);
|
||||
end;
|
||||
//Now write the writeblock
|
||||
If Not (Siz = 0) Then begin
|
||||
if Siz<=255 then begin
|
||||
b:=5;
|
||||
Write(b,SizeOf(b));
|
||||
b:=Siz;
|
||||
Write(b,SizeOf(b));
|
||||
Inc(BodySize,2);
|
||||
end else if Siz<=65535 then begin
|
||||
b:=6;
|
||||
Write(b,1);
|
||||
w:=Siz;
|
||||
Write(w,2);
|
||||
Inc(BodySize,3);
|
||||
end else begin
|
||||
b:=7;
|
||||
Write(b,1);
|
||||
Write(Siz,4);
|
||||
Inc(BodySize,5);
|
||||
end;
|
||||
PTarget:=Pointer(Integer(Target)+Start);
|
||||
j:=Start;
|
||||
repeat
|
||||
//read
|
||||
if (j+4096>Start+Siz) then begin
|
||||
Write(PTarget^,Start+Siz-j);
|
||||
break;
|
||||
end;
|
||||
Write(PTarget^,4096);
|
||||
Inc(j,4096);
|
||||
PTarget:=Pointer(Integer(PTarget)+4096);
|
||||
until false;
|
||||
Inc(BodySize,Siz);
|
||||
Inc(NoBlocks);
|
||||
end;
|
||||
end;
|
||||
Seek(NoBlocksOffset,soFromBeginning);
|
||||
Write(NoBlocks,SizeOf(NoBlocks));
|
||||
Seek(BodySizeOffset,soFromBeginning);
|
||||
Write(BodySize,SizeOf(BodySize));
|
||||
ShowDebug('Patch body size: '+IntToStr(BodySize));
|
||||
ShowDebug('Total patch size:'+IntToStr(Size));
|
||||
//now increase file count
|
||||
Seek(4,soFromBeginning);
|
||||
Read(i,SizeOf(i));
|
||||
Inc(i);
|
||||
Seek(4,soFromBeginning);
|
||||
Write(i,SizeOf(i));
|
||||
Seek(0,soFromEnd);
|
||||
Result:=BodySize;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPatchGenerator.WriteToFile(AFileName: String);
|
||||
var
|
||||
fs: TFileStream;
|
||||
begin
|
||||
fs:=TFileStream.Create(AFileName,fmCreate);
|
||||
FPatchData.Seek(0,soFromBeginning);
|
||||
fs.CopyFrom(FPatchData,FPatchData.Size);
|
||||
fs.Free;
|
||||
end;
|
||||
|
||||
procedure TPatchGenerator.LoadFromFile(AFileName: String);
|
||||
var
|
||||
fs: TFileStream;
|
||||
begin
|
||||
fs:=TFileStream.Create(AFileName,fmOpenRead);
|
||||
FPatchData.Clear;
|
||||
FPatchData.CopyFrom(fs,fs.Size);
|
||||
fs.Free;
|
||||
end;
|
||||
|
||||
procedure TPatchGenerator.RemoveExistingPatch(ACRC: Integer);
|
||||
var
|
||||
HeadID: Array[0..3] of Char;
|
||||
NoFiles, i, j, SourceCRC, MSize: Integer;
|
||||
StartPos: Integer;
|
||||
ms: TMemoryStream;
|
||||
begin
|
||||
with FPatchData do begin
|
||||
if Size = 0 then Exit;
|
||||
Seek(0,soFromBeginning);
|
||||
Read(HeadID,SizeOf(HeadID));
|
||||
if HeadID = 'VPAT' then begin
|
||||
Read(NoFiles,SizeOf(NoFiles));
|
||||
for i:=0 to Pred(NoFiles) do begin
|
||||
if Position >= Size then Break;
|
||||
StartPos:=Position;
|
||||
Read(j,SizeOf(j)); //NoBlocks
|
||||
Read(SourceCRC,SizeOf(SourceCRC)); //SourceCRC
|
||||
Read(j,SizeOf(j)); //TargetCRC
|
||||
Read(j,SizeOf(j)); //BodySize
|
||||
Seek(j,soFromCurrent);
|
||||
if SourceCRC = ACRC then begin
|
||||
ms:=TMemoryStream.Create;
|
||||
MSize:=Size-Position;
|
||||
if MSize > 0 then ms.CopyFrom(FPatchData,MSize);
|
||||
ms.Seek(0, soFromBeginning);
|
||||
FPatchData.Seek(StartPos,soFromBeginning);
|
||||
FPatchData.SetSize(Size - j - SizeOf(Integer) * 4);
|
||||
FPatchData.CopyFrom(ms,ms.Size);
|
||||
ms.Free;
|
||||
Dec(NoFiles);
|
||||
Seek(4,soFromBeginning);
|
||||
Write(NoFiles,SizeOf(NoFiles));
|
||||
Break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPatchGenerator.WriteToStream(AStream: TStream);
|
||||
begin
|
||||
FPatchData.Seek(0,soFromBeginning);
|
||||
AStream.CopyFrom(FPatchData,FPatchData.Size);
|
||||
end;
|
||||
|
||||
end.
|
245
Contrib/VPatch/Source/GenPat/TreeCode.pas
Normal file
245
Contrib/VPatch/Source/GenPat/TreeCode.pas
Normal file
|
@ -0,0 +1,245 @@
|
|||
unit TreeCode;
|
||||
|
||||
{
|
||||
VPatch 2 - Binary Checksum Tree
|
||||
===============================
|
||||
|
||||
(c) 2002-2003 Van de Sande Productions
|
||||
|
||||
This unit implements a binary search tree, which is constructed from a memory
|
||||
block by BuildTree. This memory block is divided into equal-sized blocks of
|
||||
BlockSize, and for every block a checksum is calculated. Then, it is inserted
|
||||
in the binary tree, which is sorted on the checksum.
|
||||
The patch generator will search for the checksums using a binary search, which
|
||||
is O(log n) (much better than the old 1.x algoritm, which was O(n)).
|
||||
|
||||
What's new
|
||||
----------
|
||||
2.0 20030811 Koen Initial documentation
|
||||
}
|
||||
|
||||
interface
|
||||
|
||||
type
|
||||
TSortStack=record
|
||||
lo,hi: Integer;
|
||||
end;
|
||||
PTreeNode = ^TTreeNode;
|
||||
TTreeNode = record
|
||||
Checksum: Cardinal;
|
||||
Offset: Cardinal;
|
||||
end;
|
||||
|
||||
const
|
||||
TREENODE_SIZE = SizeOf(TTreeNode);
|
||||
|
||||
procedure calculateChecksum(AData: Pointer; AStart, ASize: Cardinal; var K: Cardinal);
|
||||
procedure calculateNext(AData: Pointer; AStart, ASize: Cardinal; var K: Cardinal);
|
||||
function BuildTree(ASource: Pointer; var ATree: Pointer; ASourceSize, ABLOCKSIZE: Cardinal): Cardinal;
|
||||
procedure SortTree(ATree: Pointer; ANodeCount: Cardinal);
|
||||
procedure ClearTree(var ATree: Pointer; ANodeCount: Cardinal);
|
||||
function TreeFind(AChecksum: Cardinal; ABlockTree: Pointer; ABlockTreeNodeCount: Integer; var FoundCount: Integer): PTreeNode;
|
||||
function GetItem(ATree: Pointer; Index, ANodeCount: Cardinal): TTreeNode;
|
||||
|
||||
procedure Test;
|
||||
|
||||
implementation
|
||||
|
||||
uses SysUtils;
|
||||
|
||||
procedure calculateChecksum(AData: Pointer; AStart, ASize: Cardinal; var K: Cardinal);
|
||||
var
|
||||
A,B,i,j: Cardinal;
|
||||
begin
|
||||
A:=K and $0000FFFF;
|
||||
B:=(K and $FFFF0000) shr 16;
|
||||
j:=Cardinal(AData)+AStart;
|
||||
for i:=1 to ASize do begin
|
||||
A:=A + PByte(j)^;
|
||||
B:=B + (ASize-i+1)*PByte(j)^;
|
||||
Inc(j);
|
||||
end;
|
||||
K:=(A and $0000FFFF) or ((B and $0000FFFF) shl 16);
|
||||
end;
|
||||
|
||||
procedure calculateNext(AData: Pointer; AStart, ASize: Cardinal; var K: Cardinal);
|
||||
var
|
||||
A,B,j: Cardinal;
|
||||
begin
|
||||
j:=Cardinal(AData)+AStart;
|
||||
A:=(K-PByte(j-1)^+PByte(j+ASize-1)^) and $0000FFFF;
|
||||
B:=((K shr 16)-ASize*PByte(j-1)^+A) and $0000FFFF;
|
||||
K:=A or (B shl 16);
|
||||
end;
|
||||
|
||||
function BuildTree(ASource: Pointer; var ATree: Pointer; ASourceSize, ABLOCKSIZE: Cardinal): Cardinal;
|
||||
var
|
||||
i, NodeCount: Cardinal;
|
||||
Node: TTreeNode;
|
||||
begin
|
||||
Assert(not Assigned(ATree),'Cannot use initialized tree in BuildTree!');
|
||||
NodeCount:=ASourceSize div ABLOCKSIZE;
|
||||
GetMem(ATree,NodeCount*TREENODE_SIZE);
|
||||
if NodeCount > 0 then begin
|
||||
for i:=0 to Pred(NodeCount) do begin
|
||||
Node.Offset:=i*ABLOCKSIZE;
|
||||
Node.Checksum:=0;
|
||||
calculateChecksum(ASource,Node.Offset,ABLOCKSIZE,Node.Checksum);
|
||||
Move(Node,Pointer(Cardinal(ATree)+i*TREENODE_SIZE)^,TREENODE_SIZE);
|
||||
end;
|
||||
end;
|
||||
Result:=NodeCount;
|
||||
end;
|
||||
|
||||
procedure SetItem(ATree: Pointer; Index, ANodeCount: Cardinal; New: TTreeNode);
|
||||
var
|
||||
p: PTreeNode;
|
||||
begin
|
||||
Assert(Index<ANodeCount,'Tree/GetItem: Index too big');
|
||||
p:=PTreeNode(Cardinal(ATree)+Index*TREENODE_SIZE);
|
||||
p^:=New;
|
||||
end;
|
||||
|
||||
function GetItem(ATree: Pointer; Index, ANodeCount: Cardinal): TTreeNode;
|
||||
var
|
||||
p: PTreeNode;
|
||||
begin
|
||||
Assert(Index<ANodeCount,'Tree/GetItem: Index too big '+IntToStr(Index));
|
||||
p:=PTreeNode(Cardinal(ATree)+Index*TREENODE_SIZE);
|
||||
Result:=p^;
|
||||
end;
|
||||
|
||||
procedure SortTree(ATree: Pointer; ANodeCount: Cardinal);
|
||||
var
|
||||
compare: Cardinal;
|
||||
aStack: Array[1..128] of TSortStack;
|
||||
StackPtr: Integer;
|
||||
Mid,i,j,low,hi: Integer;
|
||||
Switcher: TTreeNode;
|
||||
begin
|
||||
If ANodeCount = 0 Then Exit;
|
||||
StackPtr:=1;
|
||||
aStack[StackPtr].lo:=0;
|
||||
aStack[StackPtr].hi:=ANodeCount - 1;
|
||||
Inc(StackPtr);
|
||||
while not (StackPtr=1) do begin
|
||||
StackPtr:=StackPtr-1;
|
||||
low:=aStack[StackPtr].lo;
|
||||
hi:=aStack[StackPtr].hi;
|
||||
while true do begin
|
||||
i:=low;
|
||||
j:=hi;
|
||||
Mid:=(low + hi) div 2;
|
||||
compare:=PTreeNode(Integer(ATree)+Mid*TREENODE_SIZE)^.Checksum;
|
||||
while true do begin
|
||||
While PTreeNode(Integer(ATree)+i*TREENODE_SIZE)^.Checksum < compare do begin
|
||||
Inc(i);
|
||||
end;
|
||||
While PTreeNode(Integer(ATree)+j*TREENODE_SIZE)^.Checksum > compare do begin
|
||||
j:=j-1;
|
||||
end;
|
||||
If (i <= j) Then begin
|
||||
Move(Pointer(Integer(ATree)+j*TREENODE_SIZE)^,Switcher,TREENODE_SIZE);
|
||||
Move(Pointer(Integer(ATree)+i*TREENODE_SIZE)^,Pointer(Integer(ATree)+j*TREENODE_SIZE)^,TREENODE_SIZE);
|
||||
Move(Switcher,Pointer(Integer(ATree)+i*TREENODE_SIZE)^,TREENODE_SIZE);
|
||||
Inc(i);
|
||||
Dec(j);
|
||||
End;
|
||||
if not (i <= j) then break;
|
||||
end;
|
||||
If j - low < hi - i Then begin
|
||||
If i < hi Then begin
|
||||
aStack[StackPtr].lo:=i;
|
||||
aStack[StackPtr].hi:=hi;
|
||||
Inc(StackPtr);
|
||||
End;
|
||||
hi:=j;
|
||||
end Else begin
|
||||
If low < j Then begin
|
||||
aStack[StackPtr].lo:=low;
|
||||
aStack[StackPtr].hi:=j;
|
||||
Inc(StackPtr);
|
||||
End;
|
||||
low:=i;
|
||||
End;
|
||||
if not (low<hi) then break;
|
||||
end;
|
||||
if StackPtr=1 then break;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure ClearTree(var ATree: Pointer; ANodeCount: Cardinal);
|
||||
begin
|
||||
FreeMem(ATree,ANodeCount*TREENODE_SIZE);
|
||||
ATree:=nil;
|
||||
end;
|
||||
|
||||
function TreeFind(AChecksum: Cardinal; ABlockTree: Pointer; ABlockTreeNodeCount: Integer; var FoundCount: Integer): PTreeNode;
|
||||
var
|
||||
lo,mid,hi,m: Integer;
|
||||
tmp: Cardinal;
|
||||
begin
|
||||
lo:=0;
|
||||
hi:=ABlockTreeNodeCount-1;
|
||||
while true do begin
|
||||
mid:=(lo+hi) div 2;
|
||||
tmp:=PCardinal(Integer(ABlockTree)+mid*TREENODE_SIZE)^;
|
||||
if tmp = AChecksum then begin
|
||||
FoundCount:=1;
|
||||
m:=mid;
|
||||
Result:=PTreeNode(Integer(ABlockTree)+m*TREENODE_SIZE);
|
||||
while m > 0 do begin
|
||||
Dec(m);
|
||||
if PCardinal(Integer(ABlockTree)+m*TREENODE_SIZE)^ = tmp then begin
|
||||
Result:=PTreeNode(Integer(ABlockTree)+m*TREENODE_SIZE);
|
||||
Inc(FoundCount);
|
||||
end else
|
||||
Break;
|
||||
end;
|
||||
m:=mid;
|
||||
while m < ABlockTreeNodeCount-1 do begin
|
||||
Inc(m);
|
||||
if PCardinal(Integer(ABlockTree)+m*TREENODE_SIZE)^ = tmp then begin
|
||||
Inc(FoundCount);
|
||||
end else
|
||||
Break;
|
||||
end;
|
||||
Exit;
|
||||
end;
|
||||
if lo>=hi then Break;
|
||||
if AChecksum < tmp then begin
|
||||
hi:=mid-1;
|
||||
end else begin
|
||||
lo:=mid+1;
|
||||
end;
|
||||
end;
|
||||
FoundCount:=0; Result:=nil;
|
||||
end;
|
||||
|
||||
procedure Test;
|
||||
var
|
||||
p: Pointer;
|
||||
t: TTreeNode;
|
||||
r: PTreeNode;
|
||||
i,q: Integer;
|
||||
NC: Integer;
|
||||
begin
|
||||
NC:=100;
|
||||
GetMem(p,800);
|
||||
for i:=0 to 99 do begin
|
||||
t.Offset:=i*100;
|
||||
t.Checksum:=i div 2;
|
||||
SetItem(p,i,NC,t);
|
||||
end;
|
||||
SortTree(p,NC);
|
||||
for i:=0 to 99 do begin
|
||||
t:=GetItem(p,i,NC);
|
||||
Write(IntToStr(t.Checksum)+' ');
|
||||
end;
|
||||
r:=TreeFind(7,p,NC,q);
|
||||
WriteLn(IntToStr(q));
|
||||
t:=r^;
|
||||
WriteLn(IntToStr(t.Checksum)+' ');
|
||||
end;
|
||||
|
||||
end.
|
77
Contrib/VPatch/Source/GenPat/VAppend.dpr
Normal file
77
Contrib/VPatch/Source/GenPat/VAppend.dpr
Normal file
|
@ -0,0 +1,77 @@
|
|||
program VAppend;
|
||||
|
||||
{$APPTYPE CONSOLE}
|
||||
|
||||
uses
|
||||
SysUtils;
|
||||
|
||||
var
|
||||
fs, fo: File;
|
||||
Patch: String;
|
||||
OutFile: String = 'VPATCH.EXE';
|
||||
Runtime: String = 'VPATCH.BIN';
|
||||
o: LongWord;
|
||||
Buf: Array[0..4095] of Byte;
|
||||
Size, BufSize: Integer;
|
||||
|
||||
begin
|
||||
WriteLn('VAppend v2.0');
|
||||
WriteLn('============');
|
||||
WriteLn;
|
||||
WriteLn('(c) 2001-2002 Van de Sande Productions');
|
||||
WriteLn('Website: http://www.tibed.net/vpatch');
|
||||
WriteLn('E-mail: koen@tibed.net');
|
||||
WriteLn;
|
||||
if ParamCount = 0 then begin
|
||||
WriteLn('Use this program to append .PAT files to the VPatch runtime.');
|
||||
WriteLn;
|
||||
WriteLn(' VAPPEND (patch file) [output file] [runtime]');
|
||||
WriteLn;
|
||||
WriteLn('By default, the output file is VPATCH.EXE and the runtime is VPATCH.BIN');
|
||||
end;
|
||||
if not FileExists(ParamStr(1)) then begin
|
||||
WriteLn('ERROR: Patch file not found');
|
||||
Exit;
|
||||
end;
|
||||
Patch := ParamStr(1);
|
||||
if ParamCount > 1 then OutFile := ParamStr(2);
|
||||
if ParamCount > 2 then Runtime := ParamStr(3);
|
||||
WriteLn('Patch: '+Patch);
|
||||
WriteLn('Runtime: '+Runtime);
|
||||
WriteLn('Output: '+OutFile);
|
||||
|
||||
AssignFile(fo,OutFile);
|
||||
Rewrite(fo,1);
|
||||
//copy the runtime
|
||||
AssignFile(fs,Runtime);
|
||||
FileMode:=fmOpenRead;
|
||||
Reset(fs,1);
|
||||
BufSize:=4096;
|
||||
o:=FileSize(fs); //patch start offset
|
||||
Size:=FileSize(fs);
|
||||
while Size>0 do begin
|
||||
if Size-BufSize<0 then BufSize:=Size;
|
||||
BlockRead(fs,Buf,BufSize);
|
||||
BlockWrite(fo,Buf,BufSize);
|
||||
Dec(Size,BufSize);
|
||||
end;
|
||||
CloseFile(fs);
|
||||
//do the patch
|
||||
AssignFile(fs,Patch);
|
||||
FileMode:=fmOpenRead;
|
||||
Reset(fs,1);
|
||||
BufSize:=4096;
|
||||
Size:=FileSize(fs);
|
||||
while Size>0 do begin
|
||||
if Size-BufSize<0 then BufSize:=Size;
|
||||
BlockRead(fs,Buf,BufSize);
|
||||
BlockWrite(fo,Buf,BufSize);
|
||||
Dec(Size,BufSize);
|
||||
end;
|
||||
CloseFile(fs);
|
||||
|
||||
BlockWrite(fo,o,SizeOf(o));
|
||||
CloseFile(fo);
|
||||
WriteLn('Created.');
|
||||
end.
|
||||
|
26
Contrib/VPatch/Source/GenPat/VPatch2.bpg
Normal file
26
Contrib/VPatch/Source/GenPat/VPatch2.bpg
Normal file
|
@ -0,0 +1,26 @@
|
|||
#------------------------------------------------------------------------------
|
||||
VERSION = BWS.01
|
||||
#------------------------------------------------------------------------------
|
||||
!ifndef ROOT
|
||||
ROOT = $(MAKEDIR)\..
|
||||
!endif
|
||||
#------------------------------------------------------------------------------
|
||||
MAKE = $(ROOT)\bin\make.exe -$(MAKEFLAGS) -f$**
|
||||
DCC = $(ROOT)\bin\dcc32.exe $**
|
||||
BRCC = $(ROOT)\bin\brcc32.exe $**
|
||||
#------------------------------------------------------------------------------
|
||||
PROJECTS = GenPat2.exe VAppend.exe VPatchGUI.exe
|
||||
#------------------------------------------------------------------------------
|
||||
default: $(PROJECTS)
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
GenPat2.exe: GenPat2.dpr
|
||||
$(DCC)
|
||||
|
||||
VAppend.exe: VAppend.dpr
|
||||
$(DCC)
|
||||
|
||||
VPatchGUI.exe: gui\VPatchGUI.dpr
|
||||
$(DCC)
|
||||
|
||||
|
115
Contrib/VPatch/Source/GenPat/vdsp_crc.pas
Normal file
115
Contrib/VPatch/Source/GenPat/vdsp_crc.pas
Normal file
|
@ -0,0 +1,115 @@
|
|||
unit VDSP_CRC;
|
||||
|
||||
{
|
||||
VPatch 2 - CRC
|
||||
==============
|
||||
|
||||
(c) 2002-2003 Van de Sande Productions
|
||||
|
||||
This unit can calculate the standard ZIP CRC32 for a filestream.
|
||||
|
||||
What's new
|
||||
----------
|
||||
2.0 20030811 Koen Initial documentation
|
||||
}
|
||||
|
||||
interface
|
||||
|
||||
uses Classes;
|
||||
|
||||
//var
|
||||
//this is the CRC32 table
|
||||
// CRCTable: Array[0..255] of LongWord;
|
||||
{= (
|
||||
//this table used to be inside the exe, but now it has been replaced by a calculation routine which saves 1 KB
|
||||
$0,$77073096,$EE0E612C,$990951BA,$76DC419,
|
||||
$706AF48F,$E963A535,$9E6495A3,$EDB8832,$79DCB8A4,
|
||||
$E0D5E91E,$97D2D988,$9B64C2B,$7EB17CBD,$E7B82D07,
|
||||
$90BF1D91,$1DB71064,$6AB020F2,$F3B97148,$84BE41DE,
|
||||
$1ADAD47D,$6DDDE4EB,$F4D4B551,$83D385C7,$136C9856,
|
||||
$646BA8C0,$FD62F97A,$8A65C9EC,$14015C4F,$63066CD9,
|
||||
$FA0F3D63,$8D080DF5,$3B6E20C8,$4C69105E,$D56041E4,
|
||||
$A2677172,$3C03E4D1,$4B04D447,$D20D85FD,$A50AB56B,
|
||||
$35B5A8FA,$42B2986C,$DBBBC9D6,$ACBCF940,$32D86CE3,
|
||||
$45DF5C75,$DCD60DCF,$ABD13D59,$26D930AC,$51DE003A,
|
||||
$C8D75180,$BFD06116,$21B4F4B5,$56B3C423,$CFBA9599,
|
||||
$B8BDA50F,$2802B89E,$5F058808,$C60CD9B2,$B10BE924,
|
||||
$2F6F7C87,$58684C11,$C1611DAB,$B6662D3D,$76DC4190,
|
||||
$1DB7106,$98D220BC,$EFD5102A,$71B18589,$6B6B51F,
|
||||
$9FBFE4A5,$E8B8D433,$7807C9A2,$F00F934,$9609A88E,
|
||||
$E10E9818,$7F6A0DBB,$86D3D2D,$91646C97,$E6635C01,
|
||||
$6B6B51F4,$1C6C6162,$856530D8,$F262004E,$6C0695ED,
|
||||
$1B01A57B,$8208F4C1,$F50FC457,$65B0D9C6,$12B7E950,
|
||||
$8BBEB8EA,$FCB9887C,$62DD1DDF,$15DA2D49,$8CD37CF3,
|
||||
$FBD44C65,$4DB26158,$3AB551CE,$A3BC0074,$D4BB30E2,
|
||||
$4ADFA541,$3DD895D7,$A4D1C46D,$D3D6F4FB,$4369E96A,
|
||||
$346ED9FC,$AD678846,$DA60B8D0,$44042D73,$33031DE5,
|
||||
$AA0A4C5F,$DD0D7CC9,$5005713C,$270241AA,$BE0B1010,
|
||||
$C90C2086,$5768B525,$206F85B3,$B966D409,$CE61E49F,
|
||||
$5EDEF90E,$29D9C998,$B0D09822,$C7D7A8B4,$59B33D17,
|
||||
$2EB40D81,$B7BD5C3B,$C0BA6CAD,$EDB88320,$9ABFB3B6,
|
||||
$3B6E20C,$74B1D29A,$EAD54739,$9DD277AF,$4DB2615,
|
||||
$73DC1683,$E3630B12,$94643B84,$D6D6A3E,$7A6A5AA8,
|
||||
$E40ECF0B,$9309FF9D,$A00AE27,$7D079EB1,$F00F9344,
|
||||
$8708A3D2,$1E01F268,$6906C2FE,$F762575D,$806567CB,
|
||||
$196C3671,$6E6B06E7,$FED41B76,$89D32BE0,$10DA7A5A,
|
||||
$67DD4ACC,$F9B9DF6F,$8EBEEFF9,$17B7BE43,$60B08ED5,
|
||||
$D6D6A3E8,$A1D1937E,$38D8C2C4,$4FDFF252,$D1BB67F1,
|
||||
$A6BC5767,$3FB506DD,$48B2364B,$D80D2BDA,$AF0A1B4C,
|
||||
$36034AF6,$41047A60,$DF60EFC3,$A867DF55,$316E8EEF,
|
||||
$4669BE79,$CB61B38C,$BC66831A,$256FD2A0,$5268E236,
|
||||
$CC0C7795,$BB0B4703,$220216B9,$5505262F,$C5BA3BBE,
|
||||
$B2BD0B28,$2BB45A92,$5CB36A04,$C2D7FFA7,$B5D0CF31,
|
||||
$2CD99E8B,$5BDEAE1D,$9B64C2B0,$EC63F226,$756AA39C,
|
||||
$26D930A,$9C0906A9,$EB0E363F,$72076785,$5005713,
|
||||
$95BF4A82,$E2B87A14,$7BB12BAE,$CB61B38,$92D28E9B,
|
||||
$E5D5BE0D,$7CDCEFB7,$BDBDF21,$86D3D2D4,$F1D4E242,
|
||||
$68DDB3F8,$1FDA836E,$81BE16CD,$F6B9265B,$6FB077E1,
|
||||
$18B74777,$88085AE6,$FF0F6A70,$66063BCA,$11010B5C,
|
||||
$8F659EFF,$F862AE69,$616BFFD3,$166CCF45,$A00AE278,
|
||||
$D70DD2EE,$4E048354,$3903B3C2,$A7672661,$D06016F7,
|
||||
$4969474D,$3E6E77DB,$AED16A4A,$D9D65ADC,$40DF0B66,
|
||||
$37D83BF0,$A9BCAE53,$DEBB9EC5,$47B2CF7F,$30B5FFE9,
|
||||
$BDBDF21C,$CABAC28A,$53B39330,$24B4A3A6,$BAD03605,
|
||||
$CDD70693,$54DE5729,$23D967BF,$B3667A2E,$C4614AB8,
|
||||
$5D681B02,$2A6F2B94,$B40BBE37,$C30C8EA1,$5A05DF1B,
|
||||
$2D02EF8D}
|
||||
|
||||
function FileCRC(fs: TFileStream): Integer;
|
||||
|
||||
implementation
|
||||
|
||||
function FileCRC(fs: TFileStream): Integer;
|
||||
const
|
||||
CRCBlock = 4096;
|
||||
var
|
||||
CRCTable: Array[0..255] of LongWord;
|
||||
c: LongWord; //!!! this must be an unsigned 32-bits var!
|
||||
Block: Array[0..CRCBlock-1] of Byte;
|
||||
i,j,bytesread: Integer;
|
||||
begin
|
||||
//this used to be the InitCRC procedure
|
||||
For i:= 0 To 255 do begin
|
||||
c:= i;
|
||||
For j:= 0 To 7 do begin
|
||||
If (c And 1)=0 Then begin
|
||||
c:= (c div 2);
|
||||
end Else begin
|
||||
c:= (c div 2) Xor $EDB88320;
|
||||
End;
|
||||
end;
|
||||
CRCTable[i]:= c;
|
||||
end;
|
||||
// InitCRC procedure end;
|
||||
c:=$FFFFFFFF;
|
||||
fs.Seek(0,soFromBeginning);
|
||||
for i:=0 to (fs.Size div CRCBlock)+1 do begin
|
||||
bytesread:=fs.Read(Block,CRCBlock);
|
||||
for j:=0 to bytesread-1 do begin
|
||||
c:=CRCTable[(c and $FF) xor Block[j]] xor (((c and $FFFFFF00) div 256) and $FFFFFF);
|
||||
end;
|
||||
end;
|
||||
FileCRC:=c xor $FFFFFFFF;
|
||||
end;
|
||||
|
||||
end.
|
Loading…
Add table
Add a link
Reference in a new issue