;*---------------------------------------------------------------------------
;  :Program.	xpackbest.asm
;  :Contents.	pack file using XPK and mode with best compression ratio
;  :Author.	Bert Jahn
;  :EMail.	wepl@whdload.org
;  :Address.	Feodorstraße 8, Zwickau, 08058, Germany
;  :Version.	$Id: xpackbest.asm 1.5 2001/05/08 15:47:01 jah Exp jah $
;  :History.	17.04.01 initial
;		27.04.01 recurse added
;		28.04.01 ^C added, progress display added
;  :Requires.	OS V37+
;  :Copyright.	Public Domain
;  :Language.	68000 Assembler
;  :Translator.	Barfly V2.9
;---------------------------------------------------------------------------*
;##########################################################################

	INCDIR	Includes:
	INCLUDE	dos/dos.i
	INCLUDE	exec/memory.i
	INCLUDE	lvo/dos.i
	INCLUDE	lvo/exec.i
	INCLUDE	lvo/xpkmaster_lib.i
	INCLUDE	xpk/xpk.i
	
	INCLUDE	macros/ntypes.i

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

GL	EQUR	A4		;a4 ptr to Globals
LOC	EQUR	A5		;a5 for local vars

	STRUCTURE	ArgArray,0
		ULONG	aa_input
		ULONG	aa_modes
		ULONG	aa_ratio
		ULONG	aa_pat
		LABEL	aa_SIZEOF

GL_PATBUFLEN = 256

	NSTRUCTURE	Globals,0
		NAPTR	gl_execbase
		NAPTR	gl_dosbase
		NAPTR	gl_xpkbase
		NULONG	gl_outbuf
		NULONG	gl_outbuflen
		NULONG	gl_outlen
		NULONG	gl_outgain
		NULONG	gl_bestbuflen
		NULONG	gl_bestgain
		NULONG	gl_bestmode
		NULONG	gl_srcptr
		NULONG	gl_srclen
		NLONG	gl_rc
		NAPTR	gl_rdargs
		NSTRUCT	gl_rdarray,aa_SIZEOF
		NLONG	gl_mode
		NSTRUCT	gl_method,8
		NSTRUCT	gl_patbuf,GL_PATBUFLEN
		NSTRUCT	gl_xpkhook,h_SIZEOF
		NALIGNLONG
		NLABEL	gl_SIZEOF

;##########################################################################

	PURE
	OUTPUT	c:xPackBest
	SECTION	"",CODE
	BOPT	O+				;enable optimizing
	BOPT	OG+				;enable optimizing
	BOPT	ODd-				;disable mul optimizing
	BOPT	ODe-				;disable mul optimizing

		bra	.start
		dc.b	"$VER: xPackBest 1.1 "
	DOSCMD	"WDate >t:date"
	INCBIN	"t:date"
		dc.b	" by Bert Jahn"
		dc.b	" V37+",0
	CNOP 0,2
.start

;##########################################################################

		link	GL,#gl_SIZEOF
		move.l	(4).w,(gl_execbase,GL)
		move.l	#RETURN_FAIL,(gl_rc,GL)
		clr.l	(gl_rdarray+aa_ratio,GL)
		clr.l	(gl_rdarray+aa_pat,GL)

		move.l	#37,d0
		lea	(_dosname),a1
		move.l	(gl_execbase,GL),a6
		jsr	_LVOOpenLibrary(a6)
		move.l	d0,(gl_dosbase,GL)
		beq	.nodoslib

		lea	(_template),a0
		move.l	a0,d1
		lea	(gl_rdarray,GL),a0
		move.l	a0,d2
		moveq	#0,d3
		move.l	(gl_dosbase,GL),a6
		jsr	(_LVOReadArgs,a6)
		move.l	d0,(gl_rdargs,GL)
		bne	.argsok
		lea	(_readargs),a0
		bsr	_PrintErrorDOS
		bra	.noargs
