/*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

#include "global.h"
#include "QSO.h"
#include "GUIProtos.h"

#ifdef AmigaOS

#include <exec/memory.h>
#include <clib/exec_protos.h>
#ifdef __GNUC__
extern void *SysBase;
#include <inline/exec.h>
#endif /*__GNUC__*/


#else

#include "amigaemu.h"

#endif /*AmigaOS*/


static const unsigned char XPacket_MainKey[256]={
        0x3B,0x5F,0x39,0x43,0x4C,0x3E,0x4B,0x29,
        0x22,0x39,0x17,0x18,0x3F,0x0E,0x37,0x1A,
        0x2A,0x2A,0x39,0x2C,0x24,0x43,0x4C,0x35,
        0x48,0x22,0x38,0x49,0x5D,0x34,0x18,0x4A,
        0x4E,0x0D,0x23,0x2D,0x26,0x1C,0x2F,0x54,
        0x0C,0x23,0x25,0x36,0x4A,0x35,0x33,0x51,
        0x1F,0x17,0x47,0x54,0x17,0x1F,0x20,0x36,
        0x2C,0x20,0x1C,0x41,0x10,0x48,0x45,0x1F,
        0x0C,0x4B,0x16,0x3F,0x53,0x17,0x40,0x2D,
        0x63,0x40,0x54,0x20,0x37,0x22,0x29,0x4C,
        0x29,0x21,0x37,0x30,0x31,0x2C,0x11,0x19,
        0x3B,0x25,0x25,0x54,0x4A,0x60,0x0D,0x26,
        0x47,0x2F,0x2D,0x2F,0x42,0x1B,0x1E,0x33,
        0x1C,0x38,0x62,0x3E,0x61,0x4E,0x21,0x2A,
        0x60,0x2C,0x36,0x11,0x2A,0x49,0x1D,0x3F,
        0x29,0x1B,0x2B,0x18,0x2B,0x2B,0x10,0x24,
        0x7E,0x02,0x1C,0x75,0x7D,0x07,0x17,0x69,
        0x10,0x35,0x21,0x28,0x16,0x08,0x14,0x1D,
        0x30,0x13,0x06,0x1D,0x6F,0x68,0x76,0x04,
        0x17,0x70,0x6B,0x65,0x7D,0x45,0x7E,0x56,
        0x47,0x6D,0x20,0x05,0x31,0x09,0x48,0x23,
        0x77,0x29,0x15,0x6F,0x03,0x79,0x64,0x2C,
        0x46,0x58,0x43,0x50,0x4B,0x37,0x29,0x0C,
        0x16,0x30,0x61,0x0C,0x09,0x55,0x57,0x49,
        0x54,0x1A,0x74,0x45,0x28,0x13,0x3A,0x23,
        0x6D,0x37,0x60,0x63,0x02,0x1E,0x6E,0x00,
        0x6D,0x1E,0x11,0x06,0x1C,0x20,0x35,0x2A,
        0x3C,0x1E,0x71,0x6F,0x34,0x47,0x46,0x4A,
        0x09,0x6A,0x00,0x6F,0x34,0x5C,0x28,0x02,
        0x0B,0x7E,0x06,0x03,0x70,0x40,0x64,0x41,
        0x06,0x56,0x3D,0x1C,0x58,0x37,0x76,0x34,
        0x24,0x02,0x03,0x00,0x1F,0xFF,0x03,0x3B,
       };


