/* Algorithm (c) Ed Ross. See RDCN.doc (bottom) for more info. */
/* Amiga implementation by Niklas Sjöberg (both C and .asm)
   reachable on 2:203/415.3@Fidonet

   After this comment, source is identical to that one in CUJ Oct 92.
*/

/* decompress inbuff_len bytes of inbuff into outbuff.
 * return length of outbuff.
 *
*/

#include <string.h>
#include <clib/exec_protos.h>

typedef unsigned char uchar;
typedef unsigned short int uint;

int rdc_decompress(uchar *inbuff, uint inbuff_len, uchar *outbuff)
{
 uint ctrl_bits;
 uint ctrl_mask = 0;
 uchar *inbuff_idx = inbuff;
 uchar *outbuff_idx = outbuff;
 uchar *inbuff_end = inbuff + inbuff_len;
 uint cmd;
 uint cnt;
 uint ofs;
 uint len;


	/* process each item in inbuff */

	while (inbuff_idx < inbuff_end)
	{
		
		/* get new load of control bits if needed */

		if ((ctrl_mask >>= 1) == 0)
		{
			ctrl_bits = * (uint *) inbuff_idx;
			inbuff_idx += 2;
			ctrl_mask = 0x8000;
		}
		
		/* just copy this char if control bit is zero */

		if ((ctrl_bits & ctrl_mask) == 0)
		{
			*outbuff_idx++ = *inbuff_idx++;
			continue;
		}

		/* undo the compression code */

		cmd = (*inbuff_idx >> 4) & 0x0F;
		cnt = *inbuff_idx++ & 0x0F;

		if (cmd > 2) {
			ofs = cnt + 3;
			ofs += (*inbuff_idx++ << 4);
			memcpy(outbuff_idx, outbuff_idx - ofs, cmd);
			outbuff_idx += cmd;
			continue;
		}
		switch (cmd)
		{
			case 0:	/* short rle */
				cnt += 3;
				memset(outbuff_idx, *inbuff_idx++, cnt);
				outbuff_idx += cnt;
				break;

			case 1:	/* long rle */
				cnt += (*inbuff_idx++ << 4);
				cnt += 19;
				memset(outbuff_idx, *inbuff_idx++, cnt);
				outbuff_idx += cnt;
				break;

			case 2: /* long pattern */
				ofs = cnt + 3;
				ofs += (*inbuff_idx++ << 4);
				cnt = *inbuff_idx++;
				cnt += 16;
				memcpy(outbuff_idx, outbuff_idx - ofs, cnt); 
				outbuff_idx += cnt;
				break;
/*Default is the most often used code, so test for this case first */
/*
			default:  short pattern 
				ofs = cnt + 3;
				ofs += (*inbuff_idx++ << 4);
				memcpy(outbuff_idx, outbuff_idx - ofs, cmd);
				outbuff_idx += cmd;
				break;
*/
		}
	}

	/* return length of decompressed buffer */

	return outbuff_idx - outbuff;
}
