	INCLUDE	AINCLUDE:IncDirs.i *sets all includedirs, needed for my ASM
	INCLUDE	HUFF/xpkLibHUFF.i
	include	"xpk/xpk.i"
	include	"xpk/xpksub.i"
	INCLUDE exec/memory.i

;                             License/Disclaimer
;                             ------------------
;
;   This  source  may be freely distributed with the XPK compression package,
;as  long  as  it is kept in its original, complete, and unmodified form.  It
;may  not  be  distributed  by  itself or in a commercial package of any kind
;without my permission.
;
;   This  source  is  distributed  in  the  hope  that it will be useful, but
;WITHOUT  ANY  WARRANTY; without even the implied warranty of MERCHANTABILITY
;or FITNESS FOR A PARTICULAR PURPOSE.
;
; M.Zimmermann (zimmerma@helios.ibr.cs.tu-bs.de)

DecrunchCode	equ	1	;0: first code
				;1: byte oriented, chache optimized
				;   decrunching
				;2: word oriented decrunching (probably best
				;   on 68000)
				;3: 68030+ cache oriented decrunching
				;4: long oriented decrunching

;CrunchMethodIdentifier:

CMI_NORMAL	equ	0	;maybe there will be other crunch
				;methods/formats l8er

;History:
;
;	V 0.1  - 12-Jul-1992 :	first version
;	V x.yy - 18-Jul-1992 :	first OK version
;	V x.yy - 19-Jul-1992 :	sped up decrunching
;	V x.yy - 21-Jul-1992 :	bug fixed in word/long decrunching: min pack
;                               chunk size now 3/5
;	V x.yy - 21-Jul-1992 :	replaced many subq/bxx with dbf (ie sped up
;                               crunching a little bit), bug fixed: there was
;                               a dbf counter wrong (one of my favorite 0/1
;                               problem bugs)
;	V 0.50 - 29-Jul-1992 :	added 68030+ cache optimized decrunch code
;	V 0.51 - 01-Aug-1992 :	byte decrunch improved, first code added,
;				indicator byte for crunchmethod used added,
;				68030+ chache optimized code does not make
;				sense any more, since byte decrunch fits to
;				cache completely, now
;	V 0.52 - 01-Aug-1992 :	unsafe encryption supported
;	V 0.53 - 03-Aug-1992 :	slight improvements made to crunch code
;				(+ 6K/s)
;	V 0.54 - 03-Aug-1992 :	inconsistence in expansion handling fixed
;	V 0.55 - 03-Aug-1992 :	bug fixed: expansion handling now considers
;				table creation, too
;	V 0.56 - 03-Aug-1992 :	bug fixed: HUFF now can crunch files
;				consisting of always the same byte (shame
;				on me, termination criterium was wrong)
;	V 0.57 - 03-Aug-1992 :	Tree creation code partially rewritten
;	V 0.58 - 05-Aug-1992 :	bug fixed: wrong termination criterium for
;				expansion check (my favorite 0/1 problem)
;	V 0.59 - 06-Aug-1992 :	now decrypting in a special buffer, not using
;				InBuf (this is read only, I was told) any more
;	V 0.60 - 07-Aug-1992 :	added extra memory required during
;				packing/unpacking
;	V 0.61 - 08-Aug-1992 :	expansion check changed, renamed from
;				xpkDHUF.library to xpkHUFF.library thus
;				corresponding to the possibility of handling
;				adaptive huffman codes later without having
;				an inconsistence in the name
;	V 0.62 - 10-Aug-1992 :	Flag XPKIF_MODES removed (I don't have modes
;				yet (but I have a mapping code :-=))

;*********************************
;*** xpk specific declarations ***
;*********************************

MaxPackChunkSize	EQU	65534	;Max input chunk size for processing

	IFEQ	DecrunchCode-0
MinPackChunkSize		equ	1	;Min input chunk size for
	ENDC					;processing
	IFEQ	DecrunchCode-1
MinPackChunkSize		equ	1	;Min input chunk size for
	ENDC					;processing
	IFEQ	DecrunchCode-2
MinPackChunkSize		equ	3	;Min input chunk size for
	ENDC					;processing
	IFEQ	DecrunchCode-3
MinPackChunkSize		equ	5	;Min input chunk size for
	ENDC					;processing
	IFEQ	DecrunchCode-4
MinPackChunkSize		equ	5	;Min input chunk size for
	ENDC					;processing

DefPackChunkSize		equ	MaxPackChunkSize
;Default processing input size (must be lower tahn or equal to MaxPackChunkSize)

DefMode				equ	50
;Default mode/efficiency, ranges from [0..100]

;********************************
;*** xpk sublibrary functions ***
;********************************

_XpksPackerInfo:
	LEA	HUFFXpkInfo,A0
	MOVE.L	A0,D0
	RTS

;**********************
;*** un-/pack modes ***
;**********************

;mode 0 performs: dynamic huffman crunching

_XpksPackChunk

;in:
;
;	a0 :	*XpkSubParams
;	a6 :	*xpkHUFF.library
;out:
;	d0 :	err
;	in XpkSubParams :	xsp_OutLen
;
;registers trashed:

	MOVEM.L	D2-D7/A2-A6,-(A7)
	BSR.B	PackMode0
	MOVEM.L	(A7)+,D2-D7/A2-A6
	RTS

;******************************
;*** huffman node structure ***
;******************************

UNUSED		equ	0
NODEUSED	equ	1
NODEUNUSED	equ	2
LEAFLINKED	equ	3 ;leaf already is linked somewhere in the tree
LEAFUNLINKED	equ	4 ;leaf is not yet linked to any node in the tree
ROOTNODE	equ	5
LEFTSUBTREE	equ	0 ;indicates that this node is at the left  branch of it's parent
RIGHTSUBTREE	equ	1 ;indicates that this node is at the right branch of it's parent
NODIRECTION	equ	-1 ;for root node only
NOLISTLINKYET	equ	-1

HuffmanNodeStructure	RSRESET
xpkHUFF_LinkToNextNodeOrLeaf	RS.L	1	;* for fast tree creation
xpkHUFF_Sum			RS.W	1	;sum of left and right subtree sums
xpkHUFF_Char			RS.B	1	;byte # (0..255) if leaf, invalid else
xpkHUFF_Type			RS.B	1	;UNUSED|NODEUSED|NODEUNUSED|
					;LEAFUSED|LEAFUNUSED
xpkHUFF_Parent			RS.L	1	;*parent node or 0
xpkHUFF_Left			RS.L	1	;*left subtree
xpkHUFF_Right:			rs.l	1	;*right subtree
xpkHUFF_Direction:		rs.w	1	;direction which to take from
						;parent to this node
xpkHUFF_pad1:			rs.w	1
xpkHUFF_pad2:			rs.l	1
xpkHUFF_pad3:			rs.l	1
xpkHUFF_HuffmanNodeStruct_SIZEOF:	rs.b	0	;size: 8 longs ==>
							;shifing for size
							;correction possible

;*************************
;*** reentry structure ***
;*************************

MAXHUFFMANCODELENGTH				equ	256/8

CrunchReentryStructure:		rsreset
xpkHUFF_CRS_xpkSubParams:			rs.l	1		;*SubParamsStructure
xpkHUFF_CRS_xpkSubLibBase:			rs.l	1		;the pointer to my sublibrary
xpkHUFF_CRS_StatisticArray:			rs.b	256*2		;Word_SIZEOF: max InputBuffer allowed is 64K!
xpkHUFF_CRS_InBuf:				rs.l	1		;*input buffer
xpkHUFF_CRS_InLen:				rs.l	1		;size of input buffer in bytes
xpkHUFF_CRS_OutBuf:				rs.l	1		;*output buffer
xpkHUFF_CRS_OutLen:				rs.l	1		;will be set to output buffer size after crunching
xpkHUFF_CRS_OutBufLen:				rs.l	1		;for overflow check when crunching (recognize expansion)
xpkHUFF_CRS_CrunchedLength:			rs.l	1
xpkHUFF_CRS_DynamicHuffmanTreeLeafs:		rs.b	256*xpkHUFF_HuffmanNodeStruct_SIZEOF
xpkHUFF_CRS_DynamicHuffmanTerminatorLeaf:	rs.b	xpkHUFF_HuffmanNodeStruct_SIZEOF	;MUST stay uninitialized for a terminating condition below
xpkHUFF_CRS_DynamicHuffmanTreeNodes:		rs.b	255*xpkHUFF_HuffmanNodeStruct_SIZEOF
xpkHUFF_CRS_RootNode:				rs.l	1
xpkHUFF_CRS_PtrTableToSizesAndCodes:		rs.l	256*4
xpkHUFF_CRS_SpaceForLengthsAndCodes:		rs.b	256*MAXHUFFMANCODELENGTH+256	;size will be stored in bytes here
xpkHUFF_CRS_SIZEOF:				rs.b	0

