diff --git a/Contrib/VPatch/GenPat.exe b/Contrib/VPatch/GenPat.exe index 0b361388..a52d6b75 100644 Binary files a/Contrib/VPatch/GenPat.exe and b/Contrib/VPatch/GenPat.exe differ diff --git a/Contrib/VPatch/Readme.html b/Contrib/VPatch/Readme.html index 372683a6..ad194d61 100644 --- a/Contrib/VPatch/Readme.html +++ b/Contrib/VPatch/Readme.html @@ -98,87 +98,161 @@ a:hover
- - + + + +
-

VPatch 2

-
-

Introduction

-

VPatch allows to create a patch file to update previous versions of your software. The GenPat -utitily generates the patch file. The plug-in can use the patch to update a file. Using a patch, -you can reduce the download size of your updates, because only the differences between the files -are included in the patch file.

-

How to use

-

Generate the patch file

-

Make sure you have the source file (original version) and the target file (version to update -to). For example, DATA.DTA (currently on user system) and DATA_20.DTA (version 2.0 of this data -file). Now call the command line tool GenPat.exe:

-
+    
+

VPatch 2

+
+

Introduction

+

VPatch allows to create a patch file to update previous versions + of your software. The GenPat utitily generates the patch file. The + plug-in can use the patch to update a file. Using a patch, you can + reduce the download size of your updates, because only the differences + between the files are included in the patch file.

+

How to use

+

Generate the patch file

+

Make sure you have the source file (original version) and the target + file (version to update to). For example, DATA.DTA (currently on user + system) and DATA_20.DTA (version 2.0 of this data file). Now call + the command line tool GenPat.exe:

+
 GENPAT data.dta data_20.dta data.pat
 
-

Now, the patch will be generated, this will take some time.

-

Using the /B=(BlockSize) parameter of the GenPat utility (put it after the filenames), you can -use a different block size. A smaller block size may result in a smaller patch, but the generation -will take more time (the default blocksize is 64).

-

Update the file during installation

-

Use the VPatch plug-in to update a file using a patch file:

-
+          

Now, the patch will be generated, this will take some time.

+

Using the /B=(BlockSize) parameter of the GenPat utility (put it + after the filenames), you can use a different block size. A smaller + block size may result in a smaller patch, but the generation will + take more time (the default blocksize is 64).

+

Update the file during installation

+

Use the VPatch plug-in to update a file using a patch file:

+
 vpatch::vpatchfile "pathfile.pat" "source.file" "new.file"
 
-

The result of the patch operating will be added to the stack and can be one of the following -texts:

-
    -
  • OK
  • -
  • OK, new version already installed
  • -
  • An error occured while patching
  • -
  • Patch data is invalid or corrupt
  • -
  • No suitable patches were found
  • -
-

Check example.nsi for an example.

-

Multiple patches in one file

-

GenPat appends a patch to the file you specified. If there is already a patch for the same -orginal file in the patch file, the patch will be replaced. For example, if you want to be able to -upgrade version 1 and 2 to version 3, you can put a 1 > 3 and 2 > 3 patch in one file.

-

You can also put patches for different files in one patch file, for example, a patch from file A -version 1 to file A version 2 and a patch from file B version 1 to file B version 2. Just call the -plug-in multiple times with the same patch file. It will automatically select the right patch -(based on the file CRC).

-

Source code

-

NSIS plug-in (C++)

-

The source of the NSIS plug-in that applies patches can be found in the Source\Plugin -folder.

-

Patch Generator (Delphi)

-

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.

-

User interface (Delphi)

-

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 The result of the patch operating will be added to the stack and + can be one of the following texts:

+
    +
  • OK
  • +
  • OK, new version already installed
  • +
  • An error occured while patching
  • +
  • Patch data is invalid or corrupt
  • +
  • No suitable patches were found
  • +
+

Check example.nsi for an example.

+

Multiple patches in one file

+

GenPat appends a patch to the file you specified. If there is already + a patch for the same orginal file in the patch file, the patch will + be replaced. For example, if you want to be able to upgrade version + 1 and 2 to version 3, you can put a 1 > 3 and 2 > 3 patch in + one file.

+

You can also put patches for different files in one patch file, for + example, a patch from file A version 1 to file A version 2 and a patch + from file B version 1 to file B version 2. Just call the plug-in multiple + times with the same patch file. It will automatically select the right + patch (based on the file CRC).

+

GenPat exit codes

+

In version 2.1 support was added for exit codes (known as error levels + in the DOS period) to GenPat. GenPat will return an exit code based + on succes of the patch generation. Here is a list of the possible + exit codes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Exit codeDescription
0Success
1Arguments missing
2Source file not found
3Target file not found
4Unknown error while reading existing patch file
5Unknown error while generating patch
6Unknown error while writing patch file to disk
10CRC of source and target file are equal
11Not enough memory for source file
12Not enough memory for target file
+

These exit codes can be useful when you generate patch files through + a script.

+
+
+

Source code

+

NSIS plug-in (C++)

+

The source of the NSIS plug-in that applies patches can be found + in the Source\Plugin folder.

+

Patch Generator (Delphi)

+

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.

+

User interface (Delphi)

+

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 VirtualTreeView component by Mike Lischke.

-

Version history

-
    -
  • 2.0 final -
      -
    • Cleaned up source code for the patch generator, which is now included (this code is written in -Borland Delphi 6 and compiles with the freely available Personal edition).
    • -
    -
  • -
  • 2.0 beta 2 -
      -
    • All new algorithm used in the patch generator: much faster (up to 90%) while using smaller -block sizes (higher compression)
    • -
    • Created a NSIS 2 plugin
    • -
    • Works with small files
    • -
    • Replaces existing patch in file if original file CRC is identical
    • -
    -
  • -
