* Lloyd's packed file system written by A.D.Webber (e-mail: adw@ukc.ac.uk)
*
* based on the LZSS compression algorithm combined with an idea or two
* from DC's Data Diet.
*
* LPAK release 0.5(beta)
*
*					Lloyd. (28/9/1993)

tab	equ	9
lf	equ	10
cr	equ	13
esc	equ	27

t1_vec	equ	$84

BLKSIZE	equ	1024
MINLEN	equ	2
MAXLEN	equ	18
WSIZE	equ	4096

APPTERM0 equ	$00
FCREATE	equ	$3c
FOPEN	equ	$3d
FCLOSE	equ	$3e
FREAD	equ	$3f
FWRITE	equ	$40
UNLINK	equ	$41
FSEEK	equ	$42
APPTERM	equ	$4c

X_ID	equ	"XBRA"
P_ID	equ	"LPAK"

	section	text
	bra	start
lpak_flag			; you can now control the operation externally
	dc.w	-1
	dc.l	X_ID
	dc.l	P_ID
t1_sav	ds.l	1
t1_rot	tst.w	lpak_flag
	beq.s	t1_done
	move.l	usp,a0		; use usp to find the desired function
	move.w	(sp),d0		; check old sr
	btst	#$d,d0
	beq.s	called_from_user_mode
called_from_supervisor_mode
	lea.l	6(sp),a0	; if called from supervisor use system stack
called_from_user_mode
do_t1	move.w	(a0),d0
	cmp.w	#FOPEN,d0	; handle open file commands
	beq	handle_new_file
	cmp.w	#FCLOSE,d0	; handle close file commands
	beq.s	handle_end_file
	cmp.w	#APPTERM0,d0	; handle program exits in a graceful fashion
	beq.s	clear_all_file_handles
	cmp.w	#APPTERM,d0
	beq.s	clear_all_file_handles
* not yet implemented
	nop
clear_all_file_handles
t1_done	move.l	t1_sav(pc),-(sp)
	rts			; nothing interesting so we pass this trap down the chain

* handle close files for files that are (really) mine
*
* this section is executed in supervisor mode
handle_end_file
	move.w	sr,d1
	move.w	2(a0),d0	; get file handle
	lsl.w	#1,d0		; convert to offset into my handle table (=> multiply by 2)
	lea.l	handles(pc),a1
	tst.w	0(a1,d0.w)	; is the handle mine?
	beq.s	t1_done		; no -> end trap
	move.w	d0,temp_handle	; yes -> close the file
	move.l	(a0),-(sp)
	pea.l	fclose_sreturn(pc)
	move.w	d1,-(sp)
	move.l	t1_sav(pc),-(sp)
	rts

* this section is executed in supervisor mode
fclose_sreturn
	addq.l	#4,sp
* this section is executed in user mode
fclose_ureturn
	move.w	temp_handle(pc),d0	; delete the handle from my table
	lea.l	handles(pc),a0
	clr.w	0(a0,d0.w)
	lea.l	temp_path(pc),a0
	lea.l	names(pc),a1
	lsl.w	#3,d0			; multiply handle by 8 (=> multiplied by 16 in total)	
	adda.w	d0,a1
	lea.l	temp_name(pc),a2
	bsr	make_name		; regenerate original temporary file name
	pea.l	temp_name(pc)		; and use to delete the temporary file
	move.w	#UNLINK,-(sp)
	trap	#1
	addq.l	#6,sp
	bra.s	return_from_trap

* handle opening of new files
* if file has suitable header at start ('LPAK') unpack it to a temporary
* file and attach the user to that file rather than the original and set
* up my tables so that the file can be closed and deleted later.
*
* otherwise don't interfere.
*
* this section is executed in supervisor mode
handle_new_file
	move.w	sr,d1
	move.w	6(a0),open_type	; perform original open operation
	move.l	4(a0),-(sp)
	move.l	0(a0),-(sp)
	pea.l	fopen_sreturn(pc)
	move.w	d1,-(sp)
	move.l	t1_sav(pc),-(sp)
	rts