;************
;*** main ***
;************


PackMode0:
	move.l	a0,a2		;keep subparams in mind
	move.l	a6,a3		;keep xpkHUFF base in mind
	move.l	#xpkHUFF_CRS_SIZEOF,d0
	move.l	#MEMF_CLEAR|MEMF_PUBLIC,d1
	move.l	4.w,a6
	jsr	_LVOAllocMem(a6)
	tst.l	d0
	beq	xpkHUFF_AllocReentryBufferFailed
	move.l	d0,a5		;reentry buffer in a5, now
	move.l	a3,xpkHUFF_CRS_xpkSubLibBase(a5)	;store xpkHUFF base
				;in the reentry structure
	move.l	a2,xpkHUFF_CRS_xpkSubParams(a5)		;store xpksubparams
				;in reentry structure

;********************************************
;*** get data from xpksubparams structure ***
;********************************************

	move.l	xsp_InBuf(a2),xpkHUFF_CRS_InBuf(a5)
	move.l	xsp_InLen(a2),xpkHUFF_CRS_InLen(a5)

	move.l	xsp_OutBuf(a2),xpkHUFF_CRS_OutBuf(a5)
	move.l	xsp_OutBufLen(a2),xpkHUFF_CRS_OutBufLen(a5)


;*******************************
;*** create statistics table ***
;*******************************

xpkHUFF_StatisticLoopInit:

	move.l	xpkHUFF_CRS_InBuf(a5),a0
	move.l	xpkHUFF_CRS_InLen(a5),d0		;InLen <= MaxPackChunkSize!
	subq.l	#1,d0					;dbf
	lea	xpkHUFF_CRS_StatisticArray(a5),a1

xpkHUFF_StatisticLoop:

	moveq	#0,d1
	move.b	(a0)+,d1
	add.w	d1,d1					;byte offset
							;-> word offset
	addq.w	#1,0(a1,d1.w)

	dbf	d0,xpkHUFF_StatisticLoop


;***************************************
;*** init leafs in reentry structure ***
;***************************************

xpkHUFF_InitLeafs:

	lea	xpkHUFF_CRS_StatisticArray(a5),a0
	lea	xpkHUFF_CRS_DynamicHuffmanTreeLeafs(a5),a1

	moveq	#-1,d0					;current byte #
	moveq	#LEAFUNLINKED,d1
	moveq	#-2,d2					;current byte's
							;word offset

	move.w	#255,d3					;256 runs (bytes range
							;from 0..255)

	moveq	#NOLISTLINKYET,d4

xpkHUFF_InitLeafsLoop:

	addq.l	#1,d0					;next byte number
	addq.l	#2,d2					;next word offset

	move.w	0(a0,d2.w),d5
	beq.s	xpkHUFF_ILL_DontConsiderEmptyLeaf

	move.b	d0,xpkHUFF_Char(a1)			;current byte #

	move.b	d1,xpkHUFF_Type(a1)			;type := LEAFUNLINKED

	move.w	d5,xpkHUFF_Sum(a1)

	move.l	d4,xpkHUFF_LinkToNextNodeOrLeaf(a1)

	lea	xpkHUFF_HuffmanNodeStruct_SIZEOF(a1),a1	;proceed to next leaf

xpkHUFF_ILL_DontConsiderEmptyLeaf:

	dbf	d3,xpkHUFF_InitLeafsLoop


;***********************************
;*** link leafs from low to high ***
;***********************************

FindHighestSum	MACRO

;finds  highest  sum of all currently unlinked nodes and returns a pointer to
;that node in a2

;
;registers trashed: a0, d1, a2


xpkHUFF_LL_FindHighestSum:

	lea	xpkHUFF_CRS_DynamicHuffmanTreeLeafs(a5),a0

	moveq	#0,d1					;initial sum
	sub.l	a2,a2					;currently no node
							;with highest sum
xpkHUFF_LL_FindHighestSumLoop:

	tst.b	xpkHUFF_Type(a0)			;= cmp.w #UNUSED,
							;pkHUFF_Type, ie did
	beq.s	xpkHUFF_LL_FoundHighestSum		; we reach the end of
							;the list?

	cmp.l	xpkHUFF_LinkToNextNodeOrLeaf(a0),d3	;already considered?
	bne.s	xpkHUFF_LL_AlreadyConsidered

	cmp.w	xpkHUFF_Sum(a0),d1
	bhi.s	xpkHUFF_LL_NoHigherSum

	move.w	xpkHUFF_Sum(a0),d1			;current sum
	move.l	a0,a2					;remember this adr


xpkHUFF_LL_AlreadyConsidered:
xpkHUFF_LL_NoHigherSum:

	lea	xpkHUFF_HuffmanNodeStruct_SIZEOF(a0),a0
	bra.s	xpkHUFF_LL_FindHighestSumLoop

xpkHUFF_LL_FoundHighestSum:


		ENDM

;register usage:
;
;	a1:	*last node

xpkHUFF_LinkLeafs:

	moveq	#0,d2					;for a fast cmp below
	sub.l	a1,a1					;keep 'last' node in
							;mind (in this case no
							;last node)
	moveq	#NOLISTLINKYET,d3			;for cmp below

xpkHUFF_LinkLeafsLoop:

	FindHighestSum

;here a2 contains the adr of the node with the highest sum or 0, if all nodes
;are linked already

	cmp.l	d2,a2					;no highest sum any
							;more?
	beq.s	xpkHUFF_LeavesLinked			;nope: ==> done

	move.l	a1,xpkHUFF_LinkToNextNodeOrLeaf(a2)
	move.l	a2,a1

	bra.s	xpkHUFF_LinkLeafsLoop

xpkHUFF_LeavesLinked:

	move.l	a1,a4


;here a4 contains the currently first node

;*********************
;*** CreateNewNode ***
;*********************

CreateNewNode	MACRO

;in:	a0: *A
;	a1: *N

	move.l	a1,a2
	move.l	xpkHUFF_LinkToNextNodeOrLeaf(a0),a1


;a0: *A
;a1: *B
;a2: *N

	move.w	#LEFTSUBTREE,xpkHUFF_Direction(a0)
	move.w	#RIGHTSUBTREE,xpkHUFF_Direction(a1)

	move.w	xpkHUFF_Sum(a0),d0
	add.w	xpkHUFF_Sum(a1),d0

	move.w	d0,xpkHUFF_Sum(a2)

	move.b	#NODEUSED,xpkHUFF_Type(a2)
;	move.b	#LEAFLINKED,xpkHUFF_Type(a0)		;not necessary, therefore not assembled
;	move.b	#LEAFLINKED,xpkHUFF_Type(a1)		;not necessary, therefore not assembled

	move.l	a0,xpkHUFF_Left(a2)
	move.l	a1,xpkHUFF_Right(a2)

	move.l	a2,xpkHUFF_Parent(a0)
	move.l	a2,xpkHUFF_Parent(a1)

	move.l	xpkHUFF_LinkToNextNodeOrLeaf(a1),a0
	lea	xpkHUFF_HuffmanNodeStruct_SIZEOF(a2),a1


;a0: *C
;a1: *O
;a2: *N

		ENDM

;***************************
;*** insert node to list ***
;***************************