.argsok	
		moveq	#3,d1
		move.l	(gl_rdarray+aa_ratio,GL),d0
		beq	.ratok
		move.l	d0,a0
		move.l	(a0),d1
.ratok		move.l	d1,(gl_rdarray+aa_ratio,GL)

		move.l	(gl_rdarray+aa_pat,GL),d1
		beq	.patok
		lea	(gl_patbuf,GL),a1
		move.l	a1,d2
		move.l	#GL_PATBUFLEN,d3
		move.l	(gl_dosbase,GL),a6
		jsr	(_LVOParsePatternNoCase,a6)
		tst.l	d0
		bpl	.patok
		lea	(_parsepattern),a0
		bsr	_PrintErrorDOS
		bra	.opend
.patok
		bsr	_Main
.opend
		move.l	(gl_rdargs,GL),d1
		move.l	(gl_dosbase,GL),a6
		jsr	(_LVOFreeArgs,a6)
.noargs
		move.l	(gl_dosbase,GL),a1
		move.l	(gl_execbase,GL),a6
		jsr	(_LVOCloseLibrary,a6)
.nodoslib
		move.l	(gl_rc,GL),d0
		unlk	GL
		rts

;##########################################################################

_Main		movem.l	d2-d7/a2-a3/a6,-(a7)

	;open xpk library
		move.l	#4,d0
		lea	(_xpkname),a1
		move.l	(gl_execbase,GL),a6
		jsr	_LVOOpenLibrary(a6)
		move.l	d0,(gl_xpkbase,GL)
		beq	.noxpklib

	;check input
		move.l	#DOS_FIB,d1			;type
		moveq	#0,d2				;tags
		move.l	(gl_dosbase,GL),a6
		jsr	(_LVOAllocDosObject,a6)
		move.l	d0,d7				;D7 = fib
		beq	.nofib
		move.l	(gl_rdarray+aa_input,GL),d1	;name
		move.l	#ACCESS_READ,d2			;mode
		jsr	(_LVOLock,a6)
		move.l	d0,d6				;D6 = lock
		bne	.lockok
		move.l	(gl_rdarray+aa_input,GL),a0
		bsr	_PrintErrorDOS
		bra	.nolock
.lockok
		move.l	d6,d1				;lock
		move.l	d7,d2				;fib
		jsr	(_LVOExamine,a6)
		tst.l	d0
		bne	.exaok
		move.l	(gl_rdarray+aa_input,GL),a0
		bsr	_PrintErrorDOS
		bra	.noexa
.exaok
		move.l	#RETURN_ERROR,(gl_rc,GL)
.noexa
		move.l	d6,d1
		move.l	(gl_dosbase,GL),a6
		jsr	(_LVOUnLock,a6)
.nolock
		move.l	#DOS_FIB,d1			;type
		move.l	d7,d2
		move.l	(gl_dosbase,GL),a6
		jsr	(_LVOFreeDosObject,a6)
.nofib
		cmp.l	#RETURN_ERROR,(gl_rc,GL)
		bhi	.retnio
		
		move.l	(gl_rdarray+aa_input,GL),a0
		pea	.return
		move.l	d7,a1
		tst.l	(fib_DirEntryType,a1)
		bmi	_PackFile
		bra	_ScanDir
.return		tst.l	d0
		beq	.retnio
		move.l	#RETURN_OK,(gl_rc,GL)		;final success
.retnio
		move.l	(gl_xpkbase,GL),a1
		move.l	(gl_execbase,GL),a6
		jsr	(_LVOCloseLibrary,a6)
.noxpklib
		movem.l	(a7)+,d2-d7/a2-a3/a6
		rts

;##########################################################################
;----------------------------------------
; recursively compress directory
; IN:	A0 = CPTR name of the directory
; OUT:	D0 = BOOL success

