Creating a Class to Access RAR Archives
The code behind this class looks like this...
{$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.
![]()