/***********************************************
 **************    serial.c   ******************
 ***********************************************/

#define INTUI_V36_NAMES_ONLY

#include <exec/exec.h>
#include <intuition/intuition.h>
#include <devices/serial.h>

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

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "windows.h"

#include "consts.h"
#include "structs.h"
#include "protos.h"

extern prj_p prj;

typedef enum {
	RDSTATE_NORMAL,
	RDSTATE_ESCCODE,
	RDSTATE_ESC1,
	RDSTATE_ESC2
}rdstate_e;

/*
Function : BOOL serial_data_put(WORD *data,BOOL literal)
Purpose : Sends data to the Z88, taking handshaking into account. 'data'
	is an array of WORDs, each WORD representing 1 character to be
	sent. The array is terminated by a -1. If 'literal' is TRUE, 0x1B
	is sent as an ESC B sequence. Returns TRUE on success,
	FALSE othewise.
*/

BOOL serial_data_put(WORD *data,BOOL literal)
{
	struct IntuiMessage *msg,curr_msg;

	ULONG signal_main,signal_serial,signals_returned;

	WORD data_put[5];
	WORD data_num,data_put_num;

	UBYTE data_byte,temp_byte;

	BOOL done;

	/* Build signals */

	signal_main = 1L << mainwin->UserPort->mp_SigBit;
	signal_serial = 1L << prj->serial_msg_port->mp_SigBit;

	for (data_num = 0; data[data_num] != -1; data_num++) {
		/* Get the data byte */

		data_byte = (UBYTE)data[data_num];

		/* Set up bytes to send */

		if ((data_byte >= 0x20) && (data_byte <= 0x7F)) {
			data_put[0] = (WORD)data_byte;
			data_put[1] = -1;
		}
		else if ((data_byte == 0x09) || (data_byte == 0x0A) ||
(data_byte == 0x0D)) {
			data_put[0] = (WORD)data_byte;
			data_put[1] = -1;
		}
		else if ((data_byte == 0x1B) && (!literal)) {
			data_put[0] = (WORD)data_byte;
			data_put[1] = -1;
		}
		else if ((data_byte == 0x1B) && (literal)) {
			data_put[0] = (WORD)0x1B;		/* ESC */
			data_put[1] = (WORD)0x42;		/* 'B' */
			data_put[2] = (WORD)0x31;		/* '1' */
			data_put[3] = (WORD)0x42;		/* 'B' */
			data_put[4] = -1;
		}
		else {
			data_put[0] = 0x1B;
			data_put[1] = (WORD)'B';

			temp_byte =  data_byte >> 4;
			if (temp_byte <= 9)
				data_put[2] = (WORD)(temp_byte + '0');
			else
				data_put[2] = (WORD)(temp_byte - 10 + 'A');

			data_byte &= 0x0F;
			if (data_byte <= 9)
				data_put[3] = (WORD)(data_byte + '0');
			else
				data_put[3] = (WORD)(data_byte - 10 + 'A');

			data_put[4] = -1;
		}

		for (data_put_num = 0; data_put[data_put_num] != -1;
data_put_num++) {
			/* Get the data byte */

			data_byte = (UBYTE)data_put[data_put_num];

			/* Set up the send request */

			prj->serialio->IOSer.io_Length = 1;
			prj->serialio->IOSer.io_Data = &data_byte;
			prj->serialio->IOSer.io_Command = CMD_WRITE;

			DoIO((struct IORequest *)prj->serialio);

			/* Set up the request for the Z88 0 handshake byte */

			prj->serialio->IOSer.io_Length = 1;
			prj->serialio->IOSer.io_Data = &data_byte;
			prj->serialio->IOSer.io_Command = CMD_READ;

			SendIO((struct IORequest *)prj->serialio);

			/* Wait for reply */

			done = FALSE;

			while (!done) {
				signals_returned = Wait(signal_main |
signal_serial);

				/* Window message */

				if (signals_returned & signal_main) {
					while (msg =
(struct IntuiMessage *)GT_GetIMsg(mainwin->UserPort)) {
						CopyMem(msg,&curr_msg,
sizeof(struct IntuiMessage));
						GT_ReplyIMsg(
(struct IntuiMessage *)msg);
						if (curr_msg.Class ==
IDCMP_CLOSEWINDOW)
							cleanup();
					}
				}

				/* Serial message */

				if (signals_returned & signal_serial) {
					/* Data should be a zero */

					if (CheckIO((struct IORequest *)prj->serialio)) {
						if (data_byte != 0) {
							error(
"Corrupt handshaking, quitting.|"
"Didn't receive correct 0 handshaking\n"
"byte from Z88. Confused.",ERROR_WARNING,__LINE__,__FILE__);
							return(TRUE);
						}

						WaitIO((struct IORequest *)prj->serialio);

						done = TRUE;
					}
				}
			}
		}
	}

	/* If we get here, we transferred the data OK and the Z88 received */
	/* it OK. */

	return (TRUE);
}

