/**********************************************************************
 *   Z M . C
 *    ZMODEM protocol primitives
 *    01-19-87  Chuck Forsberg Omen Technology Inc
 *
 * 29 July 89:
 * Major overhaul by Rick Huebner for adaptation to Amiga XPR protocol spec
 *
 * 28 October 89:
 * Converted to Lattice C 5.04
 *
 * 15 November 1991
 * Code added to support CRC-32 by William M. Perkins.
 **********************************************************************/

/*#include <proto/all.h>
#include <exec/types.h>
#include <ctype.h>
#include <stdio.h>*/

#include <string.h>

#include "xproto.h"
#include "zmodem.h"
#include "xprzmodem.h"
#include "zcrc.h"

static char *frametypes[] =
{
   "Carrier Lost",         /* -3 */
   "TIMEOUT",              /* -2 */
   "ERROR",                /* -1 */
#define FTOFFSET 3
   "ZRQINIT",
   "ZRINIT",
   "ZSINIT",
   "ZACK",
   "ZFILE",
   "ZSKIP",
   "ZNAK",
   "ZABORT",
   "ZFIN",
   "ZRPOS",
   "ZDATA",
   "ZEOF",
   "ZFERR",
   "ZCRC",
   "ZCHALLENGE",
   "ZCOMPL",
   "ZCAN",
   "ZFREECNT",
   "ZCOMMAND",
   "ZSTDERR",
   "xxxxx"
#define FRTYPES 22      /* Total number of frame types in this array */
                        /*  not including psuedo negative entries */
   };

static UBYTE DLE_actions[] =
{
   0,0,0,0,0,0,0,0, 0,0,0,0,0,2,0,0, 1,1,0,1,0,0,0,0, 1,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,

   0,0,0,0,0,0,0,0, 0,0,0,0,0,2,0,0, 1,1,0,1,0,0,0,0, 0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0
   };

/**********************************************************
 *	void zsbhdr(struct Vars *v, USHORT type)
 *
 * Send ZMODEM binary header hdr of type type
 **********************************************************/
void zsbhdr(struct Vars *v, USHORT type)
{
   UBYTE  *hdr = v->Txhdr;
   int  n;
   ULONG  crc32;
   USHORT crc;

   xsendline(v, ZPAD);
   xsendline(v, ZDLE);

   if (v->Crc32t = v->Txfcs32)	/* zsbh32() */
   {
      xsendline(v, ZBIN32);
      zsendline(v, (UBYTE) type);

#ifdef DEBUGLOG
      mysprintf(v->Msgbuf, "zsbh32: %s %lx\n", frametypes[type + FTOFFSET],
         v->Txpos);
      dlog(v, v->Msgbuf);
#endif
      crc32 = 0xFFFFFFFFL;
      crc32 = UPDC32(type, crc32);

      for (n = 4; --n >= 0; ++hdr)
      {
	 crc32 = UPDC32((0377 & *hdr), crc32);
	 zsendline(v, *hdr);
	 }
      crc32 = ~crc32;
      for (n = 4; --n >= 0;)
      {
	 zsendline(v, (int) crc32);
	 crc32 >>= 8;
	 }
      }
   else
   {
      xsendline(v, ZBIN);
      zsendline(v, (UBYTE) type);

#ifdef DEBUGLOG
      mysprintf(v->Msgbuf, "zsbhdr: %s %lx\n", frametypes[type + FTOFFSET],
         v->Txpos);
      dlog(v, v->Msgbuf);
#endif
      crc = updcrc(type, 0);
      for (n = 4; --n >= 0; )
      {
	 zsendline(v, *hdr);
	 crc = updcrc(((USHORT) (*hdr++)), crc);
	 }

      crc = updcrc(((USHORT) 0), crc);
      crc = updcrc(((USHORT) 0), crc);
      zsendline(v, (UBYTE) (crc >> 8));
      zsendline(v, (UBYTE) crc);
      }
   }	/* End of void zsbhdr() */