static const unsigned short Huffman_Codes[258]=
 {
        0xAB2C,0xAA84,0x9FC4,0xAB3C,0xAB1C,0xAAFC,0xAAEC,0xAAD4,
        0xAAB4,0xF340,0xAAA4,0x7D64,0xAADC,0xF400,0xAA94,0x9FF4,

        0x9FD4,0x7D74,0xAB44,0xAB34,0xAB24,0xAB14,0xAB04,0xAAF4,
        0xAAE4,0xAB60,0xAB0C,0xAACC,0xAABC,0xAAAC,0xAA9C,0xAA8C,

        0xC000,0x3A80,0xABC0,0x0060,0x7D40,0xAB5C,0x0000,0xAB58,
        0x7C00,0x3C80,0x7D00,0x0010,0x1200,0x7A00,0xB800,0x3200,

        0x2200,0xF600,0x3D00,0x9E00,0xBD80,0x7C80,0x0080,0xAA00,
        0xBD00,0x9F00,0x0300,0xAB78,0xAB68,0x3C00,0x3000,0x0020,

        0x7D50,0x3800,0x7800,0x9C00,0xFE00,0x2400,0xBC00,0x0200,
        0x0100,0xF100,0x0040,0x3100,0xF200,0x3400,0x1C00,0x1E00,

        0xBE00,0xABA0,0x3E00,0x1400,0x3600,0xF380,0xF080,0x2000,
        0xFC00,0x9F80,0x9E80,0xAB90,0x3B80,0xAB80,0xAB54,0x3A50,

        0xAB50,0xA000,0x1800,0x9800,0x7000,0x4000,0x0400,0xAC00,
        0xF800,0x6000,0x3A00,0xFD00,0x2800,0xB000,0x8000,0xB400,

        0x1000,0x7D20,0xE000,0x9000,0xE800,0x0800,0xF700,0xA800,
        0x7D80,0xF300,0x7E00,0xAB48,0x3A48,0xAB4C,0x3A60,0x9FFC,

        0x9FEC,0x2100,0x9FDC,0x9FCC,0xF000,0x7D7C,0x7D6C,0x3A40,
        0xAB40,0xAB38,0xAB30,0xAB28,0xAB20,0xAB18,0xAB70,0xAB10,

        0xAB08,0xAB00,0xAAF8,0xAAF0,0x3B00,0xAAE8,0xAAE0,0xAAD8,
        0xAAD0,0xAB64,0x7D30,0xAAC8,0xAAC0,0xAAB8,0xAAB0,0xAAA8,

        0xAAA0,0xAA98,0xAA90,0xAA88,0xAA80,0x9FF8,0x9FF0,0x9FE8,
        0x9FE0,0x9FD8,0x9FD0,0x9FC8,0x9FC0,0x7D78,0x7D70,0x3A58,

        0x7D68,0x7D60,0xAB46,0xAB42,0xAB3E,0xAB3A,0xAB36,0xAB32,
        0xAB2E,0xAB2A,0xAB26,0xAB22,0xAB1E,0xAB1A,0xAB16,0xAB12,

        0xAB0E,0xAB0A,0xAB06,0xAB02,0xAAFE,0xAAFA,0xAAF6,0xAAF2,
        0xAAEE,0xAAEA,0xAAE6,0xAAE2,0xAADE,0xAADA,0xAAD6,0xAAD2,

        0xAACE,0xAACA,0xAAC6,0xAAC2,0xAABE,0xAABA,0xAAB6,0xAAB2,
        0xAAAE,0xAAAA,0xAAA6,0xAAA2,0xAA9E,0x3A70,0xAA9A,0xAA96,

        0xAA92,0x3080,0xAA8E,0xAA8A,0xAA86,0xAA82,0x9FFE,0x9FFA,
        0x9FF6,0x9FF2,0x9FEE,0x9FEA,0x9FE6,0x9FE2,0x9FDE,0x9FDA,

        0x9FD6,0x9FD2,0x9FCE,0x9FCA,0x9FC6,0x9FC2,0x7D7E,0x7D7A,
        0x7D76,0x7D72,0x7D6E,0x7D6A,0x7D66,0x7D62,0x3A46,0x3A44,

        0xAAC4,0x9FE4
       };