/*
Function : BOOL serial_data_get(UBYTE *data,LONG size)
Purpose : Gets data from Z88 and places into 'data'. Receives 'size' bytes
	and de-mangles encoding of bytes outside 0x20 and 0x7F. Returns
	TRUE on success, FALSE otherwise.
*/

BOOL serial_data_get(UBYTE *data,LONG size)
{
	struct IntuiMessage *msg,curr_msg;

	ULONG signal_main,signal_serial,signals_returned;

	LONG data_num = 0;

	UBYTE data_byte;

	BOOL done;

	rdstate_e state = RDSTATE_NORMAL;

	/* Build signals */

	signal_main = 1L << mainwin->UserPort->mp_SigBit;
	signal_serial = 1L << prj->serial_msg_port->mp_SigBit;

	while (data_num < size) {
		/* Set up the request for the Z88 data */

		prj->serialio->IOSer.io_Length = 1;
		prj->serialio->IOSer.io_Data = &data_byte;
		prj->serialio->IOSer.io_Command = CMD_READ;

		SendIO((struct IORequest *)prj->serialio);

		/* Wait for reply */

		done = FALSE;

		while (!done) {
			signals_returned = Wait(signal_main | signal_serial);

			/* Window message */

			if (signals_returned & signal_main) {
				while (msg = (struct IntuiMessage *)GT_GetIMsg(
mainwin->UserPort)) {
					CopyMem(msg,&curr_msg,
sizeof(struct IntuiMessage));
					GT_ReplyIMsg((struct IntuiMessage *)msg);

					if (curr_msg.Class ==
IDCMP_CLOSEWINDOW)
						cleanup();
				}
			}

			/* Serial message */

			if (signals_returned & signal_serial) {
				if (CheckIO((struct IORequest *)prj->serialio)) {

					done = TRUE;

					WaitIO((struct IORequest *)prj->serialio);
				}
			}
		}

		/* Perform actions depending on state */

		switch (state) {
			case RDSTATE_NORMAL:
				if (data_byte >= 0x20) {
					/* Write out data byte direct */

					data[data_num++] = data_byte;
				}
				else if ((data_byte == 0x09) ||
(data_byte == 0x0A) || (data_byte == 0x0D)) {
					/* Write out data byte direct */

					data[data_num++] = data_byte;
				}
				else if (data_byte == 0x1B) {
					if (size == 1)
						data[data_num++] = 0x1B;
					else
						state = RDSTATE_ESCCODE;
				}
				else {
					error("Corrupt data.|"
"A byte outside the allowed\n"
"range was found in the data\n"
"stream.",ERROR_WARNING,__LINE__,__FILE__);
					return(FALSE);
				}
				break;
			case RDSTATE_ESCCODE:
				if (data_byte != 'B') {
					data[data_num++] = 0x1B;
					data[data_num++] = data_byte;

					state = RDSTATE_NORMAL;
				}
				else
					state = RDSTATE_ESC1;

				break;
			case RDSTATE_ESC1:
				if ((data_byte >= '0') && (data_byte <= '9'))
					data[data_num] = data_byte - '0';
				else if ((data_byte >= 'A') &&
(data_byte <= 'F'))
					data[data_num] = data_byte - 'A' + 10;
				else {
					error("Corrupt data.|"
"An ESC B sequence data was outside\n"
"the range 0-9,A-F.\n",ERROR_WARNING,__LINE__,__FILE__);
					return(FALSE);
				}

				/* Shift data */

				data[data_num] <<= 4;

				state = RDSTATE_ESC2;

				break;
			case RDSTATE_ESC2:
				if ((data_byte >= '0') && (data_byte <= '9'))
					data_byte -= '0';
				else if ((data_byte >= 'A') &&
(data_byte <= 'F'))
					data_byte = data_byte - 'A' + 10;
				else {
					error("Corrupt data.|"
"An ESC B sequence data was outside\n"
"the range 0-9,A-F.\n",ERROR_WARNING,__LINE__,__FILE__);
					return(FALSE);
				}

				data[data_num++] |= data_byte;

				state = RDSTATE_NORMAL;

				break;
			default:
				error("Unknown RDSTATE.",ERROR_INTERNAL,
__LINE__,__FILE__);
				break;
		}

		/* Send the handshaking byte back */

		data_byte = 0;

		prj->serialio->IOSer.io_Length = 1;
		prj->serialio->IOSer.io_Data = &data_byte;
		prj->serialio->IOSer.io_Command = CMD_WRITE;

		DoIO((struct IORequest *)prj->serialio);
	}

	/* If we get here, all's OK */

	return (TRUE);
}

