/* File: C:\WACKER\xfer\cmprs2.c (Created: 20-Jan-1994)
 * created from HAWIN source file
 * cmprs2.c -- Routines to implement data decompression
 *
 *	Copyright 1989,1994 by Hilgraeve Inc. -- Monroe, MI
 *	All rights reserved
 *
 *	$Revision: 1.1 $
 *	$Date: 1994/01/21 10:53:32 $
 */
#include <windows.h>

#include <tdll\stdtyp.h>

#if !defined(BYTE)
#define	BYTE	unsigned char
#endif

#include "cmprs.h"
#include "cmprs.hh"

#if SHOW
//	#include <stdio.h>
#endif

/* * * * * * * * * * * * * *
 * Decompression routines  *
 * * * * * * * * * * * * * */

typedef struct s_dcmp_node DCMP_NODE;
struct s_dcmp_node
	{
	DCMP_NODE *pstLinkBack;
	DCMP_NODE *pstLinkFwd;
	BYTE ucChar;
	};

#define NODE_CAST DCMP_NODE *

DCMP_NODE *pstDcmpTbl;			// pointer to lookup table
DCMP_NODE *pstCode = NULL;		// used to scan table for output

int (**ppfDcmpPutfunc)(void *, int); /* ptr. to ptr. to function used
											   by calling func */
int (*pfDcmpPutChar)(void *, int);	/* ptr. to function used
											   internally to get data */

void *pPsave;

DCMP_NODE *pstTblLimit = NULL;	/* pointer to table beyond 1st 256 nodes */
DCMP_NODE *pstExtraNode = NULL;	/* pointer to additional node used in spec. case */
int	 fDcmpError;					/* set TRUE if illegal code is received */
int	 fStartFresh = FALSE;
unsigned int	 usCodeMask;					/* mask to isolate varible sized codes */
unsigned int	 usOldCode; 					/* last code received */
int mcFirstChar;					/* final character of pattern readout, actually,
										   the FIRST character of pattern (characters
										   are read out in reverse order */

// #pragma optimize("lgea",on)

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * FUNCTION: decompress_start
 *
 * DESCRIPTION:
 *
 *
 * ARGUMENTS:
 *
 *
 * RETURNS:
 *
 */
int decompress_start(int (**put_func)(void *, int),
					void *pP,
					int fPauses)
	{
	unsigned int usCount;
	DCMP_NODE *pstTmp;

	if (!compress_enable())
		return(FALSE);

	pPsave = pP;

	fFlushable = fPauses;

	// Due to the use of based pointers, we must use compress_tblspace + 1
	//	in the following code. Otherwise, node 0 (which could have an offset
	//	of 0 looks like a NULL pointer.
	// pstDcmpTbl = (DCMP_NODE *)(OFFSETOF(compress_tblspace) + 1);
	pstDcmpTbl = (DCMP_NODE *)(compress_tblspace);

	pstCode = NULL;
	pstExtraNode = (NODE_CAST)&pstDcmpTbl[MAXNODES];   /* last node */
	pstExtraNode->pstLinkFwd = NULL;
	pstTblLimit = (NODE_CAST)&pstDcmpTbl[256];
	for (usCount = 0, pstTmp = pstDcmpTbl; usCount < 256; ++usCount)
		{
		pstTmp->ucChar = (BYTE)usCount;
		++pstTmp;
		}

	ulHoldReg = 0;
	sBitsLeft = 0;
	sCodeBits = 9;
	usMaxCode = 512;
	usCodeMask = (1 << sCodeBits) - 1;
	usFreeCode = FIRSTFREE;
	fDcmpError = FALSE;
	fStartFresh = FALSE;
	ppfDcmpPutfunc = put_func;
	pfDcmpPutChar = *ppfDcmpPutfunc;
	*ppfDcmpPutfunc = dcmp_putc;
	usxCmprsStatus = COMPRESS_ACTIVE;
	#if SHOW
		printf("D                         decompress_start, sCodeBits=%d\n",
				sCodeBits);
	#endif
	return(TRUE);
	}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * FUNCTION: decompress_error
 *
 * DESCRIPTION:
 *
 *
 * ARGUMENTS:
 *
 *
 * RETURNS:
 *
 */
int decompress_error(void)
	{
	return(fDcmpError);
	}


/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * FUNCTION: dcmp_start
 *
 * DESCRIPTION:
 *
 *
 * ARGUMENTS:
 *
 *
 * RETURNS:
 *
 */
int dcmp_start(void *pX, int mcStartChar)
	{
	unsigned int usCode;

	ulHoldReg |= ((unsigned long)mcStartChar << sBitsLeft);
	sBitsLeft += 8;
	#if SHOW
		printf("D %02X         %08lX,%2d  dcmp_start\n", mcStartChar,
				ulHoldReg, sBitsLeft);
	#endif
	if (sBitsLeft >= sCodeBits)
		{
		usCode = (unsigned int)ulHoldReg & usCodeMask;
		ulHoldReg >>= sCodeBits;
		sBitsLeft -= sCodeBits;
		#if SHOW
			printf("D >> %03X     %08lX,%2d  sCodeBits=%d dcmp_start\n",
					usCode, ulHoldReg, sBitsLeft, sCodeBits);
		#endif
		/* Table has just been cleared, code must be in range of 0 - 255 */
		if (!IN_RANGE((INT)usCode, 0, 255))
			{
			#if SHOW
				printf("D >> %03X                  ERROR: out of range\n",
						usCode);
			#endif
			return(dcmp_abort());
			}
		else
			{
			#if SHOW
				printf("D        %02X               dcmp_start\n", usCode);
			#endif
			mcStartChar = (*pfDcmpPutChar)(pPsave, mcFirstChar =
					(INT)(usOldCode = usCode));
			*ppfDcmpPutfunc = dcmp_putc;
			}
		}
	return(mcStartChar);
	}



/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * FUNCTION: dcmp_putc
 *
 * DESCRIPTION:
 *
 *
 * ARGUMENTS:
 *
 *
 * RETURNS:
 *
 */
int dcmp_putc(void *pX, int mcInput)
	{
	unsigned int usCode;
	unsigned int usInCode;
	int  mcPutResult;
	DCMP_NODE *pstTmp;

	ulHoldReg |= ((unsigned long)mcInput << sBitsLeft);
	sBitsLeft += 8;
	#if SHOW
		printf("D %02X         %08lX,%2d\n", mcInput, ulHoldReg, sBitsLeft);
	#endif
	if (sBitsLeft >= sCodeBits)
		{
		usCode = (unsigned int)ulHoldReg & usCodeMask;
		ulHoldReg >>= sCodeBits;
		sBitsLeft -= sCodeBits;
		#if SHOW
			printf("D >> %03X     %08lX,%2d  sCodeBits=%d\n",
					usCode, ulHoldReg, sBitsLeft, sCodeBits);
		#endif
		if (usCode == STOPCODE)
			{
			if (!fFlushable)
				decompress_stop();
			else
				{
				// Pause in the data, leave lookup table intact but start
				//	receiving a fresh stream.
				sBitsLeft = 0;
				ulHoldReg = 0L;
				fStartFresh = TRUE;
				#if SHOW
					printf("D            %08lX,%2d  setting fFreshStart\n",
							ulHoldReg, sBitsLeft);
				#endif
				}
			}
		else if (usCode == CLEARCODE)
			{
			sCodeBits = 9;
			usMaxCode = 512;
			usCodeMask = (1 << sCodeBits) - 1;
			usFreeCode = FIRSTFREE;
			*ppfDcmpPutfunc = dcmp_start;
			#if SHOW
				printf("D                         CLEARCODE, sCodeBits=%d\n",
						sCodeBits);
			#endif
			}
		else if (usCode > (unsigned int)usFreeCode)
			{
			#if SHOW
				printf("D                         ERROR: usCode > usFreeCode of %03X\n",
						usFreeCode);
			#endif
			return(dcmp_abort());
			}
		else
			{
			pstCode = (NODE_CAST)&pstDcmpTbl[usInCode = usCode];
			if (usCode == usFreeCode)  /* spec. case k<w>k<w>k */
				{
				pstCode = (NODE_CAST)&pstDcmpTbl[usCode = usOldCode];
				pstExtraNode->ucChar = (BYTE)mcFirstChar;
				pstCode->pstLinkFwd = pstExtraNode;
				#if SHOW
					printf("D                         Special case: k<w>k<w>k\n");
				#endif
				}
			else
				pstCode->pstLinkFwd = NULL;
			while(pstCode > pstTblLimit)
				{
				pstCode->pstLinkBack->pstLinkFwd = pstCode;
				pstCode = pstCode->pstLinkBack;
				}
			mcFirstChar = pstCode->ucChar;

			if (!fStartFresh)
				{
				#if SHOW
					printf("D                         D Added %03X = %03X + %02X\n",
							usFreeCode, usOldCode, mcFirstChar);
				#endif
				if (usFreeCode < MAXNODES)
					{
					pstTmp = (NODE_CAST)&pstDcmpTbl[usFreeCode++];
					pstTmp->ucChar = (BYTE)mcFirstChar;
					pstTmp->pstLinkBack = (NODE_CAST)&pstDcmpTbl[usOldCode];
					}
				}
			fStartFresh = FALSE;

			usOldCode = usInCode;
			if (usFreeCode >= usMaxCode && sCodeBits < MAXCODEBITS)
				{
				++sCodeBits;
				usCodeMask = (1 << sCodeBits) - 1;
				usMaxCode *= 2;
				#if SHOW
					printf("D                         D New sCodeBits = %d\n",
							sCodeBits);
				#endif
				}

			while (pstCode != NULL)
				{
				#if SHOW
					printf("D        %02X               ", pstCode->ucChar);
				#endif
				if ((mcPutResult = (*pfDcmpPutChar)(pPsave, pstCode->ucChar)) < 0)
					{
					if (mcPutResult == DCMP_UNFINISHED)
						{
						#if SHOW
							printf("Interrupted");
						#endif
						pstCode = pstCode->pstLinkFwd;	 //  to pick up later
						mcInput = DCMP_UNFINISHED;
						break;
						}
					else
						{
						#if SHOW
							printf("ERROR: putc returned -1");
						#endif
						pstCode = NULL;
						mcInput = ERROR;
						break;
						}
					}
				#if SHOW
					printf("\n");
				#endif
				pstCode = pstCode->pstLinkFwd;
				}
			}
		}
	return(mcInput);
	}


