Contributor: EDWIN GROOTHUIS { OOAVTEST.PAS cut out each of the units below and compile to test the use of this package} uses OOAV,Dos; var a:PArchive; sr:SearchRec; DT:DateTime; begin writeln('avail: ',memavail); { It's not necessary that you call IdentifyArchive, but it's easy for checking when you've add new archive-types } case IdentifyArchive(paramstr(1)) of '?': writeln('Cannot open/identify current archive'); 'Z': writeln('It''s a ZIP-archive'); 'A': writeln('It''s an ARJ-archive'); 'L': writeln('It''s an LZH-archive'); 'C': writeln('It''s an ARC-archive'); 'O': writeln('It''s a ZOO-archive'); end; a:=New(PArchive,Init); if not a^.Name(paramstr(1)) then begin writeln('Cannot open file'); exit; end; writeln('Name':15,'Size':10,'Date':10,'Time':12); a^.FindFirst(sr); while sr.Name<>'' do begin write (sr.Name:15,sr.Size:10); UnpackTime(sr.Time,DT); writeln(dt.day:10,dt.month:3,dt.year:5,dt.hour:4,dt.min:3,dt.sec:3); a^.FindNext(sr); end; Dispose(A,Done); writeln('End'); writeln('avail: ',memavail); end. { the rest of the units follow } { CUT ----------------------------------------------------------- } { Object-Oriented Archive-viewer, version 3 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This Object-Oriented Archive-viewer (OOAV) is copyright (c) by Edwin Groothuis, MavEtJu software. You are free to use it if you agree with these three rules: 1. You tell me you're using this unit. 2. You give me proper credit in the documentation. (Like: "This program uses the Object-Oriented Archive-viewer (c) Edwin Groothuis, MavEtJu software". 3. If you make Archive-objects for other archive-types, don't hesitate to inform me so I can add them to the unit and redistribute it! That's all! How to use this unit: ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ (see also the file ArchTest.pas) - Declare a variable Arch of the var Arch:TArchive; type TArchive begin - Call it's constructor Arch.Init; - Tell the unit which file you if not Arch.Name('TEST.ZIP') want to view. This function then begin returns a boolean. If this writeln('TEST.ZIP is not boolean is false, then the a valid archive'); file couldn't be identified exit; as a valid archive. end; - Just like the dos-functions Arch.FindFirst(sr); FindFirst and FindNext, you while sr.Name<>'' do can search through the archive. begin The parameter you give with it writeln(sr.Name); is one of the SearchRec-type. Arch.FindNext(sr); If there are no more files in end; this archive, sr.Name will be empty. Valid fields are sr.Name, sr.Size and sr.Time - Call the destructor Arch.Done; end; - You can call the function IdentifyArchive() to see what kind of archive you're dealing with. What if you want to add more archive-types ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ - Add the unit name in the second Uses-statement. - Find out how to identify it and add that algoritm to the IdentifyArchive()-function. Please choose a unique and no-nonsens character to return. - Add it to the IdentifyArchive()-case in TArchive.Name. - Create a FindFirst-method and FindNext-method for this object. - That's it! Simple, isn't it? (If it isn't, please see the files ZipView, ArjView and others for examples ;-) Author: ÄÄÄÄÄÄÄ Edwin Groothuis email: Johann Strausslaan 1 edwing@stack.urc.tue.nl (valid until 10-94) 5583ZA Aalst-Waalre Edwin_Groothuis@p1.f205.n284.z2.gds.nl The Netherlands 2:284/205.1@fidonet 115:3145/102.1@pascal-net } unit OOAV; interface uses Dos; { General Archive, which is the father of all the specific archives. See OOAVZip, OOAVArj and others for examples. } type PGeneralArchive=^TGeneralArchive; TGeneralArchive=object _FArchive:file; constructor Init; destructor Done;virtual; procedure FindFirst(var sr:SearchRec);virtual; procedure FindNext(var sr:SearchRec);virtual; end; { TArchive is the object you're working with. See the documentation at the begin of this file for more information } type PArchive=^TArchive; TArchive=object constructor Init; destructor Done; function Name(const n:string):boolean; procedure FindFirst(var sr:SearchRec); procedure FindNext(var sr:SearchRec); private _Name:string; _Archive:PGeneralArchive; end; function IdentifyArchive(const Name:string):char; implementation uses Objects,Strings, OOAVZip,OOAVArj,OOAVLzh,OOAVArc,OOAVZoo; function IdentifyArchive(const Name:string):char; { returns: '?': unknown archive 'A': Arj-archive; 'Z': Zip-archive 'L': Lzh-archive 'C': Arc-archive 'O': Zoo-archive } var f:file; a:array[0..10] of char; bc:word; s:string; OldFileMode:byte; begin if Name='' then begin IdentifyArchive:='?'; exit; end; OldFileMode:=FileMode; FileMode:=0; assign(f,Name); {$I-}reset(f,1);{$I+} FileMode:=OldFileMode; if IOresult<>0 then begin IdentifyArchive:='?'; exit; end; blockread(f,a,sizeof(a),bc); close(f); if bc=0 then begin IdentifyArchive:='?'; exit; end; if (a[0]=#$60) and (a[1]=#$EA) then begin IdentifyArchive:='A'; { ARJ } exit; end; if (a[0]='P') and (a[1]='K') then begin IdentifyArchive:='Z'; { ZIP } exit; end; if a[0]=#$1A then begin IdentifyArchive:='C'; { ARC } exit; end; if (a[0]='Z') and (a[1]='O') and (a[2]='O') then begin IdentifyArchive:='O'; { ZOO } exit; end; s:=Name; for bc:=1 to length(s) do s[bc]:=upcase(s[bc]); if copy(s,pos('.',s),4)='.LZH' then begin IdentifyArchive:='L'; { LZH } exit; end; IdentifyArchive:='?'; end; constructor TGeneralArchive.Init; begin Abstract; end; destructor TGeneralArchive.Done; begin end; procedure TGeneralArchive.FindFirst(var sr:SearchRec); begin Abstract; end; procedure TGeneralArchive.FindNext(var sr:SearchRec); begin Abstract; end; constructor TArchive.Init; begin _Name:=''; _Archive:=nil; end; destructor TArchive.Done; begin if _Archive<>nil then begin close(_Archive^._FArchive); Dispose(_Archive,Done); end; end; function TArchive.Name(const n:string):boolean; var sr:SearchRec; OldFileMode:byte; begin if _Archive<>nil then begin close(_Archive^._FArchive); Dispose(_Archive,Done); _Archive:=nil; end; Name:=false; _Name:=n; Dos.FindFirst(_Name,anyfile,sr); if DosError<>0 then exit; case IdentifyArchive(_Name) of '?': exit; 'A': _Archive:=New(PArjArchive,Init); 'Z': _Archive:=New(PZipArchive,Init); 'L': _Archive:=New(PLzhArchive,Init); 'C': _Archive:=New(PArcArchive,Init); 'O': _Archive:=New(PZooArchive,Init); end; OldFileMode:=FileMode; FileMode:=0; Assign(_Archive^._FArchive,n); {$I-}reset(_Archive^._FArchive,1);{$I+} FileMode:=OldFileMode; if IOresult<>0 then begin Dispose(_Archive); exit; end; Name:=true; end; procedure TArchive.FindFirst(var sr:SearchRec); begin FillChar(sr,sizeof(sr),0); if _Archive=nil then exit; _Archive^.FindFirst(sr); end; procedure TArchive.FindNext(var sr:SearchRec); begin FillChar(sr,sizeof(sr),0); if _Archive=nil then exit; _Archive^.FindNext(sr); end; end. { CUT ----------------------------------------------------------- } { Object-Oriented Archive-viewer: ARC-part } unit OOAVArc; interface uses Dos,OOAV; Type AFHeader = Record HeadId : byte; DataType : byte; { 0 = no more data } Name : array[0..12] of char; CompSize : longint; FileDate : word; FileTime : word; Crc : word; OrigSize : longint; end; type PArcArchive=^TArcArchive; TArcArchive=object(TGeneralArchive) constructor Init; procedure FindFirst(var sr:SearchRec);virtual; procedure FindNext(var sr:SearchRec);virtual; private _FHdr:AFHeader; _SL:longint; procedure GetHeader(var sr:SearchRec); end; implementation const BSize=4096; var BUFF:array[1..BSize] of Byte; constructor TArcArchive.Init; begin FillChar(_FHdr,sizeof(_FHdr),0); end; procedure TArcArchive.GetHeader(var sr:SearchRec); var bc:word; b:byte; begin FillChar(_FHdr,SizeOf(_FHdr),#0); FillChar(BUFF,BSize,#0); Seek(_FArchive,_SL); BlockRead(_FArchive,BUFF,BSIZE,bc); Move(BUFF[1],_FHdr,SizeOf(_FHdr)); with _FHdr do begin if DataType<>0 then begin b:=0;sr.Name:=''; while Name[b]<>#0 do begin if Name[b]='/' then sr.Name:='' else sr.Name:=sr.Name+Name[b]; inc(b); end; sr.Size:=OrigSize; if DataType=0 then sr.Size:=0; sr.Time:=FileDate*longint(256*256)+FileTime; inc(_SL,CompSize); inc(_SL,sizeof(_FHDR)); end; end; end; Procedure TArcArchive.FindFirst(var sr:SearchRec); begin _SL:=0; GetHeader(sr); end; procedure TArcArchive.FindNext(var sr:SearchRec); begin GetHeader(sr); end; end. { CUT ----------------------------------------------------------- } { Object-Oriented Archive-viewer: ARJ-part } unit OOAVArj; interface uses Dos,OOAV; Type AFHeader = Record HeadId : Word; { 60000 } BHdrSz : Word; { Basic Header Size } FHdrSz : Byte; { File Header Size } AVNo : Byte; MAVX : Byte; HostOS : Byte; Flags : Byte; SVer : Byte; FType : Byte; { must be 2 for basic header } Res1 : Byte; DOS_DT : LongInt; CSize : LongInt; { Compressed Size } OSize : LongInt; { Original Size } SEFP : LongInt; FSFPos : Word; SEDLgn : Word; Res2 : Word; NameDat : array[1..120] of char;{ start of Name, etc. } Res3 : array[1..10] of char; end; type PArjArchive=^TArjArchive; TArjArchive=object(TGeneralArchive) constructor Init; procedure FindFirst(var sr:SearchRec);virtual; procedure FindNext(var sr:SearchRec);virtual; private _FHdr:AFHeader; _SL:longint; procedure GetHeader(var sr:SearchRec); end; implementation const BSize=4096; var BUFF:array[1..BSize] of Byte; constructor TArjArchive.Init; begin FillChar(_FHdr,sizeof(_FHdr),0); end; procedure TArjArchive.GetHeader(var sr:SearchRec); var bc:word; b:byte; begin FillChar(_FHdr,SizeOf(_FHdr),#0); FillChar(BUFF,BSize,#0); Seek(_FArchive,_SL); BlockRead(_FArchive,BUFF,BSIZE,bc); Move(BUFF[1],_FHdr,SizeOf(_FHdr)); with _FHdr do begin if BHdrSz>0 then begin b:=1;sr.Name:=''; while NameDat[b]<>#0 do begin if NameDat[b]='/' then sr.Name:='' else sr.Name:=sr.Name+NameDat[b]; inc(b); end; sr.Size:=BHdrSz+CSize; if FType=2 then sr.Size:=BHdrSz; if BHdrSz=0 then sr.Size:=0; inc(_SL,sr.Size+10); sr.Time:=DOS_DT; end; end; end; Procedure TArjArchive.FindFirst(var sr:SearchRec); begin _SL:=0; GetHeader(sr); GetHeader(sr); { Why a call to GetHeader() twice? Because ARJ stores the name of the archive in the first field } end; procedure TArjArchive.FindNext(var sr:SearchRec); begin GetHeader(sr); end; end. { CUT ----------------------------------------------------------- } { Object-Oriented Archive-viewer: LZH-part } Unit OOAVLzh; Interface Uses Dos,OOAV; Type LFHeader=Record Headsize,Headchk :byte; HeadID :packed Array[1..5] of char; Packsize,Origsize,Filetime:longint; Attr :word; Filename :string[12]; f32 :pathstr; dt :DateTime; end; type PLzhArchive=^TLzhArchive; TLzhArchive=object(TGeneralArchive) constructor Init; procedure FindFirst(var sr:SearchRec);virtual; procedure FindNext(var sr:SearchRec);virtual; private _FHdr:LFHeader; _SL:longint; procedure GetHeader(var sr:SearchRec); end; Implementation constructor TLzhArchive.Init; begin _SL:=0; FillChar(_FHdr,sizeof(_FHdr),0); end; procedure TLzhArchive.GetHeader(var sr:SearchRec); var nr:word; begin fillchar(sr,sizeof(sr),0); seek(_FArchive,_SL); if eof(_FArchive) then exit; blockread(_FArchive,_FHdr,sizeof(LFHeader),nr); if _FHdr.headsize=0 then exit; inc(_SL,_FHdr.headsize); inc(_SL,2); inc(_SL,_FHdr.packsize); if _FHdr.headsize<>0 then UnPackTime(_FHdr.FileTime,_FHdr.DT); sr.Name:=_FHdr.FileName; sr.Size:=_FHdr.OrigSize; sr.Time:=_FHdr.FileTime; end; procedure TLzhArchive.FindFirst(var sr:SearchRec); begin _SL:=0; GetHeader(sr); end; procedure TLzhArchive.FindNext(var sr:SearchRec); begin GetHeader(sr); end; end. { CUT ----------------------------------------------------------- } { Object-Oriented Archive-viewer: ZIP-part } Unit OOAVZip; Interface Uses Dos,OOAV; Type ZFHeader=Record Signature :longint; Version,GPBFlag,Compress,Date,Time:word; CRC32,CSize,USize :longint; FNameLen,ExtraField :word; end; type PZipArchive=^TZipArchive; TZipArchive=object(TGeneralArchive) constructor Init; procedure FindFirst(var sr:SearchRec);virtual; procedure FindNext(var sr:SearchRec);virtual; private Hdr:ZFHeader; procedure GetHeader(var sr:SearchRec); end; implementation Const SIG = $04034B50; { Signature } constructor TZipArchive.Init; begin FillChar(Hdr,sizeof(Hdr),0); end; procedure TZipArchive.GetHeader(var sr:SearchRec); var b:byte; bc:word; begin fillchar(sr,sizeof(sr),0); if eof(_FArchive) then exit; BlockRead(_FArchive,Hdr,SizeOf(Hdr),bc); if bc<>Sizeof(Hdr) then exit; { Why checking for Hdr.FNamelen=0? Because the comments inserted in a ZIP-file are at the last field } if Hdr.FNameLen=0 then exit; sr.Name:=''; Repeat BlockRead(_FArchive,b,1); If b<>0 Then sr.Name:=sr.Name+Chr(b); Until (length(sr.Name)=Hdr.FNameLen) or (b=0); if b=0 then exit; Seek(_FArchive,FilePos(_FArchive)+Hdr.CSize+Hdr.ExtraField); sr.Size:=Hdr.USize; sr.Time:=Hdr.Date+Hdr.Time*longint(256*256); end; Procedure TZipArchive.FindFirst(var sr:SearchRec); begin GetHeader(sr); end; Procedure TZipArchive.FindNext(var sr:SearchRec); begin GetHeader(sr); end; end. { CUT ----------------------------------------------------------- } { Object-Oriented Archive-viewer: ZOO-part } unit OOAVZoo; interface uses Dos,OOAV; const SIZ_TEXT=20; const FNAMESIZE=13; const MAX_PACK=1; const LO_TAG=$a7dc; const HI_TAG=$fdc4; type ZFHeader=record lo_tag:word; hi_tag:word; _type:byte; packing_method:byte; next:longint; { pos'n of next directory entry } offset:longint; date:word; { DOS format date } time:word; { DOS format time } file_crc:word; { CRC of this file } org_size:longint; size_now:longint; major_ver:byte; minor_ver:byte; deleted:boolean; comment:longint; { points to comment; zero if none } cmt_size:word; { length of comment, 0 if none } unknown:byte; fname:array[0..FNAMESIZE-1] of char; end; type PZooArchive=^TZooArchive; TZooArchive=object(TGeneralArchive) constructor Init; procedure FindFirst(var sr:SearchRec);virtual; procedure FindNext(var sr:SearchRec);virtual; private _FHdr:ZFHeader; procedure GetHeader; procedure GetEntry(var sr:SearchRec); end; implementation type zooHeader=record text:array[0..SIZ_TEXT-1] of char; lo_tag:word; hi_tag:word; start:longint; minus:longint; major_ver:char; minor_ver:char; end; constructor TZooArchive.Init; begin FillChar(_FHdr,sizeof(_FHdr),0); end; procedure TZooArchive.GetHeader; var hdr:zooHeader; bc:word; begin seek(_FArchive,0); BlockRead(_FArchive,hdr,sizeof(hdr),bc); seek(_FArchive,hdr.start); end; procedure TZooArchive.GetEntry(var sr:SearchRec); var bc:word; b:byte; begin FillChar(_FHdr,SizeOf(_FHdr),#0); BlockRead(_FArchive,_FHdr,sizeof(_FHdr),bc); with _FHdr do begin if _Type<>0 then begin b:=0;sr.Name:=''; while FName[b]<>#0 do begin if FName[b]='/' then sr.Name:='' else sr.Name:=sr.Name+FName[b]; inc(b); end; sr.Size:=Org_Size; if _Type=0 then sr.Size:=0; sr.Time:=Date*longint(256*256)+Time; Seek(_FArchive,_FHdr.next); end; end; end; procedure TZooArchive.FindFirst(var sr:SearchRec); begin GetHeader; GetEntry(sr); end; procedure TZooArchive.FindNext(var sr:SearchRec); begin GetEntry(sr); end; end.