#include "glover.h"
//#include <string.h>
 
//#define MAX_ALLOCS	625
#define MAX_ALLOCS	400

// #define LZH_ROUTINES
#define DESC_LEN 10

typedef struct _AllocBlockType {
	DWORD			offset;					// where this block starts (offset from base)
    DWORD			size;  					// size of this block
	BYTE			inuse;					// is this block in use?
#ifdef MEMORY_DEBUGMODE
	char			desc[DESC_LEN];
	char			file[DESC_LEN];
	int				lineno;
#endif
} AllocBlockType;


typedef struct {
	AllocBlockType	blocks[MAX_ALLOCS];		// array of control structs
	DWORD			base;			   		// base address for global block
	DWORD			size;			   		// size of area
	int				lastused;		   		// number of last used block
	DWORD			tempsize;
	int				tempused;
#ifdef MEMORY_DEBUGMODE
	int				total;
	int				peak;
#endif
} AllocType;



static AllocType	pool;


/**************************************************************************
	FUNCTION:	memoryInitialise()
	PURPOSE:	Initialise memory for allocation
	PARAMETERS:	Base address, size of area
	RETURNS:	0/1
**************************************************************************/

int memoryInitialise(DWORD base, DWORD size)
{
//	InitHeap2(base, size);
//	return 1;

	pool.base = base;
	pool.size = size;
	memoryReset();
	return 1;
}


/**************************************************************************
	FUNCTION:	memoryDestroy()
	PURPOSE:	Destroy memory allocation data
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

static void ShowMemBytes(char *buf, uchar *src, int nbytes)
{
	while (nbytes-- > 0)
	{
		if (*src < ' ')
			*buf++ = '.';
		else
			*buf++ = *src;
		src++;
	}
}

void memoryDestroy()
{
	int		loop;
	char	buf[40];

#ifdef MEMORY_DEBUGMODE
	psxPrintf("** Memory usage peaked @ %d bytes (%d Kb)\n", pool.peak, pool.peak>>10);
#endif
	
	for(loop=0; loop<=pool.lastused; loop++)
	{
		if (pool.blocks[loop].inuse)
		{
			ShowMemBytes(buf, (uchar*)(pool.base+pool.size-pool.blocks[loop].size-pool.blocks[loop].offset), 32);

			psxPrintf("** Memory leak @ 0x%x, size %d, '%s'\n", 
					pool.base+pool.size-pool.blocks[loop].size-pool.blocks[loop].offset,
					pool.blocks[loop].size, buf);
#ifdef MEMORY_DEBUGMODE
			psxPrintf("   '%s' from '%s', line %d\n", pool.blocks[loop].desc,
						pool.blocks[loop].file,pool.blocks[loop].lineno);
#endif
		}
	}
	pool.size = 0;
}


/**************************************************************************
	FUNCTION:	memoryReset()
	PURPOSE:	Reset memory allocation
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

void memoryReset()
{
	int		loop;

	for(loop=0; loop<MAX_ALLOCS; loop++) 		// initialise the block array
	{
		pool.blocks[loop].inuse = 0;
		pool.blocks[loop].offset = 0;
		pool.blocks[loop].size = 0;
	}

	pool.lastused = -1;
	pool.tempsize = 0;
	pool.tempused = 0;

#ifdef MEMORY_DEBUGMODE
	pool.total = 0;
	pool.peak = 0;
#endif
}


/**************************************************************************
	FUNCTION:	memoryAllocate()
	PURPOSE:	Allocate block of memory from pool
	PARAMETERS:	Size of area, ID string
	RETURNS:	Base address of block or NULL
**************************************************************************/