InsertTreeNodeToList	MACRO

;in:	a0: *first node in list
;	a2: *node to insert in list

;a0: *first node in list (*C)
;a1: MUST BE LEFT UNTOUCHED
;a2: *node to insert (*N)


;have the last two nodes been linked to a lonely (ie in this case root) node?
;if so, we are ready


	cmp.l	a0,d7			;d7 = 0
	bne.s	HSH1L_a2IsNoLonelyNode

	move.l	a2,a0
	bra.s	xpkHUFF_InsertionComplete


HSH1L_a2IsNoLonelyNode:

	move.w	xpkHUFF_Sum(a2),d0

;_____________

	cmp.w	xpkHUFF_Sum(a0),d0
	bhi.s	xpkHUFF_DontInsertAsHeadOfList

xpkHUFF_InsertAsHeadOfList:

;case: 3 5 7 ... insert 2 before 3

	move.l	a0,xpkHUFF_LinkToNextNodeOrLeaf(a2)
	move.l	a2,a0
	bra.s	xpkHUFF_InsertionComplete

;_____________


xpkHUFF_DontInsertAsHeadOfList:


	move.l	a0,a3					;*first node in list


xpkHUFF_ProceedToNextNode:

	cmp.l	xpkHUFF_LinkToNextNodeOrLeaf(a3),d7
	beq.s	xpkHUFF_InsertAtEndOfList

	move.l	a3,a4
	move.l	xpkHUFF_LinkToNextNodeOrLeaf(a3),a3

	cmp.w	xpkHUFF_Sum(a3),d0
	bhi.s	xpkHUFF_ProceedToNextNode


;_____________


xpkHUFF_InsertInMiddleOfList:

;case: 3 5 ... insert 4 between 3 and 5

	move.l	a2,xpkHUFF_LinkToNextNodeOrLeaf(a4)

	move.l	a3,xpkHUFF_LinkToNextNodeOrLeaf(a2)
	bra.s	xpkHUFF_InsertionComplete

;_____________


xpkHUFF_InsertAtEndOfList:

;in: a3: *last node in list

;case: 3 5 7   insert 8 behind 7

	move.l	a2,xpkHUFF_LinkToNextNodeOrLeaf(a3)

	move.l	d7,xpkHUFF_LinkToNextNodeOrLeaf(a2)
;	bra.s	xpkHUFF_InsertionComplete

;_____________


xpkHUFF_InsertionComplete:

;a0: *first node in list

			ENDM

;*******************
;*** create tree ***
;*******************

;Here  we have all leafs lying next to each other, in order 0..255.  They are
;linked  via  a  single pointer (xpkHUFF_LinkToNextNodeOrLeaf).  If you start
;with  a4  here and proceed using the link pointers, you will find all leaves
;in ascending order (order: low to high by xpkHUFF_Sum).

;
;suggest we have the nodes
;
;  A  B  C  D  E  F   G  H . . . N O P
;  3  5  6  7  8  9  10 11       new (currently unused) nodes
;

;Now  we  have  to  create  a  new  node  N  with  A as xpkHUFF_Left and B as
;xpkHUFF_Right.   This new node N has to be sorted into he list (ie between D
;and  E).  The nodes A and B are excluded from the list when creating the new
;node N.  This will go on until there are only two nodes left.  These will be
;linked by the root node of the huffman tree.

;register usage:

;	a0: *current root of used nodes  (at the beginning *A)
;	a1: *currently first unused node (at the beginning *N)

	move.l	a4,a0						;a0: *A
	lea	xpkHUFF_CRS_DynamicHuffmanTreeNodes(a5),a1	;a1: *N
	moveq	#0,d7


	cmp.l	xpkHUFF_LinkToNextNodeOrLeaf(a0),d7		;is there only
								;one node?

	bne.s	xpkHUFF_CreateTreeLoop				;nope: construct
								;      tree

xpkHUFF_ThereIsOnlyOneNode:					;create tree
								;containing
								;only one leaf
;create new root node at (a1)

	move.w	#LEFTSUBTREE,xpkHUFF_Direction(a0)
;	move.w	xpkHUFF_Sum(a0),xpkHUFF_Sum(a1)

	move.l	a0,xpkHUFF_Left(a1)
	move.l	a1,xpkHUFF_Parent(a0)

	move.l	a1,a2						;new root
								;in a2
	bra.s	xpkHUFF_TreeCreationComplete



xpkHUFF_CreateTreeLoop:


	cmp.l	xpkHUFF_LinkToNextNodeOrLeaf(a0),d7
	beq.s	xpkHUFF_TreeCreationComplete

	CreateNewNode			;creates new node from a0 (A) and the
					;sequel node of A (B) in node a1 (N).
					;increments a0 (will point to C
					;afterwards) and a1 (will point to O
					;afterwards).
					;returns adr of N in a2

	InsertTreeNodeToList		;insert the node we just created
					;(a2: N) into the list (a0: C)
					;positioned according to it's
					;xpkHUFF_Sum field.

	bra.s	xpkHUFF_CreateTreeLoop

xpkHUFF_TreeCreationComplete:

;a2: *root node of huffman tree

	move.b	#ROOTNODE,xpkHUFF_Type(a2)
	move.w	#NODIRECTION,xpkHUFF_Direction(a2)

	move.l	a2,xpkHUFF_CRS_RootNode(a5)

xpkHUFF_CreateTranslationTableSizesAndCodes:

;**********************************************************
;*** create translation table, sizes of codes and codes ***
;**********************************************************


	lea	xpkHUFF_CRS_DynamicHuffmanTreeLeafs(a5),a0

	lea	xpkHUFF_CRS_PtrTableToSizesAndCodes(a5),a3
	lea	xpkHUFF_CRS_SpaceForLengthsAndCodes(a5),a4


xpkHUFF_CreateTranslationTableSizesAndCodesLoop:

;____________________________


xpkHUFF_GetOneLeafsCode:

;get code of leaf a0
;write length as byte to (a4)+
;write bitcode of leaf pointed to by a0 to (a4)+ behind size

	move.b	xpkHUFF_Char(a0),d5			;keep char in mind

	moveq	#0,d6
	move.b	d5,d6
	add.w	d6,d6					;offset:
	add.w	d6,d6					;.b -> .l

	move.l	a4,0(a3,d6.w)				;put pointer to table


	moveq	#-1,d1					;bit counter (dbf)
	move.l	a0,a1

xpkHUFF_GetOneLeafsCodeLoop:

	moveq	#0,d0
	move.w	xpkHUFF_Direction(a1),d0		;root reached?
	bmi.s	xpkHUFF_PutOneLeafsCompleteCodeOnStack	;yep

	addq.l	#1,d1					;one more bit for code
	move.w	d0,-(a7)
	move.l	xpkHUFF_Parent(a1),a1			;go up

	bra.s	xpkHUFF_GetOneLeafsCodeLoop


xpkHUFF_PutOneLeafsCompleteCodeOnStack:

;here all bits are on the stack, d1 is a dbf counter for composition

	move.b	d1,(a4)+

xpkHUFF_ComposeHuffmanCode:

	moveq	#0,d2					;will contain
							;composed code
	moveq	#7,d3					;# of bits not yet
							;used in dest
							;register (dbf)

xpkHUFF_ComposeHuffmanCodeLoop:

	move.w	(a7)+,d0


	roxr.b	#1,d0					;code bit to x & c
							;flag
	addx.b	d2,d2					;code bit to composed
							;code register
	dbf	d3,xpkHUFF_CHCL_DontWriteCodeByte

	moveq	#7,d3					;reset counter
	move.b	d2,(a4)+				;write to buffer

xpkHUFF_CHCL_DontWriteCodeByte:

	dbf	d1,xpkHUFF_ComposeHuffmanCodeLoop

;write the last byte (this could contain 1 to 7 bits) if not already written

	cmp.w	#7,d3					;last byte already
							;written?
	beq.s	xpkHUFF_CHCL_DontWriteLastByte		;yep

	addq.w	#1,d3					;was dbf counter
	lsl.b	d3,d2					;shift bits to left
							;edge of byte
	move.b	d2,(a4)+				;write last byte
							;to buffer