static const unsigned char Huffman_Lengths[258]={
        15,15,15,15,15,15,15,15,15,10,15,15,15, 7,15,15,
        15,15,15,15,15,15,15,15,15,14,15,15,15,15,15,15,
         3, 9,10,11,12,14,12,14, 9, 9,11,12, 7, 7, 6, 7,
         7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 8,13,13, 9, 9,11,

        12, 7, 7, 7, 7, 6, 8, 8, 8, 8,11, 8, 8, 7, 7, 7,
         7,11, 7, 6, 7, 9, 9, 8, 8,10, 9,12, 9,12,14,13,
        14, 5, 6, 6, 5, 3, 6, 6, 6, 4,10, 8, 5, 6, 4, 6,
        07,12, 5, 5, 5, 5, 8, 7, 9,10, 7,14,13,14,12,15,

        15, 8,15,15, 9,15,15,14,15,15,15,15,15,15,13,15,
        15,15,15,15, 9,15,15,15,15,14,12,15,15,15,15,15,
        15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,13,
        15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,

        15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
        15,15,15,15,15,15,15,15,15,15,15,15,15,12,15,15,
        15, 9,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
        15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,

        15,15
       };


/* QSO_InitHuffDecompress: Initalize huffman speedup tables for
   fast decoding of the static huffman code.
   A lookup table for the huffman encoded byte stream is created */

int QSO_InitHuffDecompress(void)
 {
  int i;
  HuffmanSpeedup=AllocVec(sizeof(*HuffmanSpeedup)*32768,0);
  if (HuffmanSpeedup==NULL) return(-1);
  for (i=257;i>=0;--i)
   {
    int k,kmax,o;
    int huffcode,hufflen;

    huffcode=Huffman_Codes[i];
    hufflen=Huffman_Lengths[i];

    kmax=(1<<(15-hufflen));

    o=huffcode>>1;

    for (k=0;k<kmax;++k,++o)
     {
      HuffmanSpeedup[o]=i;
     }

   }
  return(0);
 }


/* return a uncompressed frame for SP/XP huffman */

static int QSO_HuffmanNoCompression(const unsigned char *source,
                                    unsigned char *target,
                                    unsigned char sourcelen,int xp)
 {
  const unsigned char *sptr;
  unsigned char *tptr;
  int tlen,slen;

  sptr=source;
  tptr=target;
  slen=sourcelen;
  tlen=1;

  *(tptr++)=0xff;

  if (xp)
   {
    *(tptr++)=sourcelen;
    ++tlen;
   }

  while (slen>0)
   {
    ++tlen;
    --slen;
    *(tptr++)=*(sptr++);
   }
  return(tlen);
 }

/* QSO_HuffmanCompress : Compress function for SP-like static huffman
   returns target length of compressed bytestream or -1 for error
   targes has to be a 256 * unsigned char buffer                       */

signed long QSO_HuffmanCompress(const unsigned char *source,unsigned char *target,
                        unsigned long sourcelen)
 {
  const unsigned char *sptr;
  unsigned char *tptr;
  int slen,tlen,tinsh;
  unsigned long tshift;


  sptr=source;
  tptr=target;
  slen=sourcelen;
  tlen=0;
  tinsh=0;
  tshift=0;

  if ( (slen>256) || (slen<=0) || (HuffmanSpeedup==NULL) ) return(-1);

  *(tptr++)=slen-1;

  while (slen>0)
   {
    while (tinsh>=16)
     {
      if (tlen>=254)
       {
        return(QSO_HuffmanNoCompression(source,target,sourcelen,0));
       }
      tinsh-=16;
      tlen+=2;
      *(tptr++)=tshift>>24;
      *(tptr++)=tshift>>16;
      tshift=tshift<<16;
     }
    tshift= tshift | ( Huffman_Codes[*sptr] << (16-tinsh));
    tinsh+= Huffman_Lengths[*sptr];
    sptr++;
    --slen;
   }

  while (tinsh>0)
   {
    if (tlen>=255)
     {
      return(QSO_HuffmanNoCompression(source,target,sourcelen,0));
     }

    tinsh-=8;
    *(tptr++)=tshift>>24;
    tshift=tshift<<8;
    tlen++;
   }

  tlen++;
  if (tlen>=sourcelen)
   {
    return(QSO_HuffmanNoCompression(source,target,sourcelen,0));
   }
  return(tlen);
 }


/* QSO_HuffmanDecompress : Decompress function for SP-like static huffman
   returns decompressed length or -1 if decompression error
   target has to be a 320 * unsigned char buffer                        */

signed long QSO_HuffmanDecompress(const unsigned char *source,unsigned char *target,
                          unsigned long sourcelen)
 {
  const unsigned char *sptr;
  unsigned char *tptr;
  int slen,tlen,sclen,sinsh;
  unsigned long sshift;

  sptr=source;
  tptr=target;
  slen=sourcelen;
  tlen=0;

  if ( (slen<=0) || (HuffmanSpeedup==NULL)) return(-1);

  if (*sptr==0xff)
   {
    --slen;
    if (slen==0) return(-1);
    ++sptr;
    while (slen>0)
     {
      *(tptr++)=*(sptr++);
      --slen;++tlen;
     }
    return(tlen);
   }

  sclen=(*sptr++)+1;
  slen--;
  sinsh=0;
  sshift=0;
  while ( tlen<sclen)
   {
    unsigned long hbyte;
    int dbyte,dbytel;
    while ( (sinsh<15) && (slen>0) )
     {
      sshift|= (*(sptr++)) << (24-sinsh);
      sinsh+=8;
      slen--;
     }

    hbyte=sshift>>17;

    if (hbyte==Huffman_Codes[256])
     {
      /* huff 256 not used */
      return(-1);
     }
    if (hbyte==Huffman_Codes[257])
     {
      /* huff 257 not used */
      return(-1);
     }

    dbyte=HuffmanSpeedup[hbyte];
    dbytel=Huffman_Lengths[dbyte];
    if (dbytel>sinsh) return (-1);

    sinsh-=dbytel;
    sshift=sshift<<dbytel;

    if (tlen>=sclen) return(-1);
    *(tptr++)=dbyte;
    tlen++;
   }

  if (slen!=0) return(-1);
  if (sinsh>7) return(-1);
  if (sshift!=0) return(-1);
  return(tlen);
 }


/* QSO_HuffmanCompressXP : Compress function for XP-like static huffman
   returns target length of compressed bytestream or -1 for error
   targes has to be a 256 * unsigned char buffer                       */

signed long QSO_HuffmanCompressXP(const unsigned char *source,
                          void (*func)(const unsigned char *c,int len,int chan),
                          struct QSO *qso,int sourcelen,int channel)

 {
  const unsigned char *sptr;
  unsigned char *tptr;
  int slen,tlen,tleng,tinsh;
  unsigned long tshift;


  sptr=source;
  slen=sourcelen;
  tleng=0;

  while (slen>0)
   {
    unsigned char tempo[512];
    int sdec;

    tptr=tempo;
    tlen=0;
    tinsh=0;
    tshift=0;
    sdec=0;

    if ( (slen<=0) || (HuffmanSpeedup==NULL) ) return(-1);

    *(tptr++)=slen-1;

    while (slen>0)
     {
      int tenc=0;
      while (tinsh>=16)
       {
        if (tlen>=248)
         {
          tenc=1;
          break;
         }
        if (sdec>=250)
         {
          tenc=1;
          break;
         }
        tinsh-=16;
        tlen+=2;
        *(tptr++)=tshift>>24;
        *(tptr++)=tshift>>16;
        tshift=tshift<<16;
       }
      if (tenc) break;
      tshift= tshift | ( Huffman_Codes[*sptr] << (16-tinsh));
      tinsh+= Huffman_Lengths[*sptr];
      sptr++;
      --slen;
      ++sdec;
     }


    while (tinsh>0)
     {
      tinsh-=8;
      *(tptr++)=tshift>>24;
      tshift=tshift<<8;
      tlen++;
     }


    tempo[0]=tlen;
    tlen++;
    if (tlen>sourcelen)
     {
      tlen=QSO_HuffmanNoCompression(source,tempo,sourcelen,1);
     }

    if ( (qso!=NULL) &&
         (qso->CompressedMode&(1<<COMPRESSFLAG_XPTABLE_COMPLETE)) )
     {
      int xpoff,i;

      tptr=tempo+2;
      xpoff=2;
      for (i=0;i<(tlen-2);++i)
       {

        *tptr=(*tptr)^qso->XPacketKey[xpoff]^XPacket_MainKey[i];
        ++tptr;
        xpoff++;
        if (xpoff>=127) xpoff=0;
       }
     }
    func(tempo,tlen,channel);
    tleng+=tlen;
   }
  return(tleng);
 }