void *memoryAllocate(DWORD size)
{
#ifdef MEMORY_DEBUGMODE

	return NULL;

#else
	int		l,k;
	DWORD	oldsize;
	void	*ret;

// psxPrintf("AllocMem: %d '%s' '%s' #%d\n", size, ident, file, line);

	if ((size%4)!=0)
    	size += 4-(size%4);									// round to long boundary

	if(size == 0)
	{
		return NULL;	// yes, this happens occasionally
	}
 	if ((size<0) || (size>pool.size))						// silly :-)
	{
		psxPrintf("AllocMem failure - block size illegal (%d bytes)\n", size);
    	return NULL;
	}

	if (pool.lastused==-1)									// first ever block claimed?
	{
		pool.blocks[0].inuse = 1;
		pool.blocks[0].size = size;
		pool.blocks[0].offset = 0;
		pool.lastused = 0;
		ret = (void *)(pool.base+pool.size-size);

// psxPrintf("AllocMem: 0x%x %d '%s' '%s' #%d\n", (int)ret, size, ident, file, line);

		return ret;
	}

	for(l=0; l<MAX_ALLOCS - pool.tempused; l++)
	{
		if ((pool.blocks[l].inuse==0) && (pool.blocks[l].size>=size))
			break;
		if (l==pool.lastused+1)
			break; 
	}
	if (l==MAX_ALLOCS - pool.tempused)						// could find no available record (shouldn't happen)
	{
//		psxPrintf("AllocMem failure - could find no available record\n");
		return NULL;
	}

	if (l==pool.lastused+1)									// add to the end of the list
	{
		oldsize = (pool.blocks[pool.lastused].offset+pool.blocks[pool.lastused].size);
		if (pool.size - oldsize - pool.tempsize < size)							// not enough space left at end of pool
			return NULL;

        pool.lastused++;
		pool.blocks[l].inuse = 1;
		pool.blocks[l].size = size;
		pool.blocks[l].offset = oldsize;
		ret = (void *)(pool.base+pool.size-size-pool.blocks[pool.lastused].offset);

// psxPrintf("AllocMem: 0x%x %d '%s' '%s' #%d\n", (int)ret, size, ident, file, line);

		return ret;
	}

// Deal with splitting an existing memory block into a used & a free section by shuffling the list down one

	oldsize = pool.blocks[l].size;
	pool.blocks[l].size = size;
	pool.blocks[l].inuse = 1;
	if ((size < oldsize) && (pool.lastused < MAX_ALLOCS-pool.tempused-1))		// if the old block was larger and there is room to grow...
	{
		for(k=pool.lastused; k>l; k--)
			pool.blocks[k+1] = pool.blocks[k];
		k = l+1;
		pool.lastused++;        
		pool.blocks[k].inuse = 0;
		pool.blocks[k].size = oldsize-size;
		pool.blocks[k].offset = pool.blocks[l].offset+size; 
	}
	else if (size < oldsize)
    	pool.blocks[l].size = oldsize;						// can't split it, so to avoid drop-out :-(

	ret = (void *)(pool.base+pool.size-size-pool.blocks[l].offset);

// psxPrintf("AllocMem: 0x%x %d '%s' '%s' #%d\n", (int)ret, size, ident, file, line);

	return ret;

#endif
}


/**************************************************************************
	FUNCTION:	memoryDebugAllocate()
	PURPOSE:	Allocate block of memory from pool (with debug info)
	PARAMETERS:	Size of area, ID string, filename, line number
	RETURNS:	Base address of block or NULL
**************************************************************************/