/**********************************************************
 *	void zshhdr(struct Vars *v, USHORT type)
 *
 * Send ZMODEM HEX header hdr of type type
 **********************************************************/
void zshhdr(struct Vars *v, USHORT type)
{
   UBYTE *hdr = v->Txhdr;
   int n;
   USHORT crc;

#ifdef DEBUGLOG
   mysprintf(v->Msgbuf, "zshhdr: %s %lx\n", frametypes[type + FTOFFSET],
      v->Rxbytes);
   dlog(v, v->Msgbuf);
#endif
   sendline(v, ZPAD);
   sendline(v, ZPAD);
   sendline(v, ZDLE);
   sendline(v, ZHEX);
   zputhex(v, (UBYTE) type);
   v->Crc32t = 0;

   crc = updcrc(type, 0);
   for (n = 4; --n >= 0; )
   {
      zputhex(v, *hdr);
      crc = updcrc(((USHORT) (*hdr++)), crc);
      }

   crc = updcrc(((USHORT) 0), crc);
   crc = updcrc(((USHORT) 0), crc);
   zputhex(v, (UBYTE) (crc >> 8));
   zputhex(v, (UBYTE) crc);

   /* Make it printable on remote machine */
   sendline(v, '\r');
   sendline(v, '\n');
   /* Uncork the remote in case a fake XOFF has stopped data flow */
   if (type != ZFIN)
      sendline(v, XON);
   }	/* End of void zshhdr() */

/**********************************************************
 *	void zsdata() and void zsda32()
 *
 * Send binary array buf of length length, with ending
 * ZDLE sequence frameend
 **********************************************************/
void zsdata(struct Vars *v, short length, USHORT frameend)
{
   UBYTE  *buf, *outptr, c;
   USHORT crc;
   ULONG  crc32;

#ifdef DEBUGLOG
   mysprintf(v->Msgbuf, v->Crc32t ? "zsda32: length=%ld end=%lx\n" 
      : "zsdata: length=%ld end=%lx\n", (long) length, (long) frameend);
   dlog(v, v->Msgbuf);
#endif

   buf = v->Pktbuf;
   outptr = v->Outbuf + v->Outbuflen;
   crc32 = 0xFFFFFFFFL;			/* zsda32() */
   crc = 0;

   while (--length >= 0)
   {
      switch (DLE_actions[c = *buf])
      {
      case 2:
         if (v->Lastzsent != '@')
            goto sendit;
         /* Fallthrough */
      case 1:
         *outptr++ = ZDLE;
         c ^= 0x40;
sendit:
      case 0:
         *outptr++ = v->Lastzsent = c;
         }
      if (v->Crc32t)			/* zsda32() */
	 crc32 = UPDC32((0xFF & *buf++), crc32);
      else
	 crc = updcrc(((USHORT) (*buf++)), crc);
      }
   *outptr++ = ZDLE;
   *outptr++ = frameend;
   v->Outbuflen = outptr - v->Outbuf;

   if (v->Crc32t)				/* zsda32() */
   {
      crc32 = UPDC32(frameend, crc32);
      crc32 = ~crc32;
      for (length = 4; --length >= 0; )
      {
	 zsendline(v, (int) crc32);
	 crc32 >>= 8;
	 }
      }
   else
   {
      crc = updcrc(frameend, crc);
      crc = updcrc(((USHORT) 0), crc);
      crc = updcrc(((USHORT) 0), crc);
      zsendline(v, (UBYTE) (crc >> 8));
      zsendline(v, (UBYTE) crc);
      }

   if (frameend == ZCRCW)
      xsendline(v, XON);
   }	/* End of void zsdata() */

/**********************************************************
 *	short zrdata(struct Vars *v, UBYTE *buf, short length)
 *
 * Receive array buf of max length with ending ZDLE sequence
 * and CRC-16.  Returns the ending character or error code.
 **********************************************************/