* this section is executed in supervisor mode
fopen_sreturn
	addq.l	#8,sp
* this section is executed in user mode
fopen_ureturn
	tst.w	d0		; result of fopen from GEMDOS
	bpl.s	fopen_ok
bad_fopen			; leave here if file doesn't exist
return_from_trap
	rte			; and resume normal operation
fopen_ok
	move.w	d0,ihandle	; store input file handle
	pea.l	ibuff(pc)
	move.l	#8,-(sp)
	move.w	ihandle(pc),-(sp)
	move.w	#FREAD,-(sp)	; read first eight bytes for header
	trap	#1
	lea.l	12(sp),sp
	cmp.w	#8,d0		; did we read 8 bytes
	bne.s	not_a_compressed_file	; no -> error
	move.l	ibuff(pc),d0	; yes -> check for my 'LPAK' I.D.
	cmp.l	#P_ID,d0
	beq.s	fopen_cmp

not_a_compressed_file		; leave here if file isn't compressed
	move.w	#0,-(sp)
	move.w	ihandle(pc),-(sp)
	move.l	#0,-(sp)
	move.w	#FSEEK,-(sp)	; return file pointer to start of file
	trap	#1
	lea.l	10(sp),sp
	move.w	ihandle(pc),d0	; restore handle
	bra	return_from_trap
fopen_cmp			; handling a compressed file
	move.l	ibuff+4(pc),d0	; obtain unpacked length
	move.l	d0,length
	move.l	file_count(pc),d0
	lea.l	temp_file(pc),a0
	bsr	to_hex		; generate a really exciting temporary file name
	addq.l	#1,d0
	move.l	d0,file_count	; ensure next filename is different
	lea.l	temp_path(pc),a0
	lea.l	temp_file(pc),a1
	lea.l	temp_name(pc),a2
	bsr	make_name	; generate complete path+file name
	clr.w	-(sp)
	pea.l	temp_name(pc)
	move.w	#FCREATE,-(sp)	; create temporary file
	trap	#1
	addq.l	#8,sp
	tst.w	d0		; success?
	bmi	fatal_temp_open	; no -> major probs
	move.w	d0,temp_handle

; unpack desired file to a temporary file
	move.l	#WSIZE-1,d0	; initialise window buffer
	lea.l	pbuff(pc),a0
buffset	move.b	#' ',(a0)+
	dbra	d0,buffset

	clr.l	ulength		; initialise unpacking routine
	clr.l	olength
	move.l	#WSIZE-MAXLEN,wpos
	lea.l	ibuff+BLKSIZE(pc),a0	; indicate that data needs to be read
	lea.l	pbuff(pc),a1
	lea.l	obuff(pc),a2
* main unpack loop
unpack_loop
	cmpa.l	#ibuff+BLKSIZE,a0	; need more data?
	blt.s	ib_ok1
	movem.l	a0-a2/d0-d2,-(sp)
	lea.l	ibuff(pc),a0
	move.l	#BLKSIZE,d0
	bsr	ifread			; read more packed data
	movem.l	(sp)+,a0-a2/d0-d2
	lea.l	ibuff(pc),a0		; reset pointer to start of input buffer

* flag loop - reads a flag byte and then performs 8 operations based on
* those 8 bits
ib_ok1	move.b	(a0)+,d1	; get flags
	moveq	#0,d2		; start with flag at bit 0
u_loop1	btst	d2,d1		; test flag
	beq.s	expand		; if 0 -> represents packed item
	cmpa.l	#ibuff+BLKSIZE,a0	; otherwise literal copy
	blt.s	ib_ok2
	movem.l	a0-a2/d0-d2,-(sp)
	lea.l	ibuff(pc),a0
	move.l	#BLKSIZE,d0
	bsr	ifread
	movem.l	(sp)+,a0-a2/d0-d2
	lea.l	ibuff(pc),a0