-

Credits

-

Written by Koen van de Sande
-C plug-in by Edgewize
-New documentation and example by Joost Verburg

-

License

-
+          

Version history

+
    +
  • 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.
    • +
    +
  • +
  • 2.0 final +
      +
    • Cleaned up source code for the patch generator, which is now + included (this code is written in Borland Delphi 6 and compiles + with the freely available Personal edition).
    • +
    +
  • +
  • 2.0 beta 2 +
      +
    • All new algorithm used in the patch generator: much faster + (up to 90%) while using smaller block sizes (higher compression)
    • +
    • Created a NSIS 2 plugin
    • +
    • Works with small files
    • +
    • Replaces existing patch in file if original file CRC is identical
    • +
    +
  • +
+

Credits

+

Written by Koen van de Sande
+ C plug-in by Edgewize
+ New documentation and example by Joost Verburg

+

License

+
 Copyright (C) 2001-2003 Koen van de Sande
 
 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,
    and must not be misrepresented as being the original software.
 3. This notice may not be removed or altered from any distribution.
-
-
diff --git a/Contrib/VPatch/Source/GenPat/GenPat2.dpr b/Contrib/VPatch/Source/GenPat/GenPat2.dpr index d2a5bbb6..d868588b 100644 --- a/Contrib/VPatch/Source/GenPat/GenPat2.dpr +++ b/Contrib/VPatch/Source/GenPat/GenPat2.dpr @@ -11,6 +11,9 @@ program GenPat2; 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 } @@ -38,18 +41,20 @@ type var Config: TextFile; T1,T2: TDateTime; - d: Integer; + d, i: Integer; S,Key: String; - ShowDebug: Boolean; + SourceFile, TargetFile, PatchFile: String; + ShowDebug, ShowHelp: Boolean; PG: TPatchGenerator; EV: TEventHandler; + begin EV:=TEventHandler.Create; PG:=TPatchGenerator.Create; PG.StartBlockSize:=64; - WriteLn('GenPat v2.0 final'); - WriteLn('================='); + WriteLn('GenPat v2.1'); + WriteLn('==========='); WriteLn; WriteLn('(c) 2001-2003 Van de Sande Productions'); WriteLn('Website: http://www.tibed.net/vpatch'); @@ -75,13 +80,35 @@ begin CloseFile(Config); end; {$ENDIF} + i:=0; 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 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; - 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('With this patchfile, you can convert a (sourcefile) into (targetfile).'); WriteLn; @@ -90,36 +117,110 @@ begin 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('/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('Note: all these parameters must be *after* the filenames!'); WriteLn; - Write('Press a enter to exit '); - ReadLn(S); + WriteLn('Note: filenames should never start with / character!'); + 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; end; - if FileExists(ParamStr(3)) then begin - WriteLn('Using existing file to include patches in: '+ParamStr(3)); - PG.LoadFromFile(ParamStr(3)); + // stop if file error, result shown above + if not FileExists(SourceFile) then begin + WriteLn('Error: Source file not found'); + PG.Free; + ExitCode:=2; + Exit; 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; - 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} + // create patch file, with error handling + try + i:=PG.CreatePatch(SourceFile,TargetFile); + except + on E: Exception do begin + WriteLn('Error: Generating patch failed'); + WriteLn('Error message: ', E.ClassName, ': ', E.Message); + PG.Free; + ExitCode:=5; + Exit; + 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; end. diff --git a/Contrib/VPatch/Source/GenPat/PatchGenerator.pas b/Contrib/VPatch/Source/GenPat/PatchGenerator.pas index c4e99ce9..53745357 100644 --- a/Contrib/VPatch/Source/GenPat/PatchGenerator.pas +++ b/Contrib/VPatch/Source/GenPat/PatchGenerator.pas @@ -32,6 +32,8 @@ unit PatchGenerator; 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 } @@ -141,8 +143,8 @@ var retTO: Integer; noN: Integer; begin - fsSource:=TFileStream.Create(SourceFileName,fmOpenRead); - fsTarget:=TFileStream.Create(TargetFileName,fmOpenRead); + fsSource:=TFileStream.Create(SourceFileName,fmOpenRead+fmShareDenyNone); + fsTarget:=TFileStream.Create(TargetFileName,fmOpenRead+fmShareDenyNone); fm:=TMemoryStream.Create; SetLength(PRay,INIT_BLOCK_COUNT); @@ -150,7 +152,14 @@ begin //Load those files into memory! 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); Move(fm.Memory^,Source^,SourceSize); SourceCRC:=FileCRC(fsSource); @@ -158,13 +167,28 @@ begin fm.Clear; 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); Move(fm.Memory^,Target^,TargetSize); TargetCRC:=FileCRC(fsTarget); fsTarget.Free; fm.Free; + if(SourceCRC = TargetCRC) then begin + FreeMem(Source,SourceSize); + FreeMem(Target,TargetSize); + Result:=-1; + Exit; + end; + PRay[0].TargetOffset:=0; PRay[0].SourceOffset:=0; PRay[0].Size:=0; diff --git a/Contrib/VPatch/Source/GenPat/TreeCode.pas b/Contrib/VPatch/Source/GenPat/TreeCode.pas index 3d6c5f62..29e5cd37 100644 --- a/Contrib/VPatch/Source/GenPat/TreeCode.pas +++ b/Contrib/VPatch/Source/GenPat/TreeCode.pas @@ -15,6 +15,8 @@ unit TreeCode; 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 } @@ -179,6 +181,10 @@ var lo,mid,hi,m: Integer; tmp: Cardinal; begin + if not Assigned(ABlockTree) then begin + FoundCount:=0; Result:=nil; + Exit; + end; lo:=0; hi:=ABlockTreeNodeCount-1; while true do begin