	page	74,132
	title	ARCV - Verbose ARC directory listing

;	Special version of ARCV to be called by QB program
; usage:
;
;	CALL ARCV (Workname$,"filename[.PAK]", RETCD%)		     ' CPC151AC
;
; notes:
;	This code originated from ARCV 1.15d - Verbose ARC directory display
;	written by V.Buerg and was modified to run as a called routine under
;	Microsoft QuickBasic. It was further modified to allow PAK files by
;	Robert J. Simoneau.
;
;	Change 9/14/86 to dis-allow wildcards
;	Change 1/1/87 to recognize squash format
;	Change 2/18/87 to support network usage - - - - Jon Martin   ' CPC151A
;	Change 1/7/89 to support Pak files -------------Bob Simoneau

;	Change 890320 to support ZIP files	David Kirschbaum, Toad Hall
;	- Question:  Why do we "have to look for the damned thing" when it
;	  comes to finding ARC/PAK headers?  All comments are at file ends,
;	  so the header should be EXACTLY where it should be .. at the end of
;	  the file's compressed code.  Hacked severely to reflect this,
;	  and vastly cleaning up the code.
;	- Replaced old SDIR Binary to Ascii conversion with a hacked version
;	  from JMODEM .. about 10 times faster, plus offers integer conversion
;	  as well as long integers.
;v1.3	- FAAR RBBS reports this sucker runs once and then just returns
;	  a usage message (in the output file).
;	  Trying to find out why.  Found it .. dumb mistake, not clearing
;	  variables between runs.
;	- Adding true EOF testing for file pointer bumps.
;	  ZIP files have a good way to find EOF (e.g., the central directory),
;	  but PAK and ARC files don't.
;	- Added some more error msgs.
;	- Tightened hex output (CvH).
;	- Reduced buffer sizes to minimum (archdr and inbuf).
;
;v1.4	- Adding the new Japanese .LHZ capability.	Toad Hall
;	  See LHARC10E.ZIP (available on GEnie and BBS's) for details.
;	- Neatening up total line.
;	- Found some bugs in trying to predetermine ARC/PAK EOF.
;	  Fixed (hopefully).
;	- Added a bunch of [bx] references .. saved 100 bytes!
;	- Credits for LHARC (.LHZ) file header structure to:
;		Daniel Durbin
;		SysOp: Cygnus X-1 BBS		| CIS: 73447,1744
;		(805) 541-8505 (data)		| GEnie: D.DURBIN
;		EL major at PolySlo		| ddurbin@polyslo.CalPoly.EDU 
;	  from his LVIEW.C code.

STDOUT	equ	1			;Standard Output		v1.3
STDERR	equ	2			;Std Error (console)		v1.3
FALSE	equ	0
TRUE	equ	NOT FALSE
DEBUG	equ	FALSE

Print	macro	name			; display a field
	mov	dx,offset name
	call	PrintS
	endm

header  struc				; archive header
aMbrflag	db	1AH		;unique ARC/PAK flag		v1.3
aCmpMeth	db	0		;  compression code
aMbrName	db	13 dup (0)	;  file name
aCmpSiz		dw	0,0		;  file size in archive
aModDate	dw	0		;  creation date
aModTime	dw	0		;  creation time
aCrc16		dw	0		;  cyclic redundancy check
aUncmpSiz	dw	0,0		;  true file size, bytes
header  ends

ARCHDRLEN	equ	29		;size of ARC/PAK header.	v1.3

;v1.3	ZIP Local file header structure:

zLocalEntry	STRUC
  
zdig0	db	50H,4BH,03H,04H	;local file header signature	4 bytes
				;(0x04034b50)
zVerMade	dw	?	;version needed to extract	2 bytes
zBitflag	dw	?	;general purpose bit flag	2 bytes
zCmpMeth	dw	?	;compression method		2 bytes
zModTime	dw	?	;last mod file time 		2 bytes
zModDate	dw	?	;last mod file date		2 bytes
zCrc32		dw	?,?	;crc-32   			4 bytes
zCmpSiz		dw	?,?	;compressed size		4 bytes
zUncmpSiz	dw	?,?	;uncompressed size		4 bytes
zNameLen	dw	?	;filename length		2 bytes
zExtraLen	dw	?	;extra field length		2 bytes
zMbrName	db	?	;filename (variable size)
				;extra field (variable size)
ZLocalEntry	ENDS

ZIPHDRLEN	equ	30		;length of initial ZIP hdr read	v1.3

;v1.4	LZH header structure

lzhlfh	STRUC			;Local file header
lUnk1		db	?,?	;char unknown1[2];	;?
lCmpMeth	db	5 dup(?) ;char method[5];	;compression method
lCmpSiz		dw	?,?	;long csize;	;compressed size
lUncmpSiz	dw	?,?	;long fsize;	;uncompressed size
lModTime	dw	?	;int ftime;	;last mod file time
lModDate	dw	?	;int fdate;	;last mod file date
lFAttr		db	?	;char fattr;	;file attributes
lUnk2		db	?	;char unknown2;	;?
lNameLen	db	?	;char namelen;	;filename length
lMbrName	db	?	;char *fname;	;filename
;lCrc16		dw	?		;int crc;	;crc-16
lzhlfh	ENDS

LZHHDRLEN	equ	22	;not including lMbrName or lCrc16


CSEG	segment public para 'CODE'
	assume	CS:CSEG,DS:CSEG,ES:CSEG

	public  ArcV

ArcV	proc	far

	push	bp			; save BASIC reg
	mov	bp,sp			; get parameter list pointer
	mov	CS:stkptr,sp		; save stack ptr
	mov	CS:saveds,DS		; save QB seg reg
	mov	CS:savees,ES		; save QB seg reg
	call	Start			; do our thing			v1.3

;	set DOS error level and exit
;v1.3a	We aren't relying on the CF flag anymore to indicate errors.
;	Instead, check AL.
;	0 = success
;	1 = command line parm error
;	2..6 are file-related (not found, etc.)
;	11 = Invalid format (probably didn't find a member header)
;	13 = invalid data (probably a bad file header structure)
;	18 = Unexpected EOF ('no further files to be found')

Exit:	mov	sp,stkptr		; restore entry stack value

	push	ax			;save error value		v1.3

;v1.3	Numerous errors could be returned

	or	al,al			;no errors?
	jz	Exit_NoErr		;yep, ok

	mov	bx,offset errtbl	;assume unknown error
	mov	di,bx			;various error values
	mov	cx,ERRTBLLEN		;table length
	repne	scasb			;find the offset
	jnz	Err_TblDone		;unknown, BX has table start

	 dec	di			;back up to actual error
	 sub	di,bx			;current psn - start = relative nr
	 mov	bx,di			;into BX for msg offset

Err_TblDone:
	shl	bx,1			;*2 for words
Err_Unk:
	add	bx,offset errmsgtbl	;table of addresses
	mov	dx,[bx]			;ptr to string
	call	PrintS			;output error msg
		
Exit_NoErr:

	mov	bx,word ptr outhdl	; close listing file
	cmp	bl,STDERR		;never opened or STDERR?	v1.3
	jna	Exit1			;not a real handle		v1.3
	 mov	ah,3eh			;close file handle
	 int	21h
Exit1:
	mov	bx,word ptr archdl	;close ARC/PAK/ZIP file		v1.3
	or	bx,bx			; if it was opened		v1.3
	jz	Exit2			; nope				v1.3
	 mov	ah,3EH			;close file handle		v1.3
	 int	21H			;				v1.3
Exit2:					;				v1.3

;v1.3	Adding a test to insure we switched DTAs
;	(so we don't blow away the caller's DTA with a vector 0:0!)

	lds	dx,dword ptr savedta	;get orig DTA vector
	or	dx,dx			;did we ever get it?
	jz	Exit_NoDTA		;nope
	mov	ax,DS			;check out seg
	or	ax,ax
	jz	Exit_NoDTA		;nope
	 mov	ah,1ah			;set DTA
	 int	21h
Exit_NoDTA:

	lds	ax,dword ptr CS:saveds	;recover calling seg regs	v1.3
					;(low word is orig ES)		v1.3
	mov	ES,ax
	ASSUME	DS:NOTHING,ES:NOTHING	;a reminder

	pop	ax			;restore error level		v1.3
	xor	ah,ah			;insure msb clear		v1.3a

	mov	bp,sp			; parm ptr from entry
	mov	6[bp],ax		;return retcd variable		v1.3
	pop	bp
	ret	6			; clear parms from stack     ' CPC151A

	subttl	'--- constants, equates and work areas'
	page

CR	equ	13
LF	equ	10
BEL	equ	7
TAB	equ	9

STOPPER equ	0		; end of display line indicator
ARCMARK equ	26		; special archive marker
ARCVER  equ	10		; highest compression code used

	even			;v1.3a

stkptr  dw	0		; stack pointer upon entry

arctitl db	CR,LF,'Archive:  '	;keep this even			v1.3a
saveds  dw	0		; QB seg reg
savees  dw	0		; QB seg reg

	subttl	'--- i/o control variables'
	page

INBUFSZ equ	128	;512	; size of input buffer			v1.3

;v1.3	Completely reordered these runtime variables
;	so we can purge them with one fell swoop

PURGESTART	equ	$	;					v1.3

totsf	dw	0,0		; average stowage factor
totlen  dw	0,0		; total of file lengths
totsize dw	0,0		; total of file sizes
totmbrs dw	0		; total number of files

archdl  dw	0		; file handle
fileptr dw	0		; ptr to filename part of arcname
arclen	dw	0		;full archive filename length		v1.3
arcname db	76 dup (0)

outhdl  dw	0		; handle for output listing		v1.3
templen	dw	0		;output filename length			v1.3
temp	db	76 dup (0)	; and temporary file name

filelen	dw	0,0		;absolute archive file length		v1.3a
curpsn	dw	0,0		;remember current file pointer psn	v1.3a

savedta dw	0,0		; addr of QB dta
dta	db	48 dup (0)	; data transfer area

	even			;					v1.3

PURGELEN	EQU	($ - PURGESTART) SHR 1	;amount to purge each run v1.3

;	display lines for verbose

vhdr	db	CR,LF
 db CR,LF,'Name          Length    Stowage    SF   Size now  Date       Time    CRC '
 db CR,LF,'============  ========  ========  ====  ========  =========  ======  ===='
 db CR,LF			;v1.4
 db STOPPER

;vline	db	CR,LF
vline	label	byte		;v1.4
vname	db	14 dup (' ')
vlength db	'          '	; length in archive			v1.3
vstyle  db	'          '	; compression method
vfactor db	' xx%  '	; compression factor
vsize	db	10 dup (' ')	; actual file bytes
vdate	db	'dd '		; creation date
 vmonth db	'mmm '
 vyear  db	'yy  '
 vtime  db	'hh:mm   '	; creation time
 vcrc	db	'xxxx'		; crc in hex
	db	CR,LF		;v1.4
	db	STOPPER

hundred dw	100		; for computing percentages

;	final totals line

vthdr	db '------    --- --------            ----  --------',CR,LF	;v1.4
	db	'*Total    '						;v1.4
 vtmbrs db	'    '
 vtlen  db	8 dup (' '),'  '
	db	10 dup (' ')
 vtsf	db	'   %  '
 vtsize db	8 dup (' ')
	db	CR,LF		; for tom
	db	STOPPER

 sign	db	' '

styles  db	'  ----- '	; 1 = old, no compression
	db	'  ----- '	; 2 = new, no compression
	db	' Packed '	; 3 = dle for repeat chars
	db	'Squeezed'	; 4 = huffman encoding
	db	'crunched'	; 5 = lz, no dle
	db	'crunched'	; 6 = lz with dle
	db	'Crunched'	; 7 = lz with readjust
	db	'Crunched'	; 8 = lz with readjust and dle
	db	'Squashed'	; 9 = 13-bit lz with no dle
	db	' Crushed'	;10 = Pak10 file ---------Bob Simoneau

;v1.3	ZIP compression types:

zstyles	label	byte
	db	'  Stored'	;0 - The file is stored (no compression)
	db	'  Shrunk'	;1 - The file is Shrunk
	db	'Reduced1'	;2 - Reduced with compression factor 1
	db	'Reduced2'	;3 - Reduced with compression factor 2
	db	'Reduced3'	;4 - Reduced with compression factor 3
	db	'Reduced4'	;5 - Reduced with compression factor 4

;v1.4	LZH compression types are already coded as 5 chars of text
;	in the compressed file.
;	All we need to do is pad them out to the correct width.

months  db	'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec '

ARCPAK	=	0			;				v1.3
ZIP	=	1			;				v1.4
LZH	=	2			;				v1.4
ftype	db	ZIP			;flag which type file		v1.3

;v1.4	4 types of archive file

ziptype	db	'ZIP'
arctype	db	'ARC'
paktype	db	'PAK'
lzhtype	db	'LZH'			;v1.4
larctype db	'LZS'			;v1.4 not enabled for now

;zfilesig db	50H,4BH,03H,04H		;local file header signature	v1.3
;zdirsig db	50H,4BH,01H,02H		;central file header signature	v1.3

ZSIG	equ	4B50H			;unique ZIP signature		v1.4
ZFILESIG equ	0403H			;file member signature		v1.4
ZDIRSIG	equ	0201H			;central file header signature	v1.4

;v1.3	Centralizing errors at the exit point

;	1 = command line parm error
;	2..6 are file-related (not found, etc.)
;	11 = Invalid format (probably didn't find a member header)
;	12 = Invalid file type (not an ARC, PAK, ZIP)
;	13 = invalid data (probably a bad file header structure)
;	18 = Unexpected EOF ('no further files to be found')

errtbl db	0,1,2,3,4,5,6,11,12,13,18,25,27,29,30			;v1.3a
ERRTBLLEN	equ	$ - errtbl

errmsgtbl dw	msg0,msg1,msg2,msg3					;v1.3a
	dw	msg4,msg5,msg6,msg11
	dw	msg12,msg13,msg18,msg25
	dw	msg27,msg29,msg30

msg0	db	'Unknown error',0

msg1	db	'Invalid function number',0
msg2	db	'Archive file not found',0
msg3	db	'Path not found',0
msg4	db	'No handle available',0
msg5	db	'Access denied',0
msg6	db	'Invalid handle',0
msg11	db	'Archive header error',0
msg12	db	'Invalid file type',0
msg13	db	'Archive format error',0
msg18	db	'No further files to be found',0
msg25	db	'Disk seek error',0
msg27	db	'Disk sector not found',0
msg29	db	'Write error',0
msg30	db	'Read error',0


	subttl	'--- mainline processing'
	page
;
Start	proc	near			;				v1.3

	mov	ax,CS			;just set ES for now		v1.3
	mov	ES,ax
	ASSUME	DS:NOTHING,ES:CSEG	;a reminder			v1.3a

;v1.3	Insure all variables are cleared
	cld
	mov	di,offset PURGESTART
	xor	ax,ax			;clear all the variables	v1.3
	mov	cx,PURGELEN		;nr words to clear		v1.3
	rep	stosw			;				v1.3

;v1.3	Move first parameter (output filename) into code space

	mov	si,word ptr 10[bp]	; ptr to parameter vector    ' CPC151A
	lodsw				; get string length	     ' CPC151A
	mov	cx,ax			;			     ' CPC151A
	jcxz	Copy_Parm2		;empty, forget it		v1.3
	 mov	di,offset templen	;str length			v1.3
	 stosw				;save length			v1.3
	 mov	si,[si]			; get string offset		v1.3a
	 rep	movsb			;copy in the string		v1.3

Copy_Parm2:

;v1.3	Now copy 2d parameter (target archive filename)

	mov	si,word ptr 8[bp]	; ptr to parameter vector
	lodsw				; get string length
	mov	cx,ax			;				v1.3
	jcxz	Parm2_Done		;forget it			v1.3
	 mov	di,offset arclen	;archive name length		v1.3
	 stosw				;save length			v1.3
	 mov	si,[si]			; get string offset		v1.3
	 mov	ah,'a'			;constant for uppercasing	v1.3
Parm2_Upper:				;				v1.3
	 lodsb				;snarf char			v1.3
	 cmp	al,ah			;need uppercasing?		v1.3
	 jb	Parm2_NoU		;nope				v1.3
	  sub	al,20H			;uppercase it			v1.3
Parm2_NoU:				;				v1.3
	 stosb				;				v1.3
	 loop	Parm2_Upper		;				v1.3

Parm2_Done:

;v1.3	All done with DS

	mov	ax,CS			;				v1.3
	mov	DS,ax			;				v1.3
	ASSUME	DS:CSEG,ES:CSEG		;a reminder			v1.3a

	mov	ax,STDERR		;assume no output filename	v1.3a
	cmp	temp,0			;any output filename?		v1.3
	jz	Temp_Opened		;nope, use STDERR		v1.3a

;v1.3	Forcing output file to STDERR for debugging.
;v1.3	 mov	al,1			; will show usage		v1.3
;v1.3	 ret				;back to Exit			v1.3

;v1.3a	 mov	ax,STDERR		;force to STDERR		v1.3
;v1.3a	 jmp	short Temp_Opened	;continue			v1.3

;Got_Temp:
	mov	dx,offset temp		; open temporary file for output
	xor	cx,cx			;no special attributes		v1.3
	mov	ah,3ch			;create file
	int	21h
	jnb	Temp_Opened		;fine				v1.3
	 ret				;back to Exit, AL=error code	v1.3
					;CF set				v1.3a
Temp_Opened:
	mov	outhdl,ax		;save handle

;v1.3	Parse the target archive name
;	Separate path from name
;	Insure it's an ARC, PAK or ZIP type.

	mov	di,offset arclen	;archive name length		v1.3
	mov	ax,[di]			;snarf length			v1.3a
	inc	di			;bump to name proper		v1.3a
	inc	di			;				v1.3a
	mov	cx,ax			;into CX for scans to come	v1.3a
	jcxz	No_ArcName		;no length, ergo no name	v1.3a

	mov	dx,ax			;save in DX for later		v1.3
	xor	al,al			;will scan for AsciiZ terminator v1.3
	cmp	[di],al			;no name at all?		v1.3
	jnz	Got_ArcName		;yep				v1.3

No_ArcName:
	 mov	al,2			;'Archive file not found'	v1.3
	 ret				;back to Exit			v1.3

Got_ArcName:

;v1.3	We have some sort of target name.
;	But is it a legal type?
;	DX = filename length
;	DI -> archive filename (arcname)

	add	di,dx			;+ length -> last char+1	v1.3
	dec	di			;back up to last char		v1.3
	mov	bx,di			;BX -> last char		v1.3

	mov	al,'\'			;look for normal path delimiter	v1.3
	mov	cx,dx			;length for scan		v1.3
	std				;backwards scanning now		v1.3
	repne	scasb			;				v1.3
	jz	Got_Start		;found one			v1.3

;Ugh .. tired of typing in v1.3's!

	mov	di,bx			;back to end
	mov	cx,dx			;restore length
	mov	al,'/'			;funny path delimiter
	repne	scasb
	jz	Got_Start		;found one

	mov	di,bx			;back to end .. sigh ..
	mov	cx,dx			;restore length
	mov	al,':'			;ok, how about a drive?
	repne	scasb
	jnz	No_Paths		;nope, DI -> name start

Got_Start:
	inc	di			;bump up to the separator
No_Paths:
	inc	di			;bump to the first name char
	cld				;forward again
	mov	fileptr,di		;remember real filename start

;v1.4	You MUST specify the type .. .ARC, .PAK, .ZIP, or .LZH.
;	If .ARC or .PAK, we'll use the old code to display ARC-type
;	files.
;v1.4	Else if ZIP or LZH, it's a totally new format!
;	We remember the type archiving format in 'ftype'.

;v1.3	DS:SI -> filename's first char.

	mov	al,'.'			;find the separator		v1.3
	mov	cx,word ptr 12		;max of 12 chars		v1.3
	repne	scasb			;find it			v1.3
	jnz	BadType			;forget it			v1.3

	mov	dx,di			;save pointer to file type	v1.3
					;(just past the separator)	v1.3
	mov	ax,3			;3 chars constant

	mov	ftype,ZIP		;assume ZIP

	mov	si,offset ziptype	;is it a ZIP?
	mov	di,dx			;back to filename type
	mov	cx,ax			;3 chars
	repz	cmpsb			;compare
	jz	Got_Type		;a match

	mov	ftype,ARCPAK		;ok, assume ARC or PAK		v1.3a

	mov	si,offset arctype	;is it an ARC?			v1.3
	mov	di,dx			;back to filename type
	mov	cx,ax			;3 chars
	repz	cmpsb			;compare
	jz	Got_Type		;a match

	mov	si,offset paktype	;is it a PAK?
	mov	di,dx			;back to filename type
	mov	cx,ax			;3 chars
	repz	cmpsb			;compare
	jz	Got_Type		;a match

;v1.4	Adding .LZH types
	mov	ftype,LZH		;ok, assume .LZH file		v1.4

	mov	si,offset lzhtype	;is it an LZH?
	mov	di,dx			;back to filename type
	mov	cx,ax			;3 chars
	repz	cmpsb			;compare
	jz	Got_Type		;a match

BadType:
	 mov	al,12			;'Invalid file type'		v1.3a
	 ret				;back to Exit			v1.3

Got_Type:				;v1.3

;	find first matching file

	push	ES
	mov	ah,2fh			; get current dta ptr
	int	21h			; returned in ES:bx
	mov	savedta,ES
	mov	savedta[2],bx
	pop	ES

	mov	dx,offset dta		; set local dta for murkers
	mov	ah,1ah
	int	21h

	call	OpenArc			; see if archive exists
;	jb	ArcV_X			;nope, return, AL = error	v1.3
	jnb	ArcV1			;ok
	 jmp	ArcV_X			;nope, return, AL=error		v1.4

;v1.3a	Display archive filename, header,
;	then into a loop for each archive member.

ArcV1:	mov	dx,fileptr		;pointer to filename		v1.3a
	call	PrintS			;display, CR/LF			v1.3a
	jb	ArcV_X			;output failed			v1.3a

	Print	vhdr
	jb	ArcV_X			;output failed, AL = error	v1.3

ArcVNext:
IF	DEBUG
	Print	debug1
	jmp	short debugj1
debug1	db	'Calling GetHdr',CR,LF,0
debugj1:
ENDIF
	call	GetHdr			; load next header
	jb	ArcV_NoHdr		;failed somehow, AL=error	v1.3a
					;(could be EOF, which is ok)	v1.3a
IF	DEBUG
	Print	debug2
	jmp	short debugj2
debug2	db	'Calling ArcVgo',CR,LF,0
debugj2:
ENDIF
	call	ArcVgo			;format, write out file report
	jb	Arcv_NoHdr		;something failed, AL=error	v1.3a

IF	DEBUG
	Print	debug3
	jmp	short debugj3
debug3	db	'Calling Bump_ArcPtrs',CR,LF,0
debugj3:
ENDIF
	call	Bump_ArcPtrs		;bump to next archive file	v1.3
	jnb	ArcVNext		;loop if ok, else AL=error	v1.3a
					;(could be EOF)			v1.3a

ArcV_NoHdr:
	cmp	archdr.aCmpMeth,0	; archive eof?
	jnz	ArcV_X			;nope, something else happened	v1.3

	cmp	totmbrs,0		;any totals?			v1.3
	jz	ArcV_X			;nope				v1.3
	 push	ax			;save previous error value	v1.3
	 call	Format_Totals		;yep, format and output		v1.3
	 pop	ax			;restore prev err value		v1.3

ArcV_X:	ret				;AL=error			v1.3a

Start	endp				;				v1.3


;v1.3	Format, display single line for each member
;	On success, return:
;	 CF clear
;	 AL = 0
;	On error, return:
;	 CF set (because of output write fail)
;	 AL = error code

ArcVgo	proc	near
	mov	di,offset vname		; copy file name
	mov	si,offset archdr.aMbrName
	mov	cx,word ptr 13		;up to 12 chars long, AsciiZ 0
ArcV3:
	lodsb
	or	al,al			; end of name?			v1.3
	je	ArcV4
	 stosb
	 loop	ArcV3
	 jmp	short ArcV5

ArcV4:
	mov	al,' '			; pad with blanks
	rep	stosb
ArcV5:
; reduce the size/length to word values

	mov	bx,offset archdr.aCmpSiz	;-> compressed size	v1.4
	mov	cx,[bx]			;.lo				v1.4
	mov	dx,2[bx]		;.hi				v1.4
	mov	bx,offset archdr.aUncmpSiz	;-> uncompressed size	v1.4
	mov	ax,2[bx]		;.hi				v1.4
	mov	bx,[bx]			;.lo				v1.4

ArcV51: or	ax,ax			; big number?
	jz	ArcV52			; nope, can use it
	 shr	ax,1			; yup, divide by two
	 rcr	bx,1
	 shr	dx,1
	 rcr	cx,1
	 jmp	short ArcV51

ArcV52:
	mov	ax,bx			; low word of actual size
	mov	sign,' '
	cmp	ax,cx			; arc member is larger?
	jb	ArcV520
	 sub	ax,cx			; amount saved
	 jmp	short ArcV56

ArcV520:
	sub	ax,cx
	neg	ax
	mov	sign,'-'

ArcV56:
	mul	hundred			; to percentage
	add	ax,50
	adc	dx,0			; round up percent
	or	bx,bx			; empty file?
	jnz	ArcV53
	 mov	ax,100
	 jmp	short ArcV54

ArcV53: div	bx
ArcV54:
	cmp	ax,100			; archive fouled?
	jbe	ArcV55
	 sub	ax,ax
ArcV55:
	mov	di,offset vfactor-2	;format stowage factor		v1.3
	call	Asciify			;display AX

	mov	al,sign
	mov	vfactor,al

	mov	cx,word ptr 3		;gonna need it in a sec		v1.4
	cmp	ftype,LZH		;LZH type? (compression method	v1.4
					; is already text)		v1.4
	jnz	ArcV_GetStyles		;nope				v1.4

;v1.4	The LZH compression method (5 chars) is still in inbuf.

	mov	si,offset inbuf.lCmpMeth	;-> 5-char compression	v1.4
						;   method string	v1.4
	mov	di,si
	add	di,5			;point to beyond chars		v1.4
	mov	ax,'  '			;need 3 trailing blanks		v1.4
	stosw
	stosb
	mov	di,offset vstyle+1	;indent to be neat		v1.4
	jmp	short ArcV_GotStyle	;skip				v1.4

ArcV_GetStyles:				;				v1.4

	mov	si,offset zstyles	;assume ZIP			v1.3
	cmp	ftype,ZIP		;ZIP file?			v1.3
	jz	ArcV55A			;yep				v1.3
	 mov	si,offset styles	;ARC or PAK			v1.3
ArcV55A:				;				v1.3

	sub	bx,bx			; determine style
	mov	bl,archdr.aCmpMeth
	dec	bl			;adjust for table offset	v1.3
;v1.4	mov	cl,3			; eight bytes each entry
;v1.4	CX = 3 (eight bytes each entry)
	shl	bx,cl	;*8

	add	si,bx			;point into style table		v1.3
	mov	di,offset vstyle

ArcV_GotStyle:				;				v1.4
	inc	cx			;CX=4=words to move		v1.4
	rep	movsw			;				v1.3

	mov	bx,offset archdr.aCmpSiz	;-> compressed size	v1.4
	mov	ax,[bx]			;.lo				v1.4
	mov	dx,2[bx]		;.hi				v1.4
	mov	bx,offset totsize	;-> accumulated compressed size	v1.4
	add	[bx],ax			;.lo				v1.4
	adc	2[bx],dx		;.hi				v1.4

	mov	di,offset vsize		;format file size		v1.3
	call	Asciify_Long		;				v1.3

	mov	bx,offset archdr.aUncmpSiz	;-> uncompressed size	v1.4
	mov	ax,[bx]			;.lo				v1.4
	mov	dx,2[bx]		;.hi				v1.4
	mov	bx,offset totlen	;-> total length accumulator	v1.4
	add	[bx],ax			;.lo				v1.4
	adc	2[bx],dx		;.hi				v1.4
	
	mov	di,offset vlength	;format file length		v1.3
	call	Asciify_Long		;				v1.3

	mov	ax,archdr.aModDate	; format file date
	call	GetDate

	mov	ax,archdr.aModTime	; format file time
	call	GetTime

	mov	ax,archdr.aCrc16	; format crc in hex
	mov	di,offset vcrc
	call	Cvh

	inc	totmbrs			;NOW bump total count		v1.3a
	Print	vline			; display this file info
					;(may return error)		v1.3a
	ret

ArcVgo	endp


	subttl	'--- load next archive header'
	page

;v1.3	Adding ZIP file searching
;v1.3a	For ARC/PAK files, now testing to see if we're at the archive
;	file end.  If so (a proper file), return with EOF (CF set
;	but AL=0).
;	Archive files may have picked up some garbage on the end
;	(from XMODEM xfers, whatever).  We'll see if we at LEAST have
;	enough data for an archive header.
;	If not, assume EOF, ignoring garbage.
;	If there's more than 29 bytes of garbage .. the header will be
;	garbage and we're gonna report a format error .. but that's ok for now.
;	Zip files have a definite ending (the central directory,
;	and they'll look out for their own endings.
;
;	Also returning CF and AL per any errors.

GetHdr  proc	near

	xor	ax,ax			;handy 0
	mov	archdr.aCmpMeth,al	;assume archive EOF

	cmp	ftype,ZIP		;doing ZIP files?
	jnz	GH_NotZip		;nope				v1.4
	 jmp	Get_ZipHdr		;yep, they look out for themselves

GH_NotZip:
	cmp	ftype,LZH		;doing an LZH file?		v1.4
	jnz	GH_ArcPak_Hdr		;nope				v1.4
	 jmp	Get_LZHHdr		;yep				v1.4

GH_ArcPak_Hdr:				;				v1.4

;v1.3	New code
;	ARC/PAK headers look like this:
;aMbrFlag	db	1AH		;unique header flag
;aCmpMeth	db	0		;  compression code
;aMbrName	db	13 dup (0)	;  file name
;aCmpSiz	dw	0,0		;  file size in archive
;aModDate	dw	0		;  creation date
;aModTime	dw	0		;  creation time
;aCrc16		dw	0		;  cyclic redundancy check
;aUncmpSiz	dw	0,0		;  true file size, bytes

	mov	dx,offset archdr	;read into here
	mov	cx,ARCHDRLEN		;nr bytes to read
	mov	bx,archdl		;archive file handle
	mov	ah,3FH			;read from file/device
	int	21H
	jnb	GH_ChkHdr		;read ok			v1.3a
	 ret				;return CF set, AL=error	v1.3a

GH_ChkHdr:
	mov	bx,dx			;DS:BX -> structure start	v1.3a

	cmp	[bx].aMbrFlag,ARCMARK	;start of header?
	jne	Hdr_InvalFmt		;'invalid format', exit CF set

	mov	al,[bx].aCmpMeth	;type compression
	cmp	al,ARCVER		;reasonable code?
	ja	Hdr_InvalFmt		;nope, funny stuff

	or	al,al			; archive eof?
	je	Hdr_RetCF		;yep, done, return CF set
					;but AL=0 = not a REAL error	v1.3a
	cmp	al,1			; old format?
	jne	GetHdrX			; if so, it's short
	 mov	si,offset archdr.aCmpSiz			; CPC15-1C
	 mov	di,offset archdr.aUncmpSiz			; CPC15-1C
	 movsw				;				v1.3
	 movsw				;				v1.3
GetHdrX:
	xor	al,al			;return AL=0, success		v1.3a
	clc
	ret

Hdr_InvalFmt:
	mov	al,0BH			;'invalid format'
Hdr_EarlyEOF:				;				;v1.4
	mov	[bx].aCmpMeth,al	;signal EOF or invalid format	v1.4
Hdr_RetCF:
	stc				;return CF set, AL=error
	ret

GetHdr	endp


Get_ZipHdr	proc	near
;v1.4	GetHdr Subroutine for ZIP files
;v1.3	Reads in ZIP file entry.
;	Then scans for the unique file entry signature.
;	On success:
;	 DS:BX -> file entry directory structure
;	 CF clear
;	Else CF set for failure

	call	Read_Zip_Entry
	jb	Get_ZHdrX			;failed, AL=ERRORLEVEL

	mov	bx,offset inbuf			;use for field base
	mov	di,offset archdr.aCmpMeth	;moving into this structure

;v1.4	Remember, the ZIP header we'll be snarfing data from
;	looks like this:
;zVerMade	dw	?	;version needed to extract	2 bytes
;zBitflag	dw	?	;general purpose bit flag	2 bytes
;zCmpMeth	dw	?	;compression method		2 bytes
;zModTime	dw	?	;last mod file time 		2 bytes
;zModDate	dw	?	;last mod file date		2 bytes
;zCrc32		dw	?,?	;crc-32   			4 bytes
;zCmpSiz	dw	?,?	;compressed size		4 bytes
;zUncmpSiz	dw	?,?	;uncompressed size		4 bytes
;zNameLen	dw	?	;filename length		2 bytes
;zExtraLen	dw	?	;extra field length		2 bytes
;zMbrName	db	?	;filename (variable size)
				;extra field (variable size)
;
;	and the ARC/PAK record we'll be formatting to
;	looks like this:
;aMbrFlag db	1AH
;aCmpMeth db	0			;  compression code
;aMbrName db	13 dup (0)		;  file name
;aCmpSiz dw	0,0			;  file size in archive
;aModDate dw	0			;  creation date
;aModTime dw	0			;  creation time
;aCrc16  dw	0			;  cyclic redunancy check
;aUncmpSiz  dw	0,0			;  true file size, bytes

	mov	ax,[bx].zCmpMeth		;compression method
	inc	al				;bump to be non-0
	stosb					;->  aCmpMeth

;For now, assuming a normal file name (no paths)

	mov	ax,[bx].zNameLen		;filename length
	and	ax,15				;constrain to max 12 chars
	mov	cx,ax				;into CX for move
	lea	si,[bx].zMbrName		;pointer to actual filename
	rep	movsb				;do the move
	xor	al,al				;terminating 0
	stosb

	mov	di,offset archdr.aCmpSiz	;bump past name

;	mov	ax,[bx].zCmpSiz			;compressed size.lo
;	stosw					; -> aCmpSiz
;	mov	ax,[bx].zCmpSiz[2]		;compressed size.hi
;	stosw					; -> aCmpSiz[2]
	mov	si,offset inbuf.zCmpSiz		;-> compressed size
	movsw					;aCmpSiz.lo
	movsw					;aCmpSiz.hi

	mov	ax,[bx].zModDate		;last mod date
	stosw					; -> aModDate
	mov	ax,[bx].zModTime		;last mod time
	stosw					; -> aModTime
	mov	ax,[bx].zCrc32			;CRC-32 value.lo
	stosw					; -> aCrc16

;	mov	ax,[bx].zUncmpSiz		;uncompressed size.lo
;	stosw					; -> aUncmpSiz
;	mov	ax,[bx].zUncmpSiz[2]		;uncompressed size.hi
;	stosw					; -> aUncmpSiz[2]
	mov	si,offset inbuf.zUncmpSiz	;-> uncompressed size
	movsw					;aUncmpSiz.lo
	movsw					;aUncmpSiz.hi

	xor	ax,ax				;return AX 0
	clc					;return CF clear
Get_ZHdrX:
	ret

Get_ZipHdr	endp		;GetHdr subroutine



Get_LZHHdr	proc	near
;v1.4	GetHdr Subroutine for LZH headers
;	LZH file header has already been read in to inbuf.
;
;	If all is ok, we move the appropriate LZH fields into the
;	standard ARC/PAK structure (archdr) (so far as we can).
;
;	Gleaning from the LHARCDOC documentation, the 'laCmpMeth' field
;	(5 characters) can be:
;		'-lh0-'		stored as is (no compression)
;		'-lh1-'		compressed by LZHuf coding
;	There appear to be at least two more possible compression codes
;	that may appear:  "LARC type 4 and type 5" (whatever they may be!).
;
;	Assuming this field will ALWAYS be text, we are NOT gonna try to
;	snarf some magic code number out of the field, but will just
;	protect the field (in inbuf) and move the text directly into our
;	formatted display line later.
;
;	The only way we can test this as an LZH header is to look
;	for a '-%%%-' starting at the 2d header byte (the laCmpMeth
;	field).
;
;	On success:
;	 DS:BX -> file entry directory structure
;	 CF clear
;	Else CF set for failure

;v1.4	LZH files don't have a decent, clean EOF header.
;	We have to test for near-EOF the hard way.

	mov	di,offset archdr.aMbrFlag	;moving into this structure
	mov	ax,001AH			;fake ARC/PAK flag
	stosw					; and EOF compression code

	xor	ax,ax			;handy 0
	mov	bx,offset filelen	;-> file length
	mov	dx,[bx]			;file length.lo
	mov	cx,2[bx]		;file length.hi

	mov	bx,offset curpsn	;for fast access	
	cmp	cx,2[bx]		;length.hi = psn.hi?
	jnz	GL_AddHdr		;nope
	cmp	dx,[bx]			;length.lo = psn.lo?
	jz	GL_TrueEof		;yep, we're exactly at EOF

GL_AddHdr:
	sub	dx,LZHHDRLEN		;sub header length
	sbb	cx,ax	;0		;handle the borrow
	jb	GL_Eof			;<0, beyond EOF
	sub	dx,[bx]			;- file psn.lo
	sbb	cx,2[bx]		;- file psn.hi, minus any borrows
	jnb	GL_NotEof		;not near end .. ok

;There must've been junk on the file end.
;However .. there ALWAYS seems to be junk on the end.
; So .. we'll return no message at all (AL=0)
;If we ever figure out how to detect a TRUE LZH EOF,
;we can enable this ERRORLEVEL=18 business.

GL_Eof:
;	mov	al,18			;'No further files to be found'
GL_TrueEof:
	stc				;CF set for EOF			v1.4
	ret

GL_NotEof:

	push	di			;save ptr -> archdr.aMbrName
	call	Read_LZH_Entry
	pop	di
	jb	Get_LHdrX			;failed, AL=ERRORLEVEL

	mov	bx,offset inbuf			;use for field base

;v1.4	Remember, the LZH header we'll be snarfing data from
;	looks like this:
;lUnk1	db	?,?	;char unknown1[2];	;?
;lCmpMeth	db	5 dup(?) ;char method[5];	;compression method
;lCmpSiz	dw	?,?	;long csize;	;compressed size
;lUncmpSiz	dw	?,?	;long fsize;	;uncompressed size
;lModTime	dw	?	;int ftime;	;last mod file time
;						; (msdos format)
;lModDate	dw	?	;int fdate;	;last mod file date
;lfAttr		db	?	;char fattr;	;file attributes
;unknown2	db	?	;char unknown2;	;?
;lNameLen	db	?	;char namelen;	;filename length
;
;lMbrName	db	?	;char *fname;	;filename
;;lCrc16	dw	?	;int crc;	;crc-16
;
;	and the ARC/PAK record we'll be formatting to
;	looks like this:
;aMbrFlag db	1AH
;aCmpMeth db	0			;  compression code
;aMbrName db	13 dup (0)		;  file name
;aCmpSiz dw	0,0			;  file size in archive
;aModDate dw	0			;  creation date
;aModTime dw	0			;  creation time
;aCrc16  dw	0			;  cyclic redundancy check
;aUncmpSiz  dw	0,0			;  true file size, bytes

	mov	al,[bx].lNameLen		;filename length
	and	ax,15				;constrain to max 12 chars
	mov	cx,ax				;into CX for move
	mov	si,offset inbuf.lMbrName	;-> actual filename
	rep	movsb				;do the move
	xor	al,al				;terminating 0
	stosb

;In LZH headers, the 2-byte CRC16 word lies immediately
;after the filename.
;Snarf it now and stuff in the ARC header.

	lodsw					;lCrc16
	push	ax				;save a sec

	mov	di,offset archdr.aCmpSiz	;bump past name

;	mov	ax,[bx].lCmpSiz			;compressed size.lo
;	stosw					; -> aCmpSiz
;	mov	ax,[bx].lCmpSiz[2]		;compressed size.hi
;	stosw					; -> aCmpSiz[2]
	mov	si,offset inbuf.lCmpSiz		;-> compressed size
	movsw					;aCmpSiz.lo
	movsw					;aCmpSiz.hi

	mov	ax,[bx].lModDate		;last mod date
	stosw					; -> aModDate
	mov	ax,[bx].lModTime		;last mod time
	stosw					; -> aModTime
	pop	ax				;CRC-16 value
	stosw					; -> aCrc16
;	mov	ax,[bx].lUncmpSiz		;uncompressed size.lo
;	stosw					; -> aUncmpSiz
;	mov	ax,[bx].lUncmpSiz[2]		;uncompressed size.hi
;	stosw					; -> aUncmpSiz[2]
	mov	si,offset inbuf.lUncmpSiz	;-> uncompressed size
	movsw					;aUncmpSiz.lo
	movsw					;aUncmpSiz.hi

	xor	ax,ax				;return AX 0
	clc					;return CF clear
Get_LHdrX:
	ret

Get_LZHHdr	endp			;GetHdr Subroutine		v1.4


Read_LZH_Entry	proc	near		;GetHdr Subroutine		v1.4

	mov	dx,offset inbuf			;read into here
	mov	cx,LZHHDRLEN			;entry structure size
						;(does NOT include variable
						; length filename, and the
						;two CRC bytes following the
						;filename)
	mov	bx,archdl			;file handle
	call	ReadZ_It			;try to read in header
						;(up to filename)
	jb	ReadL_Eof			;failed, AL=error

	mov	si,dx				;structure start
	mov	al,'-'				;test for '-l%-' or whatever
	cmp	[si].lCmpMeth,al		;first part of compression
						;method string?
	jnz	ReadL_InvalDat			;bogus, failed
	 cmp	[si].lCmpMeth+4,al		;how about last char?
	 jz	ReadL_Ok1			;yep, fine
ReadL_InvalDat:
	mov	al,0DH				;force to 'invalid data'
ReadL_Eof:
	mov	archdr.aCmpMeth,al		;set per EOF or error
	stc					;return CF set
	ret

ReadL_Ok1:
	mov	dx,offset inbuf.lMbrName	;-> lMbrName psn
	mov	cl,inbuf.lNameLen		;length of member filename
	xor	ch,ch				;clear msb
	call	ReadZ_It			;read in the name
	jb	ReadL_Eof			;failed
	add	dx,cx				;bump buff ptr past name
	mov	cx,2				;LZH CRC is a word
	call	ReadZ_It			;read in the CRC word
	jb	ReadL_Eof			;failed
	ret					;success

Read_LZH_Entry	endp			;GetHdr Subroutine		v1.4


Read_Zip_Entry	proc	near		;GetHdr Subroutine

	mov	dx,offset inbuf			;read into here
	mov	cx,ZIPHDRLEN			;entry structure size
						;(does NOT include filename or
						; Extra fields, which are
						;dynamic)
	mov	bx,archdl			;file handle
	call	ReadZ_It			;try to read in header
						;(up to filename)
	jb	ReadZ_Eof			;failed, AL=error	v1.3a

	mov	si,dx				;->file signature	v1.4
	lodsw					;snarf first 2 chars	v1.4
	cmp	ax,ZSIG				;ZIP header?		v1.4
	jnz	ReadZ_InvalDat			;nope, bogus		v1.4
	lodsw					;file or central sig	v1.4
	cmp	ax,ZFILESIG			;next member?		v1.4
	jz	ReadZ_Ok1			;yep, fine		v1.4
	cmp	ax,ZDIRSIG			;central directory?	v1.4
						;(means we're done)	v1.4
	mov	al,0				;assume yes, EOF	v1.4
	jz	ReadZ_Eof			;yep			v1.4

ReadZ_InvalDat:
	mov	al,0DH				;'Invalid data'		v1.4
ReadZ_Eof:					;			v1.3a
	mov	archdr.aCmpMeth,al		;set per EOF or error	v1.3a
	stc					;return CF set		v1.3a
	ret

ReadZ_Ok1:
	mov	dx,offset inbuf.zMbrName	;move to zFilename psn
	mov	cx,inbuf.zNameLen		;length of member filename
						;fall thru to ...	v1.3a

;v1.4	Common subroutine for ReadZ and Read_LZH
;	DX -> buffer
;	CX = bytes to read
;	BX MUST have archdl .. so protect BX!

ReadZ_It:
	mov	ah,3FH				;read from file/device
	int	21H
	jb	ReadZ_ItX			;failed, error in AX	v1.3a

;v1.4	We'll update our curpsn file pointers later
;	when we try to read past compressed file contents.

;v1.4	 add	curpsn,ax			;bump current file ptr	v1.3a
						;by amount read		v1.3a
;v1.4	 adc	word ptr curpsn[2],0		;bump psn.hi if carry	v1.3a

	 cmp	ax,cx				;read all we expected?
	 mov	ax,0				;clear AX		v1.3a
	 jz	ReadZ_ItX			;yep, return CF clear	v1.3a
	  mov	al,0BH				;assume unexpected EOF
						;('invalid format')
	  stc
ReadZ_ItX:
	ret					;CF, AL set per error	v1.3a

Read_Zip_Entry	endp			;GetHdr subroutine


;v1.3	Common subroutine
;	Bumps archive file pointers to next entry
;	On success, return:
;	 CF clear
;	 AL = 0
;	On failure (e.g., couldn't move ptrs), return:
;	 CF set
;	 AL = error

Bump_ArcPtrs	proc	near

	cmp	ftype,ZIP		;ZIP file?			v1.3
	jz	Next_ZEntry		;bump file ptr to next entry	v1.3

;v1.3	Entirely new code

	mov	bx,offset archdr.aCmpSiz	;-> encoded file length	v1.4
	mov	dx,[bx]			;.lo				v1.4
	mov	cx,2[bx]		;.hi
	jmp	short Bump_Common	;common code


;v1.3	Positions ZIP file pointer to next local entry.
;	We've already read in the entire header, plus the filename,
;	so the file pointer should be just beyond the filename
;	(at the Extra field).
;	Move file pointers beyond the Extra field, and then past
;	the actual entry data (the compressed size).

Next_ZEntry:

	mov	bx,offset inbuf			;point back to structure
	mov	dx,[bx].zCmpSiz			;size.lo
	mov	cx,[bx].zCmpSiz[2]		;size.hi
	add	dx,[bx].zExtraLen		;add in extra field length
	adc	cx,0				;in case of carry

Bump_Common:

	mov	bx,archdl			;file handle
	mov	ax,4201H			;move pointer from current loc
	int	21H
	jb	Bump_X				;seek error		v1.3a
						;return CF set, AL=error v1.3a

;v1.4	Updating curpsn variables now
;	so the NEXT GetHdr call will have current data.
	 mov	bx,offset curpsn
	 mov	[bx],ax
	 mov	2[bx],dx
	 xor	ax,ax				;AX,CF clear		v1.3a
Bump_X:
	ret

Bump_ArcPtrs	endp


;v1.3	Formats, displays totals

Format_Totals	proc	near

	mov	ax,totmbrs		;total members			v1.3
	mov	di,offset vtmbrs-2	;format total members		v1.3
	call	Asciify			;				v1.3

	mov	bx,offset totlen	;-> total actual file size	v1.4
	mov	ax,[bx]			;.lo				v1.4
	mov	dx,2[bx]		;.hi				v1.4

	push	ax			;save totlen.lo			v1.4
	push	dx			; and totlen.hi			v1.4

	mov	di,offset vtlen		;format total actual file size	v1.3
	call	Asciify_Long		;				v1.3

	mov	bx,offset totsize	;-> total compressed file sizes	v1.4
	mov	ax,[bx]			;.lo				v1.4
	mov	dx,2[bx]		;.hi				v1.4

	push	ax			;save totsize.lo		v1.4
	push	dx			; and totsize.hi		v1.4

	mov	di,offset vtsize	;format total archive file size	v1.3
	call	Asciify_Long		;				v1.3

; reduce the total size/length to word values

	pop	dx			;totsize.hi			v1.4
	pop	cx			;totsize.lo			v1.4
	pop	ax			;totlen.hi			v1.4
	pop	bx			;totlen.lo			v1.4

ArcV2b: or	ax,ax			; big number?
	jz	ArcV2c			; nope, can use it
	 shr	ax,1			; yup, divide by two
	 rcr	bx,1
	 shr	dx,1
	 rcr	cx,1
	 jmp	short ArcV2b

ArcV2c:
	mov	ax,bx
	mov	sign,' '		; whata kludge
	cmp	ax,cx			; arc is bigger than orig?
	jb	ArcV2c1
	 sub	ax,cx			; amount saved
	 jmp	short ArcV2f

ArcV2c1:
	sub	ax,cx
	neg	ax
	mov	sign,'-'

ArcV2f:
	mul	hundred			; to percentage
	add	ax,50
	adc	dx,0			; round up percent
	or	bx,bx			; empty file?
	jnz	ArcV2d
	 mov	ax,100
	 jmp	short ArcV2e

ArcV2d: div	bx
ArcV2e:
	mov	di,offset vtsf-2	;format stowage factor		v1.3
	call	Asciify			;AX				v1.3

	mov	al,sign
	mov	vtsf,al
	Print	vthdr			; display totals
	ret

Format_Totals	endp


OpenArc proc	near			; open new archive

	mov	dx,offset arcname
	mov	ax,3d00h		; for input
	int	21h
	jnb	Open_GetSize		;opened ok			v1.3a
	 ret				;return CF set, AL=error	v1.3a

Open_GetSize:
	mov	bx,ax			;handle into BX			v1.3a
	mov	archdl,ax		; save file handle

;v1.3a	We get the total file size now for later EOF testing.
	xor	dx,dx			;0 offset
	xor	cx,cx
	mov	ax,4202H		;from file end
	int	21H
	mov	filelen,ax		;length.low
	mov	filelen[2],dx		;length.hi
	xor	cx,cx			;back to start
	xor	dx,dx
	mov	ax,4200H		;psn file pointer from start
	int	21H
	ret				;CF should be clear

OpenArc endp


ClosArc proc	near

	mov	bx,archdl		; previous handle
	or	bx,bx			; already open?
	jz	Closed
	 mov	ah,3eh			; yes, so close it
	 int	21H
Closed:	mov	archdl,0		;flag as closed
	ret

ClosArc endp

;
;	print null-terminated (AsciiZ) string like int 21h function 9
;	Enter with DS:DX -> AsciiZ string
;	destroys AX
;	On success, return:
;	 CF clear
;	 AL = 0
;	On failure (write fail), return:
;	 CF set
;	 AL = error

PrintS  proc	near

	push	di			;v1.3
	push	bx
	push	cx

	mov	cx,0FFFFH		;max scan			v1.3
	xor	al,al			;handy 0			v1.3
	mov	di,dx			;string start			v1.3
	repne	scasb			;find the terminator		v1.3
	inc	cx			;adjust				v1.3
	not	cx			;CX=length			v1.3

	mov	bx,outhdl		; using std out or temp file
	or	bx,bx			;never opened?			v1.3
	jnz	Print_S1		;nope, we got a handle		v1.3
	 inc	bx			;make it StdErr			v1.3
	 inc	bx
Print_S1:				;				v1.3
	mov	ah,40h			; write to file
	int	21h
	jnb	PrintS_Done		;fine				v1.3

;v1.3	What happens if we're trying to write to an output file
;	and THAT fails?  Even error msgs can't get out.
;	We switch to StdErr, that's what!

	mov	di,ax			;save error level		v1.3a
	mov	bx,STDERR		;force to STdErr		v1.3a
	mov	outhdl,bx		;and for future output		v1.3a
	mov	ah,40H			;write to STDOUT		v1.3a
	int	21H			;(CX,DX unchanged)		v1.3a
	mov	ax,di			;restore orig error		v1.3a
	stc				;return CF set			v1.3a
 
PrintS_Done:
	pop	cx			; recover registers
	pop	bx
	pop	di
	ret

PrintS  endp

	page
;
;	format the time (in AX)

time	record  hour:5,min:6,sec:5	;packed time

GetTime proc	near			;format the date
	mov	di,offset vtime
	or	ax,ax			;it is zero?
	jz	GotTime

	push	ax			;save date
	and	ax,mask hour		;get hour part
	mov	cl,hour			;bits to shift
	shr	ax,cl
	call	Cnvrt1
	stosw
	mov	al,':'
	stosb

GT3:	pop	ax			;get the time back
	and	ax,mask min		;get min part
	mov	cl,min			;bits to shift
	call	Cnvrt
	stosw
GotTime:ret
GetTime endp


Cnvrt2  proc	near			;convert to ascii
	call	Cnvrt
	cmp	al,'0'			;suppress leading zero
	jne	Cnvrtd
	 mov	al,' '
	 ret

Cnvrt:  shr	ax,cl
Cnvrt1: aam				;make al into bcd
	or	ax,'00'			; and to ascii
	xchg	al,ah
Cnvrtd: ret
Cnvrt2  endp

	page
;
;	format the date (in AX)

date	record  yr:7,mo:4,dy:5		;packed date

GetDate proc	near			;format the date
	or	ax,ax			;is it zero?
	jz	GotDate

	push	ax			;save date
	and	ax,mask yr		;get year part
	mov	cl,yr			;bits to shift
	call	Cnvrt
	mov	di,offset vyear
	or	al,'8'			;adjust for base year
	stosw

	pop	bx			;get the date back
	push	bx			;save it
	and	bx,mask mo		;get month part
	mov	cl,mo			;bits to shift
	shr	bx,cl
	add	bx,bx			; form month table index
	add	bx,bx
	lea	si,word ptr months-4[bx]
	mov	cx,word ptr 3
	mov	di,offset vmonth
	rep	movsb

	pop	ax			;get the date back
	and	ax,mask dy		;get day part
	mov	cl,dy			;bits to shift
	call	Cnvrt
	mov	di,offset vdate
	stosw
GotDate:ret
GetDate endp

	page
;
;v1.3	A severely hacked single/double precision number conversion function.
;	Originally from JMODEM, but severely hacked by Toad Hall.
;	ES:DI -> string
;	Destroys everything almost.

;Enter here if integer in AX
Asciify	proc	near

	xor	dx,dx			; clear fake long.hi
	mov	si,ax			;move integer into SI
	xor	ah,ah			;clear msb (flag)
	jmp	short Ascii_Ax		;jump into the code

;Enter here if long integer in DX:AX.
Asciify_Long:

	mov	si,ax			;move long.lo into SI
	xor	ah,ah			;clear msb (flag)
Comment	~
	MOV	CX,3B9AH		; Get billions
	MOV	BX,0CA00H
	CALL	Subtr			; Subtract them out

	MOV	CX,05F5H		; Get hundred-millions
	MOV	BX,0E100H
	CALL	Subtr			; Subtract them out
Comment	ends	~

	and	dx,4FFH			;seems likely			v1.3
	MOV	CX,word ptr 0098H	; Get ten-millions
	MOV	BX,9680H
	CALL	Subtr			; Subtract them out

	MOV	CX,word ptr 000FH	; Get millions
	MOV	BX,4240H
	CALL	Subtr			; Subtract them out

	MOV	CX,word ptr 1		; Get hundred-thousands
	MOV	BX,86A0H
	CALL	Subtr			; Subtract them out

Ascii_Ax:
	xor	cx,cx			; Get ten-thousands
	MOV	BX,2710H
	CALL	Subtr			; Subtract them out
	MOV	BX,03E8H
	CALL	Subtr			; Subtract them out

	MOV	BX,word ptr 0064H
	CALL	Subtr			; Subtract them out
	MOV	BX,word ptr 10
	CALL	Subtr			; Subtract them out
	mov	ax,si			;residual in SI
	add	AL,'0'			; Add bias to residual
	stosb				; Put in the string
	RET

;Common subroutine for Asciify

Subtr:	mov	al,'0'-1

Subtr1:	INC	al			; Bump the digit character
	SUB	si,BX			; Dword subtraction
	SBB	DX,CX
	JNB	Subtr1			; Continue until a carry

	ADD	si,BX			; One too many, add back
	ADC	DX,CX			;   and the remainder

	cmp	al,'0'
	jnz	Subtr2			;nope, turn off leading flag, stuff
	 or	ah,ah			;no more leading spaces?
	 jnz	Sub_Stuff		;right, stuff the '0'
	  mov	al,' '			;make it neat with leading spaces
Sub_Stuff:
	stosb				;stuff the char
	RET

Subtr2:	inc	ah			;turn off leading space flag
	stosb
	ret
Asciify	ENDP


;v1.3a	Convert 16-bit binary word in AX
;	to hex ASCII string at ES:DI
;	(No need to save any registers)

hexchar db	'0123456789ABCDEF'

Cvh	proc	near

	mov	si,offset hexchar	;for faster access		v1.3a
	mov	dx,ax			; save 16-bits

	mov	bl,dh			; third nibble
	mov	cx,0F04H		;CL=4 for shifting,		v1.3a
					;CH=0FH for masking		v1.3a
	shr	bl,cl
	mov	al,[si][bx]		;snarf hex char			v1.3a
	stosb

	mov	bl,dh			; last nibble
	and	bl,ch	;0fh		;				v1.3a
	mov	al,[si][bx]		;snarf hex char			v1.3a
	stosb

	mov	bl,dl			; first nibble
	sub	bh,bh
	shr	bl,cl			; isolate (CL still 4)		v1.3a
	mov	al,[si][bx]		;snarf hex char			v1.3a
	stosb

	mov	bl,dl			; second nibble
	and	bl,ch	;0fh		; isolate			v1.3a
	mov	al,[si][bx]		;snarf hex char			v1.3a
	stosb
	ret

Cvh	endp

	subttl	'--- i/o data areas'

ArcV	endp

archdr  db	30 dup (0)		; i/o area for a header		v1.3a

inbuf	db	INBUFSZ dup (0)		;just big enough for ZIP
					;directories and filenames	v1.3a

CSEG	ENDS
	END