void *memoryDebugAllocate(DWORD size, char *desc, char *file, int line)
{
#ifdef MEMORY_DEBUGMODE

	int		l,k;
	DWORD	oldsize;
	void	*ret;

/*	ret = malloc2(size);
	if (ret==NULL)
		psxPrintf("Unable to allocate %d\n", size);
	else
		psxPrintf("Allocated %d\n",size);
	return ret;*/

	if(strlen(desc) > DESC_LEN-1)
	{
		l = strlen(desc);
		desc += (l-(DESC_LEN-1));
	}

	if ((size%4)!=0)
    	size += 4-(size%4);									// round to long boundary

	if(size == 0)
	{
		return NULL;	// yes, this happens occasionally
	}
 	if ((size<0) || (size>pool.size))						// silly :-)
	{
		psxPrintf("AllocMem failure - block size illegal (%d bytes)\n", size);
		CRASH;
//    	return NULL;
	}

	pool.total += size;
	if (pool.total>pool.peak)
		pool.peak = pool.total;

	if (pool.total>pool.size - pool.tempsize)
	{
		memoryShow();
		memoryShowStats();
		psxPrintf("memoryDebugAllocate: OUT OF MEMORY (requested %d bytes, %s line %d)\n", size, file, line);
		CRASH;
//		return NULL;
	}

	if (pool.lastused==-1)									// first ever block claimed?
	{
		strncpy(pool.blocks[0].desc, desc, DESC_LEN-1);
		strncpy(pool.blocks[0].file, file, DESC_LEN-1);
		pool.blocks[0].lineno = line;

		pool.blocks[0].inuse = 1;
		pool.blocks[0].size = size;
		pool.blocks[0].offset = 0;
		pool.lastused = 0;
		ret = (void *)(pool.base+pool.size-size);
		return ret;
	}

	for(l=0; l<MAX_ALLOCS - pool.tempsize; l++)
	{
		if ((pool.blocks[l].inuse==0) && (pool.blocks[l].size>=size))
			break;
		if (l==pool.lastused+1)
			break; 
	}
	if (l==MAX_ALLOCS - pool.tempused)						// could find no available record (shouldn't happen)
	{
		memoryShow();
		memoryShowStats();
		psxPrintf("AllocMem failure - could find no available record\n");
		CRASH;
//		return NULL;
	}

	if (l==pool.lastused+1)									// add to the end of the list
	{
		oldsize = (pool.blocks[pool.lastused].offset+pool.blocks[pool.lastused].size);
		if (pool.size-oldsize-pool.tempsize < size)							// not enough space left at end of pool
		{
			memoryShow();
			memoryShowStats();
			psxPrintf("AllocMem failure - not enough space left at end of pool for size %d,%s line %d\n",size,file,line);
			CRASH;
//			return NULL;
		}
        pool.lastused++;
		strncpy(pool.blocks[l].desc, desc, DESC_LEN);
		strncpy(pool.blocks[l].file, file, DESC_LEN);
		pool.blocks[l].lineno = line;

		pool.blocks[l].inuse = 1;
		pool.blocks[l].size = size;
		pool.blocks[l].offset = oldsize;
		ret = (void *)(pool.base+pool.size-size-pool.blocks[pool.lastused].offset);
		return ret;
	}

// Deal with splitting an existing memory block into a used & a free section by shuffling the list down one

	oldsize = pool.blocks[l].size;
	strncpy(pool.blocks[l].desc, desc, DESC_LEN);
	strncpy(pool.blocks[l].file, file, DESC_LEN);
	pool.blocks[l].lineno = line;

	pool.blocks[l].size = size;
	pool.blocks[l].inuse = 1;
	if ((size < oldsize) && (pool.lastused < MAX_ALLOCS-pool.tempused-1))		// if the old block was larger and there is room to grow...
	{
		for(k=pool.lastused; k>l; k--)
			pool.blocks[k+1] = pool.blocks[k];
		k = l+1;
		pool.lastused++;        
		pool.blocks[k].inuse = 0;
		pool.blocks[k].size = oldsize-size;
		pool.blocks[k].offset = pool.blocks[l].offset+size; 
	}
	else if (size < oldsize)
    	pool.blocks[l].size = oldsize;						// can't split it, so to avoid drop-out :-(

	ret = (void *)(pool.base+pool.size-size-pool.blocks[l].offset);
	return ret;

#else

	return NULL;

#endif
}

