/*************************************************************************
**   File:  xmodem.c
**      contains xmodem send and receive functions
**
**   Functions:
**	XMODEM_Read_File() - xmodem protocol receive file from remote
**	XMODEM_Send_File() - xmodem protocol send file to remote
**	clrcrc() - initialize CRC and Checksum values
**	updcrc() - calculate CRC or Checksum
**
*************************************************************************/
#include "term.h"

SHORT crc = 1;		/* default to CRC mode */

USHORT	crcaccum,
	checksum,
	rec,
	abort = 0;	/* oper abort request */
extern struct MenuItem X_Sub_Item[];
extern int	bufr_ptr,
		timeout,
		fd;
extern char	*bufr;
extern char *getmem();

/**************************************/
/* xmodem send and recieve functions */
/************************************/

XMODEM_Read_File(file)
char *file;
{
	char line[80];
	unsigned char ch;	/* scratch handshake var  */
	unsigned char response;	/* ACK/NAK/CRC handshake  */
	unsigned char rcksum;	/* rec'd Checksum  */
	unsigned char r1;	/* current record number  */
	unsigned char r2;	/* 1's complement "       */
	int  errors,	/* error count */
		hdr_time,	/* header time out value */
		chr_time;	/* character time out value */
	USHORT i, j;
	register char *buffer;	/* start of 128 byte sector */
	
	if (X_Sub_Item[0].Flags & CHECKED)
		crc = 1;
	else
		crc = 0;

	if (X_Sub_Item[2].Flags & CHECKED) {
		hdr_time = 10;
		chr_time = 10;
	}
	else {
		hdr_time = 20;		/*  note: LONG time out values */
		chr_time = 20;
	}

	if (bufr == NULL) {
		if ((bufr = getmem(BufSize)) == NULL) {
			emits("Unable to Allocate Buffer\n");
			return FALSE;
		}
	}

	if ((fd = creat(file, 0)) < 0)
	{
		emits("Cannot Open File\n");
		return FALSE;
	}
	else
	    sprintf(line,"\nReceiving File %s\n",file);
	    emits(line);

	rec = 1;
	abort = 0;
	buffer = bufr;	/* start of big buffer */
	if (crc)
		response = CRC;	/* CRC of CheckSum */
	else
		response = NAK;

	if (!open_time()) {	/* open timer device */
		close(fd);
		return FALSE;
	}

	while (TRUE) {
		timeout = errors = 0;
		ch = '\0';
		for (i = 1;i<200;i++) {
			timeout = 0;
			if (abort)
				response = CAN;
			if (ch == '\0')
				sendchar(response);	/* send handshake */
			if (response == CAN) {
				for (j=0;j<5;j++)
					sendchar(CAN);
				for (j=0;j<6;j++)
					sendchar('\b');
				close(fd);
				close_time();
				emits("\nTransfer Cancelled\n");
				return(FALSE);
			}
			start_timer(hdr_time);
			if ((ch = readchar()) == SOH) {
				kill_timer();
				sprintf(line,"\rReceiving # %d  ",rec);
				emits(line);
				break;		/* SOH indicates rec */
			}
			if (timeout) {
				ch = '\0';
				if (response == CRC) {
					if (++errors >= ERRORMAX/2) {
						emits("\nCRC handshake failed - switching to CheckSum\n");
						response = NAK;
						crc = 0;
						i = 1;		/* begin loop over */
						continue;
					}
					continue;
				}  /* endif response == CRC */
				else {		/* response not == CRC */
					if (++errors >= ERRORMAX) {
						emits("\nCan't sync to sender\n");
						close(fd);
						close_time();
						return(FALSE);
					}
				}
			}
			kill_timer();
			if (ch == EOT) {	/* EOT indicates done */ 
				if (buffer > bufr) {
					if (write(fd,bufr,buffer - bufr) == -1) 
						emits("Error writing last buffer\n");
				}
				sendchar(ACK);
				close(fd);
				close_time();
				sprintf(line,"\nFile %s received\n",file);
				emits(line);
				return(TRUE);
			}
			if (ch == CAN) {
				if (buffer > bufr) {
					if (write(fd,bufr,buffer - bufr) == -1) 
						emits("\nError writing last buffer\n");
				}
				close(fd);
				close_time();
				emits("Sender cancelled - aborted\n");
				return(FALSE);
			}
			if (i==199) {	/* handshake timeout */
				emits("\nCan't sync to sender\n");
				close(fd);
				close_time();
				return(FALSE);
			}
		}
		start_timer(chr_time);	/* set timeout for block */
		r1 = readchar();		/* record number */
		r2 = readchar();		/* 1's comp rec # */
		clrcrc();
		for (j = 0;j < SECSIZ;j++) {
			buffer[j] = readchar();
			if (timeout) j = SECSIZ;
			else updcrc(buffer[j]);
		}
		if (!timeout) {
			rcksum = readchar();
			if (crc) {
				updcrc(rcksum);
				if (!timeout)
					updcrc(readchar());
			}
		}
		response = NAK;			/* init response */
		if (timeout) {
			emits(" *** Timeout ***\n");
			if (++errors > ERRORMAX)
				abort = TRUE;
			continue;
		}
		kill_timer();
		if ((~r1 & 0xff) != (r2 & 0xff)) {
			emits(" *** record numbers don't match\n");
			if (++errors > ERRORMAX)
				abort = TRUE;
			continue;
		}
		if (crc) {		/* CRC test */
			if (crcaccum != 0) {
				emits(" CRC error\n");
				if (++errors > ERRORMAX)
					abort = TRUE;
				continue;
			}
		}
		else if (rcksum != (checksum & 0xff)) {
			sprintf(line," CheckSum error - recvd %02x, calcd %02x\n",rcksum,checksum&0xff);
			emits(line);
			if (++errors > ERRORMAX)
				abort = TRUE;
			continue;
		}
		if (r1 == (rec - 1) & 0xff) { 	/* duplicate ? */
			emits(" received duplicate record\n");
			response = ACK;		/* duplicate is ok */
			continue;
		}
		if (r1 != (rec & 0xff)) {   /* fatal sequence error */
			emits(" Sequence error\n");
			if (++errors > ERRORMAX)
				abort = TRUE;
			continue;
		}
		rec ++;
		if ((buffer += SECSIZ) >= (bufr + BufSize)) {
			if (write(fd,bufr,BufSize) == -1) {
				emits("\nWrite error - xfer cancelled\n");
				abort = 1;
				continue;
			}
			buffer = bufr;		/* reset buffer */
		}
		response = ACK;		/*  normal loop end */
	}
}


