VPatch 2.1:

* Added argument checking and error handling to GenPat. Now returns exit codes as well to indicate success/failure (and the reason for failure). Only GenPat has changed in this version compared to 2.0 final.
* Bug Fix: GenPat no longer gives an Access Violation when attempting to patch a file smaller than 64 bytes into a file larger than 64 bytes.


git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@3318 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
kichik 2003-12-27 18:36:57 +00:00
parent 14f2e625e4
commit b7c7a31b3e
5 changed files with 314 additions and 108 deletions

Binary file not shown.

View file

@ -98,87 +98,161 @@ a:hover
<body> <body>
<div class="center"> <div class="center">
<table width="750" class="maintable" cellspacing="0" cellpadding="0"> <table width="750" class="maintable" cellspacing="0" cellpadding="0">
<tr> <tr>
<td> <td>
<h1>VPatch 2</h1> <h1>VPatch 2</h1>
<div> <div>
<h2>Introduction</h2> <h2>Introduction</h2>
<p>VPatch allows to create a patch file to update previous versions of your software. The GenPat <p>VPatch allows to create a patch file to update previous versions
utitily generates the patch file. The plug-in can use the patch to update a file. Using a patch, of your software. The GenPat utitily generates the patch file. The
you can reduce the download size of your updates, because only the differences between the files plug-in can use the patch to update a file. Using a patch, you can
are included in the patch file.</p> reduce the download size of your updates, because only the differences
<h2>How to use</h2> between the files are included in the patch file.</p>
<h3>Generate the patch file</h3> <h2>How to use</h2>
<p>Make sure you have the source file (original version) and the target file (version to update <h3>Generate the patch file</h3>
to). For example, DATA.DTA (currently on user system) and DATA_20.DTA (version 2.0 of this data <p>Make sure you have the source file (original version) and the target
file). Now call the command line tool GenPat.exe:</p> file (version to update to). For example, DATA.DTA (currently on user
<pre> system) and DATA_20.DTA (version 2.0 of this data file). Now call
the command line tool GenPat.exe:</p>
<pre>
GENPAT data.dta data_20.dta data.pat GENPAT data.dta data_20.dta data.pat
</pre> </pre>
<p>Now, the patch will be generated, this will take some time.</p> <p>Now, the patch will be generated, this will take some time.</p>
<p>Using the /B=(BlockSize) parameter of the GenPat utility (put it after the filenames), you can <p>Using the /B=(BlockSize) parameter of the GenPat utility (put it
use a different block size. A smaller block size may result in a smaller patch, but the generation after the filenames), you can use a different block size. A smaller
will take more time (the default blocksize is 64).</p> block size may result in a smaller patch, but the generation will
<h3>Update the file during installation</h3> take more time (the default blocksize is 64).</p>
<p>Use the VPatch plug-in to update a file using a patch file:</p> <h3>Update the file during installation</h3>
<pre> <p>Use the VPatch plug-in to update a file using a patch file:</p>
<pre>
vpatch::vpatchfile "pathfile.pat" "source.file" "new.file" vpatch::vpatchfile "pathfile.pat" "source.file" "new.file"
</pre> </pre>
<p>The result of the patch operating will be added to the stack and can be one of the following <p>The result of the patch operating will be added to the stack and
texts:</p> can be one of the following texts:</p>
<ul> <ul>
<li>OK</li> <li>OK</li>
<li>OK, new version already installed</li> <li>OK, new version already installed</li>
<li>An error occured while patching</li> <li>An error occured while patching</li>
<li>Patch data is invalid or corrupt</li> <li>Patch data is invalid or corrupt</li>
<li>No suitable patches were found</li> <li>No suitable patches were found</li>
</ul> </ul>
<p>Check <a href="example.nsi">example.nsi</a> for an example.</p> <p>Check <a href="example.nsi">example.nsi</a> for an example.</p>
<h3>Multiple patches in one file</h3> <h3>Multiple patches in one file</h3>
<p>GenPat appends a patch to the file you specified. If there is already a patch for the same <p>GenPat appends a patch to the file you specified. If there is already
orginal file in the patch file, the patch will be replaced. For example, if you want to be able to a patch for the same orginal file in the patch file, the patch will
upgrade version 1 and 2 to version 3, you can put a 1 &gt; 3 and 2 &gt; 3 patch in one file.</p> be replaced. For example, if you want to be able to upgrade version
<p>You can also put patches for different files in one patch file, for example, a patch from file A 1 and 2 to version 3, you can put a 1 &gt; 3 and 2 &gt; 3 patch in
version 1 to file A version 2 and a patch from file B version 1 to file B version 2. Just call the one file.</p>
plug-in multiple times with the same patch file. It will automatically select the right patch <p>You can also put patches for different files in one patch file, for
(based on the file CRC).</p> example, a patch from file A version 1 to file A version 2 and a patch
<h2>Source code</h2> from file B version 1 to file B version 2. Just call the plug-in multiple
<h3>NSIS plug-in (C++)</h3> times with the same patch file. It will automatically select the right
<p>The source of the NSIS plug-in that applies patches can be found in the Source\Plugin patch (based on the file CRC).</p>
folder.</p> <h3>GenPat exit codes</h3>
<h3>Patch Generator (Delphi)</h3> <p>In version 2.1 support was added for exit codes (known as error levels
<p>The most interesting part of VPatch, the actual patch generation algoritm, can be found in in the DOS period) to GenPat. GenPat will return an exit code based
Source\GenPat\PatchGenerator.pas. The header of that file contains a brief explanation of the on succes of the patch generation. Here is a list of the possible
algoritm as well.</p> exit codes:</p>
<h3>User interface (Delphi)</h3> <table width="547" border="0" cellspacing="0" cellpadding="0">
<p>A user interface is included as well, which you will have to build yourself because the GUI <tr>
executable was too large to include. Besides Borland Delphi 6 or higher (you can use the freely <td><b>Exit code</b></td>
available Personal edition), you will also need to install the <a href= <td><b>Description</b></td>
</tr>
<tr>
<td>0</td>
<td>Success</td>
</tr>
<tr>
<td>1</td>
<td>Arguments missing</td>
</tr>
<tr>
<td>2</td>
<td>Source file not found</td>
</tr>
<tr>
<td>3</td>
<td>Target file not found</td>
</tr>
<tr>
<td>4</td>
<td>Unknown error while reading existing patch file</td>
</tr>
<tr>
<td>5</td>
<td>Unknown error while generating patch</td>
</tr>
<tr>
<td>6</td>
<td>Unknown error while writing patch file to disk</td>
</tr>
<tr>
<td>10</td>
<td>CRC of source and target file are equal</td>
</tr>
<tr>
<td>11</td>
<td>Not enough memory for source file</td>
</tr>
<tr>
<td>12</td>
<td>Not enough memory for target file</td>
</tr>
</table>
<p>These exit codes can be useful when you generate patch files through
a script.</p>
</div>
<div>
<h2>Source code</h2>
<h3>NSIS plug-in (C++)</h3>
<p>The source of the NSIS plug-in that applies patches can be found
in the Source\Plugin folder.</p>
<h3>Patch Generator (Delphi)</h3>
<p>The most interesting part of VPatch, the actual patch generation
algoritm, can be found in Source\GenPat\PatchGenerator.pas. The header
of that file contains a brief explanation of the algoritm as well.</p>
<h3>User interface (Delphi)</h3>
<p>A user interface is included as well, which you will have to build
yourself because the GUI executable was too large to include. Besides
Borland Delphi 6 or higher (you can use the freely available Personal
edition), you will also need to install the <a href=
"http://www.delphi-gems.com">VirtualTreeView</a> component by Mike Lischke.</p> "http://www.delphi-gems.com">VirtualTreeView</a> component by Mike Lischke.</p>
<h2>Version history</h2> <h2>Version history</h2>
<ul> <ul>
<li>2.0 final <li>2.1
<ul> <ul>
<li>Cleaned up source code for the patch generator, which is now included (this code is written in <li>Added argument checking and error handling to GenPat. Now
Borland Delphi 6 and compiles with the freely available Personal edition).</li> returns exit codes as well to indicate success/failure (and
</ul> the reason for failure). Only GenPat has changed in this version
</li> compared to 2.0 final.</li>
<li>2.0 beta 2 <li>Bug Fix: GenPat no longer gives an Access Violation when attempting
<ul> to patch a file smaller than 64 bytes into a file larger than
<li>All new algorithm used in the patch generator: much faster (up to 90%) while using smaller 64 bytes.</li>
block sizes (higher compression)</li> </ul>
<li>Created a NSIS 2 plugin</li> </li>
<li>Works with small files</li> <li>2.0 final
<li>Replaces existing patch in file if original file CRC is identical</li> <ul>
</ul> <li>Cleaned up source code for the patch generator, which is now
</li> included (this code is written in Borland Delphi 6 and compiles
</ul> with the freely available Personal edition).</li>
<h2>Credits</h2> </ul>
<p>Written by Koen van de Sande<br /> </li>
C plug-in by Edgewize<br /> <li>2.0 beta 2
New documentation and example by Joost Verburg</p> <ul>
<h2>License</h2> <li>All new algorithm used in the patch generator: much faster
<pre> (up to 90%) while using smaller block sizes (higher compression)</li>
<li>Created a NSIS 2 plugin</li>
<li>Works with small files</li>
<li>Replaces existing patch in file if original file CRC is identical</li>
</ul>
</li>
</ul>
<h2>Credits</h2>
<p>Written by Koen van de Sande<br />
C plug-in by Edgewize<br />
New documentation and example by Joost Verburg</p>
<h2>License</h2>
<pre>
Copyright (C) 2001-2003 Koen van de Sande Copyright (C) 2001-2003 Koen van de Sande
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
@ -196,8 +270,9 @@ it freely, subject to the following restrictions:
2. Altered versions must be plainly marked as such, 2. Altered versions must be plainly marked as such,
and must not be misrepresented as being the original software. and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any distribution. 3. This notice may not be removed or altered from any distribution.
</pre></div> </pre>
</td> </div>
</td>
</tr> </tr>
</table> </table>
</div> </div>

