#ident "$Id: lzsd.c,v 1.1 1995/03/16 17:08:28 garyp Exp $"
/*****************************************************************************
 * Copyright (c) 1988-95 Digi International Inc., All Rights Reserved
 * 
 * This  software  contains  proprietary  and  confidential   information   of
 * Digi International  Inc.   By  accepting  transfer  of this copy, Recipient
 * agrees to retain this software in  confidence,  to  prevent  disclosure  to
 * others,  and  to make no  use of this software other than that for which it
 * was delivered.  This is an unpublished copyright work  Digi   International
 * Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is
 * strictly prohibited.
 * 
 * Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc.
 * Confidential  when   combined   with   the   aggregated  modules  for  this
 * product) OBJECT CODE ONLY SOURCE MATERIALS
 * 
 * (C) COPYRIGHT Digi International Inc. 1995
 * 
 * All Rights Reserved
 * 
 * US   Government   Users   Restricted   Rights   -   Use,   duplication   or
 * disclosure  restricted by GSA ADP Schedule Contract with Digi International
 * Inc.
 * 
 *****************************************************************************/

/*----------------------------------------------------------------------*/
/* LZS221-C Version 4.0							*/
/*----------------------------------------------------------------------*/
/*  LZSD.C - Expand Lzs(TM) tokens - Bookkeeper Layer		   	*/
/*									*/
/*  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  */
/*  !!                                                              !!	*/
/*  !!  NOTE:                                                       !!	*/
/*  !!  The contents of this document constitute                    !!	*/
/*  !!           CONFIDENTIAL INFORMATION                           !!	*/
/*  !!  		(C) Stac Electronics 1994.                  !!	*/
/*  !!					                            !!	*/
/*  !!      Including one or more U.S. Patents:			    !!	*/
/*  !!		No. 4701745, 5016009, 5126739 and 5146221 and       !!	*/
/*  !!                  other pending patents.               	    !!	*/
/*  !!                                                              !!	*/
/*  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!	*/
/*									*/
/*----------------------------------------------------------------------*/
/*									*/
/* Copyright 1988-94 Stac Electronics, Carlsbad, California.		*/
/* All rights reserved.  This code is Stac confidential property, and	*/
/* the algorithms and methods used herein may not be disclosed to any 	*/
/* party without express written consent from Stac Electronics.		*/
/*									*/
/*----------------------------------------------------------------------*/
/* All Stac product names are trademarks or registered trademarks	*/
/* of Stac Electronics.							*/
/*----------------------------------------------------------------------*/
#define Get_LZS_Optioms true
#include "lzs.h"
#define MAX_BLOCK_SIZE_TYPE long
#define SIZEOF_tOrigByteIndex 4
#define MAX_BUFFR_SIZE_TYPE long
#if defined (LZS_FAST)
#define _FAST_
#define _Small_Blocks_
#define MAX_ORIG_INDEX OrigCnt
#undef LZS_SMALL_BUFFERS
#define LZS_SMALL_BUFFERS
#endif
#if defined (_BIG_CMPRS_BFR_)
#define MAX_ORIG_CNT_FAST 57343U
#else
#define _EARLY_OUT_ 1
#define MAX_ORIG_CNT_FAST 63487U
#endif
#define MAX_INDEX	~((tOrigByteIndex) 0)
#if !defined MAX_ORIG_INDEX
#define MAX_ORIG_INDEX \
		((flags & LZS_SOURCE_FLUSH) ? OrigCnt : MAX_INDEX)
#endif
#if defined (_Small_Blocks_)
#undef MAX_BLOCK_SIZE_TYPE
#undef SIZEOF_tOrigByteIndex
#define MAX_BLOCK_SIZE_TYPE 	short
#define SIZEOF_tOrigByteIndex 	2
#endif
#if defined (LZS_SMALL_BUFFERS)
#undef MAX_BUFFR_SIZE_TYPE
#define MAX_BUFFR_SIZE_TYPE 	short
#define MAX_BUFFER_SIZE 	0xFFFFU
#endif
typedef unsigned MAX_BUFFR_SIZE_TYPE tMaxBfrSz;
typedef unsigned MAX_BLOCK_SIZE_TYPE tOrigByteIndex;
#define FP_Off(fp) fp
#define dREG
#define pREG register
#define	MEM_COPY_S(pDest, pSrc, nbytes) (mem_copy_r (pDest, pSrc, nbytes))
#define	COPY_PREV_STRING(pDest, wOffset, nbytes) \
		copy_prev_string_r (pDest, wOffset, nbytes)