XMODEM_Send_File(file)
char *file;
{
	char line[80];
	char c;		/* scratch handshake var  */
	unsigned int bytes_to_send,
			size;
	int  attempts;	/* error count */
	USHORT j;
	register char *buffer;	/* start of 128 byte sector */

	if (bufr == NULL) {
		if ((bufr = getmem(BufSize)) == NULL) {
			emits("Unable to Allocate Buffer\n");
			return FALSE;
		}
	}

	timeout=FALSE;
	if ((fd = open(file, 1)) < 0)
	{
		emits("Cannot Open Send File\n");
		return FALSE;
	}
	else
	    sprintf(line,"\nSending File %s\n",file);
	    emits(line);
	abort = attempts = 0;
	rec = 1;
	if (!open_time()) {
		close(fd);
		return FALSE;
	}
	start_timer(10);

	/* wait for sync char */
	j=1;
	while (((c = readchar()) != NAK) && (c != CRC) && (j++ < ERRORMAX)) {
		if (abort) {
			kill_timer();
			close(fd);
			close_time();
			return FALSE;
		}
		if (timeout)
			start_timer(10);
	}
	kill_timer();
	if (j >= (ERRORMAX))
	{
		emits("\nReceiver did not handshake\n");
		close(fd);
		close_time();
		return FALSE;
	};

	if (c == CRC) {
		emits("\nCRC requested\n");
		crc = 1;
	}
	else {
		emits("\nCheckSum requested\n");
		crc = 0;
	}
	while ((bytes_to_send = read(fd, bufr, BufSize)) && attempts != RETRYMAX)
	{
		if (bytes_to_send == EOF)
		{
			emits("\nError Reading File\n");
			for (j=0;j<5;j++)
				sendchar(CAN);
			for (j=0;j<6;j++)
				sendchar('\b');
			close(fd);
			close_time();
			return FALSE;
		};

		buffer = bufr;
		while (bytes_to_send > 0 && attempts != RETRYMAX)
		{
			attempts = 0;
			do
			    {
				clrcrc();
				sprintf(line,"\rSending record %d",rec);
				emits(line);
				sendchar(SOH);
				sendchar(rec&0xff);
				sendchar(~(rec&0xff));
				size = SECSIZ <= bytes_to_send ? SECSIZ : bytes_to_send;
				bytes_to_send -= size;
				for (j = 0; j < SECSIZ; j++)
					if (j < size)
					{
						sendchar(buffer[j]);
						updcrc(buffer[j]);
					}
					else {
						sendchar(0);
						updcrc(0);
					}
				if (crc) {
					updcrc(0);
					updcrc(0);
					sendchar((crcaccum >> 8) & 0xff);
					sendchar(crcaccum & 0xff);
				}
				else
					sendchar(checksum & 0xff);
				attempts++;
				start_timer(10);
				c = readchar();
				if (!timeout) kill_timer();
			}
			while ((c != ACK)
				&& (c != CAN)
				&& (attempts != RETRYMAX)
				&& (!abort));
			if (attempts == RETRYMAX) {
				emits("\nRetry Limit Reached - send cancelled\n");
				for (j=0;j<5;j++)
					sendchar(CAN);
				for (j=0;j<6;j++)
					sendchar('\b');
				close(fd);
				close_time();
				return(FALSE);
			}
			if (abort) {
				emits("\nSend cancelled per operator request\n");
				for (j=0;j<5;j++)
					sendchar(CAN);
				for (j=0;j<6;j++)
					sendchar('\b');
				close(fd);
				close_time();
				return(FALSE);
			}
			if (c == CAN) {
				emits("\nReceiver cancelled transfer\n");
				close(fd);
				close_time();
				return(FALSE);
			}
			buffer += size;
			rec++;
		}
	}
	close(fd);
	attempts = 0;
	do
	    {
	    	if (attempts && !timeout) kill_timer();
		sendchar(EOT);
		attempts++;
		start_timer(10);
	}
		while ((readchar() != ACK) && (attempts != RETRYMAX) && (timeout == FALSE));
	if (attempts == RETRYMAX)
		emits("\nNo Acknowledgment Of End Of File\n");
	if (!timeout) kill_timer();	
	close_time();
	return TRUE;
}


clrcrc()
{
	crcaccum = checksum = 0;
}

updcrc(c)
	unsigned char c;
{
	unsigned shifter, flag;

	if (crc) {
		for (shifter = 0x80;shifter;shifter >>= 1) {
			flag = (crcaccum & 0x8000);
			crcaccum <<= 1;
			crcaccum |= ((shifter & c) ? 1 : 0);
			if (flag)
				crcaccum ^= 0x1021;
		}
	}
	else {
		checksum = ((checksum + c) & 0xff);
	}
}