/*
Function : BOOL serial_data_get_single(WORD *data)
Purpose : Gets a single byte of (decoded) data. Sets 'data' to -1 if
	ESC Z is found in stream. Returns TRUE on success, FALSE otherwise.
*/

BOOL serial_data_get_single(WORD *data)
{
	UBYTE data_bytes[2];
	UBYTE data_byte;

	/* Get a byte of data */

	if (!serial_data_get(&data_byte,1))
		return (FALSE);

	/* If normal value */

	if (data_byte >= 0x20) {
		*data = (WORD)data_byte;

		return (TRUE);
	}

	/* If special value */

	if ((data_byte == 0x09) || (data_byte == 0x0A) || (data_byte == 0x0D)) {
		*data =(WORD)data_byte;

		return (TRUE);
	}

	/* If not ESC, error */

	if (data_byte != 0x1B)
		return (FALSE);

	/* Get the next byte to see which ESC code */

	if (!serial_data_get(&data_byte,1))
		return (FALSE);

	/* If ESC Z */

	if (data_byte == 'Z') {
		*data = -1;

		return (TRUE);
	}

	/* If not ESC B, error */

	if (data_byte != 'B')
		return (FALSE);

	/* Get next 2 bytes to work out real value */

	if (!serial_data_get(data_bytes,2))
		return (FALSE);

	data_byte = data_bytes[0];

	if ((data_byte >= '0') && (data_byte <= '9'))
		*data = (WORD)(data_byte - '0');
	else if ((data_byte >= 'A') && (data_byte <= 'F'))
		*data = (WORD)(data_byte - 'A' + 10);
	else
		return(FALSE);

	/* Shift data */

	*data <<= 4;

	data_byte = data_bytes[1];

	if ((data_byte >= '0') && (data_byte <= '9'))
		data_byte -= '0';
	else if ((data_byte >= 'A') && (data_byte <= 'F'))
		data_byte = data_byte - 'A' + 10;
	else 
		return(FALSE);

	*data |= (WORD)data_byte;

	/* Return OK */

	return (TRUE);
}



/*
Function : BOOL serial_sync()
Purpose : Sends the synchronising message. This is slightly
	different that all the other transfer in that return 0 bytes
	aren't returned. Returns TRUE on success, FALSE otherwise.
*/

