Image Map of Navigational Panel to Home / Contents / Search Creating a Class to Access RAR Archives

The code behind this class looks like this...

Image of line

  {$A-}
unit Archive;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

const
   (* List of Archive Types *)
   atNoArchive   = 0;
   atZIPArchive  = 1;
   atLZHArchive  = 2;
   atARJArchive  = 3;
   atPAKArchive  = 4;
   atARCArchive  = 5;
   atZOOArchive  = 6;
   atSQZArchive  = 7;
   atRARArchive  = 8;

type

   TFileDetails = class
      FileName       : PChar;
      CompressedSize : Longint;
      OriginalSize   : Longint;
      FileDate       : Longint;
      constructor    Create(AFileName : String;ACompressedSize : Longint;
			   AOriginalSize : Longint;AFileDate : Longint);
      destructor     Destroy; override;
   end;

   TArchive = class
    private
       FileName       : PChar;
       FileList       : TList;
       ArchiveType    : Longint;
       TotOrigSize,
       TotCompSize,
       FileDate       : Longint;
       ArchiveOk      : boolean;
    public
       constructor    Create(AFileName : String);
       destructor     Destroy; override;
       function       GetTotalFiles : Integer;
       function       GetAFileName(Index : Integer) : String;
       function       GetArchiveOk : Boolean;
       function       GetArchiveType : Longint;
       function       GetTotalOriginalSize : Longint;
       function       GetTotalCompressedSize : Longint;
       procedure      GetFileDate(var Year, Month, Day, Hour, Min, Sec, MSec : word);
       function       GetDateTime : TDateTime;
       function       GetFileName : String;
   end;

implementation

function GetArchiveType(FName : string) : Longint;
// From the file, determine the type of archive that we are looking
// at.  ie zip, arj, rar, etc

var
   hArchive    : file;
   OldFileMode : word;
   ReadBuf     : array[1..8] of byte;

begin
   OldFileMode := FileMode;
   AssignFile(hArchive,FName);
   FileMode := fmOpenRead + fmShareDenyNone; // Read Only
   {$I-}
   reset(hArchive,1);
   GetArchiveType := atNoArchive;
   if (ioresult = 0) then begin
      if (filesize(hArchive) >= 8) then begin
         blockread(hArchive,ReadBuf,sizeof(ReadBuf));
         if (ReadBuf[1] = $50) and
            (ReadBuf[2] = $4B) and
            (ReadBuf[3] = $03) and
            (ReadBuf[4] = $04) then
            GetArchiveType := atZIPArchive
         else if (ReadBuf[1] = $60) and
            (ReadBuf[2] = $EA) then
            GetArchiveType := atARJArchive
         else if (ReadBuf[3] = ord('-')) and
            (ReadBuf[4] = ord('l')) and
            (ReadBuf[5] = ord('h')) and
            (ReadBuf[7] = ord('-')) then
            GetArchiveType := atLZHArchive
         else if (ReadBuf[1] = ord('Z')) and
            (ReadBuf[2] = ord('O')) and
            (ReadBuf[3] = ord('O')) then
            GetArchiveType := atZOOArchive
         else if (ReadBuf[1] = 72) and
            (ReadBuf[2] = 76) and
            (ReadBuf[3] = 83) and
            (ReadBuf[4] = 81) and
            (ReadBuf[5] = 90) then
            GetArchiveType := atSQZArchive
         else if (ReadBuf[1] = ord('R')) and
            (ReadBuf[2] = ord('a')) and
            (ReadBuf[3] = ord('r')) and
            (ReadBuf[4] = ord('!')) then
            GetArchiveType := atRARArchive
         else if (ReadBuf[1] = $1A) then begin
            if (pos('.ARC',FName) > 0) then
               GetArchiveType := atARCArchive
            else
               GetArchiveType := atPAKArchive;
         end;
      end;
      close(hArchive);
   end;
   {$I+}
   OldFileMode := FileMode;
end;


constructor TFileDetails.Create(AFileName : String;ACompressedSize : Longint;
			   AOriginalSize : Longint;AFileDate : Longint);
begin
   CompressedSize := ACompressedSize;
   OriginalSize := AOriginalSize;
   FileDate := AFileDate;

   FileName := StrAlloc(length(AFileName)+1);
   StrPCopy(FileName,AFileName);
end;

destructor TFileDetails.Destroy;

begin
   StrDispose(FileName);
   inherited Destroy;
end;