void *memoryTempAllocate(DWORD size)
{
	int l;
	void *ret;
	DWORD	oldsize;

	if ((size%4)!=0)
    	size += 4-(size%4);									// round to long boundary

	if(size == 0)
	{
		return NULL;	// yes, this happens occasionally
	}
 	if ((size<0) || (size>pool.size))						// silly :-)
	{
//		psxPrintf("AllocMem failure - block size illegal (%d bytes)\n", size);
    	return NULL;
	}

	if(pool.lastused+1 + pool.tempused >= MAX_ALLOCS)
	{
		return NULL;
	}

	l = MAX_ALLOCS - 1 - pool.tempused;
	oldsize = (pool.blocks[pool.lastused].offset+pool.blocks[pool.lastused].size);

	if (pool.size - oldsize - pool.tempsize < size)							// not enough space left at base of pool
		return NULL;

	pool.tempused++;
	pool.blocks[l].inuse = 1;
	pool.blocks[l].size = size;
	pool.blocks[l].offset = pool.tempsize;
	ret = (void *)(pool.base + pool.blocks[l].offset);
	pool.tempsize += size;

// psxPrintf("TempAllocMem: 0x%x %d '%s' '%s' #%d\n", (int)ret, size, ident, file, line);

	return ret;
}

#ifdef MEMORY_DEBUGMODE
void *memoryTempDebugAllocate(DWORD size, char *desc, char *file, int line)
{
	int l;
	void *ret;
	DWORD	oldsize;

	if(strlen(desc) > DESC_LEN-1)
	{
		l = strlen(desc);
		desc += (l-(DESC_LEN-1));
	}

	if ((size%4)!=0)
    	size += 4-(size%4);									// round to long boundary

	if(size == 0)
	{
		return NULL;	// yes, this happens occasionally
	}
 	if ((size<0) || (size>pool.size))						// silly :-)
	{
		psxPrintf("TempAllocMem failure - block size illegal (%d bytes)\n", size);
    	return NULL;
	}

	if(pool.lastused+1 + pool.tempused >= MAX_ALLOCS)
	{
		memoryShow();
		memoryShowStats();
		psxPrintf("TempAllocMem failure - could find no available record\n");
		CRASH;
	}

	l = MAX_ALLOCS - 1 - pool.tempused;
	oldsize = (pool.blocks[pool.lastused].offset+pool.blocks[pool.lastused].size);

	if (pool.size - oldsize - pool.tempsize < size)							// not enough space left at base of pool
	{
			memoryShow();
			memoryShowStats();
			psxPrintf("TempAllocMem failure - not enough space left at start of pool for size %d,%s line %d\n",size,file,line);
			CRASH;
	}

	pool.tempused++;
	pool.blocks[l].inuse = 1;
	pool.blocks[l].size = size;
	pool.blocks[l].offset = pool.tempsize;

	strncpy(pool.blocks[l].desc, desc, DESC_LEN);
	strncpy(pool.blocks[l].file, file, DESC_LEN);
	pool.blocks[l].lineno = line;

	ret = (void *)(pool.base + pool.blocks[l].offset);
	pool.tempsize += size;

// psxPrintf("TempAllocMem: 0x%x %d '%s' '%s' #%d\n", (int)ret, size, ident, file, line);

	return ret;
}
#endif

void memoryTempFree(void *blk)
{
	int b;
	int l;
	int off;


	off = ((ULONG)blk) - pool.base;
	for(b = 0; b<pool.tempused; b++)
	{
		l = MAX_ALLOCS-1-b;
		if(pool.blocks[l].offset == off)
		{
			pool.blocks[l].inuse = 0;

			if(b == pool.tempused-1)	// If we just freed the highest temp block, free as many as possible
			{
				while(pool.blocks[l].inuse == 0 && l < MAX_ALLOCS)
				{
					pool.tempsize-=pool.blocks[l].size;
					pool.tempused--;
					l++;
				}
			}
			return;
		}
	}
// this will happen on object releases that came from BFF files, and from null releases
//	DB(" *** TEMP FREE NOT FOUND ***\n");
}

