⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 patchclasses.pas

📁 nsis是一个流传比较广的程序安装和解安装封装软件
💻 PAS
字号:
unit PatchClasses;interfaceuses Classes, sysutils, VDSP_CRC, DLLWrapper, Dialogs;const  DEFAULT_CONFIG = '64';type  TAbstractFile = record    FileName: String;    FriendlyName: String;    CRC32: LongWord;  //the longword/integer sign is going to give problems again...    Size: Integer;    //not sure about this one yet...    Cached: Boolean;  //True: we have cached the patch, using latest config                      //False: a) we have nothing cached (size and start are -1)                      //       b) we still have cache (start>0 and size too), but it's not generated using the latest config (we can keep it of course because the new config might be worse)       Cache: TMemoryStream;  end;  TPatchFile = class (TObject)  private    FIndex: Integer;    ConfigID: String;    FNew: TAbstractFile;    FOld: Array of TAbstractFile;  protected    procedure SetNewFN(Value: String);    function GetNewFN: String;    procedure SetOldFN(i: Integer; FileName: String);    function GetOldFN(Index: Integer): String;    function GetOldVersionCount: Integer;    procedure ResetCache; overload;    procedure ResetCache(OldIndex: Integer); overload;    procedure InvalidateCache; overload;    procedure InvalidateCache(Index: Integer); overload;    function GetCached(Index: Integer): Boolean;    function GetConfig: String;    procedure SetConfig(Value: String);  public    constructor Create(Index: Integer; FileName: String); overload;    constructor Create(Index: Integer; Stream: TStream); overload;    destructor Destroy(); override;    procedure AddOldVersion(const FileName: String);    procedure RemoveOldVersion(const Index: Integer);    property OldVersions[Index: Integer]: String read GetOldFN write SetOldFN;    procedure Generate; overload;    procedure Generate(const Index: Integer); overload;    property Generated[Index: Integer]: Boolean read GetCached;    function GetPatchSize(Index: Integer): Integer;    procedure WritePatch(Index: Integer; Stream: TStream);//  LoadFromStream not supported: Use Create(Index,Stream) instead!//    procedure LoadFromStream(Stream: TStream);    procedure SaveToStream(Stream: TStream);  published    property NewVersion: String read GetNewFN write SetNewFN;    property OldVersionCount: Integer read GetOldVersionCount;    property Index: Integer read FIndex;    property Config: String read GetConfig write SetConfig;  end;  TPatchProject = class (TObject)  private    FPat: Array of TPatchFile;  public    procedure LoadFromStream(Stream: TStream);    procedure SaveToStream(Stream: TStream);    constructor Create();    destructor Destroy(); override;    procedure AddNewVersion(FileName: String);    function PatchFile(FileName: String): TPatchFile; overload;    function PatchFile(Index: Integer): TPatchFile; overload;    function GetPatchCount: Integer;    procedure WritePatches(Stream: TStream);    procedure Generate;    procedure ResetCache;  end;implementation  function ReadStreamString(Stream: TStream): String;  var    Buf: Array[0..512] of Char;    i: LongInt;    S: String;    j: Integer;  begin    Stream.Read(i,SizeOf(i));    if i>512 then raise Exception.Create('VPJ damaged: String too long (>512)');    Stream.Read(Buf,i);    for j:=1 to i do      S:=S+Buf[j-1];    ReadStreamString:=S;  end;//a private wrapper for the FileCRC functionfunction CalcCRC(FileName: String): Integer;var  fs: TFileStream;begin  CalcCRC:=0;  fs:=nil;  try    fs:=TFileStream.Create(FileName,fmOpenRead);    CalcCRC:=FileCRC(fs);  finally    fs.Free;  end;end;function GetFileSize(FileName: String): Integer;var  fs: TFileStream;begin  GetFileSize:=0;  fs:=nil;  try    fs:=TFileStream.Create(FileName,fmOpenRead);    GetFileSize:=fs.Size;  finally    fs.Free;  end;end;{ TPatchFile }procedure TPatchFile.AddOldVersion(const FileName: String);var  i: Integer;//  fs: TFileStream;begin  i:=Length(FOld);  SetLength(FOld,i+1);  FOld[i].Cache:=TMemoryStream.Create;  SetOldFN(i,FileName);end;constructor TPatchFile.Create(Index: Integer; FileName: String);//var//  fs: TFileStream;begin  inherited Create();  FIndex:=Index;  SetLength(FOld,0);  FNew.CRC32:=0;  FNew.Size:=-1;  SetNewFN(FileName);  ConfigID:=DEFAULT_CONFIG;   //just to be on the safe side  //following is now done by SetNewFN :)  //no it's not - because that one resets the cache!!!  //doesn't matter, because we're not loading from stream!!!{  FNew.FileName:=FileName;  FNew.FriendlyName:=ExtractFileName(FileName);  FNew.CRC32:=CalcCRC(FileName);  FNew.Size:=GetFileSize(FileName);}end;constructor TPatchFile.Create(Index: Integer; Stream: TStream);var  i,q: LongInt;  CSize: Integer;  j: Integer;begin  inherited Create();  FIndex:=Index;  SetLength(FOld,0);  FNew.CRC32:=0;  FNew.Size:=-1; //just to be on the safe side  //read configuration  ConfigID:=ReadStreamString(Stream);  //now load everything...  FNew.FileName:=ReadStreamString(Stream);  FNew.FriendlyName:=ReadStreamString(Stream);  Stream.Read(FNew.CRC32,SizeOf(FNew.CRC32));  Stream.Read(FNew.Size,SizeOf(FNew.Size));  Stream.Read(i,SizeOf(i));  SetLength(FOld,i);  for j:=0 to i - 1 do begin    FOld[j].FileName:=ReadStreamString(Stream);    FOld[j].FriendlyName:=ReadStreamString(Stream);    Stream.Read(FOld[j].CRC32,SizeOf(FOld[j].CRC32));    Stream.Read(FOld[j].Size,SizeOf(FOld[j].Size));    Stream.Read(q,SizeOf(q));    FOld[j].Cached:=not (q=0);    if FOld[j].Cached then begin      Stream.Read(CSize,SizeOf(CSize));      FOld[j].Cache:=TMemoryStream.Create;      FOld[j].Cache.CopyFrom(Stream,CSize);    end;  end;end;destructor TPatchFile.Destroy;begin  SetLength(FOld,0);  inherited;end;function TPatchFile.GetNewFN: String;begin  GetNewFN:=FNew.FileName;end;function TPatchFile.GetOldFN(Index: Integer): String;begin  Result:=FOld[Index].FileName;  if FOld[Index].Cached then    if FOld[Index].Cache.Size>0 then begin      Result:=Result + ' ('+IntToStr(FOld[Index].Cache.Size)+' bytes to patch)';    end;end;function TPatchFile.GetOldVersionCount: Integer;begin  GetOldVersionCount:=Length(FOld);end;procedure TPatchFile.ResetCache;var  i: Integer;begin  for i:=0 to Length(FOld)-1 do    ResetCache(i);end;procedure TPatchFile.RemoveOldVersion(const Index: Integer);var  i: Integer;begin  FOld[Index].Cache.Free;  for i:=Index to Length(FOld)-2 do begin    FOld[i]:=FOld[i+1];  end;  SetLength(FOld,Length(FOld)-1);end;procedure TPatchFile.ResetCache(OldIndex: Integer);begin  FOld[OldIndex].Cached:=False;  FOld[OldIndex].Size:=-1;  FOld[OldIndex].Cache.Clear;end;procedure TPatchFile.SaveToStream(Stream: TStream);  procedure WriteStreamString(Stream: TStream; const S: String);  var    i: LongInt;    j: Integer;    Buf: Array[0..512] of Char;  begin    i:=Length(S);    Stream.Write(i,SizeOf(i));    for j:=1 to i do      Buf[j-1]:=S[j];    Buf[i]:=#0;    Stream.Write(Buf,i);  end;var  i,q: LongInt;  j: Integer;  tmp: Integer;begin  //write config ID  WriteStreamString(Stream,ConfigID);  WriteStreamString(Stream,FNew.FileName);  WriteStreamString(Stream,FNew.FriendlyName);  Stream.Write(FNew.CRC32,SizeOf(FNew.CRC32));  Stream.Write(FNew.Size,SizeOf(FNew.Size));  i:=Length(FOld);  Stream.Write(i,SizeOf(i));  for j:=0 to i - 1 do begin    WriteStreamString(Stream,FOld[j].FileName);    WriteStreamString(Stream,FOld[j].FriendlyName);    Stream.Write(FOld[j].CRC32,SizeOf(FOld[j].CRC32));    Stream.Write(FOld[j].Size,SizeOf(FOld[j].Size));    if FOld[j].Cached then q:=1 else q:=0;    Stream.Write(q,SizeOf(q));    if FOld[j].Cached then begin      tmp:=FOld[j].Cache.Size;      Stream.Write(tmp,SizeOf(tmp));      FOld[j].Cache.Seek(0,soFromBeginning);      Stream.CopyFrom(FOld[j].Cache,tmp);    end;  end;end;procedure TPatchFile.SetNewFN(Value: String);var  NewSize: Integer;  NewCRC: LongWord;begin  FNew.FileName:=Value;  FNew.Friendlyname:=ExtractFileName(Value);  NewCRC:=CalcCRC(Value);  NewSize:=GetFileSize(Value);  //if any changes, then reset cache :)  if not ((FNew.CRC32=NewCRC) and (FNew.Size=NewSize)) then begin    FNew.CRC32:=NewCRC;    FNew.Size:=NewSize;    ResetCache;  end;end;procedure TPatchFile.SetOldFN(i: Integer; FileName: String);begin  if((i>=0) and (i<Length(FOld))) then begin    FOld[i].FileName:=FileName;    FOld[i].FriendlyName:=ExtractFileName(FileName);    FOld[i].CRC32:=CalcCRC(FileName);    FOld[i].Size:=GetFileSize(FileName);    ResetCache(i);  end;end;procedure TPatchFile.Generate;var  i: Integer;begin  //generate all of them into the cache?  for i:=0 to OldVersionCount - 1 do    Generate(i);end;procedure TPatchFile.Generate(const Index: Integer);var  Size: Integer;  fm: TMemoryStream;begin  fm:=TMemoryStream.Create;  Size:=DoGenerate(FOld[Index].FileName,FNew.FileName,fm,ConfigID);  if not (Size=-1) then begin    if (FOld[Index].Cache.Size>Size) or (not FOld[Index].Cached) then begin //the new one is better      FOld[Index].Cache.Clear;      fm.Seek(8,soFromBeginning);      FOld[Index].Cache.CopyFrom(fm,fm.Size-8);    end;    FOld[Index].Cached:=True;  end;  fm.Free;end;function TPatchFile.GetCached(Index: Integer): Boolean;begin  GetCached:=FOld[Index].Cached;end;function TPatchFile.GetConfig: String;begin  GetConfig:=ConfigID;end;procedure TPatchFile.SetConfig(Value: String);begin  if not Assigned(Self) then Exit;  if not SameText(Value,ConfigID) then begin    InvalidateCache;  //configuration changed, invalidate cache  end;  ConfigID:=Value;end;function TPatchFile.GetPatchSize(Index: Integer): Integer;begin  if Generated[Index] then begin    GetPatchSize:=FOld[Index].Cache.Size;  end else    GetPatchSize:=-1;end;procedure TPatchFile.InvalidateCache;var  i: Integer;begin  for i:=0 to Length(FOld)-1 do    InvalidateCache(i);end;procedure TPatchFile.InvalidateCache(Index: Integer);begin  FOld[Index].Cached:=False;end;procedure TPatchFile.WritePatch(Index: Integer; Stream: TStream);begin  if not FOld[Index].Cached then    Generate(Index);  if not FOld[Index].Cached then    raise Exception.Create('Writing of patch failed: Could not generate all patches');  FOld[Index].Cache.Seek(0,soFromBeginning);  Stream.CopyFrom(FOld[Index].Cache,FOld[Index].Cache.Size);end;{ TPatchProject }procedure TPatchProject.AddNewVersion(FileName: String);var  i: Integer;begin  i:=Length(FPat);  SetLength(FPat,i+1);  FPat[i]:=TPatchFile.Create(i,FileName);  FPat[i].SetConfig(DEFAULT_CONFIG);end;constructor TPatchProject.Create;begin  inherited;  SetLength(FPat,0);end;destructor TPatchProject.Destroy;var  i: Integer;begin  for i:=0 to Length(FPat)-1 do begin    FPat[i].Free;  end;  SetLength(FPat,0);  inherited;end;procedure TPatchProject.Generate;var  i: Integer;begin  for i:=0 to GetPatchCount - 1 do    FPat[i].Generate;end;function TPatchProject.GetPatchCount: Integer;begin  GetPatchCount:=Length(FPat);end;procedure TPatchProject.LoadFromStream(Stream: TStream);var  MagicWord: Array[0..15] of Char;  i: LongInt;  j: Integer;begin  //first free all patchfiles  for j:=0 to Length(FPat)-1 do begin    FPat[j].Free;    FPat[j]:=nil;  end;  Stream.Read(MagicWord,SizeOf(MagicWord));  if SameText('VPatchProject 3'#26,MagicWord) then begin    Stream.Read(i,SizeOf(i));           //4 dummy bytes  end else    raise Exception.Create('Error: file format incompatible (only version 3 and newer are supported).');  Stream.Read(i,SizeOf(i));           // file count  SetLength(FPat,i);  for j:=0 to i - 1 do begin    FPat[j]:=TPatchFile.Create(j,Stream);  end;end;function TPatchProject.PatchFile(FileName: String): TPatchFile;var  i: Integer;begin  PatchFile:=nil;  for i:=0 to Length(FPat) - 1 do begin    if(CompareText(FPat[i].FNew.FileName,FileName)=0) then begin      PatchFile:=FPat[i];    end;  end;  for i:=0 to Length(FPat) - 1 do begin    if(CompareText(FPat[i].FNew.FriendlyName,FileName)=0) then begin      PatchFile:=FPat[i];    end;  end;end;function TPatchProject.PatchFile(Index: Integer): TPatchFile;begin  if (Index<Length(FPat)) and (Index>=0) then    PatchFile:=FPat[Index]  else    PatchFile:=nil;end;procedure TPatchProject.ResetCache;var  i: Integer;begin  for i:=0 to Pred(Length(FPat)) do    FPat[i].ResetCache;end;procedure TPatchProject.SaveToStream(Stream: TStream);var  HeadID: Array[0..15] of Char;  i: LongInt;  j: Integer;begin  HeadID:='VPatchProject 3'+#26;  Stream.Write(HeadID,SizeOf(HeadID));  //4 dummy bytes left  i:=0;  Stream.Write(i,SizeOf(i));  i:=Length(FPat);  Stream.Write(i,SizeOf(i));  for j:=0 to i - 1 do begin    FPat[j].SaveToStream(Stream);  end;end;procedure TPatchProject.WritePatches(Stream: TStream);var  i,j,k,o: LongInt;  q: LongWord;begin  k:=$54415056;  o:=Stream.Position;  Stream.Write(k,SizeOf(k));  k:=0;  Stream.Write(k,SizeOf(k));  k:=0;  for i:=0 to Length(FPat)-1 do begin    for j:=0 to FPat[i].GetOldVersionCount - 1 do begin      FPat[i].WritePatch(j,Stream);      Inc(k);    end;  end;  Stream.Seek(o+4,soFromBeginning);  q:=k;  // set the MD5 flag  q:=q or $80000000;  Stream.Write(q,SizeOf(q));  Stream.Seek(Stream.Size,soFromBeginning);  Stream.Write(o,SizeOf(o));end;end. 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -