/*ProfiPacket - packet radio terminal program
  Copyright (C) 1999  Alexander Feigl

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  Author:

  Alexander Feigl
  Burachstraße 51

  D-88250 Weingarten

  Mail : Alexander.Feigl@gmx.de
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef __GNUC__
#define _storage_text_ __attribute__ ((section(".text")))
#else
#define _storage_text_
#endif
#include <CRC.h>

#define EOF -1


#include <dos/exall.h>
#include <utility/tagitem.h>
#include <exec/memory.h>

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

#ifdef __GNUC__
#include <inline/exec.h>
#include <inline/dos.h>
#endif

#define maxcompfilesize 10000000 /* this is important */


/*----------------------------------------------------------------------*/
/*                                  */
/*      LZSS ENCODING                       */
/*                                  */
/*----------------------------------------------------------------------*/

#define N4K     4096    /* buffer size (original 4096) */
#define N2K     2048
#define F       60  /* pre-sence buffer size */
#define THRESHOLD   2
#define NILE    N2K /* term of tree */



#define N_CHAR      (256 - THRESHOLD + F) /* {code : 0 .. N_CHAR-1} */
#define T       (N_CHAR * 2 - 1)    /* size of table */
#define R       (T - 1)         /* root position */
#define MAX_FREQ    0x8000  /* tree update timing from frequency */

typedef unsigned char uchar;

typedef const unsigned char cuchar;

extern unsigned int *freq;
extern int *prnt;
extern int *son;

extern unsigned getbuf;
extern uchar getlen;


extern void *DOSBase;
extern void *SysBase;
extern BPTR infile,outfile;
extern int in_memory, out_memory;
extern unsigned long int  textsize, codesize;
extern char *srcbuf, *destbuf;
extern char *srcbufptr, *destbufptr;
extern long srclen, destlen;
extern long srcbuflen, destbuflen;
extern unsigned char *text_buf;
extern unsigned long int match_position,match_length;
extern int *lson,*rson,*dad;
extern unsigned char *same;
extern unsigned putbuf;
extern unsigned char putlen;

/*static unsigned char    text_buf[N4K + F - 1];
static unsigned int match_position, match_length;
static int      lson[N4K + 1], rson[N4K + 1 + N4K], dad[N4K + 1];
static unsigned char    same[N4K + 1];
*/


int GetBuffers(void)
{
 text_buf=AllocVec(sizeof(char)*(N4K+F-1),1);
 if (text_buf==NULL) return(FALSE);
 lson=AllocVec(sizeof(int)*(N4K+1),1);
 if (lson==NULL)
  {
   FreeVec(text_buf);
   return(FALSE);
  }
 rson=AllocVec(sizeof(int)*(N4K+N4K+1),1);
 if (rson==NULL)
  {
   FreeVec(lson);
   FreeVec(text_buf);
   return(FALSE);
  }
 dad=AllocVec(sizeof(int)*(N4K+1),1);
 if (dad==NULL)
  {
   FreeVec(rson);
   FreeVec(lson);
   FreeVec(text_buf);
   return(FALSE);
  }

 same=AllocVec(sizeof(int)*(N4K+1),1);
 if (same==NULL)
  {
   FreeVec(dad);
   FreeVec(rson);
   FreeVec(lson);
   FreeVec(text_buf);
   return(FALSE);
  }

 freq=AllocVec(sizeof(unsigned)*(T+1),1);
 if (freq==NULL)
  {
   FreeVec(same);
   FreeVec(dad);
   FreeVec(rson);
   FreeVec(lson);
   FreeVec(text_buf);
   return(FALSE);
  }
 prnt=AllocVec(sizeof(int)*(T+N_CHAR),1);
 if (prnt==NULL)
  {
   FreeVec(freq);
   FreeVec(same);
   FreeVec(dad);
   FreeVec(rson);
   FreeVec(lson);
   FreeVec(text_buf);
  }
 son=AllocVec(sizeof(int)*T,1);
 if (son==NULL)
  {
   FreeVec(prnt);
   FreeVec(freq);
   FreeVec(same);
   FreeVec(dad);
   FreeVec(rson);
   FreeVec(lson);
   FreeVec(text_buf);
  }

 putbuf=0;
 putlen=0;
 getbuf=0;
 getlen=0;
 return(TRUE);
}