BOOL serial_sync()
{
	struct IntuiMessage *msg,curr_msg;

	ULONG signal_main,signal_serial,signals_returned;

	UBYTE amisyncdata[] = {5,5,5,5,5,6};
	UBYTE z88syncdata;

	BOOL correctdata;
	BOOL done = FALSE;

	/* Set up the Amiga sync request */

	prj->serialio->IOSer.io_Length = 6;
	prj->serialio->IOSer.io_Data = amisyncdata;
	prj->serialio->IOSer.io_Command = CMD_WRITE;

	DoIO((struct IORequest *)prj->serialio);

	/* Set up the request for the Z88 sync data */

	prj->serialio->IOSer.io_Length = 1;
	prj->serialio->IOSer.io_Data = &z88syncdata;
	prj->serialio->IOSer.io_Command = CMD_READ;

	SendIO((struct IORequest *)prj->serialio);

	/* Build signals */

	signal_main = 1L << mainwin->UserPort->mp_SigBit;
	signal_serial = 1L << prj->serial_msg_port->mp_SigBit;

	/* Wait for request or closewindow */

	while (!done) {
		signals_returned = Wait(signal_main | signal_serial);

		/* Window message */

		if (signals_returned & signal_main) {
			while (msg = (struct IntuiMessage *)GT_GetIMsg(
mainwin->UserPort)) {
				CopyMem(msg,&curr_msg,
sizeof(struct IntuiMessage));
				GT_ReplyIMsg((struct IntuiMessage *)msg);

				if (curr_msg.Class ==
IDCMP_CLOSEWINDOW)
					cleanup();
			}
		}

		/* Serial message */

		if (signals_returned & signal_serial) {
			/* Even if it's returned, might not be the correct */
			/* data. Just wait for the 6 */

			correctdata = FALSE;

			if (CheckIO((struct IORequest *)prj->serialio)) {
				if (z88syncdata == 6)
					correctdata = TRUE;

				WaitIO((struct IORequest *)prj->serialio);
			}

			if (correctdata)
				done = TRUE;
			else {
				/* Build another request to listen for the */
				/* Z88 sync request. */

				prj->serialio->IOSer.io_Length = 1;
				prj->serialio->IOSer.io_Data = &z88syncdata;
				prj->serialio->IOSer.io_Command = CMD_READ;

				SendIO((struct IORequest *)prj->serialio);
			}
		}
	}

	/* If we get here, return OK */

	return (TRUE);
}

/*
Function : BOOL serial_hello()
Purpose : Sends the "Hello" transfer. Returns TRUE on success, FALSE
	otherwise.
*/

BOOL serial_hello()
{
	WORD tags_hello[] = {
		0x001B,		/* ESC */
		0x0041,		/* 'A' */
		-1};

	UBYTE tags_get[2];

	/* Synchronise the serial */

	win_status_set("Synchronising...");

	if (!serial_sync()) {
		win_status_clear();

		return(FALSE);
	}

	/* Send the message */

	win_status_set("Sending data...");

	if (!serial_data_put(tags_hello,FALSE)) {
		win_status_clear();

		return (FALSE);
	}

	/* Get reply */

	win_status_set("Receiving data...");

	if (!serial_data_get(tags_get,2)) {
		win_status_clear();

		return (FALSE);
	}

	/* Test return values */

	if ((tags_get[0] != 0x1B) || (tags_get[1] != 'Y')) {
		error("Unknown Z88 reply.|"
"The Z88 has not replied as expected\n"
"from the \"Hello\" greeting.",ERROR_WARNING,__LINE__,__FILE__);

		win_status_clear();

		return (FALSE);
	}

	/* Clear status window */

	win_status_clear();

	/* If we get here, all's OK */

	return (TRUE);
}

/*
Function : BOOL serial_devices()
Purpose : Returns devices available on the Z88. Returns TRUE on success,
	FALSE otherwise.
*/