#define  COPY_TO_STRING_END COPY_PREV_STRING
#define	MEM_SET_S(pDest, Char, nbytes) mem_set_r (pDest, Char, nbytes)
#define RAW_TKN_SZ 		9
#define EOCD_TKN_SZ 		(2+7)
#define KEY_LENGTH 		2
#define MAX_OFFSET 	 	0x7FFU
#define SPAN_CMPRS_BFR 		0x80
#define SPLITTING_STRING	0x40
#define SPLITTING_UNIQUE_BYTE	0x20
#define SPLITTING_ENDS		0x10
#define SPAN_ORIG_BFR		0x08
#define ORIG_BLK_START		0x04
#define ENGN_SOURCE_EXHAUSTED 	LZS_SOURCE_EXHAUSTED
#define INIT_HASH		0x02
#define NOT_ORIG_BFR		0x04
#define MAX_LOOK_AHEAD 		4U
#define DUM_DUM_SZ 		(2U*(MAX_OFFSET)+MAX_LOOK_AHEAD)
#if !defined (LZS_FAR)
#define LZS_FAR
#endif
#define MAX_OVERRUN 		16U
#define DUMDUM_SZ 		(2*(MAX_OFFSET+1) + MAX_OVERRUN)
struct RestartData {
	tMaxBfrSz      len;
	unsigned short bits2discard;
	unsigned short offset;
	unsigned short Sign;
	unsigned char  engnState;
};
struct SaveData {
	unsigned char LZS_FAR *pTinyBfrBrk;
	unsigned char LZS_FAR *pExpBfrBase;
	unsigned short	       CmpByteSaved;
	unsigned short 	       ExpByteSaved;
	struct RestartData     restart;
	unsigned char	       fState;
#if !defined (_FAST_)
	unsigned char SaveCmpBytes[7];
	unsigned char SaveExpBytes[DUMDUM_SZ];
#endif
};
#define	LZS_STOPPED_AT_TOKEN		0x01
#define	LZS_STOPPED_GETTING_LEN		0x02
#define	SPANNING_CMPRS_BFRS 		0x04
#define	LZS_OVERRUNNN			0x08
#define	TINY_EXPAND_BFR			0x10
#define	LZS_EXPANDING_STR 		0x20
#define	SAVED_CMPRS_BYTES		0x40
#define	LZS_END_OF_CMPRS_DATA		0x80
#define ENGN_STOPPED_AT_TOKEN		LZS_STOPPED_AT_TOKEN
#define	ENGN_STOPPED_GETTING_LEN	LZS_STOPPED_GETTING_LEN
#define	ENGN_PARTIAL_TOKEN       	0x04
#define ENGN_OVERRUNNN 			LZS_OVERRUNNN
#define	ENGN_EXPANDING_STR 		LZS_EXPANDING_STR
#define	ENGN_NEW_BFR             	0x40
#define	ENGN_END_OF_CMPRS_DATA		LZS_END_OF_CMPRS_DATA
unsigned int LZS_FAR Expand_Lzs_drEngine(
	unsigned char LZS_FAR * LZS_FAR *ppCmprsBfr,
	unsigned char LZS_FAR		*pCmprsBfrEnd,
	unsigned char LZS_FAR * LZS_FAR *ppExpBfr,
	unsigned char LZS_FAR		*pExpBfrOverEnd,
	struct RestartData LZS_FAR 	*pRestartData
);
unsigned int LZS_FAR Expand_Null_Engine(
	unsigned char LZS_FAR * LZS_FAR *ppCmprsBfr,
	unsigned char LZS_FAR		*pCmprsBfrEnd,
	unsigned char LZS_FAR * LZS_FAR *ppExpBfr,
	unsigned char LZS_FAR		*pExpBfrOverEnd,
	struct RestartData LZS_FAR 	*pRestartData
);
#define tExpndWrkSpcPtr struct SaveData
#ifndef	_Lzs_Mem_R_H
#define	_Lzs_Mem_R_H
	unsigned char LZS_FAR * LZS_FAR mem_copy_r (
		void LZS_FAR 	*pDestn,
		void LZS_FAR 	*pSource,
		unsigned short   wNbytes
	);
	unsigned char LZS_FAR * LZS_FAR copy_prev_string_r (
		unsigned char LZS_FAR *pDestn,
		unsigned short 	       wOffset,
		tMaxBfrSz    	       Nbytes
	);