ib_ok2	move.b	(a0)+,d0	; add character to window and to temporary file
	bsr	addchar
	addq.l	#1,ulength	; increase unpacked length
	bra.s	next_item
expand	cmpa.l	#ibuff+BLKSIZE,a0
	blt.s	ib_ok3
	movem.l	a0-a2/d0-d2,-(sp)
	lea.l	ibuff(pc),a0
	move.l	#BLKSIZE,d0
	bsr	ifread
	movem.l	(sp)+,a0-a2/d0-d2
	lea.l	ibuff(pc),a0
ib_ok3	move.b	(a0)+,d3
	cmpa.l	#ibuff+BLKSIZE,a0
	blt.s	ib_ok4
	movem.l	a0-a2/d0-d2,-(sp)
	lea.l	ibuff(pc),a0
	move.l	#BLKSIZE,d0
	bsr	ifread
	movem.l	(sp)+,a0-a2/d0-d2
	lea.l	ibuff(pc),a0
ib_ok4	move.b	(a0)+,d4
	move.w	d4,d0		; j = (j & 0x0f) + THRESH
	and.l	#$f,d0
	addq.l	#MINLEN,d0
	and.l	#$f0,d4		; i |= ((j & 0xf0) << 4)
	lsl.w	#4,d4
	move.b	d3,d4
	move.l	d0,d3
	addq.l	#1,d0
	add.l	d0,ulength
* copy out string from the window buffer to current position and temporary file
exploop	and.l	#WSIZE-1,d4	; & (N - 1)
	move.b	0(a1,d4.w),d0
	bsr	addchar
	addq.w	#1,d4
	dbra	d3,exploop
next_item
	move.l	ulength(pc),d0	; check for end of depack
	cmp.l	length(pc),d0
	bge	unpacked
	addq.w	#1,d2		; check for completion of current flag
	cmp.w	#8,d2
	blt	u_loop1
	bra	unpack_loop
unpacked			; depack complete
	tst.l	olength		; anything left to save?
	beq.s	unpacked_1
	pea.l	(a2)
	move.l	olength(pc),-(sp)
	move.w	temp_handle(pc),-(sp)
	move.w	#FWRITE,-(sp)	; save O/P block
	trap	#1
	lea.l	12(sp),sp
unpacked_1
	move.w	ihandle(pc),-(sp)
	move.w	#FCLOSE,-(sp)	; close input file
	trap	#1
	addq.l	#4,sp
	move.w	temp_handle(pc),-(sp)
	move.w	#FCLOSE,-(sp)	; close temporary file
	trap	#1
	addq.l	#4,sp
* update my tables and provide the user with a file pointer to the expanded file
	move.w	open_type(pc),-(sp)
	pea.l	temp_name(pc)
	move.w	#FOPEN,-(sp)
	trap	#1
	addq.l	#8,sp
	tst.w	d0
	bmi	fatal_temp_open_1
	move.w	d0,temp_handle
	lsl.w	#1,d0		; multiply by 2
	lea.l	handles(pc),a0
	move.w	#-1,0(a0,d0.w)	; set the handle active flag
	lsl.w	#3,d0		; multiply by 8 (=> 16 in total)
	lea.l	names(pc),a1
	adda.w	d0,a1
	lea.l	temp_file(pc),a0
	bsr	namecpy		; store the temporary file name
	move.w	temp_handle(pc),d0	; return the munged handle to the user
	bra	return_from_trap

make_name
fncopy1	move.b	(a0)+,(a2)+
	bne.s	fncopy1
	subq.l	#1,a2
fncopy2	move.b	(a1)+,(a2)+
	bne.s	fncopy2
	rts

namecpy	move.b	(a0)+,(a1)+
	bne.s	namecpy
	rts

