
                * Imploder Style Decrunch by Cath of LSD *
                * Taken from the depths of IEEE magazine *

                  * SProModule was extended from here ! *

* Tester!

	ifne	0
	lea	Data(pc),a0
	lea	buffer(pc),a1
	bsr	IMPL_Decr
	clr.l	d0
	rts

	endc

ID	equ	'IMP!'		; ID suggested by ieee and used by a cruncher?

       * IMP! Scrunch Pro (SPMO) Module Decruncher by Cath of LsD *


	STRUCTURE SProModule,0
	ULONG	sm_CrunchedAddr		; The address of the crunched data
	ULONG	sm_CrunchedSize		; The size of the crunched data
	ULONG	sm_DecrAddr	; This is used by the module to pass back data
	ULONG	sm_DecrSize	; This is used by the module to pass back data
	ULONG	sm_Reserved1	; Guess (For future use, currently set to zero)
	ULONG	sm_Reserved2	; Guess (For future use, currently set to zero)
	ULONG	sm_Informer	; This is set to the addrress to shove data to
				; ie _custom.color00

	LABEL	spro_SIZEOF

;	Anything beyond this is currently private!

;	............X 		; Private data


SM_NOMEMORY	equ	$DEADFFF0	; no memory for crunch/decrunch
SM_UNRECOGNISED	equ	$DEADFFF1	; not a recognised buffer
SM_UNKNOWN	equ	$DEADFFF2	; unknown error
SM_NODECRUNCH	equ	$DEADFFF3	; this module is crunch only
SM_NOCRUNCH	equ	$DEADFFF4	; this module is decrunch only
SM_COULDNTCRUNCH equ	$DEADFFF5	; actual cruncher couldn't decrunch ?
SM_COULDNTDECR equ	$DEADFFF6	; actual decruncher couldn't crunch

	dc.b	'SPMO'	; The third sprmo decr module

Start
	tst.l	sm_DecrAddr(a0)			; Is this a DeCrunch ?
	beq.s	DeCrunch			; Yes

	; This is no cruncher, baby!

NoCruncher
	move.l	#-1,sm_DecrAddr(a0)	; Tell scrunch about the error
	move.l	#SM_NOCRUNCH,sm_DecrSize(a0)	; No crunch possible
	rts

DeCrunch
	move.l	(a0),a1			; Get address of crunched data
	cmp.l	#ID,(a1)		; Is this valid data
	beq.s	YesValidData		; Yes its Ok

	move.l	#-1,sm_DecrAddr(a0)	; Tell scrunch about error
	move.l	#SM_UNRECOGNISED,sm_DecrSize(a0)	; Error code
	rts
		
YesValidData

	move.l	4(a1),sm_DecrSize(a0)	; Get size of decrunched data
	move.l	4.w,a6
	movem.l	a0/a1,-(sp)
	move.l	4(a1),d0		; Get size of decrunched data
	move.l	#MEMF_PUBLIC|MEMF_CLEAR,d1;  Mem flags
	jsr	_LVOAllocMem(a6)	; Allocate mem
	movem.l (sp)+,a0/a1		; Restore regs
	tst.l	d0
	bne.s	MemOk			; Allocated Ok,

	; Not enough memory for decrunch !

	move.l	#-1,sm_DecrAddr(a0)
	move.l	#SM_NOMEMORY,sm_DecrSize(a0)
	rts

MemOk
	move.l	d0,sm_DecrAddr(a0)	; Set address of decrunched data
					; sm_DecrSize has already been set

	movem.l	a0-a6/d1-d7,-(sp)	; Save all regs!

	move.l	sm_DecrAddr(a0),a1	; Set decrunch address
	move.l	sm_CrunchedAddr(a0),a0	; Set crunched address (doesn't need offset)
					; no point removing as its negligble)

* This decruncher requires the crunched buffer to be in the deCrunch buffer
* so we need to move the original crunched data

