/*
**	MandelSquare - AmigaDOS 2.0/3.0 Mandelbrot set explorer
**
**	Packer.c, CmpByteRun compression and decompression routines
**
**	Copyright © 1991-1992 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

	/* Packer modes. */

#define DUMP	0
#define RUN	1

	/* Minimum data run size, maximum data run size and maximum cache size. */

#define MINRUN	3
#define MAXRUN	128
#define MAXDAT	128

	/* Number of packed bytes and pack buffer. */

STATIC LONG	PackedBytes;
STATIC BYTE	Buffer[MAXDAT + 1];

	/* PutDump(register BYTE *Destination,register LONG Count):
	 *
	 *	Flush out the data dump.
	 */

STATIC BYTE * __regargs
PutDump(register BYTE *Destination,register LONG Count)
{
	register BYTE *Source = Buffer;

	*Destination++	 = Count - 1;
	 PackedBytes	+= Count + 1;

	while(Count--)
		*Destination++ = *Source++;

	return(Destination);
}

	/* PutRun(register BYTE *Destination,LONG Count,WORD Char):
	 *
	 *	Flush out a run of data bytes.
	 */

STATIC BYTE * __regargs
PutRun(register BYTE *Destination,LONG Count,WORD Char)
{
	*Destination++	 = -(Count - 1);
	*Destination++	 = Char;
	 PackedBytes	+= 2; 

	return(Destination);
}

	/* PackRow(BYTE **SourcePtr,register BYTE *Destination,LONG RowSize):
	 *
	 *	Compress a single row of bytes.
	 */

LONG __regargs
PackRow(BYTE **SourcePtr,register BYTE *Destination,LONG RowSize)
{
	register BYTE *Source = *SourcePtr;

	WORD	Buffered	= 1,
		RunStart	= 0;
	BYTE	Mode		= DUMP,
		LastChar,
		Char;

	PackedBytes = 0;

	Buffer[0] = LastChar = Char = *Source++;

	RowSize--;

	while(RowSize--)
	{
		Buffer[Buffered++] = Char = *Source++;

		if(Mode)
		{
			if((Char != LastChar) || (Buffered - RunStart > MAXRUN))
			{
				Destination	= PutRun(Destination,Buffered - 1 - RunStart,LastChar);
				Buffer[0]	= Char;
				Buffered	= 1;
				RunStart	= 0;
				Mode		= DUMP;
			}
		}
		else
		{
			if(Buffered > MAXDAT)
			{
				Destination	= PutDump(Destination,Buffered - 1);
				Buffer[0]	= Char;
				Buffered	= 1;
				RunStart	= 0;
			}
			else
			{
				if(Char == LastChar)
				{
					if(Buffered - RunStart >= MINRUN)
					{
						if(RunStart)
							Destination = PutDump(Destination,RunStart);

						Mode = RUN;
					}
					else
					{
						if(!RunStart)
							Mode = RUN;
					}
				}
				else
					RunStart = Buffered - 1;
			}
		}

		LastChar = Char;
	}

	if(Mode)
		PutRun(Destination,Buffered - RunStart,LastChar);
	else
		PutDump(Destination,Buffered);

	*SourcePtr = Source;

	return(PackedBytes);
}

	/* UnPackRow(BYTE **SourcePtr,BYTE **DestinationPtr,register WORD DestinationBytes):
	 *
	 *	Unpack CmpByteRun compressed data.
	 */

VOID __regargs
UnPackRow(BYTE **SourcePtr,BYTE **DestinationPtr,register WORD DestinationBytes)
{
	register BYTE	*Source		= *SourcePtr,
			*Destination	= *DestinationPtr,
			Char;
	register WORD	Count;

	while(DestinationBytes > 0)
	{
		if((Count = *Source++) >= 0)
		{
			Count++;

			if((DestinationBytes -= Count) < 0)
				break;
			else
			{
				do
					*Destination++ = *Source++;
				while(--Count);
			}
		}
		else
		{
			if(Count != (WORD)(-128))
			{
				Count = -Count + 1;

				if((DestinationBytes -= Count) < 0)
					break;
				else
				{
					Char = *Source++;

					do
						*Destination++ = Char;
					while(--Count);
				}
			}
		}
	}

	*SourcePtr		= Source;
	*DestinationPtr		= Destination;
}
