/****************************************************************
*                                                               *
*  PowerPacker DATA file support function V1.0                  *
*                                                               *
*    error = PP_LoadData (file, col, typeofmem, buffer, length) *
*    with:                                                      *
*       char *file;     filename                                *
*       UBYTE col;      color (see ppdata.h)                    *
*       ULONG typeofmem type of memory that will be allocated   *
*       UBYTE **buffer  pointer to pointer to buffer            *
*       ULONG *length   pointer to buffer length                *
*                                                               *
*    NOTE: After loading you must free the allocated            *
*          memory yourself :                                    *
*          DO NOT FORGET !!!!!                                  *
*             FreeMem (buffer, length);                         *
*          Errors are defined in ppdata.h                       *
*                                                               *
*    Example:                                                   *
*                                                               *
*      UBYTE *mymem = NULL;                                     *
*      ULONG mylen = 0;                                         *
*                                                               *
*      err = PP_LoadData ("df0:myfile.pp", MEMF_PUBLIC,         *
*                     DECR_POINTER, &mymem, &mylen);            *
*      if (err == PP_OKDECRUNCH) {                              *
*         DoSomething (mymem, mylen);                           *
*         FreeMem (mymem, mylen);                               *
*         }                                                     *
*                                                               *
****************************************************************/

#include <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <functions.h>

#include <ppdata.h>

#define SAFETY_MARGIN	64L
#define SIZEOF				(ULONG)sizeof

struct FileLock *pp_lock;
UBYTE *pp_filestart;
ULONG pp_bufferlen;
UWORD coladdr[4] = { 0xf180, 0xf182, 0xf1a2, 0xf102 };
struct FileInfoBlock *pp_FileInfoBlock = NULL;

PP_LoadData (pp_file, color, typeofmem, buffer, length)		/* Version 1.0 */
char *pp_file;
UBYTE color;
ULONG typeofmem;
UBYTE **buffer;
ULONG *length;
{
	ULONG hdr;
	UWORD *decrcol, instr, hicol, locol;
	ULONG pp_filelen, pp_crunlen, pp_length, pp_efficiency;
	UBYTE pp_crunched;
	extern void DecrunchBuffer(), DecrunchColor();

	if (!(pp_FileInfoBlock = (struct FileInfoBlock *)AllocMem
		(SIZEOF(*pp_FileInfoBlock), MEMF_PUBLIC))) return (PP_NOMEMORY);

	/* Set decruncher color */
	decrcol = (UWORD *)DecrunchColor;
	if (color != 4) {
		instr = 0x33c9; hicol = 0x00df;
		locol = coladdr[color];					/* = move.w a1,$dff1xx */
		}
	else instr = hicol = locol = 0x4e71; 	/* nop */
	*decrcol = instr;
	*(decrcol+1) = hicol;
	*(decrcol+2) = locol;

	if (!(pp_lock = (struct FileLock *)Lock (pp_file, ACCESS_READ))) {
		FreePPData();
		return (PP_LOCKERR);
		}
	Examine (pp_lock, pp_FileInfoBlock);
	UnLock (pp_lock);
	pp_lock = NULL;
	pp_length = pp_FileInfoBlock->fib_Size;

	/* read decrunched length */
	pp_filestart = NULL;
	if (!(pp_lock = (struct FileLock *)Open (pp_file, MODE_OLDFILE))) {
		FreePPData();
		return (PP_OPENERR);
		}
	if (Read (pp_lock, &hdr, 4L) != 4L) {
		FreePPData();
		return (PP_READERR);
		}
	/* check if crunched */
	if ((hdr == 0x50503131 || hdr == 0x50503230) && (pp_length > 16L)) {
		Seek (pp_lock, (LONG)(pp_length-4L), OFFSET_BEGINNING);
		if (Read (pp_lock, &pp_filelen, 4L) != 4L) {
			FreePPData();
			return (PP_READERR);
			}
		pp_filelen >>= 8L;
		pp_crunlen = pp_length - 8L;
		Seek (pp_lock, 4L, OFFSET_BEGINNING);
		/* read efficiency table */
		if (Read (pp_lock, &pp_efficiency, 4L) != 4L) {
			FreePPData();
			return (PP_READERR);
			}
		pp_bufferlen = pp_filelen + SAFETY_MARGIN;
		pp_crunched = TRUE;
		}
	else {
		Seek (pp_lock, 0L, OFFSET_BEGINNING);
		pp_bufferlen = pp_crunlen = pp_filelen = pp_length;
		pp_crunched = FALSE;
		}
	/* load file */
	if (!(pp_filestart=(UBYTE *)AllocMem (pp_bufferlen, typeofmem))) {
		FreePPData();
		return (PP_NOMEMORY);
		}
	if (Read (pp_lock, pp_filestart, pp_crunlen) != pp_crunlen) {
		FreePPData();
		return (PP_READERR);
		}
	Close (pp_lock);
	FreeMem (pp_FileInfoBlock, SIZEOF(*pp_FileInfoBlock));
	pp_lock = NULL;
	pp_FileInfoBlock = NULL;

	if (pp_crunched) {
		/* decrunch */
		DecrunchBuffer (pp_filestart + pp_crunlen, pp_filestart + SAFETY_MARGIN,
							 pp_efficiency);
		FreeMem (pp_filestart, SAFETY_MARGIN);
		pp_filestart += SAFETY_MARGIN;
		}
	*buffer = pp_filestart;
	*length = pp_filelen;
	return (PP_OKDECRUNCH);
}