/* QSO_HuffmanDecompressXP : Decompress function for XP-like static huffman
   returns decompressed length or -1 if decompression error
   target has to be a 1024 * unsigned char buffer                        */

signed long QSO_HuffmanDecompressXP(const unsigned char *source,unsigned char *target,
                          struct QSO *qso,unsigned long sourcelen)
 {
  const unsigned char *sptr;
  unsigned char *tptr;
  int slen,tlen,tlenc,sclen,sinsh;
  unsigned long sshift;

  sptr=source;
  slen=sourcelen;
  tlen=0;
  tptr=target;
  if ( (slen<=0) || (HuffmanSpeedup==NULL)) return(-1);


  while (slen>0)
   {
    if (*sptr==0xff)
     {
      if (slen<=2) return(-1);
      slen-=2;
      ++sptr;
      sclen=*(sptr++);
      if (sclen>slen) return(-1);
      if (
          (qso!=NULL) &&
          (qso->CompressedMode&(1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS)) )
       {
        if (qso->CompressedMode&(1<<COMPRESSFLAG_XPTABLE_COMPLETE))
         {
          int ko1,ko2;
          ko1=2;
          ko2=0;
          while (sclen>0)
           {
            *(tptr++)=*(sptr++)^qso->XPacketKey[ko1]^XPacket_MainKey[ko2];
            ko1++;ko2++;sclen--;slen--;++tlen;
            if (ko1>=127) ko1=0;
           }

          continue;
         }
         else
         {
          unsigned char *kptr;
          signed long chan;
          if (sclen!=127) return(-1);
          qso->CompressedMode|=1<<COMPRESSFLAG_XPTABLE_COMPLETE;
          kptr=qso->XPacketKey;
          while (sclen>0)
           {
            *(kptr++)=*(sptr++);
            sclen--;slen--;
           }
          chan=(qso-QSOs)+1;
          QSO_FlushXPacketDelay(chan);
          continue;
         }
       }
      while (sclen>0)
       {
        *(tptr++)=*(sptr++);
        --sclen;--slen;++tlen;
       }
      continue;
     }

    sclen=(*sptr++);
    if (sclen>slen) return(-1);
    slen--;
    sinsh=0;
    sshift=0;
    tlenc=0;

    while (1)
     {
      unsigned long hbyte;
      int dbyte,dbytel;
      while ( (sinsh<15) && (sclen>0) )
       {
        if (
            (qso!=NULL) &&
            (qso->CompressedMode&(1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS)) )
         {
          if (qso->CompressedMode&(1<<COMPRESSFLAG_XPTABLE_COMPLETE))
           {
            if (tlenc==0)
             {
              sshift|= (*(sptr++)) << (24-sinsh);
             }

             else
             {
              sshift|=( (*(sptr++)) ^ qso->XPacketKey[ (tlenc+1)%127] ^
                                    XPacket_MainKey[tlenc-1])
                                     << (24-sinsh);
             }
            tlenc++;
           }
           else
           {
            return(-1);
           }
         }
         else
         {
          sshift|= (*(sptr++)) << (24-sinsh);
         }
        sinsh+=8;
        slen--;sclen--;
       }

      hbyte=sshift>>17;

      if (hbyte==Huffman_Codes[256])
       {
        /* huff 256 not used */
        return(-1);
       }
      if (hbyte==Huffman_Codes[257])
       {
        /* huff 257 not used */
        return(-1);
       }

      dbyte=HuffmanSpeedup[hbyte];
      dbytel=Huffman_Lengths[dbyte];
      if (dbytel>sinsh) break;

      sinsh-=dbytel;
      sshift=sshift<<dbytel;

      if (tlen>=1000) return(-1);
      *(tptr++)=dbyte;
      tlen++;
     }

    if (sinsh>8) return(-1);
    if (sshift!=0) return(-1);

   }
  if (tlen==0) return(-1);
  return(tlen);
 }


