home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 March / CMCD0304.ISO / Software / Freeware / Programare / nullsoft / nsis20.exe / Contrib / VPatch / Source / GUI / PatchClasses.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2003-08-11  |  13.6 KB  |  548 lines

  1. unit PatchClasses;
  2.  
  3. interface
  4.  
  5. uses Classes, sysutils, VDSP_CRC, DLLWrapper, Dialogs;
  6.  
  7. const
  8.   DEFAULT_CONFIG = '64,64,2,32';
  9.  
  10. type
  11.   TAbstractFile = record
  12.     FileName: String;
  13.     FriendlyName: String;
  14.     CRC32: LongWord;  //the longword/integer sign is going to give problems again...
  15.     Size: Integer;
  16.     //not sure about this one yet...
  17.     Cached: Boolean;  //True: we have cached the patch, using latest config
  18.                       //False: a) we have nothing cached (size and start are -1)
  19.                       //       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)   
  20.     Cache: TMemoryStream;
  21.   end;
  22.  
  23.   TPatchFile = class (TObject)
  24.   private
  25.     FIndex: Integer;
  26.     ConfigID: String;
  27.     FNew: TAbstractFile;
  28.     FOld: Array of TAbstractFile;
  29.   protected
  30.     procedure SetNewFN(Value: String);
  31.     function GetNewFN: String;
  32.     procedure SetOldFN(i: Integer; FileName: String);
  33.     function GetOldFN(Index: Integer): String;
  34.     function GetOldVersionCount: Integer;
  35.     procedure ResetCache; overload;
  36.     procedure ResetCache(OldIndex: Integer); overload;
  37.     procedure InvalidateCache; overload;
  38.     procedure InvalidateCache(Index: Integer); overload;
  39.     function GetCached(Index: Integer): Boolean;
  40.     function GetConfig: String;
  41.     procedure SetConfig(Value: String);
  42.   public
  43.     constructor Create(Index: Integer; FileName: String); overload;
  44.     constructor Create(Index: Integer; Stream: TStream); overload;
  45.     destructor Destroy(); override;
  46.  
  47.     procedure AddOldVersion(const FileName: String);
  48.     procedure RemoveOldVersion(const Index: Integer);
  49.     property OldVersions[Index: Integer]: String read GetOldFN write SetOldFN;
  50.  
  51.     procedure Generate; overload;
  52.     procedure Generate(const Index: Integer); overload;
  53.     property Generated[Index: Integer]: Boolean read GetCached;
  54.     function GetPatchSize(Index: Integer): Integer;
  55.  
  56.     procedure WritePatch(Index: Integer; Stream: TStream);
  57.  
  58. //  LoadFromStream not supported: Use Create(Index,Stream) instead!
  59. //    procedure LoadFromStream(Stream: TStream);
  60.     procedure SaveToStream(Stream: TStream);
  61.   published
  62.     property NewVersion: String read GetNewFN write SetNewFN;
  63.     property OldVersionCount: Integer read GetOldVersionCount;
  64.     property Index: Integer read FIndex;
  65.     property Config: String read GetConfig write SetConfig;
  66.   end;
  67.  
  68.   TPatchProject = class (TObject)
  69.   private
  70.     FPat: Array of TPatchFile;
  71.   public
  72.     procedure LoadFromStream(Stream: TStream);
  73.     procedure SaveToStream(Stream: TStream);
  74.     constructor Create();
  75.     destructor Destroy(); override;
  76.     procedure AddNewVersion(FileName: String);
  77.     function PatchFile(FileName: String): TPatchFile; overload;
  78.     function PatchFile(Index: Integer): TPatchFile; overload;
  79.     function GetPatchCount: Integer;
  80.     procedure WritePatches(Stream: TStream);
  81.     procedure Generate;
  82.     procedure ResetCache;
  83.   end;
  84.  
  85. implementation
  86.  
  87.   function ReadStreamString(Stream: TStream): String;
  88.   var
  89.     Buf: Array[0..512] of Char;
  90.     i: LongInt;
  91.     S: String;
  92.     j: Integer;
  93.   begin
  94.     Stream.Read(i,SizeOf(i));
  95.     if i>512 then raise Exception.Create('VPJ damaged: String too long (>512)');
  96.     Stream.Read(Buf,i);
  97.     for j:=1 to i do
  98.       S:=S+Buf[j-1];
  99.     ReadStreamString:=S;
  100.   end;
  101.  
  102. //a private wrapper for the FileCRC function
  103. function CalcCRC(FileName: String): Integer;
  104. var
  105.   fs: TFileStream;
  106. begin
  107.   CalcCRC:=0;
  108.   fs:=nil;
  109.   try
  110.     fs:=TFileStream.Create(FileName,fmOpenRead);
  111.     CalcCRC:=FileCRC(fs);
  112.   finally
  113.     fs.Free;
  114.   end;
  115. end;
  116.  
  117. function GetFileSize(FileName: String): Integer;
  118. var
  119.   fs: TFileStream;
  120. begin
  121.   GetFileSize:=0;
  122.   fs:=nil;
  123.   try
  124.     fs:=TFileStream.Create(FileName,fmOpenRead);
  125.     GetFileSize:=fs.Size;
  126.   finally
  127.     fs.Free;
  128.   end;
  129. end;
  130.  
  131. { TPatchFile }
  132.  
  133. procedure TPatchFile.AddOldVersion(const FileName: String);
  134. var
  135.   i: Integer;
  136. //  fs: TFileStream;
  137. begin
  138.   i:=Length(FOld);
  139.   SetLength(FOld,i+1);
  140.   FOld[i].Cache:=TMemoryStream.Create;
  141.   SetOldFN(i,FileName);
  142. end;
  143.  
  144. constructor TPatchFile.Create(Index: Integer; FileName: String);
  145. //var
  146. //  fs: TFileStream;
  147. begin
  148.   inherited Create();
  149.   FIndex:=Index;
  150.   SetLength(FOld,0);
  151.   FNew.CRC32:=0;
  152.   FNew.Size:=-1;
  153.   SetNewFN(FileName);
  154.   ConfigID:=DEFAULT_CONFIG;
  155.    //just to be on the safe side
  156.   //following is now done by SetNewFN :)
  157.   //no it's not - because that one resets the cache!!!
  158.   //doesn't matter, because we're not loading from stream!!!
  159. {  FNew.FileName:=FileName;
  160.   FNew.FriendlyName:=ExtractFileName(FileName);
  161.   FNew.CRC32:=CalcCRC(FileName);
  162.   FNew.Size:=GetFileSize(FileName);}
  163. end;
  164.  
  165. constructor TPatchFile.Create(Index: Integer; Stream: TStream);
  166. var
  167.   i,q: LongInt;
  168.   CSize: Integer;
  169.   j: Integer;
  170. begin
  171.   inherited Create();
  172.   FIndex:=Index;
  173.   SetLength(FOld,0);
  174.   FNew.CRC32:=0;
  175.   FNew.Size:=-1; //just to be on the safe side
  176.  
  177.   //read configuration
  178.   ConfigID:=ReadStreamString(Stream);
  179.  
  180.   //now load everything...
  181.   FNew.FileName:=ReadStreamString(Stream);
  182.   FNew.FriendlyName:=ReadStreamString(Stream);
  183.   Stream.Read(FNew.CRC32,SizeOf(FNew.CRC32));
  184.   Stream.Read(FNew.Size,SizeOf(FNew.Size));
  185.   Stream.Read(i,SizeOf(i));
  186.   SetLength(FOld,i);
  187.   for j:=0 to i - 1 do begin
  188.     FOld[j].FileName:=ReadStreamString(Stream);
  189.     FOld[j].FriendlyName:=ReadStreamString(Stream);
  190.     Stream.Read(FOld[j].CRC32,SizeOf(FOld[j].CRC32));
  191.     Stream.Read(FOld[j].Size,SizeOf(FOld[j].Size));
  192.     Stream.Read(q,SizeOf(q));
  193.     FOld[j].Cached:=not (q=0);
  194.     if FOld[j].Cached then begin
  195.       Stream.Read(CSize,SizeOf(CSize));
  196.       FOld[j].Cache:=TMemoryStream.Create;
  197.       FOld[j].Cache.CopyFrom(Stream,CSize);
  198.     end;
  199.   end;
  200. end;
  201.  
  202. destructor TPatchFile.Destroy;
  203. begin
  204.   SetLength(FOld,0);
  205.   inherited;
  206. end;
  207.  
  208. function TPatchFile.GetNewFN: String;
  209. begin
  210.   GetNewFN:=FNew.FileName;
  211. end;
  212.  
  213. function TPatchFile.GetOldFN(Index: Integer): String;
  214. begin
  215.   Result:=FOld[Index].FileName;
  216.   if FOld[Index].Cached then
  217.     if FOld[Index].Cache.Size>0 then begin
  218.       Result:=Result + ' ('+IntToStr(FOld[Index].Cache.Size)+' bytes)';
  219.     end;
  220. end;
  221.  
  222. function TPatchFile.GetOldVersionCount: Integer;
  223. begin
  224.   GetOldVersionCount:=Length(FOld);
  225. end;
  226.  
  227. procedure TPatchFile.ResetCache;
  228. var
  229.   i: Integer;
  230. begin
  231.   for i:=0 to Length(FOld)-1 do
  232.     ResetCache(i);
  233. end;
  234.  
  235. procedure TPatchFile.RemoveOldVersion(const Index: Integer);
  236. var
  237.   i: Integer;
  238. begin
  239.   FOld[Index].Cache.Free;
  240.   for i:=Index to Length(FOld)-2 do begin
  241.     FOld[i]:=FOld[i+1];
  242.   end;
  243.   SetLength(FOld,Length(FOld)-1);
  244. end;
  245.  
  246. procedure TPatchFile.ResetCache(OldIndex: Integer);
  247. begin
  248.   FOld[OldIndex].Cached:=False;
  249.   FOld[OldIndex].Size:=-1;
  250.   FOld[OldIndex].Cache.Clear;
  251. end;
  252.  
  253. procedure TPatchFile.SaveToStream(Stream: TStream);
  254.   procedure WriteStreamString(Stream: TStream; const S: String);
  255.   var
  256.     i: LongInt;
  257.     j: Integer;
  258.     Buf: Array[0..512] of Char;
  259.   begin
  260.     i:=Length(S);
  261.     Stream.Write(i,SizeOf(i));
  262.     for j:=1 to i do
  263.       Buf[j-1]:=S[j];
  264.     Buf[i]:=#0;
  265.     Stream.Write(Buf,i);
  266.   end;
  267. var
  268.   i,q: LongInt;
  269.   j: Integer;
  270.   tmp: Integer;
  271. begin
  272.   //write config ID
  273.   WriteStreamString(Stream,ConfigID);
  274.  
  275.   WriteStreamString(Stream,FNew.FileName);
  276.   WriteStreamString(Stream,FNew.FriendlyName);
  277.   Stream.Write(FNew.CRC32,SizeOf(FNew.CRC32));
  278.   Stream.Write(FNew.Size,SizeOf(FNew.Size));
  279.  
  280.   i:=Length(FOld);
  281.   Stream.Write(i,SizeOf(i));
  282.  
  283.   for j:=0 to i - 1 do begin
  284.     WriteStreamString(Stream,FOld[j].FileName);
  285.     WriteStreamString(Stream,FOld[j].FriendlyName);
  286.     Stream.Write(FOld[j].CRC32,SizeOf(FOld[j].CRC32));
  287.     Stream.Write(FOld[j].Size,SizeOf(FOld[j].Size));
  288.     if FOld[j].Cached then q:=1 else q:=0;
  289.     Stream.Write(q,SizeOf(q));
  290.     if FOld[j].Cached then begin
  291.       tmp:=FOld[j].Cache.Size;
  292.       Stream.Write(tmp,SizeOf(tmp));
  293.       FOld[j].Cache.Seek(0,soFromBeginning);
  294.       Stream.CopyFrom(FOld[j].Cache,tmp);
  295.     end;
  296.   end;
  297. end;
  298.  
  299. procedure TPatchFile.SetNewFN(Value: String);
  300. var
  301.   NewSize: Integer;
  302.   NewCRC: LongWord;
  303. begin
  304.   FNew.FileName:=Value;
  305.   FNew.Friendlyname:=ExtractFileName(Value);
  306.   NewCRC:=CalcCRC(Value);
  307.   NewSize:=GetFileSize(Value);
  308.   //if any changes, then reset cache :)
  309.   if not ((FNew.CRC32=NewCRC) and (FNew.Size=NewSize)) then begin
  310.     FNew.CRC32:=NewCRC;
  311.     FNew.Size:=NewSize;
  312.     ResetCache;
  313.   end;
  314. end;
  315.  
  316. procedure TPatchFile.SetOldFN(i: Integer; FileName: String);
  317. begin
  318.   if((i>=0) and (i<Length(FOld))) then begin
  319.     FOld[i].FileName:=FileName;
  320.     FOld[i].FriendlyName:=ExtractFileName(FileName);
  321.     FOld[i].CRC32:=CalcCRC(FileName);
  322.     FOld[i].Size:=GetFileSize(FileName);
  323.     ResetCache(i);
  324.   end;
  325. end;
  326.  
  327. procedure TPatchFile.Generate;
  328. var
  329.   i: Integer;
  330. begin
  331.   //generate all of them into the cache?
  332.   for i:=0 to OldVersionCount - 1 do
  333.     Generate(i);
  334. end;
  335.  
  336. procedure TPatchFile.Generate(const Index: Integer);
  337. var
  338.   Size: Integer;
  339.   fm: TMemoryStream;
  340. begin
  341.   fm:=TMemoryStream.Create;
  342.   Size:=DoGenerate(FOld[Index].FileName,FNew.FileName,fm,ConfigID);
  343.   if not (Size=-1) then begin
  344.     if (FOld[Index].Cache.Size>Size) or (not FOld[Index].Cached) then begin //the new one is better
  345.       FOld[Index].Cache.Clear;
  346.       fm.Seek(8,soFromBeginning);
  347.       FOld[Index].Cache.CopyFrom(fm,fm.Size-8);
  348.     end;
  349.     FOld[Index].Cached:=True;
  350.   end;
  351.   fm.Free;
  352. end;
  353.  
  354. function TPatchFile.GetCached(Index: Integer): Boolean;
  355. begin
  356.   GetCached:=FOld[Index].Cached;
  357. end;
  358.  
  359. function TPatchFile.GetConfig: String;
  360. begin
  361.   GetConfig:=ConfigID;
  362. end;
  363.  
  364. procedure TPatchFile.SetConfig(Value: String);
  365. begin
  366.   if not Assigned(Self) then Exit;
  367.   if not SameText(Value,ConfigID) then begin
  368.     InvalidateCache;  //configuration changed, invalidate cache
  369.   end;
  370.   ConfigID:=Value;
  371. end;
  372.  
  373. function TPatchFile.GetPatchSize(Index: Integer): Integer;
  374. begin
  375.   if Generated[Index] then begin
  376.     GetPatchSize:=FOld[Index].Cache.Size;
  377.   end else
  378.     GetPatchSize:=-1;
  379. end;
  380.  
  381. procedure TPatchFile.InvalidateCache;
  382. var
  383.   i: Integer;
  384. begin
  385.   for i:=0 to Length(FOld)-1 do
  386.     InvalidateCache(i);
  387. end;
  388.  
  389. procedure TPatchFile.InvalidateCache(Index: Integer);
  390. begin
  391.   FOld[Index].Cached:=False;
  392. end;
  393.  
  394. procedure TPatchFile.WritePatch(Index: Integer; Stream: TStream);
  395. begin
  396.   if not FOld[Index].Cached then
  397.     Generate(Index);
  398.   if not FOld[Index].Cached then
  399.     raise Exception.Create('Writing of patch failed: Could not generate all patches');
  400.   FOld[Index].Cache.Seek(0,soFromBeginning);
  401.   Stream.CopyFrom(FOld[Index].Cache,FOld[Index].Cache.Size);
  402. end;
  403.  
  404. { TPatchProject }
  405.  
  406. procedure TPatchProject.AddNewVersion(FileName: String);
  407. var
  408.   i: Integer;
  409. begin
  410.   i:=Length(FPat);
  411.   SetLength(FPat,i+1);
  412.   FPat[i]:=TPatchFile.Create(i,FileName);
  413.   FPat[i].SetConfig(DEFAULT_CONFIG);
  414. end;
  415.  
  416. constructor TPatchProject.Create;
  417. begin
  418.   inherited;
  419.   SetLength(FPat,0);
  420. end;
  421.  
  422. destructor TPatchProject.Destroy;
  423. var
  424.   i: Integer;
  425. begin
  426.   for i:=0 to Length(FPat)-1 do begin
  427.     FPat[i].Free;
  428.   end;
  429.   SetLength(FPat,0);
  430.   inherited;
  431. end;
  432.  
  433. procedure TPatchProject.Generate;
  434. var
  435.   i: Integer;
  436. begin
  437.   for i:=0 to GetPatchCount - 1 do
  438.     FPat[i].Generate;
  439. end;
  440.  
  441. function TPatchProject.GetPatchCount: Integer;
  442. begin
  443.   GetPatchCount:=Length(FPat);
  444. end;
  445.  
  446. procedure TPatchProject.LoadFromStream(Stream: TStream);
  447. var
  448.   i: LongInt;
  449.   j: Integer;
  450. begin
  451.   //first free all patchfiles
  452.   for j:=0 to Length(FPat)-1 do begin
  453.     FPat[j].Free;
  454.     FPat[j]:=nil;
  455.   end;
  456.   Stream.Read(i,SizeOf(i));
  457.   if(i=$1A4A5056) then begin            //still read old files
  458.     Stream.Read(i,SizeOf(i));           //16 dummy bytes
  459.     Stream.Read(i,SizeOf(i));
  460.     Stream.Read(i,SizeOf(i));
  461.     Stream.Read(i,SizeOf(i));
  462.  
  463.     Stream.Read(i,SizeOf(i));
  464.   end;
  465.   SetLength(FPat,i);
  466.   for j:=0 to i - 1 do begin
  467.     FPat[j]:=TPatchFile.Create(j,Stream);
  468.   end;
  469. end;
  470.  
  471. function TPatchProject.PatchFile(FileName: String): TPatchFile;
  472. var
  473.   i: Integer;
  474. begin
  475.   PatchFile:=nil;
  476.   for i:=0 to Length(FPat) - 1 do begin
  477.     if(CompareText(FPat[i].FNew.FileName,FileName)=0) then begin
  478.       PatchFile:=FPat[i];
  479.     end;
  480.   end;
  481.   for i:=0 to Length(FPat) - 1 do begin
  482.     if(CompareText(FPat[i].FNew.FriendlyName,FileName)=0) then begin
  483.       PatchFile:=FPat[i];
  484.     end;
  485.   end;
  486. end;
  487.  
  488. function TPatchProject.PatchFile(Index: Integer): TPatchFile;
  489. begin
  490.   if (Index<Length(FPat)) and (Index>=0) then
  491.     PatchFile:=FPat[Index]
  492.   else
  493.     PatchFile:=nil;
  494. end;
  495.  
  496. procedure TPatchProject.ResetCache;
  497. var
  498.   i: Integer;
  499. begin
  500.   for i:=0 to Pred(Length(FPat)) do
  501.     FPat[i].ResetCache;
  502. end;
  503.  
  504. procedure TPatchProject.SaveToStream(Stream: TStream);
  505. var
  506.   HeadID: Array[0..3] of Char;
  507.   i: LongInt;
  508.   j: Integer;
  509. begin
  510.   HeadID:='VPJ'+#26;
  511.   Stream.Write(HeadID,SizeOf(HeadID));
  512.   //16 dummy bytes
  513.   i:=0;
  514.   Stream.Write(i,SizeOf(i));
  515.   Stream.Write(i,SizeOf(i));
  516.   Stream.Write(i,SizeOf(i));
  517.   Stream.Write(i,SizeOf(i));
  518.   i:=Length(FPat);
  519.   Stream.Write(i,SizeOf(i));
  520.   for j:=0 to i - 1 do begin
  521.     FPat[j].SaveToStream(Stream);
  522.   end;
  523. end;
  524.  
  525. procedure TPatchProject.WritePatches(Stream: TStream);
  526. var
  527.   i,j,k,o: LongInt;
  528. begin
  529.   k:=$54415056;
  530.   o:=Stream.Position;
  531.   Stream.Write(k,SizeOf(k));
  532.   k:=0;
  533.   Stream.Write(k,SizeOf(k));
  534.   k:=0;
  535.   for i:=0 to Length(FPat)-1 do begin
  536.     for j:=0 to FPat[i].GetOldVersionCount - 1 do begin
  537.       FPat[i].WritePatch(j,Stream);
  538.       Inc(k);
  539.     end;
  540.   end;
  541.   Stream.Seek(o+4,soFromBeginning);
  542.   Stream.Write(k,SizeOf(k));
  543.   Stream.Seek(Stream.Size,soFromBeginning);
  544.   Stream.Write(o,SizeOf(o));
  545. end;
  546.  
  547. end.
  548.