constructor TArchive.Create(AFileName : String);

   procedure ReadRar;

   type
      RarBlock = record
         HeadCRC   : Word;
         HeadType  : Byte;
         HeadFlags : Word;
         HeadSize  : Word;
      end;

      FileBlock = record
         PackSize  : Longint;
         OrigSize  : Longint;
         HostOS    : Byte;
         FileCRC   : Longint;
         FTime     : Longint;
         UnpVer    : Byte;
         Method    : Byte;
         NameSize  : Word;
         Attr      : Longint;
      end;

   const
      MaxName = 250;

   var
      HdrBlock : RarBlock;
      NumRead  : Integer;
      FBlock   : FileBlock;
      FName    : String[MaxName];
      CName    : Array[0..MaxName+1] of Char;
      hArchive : file;
      OldFileMode : Byte;

      procedure JumpAddSize(ShouldRead : Integer);

      var
         ExtLen : Longint;

      begin
         if ArchiveOK then begin
            seek(hArchive,filepos(hArchive) + ShouldRead);
            if (HdrBlock.HeadFlags shr 15 > 0) then begin
               blockread(hArchive,ExtLen,4,NumRead);
               if (NumRead <> 4) then
                  ArchiveOK := False
               else begin
                  seek(hArchive,FilePos(hArchive) + Longint(HdrBlock.HeadSize + ExtLen - SizeOf(RarBlock) - 4));
               end;
            end;
         end;
      end;

   begin
      OldFileMode := FileMode;
      AssignFile(hArchive,AFileName);
      FileMode := fmOpenRead + fmShareDenyNone; // Read Only
      {$I-}
      reset(hArchive,1);
      ArchiveOK := (IOResult = 0);
      {$I+}
      if (ArchiveOk) then begin
         // Get the datetime of the current file
         FileDate := FileGetDate(TFileRec(hArchive).Handle);

         while (not eof(hArchive)) and ArchiveOK do begin
            blockread(hArchive,HdrBlock,sizeof(HdrBlock),NumRead);
            if (NumRead <> sizeof(HdrBlock)) then
               ArchiveOK := False;
            if (HdrBlock.HeadType = $74) and (ArchiveOK) then begin
               blockread(hArchive,FBlock,sizeof(FBlock),NumRead);
               if (NumRead <> sizeof(FBlock)) then
                  ArchiveOK := False;
               if (FBlock.NameSize <= MaxName) and ArchiveOK then begin
                   blockread(hArchive,FName[1],FBlock.NameSize,NumRead);
                   if (NumRead <> FBlock.NameSize) then
                      ArchiveOK := false
                   else begin
                      // make a normal pascal style string (length first)
                      FName[0] := chr(FBlock.NameSize);
                      StrPCopy(CName,FName);
                      // Adds the individual file details to the list of files
                      FileList.Add(TFileDetails.Create(FName,FBlock.OrigSize,FBlock.PackSize,FBlock.FTime));
                      TotOrigSize := TotOrigSize + FBlock.OrigSize;
                      TotCompSize := TotCompSize + FBlock.PackSize;
                   end;
               end else
                  seek(hArchive,filepos(hArchive) + FBlock.NameSize);
               seek(hArchive,filepos(hArchive) + Longint(HdrBlock.HeadSize-sizeof(HdrBlock)-sizeof(FBlock)-FBlock.NameSize));
               if (HdrBlock.HeadFlags shr 15 > 0) then
                  seek(hArchive,filepos(hArchive) + FBlock.PackSize);
            end else
               JumpAddSize(HdrBlock.HeadSize-NumRead);
         end;
         close(hArchive);
      end;

      FileMode := OldFileMode;
   end;

begin
   ArchiveOK := True;
   FileList := TList.Create;
   FileName := StrAlloc(length(AFileName)+5);
   StrPCopy(FileName,AFileName);
   // to call a function outside of the current object, that has a method of
   // the same name, we simply place the units name in front of the function
   // eg: Archive.GetArchiveType(AFileName)
   ArchiveType := Archive.GetArchiveType(AFileName);

   TotOrigSize := 0;
   TotCompSize := 0;
   FileDate    := 0;
   case ArchiveType of
      atRARArchive : ReadRAR;
      else           ArchiveOK := False;
   end;
end;

destructor TArchive.Destroy;

var
   nCount : Integer;

begin
   // Free up the objects
   for nCount := 0 to FileList.Count-1 do begin
      TFileDetails(FileList.Items[nCount]).Free
   end;

   FileList.Free;
   StrDispose(FileName);
   inherited Destroy;
end;

function TArchive.GetTotalFiles : Integer;

begin
   GetTotalFiles := FileList.Count;
end;

function TArchive.GetArchiveOk : Boolean;

begin
   GetArchiveOk := ArchiveOk;
end;

function TArchive.GetArchiveType : Longint;

begin
   GetArchiveType := ArchiveType;
end;

function TArchive.GetTotalOriginalSize : Longint;

begin
   GetTotalOriginalSize := TotOrigSize;
end;

function TArchive.GetTotalCompressedSize : Longint;

begin
   GetTotalCompressedSize := TotCompSize;
end;

function TArchive.GetFileName : String;

begin
   GetFileName := StrPas(FileName);
end;

procedure TArchive.GetFileDate(var Year, Month, Day, Hour, Min, Sec, MSec : word);

var
   DateTime : TDateTime;

begin
   DateTime := FileDateToDateTime(FileDate);
   DecodeTime(DateTime,Hour,Min,Sec,MSec);
   DecodeDate(DateTIme,Year,Month,Day);
end;

function TArchive.GetDateTime : TDateTime;

begin
   GetDateTime := FileDateToDateTime(FileDate);
end;

function TArchive.GetAFileName(Index : Integer) : String;

var
   sResult : String;
   FileDetails : TFileDetails;

begin
   IF ((Index > 0) and (Index <= FileList.Count)) then begin
      sResult := StrPas(TFileDetails(FileList.Items[Index-1]).FileName);
   end else
      sResult := '';

   GetAFileName := sResult;
end;

end.


Image of arrow to previous article

Image of line

[HOME] [TABLE OF CONTENTS] [SEARCH]