/**************************************************************************
	FUNCTION:	memoryFree()
	PURPOSE:	Free block of memory allocated from global pool
	PARAMETERS:	Base ptr
	RETURNS:	
**************************************************************************/

void memoryFree(void *blk)
{
	BYTE	*tp;
	int		l, k;

//	free2(blk);
//	return;

// dpsxPrintf("Free 0x%x\n", (int)blk);

	if (pool.lastused == -1)								// nothing to free
		return;

	tp = (uchar*)(pool.base+pool.size);
	for(l=0; l<=pool.lastused; l++)
		{
		tp -= pool.blocks[l].size;
		if (tp==blk)
			break;
		}

	if (l>pool.lastused)										// could not find the block to allocate
	{
		memoryTempFree(blk);
		return;
	}

	pool.blocks[l].inuse = 0;
#ifdef MEMORY_DEBUGMODE
	pool.total -= pool.blocks[l].size;
#endif

	if (l==pool.lastused)
	{
		pool.lastused--;											// last item in list
		if ((pool.lastused>=0) && (pool.blocks[pool.lastused].inuse == 0))	// was the previous one empty aswell?
			pool.lastused--;
		return;
	}

	if (pool.blocks[l+1].inuse == 0)								// merge with next one
	{
		pool.blocks[l].size += pool.blocks[l+1].size;
		for(k=l+1; k<pool.lastused; k++)
			pool.blocks[k] = pool.blocks[k+1];
        pool.blocks[pool.lastused--].inuse = 0;
	}

	if ((l>0) && (pool.blocks[l-1].inuse == 0))				// merge with previous one
	{
		pool.blocks[l-1].size += pool.blocks[l].size;
		for(k=l; k<pool.lastused; k++)
			pool.blocks[k] = pool.blocks[k+1];
		pool.blocks[pool.lastused--].inuse = 0;
	}
}


