home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 March / Chip_2002-03_cd1.bin / zkuste / delphi / kompon / d5 / cak / CAKINST.ZIP / ZipRepair.pas < prev   
Encoding:
Pascal/Delphi Source File  |  2001-12-12  |  12.0 KB  |  358 lines

  1. unit ZipRepair;
  2.  
  3. //---------------------------------------------------------------------
  4. // Module:        ZipRepair
  5. // Author:        Angus Johnson
  6. // Version:       0.1 beta
  7. // Date:          15 October 1999
  8. // Copyright:     ⌐ 1999 Angus Johnson
  9. // Email:         ajohnson@rpi.net.au
  10. // Distribution:  Freeware, but please acknowledge authorship.
  11. // (Designed to complement the excellent delphi freeware Zip utilities
  12. // started by Eric Engler and continued by Chris Vleghert and found at -
  13. // http://www.geocities.com/SiliconValley/Orchard/8607/)
  14. //---------------------------------------------------------------------
  15.  
  16. interface
  17.  
  18. uses
  19.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
  20.  
  21. //Completely (and rapidly) rebuilds a Zip file where the Central Directory
  22. //and/or the End Of Central records have been corrupted. It will *not* fix
  23. //corrupted data nor will it solve 'forgotten' passwords.
  24. //note 1: Multi disk archives are not supported.
  25. function RepairZip(const SourceFile, TargetFile: string): boolean;
  26.  
  27. implementation
  28.  
  29. type
  30.  
  31.   pFileInfo = ^TFileInfo;
  32.   TFileInfo = packed record //first 42 bytes identical to the Central Header File record
  33.     MadeByVersion: byte;    //(1)
  34.     HostVersionNo: byte;    //(1)
  35.     Version: word;    //(2)
  36.     Flag: word;    //(2)
  37.     CompressionMethod: word;    //(2)
  38.     FileDate: integer; //modification datetime (4)
  39.     CRC32: integer; //(4)
  40.     CompressedSize: integer; //(4)
  41.     UncompressedSize: integer; //(4)
  42.     FileNameLength: word;    //(2)
  43.     ExtraFieldLength: word;    //(2)
  44.     FileCommentLen: word;    //(2)
  45.     StartOnDisk: word;    //disk # on which file starts (2)
  46.     IntFileAttrib: word;    //internal file attributes ie: Text/Binary(2)
  47.     ExtFileAttrib: cardinal;//external file attributes(4)
  48.     RelOffLocalHdr: cardinal;//relative offset of local file header(4)
  49.     //42 bytes above plus...
  50.     Filename: string;
  51.     //ExtraField: string;
  52.     //Comment: string;
  53.   end;
  54.  
  55.   TLocalHeader = packed record
  56.     HeaderSig: cardinal; // $04034b50 (4)
  57.     VersionNeed: word;
  58.     Flag: word;
  59.     ComprMethod: word;
  60.     FileTime: word;
  61.     FileDate: word;
  62.     CRC32: cardinal;
  63.     ComprSize: cardinal;
  64.     UnComprSize: cardinal;
  65.     FileNameLen: word;
  66.     ExtraLen: word;
  67.   end;
  68.  
  69.   TDataDescriptor = packed record  //Exists only if bit 3 of LocalHeader.Flag is set.
  70.     DescriptorSig: cardinal; //field not defined in PKWare's docs but used by WinZip
  71.     CRC32: cardinal;
  72.     ComprSize: cardinal;
  73.     UnComprSize: cardinal;
  74.   end;
  75.  
  76. (*
  77. Central directory structure:
  78.   [file header] . . .  end of central dir record
  79. *)
  80.  
  81.   //array of TCentralFileHeaders constitute the Central Header directory...
  82.   TCentralFileHeader = packed record  // fixed part size = 42 bytes
  83.     HeaderSig: cardinal; // $02014b50 { 'PK'#1#2 } (4)
  84.     MadeByVersion: byte;    //(1)
  85.     HostVersionNo: byte;    //(1)
  86.     Version: word;    //(2) version needed to extract(2)
  87.     Flag: word;    //(2)
  88.     CompressionMethod: word;    //(2)
  89.     FileDate: integer; //modification date & time (4)
  90.     CRC32: integer; //(4)
  91.     CompressedSize: integer; //(4)
  92.     UncompressedSize: integer; //(4)
  93.     FileNameLength: word;    //(2)
  94.     ExtraFieldLength: word;    //(2)
  95.     FileCommentLen: word;    //(2)
  96.     StartOnDisk: word;    //disk # on which file starts (2)
  97.     IntFileAttrib: word;    //internal file attributes ie: Text/Binary(2)
  98.     ExtFileAttrib: cardinal;//external file attributes(4)
  99.     RelOffLocalHdr: cardinal;//relative offset of local file header(4)
  100.     //FileName         variable size
  101.     //ExtraField       variable size
  102.     //FileComment      variable size
  103.   end;
  104.  
  105.   TEndOfCentralHeader = packed record  //Fixed part size = 22 bytes
  106.     HeaderSig: cardinal; //$06054B50 (4)
  107.     ThisDiskNo: word;     //This disk's number (zero based) (2)
  108.     CentralDiskNo: word;     //Disk number on which central dir starts (2)
  109.     ThisDiskEntries: word;     //Number of central dir entries on this disk (2)
  110.     TotalEntries: word;     //Total entries in central dir (2)
  111.     CentralSize: cardinal; //Size of central directory (4)
  112.     CentralOffset: cardinal; //offset of central dir on CentralDiskNo (4)
  113.     ZipCommentLen: word;     //(2)
  114.     // ZipComment       variable size
  115.   end;
  116.  
  117. const
  118.   MULTIPLE_DISK_SIG = $08074b50; // 'PK'#7#8
  119.   DATA_DESCRIPT_SIG = MULTIPLE_DISK_SIG; //!!
  120.   LOCAL_HEADERSIG   = $04034b50; // 'PK'#3#4
  121.   CENTRAL_HEADERSIG = $02014b50; // 'PK'#1#2
  122.   EOC_HEADERSIG     = $06054b50; // 'PK'#5#6
  123.  
  124. //---------------------------------------------------------------------
  125. //---------------------------------------------------------------------
  126.  
  127. function min(a, b: integer): integer;
  128. begin
  129.   if a < b then Result := a 
  130.   else 
  131.     Result := b;
  132. end;
  133.  
  134. //---------------------------------------------------------------------
  135.  
  136. var
  137.   JumpValue:  array[#0..#255] of integer; //used to find Local Header records
  138.   JumpValue2: array[#0..#255] of integer; //used to find DataDescriptor records
  139.  
  140. procedure InitializeJumpValueArray; //bmh search for TLocalHeaders
  141. var
  142.   i: char;
  143. begin
  144.   for i := #0 to #255 do JumpValue[i] := 4;
  145.   JumpValue['P'] := 3;
  146.   JumpValue['K'] := 2;
  147.   JumpValue[#3]  := 1;
  148.   JumpValue[#4]  := 0;
  149. end;
  150.  
  151. //---------------------------------------------------------------------
  152.  
  153. procedure InitializeJumpValue2Array; //bmh search for TDataDescriptors
  154. var
  155.   i: char;
  156. begin
  157.   for i := #0 to #255 do JumpValue2[i] := 4;
  158.   JumpValue2['P'] := 3;
  159.   JumpValue2['K'] := 2;
  160.   JumpValue2[#7]  := 1;
  161.   JumpValue2[#8]  := 0;
  162. end;
  163.  
  164. //---------------------------------------------------------------------
  165.  
  166. procedure FindAllLocalHeaders(FileList: TList; Stream: TMemoryStream);
  167. var
  168.   fileInfo: pFileInfo;
  169.   i, BuffPos, HeaderStartPos: integer;
  170.   buffer:   PChar;
  171. label
  172.   LocalHeaderError;
  173.  
  174.   //-------------------------------
  175.   function FindNextHeader: boolean; //moves BuffPos to start of next LocalHeader
  176.   var
  177.     n, HeaderSig: cardinal;
  178.   begin
  179.     Result := False;
  180.     while BuffPos < Stream.Size do
  181.     begin
  182.       n := JumpValue[Buffer[BuffPos]];
  183.       if n = 0 then //a #4 found at least...
  184.       begin
  185.         dec(BuffPos, 3);
  186.         move(Buffer[BuffPos], HeaderSig, 4);
  187.         if (HeaderSig = LOCAL_HEADERSIG) and
  188.           (BuffPos + Sizeof(TLocalHeader) < Stream.Size) then
  189.         begin
  190.           Result := True;
  191.           exit;
  192.         end
  193.         else
  194.           inc(BuffPos, 7);
  195.       end
  196.       else
  197.         inc(BuffPos, n);
  198.     end;
  199.   end;
  200.  
  201.   //-------------------------------
  202.   function FindDataDescriptor: boolean; //moves BuffPos to start of DataDescriptor
  203.   var
  204.     n, HeaderSig: cardinal;
  205.   begin
  206.     Result := False;
  207.     while BuffPos < Stream.Size do
  208.     begin
  209.       n := JumpValue2[Buffer[BuffPos]];
  210.       if n = 0 then //a #8 found at least...
  211.       begin
  212.         dec(BuffPos, 3);
  213.         move(Buffer[BuffPos], HeaderSig, 4);
  214.         if (HeaderSig = DATA_DESCRIPT_SIG) and
  215.           (BuffPos + Sizeof(TDataDescriptor) < Stream.Size) then
  216.         begin
  217.           Result := True;
  218.           exit;
  219.         end
  220.         else
  221.           inc(BuffPos, 7);
  222.       end
  223.       else
  224.         inc(BuffPos, n);
  225.     end;
  226.   end;
  227.  
  228. //-------------------------------
  229. begin
  230.   buffer  := Stream.Memory;
  231.   BuffPos := 3;
  232.   //prepare for boyer-moore-horspool searches...
  233.   if JumpValue[#0] = 0 then InitializeJumpValueArray;
  234.   if JumpValue2[#0] = 0 then InitializeJumpValue2Array;
  235.  
  236.   //get all local header info...
  237.   while FindNextHeader do
  238.   begin
  239.     HeaderStartPos := BuffPos;
  240.     new(fileInfo);
  241.     with fileInfo^ do
  242.     begin
  243.       //ignore these values, so zero initialize them.
  244.       //we could try and match them to the dud central directory records
  245.       //but i'm not sure it's worth the trouble.
  246.       MadeByVersion  := $0;
  247.       HostVersionNo  := $0;
  248.       IntFileAttrib  := $0;
  249.       ExtFileAttrib  := $0;
  250.       StartOnDisk    := $0;
  251.       FileCommentLen := $0;
  252.  
  253.       //copy - Version, Flag, CompressionMethod, FileDate, CRC32,
  254.       //  CompressedSize, UncompressedSize, FileNameLength, ExtraFieldLength
  255.       move(Buffer[HeaderStartPos + 4], Version, Sizeof(TLocalHeader) - 4);
  256.       //save current Local Header offset which will be updated later...
  257.       RelOffLocalHdr := HeaderStartPos;
  258.       if (fileInfo.FileNameLength < 1) or (FileNameLength > MAX_PATH) then
  259.         goto LocalHeaderError;
  260.       inc(BuffPos, Sizeof(TLocalHeader));
  261.       setlength(Filename, FileNameLength);
  262.       move(buffer[BuffPos], Filename[1], FileNameLength);
  263.       //and do an extra check to make sure the name is valid...
  264.       for i := 1 to FileNameLength do
  265.         if Filename[i] < #32 then goto LocalHeaderError;
  266.       inc(BuffPos, FileNameLength);
  267.       inc(BuffPos, ExtraFieldLength);
  268.       if (Flag and $8) = $8 then
  269.       begin
  270.         //a bit of a bummer but a TDataDescriptor record is used
  271.         //so we don't know the size of the data block.
  272.         //it's a little bit slower but it still works...
  273.         if not FindDataDescriptor then goto LocalHeaderError;
  274.         //now update: CRC32, CompressedSize, UncompressedSize
  275.         move(buffer[BuffPos + 4], CRC32, 12);
  276.         inc(BuffPos, sizeof(TDataDescriptor)); //get ready for next LocalHeader
  277.       end
  278.       else 
  279.         inc(BuffPos, CompressedSize); //get ready for next LocalHeader
  280.     end;
  281.     FileList.add(fileInfo);
  282.     continue; //avoid LocalHeaderError below
  283.  
  284.     LocalHeaderError:
  285.       dispose(fileInfo);
  286.     BuffPos := HeaderStartPos + 4; //ie: skip over this dud record
  287.   end;
  288. end;
  289.  
  290. //---------------------------------------------------------------------
  291.  
  292. function RepairZip(const SourceFile, TargetFile: string): boolean;
  293. var
  294.   i, StartOfCentral: integer;
  295.   SrcStream: TMemoryStream;
  296.   TrgStream: TFileStream;
  297.   FileList:  TList;
  298.   Eoc:       TEndOfCentralHeader;
  299.   HeaderSig: cardinal;
  300. begin
  301.   FileList      := TList.Create;
  302.   SrcStream     := TMemoryStream.Create;
  303.   screen.cursor := crHourglass;
  304.   try
  305.     SrcStream.LoadFromFile(SourceFile);
  306.     FindAllLocalHeaders(FileList, SrcStream);
  307.     //ok we have enough information now to make a new Zip file
  308.     TrgStream := TFileStream.Create(TargetFile, fmCreate);
  309.     try
  310.       //write all the local headers and data...
  311.       for i := 0 to FileList.Count - 1 do
  312.         with pFileInfo(FileList[i])^ do
  313.         begin
  314.           SrcStream.seek(RelOffLocalHdr, soFromBeginning); //use the old RelOffLocalHdr
  315.           RelOffLocalHdr   := TrgStream.Position; //now update RelOffLocalHdr
  316.           TrgStream.copyfrom(SrcStream,
  317.             sizeof(TLocalHeader) + FileNameLength + ExtraFieldLength + CompressedSize);
  318.           //i'm almost certain the Central Directory ExtraField is different
  319.           //from the local ExtraField so zero this out.
  320.           ExtraFieldLength := 0;
  321.         end;
  322.       StartOfCentral := TrgStream.position;
  323.       //recreate the central directory...
  324.       HeaderSig      := CENTRAL_HEADERSIG;
  325.       for i := 0 to FileList.Count - 1 do
  326.         with pFileInfo(FileList[i])^ do
  327.         begin
  328.           TrgStream.Write(HeaderSig, sizeof(HeaderSig));
  329.           TrgStream.Write(MadeByVersion, 42); //copy first 42 bytes
  330.           TrgStream.Write(Filename[1], length(Filename));
  331.         end;
  332.       //finally write the EndOfCentral header...
  333.       Eoc.HeaderSig       := EOC_HEADERSIG;
  334.       Eoc.ThisDiskNo      := 0;
  335.       Eoc.CentralDiskNo   := 0;
  336.       Eoc.ThisDiskEntries := FileList.Count;
  337.       Eoc.TotalEntries    := Eoc.ThisDiskEntries;
  338.       Eoc.CentralSize     := TrgStream.position - StartOfCentral;
  339.       Eoc.CentralOffset   := StartOfCentral;
  340.       Eoc.ZipCommentLen   := 0;
  341.       TrgStream.Write(Eoc, sizeof(Eoc));
  342.       Result := True;
  343.     finally
  344.       TrgStream.Free;
  345.     end;
  346.   finally
  347.     screen.cursor := crDefault;
  348.     SrcStream.Free;
  349.     for i := 0 to FileList.Count - 1 do dispose(pFileInfo(FileList[i]));
  350.     FileList.Free;
  351.   end;
  352. end;
  353.  
  354. //---------------------------------------------------------------------
  355. //---------------------------------------------------------------------
  356.  
  357.  
  358. end.