long __regargs	memread(char *,long);
long		compinit(void);
void		compexit(void);
long __regargs	decompfiles(char *,long,char *,long);
void __regargs	decompress(char *,long);
long		getcode(void);

#define BITS 12

#if BITS == 16
#	define HSIZE	69001 
#endif
#if BITS == 15
#	define HSIZE	35023 
#endif
#if BITS == 14
#	define HSIZE	18013 
#endif
#if BITS == 13
#	define HSIZE	9001 
#endif
#if BITS <= 12
#	define HSIZE	5003 
#endif

#define BLOCK_MASK	0x80
#define INIT_BITS	9

#define MAXCODE(n_bits) ((1 << (n_bits)) - 1)

#define tab_prefixof(i) codetab[i]
#define tab_suffixof(i) ((unsigned char *)(htab))[i]
#define de_stack	((unsigned char *)&tab_suffixof(1<<BITS))

#define FIRST	257 
#define CLEAR	256 

const unsigned char rmask[9] = {0x00,0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF};

long		*htab;
unsigned short	*codetab;

long buffsize;
char *buffstart;

long free_ent,block_compress,clear_flg,n_bits,maxcode,maxmaxcode;

long __regargs
memread(mem,size)
register char *mem;
register long size;
{
	register long bytesread = 0;

	while(size-- > 0)
	{
		if(buffsize-- > 0)
		{
			*mem++ = *buffstart++;
			bytesread++;
		}
		else
			return(bytesread);
	}

	return(bytesread);
}

void __regargs
decompress(sect,watermark)
register char *sect;
register long watermark;
{
	unsigned char *stackp;
	long finchar,code,oldcode,incode,reallywritten = 0;

	maxmaxcode = 1 << BITS;
	clear_flg = 0;
	block_compress = BLOCK_MASK;

	maxcode = MAXCODE(n_bits = INIT_BITS);

	for(code = 255 ; code >= 0 ; code--)
	{
		tab_prefixof(code) = 0;
		tab_suffixof(code) = (unsigned char) code;
	}

	free_ent = ((block_compress) ? FIRST : 256);
	finchar = oldcode = getcode();

	if(oldcode == -1)
		return;

	if(reallywritten++ < watermark)
		*sect++ = finchar;

	stackp = de_stack;

	while((code = getcode()) > -1)
	{
		if((code == CLEAR) && block_compress)
		{
			for(code = 255 ; code >= 0 ; code--)
				tab_prefixof(code) = 0;

			clear_flg = 1;
			free_ent = FIRST - 1;

			if((code = getcode()) == -1)
				break;
		}

		incode = code;

		if(code >= free_ent)
		{
			*stackp++ = finchar;
			code = oldcode;
		}

		while(code >= 256)
		{
			*stackp++ = tab_suffixof(code);
			code = tab_prefixof(code);
		}

		*stackp++ = finchar = tab_suffixof(code);

		do
		{
			if(reallywritten++ < watermark)
				*sect++ = *--stackp;
		}
		while(stackp > de_stack);

		if((code = free_ent) < maxmaxcode)
		{
			tab_prefixof(code) = (unsigned short)oldcode;
			tab_suffixof(code) = finchar;
			free_ent = code + 1;
		}

		oldcode = incode;
	}
}

long
getcode()
{
	static long offset = 0,size = 0;
	static unsigned char buf[BITS];

	long r_off,bits,code;
	register unsigned char *bp = buf;

	if(clear_flg > 0 || offset >= size || free_ent > maxcode)
	{
		if(free_ent > maxcode)
		{
			n_bits++;

			if(n_bits == BITS)
				maxcode = maxmaxcode;
			else
				maxcode = MAXCODE(n_bits);
		}

		if(clear_flg > 0)
		{
			maxcode = MAXCODE(n_bits = INIT_BITS);
			clear_flg = 0;
		}

		size = memread(buf,n_bits);

		if(size <= 0)
			return(-1);

		offset = 0;

		size = (size << 3) - (n_bits - 1);
	}

	r_off = offset;
	bits = n_bits;
	bp += (r_off >> 3);
	r_off &= 7;
	code = (*bp++ >> r_off);
	bits -= (8 - r_off);
	r_off = 8 - r_off;

	if(bits >= 8)
	{
		code |= *bp++ << r_off;
		r_off += 8;
		bits -= 8;
	}

	code |= (*bp & rmask[bits]) << r_off;
	offset += n_bits;

	return(code);
}

long
compinit()
{
	if(!(htab = (long *)AllocMem(HSIZE * sizeof(long),0)))
		return(FALSE);

	if(!(codetab = (unsigned short *)AllocMem(HSIZE * sizeof(unsigned short),0)))
	{
		FreeMem(htab,HSIZE * sizeof(long));

		htab = NULL;

		return(FALSE);
	}

	maxmaxcode = 1 << BITS;

	return(TRUE);
}

void
compexit()
{
	if(htab)
		FreeMem(htab,HSIZE * sizeof(long));

	if(codetab)
		FreeMem(codetab,HSIZE * sizeof(unsigned short));

	htab = NULL;
	codetab = NULL;
}

long __regargs
decompfiles(from,readsize,to,maxsize)
char *from,*to;
long readsize,maxsize;
{
	buffstart	= from;
	buffsize	= readsize;

	decompress(to,maxsize);
}