LSD_PATHLEN	= 100

	NSTRUCTURE	LocalScanDir,0
		NSTRUCT	lsd_path,LSD_PATHLEN
		NLABEL	lsd_SIZEOF

_ScanDir	movem.l	d2/d5-d7/a2-a3/a6,-(a7)
		link	LOC,#lsd_SIZEOF
		moveq	#0,d7				;D7 = success
		move.l	(gl_dosbase,GL),a6		;A6 = dosbase

	;copy actual path
		moveq	#LSD_PATHLEN,d0
		lea	(lsd_path,LOC),a1
		bsr	_CopyString
	;remember current pathlen
		lea	(lsd_path,LOC),a0
		bsr	_StrLen
		lea	(lsd_path,LOC,d0.l),a3		;A3 = pathend
	;lock directory
		lea	(lsd_path,LOC),a0
		move.l	a0,d1
		move.l	#ACCESS_READ,d2
		jsr	(_LVOLock,a6)
		move.l	d0,d6				;D6 = lock to dir
		bne	.lockok
		lea	(_lockdir),a0
		bsr	_PrintErrorDOS
		bra	.nolock
.lockok
	;get memory for fib
		move.l	#DOS_FIB,d1			;type
		moveq	#0,d2				;tags
		jsr	(_LVOAllocDosObject,a6)
		move.l	d0,a2				;A2 = fib
		tst.l	d0
		beq	.nofib
	;initial examine
		move.l	d6,d1				;lock
		move.l	a2,d2				;fib
		jsr	(_LVOExamine,a6)
		tst.l	d0
		bne	.exaok
		lea	(_examine),a0
		bsr	_PrintErrorDOS
		bra	.noexa
.exaok
	;start loop for each object in the actual directory
.next
	;get entry
		move.l	d6,d1				;lock
		move.l	a2,d2				;fib
		jsr	(_LVOExNext,a6)
		tst.l	d0
		bne	.nextok
		jsr	(_LVOIoErr,a6)
		cmp.l	#ERROR_NO_MORE_ENTRIES,d0
		seq	d7
		ext.w	d7
		ext.l	d7
		bne	.end
		lea	(_exnext),a0
		bsr	_PrintErrorDOS
		bra	.end
.nextok
	;restore and append filename to path
		clr.b	(a3)
		lea	(lsd_path,LOC),a0
		move.l	a0,d1				;dirname
		lea	(fib_FileName,a2),a0
		move.l	a0,d2				;filename
		moveq	#LSD_PATHLEN,d3			;buffer size
		jsr	(_LVOAddPart,a6)
		tst.l	d0
		beq	.end
	;check if it is a file or a directory
		tst.l	(fib_DirEntryType,a2)
		bmi	.file
		lea	(lsd_path,LOC),a0		;dir name
		bsr	_ScanDir			;recurse
		tst.l	d0
		bne	.next
		bra	.end
	;check for pattern
.file		tst.l	(gl_rdarray+aa_pat,GL)
		beq	.nopat
		lea	(gl_patbuf,GL),a0
		move.l	a0,d1				;pattern
		lea	(fib_FileName,a2),a0
		move.l	a0,d2				;string
		jsr	(_LVOMatchPatternNoCase,a6)
		tst.l	d0
		beq	.next
.nopat
	;pack file
		lea	(lsd_path,LOC),a0		;file name
		bsr	_PackFile
		tst.l	d0
		bne	.next
.end
.noexa
	;free memory for fib
		move.l	#DOS_FIB,d1			;type
		move.l	a2,d2
		jsr	(_LVOFreeDosObject,a6)
.nofib
	;free directory lock
		move.l	d6,d1
		jsr	(_LVOUnLock,a6)
.nolock
		move.l	d7,d0				;success
		unlk	LOC
		movem.l	(a7)+,_MOVEMREGS
		rts

;##########################################################################
;----------------------------------------
; compress file
; IN:	A0 = CPTR name of the file
; OUT:	D0 = BOOL success