xpkHUFF_CHCL_DontWriteLastByte:


;____________________________


	lea	xpkHUFF_HuffmanNodeStruct_SIZEOF(a0),a0	;proceed to next leaf

	tst.b	xpkHUFF_Type(a0)			;equ cmp.b #UNUSED,...
	bne.s	xpkHUFF_CreateTranslationTableSizesAndCodesLoop

;**************************************
;*** identifier & password handling ***
;**************************************

	cmp.l	#6,xpkHUFF_CRS_OutBufLen(a5)		;2: indicator word,
							;4: password checksum
	bls	xpkHUFF_C_Expansion


;*****************************
;*** write identifier word ***
;*****************************

	move.l	xpkHUFF_CRS_OutBuf(a5),a1
	move.w	#CMI_NORMAL,(a1)+			;word indicating
							;crunchmethod used

;____________________________


;*************************
;*** password handling ***
;*************************


	move.l	xpkHUFF_DRS_xpkSubParams(a5),a0

xpkHUFF_C_CalcPasswordCheckSum:

	move.l	#$ABADCAFE,d0

	move.l	xsp_Password(a0),d2
	beq.s	xpkHUFF_C_CPCS_NoPassword
	
	move.w	#$C0DE,d1
	move.l	d2,a2

xpkHUFF_C_CalcPasswordChecksumLoop1:

	move.b	(a2)+,d1
	beq.s	xpkHUFF_C_CalcPasswordChecksumLoop1End

	add.w	d1,d0
	bra.s	xpkHUFF_C_CalcPasswordChecksumLoop1

xpkHUFF_C_CalcPasswordChecksumLoop1End:

	move.l	xsp_Password(a0),a2
	swap	d0

xpkHUFF_C_CalcPasswordChecksumLoop2:

	move.b	(a2)+,d1
	beq.s	xpkHUFF_C_CalcPasswordChecksumLoop2End

	eor.w	d1,d0
	rol.w	#8,d0
	bra.s	xpkHUFF_C_CalcPasswordChecksumLoop2

xpkHUFF_C_CalcPasswordChecksumLoop2End:

xpkHUFF_C_CPCS_NoPassword:

	move.l	d0,(a1)+


;____________________________


;*******************************************************
;*** write translation sizes & codes to outputbuffer ***
;*******************************************************


;Format: size.b
;If  size  <>  0 (or, in this representation -1 (because of size beeing a dbf
;counter))  there  is  the  full  code  following,  byte aligned, thus saving
;diskspace.   If  size  = 0 (ie -1 (dbf, see above)) no code will follow, coz
;size = 0 indicates that this code won't be used.

xpkHUFF_WriteTranslationSizesAndCodesToOuputBuffer:


	lea	xpkHUFF_CRS_PtrTableToSizesAndCodes(a5),a0

	move.l	xpkHUFF_CRS_OutBuf(a5),a4
	add.l	xpkHUFF_CRS_InLen(a5),a4		;a4: *to check for
							;expansion
;	add.l	#XPKMARGIN,a4				;a cruncher that
							;expands data makes no
							;sense

	move.w	#255,d0					;dbf counter over
							;all table entries
	moveq	#-1,d3

xpkHUFF_WriteTranslationSizesAndCodesToOuputBufferLoop:

	move.l	(a0)+,d2				;d2: *size
	beq.s	xpkHUFF_WTSACTOB_DontWriteCodeJustMinusOne

	move.l	d2,a2

	moveq	#0,d1
	move.b	(a2)+,d1				;a2: *code

	move.b	d1,(a1)+

	cmp.l	a4,a1					;check for expansion
	bhs	xpkHUFF_C_Expansion			;Uh, Oh, expansion



xpkHUFF_WTSACTOB_WriteCode:

	lsr.b	#3,d1					;8 bits = 1 byte
							;(worx! :-)

xpkHUFF_WTSACTOB_WriteCodeLoop:

	move.b	(a2)+,(a1)+

	cmp.l	a4,a1					;check for expansion
	bhs	xpkHUFF_C_Expansion			;Uh, Oh, expansion
	
	dbf	d1,xpkHUFF_WTSACTOB_WriteCodeLoop

	dbf	d0,xpkHUFF_WriteTranslationSizesAndCodesToOuputBufferLoop

	bra.s	xpkHUFF_WTSACTOB_Done

xpkHUFF_WTSACTOB_DontWriteCodeJustMinusOne:

	move.b	d3,(a1)+				;-1 (= dbf 0)

	cmp.l	a4,a1					;check for expansion
	bhs	xpkHUFF_C_Expansion			;Uh, Oh, expansion

	dbf	d0,xpkHUFF_WriteTranslationSizesAndCodesToOuputBufferLoop


xpkHUFF_WTSACTOB_Done:


;********************************************
;*** write crunched data to output buffer ***
;********************************************



xpkHUFF_WriteCrunchedDataToOutputBuffer:

xpkHUFF_WCDTOB_Init:

;here a1 contains *OutBuf, right behind the table

	move.l	xpkHUFF_CRS_InBuf(a5),a0
	move.l	xpkHUFF_CRS_InLen(a5),d0
	subq.l	#1,d0			;dbf

	lea	xpkHUFF_CRS_PtrTableToSizesAndCodes(a5),a2

	moveq	#7,d4					;# of bits not yet
							;used in dest
							;representation (dbf)

	move.l	xpkHUFF_CRS_OutBuf(a5),a4
	add.l	xpkHUFF_CRS_InLen(a5),a4
;	add.l	#XPKMARGIN,a4

xpkHUFF_WCDTOB_OuterLoop:

;*******************************************************************************
;*** This part will write one byte in it's crunched representation to OutBuf ***
;*******************************************************************************

	moveq	#0,d1
	move.b	(a0)+,d1				;byte to crunch
	add.w	d1,d1					;make long
	add.w	d1,d1					;offset

	move.l	0(a2,d1.w),a3				;*size of bytes'
							;huffman
							;representation
	moveq	#0,d2
	move.b	(a3)+,d2				;number of bits in
							;byte representation
							;(dbf counter)

;register usage here:

;	d0: MUST NOT BE TOUCHED
;	d1: (current byte to be coded)*4
;	d2: #of bits in huffamn representation for byte in d1
;	d4: #of bits not yet used in dest byte (dbf)
;	a0: MUST NOT BE TOUCHED
;	a1: MUST NOT BE TOUCHED
;	a3: *code of byte in d1:8
	moveq	#0,d3					;force read of first
							;byte below

xpkHUFF_WCDTOB_WriteHuffmanRepresentationOfOneByteLoop:

;register usage here:
;	d3:   #of not yet used bits in source representation (dbf)
;	d5:8: contains (partial) huffman code
;	d6:   compose register
	dbf	d3,xpkHUFF_WCDTOB_DontGetNewSourceRepresentationByte
	moveq	#7,d3
	move.b	(a3)+,d5

xpkHUFF_WCDTOB_DontGetNewSourceRepresentationByte:
	addx.b	d5,d5
	addx.b	d6,d6
	dbf	d4,xpkHUFF_WCDTOB_DontWriteDestByte
	moveq	#7,d4
	move.b	d6,(a1)+
	cmp.l	a4,a1					;check for expansion
	bhs.s	xpkHUFF_C_Expansion			;Uh, Oh, expansion

xpkHUFF_WCDTOB_DontWriteDestByte:
	dbf	d2,xpkHUFF_WCDTOB_WriteHuffmanRepresentationOfOneByteLoop
;============================
	dbf	d0,xpkHUFF_WCDTOB_OuterLoop
	cmp.b	#7,d4
	beq.s	xpkHUFF_DontWriteLastPartialByte
	cmp.l	a4,a1
	bhs.s	xpkHUFF_C_Expansion
	addq.l	#1,d4					;former dbf counter
	lsl.b	d4,d6
	move.b	d6,(a1)+