BOOL serial_devices()
{
	WORD tags_devices[] = {
		0x001B,		/* ESC */
		0x0048,		/* 'H' */
		-1};

	WORD count;

	UBYTE tags_get[2];
	UBYTE data_byte;

	BOOL done = FALSE;

	struct listviewdata lvdata;

	rdstate_e state = RDSTATE_NORMAL;

	/* Synchronise the serial */

	win_status_set("Synchronising...");

	if (!serial_sync()) {
		win_status_clear();

		return(FALSE);
	}

	/* Send the message */

	win_status_set("Sending data...");

	if (!serial_data_put(tags_devices,FALSE)) {
		win_status_clear();

		return (FALSE);
	}

	/* Get reply */

	win_status_set("Receiving data...");

	/* Get tags, should be ESC N or ESC Z */

	if (!serial_data_get(tags_get,2)) {
		error("Unable to get reply from Z88.",ERROR_WARNING,
__LINE__,__FILE__);

		win_status_clear();

		return(FALSE);
	}

	if (tags_get[0] != 0x1B) {
		error("Unknown Z88 reply.|"
"Expecting ESC N or ESC Z in reply\n"
"to devices request.",ERROR_WARNING,__LINE__,__FILE__);

		win_status_clear();

		return(FALSE);
	}

	if ((tags_get[1] != 'N') && (tags_get[1] != 'Z')) {
		error("Unknown Z88 reply.|"
"Expecting ESC N or ESC Z in reply\n"
"to devices request.",ERROR_WARNING,__LINE__,__FILE__);

		win_status_clear();

		return(FALSE);
	}

	/* If it's the end, no devices were returned */

	if (tags_get[1] == 'Z') {
		win_status_clear();

		return (TRUE);
	}

	/* Keep on reading device names */

	count = 0;

	while (!done) {
		switch(state) {
			case RDSTATE_NORMAL:
				/* Get the next byte */

				if (!serial_data_get(&data_byte,1)) {
					error("Unable to read devices data.",
ERROR_WARNING,__LINE__,__FILE__);

					return (FALSE);
				}

				/* If it's a normal character */

				if (data_byte != 0x1B)
					lvdata.fname[count++] = data_byte;
				else
					state = RDSTATE_ESCCODE;

				break;
			case RDSTATE_ESCCODE:
				/* Get the ESC code */

				if (!serial_data_get(&data_byte,1)) {
					error("Unable to read devices data.",
ERROR_WARNING,__LINE__,__FILE__);

					return (FALSE);
				}

				/* If end of devices */

				if (data_byte == 'Z') {
					lvdata.fname[count] ='\0';

					win_listview_add(&lvdata,ZFILETYPE_DEV);

					done = TRUE;
				}
				else if (data_byte == 'N') {
					lvdata.fname[count] ='\0';

					win_listview_add(&lvdata,ZFILETYPE_DEV);

					count = 0;
				}
				else {
					error("Unknown devices data.|"
"ESC code received was not either\n"
"N or Z.",ERROR_WARNING,__LINE__,__FILE__);

					return (FALSE);
				}

				state = RDSTATE_NORMAL;

				break;
			default:
				error("Unknown RDSTATE.",ERROR_INTERNAL,
__LINE__,__FILE__);
				break;
		}
	}

	/* Clear the status */

	win_status_clear();

	/* Return OK */

	return (TRUE);
}

/*
Function : BOOL serial_directories()
Purpose : Returns directories available on prj->zpath device on the Z88.
	Returns TRUE on success, FALSE otherwise.
*/

BOOL serial_directories()
{
	WORD tags_directories[STRLEN_ZPATH];

	WORD tag_num = 0;
	WORD count;

	UBYTE tags_get[2];
	UBYTE data_byte;

	BOOL done = FALSE;

	struct listviewdata lvdata;

	rdstate_e state = RDSTATE_NORMAL;

	/* Synchronise the serial */

	win_status_set("Synchronising...");

	if (!serial_sync()) {
		win_status_clear();

		return(FALSE);
	}

	/* Set up request */

	tags_directories[tag_num++] = 0x001B;		/* ESC */
	tags_directories[tag_num++] = 0x0044;		/* 'D' */

	for (count = 0; prj->zpath[count] != '\0'; count++)
		tags_directories[tag_num++] = (WORD)prj->zpath[count];

	tags_directories[tag_num++] = 0x001B;		/* ESC */
	tags_directories[tag_num++] = 0x005A;		/* 'Z' */
	tags_directories[tag_num] = -1;

	/* Send the message */

	win_status_set("Sending data...");

	if (!serial_data_put(tags_directories,FALSE)) {
		win_status_clear();

		return (FALSE);
	}
	/* Get reply */

	win_status_set("Receiving data...");

	/* Get tags, should be ESC N or ESC Z */

	if (!serial_data_get(tags_get,2)) {
		error("Unable to get reply from Z88.",ERROR_WARNING,
__LINE__,__FILE__);

		win_status_clear();

		return(FALSE);
	}

	if (tags_get[0] != 0x1B) {
		error("Unknown Z88 reply.|"
"Expecting ESC N or ESC Z in reply\n"
"to devices request.",ERROR_WARNING,__LINE__,__FILE__);

		win_status_clear();

		return(FALSE);
	}

	if ((tags_get[1] != 'N') && (tags_get[1] != 'Z')) {
		error("Unknown Z88 reply.|"
"Expecting ESC N or ESC Z in reply\n"
"to devices request.",ERROR_WARNING,__LINE__,__FILE__);

		win_status_clear();

		return(FALSE);
	}

	/* If it's the end, no devices were returned */

	if (tags_get[1] == 'Z') {
		win_status_clear();

		return (TRUE);
	}

	/* Keep on reading device names */

	count = 0;

	while (!done) {
		switch(state) {
			case RDSTATE_NORMAL:
				/* Get the next byte */

				if (!serial_data_get(&data_byte,1)) {
					error("Unable to read directories data.",
ERROR_WARNING,__LINE__,__FILE__);

					return (FALSE);
				}

				/* If it's a normal character */

				if (data_byte != 0x1B)
					lvdata.fname[count++] = data_byte;
				else
					state = RDSTATE_ESCCODE;

				break;
			case RDSTATE_ESCCODE:
				/* Get the ESC code */

				if (!serial_data_get(&data_byte,1)) {
					error("Unable to read directories data.",
ERROR_WARNING,__LINE__,__FILE__);

					return (FALSE);
				}

				/* If end of devices */

				if (data_byte == 'Z') {
					lvdata.fname[count] ='\0';

					win_listview_add(&lvdata,ZFILETYPE_DIR);

					done = TRUE;
				}
				else if (data_byte == 'N') {
					lvdata.fname[count] ='\0';

					win_listview_add(&lvdata,ZFILETYPE_DIR);

					count = 0;
				}
				else {
					error("Unknown directories data.|"
"ESC code received was not either\n"
"N or Z.",ERROR_WARNING,__LINE__,__FILE__);

					return (FALSE);
				}

				state = RDSTATE_NORMAL;

				break;
			default:
				error("Unknown RDSTATE.",ERROR_INTERNAL,
__LINE__,__FILE__);
				break;
		}
	}

	/* Clear the status */

	win_status_clear();

	/* Return OK */

	return (TRUE);
}