void FreeBuffers(void)
{
 FreeVec(text_buf);
 FreeVec(lson);
 FreeVec(rson);
 FreeVec(dad);
 FreeVec(same);
}

static int read_char()
{
  int chr;

  if (in_memory) {
    if (srclen < srcbuflen) {
      chr = *srcbufptr;
      srcbufptr++;
      srclen++;
      return(chr);
    }
    else {
      return(EOF);
    }
  }
  else {
    return(FGetC(infile));
  }
}

/* read byte from input, ignore EOF */
static unsigned int read_char1(int mode,unsigned short *crc)
{
  unsigned int chr;

  if (in_memory) {
    if (srclen < srcbuflen) {
      chr = *srcbufptr;
      srcbufptr++;
      srclen++;
      if (mode) crcfbb(chr,crc);
      return(chr);
    }
    else {
      return(0);
    }
  }
  else {
    if ((chr = FGetC(infile)) ==-1)
     {
      return(0);
     }
    if (mode) crcfbb(chr,crc);
    return(chr);
  }
}

/* write byte to output */
static int wri_char(int chr,int mode,unsigned short *crc)
{
  if (out_memory) {
    if (destlen < destbuflen) {
      *destbufptr = (char) chr;
      if (mode) crcfbb(chr,crc);
      destbufptr++;
      destlen++;
      return((char) chr);
    }
    else {
      return(EOF);
    }
  }
  else {
    if (mode) crcfbb(chr,crc);
    return (FPutC(outfile,chr));
  }
}



int getlong(unsigned long *textsize,int mode,unsigned short *crc)
{
 int char1,char2,char3,char4;
 char1=read_char1(mode,crc);
 char2=read_char1(mode,crc);
 char3=read_char1(mode,crc);
 char4=read_char1(mode,crc);
 *textsize=    (char1&0xff) +
               (char2&0xff) * 256 +
               (char3&0xff) * 65536 +
               (char4&0xff) * 65536 * 256;
 return(1);

}

int putlong(unsigned long textsize,int mode,unsigned short *crc)
{
 unsigned long c1,c2,c3,c4;
 c1= textsize % 256;
 c2= (textsize/256) % 256;
 c3= (textsize/65536) % 256;
 c4= (textsize/(65536*256)) % 256;
 wri_char(c1,mode,crc);
 wri_char(c2,mode,crc);
 wri_char(c3,mode,crc);
 wri_char(c4,mode,crc);
 return(1);
}



/*static int isdigit(unsigned char di)
{
 if ( (di>='0') && (di<=9) ) return(TRUE);
 return(FALSE);
}

static void strcpy(unsigned char *s1,unsigned char *s2)
{
 while (*s2!='\0') *(s1++)=*(s2++);
} */

static int Tell(BPTR file)
{
 signed long oldptr;

 oldptr=Seek(file,0,OFFSET_CURRENT);
 if (oldptr==-1) return(0);
 return(oldptr);
}

/* Initialize Tree */
static void InitTree ()
{
    register int *p, *e;

    for (p = (rson + N2K + 1), e = (rson + N4K + N4K); p <= e; )
        *p++ = NILE;
    for (p = dad, e = dad + N4K; p < e; )
        *p++ = NILE;
}

/* Insert to node */
static void InsertNode (int r)
{
    register int        p;
    int         cmp;
    register unsigned char  *key;
    register unsigned int   c;
    register unsigned int   i, j;

    cmp = 1;
    key = &text_buf[r];
    i = key[1] ^ key[2];
    i ^= i >> 4;
    p = N2K + 1 + key[0] + ((i & 0x0f) << 8);
    rson[r] = lson[r] = NILE;
    match_length = 0;
    i = j = 1;
    for ( ; ; ) {
        if (cmp >= 0) {
            if (rson[p] != NILE) {
                p = rson[p];
                j = same[p];
            } else {
                rson[p] = r;
                dad[r] = p;
                same[r] = i;
                return;
            }
        } else {
            if (lson[p] != NILE) {
                p = lson[p];
                j = same[p];
            } else {
                lson[p] = r;
                dad[r] = p;
                same[r] = i;
                return;
            }
        }

        if (i > j) {
            i = j;
            cmp = key[i] - text_buf[p + i];
        } else
        if (i == j) {

            for (; i < F; i++)
                if ((cmp = key[i] - text_buf[p + i]) != 0)
                    break;
        }

        if (i > THRESHOLD) {
            if (i > match_length) {
                match_position = ((r - p) & (N2K - 1)) - 1;
                if ((match_length = i) >= F)
                    break;
            } else
            if (i == match_length) {
                if ((c = ((r - p) & (N2K - 1)) - 1) < match_position) {
                    match_position = c;
                }
            }
        }
    }
    same[r] = same[p];
    dad[r] = dad[p];
    lson[r] = lson[p];
    rson[r] = rson[p];
    dad[lson[p]] = r;
    dad[rson[p]] = r;
    if (rson[dad[p]] == p)
        rson[dad[p]] = r;
    else
        lson[dad[p]] = r;
    dad[p] = NILE;  /* remove p */
}

static void link (int n, int p, int q)
{
    register unsigned char *s1, *s2, *s3;
    if (p >= NILE) {
        same[q] = 1;
        return;
    }
    s1 = text_buf + p + n;
    s2 = text_buf + q + n;
    s3 = text_buf + p + F;
    while (s1 < s3) {
        if (*s1++ != *s2++) {
            same[q] = s1 - 1 - text_buf - p;
            return;
        }
    }
    same[q] = F;
}


static void linknode (int p, int q, int r)
{
    int cmp;

    if ((cmp = same[q] - same[r]) == 0) {
        link(same[q], p, r);
    } else if (cmp < 0) {
        same[r] = same[q];
    }

}

static void DeleteNode (int p)
{
    register int  q;

    if (dad[p] == NILE)
        return;         /* has no linked */
    if (rson[p] == NILE) {
        if ((q = lson[p]) != NILE)
            linknode(dad[p], p, q);
    } else
    if (lson[p] == NILE) {
        q = rson[p];
        linknode(dad[p], p, q);
    } else {
        q = lson[p];
        if (rson[q] != NILE) {
            do {
                q = rson[q];
            } while (rson[q] != NILE);
            if (lson[q] != NILE)
                linknode(dad[q], q, lson[q]);
            link(1, q, lson[p]);
            rson[dad[q]] = lson[q];
            dad[lson[q]] = dad[q];
            lson[q] = lson[p];
            dad[lson[p]] = q;
        }
        link(1, dad[p], q);
        link(1, q, rson[p]);
        rson[q] = rson[p];
        dad[rson[p]] = q;
    }
    dad[q] = dad[p];
    if (rson[dad[p]] == p)
        rson[dad[p]] = q;
    else
        lson[dad[p]] = q;
    dad[p] = NILE;
}

/*----------------------------------------------------------------------*/
/*                                  */
/*      HUFFMAN ENCODING                    */
/*                                  */
/*----------------------------------------------------------------------*/


/* TABLE OF ENCODE/DECODE for upper 6bits position information */

/* for encode */
cuchar _storage_text_ p_len[64] = {
    0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
};

cuchar _storage_text_ p_code[64] = {
    0x00, 0x20, 0x30, 0x40, 0x50, 0x58, 0x60, 0x68,
    0x70, 0x78, 0x80, 0x88, 0x90, 0x94, 0x98, 0x9C,
    0xA0, 0xA4, 0xA8, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC,
    0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE,
    0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE,
    0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE,
    0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
    0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};

/* for decode */
cuchar _storage_text_ d_code[256] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
    0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
    0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
    0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
    0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D,
    0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F,
    0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,
    0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
    0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
    0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
    0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B,
    0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F,
    0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,
    0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
    0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B,
    0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
};

cuchar _storage_text_ d_len[256] = {
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
};



//static unsigned freq[T + 1];    /* frequency table */

//static int prnt[T + N_CHAR];    /* points to parent node */
//* notes :
//   prnt[T .. T + N_CHAR - 1] used by
//   indicates leaf position that corresponding to code */

//static int son[T];      /* points to son node (son[i],son[i+]) */

//static unsigned getbuf = 0;
//static uchar getlen = 0;


/* get one bit */
/* returning in Bit 0 */


static int GetBit (int mode,unsigned short *crc)
{
    register unsigned int dx = getbuf;
    register unsigned int c;

    if (getlen <= 8)
        {
            c = read_char1(mode,crc);
            dx |= c << (8 - getlen);
            getlen += 8;
        }
    getbuf = dx << 1;
    getlen--;
    return (dx & 0x8000) ? 1 : 0;
}

/* get one byte */
/* returning in Bit7...0 */
static int GetByte (int mode,unsigned short *crc)
{
    register unsigned int dx = getbuf;
    register unsigned c;

    if (getlen <= 8) {
        c = read_char1(mode,crc);
        dx |= c << (8 - getlen);
        getlen += 8;
    }
    getbuf = dx << 8;
    getlen -= 8;
    return (dx >> 8) & 0xff;
}

const static _storage_text_ int getn_mask[17] = {
        0x0000,
        0x0001, 0x0003, 0x0007, 0x000f,
        0x001f, 0x003f, 0x007f, 0x00ff,
        0x01ff, 0x03ff, 0x07ff, 0x0fff,
        0x1fff, 0x3fff, 0x0fff, 0xffff };
const static _storage_text_ int getn_shift[17] = {
        16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };


/* get N bit */
/* returning in Bit(N-1)...Bit 0 */
static int GetNBits (unsigned int n,int mode,unsigned short *crc)
{
    register unsigned int dx = getbuf;
    register unsigned int c;

    if (getlen <= 8)
        {
            c = read_char1(mode,crc);
            dx |= c << (8 - getlen);
            getlen += 8;
        }
    getbuf = dx << n;
    getlen -= n;
    return (dx >> getn_shift[n]) & getn_mask[n];
}

/* output C bits */
static int Putcode (int l, unsigned int c,int mode,unsigned short *crc)
{
    int len = putlen;
    register unsigned int b = putbuf;
    b |= c >> len;
    if ((len += l) >= 8) {
        if (wri_char(b >> 8,mode,crc) == EOF) return(1);
        if ((len -= 8) >= 8) {
            if (wri_char(b,mode,crc) == EOF) return(1);
            codesize += 2;
            len -= 8;
            b = c << (l - len);
        } else {
            b <<= 8;
            codesize++;
        }
    }
    putbuf = b;
    putlen = len;
    return(0);
}



/* Initialize tree */

static void StartHuff ()
{
    register int i, j;

    for (i = 0; i < N_CHAR; i++) {
        freq[i] = 1;
        son[i] = i + T;
        prnt[i + T] = i;
    }
    i = 0; j = N_CHAR;
    while (j <= R) {
        freq[j] = freq[i] + freq[i + 1];
        son[j] = i;
        prnt[i] = prnt[i + 1] = j;
        i += 2; j++;
    }
    freq[T] = 0xffff;
    prnt[R] = 0;
    putlen = getlen = 0;
    putbuf = getbuf = 0;
}


/* reconstruct tree */
static void reconst ()
{
    register int i, j, k;
    register unsigned f;

    /* correct leaf node into of first half,
       and set these freqency to (freq+1)/2       */
    j = 0;
    for (i = 0; i < T; i++) {
        if (son[i] >= T) {
            freq[j] = (freq[i] + 1) / 2;
            son[j] = son[i];
            j++;
        }
    }
    /* build tree.  Link sons first */
    for (i = 0, j = N_CHAR; j < T; i += 2, j++) {
        k = i + 1;
        f = freq[j] = freq[i] + freq[k];
        for (k = j - 1; f < freq[k]; k--);
        k++;
        {   register unsigned *p, *e;
            for (p = &freq[j], e = &freq[k]; p > e; p--)
                p[0] = p[-1];
            freq[k] = f;
        }
        {   register int *p, *e;
            for (p = &son[j], e = &son[k]; p > e; p--)
                p[0] = p[-1];
            son[k] = i;
        }
    }
    /* link parents */
    for (i = 0; i < T; i++) {
        if ((k = son[i]) >= T) {
            prnt[k] = i;
        } else {
            prnt[k] = prnt[k + 1] = i;
        }
    }
}


/* update given code's frequency, and update tree */

static void update (int c)
{
    register unsigned *p;
    register int i, j, k, l;

    if (freq[R] == MAX_FREQ) {
        reconst();
    }
    c = prnt[c + T];
    do {
        k = ++freq[c];

        /* swap nodes when become wrong frequency order. */
        if (k > freq[l = c + 1]) {
            for (p = freq+l+1; k > *p++; ) ;
            l = p - freq - 2;
            freq[c] = p[-2];
            p[-2] = k;

            i = son[c];
            prnt[i] = l;
            if (i < T) prnt[i + 1] = l;

            j = son[l];
            son[l] = i;

            prnt[j] = c;
            if (j < T) prnt[j + 1] = c;
            son[c] = j;

            c = l;
        }
    } while ((c = prnt[c]) != 0);   /* loop until reach to root */
}

/* static unsigned code, len; */

static int EncodeChar (unsigned c,int mode,unsigned short *crc)
{
    register int *p;
    register unsigned long i;
    register int j, k;

    i = 0;
    j = 0;
    p = prnt;
    k = p[c + T];

    /* trace links from leaf node to root */
    do {
        i >>= 1;

        /* if node index is odd, trace larger of sons */
        if (k & 1) i += 0x80000000;

        j++;
    } while ((k = p[k]) != R) ;
    if (j > 16) {
        if (Putcode(16, (unsigned int)(i >> 16),mode,crc)) return(1);
        if (Putcode(j - 16, (unsigned int)i,mode,crc)) return(1);
    } else {
        if (Putcode(j, (unsigned int)(i >> 16),mode,crc)) return(1);
    }
/*  code = i; */
/*  len = j; */
    update(c);
    return(0);
}

static int EncodePosition (unsigned c,int mode,unsigned short *crc)
{
    unsigned i;

    /* output upper 6bit from table */
    i = c >> 6;
    if (Putcode((int)(p_len[i]), (unsigned int)(p_code[i]) << 8,mode,crc)) return(1);

    /* output lower 6 bit */
    if (Putcode(6, (unsigned int)(c & 0x3f) << 10,mode,crc)) return(1);
    return(0);
}

static int EncodeEnd(int mode,unsigned short *crc)
{
        if (putlen) {
                if (wri_char(putbuf >> 8,mode,crc) == EOF) return(1);
                codesize++;
        }
        return(0);
}


static int DecodeChar (int mode,unsigned short *crc)
{
    register unsigned c;

    c = son[R];

    /* trace from root to leaf,
       got bit is 0 to small(son[]), 1 to large (son[]+1) son node */
    while (c < T) {
        c += GetBit(mode,crc);
        c = son[c];
    }
    c -= T;
    update(c);
    return c;
}

static int DecodePosition (int mode,unsigned short *crc)
{
    unsigned i, j, c;

    /* decode upper 6bit from table */
    i = GetByte(mode,crc);
    c = (unsigned)d_code[i] << 6;
    j = d_len[i];

    /* get lower 6bit */
    j -= 2;
    return c | (((i << j) | GetNBits (j,mode,crc)) & 0x3f);
}


/* compression */

static int Encode (int mode,unsigned short *crc)
{
    register int  i, c, len, r, s, last_match_length;

    textsize = 0;
    StartHuff();
    InitTree();
    s = 0;
    r = N4K - F;
    for (i = s; i < r; i++)
        text_buf[i] = ' ';
    r = N2K - F; /* this is important! */
    for (len = 0; len < F && (c = read_char()) != EOF; len++)
        text_buf[r + len] = c;
    textsize = len;
    for (i = 1; i <= F; i++)
        InsertNode(r - i);
    InsertNode(r);
    do {
        if (match_length > len)
            match_length = len;
        if (match_length <= THRESHOLD) {
            match_length = 1;
            if (EncodeChar(text_buf[r],mode,crc)) return(1);
        } else {
            if (EncodeChar(255 - THRESHOLD + match_length,mode,crc)) return(1);
            if (EncodePosition(match_position,mode,crc)) return(1);
        }
        last_match_length = match_length;
        for (i = 0; i < last_match_length && (c = read_char()) != EOF; i++) {
            DeleteNode(s);
            text_buf[s] = c;
            if (s < F - 1)
                text_buf[s + N2K] = c;
            s = (s + 1) & (N2K - 1);
            r = (r + 1) & (N2K - 1);
            InsertNode(r);
        }

        textsize += i;
        while (i++ < last_match_length) {
            DeleteNode(s);
            s = (s + 1) & (N2K - 1);
            r = (r + 1) & (N2K - 1);
            if (--len) InsertNode(r);
        }
    } while (len > 0);
    if (EncodeEnd(mode,crc)) return(1);
    return(0);
}

static int Decode(unsigned long int textsize,
                  int mode,unsigned short *fbb_check)  /* recover */
{
    register int    i, j, k, r, c;
    register unsigned long int count;

    StartHuff();
    r = N4K - F;
    for (i = 0; i < r; i++)
        text_buf[i] = ' ';
    for (count = 0; count < textsize; ) {
        c = DecodeChar(mode,fbb_check);
        if (c < 256) {
            if (wri_char(c,0,NULL) == EOF) return(1);
            text_buf[r++] = c;
            r &= (N4K - 1);
            count++;
        } else {
            i = (r - DecodePosition(mode,fbb_check) - 1) & (N4K - 1);
            j = c - 255 + THRESHOLD;
            for (k = 0; k < j; k++) {
                c = text_buf[(i + k) & (N4K - 1)];
                if (wri_char(c,0,NULL) == EOF) return(1);
                text_buf[r++] = c;
                r &= (N4K - 1);
                count++;
            }
        }

    }
    return(0);
}


static void init_huf()
{
  textsize = 0;
  codesize = 0;
  getbuf = 0;
  getlen = 0;
  putbuf = 0;
  putlen = 0;
}

int enchuf(char *inputfile,char *outputfile,int mode,char *title)
{
  int error;
  char *inputfptr;

  unsigned short fbb_check;

  inputfptr = inputfile;
  in_memory = 0;
  out_memory = 0;
  fbb_check=0;

  if (mode==2)
   {
    char *sptr;
    sptr=title;
    while (*sptr!='\0')
     {
      crcfbb(*(sptr++),&fbb_check);
     }
   }

  init_huf();
  if ((infile = Open(inputfptr,MODE_OLDFILE)) == NULL) {

    return(1);
  }
  if ((outfile = Open(outputfile,MODE_NEWFILE)) == NULL) {
    Close(infile);
    return(1);
  }
  error = 0;
  Seek(infile, 0L, OFFSET_END);
  textsize = Tell(infile);
  if (mode) {
    wri_char(0,0,NULL);
    wri_char(0,0,NULL);
   }
  if (!putlong(textsize,mode,&fbb_check))
    {
     error = 1;
    }

  if (!error) {
    if (textsize == 0)
      error = 1;
    if (!error) {
      Seek(infile,0,OFFSET_BEGINNING);
      if (Encode(mode,&fbb_check))
        error = 1;
      if ( (!error) && (mode) )
       {
        unsigned char c1,c2;
        Seek(outfile,0,OFFSET_BEGINNING);
        c1=fbb_check & 0xff;
        c2=(fbb_check >> 8) & 0xff;
        wri_char(c1,0,NULL);
        wri_char(c2,0,NULL);
       }
    }
  }
  Close(infile);
  Close(outfile);
  return(error);
}



int dechuf(char *inputfile,char *outputfile,int mode,char *title)
{
  int error;
  char *outputfptr;
  unsigned short fbb_check;

  unsigned short fbb_check1;



  fbb_check=0;fbb_check1=0;
  if (mode==2)
   {
    char *sptr;
    sptr=title;
    while (*sptr!='\0')
     {
      crcfbb(*(sptr++),&fbb_check);
     }
   }

  outputfptr = outputfile;
  in_memory = 0;
  out_memory = 0;
  init_huf();
  if ((infile = Open(inputfile,MODE_OLDFILE)) == NULL) {
    return(1);
  }
  if ((outfile = Open(outputfptr,MODE_NEWFILE)) == NULL) {
    Close(infile);
    return(1);
  }
  if (mode)
   {
    unsigned char c1,c2;
    c1=read_char1(0,NULL);
    c2=read_char1(0,NULL);
    fbb_check1= ( ((unsigned long) c1) & 0xff) +
                ( ((unsigned long) c2) & 0xff) *256;

   }
  error = 0;
   {
    unsigned long txsize;
    if (!getlong(&txsize,mode,&fbb_check)) error=1;
    textsize=txsize;
   }
  if (!error) {
    if (textsize == 0)
      error = 1;
    if (!error) {
      if (textsize > maxcompfilesize)
        error = 1;
      if (!error) {
        if (Decode(textsize,mode,&fbb_check))
          error = 1;
        if (mode)
         {
          if (fbb_check!=fbb_check1) error=2;
         }
      }
    }
  }
  Close(infile);
  Close(outfile);
  return(error);
}