View file

@ -11,6 +11,9 @@ program GenPat2;
What's new What's new
---------- ----------
2.1 20031219 Koen Added error checking, handling, shouldn't
crash when invalid arguments, returns
exit codes now.
2.0 20030811 Koen Initial documentation 2.0 20030811 Koen Initial documentation
} }
@ -38,18 +41,20 @@ type
var var
Config: TextFile; Config: TextFile;
T1,T2: TDateTime; T1,T2: TDateTime;
d: Integer; d, i: Integer;
S,Key: String; S,Key: String;
ShowDebug: Boolean; SourceFile, TargetFile, PatchFile: String;
ShowDebug, ShowHelp: Boolean;
PG: TPatchGenerator; PG: TPatchGenerator;
EV: TEventHandler; EV: TEventHandler;
begin begin
EV:=TEventHandler.Create; EV:=TEventHandler.Create;
PG:=TPatchGenerator.Create; PG:=TPatchGenerator.Create;
PG.StartBlockSize:=64; PG.StartBlockSize:=64;
WriteLn('GenPat v2.0 final'); WriteLn('GenPat v2.1');
WriteLn('================='); WriteLn('===========');
WriteLn; WriteLn;
WriteLn('(c) 2001-2003 Van de Sande Productions'); WriteLn('(c) 2001-2003 Van de Sande Productions');
WriteLn('Website: http://www.tibed.net/vpatch'); WriteLn('Website: http://www.tibed.net/vpatch');
@ -75,13 +80,35 @@ begin
CloseFile(Config); CloseFile(Config);
end; end;
{$ENDIF} {$ENDIF}
i:=0;
for d:=1 to ParamCount do begin for d:=1 to ParamCount do begin
if CompareStr(LowerCase(Copy(ParamStr(d),1,3)),'/b=')=0 then begin if CompareStr(LowerCase(Copy(ParamStr(d),1,3)),'/b=')=0 then begin
PG.StartBlockSize:=StrToInt(Copy(ParamStr(d),4,10)); PG.StartBlockSize:=StrToInt(Copy(ParamStr(d),4,10));
end else begin
// not a parameter?
if not (ParamStr(d)[1] = '/') then begin
if i = 2 then begin
PatchFile:=ParamStr(d);
Inc(i);
end;
if i = 1 then begin
TargetFile:=ParamStr(d);
Inc(i);
end;
if i = 0 then begin
SourceFile:=ParamStr(d);
Inc(i);
end;
end;
end; end;
end; end;
if (CompareStr(ParamStr(1),'')=0) or (CompareStr(ParamStr(2),'')=0) or (CompareStr(ParamStr(3),'')=0) then begin ShowHelp:=False;
if(CompareStr(PatchFile,'')=0) then ShowHelp:=True;
if SourceFile = '' then ShowHelp:=True;
if TargetFile = '' then ShowHelp:=True;
if ShowHelp then begin
WriteLn('This program will take (sourcefile) as input and create a (patchfile).'); WriteLn('This program will take (sourcefile) as input and create a (patchfile).');
WriteLn('With this patchfile, you can convert a (sourcefile) into (targetfile).'); WriteLn('With this patchfile, you can convert a (sourcefile) into (targetfile).');
WriteLn; WriteLn;
@ -90,36 +117,110 @@ begin
WriteLn; WriteLn;
WriteLn('Command line options (you do not need them):'); WriteLn('Command line options (you do not need them):');
WriteLn('/B=(BlockSize) Set blocksize (def=64), multiple of 2'); WriteLn('/B=(BlockSize) Set blocksize (def=64), multiple of 2');
WriteLn('/WAIT Wait for a keypress after program complete'); WriteLn('/NOEQUALERROR Exit code becomes 0 instead of 10 when');
WriteLn(' two files with equal CRC are encountered');
WriteLn(' (patch file will remain unchanged)');
WriteLn('/DEBUG Show runtime debug information'); WriteLn('/DEBUG Show runtime debug information');
WriteLn('Note: all these parameters must be *after* the filenames!');
WriteLn; WriteLn;
Write('Press a enter to exit '); WriteLn('Note: filenames should never start with / character!');
ReadLn(S); WriteLn;
WriteLn('Possible exit codes:');
WriteLn(' 0 Success');
WriteLn(' 1 Arguments missing');
WriteLn(' 2 Source file not found');
WriteLn(' 3 Target file not found');
WriteLn(' 4 Unknown error while reading existing patch file');
WriteLn(' 5 Unknown error while generating patch');
WriteLn(' 6 Unknown error while writing patch file to disk');
WriteLn(' 10 CRC of source and target file are equal (impossible with /NOEQUALERROR)');
WriteLn(' 11 Not enough memory for source file');
WriteLn(' 12 Not enough memory for target file');
PG.Free;
ExitCode:=1;
Exit; Exit;
end; end;
if FileExists(ParamStr(3)) then begin // stop if file error, result shown above
WriteLn('Using existing file to include patches in: '+ParamStr(3)); if not FileExists(SourceFile) then begin
PG.LoadFromFile(ParamStr(3)); WriteLn('Error: Source file not found');
PG.Free;
ExitCode:=2;
Exit;
end; end;
if not FileExists(TargetFile) then begin
WriteLn('Error: Target file not found');
PG.Free;
ExitCode:=3;
Exit;
end;
if FileExists(PatchFile) then begin
WriteLn('Using existing file to include patches in: '+PatchFile);
try
PG.LoadFromFile(PatchFile);
except
on E: Exception do begin
WriteLn('Error: Reading existing patch file failed');
WriteLn('Error message: ', E.ClassName, ': ', E.Message);
PG.Free;
ExitCode:=4;
Exit;
end;
end;
end;
WriteLn('Source (original) file: ', SourceFile);
WriteLn('Target (newer) file: ', TargetFile);
T1:=Now; T1:=Now;
WriteLn('Patch body size: '+IntToStr(PG.CreatePatch(ParamStr(1),ParamStr(2))));
PG.WriteToFile(ParamStr(3));
T2:=Now; // create patch file, with error handling
Write('GenPat.exe finished execution in: '); try
WriteLn(FloatToStr((T2-T1)*24*60*60),'s'); i:=PG.CreatePatch(SourceFile,TargetFile);
WriteLn; except
{$IFNDEF AUTOWAIT} on E: Exception do begin
if FindCmdLineSwitch('wait',['/'],True) then begin WriteLn('Error: Generating patch failed');
{$ENDIF} WriteLn('Error message: ', E.ClassName, ': ', E.Message);
WriteLn; PG.Free;
WriteLn('Press a key'); ExitCode:=5;
ReadLn(S); Exit;
{$IFNDEF AUTOWAIT} end;
end; end;
{$ENDIF}
if(i < 0) then begin
if(i = -1) then begin
if not FindCmdLineSwitch('noequalerror',['/'],True) then
WriteLn('Error: CRC of source and target file are equal');
end;
if(i = -2) then WriteLn('Error: Not enough memory for source file');
if(i = -3) then WriteLn('Error: Not enough memory for target file');
ExitCode:=9 - i;
if(i = -1) and (FindCmdLineSwitch('noequalerror',['/'],True)) then begin
WriteLn('Equal CRCs ignored (no patch will be written and exit code is 0)');
ExitCode:=0;
end;
end else begin
WriteLn('Patch body size: '+IntToStr(i));
try
PG.WriteToFile(PatchFile);
except
on E: Exception do begin
WriteLn('Error: Writing patch to file ' + PatchFile + ' failed');
WriteLn('Error message: ', E.ClassName, ': ', E.Message);
PG.Free;
ExitCode:=6;
Exit;
end;
end;
T2:=Now;
Write('Time taken for generation: ');
WriteLn(FloatToStr((T2-T1)*24*60*60),'s');
WriteLn;
ExitCode:=0;
end;
PG.Free; PG.Free;
end. end.