xpkHUFF_DontWriteLastPartialByte:
	move.l	a1,d1			;end of crunched data
	sub.l	xpkHUFF_CRS_OutBuf(a5),d1
	move.l	d1,xpkHUFF_CRS_OutLen(a5)
	move.l	d1,xpkHUFF_CRS_CrunchedLength(a5)
	move.l	xpkHUFF_CRS_xpkSubParams(a5),a0
	move.l	d1,xsp_OutLen(a0)

;__________________________________________________________

xpkHUFF_Encrypt:
	move.l	xpkHUFF_CRS_xpkSubParams(a5),a0
	move.l	xsp_Password(a0),d3
	beq.s	xpkHUFF_DontEncrypt
	move.l	d3,a3
	move.l	xsp_OutBuf(a0),a1
	addq.l	#6,a1			;skip indicator word
					;& password checksum
	move.l	xsp_OutLen(a0),d0
	move.b	(a3),d3			;get encryptor for first byte (for the
					;other bytes it's always the
					;predecessor)
	subq.w	#7,d0			;dbf, skip indicator word, password
					;checksum

;register usage:
;
;d0:	Length of buffer to work on (dbf)
;d1:	current byte to encrypt
;d2:	byte out of passkey for encryption
;d3:	last encrypted byte for more encrytion on next byte
;a1:	*Buffer
;a3:	*password

xpkHUFF_EncryptLoop:
	move.b	(a1),d1				;byte that is to be coded
	move.b	(a3)+,d2			;byte of password
	bne.s	xpkHUFF_EncryptLoopDontResetPasskeyPtr
	move.l	xsp_Password(a0),a3		;reset *password
	move.b	(a3)+,d2			;get first byte of passkey
xpkHUFF_EncryptLoopDontResetPasskeyPtr:
	eor.b	d2,d1				;password byte as encryptor
	add.b	d3,d1				;last encrypted byte as
						;encryptor
	move.b	d1,(a1)+			;write current encrypted byte
	move.b	d1,d3				;last byte is encryptor for
						;next byte
	dbf	d0,xpkHUFF_EncryptLoop

xpkHUFF_DontEncrypt:

;__________________________________________________________

xpkHUFF_FreeReentry:
	move.l	xpkHUFF_CRS_xpkSubParams(a5),-(a7)
	move.l	#xpkHUFF_CRS_SIZEOF,d0
	move.l	a5,a1
	move.l	4.w,a6
	jsr	_LVOFreeMem(a6)
	sub.l	a5,a5
	move.l	(a7)+,a0
	moveq	#XPKERR_OK,d0
	rts					;exit back to mapping code

xpkHUFF_AllocReentryBufferFailed:
	move.l	a2,a0				;subparams
	moveq	#XPKERR_NOMEM,d0
	rts					;don't crunch

xpkHUFF_C_Expansion:
	move.l	xpkHUFF_CRS_xpkSubParams(a5),-(a7)
	move.l	#xpkHUFF_CRS_SIZEOF,d0
	move.l	a5,a1
	move.l	4.w,a6
	jsr	_LVOFreeMem(a6)
	sub.l	a5,a5
	move.l	(a7)+,a0
	tst.l	xsp_Password(a0)
	beq.s	XPKHUFF_C_NormalExpansion

xpkHUFF_C_CantEncryptBecauseOfExpansion:

;if  file would expand I would have to copy data to OutBuf and encrypt there
;without  trying  to crunch.  But my buffer would be 6 bytes larger than the
;original  one,  for  I  add  an identifier and the password checksum at the
;beginning  of  buffer.  This means I would exceed my buffer, which is (with
;the current version of xpkmaster.library (V 2.1)) not allowed.  Therefore I
;return  this  error,  to  make  sure  user  notices  that  data WILL NOT BE
;ENCRYPTED.
;
;Should  only  occur  with short files (I have an indicator word, a password
;checksum and a table size of >256 bytes which I put to OutBuf, so indicator
;word,  password  checksum, tablesize and crunched data must be smaller than
;InLen) or files that have already been crunched.

	moveq	#XPKERR_SMALLBUF,d0		;indicates that outbuf was
						;too small
	rts

XPKHUFF_C_NormalExpansion:
	moveq	#XPKERR_EXPANSION,d0
	rts					;back to mapping code

;__________________________________________________________

_XpksUnpackChunk:
;in:
;
;	a0 :	*XpkSubParams
;	a6 :	*xpkHUFF.library
;out:
;	d0 :	err
;	in XpkSubParams :	xsp_OutLen
;
;registers trashed:
;

	MOVEM.L	D2-D7/A2-A6,-(A7)
	BSR.B	UnPackMode0
	MOVEM.L	(A7)+,D2-D7/A2-A6
	RTS

ROOT	equ	 0
NODE	equ	 0
LEAF	equ	 $ff					;leftmost bit set in
							;word TypeAndChar
							;for bpl below
xpkHUFF_DecrunchTreeNodeStruct:		rsreset
xpkHUFF_DTNS_TypeAndChar:		rs.b	0	;for fast decrunching
xpkHUFF_DTNS_Type:			rs.b	1	;ROOT|NODE|LEAF
xpkHUFF_DTNS_Char:			rs.b	1	;only for leafs
xpkHUFF_DTNS_LeftSubTree:		rs.l	1	;*left subtree
xpkHUFF_DTNS_RightSubTree:		rs.l	1	;*righ subtree
xpkHUFF_DRS_TreeNode_SIZEOF:		rs.b	0

xpkHUFF_DecrunchReentryStructure:	rsreset
xpkHUFF_DRS_xpkSubParams:		rs.l	1
xpkHUFF_DRS_xpkSubLibBase:		rs.l	1
xpkHUFF_DRS_CrunchMethodIdentifier:	rs.w	1	;to ensure I can distuinguish old crunches from new ones
xpkHUFF_DRS_PasswordChecksum:		rs.l	1	;space for checksum of password, if password was used, a constant (currently '$ABADCAFE') otherwise
xpkHUFF_DRS_CrunchedDataTreeBuffer:	rs.l	1	;*decrunch tree in crunched data buffer
xpkHUFF_DRS_CrunchedDataBuffer:		rs.l	1	;*crunched data
xpkHUFF_DRS_DecrunchedDataBuffer:	rs.l	1	;*buffer to decrunch to
xpkHUFF_DRS_CrunchedLength:		rs.l	1	;size of crunched data in bytes
xpkHUFF_DRS_DecrunchedLength:		rs.l	1	;size of decrunched data in bytes
xpkHUFF_DRS_RootNode:			rs.l	1	;*root node of decrunch tree
xpkHUFF_DRS_SpaceForDecrunchTree:	rs.b	(256+255)*xpkHUFF_DRS_TreeNode_SIZEOF
xpkHUFF_DRS_DecryptBuffer:		rs.b	MaxPackChunkSize
xpkHUFF_DRS_SIZEOF:			rs.b	0

UnPackMode0:
	move.l	a0,a2					;keep xpksubparams
							;in mind
	move.l	a6,a3					;keep xpkHUFF base
							;in mind
	move.l	#xpkHUFF_DRS_SIZEOF,d0
	move.l	#MEMF_CLEAR|MEMF_PUBLIC,d1
	move.l	4.w,a6
	jsr	_LVOAllocMem(a6)
	tst.l	d0
	beq	xpkHUFF_AllocDecrunchReentryBufferFailed
	move.l	d0,a5					;reentry buffer
							;now in a5
	move.l	a3,xpkHUFF_DRS_xpkSubLibBase(a5)	;store xpkHUFF base in
							;the reentry structure
	move.l	a2,xpkHUFF_DRS_xpkSubParams(a5)		;store subparams in
							;the reentry structure
