home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 25 / nopv25.iso / 040A / DELZIP10.ZIP / VCL.ZIP / ZIPDIR.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-04-07  |  8.7 KB  |  256 lines

  1. { ZIPDIR.PAS  - This is a VCL to get you the "Table of Contents" of a Zip File.
  2.   This Delphi v2 VCL is public domain by Eric W. Engler.
  3.   This is based on two similar components:
  4.      TZip by Pier Carlo Chiodi, pc.chiodi@mbox.thunder.it
  5.      TZReader by Dennis Passmore, CIS: 71640,2464
  6.  
  7.   My changes were specifically oriented towards making this more useful
  8.   for my ZipMaster VCL, and to remove unneeded features.  Datanames were
  9.   changed in many cases to assit in debugging. }
  10.  
  11. (* Simple Usage Instructions:
  12.     1. Install this VCL into a directory in your VCL search path
  13.     2. drop this on a form, or create it dynamically.
  14.     3. At runtime or design time, assign a filename to ZipDir1.Filename.
  15.     4. At runtime, ZipDir1.Count tells you how many files are contained im
  16.        the zip file.  Note: the entry numbers are zero-based, so if the
  17.        ZipDir1.Count is 4, then the entry numbers will be: 0, 1, 2, 3.
  18.     5. At runtime, get the contents of the Zip file. Examine the
  19.        "ZipContents" TList, which contains a bunch of ZipDirEntry records:
  20.            var
  21.              i: Integer;
  22.            begin
  23.               ZipDir1.Filename:='C:\MYSTUFF\TEST.ZIP'; { List method auto-execs }
  24.               for i:=0 to ZipDir1.Count-1 do
  25.               begin
  26.                  with ZipDirEntry(ZipDir1.ZipContents[i]^) do
  27.                  begin
  28.                     ShowMessage('Entry ' + IntToStr(i) + ': '
  29.                                  + 'Filename is ' + FileName);
  30.                     ShowMessage('Filename is ' + FileName);
  31.                     ShowMessage('Compr size is ' + IntToStr(CompressedSize));
  32.                     ShowMessage('DateTime stamp is ' +
  33.                        FormatDateTime('ddddd  t',FileDateToDateTime(DateTime)));
  34.                  end; // end with
  35.               end; // end for
  36.             end;
  37.  
  38.      6. The List method manually forces the TList to be rebuilt.  Note,
  39.         however, it gets automatically executed whenever you set the filename.
  40.  
  41.   For a more aggressive application of this VCL, see ZipDemo1 in my Delphi Zip
  42.   package.  It uses this to populate a TSortGrid, which is also public domain.
  43. *)
  44.  
  45. unit ZipDir;
  46.  
  47. interface
  48.  
  49. uses
  50.   WinTypes, Winprocs, SysUtils, Classes, Dialogs, ZipDLL, UnzDLL, ZCALLBCK;
  51.  
  52. type
  53.   EInvalidOperation = class(exception);
  54.  
  55. type ZipDirEntry = packed Record
  56.   Version                     : WORD;
  57.   Flag                        : WORD;
  58.   CompressionMethod           : WORD;
  59.   DateTime                    : Longint; { Time: word; Date: Word; }
  60.   CRC32                       : Longint;
  61.   CompressedSize              : Longint;
  62.   UncompressedSize            : Longint;
  63.   FileNameLength              : WORD;
  64.   ExtraFieldLength            : WORD;
  65.   FileName                    : String;
  66. end;
  67.  
  68. type
  69.   PZipDirEntry = ^ZipDirEntry;
  70.  
  71. const
  72.   LocalFileHeaderSig   = $04034b50; { 'PK03' }
  73.   CentralFileHeaderSig = $02014b50; { 'PK12' }
  74.   EndCentralDirSig     = $06054b50; { 'PK56' }
  75.  
  76. type
  77.   TZipDir = class(TComponent)
  78.   private
  79.     FZipContents: TList;
  80.     FCount: Integer;
  81.     FZipFileName: String;
  82.  
  83.     procedure FreeZipDirEntryRecords;
  84.     function  GetCount: Integer;
  85.     procedure SetZipFileName(Value: String);
  86.  
  87.   public
  88.     constructor Create(AOwner: TComponent); override;
  89.     destructor Destroy; override;
  90.  
  91.     { run-time-only methods }
  92.     procedure List;  { force a re-read of Zip file }
  93.  
  94.     { run-time-only properties }
  95.     property ZipContents: TList read FZipContents;
  96.     property Count: Integer read GetCount;
  97.  
  98.   published
  99.     { properties for both design-time and run time }
  100.  
  101.     { At runtime: every time the filename is assigned a value, the
  102.       ZipDir will be read.  You don't need to call List yourself,
  103.       unless you just want to refresh your list. Of course, if you
  104.       set the ZipFileName in the property inspector, no auto List will
  105.       later occur at runtime.}
  106.     property ZipFileName: String  read FZipFileName write SetZipFileName;
  107.   end;
  108.  
  109. implementation
  110.  
  111. const
  112.   LocalDirEntrySize = 26;   { size of zip dir entry in local zip directory }
  113.  
  114. constructor TZipDir.Create(AOwner:TComponent);
  115. begin
  116.   inherited Create(AOwner);
  117.   FZipContents:=TList.Create;
  118.   FZipFileName:='';
  119. end;
  120.  
  121. destructor TZipDir.Destroy;
  122. begin
  123.   FreeZipDirEntryRecords;
  124.   FZipContents.Free;
  125.   inherited Destroy;
  126. end;
  127.  
  128. procedure TZipDir.SetZipFileName(Value: String);
  129. begin
  130.   FreeZipDirEntryRecords;
  131.   FZipFileName := Value;
  132.   if not (csDesigning in ComponentState) then
  133.   begin
  134.     if FileExists(FZipFileName) then
  135.        { I am intentionally letting this be done again if you set the filename
  136.          to the same name it already was.  This forces a refresh, in case the
  137.          zip file has been changed. }
  138.        List;
  139.   end;
  140. end;
  141.  
  142. procedure TZipDir.FreeZipDirEntryRecords;
  143. var
  144.   i: integer;
  145. begin
  146.    for i:=FZipContents.Count-1 downto 0 do
  147.    begin
  148.      if Assigned(FZipContents[i]) then
  149.         // unalloc storage for a ZipDirEntry record
  150.         dispose(PZipDirEntry(FZipContents[i]));
  151.      FZipContents.Delete(i); // delete the TList pointer to the freed record
  152.    end; // end for
  153.    // The caller will free the FZipContents TList itself, if needed
  154. end;
  155.  
  156. function TZipDir.GetCount:Integer;
  157. begin
  158.   if FZipFileName <> '' then
  159.      Result:=FZipContents.Count
  160.   else
  161.      Result:=0;
  162. end;
  163.  
  164. // Read thru all entries in the local Zip directory.
  165. // This is triggered by assigning a filename to your ZipDir component, or
  166. // by calling this method directly.
  167. procedure TZipDir.List;
  168. var
  169.   Sig: Longint;
  170.   ZipStream: TFileStream;
  171.   Res: Longint;
  172.   ZipDirEntry: PZipDirEntry;
  173.   Name: array [0..255] of char;
  174. begin
  175.   if (csDesigning in ComponentState) then
  176.      Exit;
  177.   FreeZipDirEntryRecords;
  178.   if not FileExists(FZipFileName) then
  179.   begin
  180.      ShowMessage('Error opening file: ' + FZipFilename);
  181.      exit;
  182.   end;
  183.  
  184.   ZipStream := TFileStream.Create(FZipFileName,fmOpenRead);
  185.   try
  186.      while TRUE do
  187.      begin
  188.         Res := ZipStream.Read(Sig, SizeOf(Sig));
  189.  
  190.         if (Res = HFILE_ERROR) or (Res <> SizeOf(Sig)) then
  191.            raise EStreamError.create('Error reading Zip File');
  192.  
  193.         if Sig = LocalFileHeaderSig then
  194.         begin
  195.            {===============================================================}
  196.            { This is what we want.  We'll read the local file header info. }
  197.  
  198.            { Create a new ZipDirEntry record, and zero fill it }
  199.            new(ZipDirEntry);
  200.            fillchar(ZipDirEntry^, sizeof(ZipDirEntry^), 0);
  201.  
  202.            { fill the ZipDirEntry struct with local header info for one entry. }
  203.            { Note: In the "if" statement's first clause we're reading the info
  204.              for a whole Zip dir entry, not just the version info. }
  205.            with ZipDirEntry^ do
  206.            if (ZipStream.Read(Version, LocalDirEntrySize) = LocalDirEntrySize)
  207.            and (ZipStream.Read(Name, FileNameLength)=FileNameLength) then
  208.               FileName := Copy(Name, 0, FileNameLength)
  209.            else
  210.            begin
  211.               dispose(ZipDirEntry);  { bad entry - free up memory for it }
  212.               raise EStreamError.create('Error reading Zip file');
  213.            end;
  214.            FZipContents.Add(pointer(ZipDirEntry));
  215.  
  216.            { ShowMessage('Just found file: ' + ZipDirEntry^.FileName); } // DEBUG
  217.  
  218.            if (ZipStream.Position + ZipDirEntry^.ExtraFieldLength +
  219.             ZipDirEntry^.CompressedSize) > (ZipStream.Size - 22) then
  220.            begin
  221.               { should never happen due to presence of central dir }
  222.               raise EStreamError.create('Error reading Zip file');
  223.               break;
  224.            end;
  225.  
  226.            with ZipDirEntry^ do
  227.            begin
  228.               if ExtraFieldLength > 0 then
  229.               begin
  230.                  { skip over the extra fields }
  231.                  res := (ZipStream.Position + ExtraFieldLength);
  232.                  if ZipStream.Seek(ExtraFieldLength, soFromCurrent) <> res then
  233.                     raise EStreamError.create('Error reading Zip file');
  234.               end;
  235.  
  236.               { skip over the compressed data for the file entry just parsed }
  237.               res := (ZipStream.Position + CompressedSize);
  238.               if ZipStream.Seek(CompressedSize, soFromCurrent) <> res then
  239.                  raise EStreamError.create('Error reading Zip file');
  240.            end;
  241.            {===============================================================}
  242.         end  { end of local stuff }
  243.  
  244.         else
  245.            { we're not going to read the Central or End directories }
  246.            if (Sig = CentralFileHeaderSig) or (Sig = EndCentralDirSig) then
  247.               break;   { found end of local stuff - we're done }
  248.      end;  { end of loop }
  249.  
  250.   finally
  251.      ZipStream.Free;
  252.   end;  { end of try...finally }
  253. end;
  254.  
  255. end.
  256.