1193 lines
38 KiB
Plaintext
1193 lines
38 KiB
Plaintext
Program DeZip;
|
|
|
|
{ DeZip v1.5 (C) Copyright 1989 by R. P. Byrne }
|
|
{ }
|
|
{ This is a "bare-bones" program to extract files from ZIP archives. }
|
|
{ By "bare-bones", I mean that there is no facility included to do anything }
|
|
{ but extraction (ie. no echo to console, no send to printer, etc.). }
|
|
{ If relative pathnames are stored in the Zip file, make sure all of the }
|
|
{ required directories exist on your system before attempting an }
|
|
{ extraction. }
|
|
|
|
{$M 10240, 0, 0} { Stack, Min. Heap, Max. Heap}
|
|
{$F+} { Force far calls }
|
|
|
|
Uses
|
|
Dos,
|
|
Crt,
|
|
MemAlloc,
|
|
StrProcs;
|
|
|
|
Const
|
|
COPYRIGHT = 'DeZip (C) Copyright 1989 by R. P. Byrne';
|
|
VERSION = 'Version 1.5 - Compiled on March 11, 1989';
|
|
|
|
{ Stuff needed generically by all uncompression methods }
|
|
|
|
Const
|
|
MAXNAMES = 20;
|
|
|
|
Var
|
|
InFileSpecs : Array[1..MAXNAMES] of String; { Input file specifications }
|
|
MaxSpecs : Word; { Total number of entries in InFileSpecs array }
|
|
OutPath : String; { Output path specification }
|
|
|
|
TenPercent : LongInt;
|
|
|
|
{ Define ZIP file header types }
|
|
|
|
Const
|
|
LOCAL_FILE_HEADER_SIGNATURE = $04034B50;
|
|
|
|
Type
|
|
Local_File_Header_Type = Record
|
|
{ Signature : LongInt; }
|
|
Extract_Version_Reqd : Word;
|
|
Bit_Flag : Word;
|
|
Compress_Method : Word;
|
|
Last_Mod_Time : Word;
|
|
Last_Mod_Date : Word;
|
|
Crc32 : LongInt;
|
|
Compressed_Size : LongInt;
|
|
Uncompressed_Size : LongInt;
|
|
Filename_Length : Word;
|
|
Extra_Field_Length : Word;
|
|
end;
|
|
|
|
Const
|
|
CENTRAL_FILE_HEADER_SIGNATURE = $02014B50;
|
|
|
|
Type
|
|
Central_File_Header_Type = Record
|
|
{ Signature : LongInt; }
|
|
MadeBy_Version : Word;
|
|
Extract_Version_Reqd : Word;
|
|
Bit_Flag : Word;
|
|
Compress_Method : Word;
|
|
Last_Mod_Time : Word;
|
|
Last_Mod_Date : Word;
|
|
Crc32 : LongInt;
|
|
Compressed_Size : LongInt;
|
|
Uncompressed_Size : LongInt;
|
|
Filename_Length : Word;
|
|
Extra_Field_Length : Word;
|
|
File_Comment_Length : Word;
|
|
Starting_Disk_Num : Word;
|
|
Internal_Attributes : Word;
|
|
External_Attributes : LongInt;
|
|
Local_Header_Offset : LongInt;
|
|
End;
|
|
|
|
Const
|
|
END_OF_CENTRAL_DIR_SIGNATURE = $06054B50;
|
|
|
|
Type
|
|
End_of_Central_Dir_Type = Record
|
|
{ Signature : LongInt; }
|
|
Disk_Number : Word;
|
|
Central_Dir_Start_Disk : Word;
|
|
Entries_This_Disk : Word;
|
|
Total_Entries : Word;
|
|
Central_Dir_Size : LongInt;
|
|
Start_Disk_Offset : LongInt;
|
|
ZipFile_Comment_Length : Word;
|
|
end;
|
|
|
|
Const
|
|
CRC_32_TAB : Array[0..255] of LongInt = (
|
|
$00000000, $77073096, $ee0e612c, $990951ba, $076dc419, $706af48f, $e963a535, $9e6495a3,
|
|
$0edb8832, $79dcb8a4, $e0d5e91e, $97d2d988, $09b64c2b, $7eb17cbd, $e7b82d07, $90bf1d91,
|
|
$1db71064, $6ab020f2, $f3b97148, $84be41de, $1adad47d, $6ddde4eb, $f4d4b551, $83d385c7,
|
|
$136c9856, $646ba8c0, $fd62f97a, $8a65c9ec, $14015c4f, $63066cd9, $fa0f3d63, $8d080df5,
|
|
$3b6e20c8, $4c69105e, $d56041e4, $a2677172, $3c03e4d1, $4b04d447, $d20d85fd, $a50ab56b,
|
|
$35b5a8fa, $42b2986c, $dbbbc9d6, $acbcf940, $32d86ce3, $45df5c75, $dcd60dcf, $abd13d59,
|
|
$26d930ac, $51de003a, $c8d75180, $bfd06116, $21b4f4b5, $56b3c423, $cfba9599, $b8bda50f,
|
|
$2802b89e, $5f058808, $c60cd9b2, $b10be924, $2f6f7c87, $58684c11, $c1611dab, $b6662d3d,
|
|
$76dc4190, $01db7106, $98d220bc, $efd5102a, $71b18589, $06b6b51f, $9fbfe4a5, $e8b8d433,
|
|
$7807c9a2, $0f00f934, $9609a88e, $e10e9818, $7f6a0dbb, $086d3d2d, $91646c97, $e6635c01,
|
|
$6b6b51f4, $1c6c6162, $856530d8, $f262004e, $6c0695ed, $1b01a57b, $8208f4c1, $f50fc457,
|
|
$65b0d9c6, $12b7e950, $8bbeb8ea, $fcb9887c, $62dd1ddf, $15da2d49, $8cd37cf3, $fbd44c65,
|
|
$4db26158, $3ab551ce, $a3bc0074, $d4bb30e2, $4adfa541, $3dd895d7, $a4d1c46d, $d3d6f4fb,
|
|
$4369e96a, $346ed9fc, $ad678846, $da60b8d0, $44042d73, $33031de5, $aa0a4c5f, $dd0d7cc9,
|
|
$5005713c, $270241aa, $be0b1010, $c90c2086, $5768b525, $206f85b3, $b966d409, $ce61e49f,
|
|
$5edef90e, $29d9c998, $b0d09822, $c7d7a8b4, $59b33d17, $2eb40d81, $b7bd5c3b, $c0ba6cad,
|
|
$edb88320, $9abfb3b6, $03b6e20c, $74b1d29a, $ead54739, $9dd277af, $04db2615, $73dc1683,
|
|
$e3630b12, $94643b84, $0d6d6a3e, $7a6a5aa8, $e40ecf0b, $9309ff9d, $0a00ae27, $7d079eb1,
|
|
$f00f9344, $8708a3d2, $1e01f268, $6906c2fe, $f762575d, $806567cb, $196c3671, $6e6b06e7,
|
|
$fed41b76, $89d32be0, $10da7a5a, $67dd4acc, $f9b9df6f, $8ebeeff9, $17b7be43, $60b08ed5,
|
|
$d6d6a3e8, $a1d1937e, $38d8c2c4, $4fdff252, $d1bb67f1, $a6bc5767, $3fb506dd, $48b2364b,
|
|
$d80d2bda, $af0a1b4c, $36034af6, $41047a60, $df60efc3, $a867df55, $316e8eef, $4669be79,
|
|
$cb61b38c, $bc66831a, $256fd2a0, $5268e236, $cc0c7795, $bb0b4703, $220216b9, $5505262f,
|
|
$c5ba3bbe, $b2bd0b28, $2bb45a92, $5cb36a04, $c2d7ffa7, $b5d0cf31, $2cd99e8b, $5bdeae1d,
|
|
$9b64c2b0, $ec63f226, $756aa39c, $026d930a, $9c0906a9, $eb0e363f, $72076785, $05005713,
|
|
$95bf4a82, $e2b87a14, $7bb12bae, $0cb61b38, $92d28e9b, $e5d5be0d, $7cdcefb7, $0bdbdf21,
|
|
$86d3d2d4, $f1d4e242, $68ddb3f8, $1fda836e, $81be16cd, $f6b9265b, $6fb077e1, $18b74777,
|
|
$88085ae6, $ff0f6a70, $66063bca, $11010b5c, $8f659eff, $f862ae69, $616bffd3, $166ccf45,
|
|
$a00ae278, $d70dd2ee, $4e048354, $3903b3c2, $a7672661, $d06016f7, $4969474d, $3e6e77db,
|
|
$aed16a4a, $d9d65adc, $40df0b66, $37d83bf0, $a9bcae53, $debb9ec5, $47b2cf7f, $30b5ffe9,
|
|
$bdbdf21c, $cabac28a, $53b39330, $24b4a3a6, $bad03605, $cdd70693, $54de5729, $23d967bf,
|
|
$b3667a2e, $c4614ab8, $5d681b02, $2a6f2b94, $b40bbe37, $c30c8ea1, $5a05df1b, $2d02ef8d
|
|
);
|
|
|
|
Const
|
|
BUFSIZE = 8192; { Size of buffers for I/O }
|
|
|
|
Type
|
|
BufPtr = ^BufType;
|
|
BufType = Array[1..BUFSIZE] of Byte;
|
|
|
|
Var
|
|
ZipName : String; { Name of Zip file to be processed }
|
|
ZipFile : File; { Zip file variable }
|
|
EndFile : Boolean; { End of file indicator for ZipFile }
|
|
ZipBuf : BufPtr; { Input buffer for ZipFile }
|
|
ZipPtr : Word; { Index for ZipFile input buffer }
|
|
ZipCount : Word; { Count of bytes in ZipFile input buffer }
|
|
|
|
ExtFile : File; { Output file variable }
|
|
ExtBuf : BufPtr; { Output buffer for ExtFile }
|
|
ExtPtr : Word; { Index for ExtFile output buffer }
|
|
ExtCount : LongInt; { Count of characters written to output }
|
|
|
|
LocalHdr : Local_File_Header_Type; { Storage for a local file hdr }
|
|
Hdr_FileName : String;
|
|
Hdr_ExtraField : String;
|
|
Hdr_Comment : String;
|
|
|
|
Crc32Val : LongInt; { Running CRC (32 bit) value }
|
|
|
|
Bytes_To_Go : LongInt; { Bytes left to process in compressed file }
|
|
|
|
|
|
{ Stuff needed for unSHRINKing }
|
|
|
|
Const
|
|
MINCODESIZE = 9;
|
|
MAXCODESIZE = 13;
|
|
SPECIAL = 256;
|
|
FIRSTFREE = 257;
|
|
LZW_TABLE_SIZE = (1 SHL MAXCODESIZE) - 1; { 0..8191 }
|
|
LZW_STACK_SIZE = (1 SHL MAXCODESIZE) - 1; { 0..8191 }
|
|
|
|
Type
|
|
|
|
LZW_Table_Rec = Record
|
|
Prefix : Integer;
|
|
Suffix : Byte;
|
|
ChildCount : Word; { If ChildCount = 0 then leaf node }
|
|
end;
|
|
LZW_Table_Ptr = ^LZW_Table_Type;
|
|
LZW_Table_Type = Array[0..LZW_TABLE_SIZE] of LZW_Table_Rec;
|
|
|
|
FreeListPtr = ^FreeListArray;
|
|
FreeListArray = Array[FIRSTFREE..LZW_TABLE_SIZE] of Word;
|
|
|
|
StackPtr = ^StackType;
|
|
StackType = Array[0..LZW_STACK_SIZE] of Word;
|
|
|
|
Var
|
|
LZW_Table : LZW_Table_Ptr; { Code table for LZW decoding }
|
|
FreeList : FreeListPtr; { List of free table entries }
|
|
NextFree : Word; { Index for free list array }
|
|
{ FreeList^[NextFree] always contains the }
|
|
{ index of the next available entry in }
|
|
{ the LZW Prefix:Suffix table (LZW_Table^) }
|
|
LZW_Stack : StackPtr; { A stack used to build decoded strings }
|
|
StackIdx : Word; { Stack array index variable }
|
|
{ StackIdx always points to the next }
|
|
{ available entry in the stack }
|
|
SaveByte : Byte; { Our input code buffer - 1 byte long }
|
|
BitsLeft : Byte; { Unprocessed bits in the input code buffer }
|
|
FirstCh : Boolean; { Flag indicating first char being processed }
|
|
|
|
|
|
{ Stuff needed for unREDUCEing }
|
|
|
|
Type
|
|
FollowerSet = Record
|
|
SetSize : Word;
|
|
FSet : Array[0..31] of Byte;
|
|
end;
|
|
FollowerPtr = ^FollowerArray;
|
|
FollowerArray = Array[0..255] of FollowerSet;
|
|
|
|
StreamPtr = ^StreamArray;
|
|
StreamArray = Array[0..4095] of Byte;
|
|
|
|
Var
|
|
Followers : FollowerPtr;
|
|
Stream : StreamPtr; { The output stream }
|
|
StreamIdx : Word; { Always points to next pos. to be filled }
|
|
State : Byte;
|
|
Len : Word;
|
|
V : Byte;
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Abort(Msg : String);
|
|
Begin
|
|
Writeln;
|
|
Writeln(Msg);
|
|
Writeln('Returning to DOS');
|
|
Writeln;
|
|
Halt;
|
|
end {Abort};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Syntax;
|
|
Begin
|
|
Writeln('Usage: DeZip ZipFileName [OutPathSpec] [FileSpec [...]]');
|
|
Writeln;
|
|
Writeln('Optional file specifications may contain DOS ');
|
|
Writeln('wildcard characters.');
|
|
Writeln;
|
|
Writeln('If no filespecs are entered, *.* is assumed.');
|
|
Writeln;
|
|
Halt;
|
|
End;
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function HexLInt(L : LongInt) : String;
|
|
Type
|
|
HexType = Array [0..15] of Char;
|
|
Const
|
|
HexChar : HexType = ('0','1','2','3','4','5','6','7',
|
|
'8','9','A','B','C','D','E','F');
|
|
Begin
|
|
HexLInt := HexChar[(L AND $F0000000) SHR 28] +
|
|
HexChar[(L AND $0F000000) SHR 24] +
|
|
HexChar[(L AND $00F00000) SHR 20] +
|
|
HexChar[(L AND $000F0000) SHR 16] +
|
|
HexChar[(L AND $0000F000) SHR 12] +
|
|
HexChar[(L AND $00000F00) SHR 8] +
|
|
HexChar[(L AND $000000F0) SHR 4] +
|
|
HexChar[(L AND $0000000F) ] +
|
|
'h';
|
|
end {HexLInt};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function IO_Test : Boolean;
|
|
Var
|
|
ErrorCode : Word;
|
|
CodeStr : String;
|
|
Ok : Boolean;
|
|
Begin
|
|
Ok := TRUE;
|
|
ErrorCode := IOResult;
|
|
If ErrorCode <> 0 then begin
|
|
Ok := FALSE;
|
|
Case ErrorCode of
|
|
2 : Writeln('File Not Found');
|
|
3 : Writeln('Path Not Found');
|
|
5 : Writeln('Access Denied');
|
|
101 : Writeln('Disk Full');
|
|
else Writeln('I/O Error # ', ErrorCode);
|
|
end {Case};
|
|
end {if};
|
|
IO_Test := Ok;
|
|
end {IO_Test};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Load_Parms;
|
|
Var
|
|
I : Word;
|
|
Name : String;
|
|
DosDTA : SearchRec;
|
|
Begin
|
|
I := ParamCount;
|
|
If I < 1 then
|
|
Syntax;
|
|
|
|
ZipName := ParamStr(1);
|
|
For I := 1 to Length(ZipName) do
|
|
ZipName[I] := UpCase(ZipName[I]);
|
|
If Pos('.', ZipName) = 0 then
|
|
ZipName := ZipName + '.ZIP';
|
|
|
|
MaxSpecs := 0;
|
|
OutPath := '';
|
|
I := 1;
|
|
While I < ParamCount do begin
|
|
Inc(I);
|
|
Name := ParamStr(I);
|
|
If Name[length(Name)] = '\' then
|
|
Delete(Name, length(Name), 1);
|
|
FindFirst(Name, DIRECTORY, DosDTA); { outpath spec? }
|
|
If DosError = 0 then begin
|
|
If (DosDTA.Attr AND DIRECTORY) <> 0 then begin { yup }
|
|
OutPath := Name;
|
|
If OutPath[Length(OutPath)] <> '\' then
|
|
OutPath := OutPath + '\';
|
|
end {then}
|
|
else begin
|
|
If MaxSpecs < MAXNAMES then begin
|
|
Inc(MaxSpecs);
|
|
InFileSpecs[MaxSpecs] := Name;
|
|
end {if};
|
|
end {if};
|
|
end {then}
|
|
else begin
|
|
If MaxSpecs < MAXNAMES then begin
|
|
Inc(MaxSpecs);
|
|
InFileSpecs[MaxSpecs] := Name;
|
|
end {if};
|
|
end {if}
|
|
end {while};
|
|
|
|
If MaxSpecs = 0 then begin
|
|
MaxSpecs := 1;
|
|
InFileSpecs[1] := '*.*';
|
|
end {if};
|
|
|
|
end {Load_Parms};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Initialize;
|
|
Var
|
|
Code : Integer;
|
|
Begin
|
|
Code := Malloc(ZipBuf, SizeOf(ZipBuf^)) OR
|
|
Malloc(ExtBuf, SizeOf(ExtBuf^));
|
|
If Code <> 0 then
|
|
Abort('Not enough memory available to allocate I/O buffers!');
|
|
end {Initialize};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
{ Converted to Turbo Pascal (tm) V4.0 March, 1988 by J.R.Louvau }
|
|
{ COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or }
|
|
{ code or tables extracted from it, as desired without restriction. }
|
|
{ }
|
|
{ First, the polynomial itself and its table of feedback terms. The }
|
|
{ polynomial is }
|
|
{ X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 }
|
|
{ }
|
|
{ Note that we take it "backwards" and put the highest-order term in }
|
|
{ the lowest-order bit. The X^32 term is "implied"; the LSB is the }
|
|
{ X^31 term, etc. The X^0 term (usually shown as "+1") results in }
|
|
{ the MSB being 1. }
|
|
{ }
|
|
{ Note that the usual hardware shift register implementation, which }
|
|
{ is what we're using (we're merely optimizing it by doing eight-bit }
|
|
{ chunks at a time) shifts bits into the lowest-order term. In our }
|
|
{ implementation, that means shifting towards the right. Why do we }
|
|
{ do it this way? Because the calculated CRC must be transmitted in }
|
|
{ order from highest-order term to lowest-order term. UARTs transmit }
|
|
{ characters in order from LSB to MSB. By storing the CRC this way, }
|
|
{ we hand it to the UART in the order low-byte to high-byte; the UART }
|
|
{ sends each low-bit to hight-bit; and the result is transmission bit }
|
|
{ by bit from highest- to lowest-order term without requiring any bit }
|
|
{ shuffling on our part. Reception works similarly. }
|
|
{ }
|
|
{ The feedback terms table consists of 256, 32-bit entries. Notes: }
|
|
{ }
|
|
{ The table can be generated at runtime if desired; code to do so }
|
|
{ is shown later. It might not be obvious, but the feedback }
|
|
{ terms simply represent the results of eight shift/xor opera- }
|
|
{ tions for all combinations of data and CRC register values. }
|
|
{ }
|
|
{ The values must be right-shifted by eight bits by the "updcrc" }
|
|
{ logic; the shift must be unsigned (bring in zeroes). On some }
|
|
{ hardware you could probably optimize the shift in assembler by }
|
|
{ using byte-swap instructions. }
|
|
{ polynomial $edb88320 }
|
|
{ }
|
|
|
|
Function UpdC32(Octet: Byte; Crc: LongInt) : LongInt;
|
|
Var
|
|
L : LongInt;
|
|
W : Array[1..4] of Byte Absolute L;
|
|
Begin
|
|
|
|
UpdC32 := CRC_32_TAB[Byte(Crc XOR LongInt(Octet))] XOR ((Crc SHR 8) AND $00FFFFFF);
|
|
|
|
end {UpdC32};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Read_Zip_Block;
|
|
Begin
|
|
BlockRead(ZipFile, ZipBuf^, BUFSIZE, ZipCount);
|
|
If ZipCount = 0 then
|
|
EndFile := TRUE;
|
|
ZipPtr := 1;
|
|
End {Read_Zip_Block};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Write_Ext_Block;
|
|
Begin
|
|
If ExtPtr > 1 then begin
|
|
BlockWrite(ExtFile, ExtBuf^, Pred(ExtPtr));
|
|
If NOT IO_Test then
|
|
Halt;
|
|
ExtPtr := 1;
|
|
end {if};
|
|
End {Write_Ext_Block};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Open_Zip;
|
|
Begin
|
|
Assign(ZipFile, ZipName);
|
|
FileMode := 64; { Read Only / Deny None }
|
|
{$I-} Reset(ZipFile, 1) {$I+};
|
|
If NOT IO_Test then
|
|
Halt;
|
|
EndFile := FALSE;
|
|
Read_Zip_Block;
|
|
End {Open_Zip};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function Open_Ext : Boolean;
|
|
Begin
|
|
Assign(ExtFile, OutPath + Hdr_FileName);
|
|
FileMode := 66; { Read & Write / Deny None }
|
|
{$I-} Rewrite(ExtFile, 1) {$I+};
|
|
If NOT IO_Test then
|
|
Open_Ext := FALSE
|
|
else begin
|
|
ExtPtr := 1;
|
|
Open_Ext := TRUE;
|
|
end {if};
|
|
end {Open_Ext};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function Get_Zip : Integer;
|
|
Begin
|
|
If ZipPtr > ZipCount then
|
|
Read_Zip_Block;
|
|
|
|
If EndFile then
|
|
Get_Zip := -1
|
|
else begin
|
|
Get_Zip := ZipBuf^[ZipPtr];
|
|
Inc(ZipPtr);
|
|
end {if};
|
|
end {Get_Zip};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Put_Ext(C : Byte);
|
|
Begin
|
|
Crc32Val := UpdC32(C, Crc32Val);
|
|
ExtBuf^[ExtPtr] := C;
|
|
Inc(ExtPtr);
|
|
Inc(ExtCount);
|
|
If ExtPtr > BUFSIZE then
|
|
Write_Ext_Block;
|
|
end {Put_Ext};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Close_Zip;
|
|
Begin
|
|
{$I-} Close(Zipfile) {$I+};
|
|
If IO_Test then ;
|
|
end {Close_Zip};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Close_Ext;
|
|
Type
|
|
TimeDateRec = Record
|
|
Time : Word;
|
|
Date : Word;
|
|
end {record};
|
|
Var
|
|
TimeDate : TimeDateRec;
|
|
TimeDateStamp : LongInt Absolute TimeDate;
|
|
Begin
|
|
Write_Ext_Block;
|
|
TimeDate.Time := LocalHdr.Last_Mod_Time;
|
|
TimeDate.Date := LocalHdr.Last_Mod_Date;
|
|
SetFTime(ExtFile, TimeDateStamp);
|
|
{$I-} Close(ExtFile) {$I+};
|
|
If IO_Test then ;
|
|
GotoXY(1, WhereY);
|
|
Write(ExtCount);
|
|
GotoXY(1, WhereY);
|
|
end {Close_Ext};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure FSkip(Offset : LongInt);
|
|
Var
|
|
Rec : LongInt;
|
|
Begin
|
|
If (Offset + ZipPtr) <= ZipCount then
|
|
Inc(ZipPtr, Offset)
|
|
else begin
|
|
Rec := FilePos(ZipFile) + (Offset - (ZipCount - ZipPtr) - 1);
|
|
{$I-} Seek(ZipFile, Rec) {$I+};
|
|
If NOT IO_Test then
|
|
Halt;
|
|
Read_Zip_Block;
|
|
end {if};
|
|
end {FSkip};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure FRead(Var Buf; RecLen : Word);
|
|
Var
|
|
I : Word;
|
|
B : Array[1..MaxInt] of Byte Absolute Buf;
|
|
Begin
|
|
For I := 1 to RecLen do
|
|
B[I] := Get_Zip;
|
|
end {FRead};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function Read_Local_Hdr : Boolean;
|
|
Var
|
|
Sig : LongInt;
|
|
Begin
|
|
If EndFile then
|
|
Read_Local_Hdr := FALSE
|
|
else begin
|
|
FRead(Sig, SizeOf(Sig));
|
|
If Sig = CENTRAL_FILE_HEADER_SIGNATURE then begin
|
|
Read_Local_Hdr := FALSE;
|
|
EndFile := TRUE;
|
|
end {then}
|
|
else begin
|
|
If Sig <> LOCAL_FILE_HEADER_SIGNATURE then
|
|
Abort('Missing or invalid local file header in ' + ZipName);
|
|
FRead(LocalHdr, SizeOf(LocalHdr));
|
|
With LocalHdr do begin
|
|
If FileName_Length > 255 then
|
|
Abort('Filename of compressed file exceeds 255 characters!');
|
|
FRead(Hdr_FileName[1], FileName_Length);
|
|
Hdr_FileName[0] := Chr(FileName_Length);
|
|
If Extra_Field_Length > 255 then
|
|
Abort('Extra field of compressed file exceeds 255 characters!');
|
|
FRead(Hdr_ExtraField[1], Extra_Field_Length);
|
|
Hdr_ExtraField[0] := Chr(Extra_Field_Length);
|
|
end {with};
|
|
Read_Local_Hdr := TRUE;
|
|
end {if};
|
|
end {if};
|
|
end {Read_Local_Hdr};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function Get_Compressed : Integer;
|
|
Var
|
|
PctDone : Integer;
|
|
Begin
|
|
If Bytes_To_Go = 0 then
|
|
Get_Compressed := -1
|
|
else begin
|
|
Get_Compressed := Get_Zip;
|
|
If Bytes_To_Go mod TenPercent = 0 then begin
|
|
PctDone := 100 - Round( 100 * (Bytes_To_Go / LocalHdr.Compressed_Size));
|
|
GotoXY(WhereX - 4, WhereY);
|
|
Write(PctDone:3, '%');
|
|
end {if};
|
|
Dec(Bytes_To_Go);
|
|
end {if};
|
|
end {Get_Compressed};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function LZW_Init : Boolean;
|
|
Var
|
|
RC : Word;
|
|
I : Word;
|
|
Label
|
|
Exit;
|
|
Begin
|
|
{ Initialize LZW Table }
|
|
RC := Malloc(LZW_Table, SizeOf(LZW_Table^));
|
|
If RC <> 0 then begin
|
|
LZW_Init := FALSE;
|
|
Goto Exit;
|
|
end {if};
|
|
For I := 0 to LZW_TABLE_SIZE do begin
|
|
With LZW_Table^[I] do begin
|
|
Prefix := -1;
|
|
If I < 256 then
|
|
Suffix := I
|
|
else
|
|
Suffix := 0;
|
|
ChildCount := 0;
|
|
end {with};
|
|
end {for};
|
|
|
|
RC := Malloc(FreeList, SizeOf(FreeList^));
|
|
If RC <> 0 then begin
|
|
LZW_Init := FALSE;
|
|
Goto Exit;
|
|
end {if};
|
|
For I := FIRSTFREE to LZW_TABLE_SIZE do
|
|
FreeList^[I] := I;
|
|
NextFree := FIRSTFREE;
|
|
|
|
{ Initialize the LZW Character Stack }
|
|
RC := Malloc(LZW_Stack, SizeOf(LZW_Stack^));
|
|
If RC <> 0 then begin
|
|
LZW_Init := FALSE;
|
|
Goto Exit;
|
|
end {if};
|
|
StackIdx := 0;
|
|
LZW_Init := TRUE;
|
|
|
|
Exit:
|
|
end {LZW_Init};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure LZW_Cleanup;
|
|
Var
|
|
Code : Word;
|
|
Begin
|
|
Code := Dalloc(LZW_Table);
|
|
Code := Dalloc(FreeList);
|
|
Code := Dalloc(LZW_Stack);
|
|
end {LZW_Cleanup};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Clear_LZW_Table;
|
|
Var
|
|
I : Word;
|
|
Begin
|
|
StackIdx := 0;
|
|
|
|
For I := FIRSTFREE to LZW_TABLE_SIZE do begin { Find all leaf nodes }
|
|
If LZW_Table^[I].ChildCount = 0 then begin
|
|
LZW_Stack^[StackIdx] := I; { and put each on stack }
|
|
Inc(StackIdx);
|
|
end {if};
|
|
end {for};
|
|
|
|
NextFree := Succ(LZW_TABLE_SIZE);
|
|
|
|
While StackIdx > 0 do begin { clear all leaf nodes }
|
|
Dec(StackIdx);
|
|
I := LZW_Stack^[StackIdx];
|
|
With LZW_Table^[I] do begin
|
|
If LZW_Table^[I].Prefix <> -1 then
|
|
Dec(LZW_Table^[Prefix].ChildCount);
|
|
Prefix := -1;
|
|
Suffix := 0;
|
|
ChildCount := 0;
|
|
end {with};
|
|
Dec(NextFree); { add cleared nodes to freelist }
|
|
FreeList^[NextFree] := I;
|
|
end {while};
|
|
|
|
End {Clear_LZW_Table};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Add_To_LZW_Table(Prefix : Integer; Suffix : Byte);
|
|
Var
|
|
I : Word;
|
|
Begin
|
|
|
|
If NextFree <= LZW_TABLE_SIZE then begin
|
|
I := FreeList^[NextFree];
|
|
Inc(NextFree);
|
|
LZW_Table^[I].Prefix := Prefix;
|
|
LZW_Table^[I].Suffix := Suffix;
|
|
Inc(LZW_Table^[Prefix].ChildCount);
|
|
end {if};
|
|
|
|
End {Add_To_LZW_Table};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function Get_Code(CodeSize : Byte) : Integer;
|
|
Const
|
|
Mask : Array[1..8] of Byte = ($01, $03, $07, $0F, $1F, $3F, $7F, $FF);
|
|
TmpInt : Integer = 0;
|
|
Var
|
|
BitsNeeded : Byte;
|
|
HowMany : Byte;
|
|
HoldCode : Integer;
|
|
Label
|
|
Exit;
|
|
Begin
|
|
If FirstCh then begin { If first time through ... }
|
|
TmpInt := Get_Compressed; { ... then prime the code buffer }
|
|
If TmpInt = -1 then begin { If EOF on fill attempt ... }
|
|
Get_Code := -1; { ... then return EOF indicator ... }
|
|
Goto Exit; { ... and return to caller. }
|
|
end {if};
|
|
SaveByte := TmpInt;
|
|
BitsLeft := 8; { there's now 8 bits in our buffer }
|
|
FirstCh := FALSE;
|
|
end {if};
|
|
|
|
BitsNeeded := CodeSize;
|
|
HoldCode := 0;
|
|
|
|
While (BitsNeeded > 0) And (TmpInt <> -1) do begin
|
|
|
|
If BitsNeeded >= BitsLeft
|
|
then HowMany := BitsLeft { HowMany <-- Min(BitsLeft, BitsNeeded) }
|
|
else HowMany := BitsNeeded;
|
|
|
|
HoldCode := HoldCode OR ((SaveByte AND Mask[HowMany]) SHL (CodeSize - BitsNeeded));
|
|
SaveByte := SaveByte SHR HowMany;
|
|
Dec(BitsNeeded, HowMany);
|
|
Dec(BitsLeft, HowMany);
|
|
|
|
If BitsLeft <= 0 then begin { If no bits left in buffer ... }
|
|
TmpInt := Get_Compressed; { ... then attempt to get 8 more. }
|
|
If TmpInt = -1 then
|
|
Goto Exit;
|
|
SaveByte := TmpInt;
|
|
BitsLeft := 8;
|
|
end {if};
|
|
|
|
end {while};
|
|
|
|
Exit:
|
|
|
|
If (BitsNeeded = 0) then { If we got what we came for ... }
|
|
Get_Code := HoldCode { ... then return it }
|
|
else
|
|
Get_Code := -1; { ... Otherwise, return EOF }
|
|
|
|
end {Get_Code};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure UnShrink;
|
|
Var
|
|
Ch : Char;
|
|
CodeSize : Byte; { Current size (in bits) of codes coming in }
|
|
CurrCode : Integer;
|
|
SaveCode : Integer;
|
|
PrevCode : Integer;
|
|
BaseChar : Byte;
|
|
Label
|
|
Exit;
|
|
Begin
|
|
CodeSize := MINCODESIZE; { Start with the smallest code size }
|
|
|
|
PrevCode := Get_Code(CodeSize); { Get first code from file }
|
|
If PrevCode = -1 then { If EOF already, then ... }
|
|
Goto Exit; { ... just exit without further ado }
|
|
BaseChar := PrevCode;
|
|
Put_Ext(BaseChar); { Unpack the first character }
|
|
|
|
CurrCode := Get_Code(CodeSize); { Get next code to prime the while loop }
|
|
|
|
While CurrCode <> -1 do begin { Repeat for all compressed bytes }
|
|
|
|
If CurrCode = SPECIAL then begin { If we've got a "special" code ... }
|
|
|
|
CurrCode := Get_Code(CodeSize);
|
|
Case CurrCode of
|
|
1 : Begin { ... and if followed by a 1 ... }
|
|
Inc(CodeSize); { ... then increase code size }
|
|
end {1};
|
|
2 : Begin { ... and if followed by a 2 ... }
|
|
Clear_LZW_Table; { ... clear leaf nodes in the table }
|
|
end {2};
|
|
else begin { ... if neither 1 or 2, discard }
|
|
Writeln;
|
|
Writeln('Encountered code 256 not followed by 1 or 2!');
|
|
Writeln;
|
|
Write('Press a key to continue ...');
|
|
Ch := ReadKey;
|
|
DelLine;
|
|
GotoXY(1, WhereY);
|
|
end {else};
|
|
end {case};
|
|
|
|
end {then}
|
|
else begin { Not a "special" code }
|
|
|
|
SaveCode := CurrCode; { Save this code someplace safe... }
|
|
|
|
If CurrCode > LZW_TABLE_SIZE then
|
|
Abort('Invalid code encountered!');
|
|
|
|
If (CurrCode >= FIRSTFREE) and (LZW_Table^[CurrCode].Prefix = -1) then begin
|
|
If StackIdx > LZW_STACK_SIZE then begin
|
|
Write_Ext_Block;
|
|
Writeln;
|
|
Writeln('Stack Overflow (', StackIdx, ')!');
|
|
Halt;
|
|
end {if};
|
|
LZW_Stack^[StackIdx] := BaseChar;
|
|
Inc(StackIdx);
|
|
CurrCode := PrevCode;
|
|
end {if};
|
|
|
|
While CurrCode >= FIRSTFREE do begin
|
|
If StackIdx > LZW_STACK_SIZE then begin
|
|
Write_Ext_Block;
|
|
Writeln;
|
|
Writeln('Stack Overflow (', StackIdx, ')!');
|
|
Halt;
|
|
end {if};
|
|
LZW_Stack^[StackIdx] := LZW_Table^[CurrCode].Suffix;
|
|
Inc(StackIdx);
|
|
CurrCode := LZW_Table^[CurrCode].Prefix;
|
|
end {while};
|
|
|
|
BaseChar := LZW_Table^[CurrCode].Suffix; { Get last character ... }
|
|
Put_Ext(BaseChar);
|
|
|
|
While (StackIdx > 0) do begin
|
|
Dec(StackIdx);
|
|
Put_Ext(LZW_Stack^[StackIdx]);
|
|
end {while}; { ... until there are none left }
|
|
|
|
Add_to_LZW_Table(PrevCode, BaseChar); { Add new entry to table }
|
|
|
|
PrevCode := SaveCode;
|
|
|
|
end {if};
|
|
|
|
CurrCode := Get_Code(CodeSize); { Get next code from input stream }
|
|
|
|
end {while};
|
|
Exit:
|
|
end {UnShrink};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function Init_UnReduce : Boolean;
|
|
Var
|
|
Code : Word;
|
|
Label
|
|
Exit;
|
|
Begin
|
|
Code := Malloc(Followers, SizeOf(Followers^));
|
|
If Code <> 0 then begin
|
|
Init_UnReduce := FALSE;
|
|
Goto Exit;
|
|
end {if};
|
|
|
|
Code := Malloc(Stream, SizeOf(Stream^));
|
|
If Code <> 0 then begin
|
|
Init_UnReduce := FALSE;
|
|
Goto Exit;
|
|
end {if};
|
|
|
|
Init_UnReduce := TRUE;
|
|
|
|
Exit:
|
|
end {Init_UnReduce};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Cleanup_UnReduce;
|
|
Var
|
|
Code : Word;
|
|
Begin
|
|
Code := Dalloc(Followers);
|
|
Code := Dalloc(Stream);
|
|
end {Cleanup_UnReduce};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function D(X, Y : Byte) : Word;
|
|
Var
|
|
tmp : LongInt;
|
|
Begin
|
|
X := X SHR (8 - Pred(LocalHdr.Compress_Method));
|
|
Tmp := X * 256;
|
|
D := Tmp + Y + 1;
|
|
end {D};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function F(X : Word) : Byte;
|
|
Const
|
|
TestVal : Array[1..4] of Byte = (127, 63, 31, 15);
|
|
Begin
|
|
If X = TestVal[Pred(LocalHdr.Compress_Method)] then
|
|
F := 2
|
|
else
|
|
F := 3;
|
|
end {F};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function L(X : Byte) : Byte;
|
|
Const
|
|
Mask : Array[1..4] of Byte = ($7F, $3F, $1F, $0F);
|
|
Begin
|
|
L := X AND Mask[Pred(LocalHdr.Compress_Method)];
|
|
end {L};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure StreamOut(C : Byte);
|
|
Begin
|
|
Put_Ext(C);
|
|
Stream^[StreamIdx] := C;
|
|
StreamIdx := Succ(StreamIdx) MOD 4096;
|
|
end {StreamOut};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure ScrnchInit;
|
|
Begin
|
|
State := 0;
|
|
For StreamIdx := 0 to 4095 do
|
|
Stream^[StreamIdx] := 0;
|
|
StreamIdx := 0;
|
|
end {ScrnchInit};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure UnScrnch(C : Byte);
|
|
Const
|
|
DLE = $90;
|
|
Var
|
|
S : Integer;
|
|
Count : Word;
|
|
OneByte : Byte;
|
|
Tmp1 : LongInt;
|
|
Begin
|
|
Case State of
|
|
0 : begin
|
|
If C = DLE then
|
|
State := 1
|
|
else
|
|
StreamOut(C);
|
|
end {0};
|
|
1 : begin
|
|
If C = 0 then begin
|
|
StreamOut(DLE);
|
|
State := 0;
|
|
end {then}
|
|
else begin
|
|
V := C;
|
|
Len := L(V);
|
|
State := F(Len);
|
|
end {if};
|
|
end {1};
|
|
2 : begin
|
|
Inc(Len, C);
|
|
State := 3;
|
|
end {2};
|
|
3 : begin
|
|
Tmp1 := D(V, C);
|
|
S := StreamIdx - Tmp1;
|
|
If S < 0 then
|
|
S := S + 4096;
|
|
Count := Len + 3;
|
|
While Count > 0 do begin
|
|
OneByte := Stream^[S];
|
|
StreamOut(OneByte);
|
|
S := Succ(S) MOD 4096;
|
|
Dec(Count);
|
|
end {while};
|
|
State := 0;
|
|
end {3};
|
|
end {case};
|
|
|
|
end {UnScrnch};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Function MinBits(Val : Byte) : Byte;
|
|
Begin
|
|
Dec(Val);
|
|
Case Val of
|
|
0..1 : MinBits := 1;
|
|
2..3 : MinBits := 2;
|
|
4..7 : MinBits := 3;
|
|
8..15 : MinBits := 4;
|
|
16..31 : MinBits := 5;
|
|
else MinBits := 6;
|
|
end {case};
|
|
end {MinBits};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure UnReduce;
|
|
Var
|
|
LastChar : Byte;
|
|
N : Byte;
|
|
I, J : Word;
|
|
Code : Integer;
|
|
Ch : Char;
|
|
Begin
|
|
For I := 255 downto 0 do begin { Load follower sets }
|
|
N := Get_Code(6); { Get size of 1st set }
|
|
Followers^[I].SetSize := N;
|
|
If N > 0 then
|
|
For J := 0 to Pred(N) do
|
|
Followers^[I].FSet[J] := Get_Code(8);
|
|
end {for};
|
|
|
|
ScrnchInit;
|
|
|
|
LastChar := 0;
|
|
Repeat
|
|
|
|
If Followers^[LastChar].SetSize = 0 then begin
|
|
Code := Get_Code(8);
|
|
UnScrnch(Code);
|
|
LastChar := Code;
|
|
end {then}
|
|
else begin
|
|
Code := Get_Code(1);
|
|
If Code <> 0 then begin
|
|
Code := Get_Code(8);
|
|
UnScrnch(Code);
|
|
LastChar := Code;
|
|
end {then}
|
|
else begin
|
|
I := MinBits(Followers^[LastChar].SetSize);
|
|
Code := Get_Code(I);
|
|
UnScrnch(Followers^[LastChar].FSet[Code]);
|
|
LastChar := Followers^[LastChar].FSet[Code];
|
|
end {if};
|
|
end {if};
|
|
Until (ExtCount = LocalHdr.Uncompressed_Size);
|
|
Code := Dalloc(Followers);
|
|
end {UnReduce};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure UnZip;
|
|
Var
|
|
C : Integer;
|
|
Begin
|
|
Crc32Val := $FFFFFFFF;
|
|
Bytes_To_Go := LocalHdr.Compressed_Size;
|
|
FirstCh := TRUE;
|
|
|
|
ExtCount := 0;
|
|
|
|
TenPercent := LocalHdr.Compressed_Size DIV 10;
|
|
|
|
Case LocalHdr.Compress_Method of
|
|
0 : Begin
|
|
While Bytes_to_go > 0 do
|
|
Put_Ext(Get_Compressed);
|
|
end {0 = Stored};
|
|
1 : Begin
|
|
If LZW_Init then
|
|
UnShrink
|
|
else begin
|
|
Writeln('Not enough memory available to unshrink!');
|
|
Writeln('Skipping ', Hdr_FileName, ' ...');
|
|
FSkip(LocalHdr.Compressed_Size);
|
|
Crc32Val := NOT LocalHdr.Crc32;
|
|
end {if};
|
|
LZW_Cleanup;
|
|
end {1 = shrunk};
|
|
2..5 : Begin
|
|
If Init_UnReduce then
|
|
UnReduce
|
|
else begin
|
|
Writeln('Not enough memory available to unreduce!');
|
|
Writeln('Skipping ', Hdr_FileName, ' ...');
|
|
FSkip(LocalHdr.Compressed_Size);
|
|
Crc32Val := NOT LocalHdr.Crc32;
|
|
end {if};
|
|
Cleanup_UnReduce;
|
|
end {2..5};
|
|
else Begin
|
|
Writeln('Unknown compression method used on ', Hdr_FileName);
|
|
Writeln('Skipping ', Hdr_FileName, ' ...');
|
|
FSkip(LocalHdr.Compressed_Size);
|
|
Crc32Val := NOT LocalHdr.Crc32;
|
|
end {else};
|
|
end {case};
|
|
|
|
Crc32Val := NOT Crc32Val;
|
|
If Crc32Val <> LocalHdr.Crc32 then begin
|
|
Writeln;
|
|
Writeln('WARNING: File ', OutPath + Hdr_FileName, ' fails CRC check!');
|
|
Writeln(' Stored CRC = ', HexLInt(LocalHdr.Crc32),
|
|
' Calculated CRC = ', HexLInt(Crc32Val));
|
|
end {if};
|
|
|
|
end {UnZip};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Extract_File;
|
|
Var
|
|
YesNo : Char;
|
|
DosDTA : SearchRec;
|
|
Label
|
|
Exit;
|
|
Begin
|
|
FindFirst(OutPath + Hdr_FileName, ANYFILE, DosDTA);
|
|
If DosError = 0 then begin
|
|
Write('WARNING: ', OutPath + Hdr_FileName, ' already exists. Overwrite (Y/N)? ');
|
|
YesNo := ReadKey;
|
|
Writeln(YesNo);
|
|
If UpCase(YesNo) <> 'Y' then begin
|
|
FSkip(LocalHdr.Compressed_Size);
|
|
Goto Exit;
|
|
end {if};
|
|
end {if};
|
|
|
|
If Open_Ext then begin
|
|
Write('Extracting: ', OutPath + Hdr_FileName, ' ... ');
|
|
UnZip;
|
|
GotoXY(WhereX - 4, WhereY);
|
|
ClrEol;
|
|
Writeln(' done');
|
|
Close_Ext;
|
|
end {then}
|
|
else begin
|
|
Writeln('Could not open output file ', OutPath + Hdr_FileName, '! Skipping to next file ...');
|
|
FSkip(LocalHdr.Compressed_Size);
|
|
end {If};
|
|
Exit:
|
|
end {Extract_File};
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Procedure Extract_Zip;
|
|
Var
|
|
Match : Boolean;
|
|
I : Word;
|
|
Begin
|
|
Open_Zip;
|
|
While Read_Local_Hdr do begin
|
|
Match := FALSE;
|
|
I := 1;
|
|
Repeat
|
|
If SameFile(InFileSpecs[I], Hdr_FileName) then
|
|
Match := TRUE;
|
|
Inc(I);
|
|
Until Match or (I > MaxSpecs);
|
|
If Match then
|
|
Extract_File
|
|
else
|
|
FSkip(LocalHdr.Compressed_Size);
|
|
end {while};
|
|
Close_Zip;
|
|
GotoXY(1, WhereY);
|
|
ClrEOL;
|
|
end;
|
|
|
|
{ --------------------------------------------------------------------------- }
|
|
|
|
Begin
|
|
Assign(Output, '');
|
|
Rewrite(Output);
|
|
Writeln;
|
|
Writeln(COPYRIGHT);
|
|
Writeln(VERSION);
|
|
Writeln;
|
|
Load_Parms; { get command line parameters }
|
|
Initialize; { one-time initialization }
|
|
Extract_Zip; { de-arc the file }
|
|
end.
|