* +---------------------------------Decrunch Mem area -------------+
* +-----------Crunched Data ------+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+
*                                  ^ filled in by decruncher

	movem.l	a0-a6/d0-d7,-(sp)

	move.l	8(a0),d0		; Get size of crunched data
	add.l	#80,d0			; And a bit more for luck (to cover table)
	move.l	4.w,a6			; Execbase
	jsr	_LVOCopyMemQuick(a6)	; Copymem

	movem.l	(sp)+,a0-a6/d0-d7

	move.l	a1,a0			; Set both addresses to same area

	bsr.s	IMPL_Decr		; Decrunch the Data
	movem.l	(sp)+,a0-a6/d1-d7	; Ok

	beq.s	DecrError		; There was an error!

	rts				; Return to Scrunch Ok!

DecrError
	move.l	sm_DecrAddr(a0),a1	; An error in the decrunch
	move.l	sm_DecrSize(a0),d0
	move.l	4.w,a6
	move.l	a0,-(sp)
	jsr	_LVOFreeMem(a6)		; So free the memory
	move.l	(sp)+,a0

	move.l	#-1,sm_DecrAddr(a0)
	move.l	#SM_COULDNTDECR,sm_DecrSize(a0)	; Tell scrunch about the error
	rts


	; this is the actual decruncher

Leave
	moveq	#0,d0			; setup bad ret code
	rts

IMPL_Decr *******************************
* A0-ptr to buffer,A0 ptr to out buffer *
*****************************************

* Taken from the depths of IEEE magazine
* This is basically the same routine as imploder uses (its where it gets its name from, the
* program not the algorithm and its used widely). Its based on LZW compression
* this is the basis on which AT&T and BT based their own compression systems on
* other programs that use it are lha and pkware

Decompress
	cmp.l	#ID,(a0)		; Check imploder header string
					; this is the ISO/IEEE string used
	bne.s	Leave

	movem.l	d2-d5/a2-a5,-(sp)	; save blasted regs

	move.l	a0,a3			; get copy of buffer ptr
	move.l	a0,a5			; get copy of destination buffer ptr

	tst.l	(a0)+			; Skip IMP! ID

	add.l	(a0)+,a5		; Get decrunched length

	add.l	(a0)+,a3		; Get crunched length

	move.l	a3,a2			; A2->End of crunched buffer
					; A5->End of Complete buffer

	move.l	(a2)+,-(a0)		; Get data table
	move.l	(a2)+,-(a0)
	move.l	(a2)+,-(a0)
	move.l	(a2)+,d2
	move.w	(a2)+,d3

	bmi.s	DontFix

	subq.l	#1,a3			; Ptr to Buffer End - 1 

DontFix

	lea	-$1C(sp),sp		; Get some stack space

	move.l	sp,a1			; point to it

	moveq	#6,d0			; rest of table
MoveTable
	move.l	(a2)+,(a1)+
	dbra	d0,MoveTable

	move.l	sp,a1			; point to source again

StartDecr
	tst.l	d2			; Anything to store ?
	beq.s	Nope			; No

CopyLoop
	move.b	-(a3),-(a5)		; Store decrunched data
	subq.l	#1,d2			; Decrement byte count
	bne.s	CopyLoop		; Any more to do 

Nope
	cmp.l	a5,a0			; Have we reached begininng of buffer
	bcs.s	DoDecrunch		; Still to decrunch


	lea	$1C(sp),sp		; free stack space

	moveq	#-1,d0			; Set return code as good

	cmp.l	a3,a0			; Do the end addresses match? 
	beq.s	MatchOk

	moveq	#0,d0			; Set return code as bad

MatchOk
	movem.l	(sp)+,d2-d5/a2-a5
	tst.l	d0			; Save returned prog checking
	rts				; Return code


DoDecrunch
	add.b	d3,d3			; doubler
	bne.s	DOA
	move.b	-(a3),d3
	addx.b	d3,d3

DOA
	bcc.s	SetupDecodeTable_4
	add.b	d3,d3
	bne.s	Extract_1
	move.b	-(a3),d3
	addx.b	d3,d3

Extract_1
	bcc.s	SetupDecodeTable_3
	add.b	d3,d3
	bne.s	Extract_2
	move.b	-(a3),d3
	addx.b	d3,d3