;******************************
;*** fill reentry structure ***
;******************************
	move.l	xsp_InBuf(a2),a0
	move.l	xsp_InLen(a2),xpkHUFF_DRS_CrunchedLength(a5)
	subq.l	#6,xpkHUFF_DRS_CrunchedLength(a5);indicator word, password checksum
	move.l	xsp_OutBuf(a2),xpkHUFF_DRS_DecrunchedDataBuffer(a5)
	move.l	xsp_OutLen(a2),xpkHUFF_DRS_DecrunchedLength(a5)
	move.w	(a0)+,xpkHUFF_DRS_CrunchMethodIdentifier(a5)
	move.l	(a0)+,xpkHUFF_DRS_PasswordChecksum(a5)
	cmp.w	#CMI_NORMAL,xpkHUFF_DRS_CrunchMethodIdentifier(a5)
	bne	xpkHUFF_UnknownCrunchMethod			;I cant't
								;handle this
	tst.l	xsp_Password(a2)
	beq.s	xpkHUFF_D_InBufNotEncrypted

;__________________________________________________________

;***************************
;*** decryption handling ***
;***************************

xpkHUFF_D_CalcPasswordCheckSum:
	move.l	xsp_Password(a2),a3
	move.l	#$ABADCAFE,d0
	move.w	#$C0DE,d1

xpkHUFF_D_CalcPasswordChecksumLoop1:
	move.b	(a3)+,d1
	beq.s	xpkHUFF_D_CalcPasswordChecksumLoop1End
	add.w	d1,d0
	bra.s	xpkHUFF_D_CalcPasswordChecksumLoop1

xpkHUFF_D_CalcPasswordChecksumLoop1End:
	move.l	xsp_Password(a2),a3
	swap	d0

xpkHUFF_D_CalcPasswordChecksumLoop2:
	move.b	(a3)+,d1
	beq.s	xpkHUFF_D_CalcPasswordChecksumLoop2End
	eor.w	d1,d0
	rol.w	#8,d0
	bra.s	xpkHUFF_D_CalcPasswordChecksumLoop2

xpkHUFF_D_CalcPasswordChecksumLoop2End:
	cmp.l	xpkHUFF_DRS_PasswordChecksum(a5),d0
	beq.s	xpkHUFF_D_PasswordProbablyOK
	bsr	xpkHUFF_FreeMem
	moveq	#XPKERR_WRONGPW,d0	;wrong password used to decrunch
	rts

xpkHUFF_D_PasswordProbablyOK:
;***************
;*** decrypt ***
;***************
	move.l	xpkHUFF_DRS_CrunchedLength(a5),d0
	subq.w	#1,d0			;dbf
	move.l	a0,a1			;*InBuf
	move.l	a2,a0			;*xpk sub params
	lea	xpkHUFF_DRS_DecryptBuffer(a5),a2
	move.l	xsp_Password(a0),a3
	move.b	(a3),d3			;get decryptor for first byte (for the
					;other bytes it's always the
					;predecessor)
;register usage:
;
;d0:	Length of buffer to work on
;d1:	current byte to encrypt
;d2:	byte out of passkey for encryption
;d3:	last encrypted byte for more encrytion on next byte
;a0:	*xpk sub params
;a1:	*InBuf
;a2:	*DecryptBuffer
;a3:	*password

xpkHUFF_DecryptLoop:
	move.b	(a1)+,d1		;byte that is to be decoded
	move.b	d1,d4
	move.b	(a3)+,d2		;byte of password
	bne.s	xpkHUFF_DontResetPassWordPointer
	move.l	xsp_Password(a0),a3	;reset *password
	move.b	(a3)+,d2		;get first byte of password

xpkHUFF_DontResetPassWordPointer:
	sub.b	d3,d1			;last encrypted byte as decryptor
	eor.b	d2,d1			;password byte as decryptor
	move.b	d1,(a2)+		;write current decrypted byte
	move.b	d4,d3			;last encrypted byte is decryptor for
					;next byte
	dbf	d0,xpkHUFF_DecryptLoop
	lea	xpkHUFF_DRS_DecryptBuffer(a5),a0
;	bra.s	xpkHUFF_D_OKToDecrunch

;__________________________________________________________

xpkHUFF_D_InBufNotEncrypted:
xpkHUFF_D_OKToDecrunch:
	move.l	a0,xpkHUFF_DRS_CrunchedDataTreeBuffer(a5)

;****************************
;*** create decrunch tree ***
;****************************

xpkHUFF_D_CreateDecrunchTree:
	move.l	xpkHUFF_DRS_CrunchedDataTreeBuffer(a5),a0
	lea	xpkHUFF_DRS_SpaceForDecrunchTree(a5),a1
	move.l	a1,xpkHUFF_DRS_RootNode(a5)
	move.l	a1,a3
	move.b	#ROOT,xpkHUFF_DTNS_Type(a1)
	lea	xpkHUFF_DRS_TreeNode_SIZEOF(a1),a1	;proceed to next node
	move.l	#255,d0					;256 runs
	moveq	#-1,d1					;for fast cmp.b below
	moveq	#-1,d2					;initial byte