/**************************************************************************
	FUNCTION:	memoryShow()
	PURPOSE:	Send list of allocated blocks to debug output
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

void memoryPrintGarbageGap()
{
	int top;
	if(pool.lastused != -1)
		top = pool.size - pool.blocks[pool.lastused].offset - pool.blocks[pool.lastused].size;
	else
		top = pool.size;
	psxPrintf("  --- Non-garbage gap = (%8x - %8x) = %d ---\n",pool.base+top, pool.base+pool.tempsize, top - pool.tempsize);
}


void memoryShow()
{
	int		loop;
	BYTE	*ucp;

	psxPrintf("\nMemory block list:\n");
	psxPrintf("--------------------\n");
    psxPrintf("%d blocks: %d temps\n", pool.lastused+1,pool.tempused);

	if (pool.lastused == -1)
		return;

	ucp = (BYTE *)(pool.base+pool.size);
	for(loop=0; loop<=pool.lastused; loop++)
	{
		ucp -= pool.blocks[loop].size;
		if (pool.blocks[loop].inuse)
		{
#ifdef MEMORY_DEBUGMODE
			psxPrintf("  #%3d ---- @ 0x%x, %6d: '%s' %s/%d\n", loop,
					(int)ucp, pool.blocks[loop].size, pool.blocks[loop].desc, pool.blocks[loop].file,pool.blocks[loop].lineno);
#else
			psxPrintf("  #%3d ---- @ 0x%x, %6d\n", loop, (int)ucp, pool.blocks[loop].size);
#endif
		}
		else
		{
			psxPrintf("  #%3d FREE @ 0x%x, %6d\n", loop,
					(int)ucp, pool.blocks[loop].size);
		}
	}


	if(pool.tempused)
	{
		psxPrintf("  --- Temporary Mem Pool ---\n");
	}
	ucp = (BYTE *)(pool.base);

	

	
	for(loop=MAX_ALLOCS-1; loop > MAX_ALLOCS-1-pool.tempused; loop--)
	{
		ucp += pool.blocks[loop].size;
		if (pool.blocks[loop].inuse)
		{
#ifdef MEMORY_DEBUGMODE
			psxPrintf("  #%3d ---- @ 0x%x, %6d: '%s' %s/%d\n", loop,
					(int)ucp, pool.blocks[loop].size, pool.blocks[loop].desc, pool.blocks[loop].file,pool.blocks[loop].lineno);
#else
			psxPrintf("  #%3d ---- @ 0x%x, %6d\n", loop, (int)ucp, pool.blocks[loop].size);
#endif
		}
		else
		{
			psxPrintf("  #%3d FREE @ 0x%x, %6d\n", loop,
					(int)ucp, pool.blocks[loop].size);
		}
	}

	memoryPrintGarbageGap();

	
    psxPrintf("\n");
}


/**************************************************************************
	FUNCTION:	memoryShowStats()
	PURPOSE:	Show memory usage statistics
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

void memoryShowStats()
{
	int		loop, inuse, notinuse;
	DWORD	used, freed;
	int largest;

	used = 0;
	freed = 0;
	inuse = 0;
	largest = 0;
	notinuse = 0;

	for(loop=0; loop<=pool.lastused; loop++)
	{
		if(pool.blocks[loop].inuse)
		{
			used += pool.blocks[loop].size;
			inuse++;
		}
		else
		{
			freed += pool.blocks[loop].size;
			notinuse++;
			if(pool.blocks[loop].size > largest)
				largest = pool.blocks[loop].size;
		}
		
	}

//	for(loop=MAX_ALLOCS-1-pool.tempused; loop<MAX_ALLOCS; loop++)
	for(loop=MAX_ALLOCS-pool.tempused; loop<MAX_ALLOCS; loop++)
	{
		if(pool.blocks[loop].inuse)
		{
			used += pool.blocks[loop].size;
			inuse++;
		}
		else
		{
			notinuse++;
			freed += pool.blocks[loop].size;
		}
	}

// non-garbage area...
	if(pool.size - used - freed > largest)
		largest = pool.size - used - freed;


	psxPrintf("MEMORY: Tot %d. Free %d (%d). (%d/%d blocks, %d gaps)\n",
		pool.size, 
		pool.size - used,
		largest,
		inuse,
		MAX_ALLOCS,
		notinuse
		);



//	psxPrintf("TOTAL RAM:   %d Kb\n", pool.size>>10);
//	psxPrintf("USED BLOCKS: %d, %d used (%d/100 usage)\n", pool.lastused+1, inuse, (inuse*100)/(pool.lastused+1));
//	psxPrintf("USED RAM:    %dKb, %dKb used (%dKb free %d/100 gaps)\n", (used+freed)>>10,used>>10,freed>>10, (freed*100)/used);
}


void memoryDump(void *pMem)
{
	char *pRow;
	char ch;
	int bHit, row, c;
	int nRows=10, bPad=TRUE, nSize=4;

	for(;;)
	{
		Start_Loop();

		pRow=(char *)pMem;
		for(row=0; row<nRows; row++)
		{
			// print address
			TEXTSETPOS(32, (8+row*12));
			textPrintf("%08x  ", (ULONG)pRow);
			switch(nSize)
			{
			case 1:
				for(c=0; c<16; c++) 
					textPrintf("%02x ", ((UBYTE *)pRow)[c]);
				break;
			case 2:
				for(c=0; c<8; c++) 
					textPrintf("%04x ", ((USHORT *)pRow)[c]);
				break;
			case 4:
				for(c=0; c<4; c++) 
					textPrintf("%08x ", ((ULONG *)pRow)[c]);
			}
			textPrintf(" ");
			for(c=0; c<16; c++)
			{
				ch=pRow[c];
				if((ch=='\r')||(ch=='\n')||(ch==7))
					textPrintf(" ");
				else
					textPrintf("%c", ch);
			}
			textPrintf("\n");

			pRow+=16;
		}
		textPrintf("\n");

		if(!bPad)
			return;
		bHit=0;
		
		// wait for keypress ...
		textPrintf("Press Up, Down, Select or Start to exit\r");
		while(!bHit)
		{

			if(debounce[0]& PAD_START)
				return;
			if(debounce[0]& PAD_SELECT)
			{
				nSize=nSize>>1;
				if(!nSize)
					nSize=4;
				bHit++;
			}
			if(debounce[0]& PAD_DOWN)
			{
				pMem=(void *)(((long)pMem)+16*nRows);
				bHit++;
			}
			if(debounce[0]& PAD_UP)
			{
				pMem=(void *)(((long)pMem)-16*nRows);
				bHit++;
			}
		}

		End_Loop(2);
	}
}

/**********************************************************************************************/
/***DECOMPRESSION*******/