#endif
void LZS_FAR LZS_InitDecompressionHistory(void LZS_FAR *history);
unsigned int EocdToken(
	unsigned char LZS_FAR	  **ppCmprsBfr,
	unsigned char LZS_FAR	   *pCmprsBfrEnd,
	struct RestartData LZS_FAR *pRestartData
);
unsigned short LZS_FAR LZS_Decompress(
	unsigned char LZS_FAR * LZS_FAR *source,
	unsigned char LZS_FAR * LZS_FAR *dest,
	unsigned long LZS_FAR           *sourceCnt,
	unsigned long LZS_FAR           *destCnt,
	void          LZS_FAR           *history,
	unsigned short                   flags
);
#define EXP_SIGNATURE 0x1234
static void Init_Expand_Data(
	struct RestartData LZS_FAR *pRestartData
)
{
	pRestartData->bits2discard = 0;
	pRestartData->Sign 	   = EXP_SIGNATURE;
	pRestartData->engnState    = ENGN_NEW_BFR;
	return;
}
void LZS_FAR LZS_InitDecompressionHistory(
	void LZS_FAR *history	
)
{
	struct SaveData LZS_FAR *p_temp ;
	p_temp = history ;
	Init_Expand_Data( (struct RestartData LZS_FAR *)
		(&(*(struct SaveData LZS_FAR *)p_temp).restart) );
#if !defined (_FAST_)
	p_temp->pTinyBfrBrk = &p_temp->SaveExpBytes[0];
#endif
	p_temp->ExpByteSaved = (p_temp->CmpByteSaved = 0);
	p_temp->fState = 0;
	return;
}
unsigned short LZS_FAR LZS_SizeOfDecompressionHistory (void)
{
	return (sizeof (struct SaveData));
}
#include "lzs.h"
#define S_FIELD 0x80000000UL  
#define T_FIELD	0x40000000L   
unsigned int LZS_FAR Expand_Lzs_drEngine(
	unsigned char LZS_FAR * LZS_FAR *ppCmprsBfr,
	unsigned char LZS_FAR	   	*pCmprsBfrEnd,
	unsigned char LZS_FAR * LZS_FAR *ppExpBfr,
	unsigned char LZS_FAR	   	*pExpBfrEnd,
	struct RestartData LZS_FAR 	*pRestartData
)
{
	dREG unsigned long BitStream;	
					
	pREG unsigned char LZS_FAR 	*pCmprsBfr;
	pREG unsigned char LZS_FAR 	*pExpBfr;
	pREG unsigned char LZS_FAR 	*pExpBfrOverRun;
	pREG unsigned char LZS_FAR 	*pSi;
	dREG tMaxBfrSz len;			
	     tMaxBfrSz LengthReg;		
						
	dREG unsigned short bits2discard;	
						
	dREG unsigned short offset;		
						
#if !defined (_FAST_)
	unsigned int    ndx;		
#endif
	
	pCmprsBfr = pCmprsBfrEnd;	
	pCmprsBfr = *ppCmprsBfr;
	pExpBfr   = *ppExpBfr;
	pExpBfrOverRun = (pExpBfrEnd - pExpBfr < MAX_OVERRUN) ?
				       pExpBfr:
				       pExpBfrEnd - (MAX_OVERRUN-1);
	bits2discard = pRestartData->bits2discard;
#if defined (_FAST_)
	
	
	bits2discard = 0;
#else
	offset = pRestartData->offset;
	len = pRestartData->len;
	pCmprsBfr += (bits2discard >> 3);
	bits2discard &= 7;
	LengthReg = (tMaxBfrSz)(FP_Off(pCmprsBfrEnd) - FP_Off(pCmprsBfr));
	
	if (LengthReg < 4) {	
		BitStream = 0;
		bits2discard += 4*8;
		pCmprsBfr -= 4;
		for (ndx = 0 ; (ndx < LengthReg); ndx++) {
			bits2discard -= 8;
			pCmprsBfrEnd --;
			pCmprsBfr ++;
			BitStream |=    ((unsigned long)*pCmprsBfrEnd)
				     << (ndx<<3);
			
		}
		BitStream <<= bits2discard;
		if (bits2discard > 32-9)
			goto No_Mas;
		if (pRestartData->engnState & (  ENGN_NEW_BFR
					       | ENGN_STOPPED_AT_TOKEN
					       | ENGN_OVERRUNNN) ) {
			if ( !(BitStream & S_FIELD) ) {
				
				*pExpBfr++ = (unsigned char)
					(BitStream >> (16-RAW_TKN_SZ+16));
					
				bits2discard += RAW_TKN_SZ;
				goto Fill_Bit_Stream;
				
			}
			if (bits2discard < (32-(2+11+2)))
				goto Have_A_String;
				
			if (BitStream & T_FIELD) {	
				if (bits2discard < (32-(2+7+2)) )
					goto Stng_Off7;
					
					
					
				if (0 == (( (unsigned short)
					(BitStream >> (23))) & 0x007F) ) {
					bits2discard += EOCD_TKN_SZ;
					goto Done;
				}
			}
			goto No_Mas;
		}
	if (pRestartData->engnState & ENGN_EXPANDING_STR)
		goto Room_4_String;
	if (len < 8)
		goto Get_Len_Single_byte_restart;
	goto Get_Len_Too_Single_byte_restart;
	}
	pCmprsBfrEnd -= 3;	
	if (pRestartData->engnState & (  ENGN_NEW_BFR
				       | ENGN_STOPPED_AT_TOKEN
				       | ENGN_OVERRUNNN) )
		goto Fill_Bit_Stream;
	if (pRestartData->engnState & ENGN_EXPANDING_STR)
		goto Room_4_String;
	if (len < 8)
		goto Get_Len;
	goto Get_Len_Too;
#endif
Fill_Bit_Stream:
	
	
	pCmprsBfr += (bits2discard >> 3);
	bits2discard &= 7;
#if !defined (_FAST_)
	if (pCmprsBfr >= pCmprsBfrEnd)
		goto Cmprs_End;
#endif
	if (pExpBfr >= pExpBfrOverRun)
		goto In_OverRun_Zone;
#if (!defined (LZS_ALIGNED) || (LZS_BYTE_ORDER == LZS_LITTLE_END))
	BitStream =( (((unsigned long) *(pCmprsBfr  )) << 3*8) |
		     (((unsigned long) *(pCmprsBfr+1)) << 2*8) |
		     (((unsigned long) *(pCmprsBfr+2)) << 1*8) |
		       (unsigned long) *(pCmprsBfr+3)) << bits2discard;
#else
	BitStream = *(unsigned long *)pCmprsBfr << bits2discard;
#endif
	if ( !(BitStream & S_FIELD) ) {
		*pExpBfr++ = (unsigned char)(BitStream >> (32-RAW_TKN_SZ));
		bits2discard += RAW_TKN_SZ;
		if ( !(BitStream & (S_FIELD >> RAW_TKN_SZ) ) ) {
			*pExpBfr++ = (unsigned char)
				(BitStream >> (32-2*RAW_TKN_SZ));
			bits2discard += RAW_TKN_SZ;
			if (bits2discard > (32-RAW_TKN_SZ))
				goto Fill_Bit_Stream;
			if (!(BitStream & (S_FIELD >> 2*RAW_TKN_SZ) ) ) {
				*pExpBfr++ = (unsigned char)
					(BitStream >> (32-3*RAW_TKN_SZ));
				bits2discard += RAW_TKN_SZ;
				goto Fill_Bit_Stream;
			}
			if (!(BitStream & (T_FIELD>>(2*RAW_TKN_SZ))))
				goto Fill_Bit_Stream;
			if (bits2discard > (32-(2+7+2)))
				goto Fill_Bit_Stream;
			BitStream = BitStream << 2*RAW_TKN_SZ;
			goto Stng_Off7;
		}
		BitStream = BitStream << RAW_TKN_SZ;
	}
Have_A_String:
	if (BitStream & T_FIELD) {
Stng_Off7:
		bits2discard += (2+7);
		offset = (((unsigned short)(BitStream >> 23)) & 0x007FU);
		if (offset == 0)
			goto Done;
		BitStream   <<= (2+7) ;
	}
	else {
		offset = ((unsigned short)(BitStream >> (19)) & 0x07FF);
		BitStream   <<= (2+11) ;
		bits2discard += (2+11) ;
	}
	if (BitStream < 0xC0000000UL) {
		pSi = pExpBfr - offset ;
		*pExpBfr = *pSi;
		*(pExpBfr+1) = *(pSi+1);
		pExpBfr += 2;
		if (BitStream & 0x40000000UL) {
			*pExpBfr++ = *(pSi+2);
		}
		else if (BitStream & 0x80000000UL) {
			*pExpBfr = *(pSi+2);
			*(pExpBfr+1) = *(pSi+3);
			pExpBfr += 2;
		}
		bits2discard += 2;
		if ( (BitStream & (S_FIELD>>2) ) &&
			 (bits2discard <= (32-(2+7+2)) ) ) {
			BitStream <<= 2;
			if (BitStream & T_FIELD)
				goto Stng_Off7;
			if (bits2discard <= (32-(2+11+2)) )
				goto Have_A_String;
		}
		goto Fill_Bit_Stream;
	}
#if !defined (_FAST_)
	Get_Len_Single_byte_restart:
#endif
	if (bits2discard > 28) {
		len = 0;
		pCmprsBfr += (bits2discard >> 3);
		bits2discard &= 7;
#if !defined (_FAST_)
		if (pCmprsBfr >= pCmprsBfrEnd)
			goto Cmprs_End_Getting_Len;
Get_Len:
#endif
#if (!defined (LZS_ALIGNED) || (LZS_BYTE_ORDER == LZS_LITTLE_END))
		BitStream =(
			(((unsigned long) *(pCmprsBfr  )) << 3*8) |
			(((unsigned long) *(pCmprsBfr+1)) << 2*8) |
			(((unsigned long) *(pCmprsBfr+2)) << 1*8) |
			  (unsigned long) *(pCmprsBfr+3)) << bits2discard;
#else
		BitStream = *(unsigned long *)pCmprsBfr << bits2discard;
#endif
		}
		if (BitStream < 0xF0000000UL)  {
			len = 5 + (unsigned short)((BitStream >> (28)) & 3);
			goto Copy_String;
		}
		len = (tMaxBfrSz) - 7;
		do {
			len += 15;
			BitStream <<= 4;
			bits2discard += 4;
#if !defined (_FAST_)
Get_Len_Too_Single_byte_restart:
#endif
			if (bits2discard > 28) {
				pCmprsBfr += (bits2discard >> 3);
				bits2discard &= 7;
#if !defined (_FAST_)
				if (pCmprsBfr >= pCmprsBfrEnd)
					goto Cmprs_End_Getting_Len_Too;
Get_Len_Too:
#endif
#if (!defined (LZS_ALIGNED) || (LZS_BYTE_ORDER == LZS_LITTLE_END))
				BitStream =(
				    (((unsigned long) *(pCmprsBfr  )) << 3*8) |
				    (((unsigned long) *(pCmprsBfr+1)) << 2*8) |
				    (((unsigned long) *(pCmprsBfr+2)) << 1*8) |
				      (unsigned long) *(pCmprsBfr+3)) <<
					bits2discard;
#else
				BitStream = *(unsigned long *)pCmprsBfr <<
					bits2discard;
#endif
			}
		} while (BitStream >= 0xF0000000UL);
	len += (unsigned short)(BitStream >> (4 + 8 + 16));
Copy_String:
	BitStream <<= 4;
	bits2discard += 4;
#if !defined (_FAST_)
    Room_4_String:
#endif
	if (len <= (tMaxBfrSz)(FP_Off(pExpBfrEnd) - FP_Off(pExpBfr))) {
		
		pExpBfr = COPY_TO_STRING_END(pExpBfr, offset, len);
		goto Fill_Bit_Stream;   
	}
	
	LengthReg = (tMaxBfrSz)(pExpBfrEnd - pExpBfr);
	pExpBfr = COPY_TO_STRING_END(pExpBfr, offset, LengthReg);
	len -= LengthReg;
	pRestartData->engnState = ENGN_EXPANDING_STR;
	goto UpDateVIPcount;
#if !defined (_FAST_)
No_Mas:
	pRestartData->engnState |= ENGN_PARTIAL_TOKEN;
	goto UpDateVIPcount;
Cmprs_End_Getting_Len_Too:
Cmprs_End_Getting_Len:
	pRestartData->engnState = ENGN_STOPPED_GETTING_LEN;
	goto Report_Results;
Cmprs_End:
	pRestartData->engnState = ENGN_STOPPED_AT_TOKEN;
	goto Report_Results;
#endif	
In_OverRun_Zone:
	pRestartData->engnState = ENGN_OVERRUNNN;
	goto Report_Results;
Done:	
	
	pRestartData->engnState = ENGN_END_OF_CMPRS_DATA;
	bits2discard += (8 - (bits2discard & 7)) & 7;
UpDateVIPcount:
	pCmprsBfr += (bits2discard >> 3);
	bits2discard &= 7;	
Report_Results:
#if !defined (_FAST_)
	pRestartData->bits2discard = bits2discard;
	pRestartData->offset 	   = offset;
	pRestartData->len 	   = len;
#endif
	*ppCmprsBfr = pCmprsBfr;
	*ppExpBfr   = pExpBfr;
	return (pRestartData->engnState);
}
unsigned int EocdToken(
	unsigned char LZS_FAR	  **ppCmprsBfr,
	unsigned char LZS_FAR	   *pCmprsBfrEnd,
	struct RestartData LZS_FAR *pRestartData
)
{
	unsigned char LZS_FAR	   *p_Peaker;
	p_Peaker = *ppCmprsBfr;
	if (pCmprsBfrEnd < (p_Peaker + 2U))
		return(ENGN_PARTIAL_TOKEN);
	if (0xC000U == (((   (p_Peaker[0] << 8) | p_Peaker[1])
			  << pRestartData->bits2discard) & 0xFF80U ) ) {
		pRestartData->bits2discard += EOCD_TKN_SZ;
		*ppCmprsBfr += pRestartData->bits2discard >> 3;
		pRestartData->bits2discard &= 7;
		if (pRestartData->bits2discard)
			(*ppCmprsBfr)++;
		return(ENGN_END_OF_CMPRS_DATA);
	}
	return(0);
}
unsigned short LZS_FAR	LZS_Decompress(
	unsigned char LZS_FAR * LZS_FAR *source, 
	unsigned char LZS_FAR * LZS_FAR *dest,   
	unsigned long LZS_FAR	*sourceCnt,	 
	unsigned long LZS_FAR	*destCnt,	 
	void          LZS_FAR	*history,	 
	unsigned short 		 wOption	 
)
{
	unsigned char LZS_FAR 	*pExpandBuffer;
	unsigned char LZS_FAR 	*pExpBfrEnd;
	unsigned char LZS_FAR 	*pExpBfr;
	unsigned char LZS_FAR 	*pCmprsBfr;
	unsigned char LZS_FAR 	*pCmprsBfrEnd;
#if !defined (_FAST_)
	unsigned char LZS_FAR 	*pExpBfrStart = 0;
	unsigned char LZS_FAR 	*pExpStopped  = 0;
	unsigned char LZS_FAR 	*pTemp;
#endif
	tMaxBfrSz	bytes_left;	
					
#if !defined (_FAST_)
	tMaxBfrSz 	bytes_saved;	
	tMaxBfrSz       LengthReg;	
					
	unsigned short	wDataReg;	
#endif
	unsigned short	ret_code = 0;
	unsigned int	engn_return_code = 0;
	tExpndWrkSpcPtr LZS_FAR	*p_temp;
	p_temp = history ;
	pCmprsBfr = *source;
	pCmprsBfrEnd = pCmprsBfr + ((tMaxBfrSz) *sourceCnt);
#if defined (LZS_SMALL_BUFFERS)
	if ((*sourceCnt > MAX_BUFFER_SIZE) || (*destCnt > MAX_BUFFER_SIZE))
		   return (LZS_INVALID) ;
#endif
	if (wOption & (~(  LZS_SAVE_HISTORY
			 | LZS_UNCOMPRESSED  ) ))
		return (LZS_INVALID);
	bytes_left = (tMaxBfrSz) *destCnt;
	if (bytes_left == 0)
		return (LZS_DEST_EXHAUSTED);
	if (p_temp->restart.Sign != EXP_SIGNATURE)
		return (LZS_INVALID);
	pExpBfr = pExpandBuffer = *dest;
	
	pExpBfrEnd = pExpBfr + bytes_left;
	if (p_temp->fState == 0) {
		if (*sourceCnt == 0)
			return (LZS_SOURCE_EXHAUSTED);
		if (*sourceCnt  < 3)  
			return (LZS_INVALID);
		p_temp->pExpBfrBase = pExpandBuffer;
		goto Resume_Parsing;
	}
#if !defined (_FAST_)
	if (   (wOption & LZS_UNCOMPRESSED)
	    && (p_temp->fState & ~TINY_EXPAND_BFR))
		return (LZS_INVALID);
	if (p_temp->fState & TINY_EXPAND_BFR) {
		p_temp->pExpBfrBase = pExpandBuffer;
		bytes_saved = p_temp->ExpByteSaved;
		
		if (p_temp->fState & LZS_EXPANDING_STR) {
Expand_String:
			pTemp = p_temp->pTinyBfrBrk + bytes_saved;
			
			
			wDataReg = (unsigned short)
				(&p_temp->SaveExpBytes[DUMDUM_SZ] - pTemp);
			if (wDataReg < p_temp->restart.len) {
				
				p_temp->restart.len -= wDataReg;
			}
			else {
				wDataReg = (unsigned short)
					p_temp->restart.len;
				p_temp->fState &= ~LZS_EXPANDING_STR;
				p_temp->restart.engnState |=
					ENGN_STOPPED_AT_TOKEN;
					
			}
			if (wDataReg) {
				COPY_PREV_STRING(pTemp,
						 p_temp->restart.offset,
						 (tMaxBfrSz) wDataReg);
						 bytes_saved += wDataReg;
			}
		}	
		if (bytes_left <= bytes_saved) {
			ret_code |= LZS_DEST_EXHAUSTED;
			p_temp->fState &= ~LZS_OVERRUNNN;
			pExpBfr = MEM_COPY_S(pExpBfr,
					     p_temp->pTinyBfrBrk,
					     (unsigned short) bytes_left);
			p_temp->pTinyBfrBrk += bytes_left;
			p_temp->ExpByteSaved = (unsigned short)
				(bytes_saved - bytes_left);
			if (0 == p_temp->ExpByteSaved) {
				if (!(LZS_EXPANDING_STR & p_temp->fState)) {
				
				
					pTemp = &p_temp->SaveCmpBytes[0];
					if (  (EocdToken(&pTemp,
					       &p_temp->
						 SaveCmpBytes[p_temp->
						 CmpByteSaved],
					       (struct RestartData LZS_FAR *)
						 &p_temp->restart ))
					    & ENGN_END_OF_CMPRS_DATA)
						goto Report_EOD;
				}
			}
			goto Report_Results;
		}
		
		
		pExpBfr = MEM_COPY_S(pExpBfr,
				     p_temp->pTinyBfrBrk,
				     (unsigned short) bytes_saved);
		pExpStopped = pExpBfr;
		if (!(p_temp->fState & LZS_EXPANDING_STR)) {
			if (p_temp->fState & LZS_END_OF_CMPRS_DATA) {
				pExpBfr = pExpStopped;
				goto Report_EOD;
			}
		}
		wDataReg = (unsigned short) bytes_saved + (unsigned short)
			(p_temp->pTinyBfrBrk - &p_temp->SaveExpBytes[0]);
		if (wDataReg > MAX_OFFSET) {
			MEM_COPY_S(&p_temp->SaveExpBytes[0],
				&p_temp->SaveExpBytes[wDataReg-MAX_OFFSET],
				MAX_OFFSET);
			wDataReg = MAX_OFFSET;
		}
		bytes_left -= bytes_saved;
		bytes_saved = 0;
		p_temp->pTinyBfrBrk = &p_temp->SaveExpBytes[wDataReg];
		if (p_temp->fState & LZS_EXPANDING_STR)
			goto Expand_String;
		pExpBfrStart = pExpBfr = &p_temp->SaveExpBytes[wDataReg];
		pExpBfrEnd = &p_temp->SaveExpBytes[DUMDUM_SZ];
	}
	p_temp->fState &= ~SAVED_CMPRS_BYTES;
	if (p_temp->CmpByteSaved) {
		wDataReg = (*sourceCnt >= 4) ? 4 : (unsigned short)*sourceCnt;
		pCmprsBfrEnd = &p_temp->SaveCmpBytes[p_temp->CmpByteSaved];
		for ( ; (wDataReg > 0);	wDataReg--) {
			*pCmprsBfrEnd++ = *pCmprsBfr++;
			
		}
		pCmprsBfr = &p_temp->SaveCmpBytes[0];
		p_temp->fState |= SPANNING_CMPRS_BFRS;
	}
#endif	
Resume_Parsing:
	engn_return_code =
#if !defined (_FAST_)
		(wOption & LZS_UNCOMPRESSED) ?
		Expand_Null_Engine (&pCmprsBfr, pCmprsBfrEnd,
				    &pExpBfr,   pExpBfrEnd,
				    (struct RestartData LZS_FAR *)
					&p_temp->restart ) :
#endif	
		Expand_Lzs_drEngine (&pCmprsBfr, pCmprsBfrEnd,
				     &pExpBfr,   pExpBfrEnd,
				     (struct RestartData LZS_FAR *)
					&p_temp->restart );
#if defined (_FAST_)
	LZS_InitDecompressionHistory(p_temp);
	if (engn_return_code & ENGN_END_OF_CMPRS_DATA)
		 ret_code |= LZS_END_MARKER;
	else if (engn_return_code & (	ENGN_STOPPED_AT_TOKEN
				     || ENGN_PARTIAL_TOKEN
				     || ENGN_STOPPED_GETTING_LEN))
		ret_code |= LZS_SOURCE_EXHAUSTED;
	else
		ret_code |= LZS_DEST_EXHAUSTED;
	goto Report_Results;
#else
	if (engn_return_code & ENGN_END_OF_CMPRS_DATA)
		goto End_Of_Data;
	if (engn_return_code & ENGN_STOPPED_AT_TOKEN)
		goto Cmprs_Data_End;
	if (engn_return_code & ENGN_PARTIAL_TOKEN)
		goto Cmprs_Data_End_Partial;
	if (engn_return_code & ENGN_STOPPED_GETTING_LEN)
		goto Cmprs_End_Getting_Len;
	if (engn_return_code & ENGN_EXPANDING_STR)
		goto Expand_Bfr_2_small;
	if (p_temp->fState & LZS_OVERRUNNN) {
OverRunnn:
Fill_Expand_Buffer:
		p_temp->fState      &= ~LZS_OVERRUNNN;
		p_temp->pTinyBfrBrk  = pExpBfrStart + bytes_left;
		p_temp->ExpByteSaved = (unsigned short)
			(FP_Off(pExpBfr) - FP_Off(p_temp->pTinyBfrBrk));
		pExpBfr = MEM_COPY_S(pExpStopped, pExpBfrStart,
				     (unsigned short)bytes_left);
		p_temp->fState |= TINY_EXPAND_BFR;
Break_Cmprs_Bfr:
		ret_code |= LZS_DEST_EXHAUSTED;
		if (p_temp->fState & SPANNING_CMPRS_BFRS) {
			p_temp->fState &= ~SPANNING_CMPRS_BFRS;
			pCmprsBfr =  *source
				   + (unsigned short)((FP_Off(pCmprsBfr)
				     - FP_Off(&p_temp->SaveCmpBytes[0]))
				   - p_temp->CmpByteSaved);
		}
		if (pCmprsBfrEnd == pCmprsBfr)
			ret_code |= LZS_SOURCE_EXHAUSTED;
		else
			pCmprsBfrEnd = pCmprsBfr + 1;
			
		goto Cmprs_Data_Save;
	}
	if (p_temp->fState & TINY_EXPAND_BFR) {
		wDataReg = (unsigned short)(pExpBfr - pExpBfrStart);
		
		if (wDataReg > bytes_left)
			goto Fill_Expand_Buffer;
			
			
		pExpBfr = MEM_COPY_S(pExpStopped, pExpBfrStart, wDataReg);
		pExpBfrEnd = pExpandBuffer + (tMaxBfrSz) *destCnt;
		p_temp->fState &= ~TINY_EXPAND_BFR;
	}
	else {	
		p_temp->fState |= (LZS_OVERRUNNN | TINY_EXPAND_BFR);
		bytes_left = (tMaxBfrSz)(pExpBfrEnd - pExpBfr );
		pExpStopped = pExpBfr;
		LengthReg = (tMaxBfrSz)(  FP_Off(pExpBfr)
					- FP_Off(p_temp->pExpBfrBase) );
		if (LengthReg > MAX_OFFSET)
			LengthReg = MAX_OFFSET;
		pExpBfrStart = pExpBfr = p_temp->pTinyBfrBrk =
			MEM_COPY_S (&p_temp->SaveExpBytes[0],
				    pExpBfr - (unsigned short)LengthReg,
				    (unsigned short)LengthReg);
		pExpBfrEnd = &p_temp->SaveExpBytes[DUMDUM_SZ];
		if (!(bytes_left))
			goto OverRunnn;
	}
	goto Resume_Parsing;
Expand_Bfr_2_small:
	p_temp->fState |= LZS_EXPANDING_STR;
	if (p_temp->fState & TINY_EXPAND_BFR) { 
		wDataReg = (unsigned short)(
			FP_Off(pExpBfrEnd) - FP_Off(pExpBfrStart));
		if (bytes_left <= wDataReg)
			goto Fill_Expand_Buffer;
			
		bytes_left -= wDataReg;
		MEM_COPY_S(pExpStopped, pExpBfrStart, wDataReg);
		pExpStopped += wDataReg;
		wDataReg = (unsigned short)(
			  FP_Off(pExpBfr) - FP_Off(&p_temp->SaveExpBytes[0]));
		if (wDataReg > MAX_OFFSET) {
			
			MEM_COPY_S(&p_temp->SaveExpBytes[0],
				&p_temp->SaveExpBytes[wDataReg-MAX_OFFSET],
				MAX_OFFSET);
			wDataReg = MAX_OFFSET;
		}
		pExpBfrStart = pExpBfr = &p_temp->SaveExpBytes[wDataReg];
		p_temp->fState &= ~LZS_EXPANDING_STR;
		goto Resume_Parsing;	
	}
	else {
		p_temp->fState |= TINY_EXPAND_BFR;
		LengthReg = (tMaxBfrSz)(pExpBfrEnd - p_temp->pExpBfrBase);
		if (LengthReg > MAX_OFFSET)
			LengthReg = MAX_OFFSET;
		MEM_COPY_S (&p_temp->SaveExpBytes[0],
			    pExpBfrEnd - (unsigned short)LengthReg,
			    (unsigned short)LengthReg);
		p_temp->pTinyBfrBrk =
			&p_temp->SaveExpBytes[(unsigned short)LengthReg];
		p_temp->ExpByteSaved = 0;
		if (p_temp->fState & LZS_OVERRUNNN)
			goto OverRunnn;
	}
	goto Break_Cmprs_Bfr;
Cmprs_End_Getting_Len:
Cmprs_Data_End:
Cmprs_Data_End_Partial:
	LengthReg = 0;
	if (p_temp->fState & SPANNING_CMPRS_BFRS) {
		LengthReg = (tMaxBfrSz)(
			FP_Off(pCmprsBfr) - FP_Off(&p_temp->SaveCmpBytes[0]));
		if (LengthReg < p_temp->CmpByteSaved) {
			p_temp->fState |= SAVED_CMPRS_BYTES;
		}
		else {
			p_temp->fState &= ~SPANNING_CMPRS_BFRS;
			pCmprsBfrEnd =   (pCmprsBfr = *source)
				       + (tMaxBfrSz) *sourceCnt;
			pCmprsBfr += LengthReg - p_temp->CmpByteSaved;
			if (*sourceCnt >= 4)
				goto Resume_Parsing;
		}
	}
	if (   !(engn_return_code & ENGN_PARTIAL_TOKEN)
	    && (FP_Off(pCmprsBfr) != FP_Off(pCmprsBfrEnd)))
		
		goto Resume_Parsing;
	if (p_temp->fState & TINY_EXPAND_BFR) {
		p_temp->pTinyBfrBrk = pExpBfrStart;
		p_temp->ExpByteSaved = (unsigned short)(
			FP_Off(pExpBfr) - FP_Off(pExpBfrStart) );
		pExpBfr = pExpStopped;
	}
	ret_code |= LZS_SOURCE_EXHAUSTED;
	
	if (p_temp->fState & SAVED_CMPRS_BYTES) {
		if (LengthReg) {
			pCmprsBfrEnd =
				  pCmprsBfr
				+  
				  (  p_temp->CmpByteSaved
				   - (unsigned short)LengthReg)
				+ (unsigned short)*sourceCnt;
		 for (wDataReg = 0; FP_Off(pCmprsBfr) < FP_Off(pCmprsBfrEnd);
			wDataReg++)
			p_temp->SaveCmpBytes[wDataReg] = *pCmprsBfr++;
		}
		p_temp->CmpByteSaved += (unsigned short)*sourceCnt;
		pCmprsBfr = *source + ((unsigned short)*sourceCnt);
		goto Report_Results;
	}
Cmprs_Data_Save:
	if ((ENGN_END_OF_CMPRS_DATA & EocdToken(&pCmprsBfr, pCmprsBfrEnd,
		(struct RestartData LZS_FAR *) &p_temp->restart )) &&
		(p_temp->ExpByteSaved == 0))
		goto Report_EOD;
	p_temp->fState |= SAVED_CMPRS_BYTES;
	p_temp->CmpByteSaved = (unsigned short)(pCmprsBfrEnd - pCmprsBfr);
	for (wDataReg=0; FP_Off(pCmprsBfr)<FP_Off(pCmprsBfrEnd); wDataReg++)
		p_temp->SaveCmpBytes[wDataReg] = *pCmprsBfr++;
	goto Report_Results;
End_Of_Data:
	if (p_temp->fState & SPANNING_CMPRS_BFRS) {
		
		p_temp->fState &= ~SPANNING_CMPRS_BFRS;
		LengthReg = (tMaxBfrSz)(
			FP_Off(pCmprsBfr) - FP_Off(&p_temp->SaveCmpBytes[0]));
		pCmprsBfrEnd =   (pCmprsBfr = *source)
			       + (tMaxBfrSz) *sourceCnt;
		if (LengthReg > p_temp->CmpByteSaved) {
			pCmprsBfr += LengthReg - p_temp->CmpByteSaved;
		}
	}
	if (p_temp->fState & TINY_EXPAND_BFR) {
		p_temp->fState |= LZS_END_OF_CMPRS_DATA;
		wDataReg = (unsigned short)(pExpBfr - pExpBfrStart);
		if (wDataReg > bytes_left)
			goto Fill_Expand_Buffer;
		pExpBfr = MEM_COPY_S(pExpStopped, pExpBfrStart, wDataReg) ;
	}
	if ((tMaxBfrSz)(pExpBfr - pExpandBuffer) == *destCnt)
		ret_code |= LZS_DEST_EXHAUSTED;
Report_EOD:
	
	if (*sourceCnt == (tMaxBfrSz)(pCmprsBfr - *source))
		ret_code |= LZS_SOURCE_EXHAUSTED;
	bytes_saved = (tMaxBfrSz)(p_temp->pTinyBfrBrk
		- &p_temp->SaveExpBytes[0]);
	LZS_InitDecompressionHistory(p_temp);
	if (wOption & LZS_SAVE_HISTORY) {
		LengthReg = (tMaxBfrSz)(pExpBfr - pExpandBuffer);
		
		if ((LengthReg + bytes_saved) > MAX_OFFSET) {
			if (LengthReg > MAX_OFFSET) {
				LengthReg = MAX_OFFSET;
				bytes_saved = 0;
			}
			else {
				wDataReg = (unsigned short)bytes_saved;
				bytes_saved = (unsigned short)(
					    MAX_OFFSET
					  - (unsigned short)LengthReg);
				wDataReg -= (unsigned short)bytes_saved;
				MEM_COPY_S(&p_temp->SaveExpBytes[0],
					   &p_temp->SaveExpBytes[wDataReg],
					   (unsigned short)bytes_saved);
			}
		}
		p_temp->pTinyBfrBrk =
			MEM_COPY_S(&p_temp->SaveExpBytes[bytes_saved],
				   pExpBfr - (unsigned short)LengthReg,
				   (unsigned short)LengthReg);
		p_temp->fState = TINY_EXPAND_BFR;
	}
	ret_code |= LZS_END_MARKER;
#endif	
Report_Results:
	*sourceCnt -= (unsigned long)(FP_Off(pCmprsBfr) - FP_Off(*source));
	*source	    = pCmprsBfr;
	*destCnt   -= (unsigned long)(FP_Off(pExpBfr)   - FP_Off(pExpandBuffer));
	*dest       = pExpBfr;
	return (ret_code);
}
#if !defined (_FAST_)
unsigned int LZS_FAR Expand_Null_Engine(
	unsigned char LZS_FAR * LZS_FAR *ppCmprsBfr,
	unsigned char LZS_FAR	        *pCmprsBfrEnd,
	unsigned char LZS_FAR * LZS_FAR *ppExpBfr,
	unsigned char LZS_FAR	        *pExpBfrEnd,
	struct RestartData LZS_FAR      *pRestartData
)
{
	dREG unsigned long len;		
	unsigned long 	   Length;	
	unsigned short 	   delta;
	pRestartData->engnState = ENGN_END_OF_CMPRS_DATA;
	len    = (tMaxBfrSz)(pCmprsBfrEnd - *ppCmprsBfr);
	Length = (tMaxBfrSz)(pExpBfrEnd   - *ppExpBfr);
	if (len > Length) {
		len = Length;
		pRestartData->engnState = ENGN_OVERRUNNN;
	}
	while (len > 0 ) {
		delta = (len > 0xFFFFUL) ? (unsigned short) len: 1U;
		*ppExpBfr = mem_copy_r (*ppExpBfr, *ppCmprsBfr, delta);
		*ppCmprsBfr += delta;
		len -= delta;
	}
	return (pRestartData->engnState);
}
#endif	