xpkHUFF_D_CreateDecrunchTreeLeafLoop:
	addq.b	#1,d2
	moveq	#0,d3
	move.b	(a0)+,d3				;d3:Length
	cmp.b	d3,d1					;length = -1 (ie 0)?
							;(255 can't occur!)
	beq.s	xpkHUFF_D_CDTL_DoneWithChar
	moveq	#0,d5					;force dbf below
							;to read next byte
	move.l	a3,a2					;*root node

xpkHUFF_D_CreateDecrunchTreeNodeLoop:
	dbf	d5,xpkHUFF_D_CDTN_DontGetNextCodeByte
	move.b	(a0)+,d4
	moveq	#7,d5
	
xpkHUFF_D_CDTN_DontGetNextCodeByte:
	add.b	d4,d4					;leftmost bit to carry
	bcs.s	xpkHUFF_D_CDTN_RightSubTree
	
;____________________________

xpkHUFF_D_CDTN_LeftSubTree:
	move.l	xpkHUFF_DTNS_LeftSubTree(a2),d6
	beq.s	xpkHUFF_D_CDTN_LeftSubTree_CreateNewNode
	move.l	d6,a2
	dbf	d3,xpkHUFF_D_CreateDecrunchTreeNodeLoop
	bra.s	xpkHUFF_D_CDTN_WriteCharDataToLeaf

xpkHUFF_D_CDTN_LeftSubTree_CreateNewNode:
	move.l	a1,xpkHUFF_DTNS_LeftSubTree(a2)
	move.l	a1,a2
	lea	xpkHUFF_DRS_TreeNode_SIZEOF(a1),a1
	dbf	d3,xpkHUFF_D_CreateDecrunchTreeNodeLoop
	bra.s	xpkHUFF_D_CDTN_WriteCharDataToLeaf

;____________________________

xpkHUFF_D_CDTN_RightSubTree:
	move.l	xpkHUFF_DTNS_RightSubTree(a2),d6
	beq.s	xpkHUFF_D_CDTN_RightSubTree_CreateNewNode
	move.l	d6,a2
	dbf	d3,xpkHUFF_D_CreateDecrunchTreeNodeLoop
	bra.s	xpkHUFF_D_CDTN_WriteCharDataToLeaf

xpkHUFF_D_CDTN_RightSubTree_CreateNewNode:
	move.l	a1,xpkHUFF_DTNS_RightSubTree(a2)
	move.l	a1,a2
	lea	xpkHUFF_DRS_TreeNode_SIZEOF(a1),a1
	dbf	d3,xpkHUFF_D_CreateDecrunchTreeNodeLoop
;	bra.s	xpkHUFF_D_CDTN_WriteCharDataToLeaf
;____________________________

xpkHUFF_D_CDTN_WriteCharDataToLeaf:
	move.b	d2,xpkHUFF_DTNS_Char(a2)
	move.b	#LEAF,xpkHUFF_DTNS_Type(a2)

xpkHUFF_D_CDTL_DoneWithChar:
	dbf	d0,xpkHUFF_D_CreateDecrunchTreeLeafLoop
	move.l	a0,xpkHUFF_DRS_CrunchedDataBuffer(a5)

;*********************
;*** decrunch data ***
;*********************

DecrunchData:

;__________________________________________________________

	IFEQ	DecrunchCode
	move.l	xpkHUFF_DRS_DecrunchedLength(a5),d0
	subq.l	#1,d0
	move.l	xpkHUFF_DRS_CrunchedDataBuffer(a5),a0
	move.l	xpkHUFF_DRS_DecrunchedDataBuffer(a5),a1
	moveq	#0,d1			;force read of first byte below
					;register
	move.l	xpkHUFF_DRS_RootNode(a5),a3

xpkHUFF_DD_ByteLoop:
	move.l	a3,a2			;root node

xpkHUFF_DD_InnerLoop:
	dbf	d1,xpkHUFF_DD_IL_DontGetNewSourceByte
	move.b	(a0)+,d2
	moveq	#7,d1

xpkHUFF_DD_IL_DontGetNewSourceByte:
	add.b	d2,d2
	bcs.s	xpkHUFF_DD_RightSubTree

xpkHUFF_DD_LeftSubTree:
	move.l	xpkHUFF_DTNS_LeftSubTree(a2),a2
	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	xpkHUFF_DD_InnerLoop
	bra.s	xpkHUFF_DD_WriteChar

xpkHUFF_DD_RightSubTree:
	move.l	xpkHUFF_DTNS_RightSubTree(a2),a2
	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	xpkHUFF_DD_InnerLoop
;	bra.s	xpkHUFF_DD_WriteChar

xpkHUFF_DD_WriteChar:
	move.b	d3,(a1)+
	dbf	d0,xpkHUFF_DD_ByteLoop
	bsr.s	xpkHUFF_FreeMem
	moveq	#XPKERR_OK,d0
	rts				;back to mapping code

xpkHUFF_AllocDecrunchReentryBufferFailed:
	moveq	#XPKERR_NOMEM,d0
	rts
	ENDC		;of IFEQ DecrunchCode

;__________________________________________________________
	IFEQ	((DecrunchCode-1)*(DecrunchCode-2)*(DecrunchCode-3)*(DecrunchCode-4))


;*************************************************
;*** calculate crunched length (without table) ***
;*************************************************

	move.l	xpkHUFF_DRS_CrunchedDataTreeBuffer(a5),d0
	add.l	xpkHUFF_DRS_CrunchedLength(a5),d0	;equ *byte behind
							;crunched data
	sub.l	xpkHUFF_DRS_CrunchedDataBuffer(a5),d0	;equ size of crunched
							;data without tree
							;information
	IFEQ	((DecrunchCode-1)*(DecrunchCode-2)*(DecrunchCode-4))
;******************************************************************************
;*** here the size of the crunched data excluding tree information is in d0 ***
;******************************************************************************
	subq.l	#1,d0					;-1: last byte might
							;not be used
							;completely
	IFEQ	DecrunchCode-2
	lsr.l	#1,d0					;we are processing a
	ENDC						;word below
	IFEQ	DecrunchCode-4
	lsr.l	#2,d0					;we are processing a
	ENDC						;long below
	subq.l	#1,d0					;-1: dbf
	move.l	xpkHUFF_DRS_CrunchedDataBuffer(a5),a0
	move.l	xpkHUFF_DRS_DecrunchedDataBuffer(a5),a1
	move.l	xpkHUFF_DRS_RootNode(a5),a3
	move.l	a3,a2					;root node

DD_ProcessOneCrunchedByteWordLongLoop:
	IFEQ	DecrunchCode-1
	move.b	(a0)+,d2
	ENDC
	IFEQ	DecrunchCode-2
	move.w	(a0)+,d2
	ENDC
	IFEQ	DecrunchCode-4
	move.l	(a0)+,d2
	ENDC

ProcessOneBit	MACRO
	IFEQ	DecrunchCode-1
	add.b	d2,d2				;leftmost bit to carry
	ENDC
	IFEQ	DecrunchCode-2
	add.w	d2,d2				;leftmost bit to carry
	ENDC
	IFEQ	DecrunchCode-4
	add.l	d2,d2				;leftmost bit to carry
	ENDC
	bcc.s	DD_LeftSubTree\@

DD_RightSubTree\@:
	move.l	xpkHUFF_DTNS_RightSubTree(a2),a2
	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	DD_ProceedWithNextBit\@
	move.b	d3,(a1)+			;write char
	move.l	a3,a2				;start again at root node
	bra.s	DD_CharWritten\@

DD_LeftSubTree\@:
	move.l	xpkHUFF_DTNS_LeftSubTree(a2),a2
	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	DD_ProceedWithNextBit\@
	move.b	d3,(a1)+			;write char
	move.l	a3,a2				;start again at root node
;	bra.s	DD_CharWritten\@

DD_CharWritten\@:
DD_ProceedWithNextBit\@:

	ENDM
	IFEQ	DecrunchCode-1
	ProcessOneBit		;1
	ProcessOneBit		;2
	ProcessOneBit		;3
	ProcessOneBit		;4
	ProcessOneBit		;5
	ProcessOneBit		;6
	ProcessOneBit		;7
	ProcessOneBit		;8
	ENDC
	IFEQ	DecrunchCode-2			;I know I could use a smarter
						;condition here, but I'm lazy
						;and it doesn't affect
						;executable speed
	ProcessOneBit		;1
	ProcessOneBit		;2
	ProcessOneBit		;3
	ProcessOneBit		;4
	ProcessOneBit		;5
	ProcessOneBit		;6
	ProcessOneBit		;7
	ProcessOneBit		;8
	ProcessOneBit		;9
	ProcessOneBit		;10
	ProcessOneBit		;11
	ProcessOneBit		;12
	ProcessOneBit		;13
	ProcessOneBit		;14
	ProcessOneBit		;15
	ProcessOneBit		;16
	ENDC

	IFEQ	DecrunchCode-4
	ProcessOneBit		;1
	ProcessOneBit		;2
	ProcessOneBit		;3
	ProcessOneBit		;4
	ProcessOneBit		;5
	ProcessOneBit		;6
	ProcessOneBit		;7
	ProcessOneBit		;8
	ProcessOneBit		;9
	ProcessOneBit		;10
	ProcessOneBit		;11
	ProcessOneBit		;12
	ProcessOneBit		;13
	ProcessOneBit		;14
	ProcessOneBit		;15
	ProcessOneBit		;16
	ProcessOneBit		;17
	ProcessOneBit		;18
	ProcessOneBit		;19
	ProcessOneBit		;20
	ProcessOneBit		;21
	ProcessOneBit		;22
	ProcessOneBit		;23
	ProcessOneBit		;24
	ProcessOneBit		;25
	ProcessOneBit		;26
	ProcessOneBit		;27
	ProcessOneBit		;28
	ProcessOneBit		;29
	ProcessOneBit		;30
	ProcessOneBit		;31
	ProcessOneBit		;32
	ENDC
	dbf	d0,DD_ProcessOneCrunchedByteWordLongLoop
	ENDC	;of IFEQ ((DecrunchCode-1)*(DecrunchCode-2)*(DecrunchCode-4))

;__________________________________________________________

	IFEQ	DecrunchCode-3		;68030+ cache oriented decrunch code

;******************************************************************************
;*** here the size of the crunched data excluding tree information is in d0 ***
;******************************************************************************

	subq.l	#1,d0				;-1: last byte might not be used completely
	lsr.l	#2,d0				;we are processing a whole long below
	subq.l	#1,d0				;-1: dbf
	move.l	xpkHUFF_DRS_CrunchedDataBuffer(a5),a0
	move.l	xpkHUFF_DRS_DecrunchedDataBuffer(a5),a1
	move.l	xpkHUFF_DRS_RootNode(a5),a3
	move.l	a3,a2				;root node

DD_ProcessOneCrunchedByteLoop:
	move.l	(a0)+,d2
	bsr.s	DD_ProcessFourBitSubRoutine	; 1- 4
	bsr.s	DD_ProcessFourBitSubRoutine	; 5- 8
	bsr.s	DD_ProcessFourBitSubRoutine	; 9-12
	bsr.s	DD_ProcessFourBitSubRoutine	;13-16
	bsr.s	DD_ProcessFourBitSubRoutine	;17-20
	bsr.s	DD_ProcessFourBitSubRoutine	;21-24
	bsr.s	DD_ProcessFourBitSubRoutine	;25-28
	bsr.s	DD_ProcessFourBitSubRoutine	;29-32
	dbf	d0,DD_ProcessOneCrunchedByteLoop
	bra	DD_AlmostDone

DD_ProcessFourBitSubRoutine:

;____________________________

ProcessOneBitCodeMACRO	MACRO
	add.l	d2,d2				;leftmost bit to carry
	bcc.s	DD_LeftSubTree\@

DD_RightSubTree\@:
	move.l	xpkHUFF_DTNS_RightSubTree(a2),a2
	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	DD_ProceedWithNextBit\@
	move.b	d3,(a1)+			;write char
	move.l	a3,a2				;start again at root node
	IFEQ	\1-0
	bra.s	DD_CharWritten\@
	ENDC
	IFEQ	\1-1
	rts
	ENDC

DD_LeftSubTree\@:
	move.l	xpkHUFF_DTNS_LeftSubTree(a2),a2
	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	DD_ProceedWithNextBit\@
	move.b	d3,(a1)+			;write char
	move.l	a3,a2				;start again at root node
;	bra.s	DD_CharWritten\@

DD_CharWritten\@:
DD_ProceedWithNextBit\@:
	IFEQ	\1-1
	rts
	ENDC

	ENDM
;____________________________

	ProcessOneBitCodeMACRO	0	;1
	ProcessOneBitCodeMACRO	0	;2
	ProcessOneBitCodeMACRO	0	;3
	ProcessOneBitCodeMACRO	1	;4	;last MACRO will have rts
						;instead of bra
DD_AlmostDone:
	ENDC ;of IFEQ DecrunchCode-3 ;68030+ cache oriented decrunch code

;__________________________________________________________

ProcessLastByteOrBytesOfCrunchedData:
	move.l	a1,d1
	sub.l	xpkHUFF_DRS_DecrunchedDataBuffer(a5),d1
;= number of bytes already decrunched to output buffer
	move.l	xpkHUFF_DRS_DecrunchedLength(a5),d0
	sub.l	d1,d0
;= number of bytes still to decrunch (ranges from 1..31, depending on decrunch code, too)
	beq.s	DD_Done
	subq.l	#1,d0					;dbf
	moveq	#0,d4					;force read of next byte below
;note: the loop above was over the crunched bytes, the loop below is over
;       uncrunched bytes, therefore we have a different code here

;____________________________

DD_Loop:
	dbf	d4,DD_DontGetNewByte
	move.b	(a0)+,d2
	moveq	#7,d4

DD_DontGetNewByte:
	add.b	d2,d2			;leftmost bit to carry
	bcc.s	DD_LeftSubTree

DD_RightSubTree:
	move.l	xpkHUFF_DTNS_RightSubTree(a2),a2
	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	DD_Loop
	bra.s	DD_WriteChar

DD_LeftSubTree:
	move.l	xpkHUFF_DTNS_LeftSubTree(a2),a2
	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	DD_Loop
;	bra.s	DD_WriteChar

DD_WriteChar:
	move.b	d3,(a1)+	;write decrunched byte
	move.l	a3,a2		;start again at root node
	dbf	d0,DD_Loop
;____________________________

DD_Done:
	move.l	xpkHUFF_CRS_xpkSubParams(a5),a0
	bsr.s	xpkHUFF_FreeMem
	moveq	#XPKERR_OK,d0
	rts						;back to mapping code

xpkHUFF_AllocDecrunchReentryBufferFailed:
	move.l	a2,a0					;subparams
	moveq	#XPKERR_NOMEM,d0
	rts
	ENDC	;of IFEQ ((DecrunchCode-1)*(DecrunchCode-2)*(DecrunchCode-3)*(DecrunchCode-4))

xpkHUFF_UnknownCrunchMethod:
	move.l	xpkHUFF_DRS_xpkSubParams(a5),a0
	bsr.s	xpkHUFF_FreeMem
	moveq	#XPKERR_OLDSUBLIB,d0
	rts

xpkHUFF_FreeMem:
	movem.l	d0/d1/a0/a1,-(a7)
	move.l	#xpkHUFF_DRS_SIZEOF,d0
	move.l	a5,a1
	move.l	4.w,a6
	JSR	_LVOFreeMem(A6)
	movem.l	(a7)+,d0/d1/a0/a1
	rts

HUFFXpkInfo
	DC.W	1		; Version number of this structure
	DC.W	VERSION		; The version of this sublibrary
	DC.W	1		; The required master lib version
	DC.W	0		; Longword align
	DC.L	BriefName	; Brief name of the packer
	DC.L	FullName	; Full name of the packer
	DC.L	Description	; One line description of packer
	DC.L	'HUFF'		; ID the packer goes by (XPK format)
	DC.L	XPKIF_PK_CHUNK|XPKIF_UP_CHUNK|XPKIF_ENCRYPTION
	DC.L	MaxPackChunkSize; Max input chunk size for packing
	DC.L	MinPackChunkSize; Min input chunk size for packing
	DC.L	DefPackChunkSize; Default packing chunk size
	DC.L	PackMsg		; Packing message, present tense
	DC.L	UnpackMsg	; Unpacking message, present tense
	DC.L	PackedMsg	; Packing message, past tense
	DC.L	UnpackedMsg	; Unpacking message, past tense
	DC.W	DefMode		; Default mode number
	DC.W	0		; for future use
	DC.L	HUFFXpkMode	; Array of compression modes
	DC.L	0,0,0,0,0,0	; Future expansion - set to zero

HUFFXpkMode
	DC.L	0			; Chain to next descriptor for ModeDesc list
	DC.L	100			; Maximum efficiency handled by this mode
	DC.L	XPKMF_A3000SPEED
	DC.L	xpkHUFF_CRS_SIZEOF 	; Extra memory required during packing
	DC.L	xpkHUFF_DRS_SIZEOF 	; Extra memory during unpacking
	DC.L	88			; Approx packing speed in K per second
	IFEQ	DecrunchCode-0
	DC.L	100			; Approx unpacking speed in K per second
	ENDC
	IFEQ	DecrunchCode-1
	DC.L	138			; Approx unpacking speed in K per second
	ENDC
	IFEQ	DecrunchCode-2
	DC.L	89			; Approx unpacking speed in K per second
	ENDC
	IFEQ	DecrunchCode-3
	DC.L	93			; Approx unpacking speed in K per second
	ENDC
	IFEQ	DecrunchCode-4
	DC.L	91			; Approx unpacking speed in K per second
	ENDC
	DC.W	241			; CF in 0.1% for AmigaVision executable
	DC.W	MaxPackChunkSize/1024	; Desired chunk size in K (!!) for this mode
	DC.B	'normal',0,0,0,0	;8 character mode description

PackMsg		DC.B	'Crunching',0
UnpackMsg	DC.B	'Decrunching',0
PackedMsg	DC.B	'Crunched',0
UnpackedMsg	DC.B	'Decrunched',0
BriefName	DC.B	'HUFF',0
FullName	DC.B	'Huffman',0
Description
	IFEQ	DecrunchCode-0
	dc.b	"Dynamic huffman crunch algorithm, "
	dc.b	"very simple decrunch algorithm",00
	ENDC
	IFEQ	DecrunchCode-1
	dc.b	"Dynamic huffman crunch algorithm, "
	dc.b	"cache optimized byte decrunch algorithm",00
	ENDC
	IFEQ	DecrunchCode-2
	dc.b	"Dynamic huffman crunch algorithm, "
	dc.b	"word oriented decrunch algorithm",00
	ENDC
	IFEQ	DecrunchCode-3
	dc.b	"Dynamic huffman crunch algorithm, "
	dc.b	"68030+ cache oriented decrunch algorithm",00
	ENDC
	IFEQ	DecrunchCode-4
	dc.b	"Dynamic huffman crunch algorithm, "
	dc.b	"long oriented decrunch algorithm",00
	ENDC
	END