/*
Function : BOOL serial_filenames()
Purpose : Returns filenames available on prj->zpath device on the Z88.
	Returns TRUE on success, FALSE otherwise.
*/

BOOL serial_filenames()
{
	WORD tags_filenames[STRLEN_ZPATH];

	WORD tag_num = 0;
	WORD count;

	UBYTE tags_get[2];
	UBYTE data_byte;

	BOOL done = FALSE;

	struct listviewdata lvdata;

	rdstate_e state = RDSTATE_NORMAL;

	/* Synchronise the serial */

	win_status_set("Synchronising...");

	if (!serial_sync()) {
		win_status_clear();

		return(FALSE);
	}

	/* Set up request */

	tags_filenames[tag_num++] = 0x001B;		/* ESC */
	tags_filenames[tag_num++] = 0x004E;		/* 'N' */

	for (count = 0; prj->zpath[count] != '\0'; count++)
		tags_filenames[tag_num++] = (WORD)prj->zpath[count];

	tags_filenames[tag_num++] = 0x001B;		/* ESC */
	tags_filenames[tag_num++] = 0x005A;		/* 'Z' */
	tags_filenames[tag_num] = -1;

	/* Send the message */

	win_status_set("Sending data...");

	if (!serial_data_put(tags_filenames,FALSE)) {
		win_status_clear();

		return (FALSE);
	}

	/* Get reply */

	win_status_set("Receiving data...");

	/* Get tags, should be ESC N or ESC Z */

	if (!serial_data_get(tags_get,2)) {
		error("Unable to get reply from Z88.",ERROR_WARNING,
__LINE__,__FILE__);

		win_status_clear();

		return(FALSE);
	}

	if (tags_get[0] != 0x1B) {
		error("Unknown Z88 reply.|"
"Expecting ESC N or ESC Z in reply\n"
"to devices request.",ERROR_WARNING,__LINE__,__FILE__);

		win_status_clear();

		return(FALSE);
	}

	if ((tags_get[1] != 'N') && (tags_get[1] != 'Z')) {
		error("Unknown Z88 reply.|"
"Expecting ESC N or ESC Z in reply\n"
"to devices request.",ERROR_WARNING,__LINE__,__FILE__);

		win_status_clear();

		return(FALSE);
	}

	/* If it's the end, no devices were returned */

	if (tags_get[1] == 'Z') {
		win_status_clear();

		return (TRUE);
	}

	/* Keep on reading device names */

	count = 0;

	while (!done) {
		switch(state) {
			case RDSTATE_NORMAL:
				/* Get the next byte */

				if (!serial_data_get(&data_byte,1)) {
					error("Unable to read filenames data.",
ERROR_WARNING,__LINE__,__FILE__);

					return (FALSE);
				}

				/* If it's a normal character */

				if (data_byte != 0x1B)
					lvdata.fname[count++] = data_byte;
				else
					state = RDSTATE_ESCCODE;

				break;
			case RDSTATE_ESCCODE:
				/* Get the ESC code */

				if (!serial_data_get(&data_byte,1)) {
					error("Unable to read filenames data.",
ERROR_WARNING,__LINE__,__FILE__);

					return (FALSE);
				}

				/* If end of devices */

				if (data_byte == 'Z') {
					lvdata.fname[count] ='\0';

					win_listview_add(&lvdata,ZFILETYPE_FILE);

					done = TRUE;
				}
				else if (data_byte == 'N') {
					lvdata.fname[count] ='\0';

					win_listview_add(&lvdata,ZFILETYPE_FILE);

					count = 0;
				}
				else {
					error("Unknown filenames data.|"
"ESC code received was not either\n"
"N or Z.",ERROR_WARNING,__LINE__,__FILE__);

					return (FALSE);
				}

				state = RDSTATE_NORMAL;

				break;
			default:
				error("Unknown RDSTATE.",ERROR_INTERNAL,
__LINE__,__FILE__);
				break;
		}
	}

	/* Clear the status */

	win_status_clear();

	/* Return OK */

	return (TRUE);
}