addchar	movem.l	a0-a2/d0-d2,-(sp)
	move.l	wpos(pc),d1	; window[r++] = c
	move.b	d0,0(a1,d1.w)
	addq.l	#1,d1
	and.l	#WSIZE-1,d1	; r &= (N - 1)
	move.l	d1,wpos
	move.l	olength(pc),d1
	move.b	d0,0(a2,d1.w)
	addq.l	#1,d1
	cmp.l	#BLKSIZE,d1
	blt.s	add_done
	pea.l	(a2)		; save O/P block
	move.l	#BLKSIZE,-(sp)
	move.w	temp_handle(pc),-(sp)
	move.w	#FWRITE,-(sp)
	trap	#1
	lea.l	12(sp),sp
	moveq	#0,d1
add_done
	move.l	d1,olength
	movem.l	(sp)+,a0-a2/d0-d2
	rts

ifread	move.l	a0,-(sp)	; read a block from the I/P file
	move.l	d0,-(sp)
	move.w	ihandle(pc),-(sp)
	move.w	#FREAD,-(sp)
	trap	#1
	lea.l	12(sp),sp
	rts

fatal_temp_open			; major disk problem (probably)
	move.w	ihandle(pc),-(sp)
	move.w	#FCLOSE,-(sp)	; close I/P file
	trap	#1
	addq.l	#4,sp
fatal_temp_open_1
	moveq	#-1,d0		; return bad file handle to indicate error
	bra	return_from_trap

; convert the number in d0 to a string at a0
to_hex	move.l	d0,d1
	swap	d1
	lsr.w	#8,d1	; first byte
	bsr	hexbyte
	move.l	d0,d1
	swap	d1
	bsr	hexbyte
	move.l	d0,d1
	lsr.w	#8,d1
	bsr	hexbyte
	move.l	d0,d1
hexbyte	and.w	#$ff,d1
	move.w	d1,d2
	lsr.w	#4,d2
	move.b	hextab(pc,d2.w),(a0)+
	and.w	#$f,d1
	move.b	hextab(pc,d1.w),(a0)+
	rts
hextab	dc.b	'0123456789ABCDEF',0
	even

open_type
	ds.w	1
return_temp
	ds.w	1

temp_handle
	ds.w	1
ihandle	ds.w	1
otemp	ds.w	1
handles	ds.w	64		; allowed 64 files at once
names	ds.b	16*64		; allowed 16 letters in my temp filenames

ulength	ds.l	1
length	ds.l	1
olength	ds.l	1
wpos	ds.l	1

pbuff	ds.b	WSIZE
ibuff	ds.b	BLKSIZE
obuff	ds.b	BLKSIZE+MAXLEN
temp_path
	ds.b	40
file_count
	ds.l	1
temp_file
	dc.b	'00000000.tmp',0
	even
temp_name
	ds.b	64

start	lea.l	text1(pc),a0
	bsr	print

	lea.l	inf_file(pc),a0
	bsr	fopen
	tst.w	d0
	bmi.s	use_default_path
	move.w	d0,d7
	lea.l	temp(pc),a0
	move.l	#128,a1
	bsr	fread
	bsr	fclose
	lea.l	temp(pc),a0
	cmp.b	#':',1(a0)
	bne.s	use_default_path
	lea.l	default_path(pc),a1
	moveq	#0,d1
copy_path
	move.b	(a0)+,d0
	tst.b	d0
	beq.s	eof_path
	cmp.b	#cr,d0
	beq.s	eof_path
	cmp.b	#lf,d0
	beq.s	eof_path
	cmp.b	#'\',d0
	bne.s	copy
	addq.w	#1,d1
	cmp.w	#2,d1
	beq.s	eof_path
copy	move.b	d0,(a1)+
	bra.s	copy_path
eof_path
	clr.b	(a1)+
	clr.b	(a1)
	bra.s	check_path

use_default_path
	lea.l	bad_inf(pc),a0
	bsr	print

check_path
	lea.l	text2(pc),a0
	bsr	print
	bsr	do_check_p
	tst.l	d0
	bmi.s	error

	lea.l	super(pc),a0
	bsr	go_super

	lea.l	text3(pc),a0
	bsr	print