View file

@ -32,6 +32,8 @@ unit PatchGenerator;
What's new What's new
---------- ----------
2.1 20031219 Koen Added error checking to CreatePatch, returns
negative numbers when there are errors.
2.0 20030811 Koen Initial documentation 2.0 20030811 Koen Initial documentation
} }
@ -141,8 +143,8 @@ var
retTO: Integer; retTO: Integer;
noN: Integer; noN: Integer;
begin begin
fsSource:=TFileStream.Create(SourceFileName,fmOpenRead); fsSource:=TFileStream.Create(SourceFileName,fmOpenRead+fmShareDenyNone);
fsTarget:=TFileStream.Create(TargetFileName,fmOpenRead); fsTarget:=TFileStream.Create(TargetFileName,fmOpenRead+fmShareDenyNone);
fm:=TMemoryStream.Create; fm:=TMemoryStream.Create;
SetLength(PRay,INIT_BLOCK_COUNT); SetLength(PRay,INIT_BLOCK_COUNT);
@ -150,7 +152,14 @@ begin
//Load those files into memory! //Load those files into memory!
SourceSize:=fsSource.Size; SourceSize:=fsSource.Size;
GetMem(Source,SourceSize); try
GetMem(Source,SourceSize);
except
on EOutOfMemory do begin
Result:=-2; // not enough memory for source file
Exit;
end;
end;
fm.CopyFrom(fsSource,SourceSize); fm.CopyFrom(fsSource,SourceSize);
Move(fm.Memory^,Source^,SourceSize); Move(fm.Memory^,Source^,SourceSize);
SourceCRC:=FileCRC(fsSource); SourceCRC:=FileCRC(fsSource);
@ -158,13 +167,28 @@ begin
fm.Clear; fm.Clear;
TargetSize:=fsTarget.Size; TargetSize:=fsTarget.Size;
GetMem(Target,TargetSize); try
GetMem(Target,TargetSize);
except
on EOutOfMemory do begin
FreeMem(Source,SourceSize);
Result:=-3; // not enough memory for target file
Exit;
end;
end;
fm.CopyFrom(fsTarget,TargetSize); fm.CopyFrom(fsTarget,TargetSize);
Move(fm.Memory^,Target^,TargetSize); Move(fm.Memory^,Target^,TargetSize);
TargetCRC:=FileCRC(fsTarget); TargetCRC:=FileCRC(fsTarget);
fsTarget.Free; fsTarget.Free;
fm.Free; fm.Free;
if(SourceCRC = TargetCRC) then begin
FreeMem(Source,SourceSize);
FreeMem(Target,TargetSize);
Result:=-1;
Exit;
end;
PRay[0].TargetOffset:=0; PRay[0].TargetOffset:=0;
PRay[0].SourceOffset:=0; PRay[0].SourceOffset:=0;
PRay[0].Size:=0; PRay[0].Size:=0;

View file

@ -15,6 +15,8 @@ unit TreeCode;
What's new What's new
---------- ----------
2.1 20031219 Koen Fixed bug in TreeFind: when tree was a nil
pointer, now returns instead of AVing
2.0 20030811 Koen Initial documentation 2.0 20030811 Koen Initial documentation
} }
@ -179,6 +181,10 @@ var
lo,mid,hi,m: Integer; lo,mid,hi,m: Integer;
tmp: Cardinal; tmp: Cardinal;
begin begin
if not Assigned(ABlockTree) then begin
FoundCount:=0; Result:=nil;
Exit;
end;
lo:=0; lo:=0;
hi:=ABlockTreeNodeCount-1; hi:=ABlockTreeNodeCount-1;
while true do begin while true do begin