/* Modified version of Nico's ppLoadData */

#include <pragmas.h>
#include <libraries/dosextens.h>
#include <exec/memory.h>

#define SAFETY	8	/* Safety decrunch margin */

extern int pp_DecrunchBuffer(UBYTE *,UBYTE *,int *);
extern UBYTE *fragallocabs(int,int,UBYTE *);
extern void fragfree(UBYTE *,int,int);

#asm
;
; What follows is Nico's own decrunch routine (v2.0). I couldn't get the
; in-library version to work. How about fixing the .fd file, Nico?!
;
; The code has undergone some optimization and application-speciffic
; modification by myself. Nothing too radical, but should do better than
; Nico's own code in most cases. Considered doing a Forbid() around tight
; loops, but dropped the idea (too hostile towards the rest of the system).
;

_pp_DecrunchBuffer:
		movem.l	d2-d7/a2-a3/a5,-(sp)	; /mich: Only preserve
		move.l	36+4(a7),a0		;        relevant registers
		move.l	36+8(a7),a3
		move.l	36+12(a7),a5
;					' /mich: dropped 'move.l 36+16(a7),a6'
		bsr.s	Decrunch
		movem.l	(sp)+,d2-d7/a2-a3/a5
		rts

; a3 -> file, a0 -> longword after crunched, a5 -> ptr to eff.
; destroys a0-a6 and d0-d7 (/mich: no longer entirely true)
Decrunch:	moveq	#3,d6
		moveq	#7,d7
		moveq	#1,d5
		move.l	d5,d4		; /mich: Constant '#1' in d4
		move.l	a3,a2		; remember start of file
		move.l	-(a0),d1	; get file length and empty bits
		tst.b	d1
		beq.s	NoEmptyBits
		bsr.s	ReadBit		; this will always get the next long (D5 = 1)
		sub.b	d4,d1		; /mich: Use constant '#1'
		lsr.l	d1,d5		; get rid of empty bits
NoEmptyBits:	lsr.l	#8,d1
		add.l	d1,a3		; a3 = endfile
CheckCrunch:	bsr.s	ReadBit		; check if crunch or normal
		bcs.s	CrunchedBytes
NormalBytes:	moveq	#0,d2
Read2BitsRow:	move.l	d4,d0		; /mich: Use constant '#1'
		bsr.s	ReadD1
		add.w	d1,d2
		cmp.w	d6,d1
		beq.s	Read2BitsRow
ReadNormalByte:	moveq	#7,d0
		bsr.s	ReadD1
		move.b	d1,-(a3)
		dbf	d2,ReadNormalByte
		cmp.l	a3,a2
		bcs.s	CrunchedBytes
		rts

ReadBit:	lsr.l	d4,d5		; this will also set X if d5 becomes zero
		beq.s	GetNextLong	; /mich: Use constant '#1' (previous line)
		rts

GetNextLong:	move.l	-(a0),d5
		roxr.l	d4,d5		; X-bit set by lsr above
		rts			; /mich: Use constant '#1' (previous line)

ReadD1sub:	sub.w	d4,d0		; /mich: Use constant '#1'
ReadD1:		moveq	#0,d1
ReadBits:	lsr.l	d4,d5		; this will also set X if d5 becomes zero
		beq.s	GetNext		; /mich: Use constant '#1' (previous line)
RotX:		roxl.l	d4,d1		; /mich: Use constant '#1'
		dbf	d0,ReadBits
		rts

GetNext:	move.l	-(a0),d5
		roxr.l	d4,d5		; X-bit set by lsr above
		bra.s	RotX		; /mich: Use constant '#1' (prev line)

CrunchedBytes:	move.l	d4,d0		; /mich: Use constant '#1'
		bsr.s	ReadD1		; read code
		moveq	#0,d0
		move.b	0(a5,d1.w),d0	; get number of bits of offset
		move.w	d1,d2		; d2 = code = length-2
		cmp.w	d6,d2		; if d2 = 3 check offset bit and read length
		bne.s	ReadOffset
		bsr.s	ReadBit		; read offset bit (long/short)
		bcs.s	LongBlkOffs
		moveq	#7,d0
LongBlkOffs:	bsr.s	ReadD1sub
		move.w	d1,d3		; d3 = offset
Read3BitsRow:	moveq	#2,d0
		bsr.s	ReadD1
		add.w	d1,d2		; d2 = length-1
		cmp.w	d7,d1		; cmp with #7
		beq.s	Read3BitsRow
		bra.s	DecrunchBlock

ReadOffset:	bsr.s	ReadD1sub	; read offset
		move.w	d1,d3		; d3 = offset
DecrunchBlock:	add.w	d4,d2		; /mich: Use constant '#1'
1$		move.b	0(a3,d3.w),-(a3)
		dbf	d2,1$
EndOfLoop:				; /mich: dropped 'move.w a3,(a6)'
		cmp.l	a3,a2
		bcs	CheckCrunch
		rts
#endasm

myPPLoadData(register BPTR filehandle,register UBYTE **mem,register *size)
{
	int efficiency,decrunched;
	register crunlen;
	register UBYTE *memgot;
	register returnval = 1;

	Read(filehandle,(char *)&efficiency,sizeof(int));
	Seek(filehandle,-sizeof(int),OFFSET_END);
	Read(filehandle,(char *)&decrunched,sizeof(int));

	if (memgot = AllocMem((decrunched >>= 8) + SAFETY,MEMF_PUBLIC))
	{
		UBYTE *filebody;

		Forbid();
		FreeMem(memgot,decrunched + SAFETY);
		filebody = fragallocabs(decrunched,10240,memgot+SAFETY);
		memgot   = AllocAbs(SAFETY,memgot);
		Permit();

		crunlen = Seek(filehandle,8,OFFSET_BEGINNING) - 8;

		if (Read(filehandle,(char *)memgot,crunlen) == crunlen)
		{
			pp_DecrunchBuffer(memgot+crunlen,memgot+SAFETY,&efficiency);

			FreeMem(memgot,SAFETY);

			*mem  = filebody;
			*size = decrunched;

			returnval = 0;
		}
		else
		{
			FreeMem(memgot,SAFETY);
			fragfree(filebody,decrunched,10240);
		}
	}

	return(returnval);
}
