#include <xpk/xpksub.h>

#ifdef __MAXON__
  #define __asm
#endif

struct XpkMode RlenMode =
{
  NULL,				/* next			*/
  100,				/* upto			*/
  XPKMF_A3000SPEED,		/* flags		*/
  0,				/* packmem		*/
  0,				/* unpackmem		*/
  140,				/* packspeed, K / sec	*/
  1043,				/* unpackspeed, K / sec	*/
  45,				/* ratio, *0.1 %	*/
  0,				/* reserved		*/
  "normal"			/* description		*/
};

static struct XpkInfo RlenInfo =
{
  1,				/* info version */
  1,				/* lib  version */
  0,				/* master vers  */
  0,				/* pad          */
  "RLEN",			/* short name   */
  "Run Length",			/* long name    */
  "Fast and simple compression usable for simple data",	/* description*/
  0x524C454E,			/* 4 letter ID  */
  XPKIF_PK_CHUNK |		/* flags        */
  XPKIF_UP_CHUNK,
  0x7fffffff,			/* max in chunk */
  0,				/* min in chunk */
  0x4004,			/* def in chunk */
  NULL,				/* pk message   */
  NULL,				/* up message   */
  NULL,				/* pk past msg  */
  NULL,				/* up past msg  */
  50,				/* def mode     */
  0,				/* pad          */
  &RlenMode			/* modes        */
};

/*
 * Returns an info structure about our packer
 */

#ifdef __cplusplus
  extern "C"
#endif

struct XpkInfo * __asm XpksPackerInfo(void)
{
  return &RlenInfo;
}

/*
 * Pack a chunk
 */

#ifdef __cplusplus
  extern "C"
#endif

LONG __asm XpksPackChunk(register __a0 struct XpkSubParams *xpar)
{
  UBYTE *get = xpar->xsp_InBuf, *start = xpar->xsp_InBuf;
  UBYTE *end = get + xpar->xsp_InLen, *put = xpar->xsp_OutBuf;
  UBYTE *wend = put + xpar->xsp_OutBufLen;
  LONG run, i;

  for(;;)
  {
    run = get[0] == get[1] && get[1] == get[2];

    if(put + (get - start) + 4 > wend)
      return XPKERR_EXPANSION;

    if(run || get - start == 127 || get == end)
    {	/* write uncompressed */
      if(get - start)
      {
	*put++ = get - start;
	for(i = get - start; i > 0; i--)
	  *put++ = *start++;
      }
      if(get == end)
      {
	*put++ = 0;
	break;
      }
      start = get;
    }

    if(run)
    {			/* write compressed   */
      for(i = 3; get + i < end && get[i - 1] == get[i] && i < 127; i++);
      *put++ = -i;
      *put++ = get[0];
      get += i;
      start = get;
    }
    else
      get++;
  }
  xpar->xsp_OutLen = put - (UBYTE *) xpar->xsp_OutBuf;

  return 0;
}

#ifdef __cplusplus
  extern "C"
#endif

LONG __asm XpksUnpackChunk(register __a0 struct XpkSubParams *xpar)
{
  UBYTE *get = xpar->xsp_InBuf, *put = xpar->xsp_OutBuf, v;
  LONG i;

  while(i = (BYTE) *get++)
    if(i > 0)
      for(; i > 0; i--)
	*put++ = *get++;
    else
      for(i = -i, v = *get++; i > 0; i--)
	*put++ = v;

  return 0;
}