int zrdata(struct Vars *v, UBYTE *buf, short length)
{
   int c, d;
   USHORT crc;

   if (v->Rxframeind == ZBIN32)
      return zrdat32(v, buf, length);

   crc = v->Rxcount = 0;
   for (;;)
   {
      if ((c = zdlread(v)) & ~0xFF)
      {
crcfoo:
         switch (c)
	 {
         case GOTCRCE:
         case GOTCRCG:
         case GOTCRCQ:
         case GOTCRCW:
            crc = updcrc(((d = c) & 0xFF), crc);
            if ((c = zdlread(v)) & ~0xFF)
	       goto crcfoo;
            crc = updcrc(c, crc);
            if ((c = zdlread(v)) & ~0xFF)
	       goto crcfoo;
            crc = updcrc(c, crc);
            if (crc & 0xFFFF)
	    {
               strcpy(v->Msgbuf, "CRC-Fehler in Datenpaket ");
               return ERROR;
               }
#ifdef DEBUGLOG
            mysprintf(v->Msgbuf, "zrdata: cnt = %ld ret = %lx\n",
	       (long) v->Rxcount, (long) d);
            dlog(v, v->Msgbuf);
#endif
            return d;
         case GOTCAN:
            strcpy(v->Msgbuf, "Sender hat abgebrochen ");
            return ZCAN;
         case TIMEOUT:
            strcpy(v->Msgbuf, "Paket-Timeout ");
            return c;
         case RCDO:
            return c;
         default:
            strcpy(v->Msgbuf, "Unbekanntes Paket ");
            return c;
            }
         }
      if (--length < 0) 
      {
         strcpy(v->Msgbuf, "zu langes Datenpaket ");
         return ERROR;
         }
      ++v->Rxcount;
      *buf++ = c;
      crc = updcrc(c, crc);
      continue;
      }
   }	/* End of short zrdata() */

/**********************************************************
 *	short zrdat32(struct Vars *v, UBYTE *buf, short length)
 *
 * Receive array buf of max length with ending ZDLE sequence
 * and CRC-32.  Returns the ending character or error code.
 **********************************************************/
int zrdat32(struct Vars *v, UBYTE *buf, short length)
{
   int c, d;
   ULONG crc32;

   crc32 = 0xFFFFFFFFL;
   v->Rxcount = 0;

   for (;;)
   {
      if ((c = zdlread(v)) & ~0xFF)
      {
crcfoo:
         switch (c)
	 {
         case GOTCRCE:
         case GOTCRCG:
         case GOTCRCQ:
         case GOTCRCW:
	    d = c;
	    c &= 0xFF;
            crc32 = UPDC32(c, crc32);
            if ((c = zdlread(v)) & ~0xFF)
	       goto crcfoo;
            crc32 = UPDC32(c, crc32);
            if ((c = zdlread(v)) & ~0xFF)
	       goto crcfoo;
            crc32 = UPDC32(c, crc32);
            if ((c = zdlread(v)) & ~0xFF)
	       goto crcfoo;
            crc32 = UPDC32(c, crc32);
            if ((c = zdlread(v)) & ~0xFF)
	       goto crcfoo;
            crc32 = UPDC32(c, crc32);
	    if (crc32 != 0xDEBB20E3)
	    {
               strcpy(v->Msgbuf, "CRC-Fehler in Datenpaket");
               return ERROR;
               }
#ifdef DEBUGLOG
            mysprintf(v->Msgbuf, "zrdat32: cnt = %ld ret = %lx\n",
	       (long) v->Rxcount, (long) d);
            dlog(v, v->Msgbuf);
#endif
            return d;
         case GOTCAN:
            strcpy(v->Msgbuf, "Sender hat abgebrochen ");
            return ZCAN;
         case TIMEOUT:
            strcpy(v->Msgbuf, "Paket-Timeout ");
            return c;
         case RCDO:
            return c;
         default:
            strcpy(v->Msgbuf, "Unbekanntes Paket ");
            return c;
            }
         }
      if (--length < 0) 
      {
         strcpy(v->Msgbuf, "zu langes Datenpaket ");
         return ERROR;
         }
      ++v->Rxcount;
      *buf++ = c;
      crc32 = UPDC32(c, crc32);
      continue;
      }
   }	/* End of short zrdat32() */

/**********************************************************
 *	short zgethdr(struct Vars *v)
 *
 * Read a ZMODEM header to hdr, either binary or hex.
 *  On success return type of header.
 *  Otherwise return negative on error.
 **********************************************************/
int zgethdr(struct Vars *v)
{
   int c, cancount;
   long n;
#ifdef DEBUGLOG
   UBYTE msgbuf [128];
#endif

   n = 8192;   /* Max characters before start of frame */
   cancount = 5;
again:
   v->Rxframeind = v->Rxtype = 0;
   switch (c = noxrd7(v))
   {
   case RCDO:
   case TIMEOUT:
      goto fifi;
   case CAN:
      if (--cancount <= 0)
      {
         c = ZCAN;
         goto fifi;
         }
   default:
agn2:
      if (--n <= 0)
      {
         strcpy(v->Msgbuf, "Entstörungsfehler ");
         return ERROR;
         }
      if (c != CAN)
	 cancount = 5;
      goto again;
   case ZPAD:              /* This is what we want. */
      break;
      }
   cancount = 5;
splat:
   switch (c = noxrd7(v))
   {
   case ZPAD:
      goto splat;
   case RCDO:
   case TIMEOUT:
      goto fifi;
   default:
      goto agn2;
   case ZDLE:              /* This is what we want. */
      break;
      }

   switch (c = noxrd7(v))
   {
   case RCDO:
   case TIMEOUT:
      goto fifi;
   case ZBIN:
      v->Rxframeind = ZBIN;
      v->Crc32 = FALSE;
      c =  zrbhdr(v);
      break;
   case ZBIN32:
      v->Crc32 = v->Rxframeind = ZBIN32;
      c =  zrbhdr32(v);
      break;
   case ZHEX:
      v->Rxframeind = ZHEX;
      v->Crc32 = FALSE;
      c =  zrhhdr(v);
      break;
   case CAN:
      if (--cancount <= 0)
      {
         c = ZCAN;
         goto fifi;
         }
      goto agn2;
   default:
      goto agn2;
      }
   v->Rxpos = rclhdr(v);
fifi:
   switch (c)
   {
   case GOTCAN:
      c = ZCAN;
   case ZNAK:
   case ZCAN:
   case ERROR:
   case TIMEOUT:
   case RCDO:
      mysprintf(v->Msgbuf, "%s %s ", frametypes[c + FTOFFSET],
         (c >= 0) ? "header" : "error");
#ifdef DEBUGLOG
   default:
      if (c >= -3 && c <= FRTYPES)
         mysprintf(msgbuf, "zgethdr: %s @ %ld\n", frametypes[c + FTOFFSET],
	    v->Rxpos);
      else
         mysprintf(msgbuf, "zgethdr: Unknown type %ld @ %ld\n", (long) c,
	    v->Rxpos);
      dlog(v, msgbuf);
#endif
      }
   return c;
   }	/* End of short zgethdr() */

/**********************************************************
 *	short zrbhdr(struct Vars *v)
 *
 * Receive a binary style header (type and position)
 **********************************************************/
int zrbhdr(struct Vars *v)
{
   UBYTE *hdr = v->Rxhdr;
   int c, n;
   USHORT crc;

   if ((c = zdlread(v)) & ~0xFF)
      return c;
   v->Rxtype = c;
   crc = updcrc(c, 0);

   for (n = 4; --n >= 0; )
   {
      if ((c = zdlread(v)) & ~0xFF)
	 return c;
      crc = updcrc(c, crc);
      *hdr++ = c;
      }
   if ((c = zdlread(v)) & ~0xFF)
      return c;
   crc = updcrc(c, crc);
   if ((c = zdlread(v)) & ~0xFF)
      return c;
   crc = updcrc(c, crc);
   if (crc & 0xFFFF)
   {
      strcpy(v->Msgbuf, "CRC-Fehler in Header ");
      return ERROR;
      }
   return v->Rxtype;
   }	/* End of short zrbhdr() */