FreePPData()
{
	if (pp_lock) Close (pp_lock);
	if (pp_filestart) FreeMem (pp_filestart, pp_bufferlen);
	if (pp_FileInfoBlock) FreeMem (pp_FileInfoBlock, SIZEOF(*pp_FileInfoBlock));
	pp_FileInfoBlock = NULL;
	pp_lock = NULL;
	pp_filestart = NULL;
}

#asm

; PowerPacker Decrunch assembler subroutine V1.0
;
; call as:
;    DecrunchBuffer (endcrun, buffer, efficiency);
; with:
;    endcrun   : UBYTE * just after last byte of crunched file
;    buffer    : UBYTE * to memory block to decrunch in
;    efficiency: Longword defining efficiency with wich file was crunched
;
; NOTE:
;    Decrunch a few bytes higher (safety margin) than the crunched file
;    to decrunch in the same memory space. (64 bytes suffice)
;

	XDEF _DecrunchBuffer
	XDEF _DecrunchColor

_DecrunchBuffer:
	move.l 4(a7),a0
	move.l 8(a7),a1
	move.l 12(a7),d0
	movem.l d1-d7/a2-a6,-(a7)
	bsr.s Decrunch
	movem.l (a7)+,d1-d7/a2-a6
	rts

Decrunch:
	lea myBitsTable(PC),a5
	move.l d0,(a5)
	move.l a1,a2
	move.l -(a0),d5
	moveq #0,d1
	move.b d5,d1
	lsr.l #8,d5
	add.l d5,a1
	move.l -(a0),d5
	lsr.l d1,d5
	move.b #32,d7
	sub.b d1,d7
LoopCheckCrunch:
	bsr.s ReadBit
	tst.b d1
	bne.s CrunchedBytes
NormalBytes:
	moveq #0,d2
Read2BitsRow:
	moveq #2,d0
	bsr.s ReadD1
	add.w d1,d2
	cmp.w #3,d1
	beq.s Read2BitsRow
ReadNormalByte:
	move.w #8,d0
	bsr.s ReadD1
	move.b d1,-(a1)
	dbf d2,ReadNormalByte
	cmp.l a1,a2
	bcs.s CrunchedBytes
	rts
CrunchedBytes:
	moveq #2,d0
	bsr.s ReadD1
	moveq #0,d0
	move.b (a5,d1.w),d0
	move.l d0,d4
	move.w d1,d2
	addq.w #1,d2
	cmp.w #4,d2
	bne.s ReadOffset
	bsr.s ReadBit
	move.l d4,d0
	tst.b d1
	bne.s LongBlockOffset
	moveq #7,d0
LongBlockOffset:
	bsr.s ReadD1
	move.w d1,d3
Read3BitsRow:
	moveq #3,d0
	bsr.s ReadD1
	add.w d1,d2
	cmp.w #7,d1
	beq.s Read3BitsRow
	bra.s DecrunchBlock
ReadOffset:
	bsr.s ReadD1
	move.w d1,d3
DecrunchBlock:
	move.b (a1,d3.w),d0
	move.b d0,-(a1)
	dbf d2,DecrunchBlock
EndOfLoop:
_DecrunchColor:
	move.w a1,$dff1a2
	cmp.l a1,a2
	bcs.s LoopCheckCrunch
	rts
ReadBit:
	moveq #1,d0
ReadD1:
	moveq #0,d1
	subq.w #1,d0
ReadBits:
	lsr.l #1,d5
	roxl.l #1,d1
	subq.b #1,d7
	bne.s No32Read
	move.b #32,d7
	move.l -(a0),d5
No32Read:
	dbf d0,ReadBits
	rts
myBitsTable:
	dc.b $09,$0a,$0b,$0b

#endasm