#ifdef LZH_ROUTINES

#define	HISTORY_SIZE		4096
#define MASK_HISTORY		(HISTORY_SIZE-1)
#define MASK_UPPER			(0xF0)
#define MASK_LOWER			(0x0F)
#define SHIFT_UPPER			16
#define LSR_UPPER			4
#define MAX_COMP_LEN		17

static unsigned char	*outputBufPtr;
static int				outputBufLen;

static UBYTE	LZhistory[HISTORY_SIZE];
static LONG	LZhistoryOff;

static inline void DecompressOutputByte(UBYTE data)
{
	*outputBufPtr++ = data;
	outputBufLen++;
	LZhistory[LZhistoryOff] = data;
	LZhistoryOff = (LZhistoryOff+1) & MASK_HISTORY;
}

/*	--------------------------------------------------------------------------------
	FUNCTION:	DecompressBuffer
	PURPOSE:	Decompress data from input buffer into output buffer
	PARAMETERS:	Input buffer start, Output buffer start
	RETURNS:	Length of uncompressed data
	INFO:
*/

int DecompressBuffer(UBYTE *inBuf, UBYTE *outBuf)
{
	LONG	tag, count, offset, loop;

	outputBufPtr = outBuf;								// Initialise output
	outputBufLen = 0;
	
	LZhistoryOff = 0;									// Clear history
	memset(LZhistory, 0, HISTORY_SIZE);

	while(1)
	{
		tag = *inBuf++;
		for(loop=0; loop!=8; loop++)
		{
			if (tag & 0x80)
			{
				if ((count=*inBuf++) == 0)
				{
					return outputBufLen;				// Finished now
				}
				else
				{										// Copy from history
					offset = HISTORY_SIZE-(((MASK_UPPER & count)*SHIFT_UPPER)+(*inBuf++));
					count &= MASK_LOWER;
					count += 2;
					while (count!=0)
					{
						DecompressOutputByte(LZhistory[(LZhistoryOff+offset) & MASK_HISTORY]);
						count--;
					}
				}
			}
			else
			{
				DecompressOutputByte(*inBuf++);			// Copy data byte
			}
			tag += tag;
		}
	}
	return outputBufLen;
}

#endif	// if ifdef lzh_routines

/**********************************************************************************************/

#define MAX_JOINMALLOCS 32

static size_t jmSize[MAX_JOINMALLOCS];
static void **jmPtr[MAX_JOINMALLOCS];

static ULONG nJoinMallocs;
static size_t sizeJoinMallocs;

void memoryStartJoinMalloc(void)
{
	nJoinMallocs=0;
	sizeJoinMallocs=0;
}

void memoryJoinMalloc(void **pp, size_t size)
{
	jmPtr[nJoinMallocs]=pp;
	jmSize[nJoinMallocs]=size;

	sizeJoinMallocs+=size;

	nJoinMallocs++;
}

void memoryEndJoinMalloc(char *s)
{
	int i,off=0;
	void *p=MALLOC(sizeJoinMallocs, s); // malloc whole pool of memory

	for(i=0; i<nJoinMallocs; i++)
	{
		*(jmPtr[i])=(void *)(off+(ULONG)p);
		off+=jmSize[i];
	}
}