/**********************************************************
 *	short zrbhdr32(struct Vars *v)
 *
 * Receive a binary style header (type and position) with
 * 32 bit FCS
 **********************************************************/
int zrbhdr32(struct Vars *v)
{
   UBYTE *hdr = v->Rxhdr;
   int c, n;
   ULONG crc32;

   if ((c = zdlread(v)) & ~0xFF)
      return c;
   v->Rxtype = c;
   crc32 = 0xFFFFFFFFL;
   crc32 = UPDC32(c, crc32);
#ifdef DEBUGLOG
   mysprintf(v->Msgbuf, "zrbhdr32: c=%x  crc32=%1x\n", c, crc32);
   dlog(v, v->Msgbuf);
#endif

   for (n = 4; --n >= 0; )
   {
      if ((c = zdlread(v)) & ~0xFF)
	 return c;
      crc32 = UPDC32(c, crc32);
      *hdr++ = c;
#ifdef DEBUGLOG
      mysprintf(v->Msgbuf, "zrbhdr32: c=%x  crc32=%1x\n", c, crc32);
      dlog(v, v->Msgbuf);
#endif
      }
   for (n = 4; --n >= 0; )
   {
      if ((c = zdlread(v)) & ~0xFF)
	 return c;
      crc32 = UPDC32(c, crc32);
#ifdef DEBUGLOG
      mysprintf(v->Msgbuf, "zrbhdr32: c=%x  crc32=%1x\n", c, crc32);
      dlog(v, v->Msgbuf);
#endif
      }
   if (crc32 != 0xDEBB20E3)
   {
      strcpy(v->Msgbuf, "CRC-Fehler in Header ");
      return ERROR;
      }
   return v->Rxtype;
   }	/* End of short zrbhdr32() */

/**********************************************************
 *	short zrhhdr(struct Vars *v)
 *
 * Receive a hex style header (type and position)
 **********************************************************/
int zrhhdr(struct Vars *v)
{
   UBYTE *hdr = v->Rxhdr;
   int c, n;
   USHORT crc;

   if ((c = zgethex(v)) < 0)
      return c;
   v->Rxtype = c;
   crc = updcrc(c, 0);

   for (n = 4; --n >= 0; )
   {
      if ((c = zgethex(v)) < 0)
	 return c;
      crc = updcrc(c, crc);
      *hdr++ = c;
      }
   if ((c = zgethex(v)) < 0)
      return c;
   crc = updcrc(c, crc);
   if ((c = zgethex(v)) < 0)
      return c;
   crc = updcrc(c, crc);
   if (crc & 0xFFFF)
   {
      strcpy(v->Msgbuf, "CRC-Fehler im Header ");
      return ERROR;
      }
   if (readock(v, 1) == '\r')
      readock(v, 1);		/* Throw away possible cr/lf */
   return v->Rxtype;
   }	/* End of short zrhhdr() */

/**********************************************************
 *	void zputhex(struct Vars *v, UBYTE c)
 *
 * Send a byte as two hex digits
 **********************************************************/
void zputhex(struct Vars *v, UBYTE c)
{
   static char digits[] = "0123456789abcdef";

   sendline(v, digits[(c >> 4) & 0x0F]);
   sendline(v, digits[c & 0x0F]);
   }	/* End of void zputhex() */

/**********************************************************
 *	void zsendline(struct Vars *v, UBYTE c)
 *
 * Send character c with ZMODEM escape sequence encoding.
 * Escape ZDLE, real DLE, XON, XOFF, and CR following @ (Telenet net escape)
 **********************************************************/