_PackFile	movem.l	d2-d3/d5-d7/a2/a6,-(a7)
		move.l	a0,d3				;D3 = filename
		moveq	#0,d5				;D5 = bestbuf
		moveq	#-1,d6				;D6 = bestlen
		moveq	#0,d7				;D7 = success

		bsr	_LoadFileMsg
		move.l	d1,(gl_srclen,GL)
		move.l	d0,(gl_srcptr,GL)
		beq	.nosrc
		
		lea	(_loadmsg),a0
		lea	(gl_srclen,GL),a1
		bsr	_PrintArgs

		move.l	(gl_rdarray+aa_modes,GL),a2	;A2 = modes
.next		move.l	(a2),a0
		move.l	(a0),(gl_method,GL)
		clr.b	(gl_method+4,GL)
		addq.l	#5,a0
		bsr	_atoi
		move.l	d0,(gl_mode,GL)
		
		lea	(_ln),a0
		bsr	_Print
		
	;init hook
		pea	(.xpkhook)
		move.l	(a7)+,(gl_xpkhook+h_Entry,GL)
		clr.l	(gl_xpkhook+h_SubEntry,GL)
		move.l	GL,(gl_xpkhook+h_Data,GL)

		move.l	a7,d2				;D2 = old sp
		clr.l	-(a7)
		pea	(gl_method,GL)
		pea	XPK_PackMethod
		move.l	(gl_mode,GL),-(a7)
		pea	XPK_PackMode
		move.l	(gl_srclen,GL),-(a7)
		pea	XPK_InLen
		move.l	(gl_srcptr,GL),-(a7)
		pea	XPK_InBuf
		pea	(gl_outlen,GL)
		pea	XPK_GetOutLen
		pea	(gl_outbuflen,GL)
		pea	XPK_GetOutBufLen
		pea	(gl_outbuf,GL)
		pea	XPK_GetOutBuf
		pea	(gl_xpkhook,GL)
		pea	XPK_ChunkHook
		pea	-1
		pea	XPK_TaskPri
		move.l	a7,a0
		move.l	(gl_xpkbase,GL),a6
		jsr	(_LVOXpkPack,a6)
		move.l	d2,a7				;restore sp
		tst.l	d0
		beq	.packok
		sub.l	a0,a0
		jsr	(_LVOXpkPrintFault,a6)
		bra	.freebest
.packok
		move.l	(gl_outlen,GL),d0
		cmp.l	d6,d0
		blo	.newbest
		move.l	(gl_outbuflen,GL),d0
		move.l	(gl_outbuf,GL),a1
		move.l	(gl_execbase,GL),a6
		jsr	(_LVOFreeMem,a6)
		bra	.checknext

.newbest	move.l	d5,d0
		beq	.setnew
		move.l	d0,a1
		move.l	(gl_bestbuflen,GL),d0
		move.l	(gl_execbase,GL),a6
		jsr	(_LVOFreeMem,a6)
.setnew		move.l	(gl_outgain,GL),(gl_bestgain,GL)
		move.l	(gl_outbuf,GL),d5
		move.l	(gl_outbuflen,GL),(gl_bestbuflen,GL)
		move.l	(gl_outlen,GL),d6
		move.l	(a2),(gl_bestmode,GL)

.checknext	addq.l	#4,a2
		tst.l	(a2)
		bne	.next
		
		move.l	(gl_rdarray+aa_ratio,GL),d0
		cmp.l	(gl_bestgain,GL),d0
		bgt	.skip

		lea	(_savemsg),a0
		lea	(gl_bestmode,GL),a1
		bsr	_PrintArgs

		move.l	d6,d0
		move.l	d5,a0
		move.l	d3,a1
		bsr	_SaveFile
		tst.l	d0
		beq	.freebest
		bra	.success

.skip		lea	(_skipmsg),a0
		bsr	_Print

.success	moveq	#-1,d7				;success