/*
Function : BOOL serial_binary_get(char *zname)
Purpose : Gets the selected file. The current Z88 path is taken from
	prj->zpath and the Amiga path from prj->path_binary_get.
	Returns TRUE on success, FALSE otherwise.
*/

BOOL serial_binary_get(char *zname)
{
	LONG count;

	WORD tags_get[STRLEN_ZPATH];

	WORD tag_num = 0;
	WORD data_word;

	BOOL done = FALSE;

	FILE *fh;

	char fname[STRLEN_FNAME];
	char zfname[STRLEN_ZPATH];
	char status_text[STRLEN_STATUS];

	/* Create the Amiga filename */

	strcpy(fname,prj->path_binary_get);
	AddPart(fname,zname,STRLEN_FNAME);

	/* Create the Z88 filename */

	strcpy(zfname,prj->zpath);
	strcat(zfname,zname);

	/* Synchronise the serial */

	win_status_set("Synchronising...");

	if (!serial_sync()) {
		win_status_clear();

		return(FALSE);
	}

	/* Set up request */

	tags_get[tag_num++] = 0x001B;		/* ESC */
	tags_get[tag_num++] = 0x0047;		/* 'G' */

	for (count = 0; zfname[count] != '\0'; count++)
		tags_get[tag_num++] = (WORD)zfname[count];

	tags_get[tag_num++] = 0x001B;		/* ESC */
	tags_get[tag_num++] = 0x005A;		/* 'Z' */
	tags_get[tag_num] = -1;

	/* Send the message */

	win_status_set("Sending data...");

	if (!serial_data_put(tags_get,FALSE)) {
		win_status_clear();

		return (FALSE);
	}

	/* Open the Amiga file */

	fh = fopen(fname,"wb");

	if (!fh) {
		error("Unable to open Amiga file.",ERROR_WARNING,__LINE__,
__FILE__);
		win_status_clear();

		return (FALSE);
	}

	/* Set up a small buffer on file */

	setvbuf(fh,NULL,_IOFBF,1024);

	/* Receive the file */

	count = 0;

	while (!done) {
		/* Update the status gadget */

		if (!(count++ & 0x03FF)) {
			sprintf(status_text,"Getting %s (%dK done)...",zname,
count / 1024);
			win_status_set(status_text);
		}

		/* Get the next byte */

		if (!serial_data_get_single(&data_word)) {
			error("Unable to get data.",ERROR_WARNING,
__LINE__,__FILE__);
			fclose(fh);

			win_status_clear();

			return (FALSE);
		}

		/* If we've finished */

		if (data_word == -1)
			done = TRUE;
		else {
			/* It's a normal character */

			if (fputc((int)data_word,fh) == EOF) {

				fclose(fh);

				win_status_clear();

				return (FALSE);
			}
		}
	}

	/* Clear the status */

	win_status_clear();

	/* Close Amiga file */

	fclose(fh);

	/* Return OK */

	return (TRUE);
}