tsr	lea.l	start(pc),a0	; hold everything before the start!
	suba.l	4(sp),a0
	clr.w	-(sp)
	move.l	a0,-(sp)
	move.w	#$31,-(sp)
	trap	#1

error	lea.l	errtext(pc),a0
	bsr	print
	move.l	#$4c0000,-(sp)
	trap	#1

super	lea.l	(t1_vec).w,a0
	move.l	(a0),a1
check_XBRA_chain
	cmp.l	#X_ID,-12(a1)
	bne.s	install_new_XBRA
	cmp.l	#P_ID,-8(a1)
	beq.s	XBRA_already_there
	move.l	-4(a1),a1
	bra.s	check_XBRA_chain
install_new_XBRA
	lea.l	t1_rot(pc),a1
	move.l	(a0),-4(a1)	; patch old vector into my handler
	move.l	a1,(a0)		; path my handler into the TRAP #1 vector
XBRA_already_there
	rts

do_check_p
	moveq	#0,d0
	move.b	default_path(pc),d0
	cmp.b	#'a',d0
	bge.s	lcase
	subi.b	#'A',d0
	bra.s	dcp_1
lcase	subi.b	#'a',d0
dcp_1	move.l	d0,-(sp)
	move.w	d0,-(sp)
	move.w	#$e,-(sp)
	trap	#1
	addq.l	#4,sp
	move.l	(sp)+,d1
	btst	d1,d0			; $e returns contents of 4c2.w
	beq.s	bad_path
	lea.l	default_path+3(pc),a0
	tst.b	(a0)
	beq.s	path_ok
	pea.l	(a0)			; already exists?
	move.w	#$3b,-(sp)
	trap	#1
	addq.l	#6,sp
	tst.w	d0
	beq.s	path_ok
	pea.l	default_path+3(pc)	; create a new one?
	move.w	#$39,-(sp)
	trap	#1
	addq.l	#6,sp
	tst.w	d0
	bne.s	bad_path
path_ok	lea.l	default_path(pc),a0
	lea.l	temp_path(pc),a1
strcpy	move.b	(a0)+,(a1)+
	bne.s	strcpy
	cmp.b	#'\',-2(a1)
	beq.s	path_ok1
	move.b	#'\',-1(a1)
path_ok1
	clr.b	(a1)
	moveq	#0,d0
	rts
bad_path
	move.l	#-1,d0
	rts

go_super
	pea.l	(a0)
	move.w	#$26,-(sp)
	trap	#14
	addq.l	#6,sp
	rts

print	pea.l	(a0)
	move.w	#9,-(sp)
	trap	#1
	addq.l	#6,sp
	rts

fopen	clr.w	-(sp)
	pea.l	(a0)
	move.w	#FOPEN,-(sp)
	trap	#1
	addq.l	#8,sp
	rts

fread	pea.l	(a0)
	pea.l	(a1)
	move.w	d7,-(sp)
	move.w	#FREAD,-(sp)
	trap	#1
	lea.l	12(sp),sp
	rts

fclose	move.w	d7,-(sp)
	move.w	#FCLOSE,-(sp)
	trap	#1
	addq.l	#4,sp
	rts

	section	data
text1	dc.b	cr,lf,tab,'Lloyd''s packed filesystem',cr,lf
version_text:
	dc.b	tab,'--  version 0.5(beta)  --',cr,lf,0
bad_inf	dc.b	'Using default path definition',cr,lf,0
text2	dc.b	'Temporary files stored in: '
default_path
	dc.b	'A:\',0
	ds.b	128
text3	dc.b	cr,lf,tab,esc,'pLPAK Installed!',esc,'q',cr,lf,lf,0
errtext	dc.b	cr,lf,'Unable to locate specified path',cr,lf
	dc.b	tab,esc,'pLPAK Not Installed!',esc,'q',cr,lf,lf,0

inf_file
	dc.b	'lpak.inf',0
	even

	section	bss
temp	ds.b	130
	end