.freebest
		move.l	d5,d0
		beq	.freesource
		move.l	d0,a1
		move.l	(gl_bestbuflen,GL),d0
		move.l	(gl_execbase,GL),a6
		jsr	(_LVOFreeMem,a6)
.freesource
		move.l	(gl_srcptr,GL),a1
		move.l	(gl_execbase,GL),a6
		jsr	(_LVOFreeVec,a6)
.nosrc
		move.l	d7,d0
		movem.l	(a7)+,_MOVEMREGS
		rts

; A0 = hook
; A1 = progress report

.xpkhook	movem.l	GL/a6,-(a7)
		move.l	(h_Data,a0),GL
		move.l	(xp_CF,a1),(gl_outgain,GL)
		move.l	a7,a6
		cmp.l	#XPKPROG_END,(xp_Type,a1)
		beq	.end
		lea	(_packmsg),a0
		move.l	(xp_Speed,a1),-(a7)
		move.l	(xp_CF,a1),-(a7)
		move.l	(xp_Done,a1),-(a7)
		move.l	(gl_mode,GL),-(a7)
		pea	(gl_method,GL)
		bra	.print
.end		cmp.l	#100,(xp_Done,a1)
		bne	.error
		lea	(_packmsgend),a0
		move.l	a7,a6
		move.l	(xp_Speed,a1),-(a7)
		move.l	(xp_CF,a1),-(a7)
		move.l	(xp_CCur,a1),-(a7)
		move.l	(gl_mode,GL),-(a7)
		pea	(gl_method,GL)
		bra	.print
.error		lea	(_packmsgerr),a0
		move.l	a7,a6
		move.l	(xp_Speed,a1),-(a7)
		move.l	(xp_CF,a1),-(a7)
		move.l	(xp_CCur,a1),-(a7)
		move.l	(gl_mode,GL),-(a7)
		pea	(gl_method,GL)
.print		move.l	a7,a1
		bsr	_PrintArgs
		move.l	a6,a7
		move.l	#SIGBREAKF_CTRL_C,d1
		move.l	(gl_dosbase,GL),a6
		jsr	(_LVOCheckSignal,a6)
		movem.l	(a7)+,_MOVEMREGS
		rts

;##########################################################################

	INCDIR	Sources:
	INCLUDE	dosio.i
		FlushOutput
		Print
		PrintArgs
	INCLUDE	error.i
		PrintErrorDOS
	INCLUDE	files.i
		LoadFileMsg
		SaveFile
	INCLUDE	strings.i
		atoi
		CopyString
		StrLen

;##########################################################################

_loadmsg	dc.b	"file has length of %ld bytes",10,0
_packmsg	dc.b	11,155,"Kpacking using %s.%ld, done %ld%%, gain %ld%%, %ld bytes/sec",10,0
_packmsgend	dc.b	11,155,"Kpacked using %s.%ld, length %ld bytes, gain %ld%%, %ld bytes/sec",10,0
_packmsgerr	dc.b	11,155,"Kpacking using %s.%ld, ",0
_savemsg	dc.b	"saving file using %s",10,0
_skipmsg	dc.b	"file skipped"
_ln		dc.b	10,0

; operations
_readargs	dc.b	"read arguments",0
_parsepattern	dc.b	"parse pattern",0
_allocdestmem	dc.b	"alloc temp dest mem",0
_lockdir	dc.b	"lock directory",0
_examine	dc.b	"examine",0
_exnext		dc.b	"exnext",0

; subsystems
_dosname	dc.b	"dos.library",0
_xpkname	dc.b	"xpkmaster.library",0

_template	dc.b	"Input/A"		;file to pack
		dc.b	",Modes/M/A"		;modes to test
		dc.b	",MinRatio/K/N"		;required ratio to store packed
		dc.b	",Pattern/K"		;file pattern if running recursively
		dc.b	0

;##########################################################################

	END