/*
Function : BOOL serial_binary_put(char *file,char *path)
Purpose : Puts the selected file. The current Z88 path is taken from
	prj->zpath. Returns TRUE on success, FALSE otherwise.
*/

BOOL serial_binary_put(char *path,char *file)
{
	LONG count;

	WORD tags_put[STRLEN_ZPATH];
	WORD tag_num = 0;
	WORD l;

	BOOL done = FALSE;

	FILE *fh;

	char *chrptr;

	char fname[STRLEN_FNAME];
	char zfile[STRLEN_FNAME];
	char zfname[STRLEN_ZPATH];
	char status_text[STRLEN_STATUS];

	int num_periods = 0;
	int chr;

	/* Build full Amiga filename */

	strcpy(fname,path);
	AddPart(fname,file,STRLEN_FNAME);

	/* Build Z88 filename */

	strcpy(zfname,prj->zpath);

	/* Chop length to max 12 chars */

	strncpy(zfile,file,12);
	zfile[11] = '\0';

	/* Make sure there's at most 1 period */

	for (l = 0; zfile[l] != '\0'; l++) {
		if (zfile[l] == '.')
			num_periods++;
	}

	if (num_periods >= 2) {
		for (l = 0; l < (num_periods - 1); l++) {
			chrptr = strchr(zfile,'.');

			*chrptr = 'X';
		}
	}

	/* Remove any non-alphanumeric characters */

	for (l = 0; l < strlen(zfile); l++) {
		if ((!isalnum(zfile[l])) && (zfile[l] != '.'))
			zfile[l] = 'X';
	}

	/* Make sure the extension is 3 characters max */

	chrptr = strchr(zfile,'.');

	if (chrptr) {
		if (strlen(chrptr) > 4)
			chrptr[4] = '\0';
	}

	strcat(zfname,zfile);

	/* Synchronise the serial */

	win_status_set("Synchronising...");

	if (!serial_sync()) {
		win_status_clear();

		return(FALSE);
	}

	/* Set up request */

	tags_put[tag_num++] = 0x001B;		/* ESC */
	tags_put[tag_num++] = 0x0053;		/* 'S' */
	tags_put[tag_num++] = 0x001B;		/* ESC */
	tags_put[tag_num++] = 0x004E;		/* 'N' */

	for (count = 0; zfname[count] != '\0'; count++)
		tags_put[tag_num++] = (WORD)zfname[count];

	tags_put[tag_num++] = 0x001B;		/* ESC */
	tags_put[tag_num++] = 0x0046;		/* 'F' */
	tags_put[tag_num] = -1;

	/* Send the message */

	win_status_set("Sending data...");

	if (!serial_data_put(tags_put,FALSE)) {
		win_status_clear();

		return (FALSE);
	}

	/* Open the Amiga file */

	fh = fopen(fname,"rb");

	if (!fh) {
		error("Unable to open Amiga file.",ERROR_WARNING,__LINE__,
__FILE__);
		win_status_clear();

		return (FALSE);
	}

	/* Set up a small buffer on file */

	setvbuf(fh,NULL,_IOFBF,1024);

	/* Receive the file */

	count = 0;

	while (!done) {
		/* Update the status gadget */

		if (!(count++ & 0x03FF)) {
			sprintf(status_text,"Putting %s (%dK done)...",zfile,
count / 1024);
			win_status_set(status_text);
		}

		chr = fgetc(fh);

		if (chr == EOF)
			done = TRUE;
		else {
			tags_put[0] = (WORD)chr;
			tags_put[1] = -1;

			if (!serial_data_put(tags_put,TRUE)) {
				error("Unable to put data.",ERROR_WARNING,
__LINE__,__FILE__);

				done = TRUE;
			}
		}
	}

	tags_put[0] = 0x001B;	/* ESC */
	tags_put[1] = 0x005A;	/* 'Z' */
	tags_put[2] = -1;

	if (!serial_data_put(tags_put,FALSE))
		error("Unable to put data.",ERROR_WARNING,__LINE__,__FILE__);

	win_status_clear();

	/* Close file */

	fclose(fh);

	/* Return OK */

	return (TRUE);
}