void zsendline(struct Vars *v, UBYTE c)
{
   switch (DLE_actions[c])
   {
   case 2:
      if (v->Lastzsent != '@')
         goto sendit;
      /* Fallthrough */
   case 1:
      xsendline(v, ZDLE);
      c ^= 0x40;
sendit:
   case 0:
      xsendline(v, v->Lastzsent = c);
      }
   }	/* End of void zsendline() */

/**********************************************************
 *	short zgethex(struct Vars *v)
 *
 * Decode two lower case hex digits into an 8 bit byte value
 **********************************************************/
int zgethex(struct Vars *v)
{
   int c, n;

   if ((n = noxrd7(v)) < 0)
      return n;
   n -= '0';
   if (n > 9)
      n -= ('a' - ':');
   if (n & ~0xF)
      return ERROR;

   if ((c = noxrd7(v)) < 0)
      return c;
   c -= '0';
   if (c > 9)
      c -= ('a' - ':');
   if (c & ~0xF)
      return ERROR;

   return (n << 4 | c);
   }	/* End of short zgethex() */

/**********************************************************
 *	short zdlread(struct Vars *v)
 *
 * Read a byte, checking for ZMODEM escape encoding
 * including CAN*5 which represents a quick abort.
 **********************************************************/
int zdlread(struct Vars *v)
{
   int c;

   if ((c = readock(v, v->Rxtimeout)) != ZDLE)
      return c;
   if ((c = readock(v, v->Rxtimeout)) < 0)
      return c;
   if (c == CAN && (c = readock(v, v->Rxtimeout)) < 0)
      return c;
   if (c == CAN && (c = readock(v, v->Rxtimeout)) < 0)
      return c;
   if (c == CAN && (c = readock(v, v->Rxtimeout)) < 0)
      return c;
   switch (c)
   {
   case CAN:
      return GOTCAN;
   case ZCRCE:
   case ZCRCG:
   case ZCRCQ:
   case ZCRCW:
      return (c | GOTOR);
   case ZRUB0:
      return 0x7F;
   case ZRUB1:
      return 0xFF;
   default:
      if ((c & 0x60) ==  0x40)
	 return (c ^ 0x40);
      break;
      }
   strcpy(v->Msgbuf, "Falsche ZMODEM-ESC-Sequenz ");
   return ERROR;
   }	/* End of short zdlread() */

/**********************************************************
 *	short noxrd7(struct Vars *v)
 *
 * Read a character from the modem line with timeout.
 * Eat parity, XON and XOFF characters.
 **********************************************************/
int noxrd7(struct Vars *v)
{
   int c;

   for (;;)
   {
      if ((c = readock(v, v->Rxtimeout)) < 0)
	 return c;
      switch (c &= 0x7F)
      {
      case XON:
      case XOFF:
         continue;
      default:
         return c;
         }
      }
   }	/* short noxrd7() */

/**********************************************************
 *	void stohdr(struct Vars *v, long pos)
 *
 * Store long integer pos in Txhdr
 **********************************************************/
void stohdr(struct Vars *v, long pos)
{
   v->Txhdr[ZP0] = pos;
   pos >>= 8;
   v->Txhdr[ZP1] = pos;
   pos >>= 8;
   v->Txhdr[ZP2] = pos;
   pos >>= 8;
   v->Txhdr[ZP3] = pos;
   }	/* End of void stohdr() */

/**********************************************************
 *	long rclhdr(struct Vars *v)
 *
 * Recover a long integer from a header
 **********************************************************/
long rclhdr(struct Vars *v)
{
   long l;

   l = v->Rxhdr[ZP3];
   l = (l << 8) | v->Rxhdr[ZP2];
   l = (l << 8) | v->Rxhdr[ZP1];
   l = (l << 8) | v->Rxhdr[ZP0];
   return l;
   }	/* End of long rclhdr() */
/* End of Zm.c source */