/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * FUNCTION: decompress_continue
 *
 * DESCRIPTION:
 *	Needed for compression in remote control. Picks up expansion of an
 *	output string after it has been interrupted.
 *
 * ARGUMENTS:
 *
 *
 * RETURNS:
 *
 */
int decompress_continue(void)
	{
	int mcPutResult;
	int mcRetCode;

	// Deliver an initial unfinished code so routines downstream can pick
	//	up midstream if necessary
	if ((*pfDcmpPutChar)(pPsave, DCMP_UNFINISHED) == DCMP_UNFINISHED)
		return DCMP_UNFINISHED;

	// Now continue delivering any remaining expansion codes unless
	//	interrupted again
	while (pstCode != NULL)
		{
		if ((mcPutResult = (*pfDcmpPutChar)(pPsave, pstCode->ucChar)) < 0)
			{
			if (mcPutResult == DCMP_UNFINISHED)
				{
				pstCode = pstCode->pstLinkFwd;	 //  to pick up later
				mcRetCode = DCMP_UNFINISHED;
				break;
				}
			else
				{
				pstCode = NULL;
				mcRetCode = ERROR;
				break;
				}
			}
		pstCode = pstCode->pstLinkFwd;
		}

	return mcRetCode;
	}


/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * FUNCTION: dcmp_abort
 *
 * DESCRIPTION:
 *
 *
 * ARGUMENTS:
 *
 *
 * RETURNS:
 *
 */
int dcmp_abort(void)
	{
	/* print error message or whatever */
	fDcmpError = TRUE;
	decompress_stop();
	return(ERROR);
	}


/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * FUNCTION: decompress_stop
 *
 * DESCRIPTION:
 *
 *
 * ARGUMENTS:
 *
 *
 * RETURNS:
 *
 */
void decompress_stop(void)
	{
	#if SHOW
		printf("D                         Decompress_stop\n");
	#endif
	if (ppfDcmpPutfunc != NULL)
		{
		*ppfDcmpPutfunc = pfDcmpPutChar;
		ppfDcmpPutfunc = NULL;
		}
	usxCmprsStatus = COMPRESS_IDLE;
	}


/* end of cmprs2.c */