Extract_2
	bcc.s	SetupDecodeTable_2
	add.b	d3,d3
	bne.s	Extract_3
	move.b	-(a3),d3
	addx.b	d3,d3

Extract_3
	bcc.s	SetupDecodeTable_1
	moveq	#0,d4
	add.b	d3,d3
	bne.s	Extract_4
	move.b	-(a3),d3
	addx.b	d3,d3

Extract_4
	bcc.s	Extract_5
	move.b	-(a3),d4
	moveq	#3,d0
	subq.b	#1,d4
	bra.s	DecodeTable_1

Extract_5
	add.b	d3,d3
	bne.s	Extract_6
	move.b	-(a3),d3
	addx.b	d3,d3

Extract_6
	addx.b	d4,d4
	add.b	d3,d3
	bne.s	Extract_7
	move.b	-(a3),d3
	addx.b	d3,d3

Extract_7
	addx.b	d4,d4
	add.b	d3,d3
	bne.s	Extract_8
	move.b	-(a3),d3
	addx.b	d3,d3

Extract_8
	addx.b	d4,d4
	addq.b	#5,d4
	moveq	#3,d0
	bra.s	DecodeTable_1

SetupDecodeTable_1
	moveq	#4,d4
	moveq	#3,d0
	bra.s	DecodeTable_1

SetupDecodeTable_2
	moveq	#3,d4
	moveq	#2,d0
	bra.s	DecodeTable_1

SetupDecodeTable_3
	moveq	#2,d4
	moveq	#1,d0
	bra.s	DecodeTable_1

SetupDecodeTable_4
	moveq	#1,d4
	moveq	#0,d0

DecodeTable_1
	moveq	#0,d5
	move.w	d0,d1
	add.b	d3,d3
	bne.s	NoFetch1
	move.b	-(a3),d3
	addx.b	d3,d3

NoFetch1
	bcc.s	DoTable
	add.b	d3,d3
	bne.s	NoFetch2
	move.b	-(a3),d3
	addx.b	d3,d3

NoFetch2
	bcc.s	NoEntry
	move.b	ImpTable(pc,d0.w),d5
	addq.b	#8,d0
	bra.s	DoTable

NoEntry
	moveq	#2,d5				; Set default
	addq.b	#4,d0

DoTable
	move.b	RunTable(pc,d0.w),d0		; ptr to run table

_DoTable
	add.b	d3,d3
	bne.s	NoFetch3
	move.b	-(a3),d3
	addx.b	d3,d3

NoFetch3
	addx.w	d2,d2
	subq.b	#1,d0
	bne.s	_DoTable
	add.w	d5,d2
	moveq	#0,d5
	move.l	d5,a2
	move.w	d1,d0
	add.b	d3,d3
	bne.s	NoFetch4
	move.b	-(a3),d3
	addx.b	d3,d3

NoFetch4
	bcc.s	NoFetch5
	add.w	d1,d1
	add.b	d3,d3
	bne.s	NoFetch5a
	move.b	-(a3),d3
	addx.b	d3,d3

NoFetch5a
	bcc.s	DoAgain

	move.w	8(a1,d1.w),a2
	addq.b	#8,d0
	bra.s	NoFetch5

DoAgain
	move.w	(a1,d1.w),a2
	addq.b	#4,d0

NoFetch5
	move.b	16(a1,d0.w),d0

DoBytes
	add.b	d3,d3
	bne.s	NoFetch7
	move.b	-(a3),d3
	addx.b	d3,d3

NoFetch7
	addx.l	d5,d5
	subq.b	#1,d0
	bne.s	DoBytes

	addq.w	#1,a2
	add.l	d5,a2
	add.l	a5,a2

CopyLoop2
	move.b	-(a2),-(a5)			; Store bytes
	dbra	d4,CopyLoop2

	bra	StartDecr			; Continue, and check for end


* --------------- Data Area ------------- *

ImpTable
	dc.b	6,10,10,18

RunTable
	dc.b	1,1,1,1,2,3,3,4,4,5,7,14	; Length of runs

	ifne	0

Data
	incbin	spmo:test.impl
buffer
	dcb.b	500000,0
	endc

	END
