// sersetup.c
// Created by Id Software for SERSETUP.EXE, 1993, 1994.
// Hacked by Russell Gilbert and others SER4.EXE, SER5.EXE, and SER6.EXE 1994.
// Hacked by Paul T. Hermann for SER5.EXE, SER6.EXE 1994.
//
// Compiled under Borland C++ 3.1 using the Compact Memory module
// Using 80386 instruction set and 80387 emualation support
// Optimizations for speed enabled
//

#include "sersetup.h"
#include "DoomNet.h"
#include <time.h>
#include <dirent.h>
#include <io.h>

#define PROG_NAME "SER6"
#define PROG_EXE "SER6.EXE"
#define PROG_CONFIG "SER6.CFG"

extern que_t inque;
extern que_t outque;
extern int uart_type;

int rgstrncpy (char *, char *, int );
int getwadlevel ( char * );
void pickdirectory ( void );
void pickwads ( void );
int sort_function (const void *a, const void *b);
void parsewad ( char * , char * );
int checkwad ( int , char * );
void kill_nonwads ( void );
int SendFile ( void );
int ReceiveFile (char *);
void dir ( int );
void pause (void);
void DOOM (void);
void jump_start( void );
void GetUart (void);
void ModemCommand (char *str);
int ModemResponse (char *resp);
void configure (void);
void reset_counters (void);

int usemodem = FALSE;
time_t starttime = 0;
time_t endtime = 0;
time_t playtime = 0;
char init1 [80+1];
char init2 [80+1];
char altbaudinit [80+1];
int use_altbaudinit = FALSE;
char hangup [80+1];
char config [80+1];
char devparm [256+1];
char directory [39+1];
char wadname [21][12+1];
char add [50+1];
char doomexe[12+1];
char levels[32][5+1];
char dial [50+1];
unsigned long baud = 9600;
unsigned long altbaud = 9600;
int comport = 1;
int irq = -1;
int uart = -1;
int nodropdtr = 0;
int vector = -1;
int loadgame = -1;
int episode = -1;
int map = -1;
int skill = -1;
int deathmatch = 0;
int altdeath = 0;
int pulsedial = 0;
int player = 0;
int nomonsters = 0;
int respawn = 0;
int doomver1_2 = 0;
int doom2 = 0;

/* showReadStats() counters. */
unsigned long writeBufferOverruns       = 0;
unsigned long bytesRead                 = 0;
unsigned long packetsRead               = 0;
unsigned long largestReadPacket         = 0;
unsigned long smallestReadPacket        = 0xFFFFFFFFl;
unsigned long readBufferOverruns        = 0;
unsigned long totalReadPacketBytes      = 0;
unsigned long oversizeReadPackets       = 0;
unsigned long largestOversizeReadPacket = 0;
unsigned long overReadPacketLen         = 0;

/* showWriteStats() counters. */
unsigned long bytesWritten               = 0;
unsigned long packetsWrite               = 0;
unsigned long largestWritePacket         = 0;
unsigned long smallestWritePacket        = 0xFFFFFFFFl;
unsigned long totalWritePacketBytes      = 0;
unsigned long oversizeWritePackets       = 0;
unsigned long largestOversizeWritePacket = 0;

/* showUartErrors() counters. */
unsigned long numBreak          = 0;
unsigned long numFramingError   = 0;
unsigned long numParityError    = 0;
unsigned long numOverrunError   = 0;
unsigned long numTxInterrupts   = 0;
unsigned long numRxInterrupts   = 0;

void showReadStats()
{
	if ( smallestReadPacket == 0xFFFFFFFFl )
		smallestReadPacket = 0;

	printf ("Read statistics:\n");

	printf ("%9lu Largest packet      %9lu Smallest packet\n",
				largestReadPacket, smallestReadPacket);

	printf ("%9lu Oversize packets    %9lu Largest oversize packet\n",
				oversizeReadPackets, largestOversizeReadPacket);

	printf ("%9lu Total packets       %9lu Buffer overruns\n",
				packetsRead, readBufferOverruns);

	printf ("%9lu Total bytes         %9.1f Average bytes/minute\n",
				totalReadPacketBytes,
				starttime == 0 || playtime == 0 ? 0 :
					(float) totalReadPacketBytes / (float) ((float) playtime / 60));
	printf ("%9lu Receive interrupts  %9.1f Average bytes/interrupt\n",
		numRxInterrupts,
		numRxInterrupts == 0 ? 0 :
			(float) totalReadPacketBytes / (float) numRxInterrupts);
	printf ("\n");
}

void showWriteStats()
{
	if ( smallestWritePacket == 0xFFFFFFFFl )
		smallestWritePacket = 0;

	printf ("Write statistics:\n");

	printf ("%9lu Largest packet      %9lu Smallest packet\n",
				largestWritePacket, smallestWritePacket);

	printf ("%9lu Oversize packets    %9lu Largest oversize packet\n",
				oversizeWritePackets, largestOversizeWritePacket);

	printf ("%9lu Total packets       %9lu Buffer overruns\n",
				packetsWrite, writeBufferOverruns);

	printf ("%9lu Total bytes         %9.1f Average bytes/minute\n",
				totalWritePacketBytes,
				starttime == 0 || playtime == 0 ? 0 :
					(float) totalWritePacketBytes / (float) ((float) playtime / 60));
	printf ("%9lu Transmit interrupts %9.1f Average bytes/interrupt\n",
		numTxInterrupts,
		numTxInterrupts == 0 ? 0 :
			(float) totalWritePacketBytes / (float) numTxInterrupts);
	printf ("\n");
}

void showUartErrors()
{
	printf ("UART line status\n");
	printf ("%9lu Breaks detected     %9lu Framing errors\n",
		numBreak, numFramingError);
	printf ("%9lu Parity errors       %9lu Overrun errors\n",
		numParityError, numOverrunError);
}

/*
================
=
= write_buffer
=
================
*/
void write_buffer( char *buffer, unsigned int count )
{
// if this would overrun the buffer, throw everything else out
	if (outque.size + count > QUESIZE)
	{
		++writeBufferOverruns;
		outque.tail = outque.head;
		outque.size = 0;
	}

	while (count--)
		write_byte  (*buffer++);

	if ( INPUT( uart + LINE_STATUS_REGISTER ) & 0x40)
		jump_start();
}

void hangup_modem (void)
{
	if (nodropdtr == 1)
		printf("\nLeaving DTR up!\n");
	else
	{
		printf ("\n");
		printf ("\nDropping DTR\n");
		OUTPUT( uart + MODEM_CONTROL_REGISTER
			, INPUT( uart + MODEM_CONTROL_REGISTER ) & ~MCR_DTR );
		delay (1250);
		OUTPUT( uart + MODEM_CONTROL_REGISTER
			, INPUT( uart + MODEM_CONTROL_REGISTER ) | MCR_DTR );
		ModemCommand("+++");
		delay (1250);
		if (hangup [0] != EOS)
			ModemCommand(hangup);
		else
		{
			printf ("Warning: No HANGUP= string in config file. Using default.\n");
			ModemCommand("ATH0");
		}
		delay (1250);
		while (read_byte () != -1)
			;
	}
}

/*
=================
=
= Error
=
= For abnormal program terminations
=
=================
*/
void Error (char *error, ...)
{
	va_list argptr;

	if (usemodem)
		hangup_modem ();

	ShutdownPort ();

	if (vectorishooked)
		setvect (doomcom.intnum,olddoomvect);
	if (error)
	{
		va_start (argptr,error);
		vprintf (error,argptr);
		va_end (argptr);
		printf ("\n\n");
	}

	exit (error != (char *) NULL);
}

/*
================
=
= ReadPacket
=
================
*/
#define MAXPACKET	512
#define	FRAMECHAR	0x70

char	packet[MAXPACKET];
int		packetlen;
int		inescape;
int		newpacket;

boolean ReadPacket (void)
{
	int	c;

// if the buffer has overflowed, throw everything out

	if (inque.size > QUESIZE - 4)	// check for buffer overflow
	{
		++readBufferOverruns;           /* Count read overruns */
		inque.tail = inque.head;
		inque.size = 0;
		newpacket = true;
		return false;
	}

	if (newpacket)
	{
		packetlen = 0;
		newpacket = 0;
		overReadPacketLen = 0;
	}

	while ( 1 )
	{
		if ((c = read_byte ()) < 0)
			return false;      // haven't read a complete packet
		if (inescape)
		{
			inescape = false;
			if (c != FRAMECHAR)
			{
				newpacket = 1;
				++packetsRead;  /* Count packets read */

				if ( packetlen > largestReadPacket ) /* Track largest packet */
					largestReadPacket = packetlen;

				if ( packetlen < smallestReadPacket ) /* Track smallest packet */
					smallestReadPacket = packetlen;

				totalReadPacketBytes += packetlen;    /* Count total packet bytes */

				return true;    // got a good packet
			}
		}
		else if (c == FRAMECHAR)
		{
			inescape = true;
			continue;			// don't know yet if it is a terminator
		}						// or a literal FRAMECHAR

		if (packetlen >= MAXPACKET)
		{
			++overReadPacketLen;			/* Keep track of size of oversize packet */
			oversizeReadPackets++;		/* Count oversize packets */

			if ( overReadPacketLen > largestOversizeReadPacket )
				largestOversizeReadPacket = overReadPacketLen;

			continue;			// oversize packet
		}

		packet[packetlen] = c;
		packetlen++;
	}
}

/*
=============
=
= WritePacket
=
=============
*/
void WritePacket (char *buffer, int len)
{
	int		b;
	char	static localbuffer[MAXPACKET*2+2];

	b = 0;
	if (len > MAXPACKET)
	{
		++oversizeWritePackets;         /* Count oversize write packets */
		if ( len > largestOversizeWritePacket )
			++largestOversizeWritePacket;

		return;
	}

	if ( len > largestWritePacket )
		largestWritePacket = len;

	if ( len < smallestWritePacket )
		smallestWritePacket = len;

	totalWritePacketBytes += len;

	++packetsWrite;

	while (len--)
	{
		if (*buffer == FRAMECHAR)
			localbuffer[b++] = FRAMECHAR;	// escape it for literal
		localbuffer[b++] = *buffer++;
	}

	localbuffer[b++] = FRAMECHAR;
	localbuffer[b++] = 0;

	write_buffer (localbuffer, b);
}

/*
=============
=
= NetISR
=
=============
*/
void interrupt NetISR (void)
{
	if (doomcom.command == CMD_SEND)
		WritePacket ((char *)&doomcom.data, doomcom.datalength);
	else if (doomcom.command == CMD_GET)
	{
		if (ReadPacket () && packetlen <= sizeof(doomcom.data) )
		{
			doomcom.remotenode = 1;
			doomcom.datalength = packetlen;
			memcpy (&doomcom.data, &packet, packetlen);
		}
		else
			doomcom.remotenode = -1;
	}
}

/*
=================
=
= Connect
=
= Figures out who is player 0 and 1
=================
*/
int Connect (void)
{
	struct time		time;
	int				oldsec;
	int		localstage, remotestage;
	char	str[10];

//
// wait for a good packet
//
	printf ("Attempting to connect across serial link, press escape to abort.\n");

	doomcom.consoleplayer = player;
	oldsec = -1;
	localstage = remotestage = 0;
	inque.tail = inque.head;   //clears the buffer.
	inque.size = 0;
	newpacket = TRUE;

	while (remotestage < 4)
	{
		while ( bioskey(1) )
		{
			if ( (bioskey (0) & 0xff) == ESC)
			{
				printf ("\n\nSerial game synchronization aborted.\n");
				while (read_byte () != -1);
				return FALSE;
			}
		}
		if (ReadPacket ())
		{
			packet[packetlen] = 0;
			printf ("read: '%s'\n", packet);
			if (packetlen != 7)
			{
				printf ("bad packet len = %d (should be 7)\n", packetlen);
				continue;
			}
			if (strncmp(packet,"PLAY",4) )
			{
				printf ("error: first 4 char's aren't 'PLAY'\n");
				continue;
			}
			remotestage = packet[6] - '0';
			localstage = remotestage+1;
			if (packet[4] == '0'+doomcom.consoleplayer)
			{
				doomcom.consoleplayer ^= 1;
				localstage = remotestage = 0;
			}
			oldsec = -1;
		}
		gettime (&time);
		if (time.ti_sec != oldsec)
		{
			oldsec = time.ti_sec;
			sprintf (str,"PLAY%i_%i",doomcom.consoleplayer,localstage);
			WritePacket (str,strlen(str));
			printf ("wrote: '%s'\n",str);
		}
	}
	while (ReadPacket ()) // flush out any extras
	;
	return TRUE;
}

/*
==============
=
= ModemCommand
=
==============
*/
void ModemCommand (char *str)
{
	int i;
	char *ptr;

	printf ("Modem command : %s\n",str);
	ptr = str;
	for (i = 0; i < strlen (str); i++)
	{
		write_buffer (ptr++, 1);
		delay (20);
	}
	write_buffer ("\r",1);
}

/*
==============
=
= ModemResponse
=
= Waits for OK, RING, CONNECT, etc
==============
*/
int ModemResponse (char *resp)
{
	int		c;
	int		respptr;
	char	response[80];

	do
	{
		printf ("Modem response: ");
		respptr=0;
		while (1)
		{
			while ( bioskey(1) )
			{
				if ( (bioskey (0) & 0xff) == ESC)
				{
					printf ("\nModem response aborted.\n");
					hangup_modem ();
					return FALSE;
				}
			}
			c = read_byte ();
			if (c==-1)
				continue;
			if (c=='\n' || respptr == 79)
			{
				response[respptr] = 0;
				printf ("%s\n",response);
				break;
			}
			if (c >= ' ')
			{
				response[respptr] = c;
				respptr++;
			}
		}
	}while (strncmp(response,resp,strlen(resp)));
	return TRUE;
}

/*
=============
=
= InitModem
=
=============
*/
void InitModem (void)
{
	if (init1 [0] != EOS)
	{
		ModemCommand (init1);
		if (! ModemResponse ("OK"))
			return;
	}

	if (init2 [0] != EOS)
	{
		ModemCommand (init2);
		if ( !ModemResponse ("OK"))
			return;
	}

	if (use_altbaudinit == TRUE && altbaudinit [0] != EOS)
	{
		ModemCommand (altbaudinit);
		if ( !ModemResponse ("OK"))
			return;
	}
}

void alt_init (void)
{
	int ch;

	printf ("\nUse %lu baud and init string '%s'? (y/n): ",
		altbaud, altbaudinit);
	if ((ch = getch()) == 'y' || ch == 'Y')
	{
		use_altbaudinit = TRUE;
		baud = altbaud;
		InitPort();   //Re-inits the port with new baud rate.
		printf (
			"\n\nPort re-initialized.  Alternate init string will be used for dial and answer.\n\n");
		pause ();
	}
	printf ("\n");
}

/*
=============
=
= Dial_Modem
=
=============
*/
int Dial_Modem (void)
{
	char	cmd[80];
	int ch;
	char line [50];
	FILE	*infile;
	int found_name = FALSE;
	int prompt = TRUE;
	char buf [50];

	usemodem = true;
	InitModem ();

	if (dial [0] != EOS)
	{
		printf ("\nDial %s? (y/n): ", dial);
		if ((ch = getch()) == 'y' || ch == 'Y')
			prompt = FALSE;
	}

	if (prompt)
	{
		printf ("\nEnter number to dial or phonebook name: ");
		gets (line);
		if (sscanf (line, "%s", dial) == EOF)
			return FALSE;

		if ((infile = fopen (PROG_CONFIG, "r")) == NULL)
		{
			sprintf (buf, "Couldn't read file %s.\n", PROG_CONFIG);
			Error (buf);
		}

		while (fgets (line, 256, infile) != NULL)
		{
			if (line [0] == 'N' && line [1] == 'A')
			{
				/* NAME */
				if (strncmp (&line [5], dial, strlen (dial)) == 0)
					found_name = TRUE;
			}
			else if (found_name && line [0] == 'N' && line [1] == 'U')
			{
				/* NUMBER */
				sscanf (&line [7], "%s", dial);
				break;          // Found a number break out and dial
			}
		}
		fclose (infile);
	}

	printf ("\nDialing %s\n\n", dial);
	if (pulsedial)
		sprintf (cmd,"ATDP%s", dial);
	else
		sprintf (cmd,"ATDT%s", dial);

	ModemCommand(cmd);
	return ModemResponse ("CONNECT");
}

/*
=============
=
= Answer
=
=============
*/
int Answer (void)
{
	usemodem = true;
	InitModem ();
	printf ("\nWaiting for ring...\n\n");

	if (! ModemResponse ("RING"))
		return FALSE;
	ModemCommand ("ATA");
	return ModemResponse ("CONNECT");
}

int rgstrncpy (char *dest, char *src, int max)
{
	int i = 0;
	char *srcptr;
	char *destptr;

	/* Just like strncpy(), but force a null at the end regardless of
	 * whether we copied the max # of characters or not.
	 */
	srcptr = src;
	destptr = dest;
	while (i < max)
	{
		if ((*destptr++ = *srcptr++) == EOS)
			break;
		++i;
	}
	if (i == max)
		dest [max-1] = EOS;
	return i;
}

void get_config_settings (void)
{
	char line [80+1];
	char space[]=" ";
	int len;
	FILE	*infile;
	int i;
	unsigned char far *vectorptr;
	unsigned long lnum;
	char buf [50];

	if ((infile = fopen (PROG_CONFIG, "r")) == NULL)
	{
		sprintf (buf, "Couldn't read %s file.", PROG_CONFIG);
		Error (buf);
	}

	while (fgets (line, 81, infile) != NULL)
	{
		if (line [(len = strlen (line))-1] == '\n')	/* Get rid of cr at end */
			line [len-1] = EOS;
		switch (line [0])
		{
			case 'A' :
				if (line[3] == 'D')
				{		/* ALTDEATH */
					sscanf (&line [9], "%d", &altdeath);
				}
				else if (line [7] == 'I')
				{		/* ALTBAUDINIT */
					if ( line[12] != EOS )
						rgstrncpy (altbaudinit, &line [12], 80+1);
				}
				else if (line [3] == 'B')
				{		/* ALTBAUD */
					if (sscanf (&line [8], "%lu", &altbaud) != EOF &&
						altbaud != 9600 && altbaud != 14400 && altbaud != 19200 &&
						altbaud != 38400 && altbaud != 57600 && altbaud != 115200)
					{
						altbaud = 9600;
					}
				}
				break;
			case 'B' :
				/* BAUD */
				if (sscanf (&line [5], "%lu", &baud) != EOF &&
					baud != 9600 && baud != 14400 && baud != 19200 &&
					baud != 38400 && baud != 57600 && baud != 115200)
				{
					baud = 9600;
				}
				break;
			case 'C' :
				if (line [3] == 'F')
				{
					/* CONFIG */
					rgstrncpy (config, &line [7], 80+1);
				}
				else
				{
					/* COM */
					sscanf (&line [4], "%d", &comport);
				}
				break;
			case 'D' :
				if (line [2] == 'V')
				{
					/* DEVPARM */
					rgstrncpy (devparm, &line [8], 256+1);
					if (strncmpi(devparm, "-f", 2) != 0 && devparm[0] != EOS)
					{
						rgstrncpy (add, devparm, 50);
						strcat(add, space);
					}
				}
				else if (line [3] == 'L')
				{
					/* DIAL */
					rgstrncpy (dial, &line [5], 50+1);
				}
				else if (line [2] == 'R')
				{
					/* DIRECTORY  */
					if (rgstrncpy (directory, &line [10], 40+1) > 0)
					{
						if (directory[(len = strlen (directory))-1] != ':'
							&& directory[len-1] != '\\')
						{
							directory [len] = '\\';
							directory [len+1] = EOS;
						}
					}
					else
						strcpy (directory, ".\\");
				}
				else if (line [3] == 'T')
				{
					/* DEATHMATCH */
					sscanf (&line [11], "%d", &deathmatch);
				}
				else if (line [5]	== 'X')
				{
					/* DOOMEXE */
						rgstrncpy (doomexe, &line [8], 12+1);
				}
				else if (line [4] == '2')
				{   /* DOOM2 */
					sscanf (&line [6], "%d", &doom2);
				}
				else if (line [4] == 'V')
				{
					/* DOOMVER1_2 */
					sscanf (&line [11], "%d", &doomver1_2);
				}
				break;
			case 'E' :
				/* EPISODE */
				sscanf (&line [8], "%d", &episode);
				break;
			case 'H' :
				/* HANGUP */
				rgstrncpy (hangup, &line [7], 80+1);
				break;
			case 'I' :
				if (line [1] == 'N')
				{
					if (line [4] == '1')
					{
						/* INIT1 */
						rgstrncpy (init1, &line [6], 80+1);
					}
					else
					{
						/* INIT2 */
						rgstrncpy (init2, &line [6], 80+1);
					}
				}
				else
				{
					/* IRQ */
					sscanf (&line [4], "%d", &irq);
				}
				break;
			case 'L' :
				/* LOADGAME */
				sscanf (&line [9], "%d", &loadgame);
				break;
			case 'M' :
				/* MAP */
				sscanf (&line [4], "%d", &map);
				break;
			case 'N' :
				if (line [3] == 'O')
				{
					/* NOMONSTERS (as opposed to NAME or NUMBER) */
					sscanf (&line [11], "%d", &nomonsters);
				}
				else if (line [3] == 'R')
				{
					/* NODROPDTR */
					sscanf (&line [10], "%d", &nodropdtr);
				}
				break;
			case 'P' :
				if (line [1] == 'O')
				{
					/* PORT */
					sscanf (&line [5], "0x%x", &uart);
				}
				else if (line [1] == 'L')
				{
					/* PLAYER */
					sscanf (&line [7], "%d", &player);
					if (player < 0 || player > 1)
						player = 0;
				}
				else
				{
					/* PULSEDIAL */
					sscanf (&line [10], "%d", &pulsedial);
				}
				break;
			case 'R' :
				/* RESPAWN */
				sscanf (&line [8], "%d", &respawn);
				break;
			case 'S' :
				/* SKILL */
				sscanf (&line [6], "%d", &skill);
				break;
			case 'V' :
				/* VECTOR */
				sscanf (&line [7], "0x%x", &vector);
				break;
			case 'W' :
				/* WADNAME */
				rgstrncpy (wadname [0], &line [8], 12+1);
				break;
		}
	}
	fclose (infile);
	if ( doomexe[0] == EOS )
	{
		if (doom2)
			strcpy(doomexe, "DOOM2");
		else
			strcpy(doomexe, "DOOM");
	}

	/* Get irq and uart if not already set */
	if (irq == -1 || uart == -1)
		GetUart ();

	/* Get an interrupt vector if not already set */
	if (vector == -1)
	{
		for (vector = 0x60 ; vector <= 0x66 ; vector++)
		{
			vectorptr = *(unsigned char far * far *)(vector*4);
			if ( !vectorptr || *vectorptr == 0xcf )
				break;
		}
		if (vector == 0x67)
		{
			printf ("Warning: no NULL or iret interrupt vectors were found in the 0x60 to 0x66\n");
			printf ("range.  You can specify a vector with the VECTOR=0x<num> line in %s.\n\n", PROG_CONFIG);
			pause ();
			vector = 0x66;
		}
	}
	doomcom.intnum = vector;
}

void print_config_settings (void)
{
	clrscr ();
	printf ("   Port settings (%s-type UART detected):\n\n",
		uart_type == UART_8250 ? "8250" : "16550");
	printf ("INIT1=%.73s\n", init1);
	printf ("INIT2=%.73s\n", init2);
	printf ("HANGUP=%.72s\n", hangup);
	printf ("BAUD=%-6lu\n", baud);
	printf ("COM=%d\n", comport);
	printf ("IRQ=%d\n", irq);
	printf ("PORT=0x%x\n", uart);
	printf ("NODROPDTR=%d\n", nodropdtr);

	printf ("\n   DOOM settings:  ");
	textcolor(RED);
	if (doom2)
		cprintf("DOOM ][: Hell on Earth\r\n\n");
	else
		cprintf("DOOM I\r\n\n");
	textcolor(LIGHTGRAY);
	if (doom2)
		printf ("EPISODE=n/a          ");
	else
		printf ("EPISODE=%d            ", episode == -1 ? 1 : episode);
	printf ("MAP=%d\n", map == -1 ? 1 : map);
	printf ("DEATHMATCH=%d         ALTDEATH=%d\n", deathmatch, altdeath);
	printf ("NOMONSTERS=%d         RESPAWN=%d\n", nomonsters, respawn);
	printf ("LOADGAME=%c           DEVPARM=%.50s\n",
		loadgame == -1 ? ' ' : loadgame + '0',
		devparm);
	printf ("SKILL=%d              DIAL=%.50s\n",
		skill == -1 ? 3 : skill,
		dial);
	printf ("WADNAME=%-12.12s DIRECTORY=%.39s\n", wadname [0], directory);
	printf ("DOOMEXE=%-12.12s CONFIG=%.51s\n", doomexe, config);
	printf ("DOOMVER1_2=%d         PULSEDIAL=%d\n", doomver1_2, pulsedial);
	printf ("PLAYER=%d             VECTOR=0x%x\n", player, vector);
}

void configure (void)
{
	char line [256];
	char files [256];
	char space[]=" ";
	int i = 0;
	int j = 0;
	int xpos;
	int ypos;

	clrscr ();
	printf ("Configure...  Press ENTER to keep current value shown in [].\n\n");

	printf ("  Wadfile directory [%s]: ", directory);
	pickdirectory ();
	printf ("\n");
	pickwads ();

	if (wadname [0][0] != EOS)       // Clear devparm for new wad files
		devparm [0] = EOS;

	files[0]=EOS;
	while(wadname[i][0] != EOS)
	{
		if (checkwad (i, wadname[i]) != FALSE)			  // Check for wadfile existance , allow missing wads to remain
			strcat(files,devparm);							  // put all files into 1 string
		else if (i == 0)
			wadname [0][0] = EOS;
		i++;
	}

	if (wadname [0][0] != EOS)   // Construct file arguments into the devparm line
		sprintf(devparm,"%s-file%s\0",add,files);   // places devparm stuff first
	else
		strcpy(devparm,add);

	if ((wadname[0] == EOS) && (add [0] == EOS))
		devparm [0] = EOS;
	 /* Prints the previous Devparm strings in a neat way */
	cprintf ("\r\n  Doom switches (-1 to Clear, \"+-switch\" to add, \"-switch\" to change)\r\n"
					 "                (This only changes Misc. switches)\r\n");
	cprintf ("   Misc. [");
	cprintf("]\r\n   Pwads [");
	xpos=wherex();
	ypos=wherey();
	printf("]");
	gotoxy(xpos,ypos-1);
	i=0;
	while (devparm[i] != EOS)	  /* Displays previous arguments */
	{
		if ( (devparm[i] == '-') && (devparm[i+1] == 'f') )
		{
			cprintf("]");			  //moves to the File section of the display
			gotoxy(xpos,ypos);
		}
		if ( (devparm[i] == ' ') && (devparm[i-4] == '.') )
			if (++j == 2)			//Allows 2 Pwads displayed per line
			{
				j=0;
				ypos++;
				cprintf("]\r\n         [     ");
			}
		cprintf ("%c",devparm[i++]);
		}
	cprintf("]");
	gotoxy(1,ypos+1);
	cprintf("     ??? : ");

	gets (line);
	if (strcmp (line, "-1") == 0)
	{
		add [0] = EOS;
		if (files[0] != EOS)
			sprintf(devparm,"-file%s", files);   //resets devparm to just files
		else
			devparm[0]=EOS;

		printf("\nNew Misc. Options: (none)\n");  //redisplay options
	}
	else if (line [0] == '+')
	{
		strcat (add, &line[1]);		//Removes the "+"
		strcat (add, space);

		if (files[0] != EOS)
			sprintf(devparm, "%s-file%s", add, files);  //Places additional stuff first
		else
			strcpy(devparm, add);
		line [0] = EOS;

		printf("\nNew Misc. Options: %s\n",add);  //redisplay options
	}
	else if (line [0] != EOS)       //Completely new additional stuff
	{
		sprintf(add,"%s ", line);
		if (files[0] != EOS)
			sprintf(devparm,"%s-file%s", add, files);
		else
			strcpy(devparm, add);

		printf("\nNew Misc. Options: %s\n",add);  //redisplay options
	}
	printf ("\n");

	printf ("  deathmatch (0/1)     [%d]: ", deathmatch);
	gets (line);
	sscanf (line, "%d", &deathmatch);

	if (deathmatch == 1)
	{
		printf ("  altdeath (0/1)       [%d]: ",altdeath);
		gets (line);
		sscanf (line, "%d", &altdeath);
	}
	else
		altdeath = 0;

	printf ("  nomonsters (0/1)     [%d]: ", nomonsters);
	gets (line);
	sscanf (line, "%d", &nomonsters);

	printf ("  respawn (0/1)        [%d]: ", respawn);
	gets (line);
	sscanf (line, "%d", &respawn);

	if (!doom2)  //Doom 2 doesn't have episodes
	{
		printf ("  episode (1-3)        [%d]: ", episode == -1 ? 1 : episode);
		gets (line);
		sscanf (line, "%d", &episode);
		if (episode < 2 || episode > 3)	/* If default, set back to -1 */
			episode = -1;
	}

	printf ("  map (1-32)           [%d]: ", map == -1 ? 1 : map);
	gets (line);
	sscanf (line, "%d", &map);
	if (map < 2 || map > 32)				/* If default, set back to -1 */
		map = -1;

	printf ("  skill (1-5)          [%d]: ", skill == -1 ? 3 : skill);
	gets (line);
	sscanf (line, "%d", &skill);
	if (skill < 1 || skill > 5 || skill == 3)	/* If default, set back to -1 */
		skill = -1;

	printf ("  loadgame (0-5 or -1) [%c]: ", loadgame == -1 ? ' ' : loadgame + '0');
	gets (line);
	sscanf (line, "%d", &loadgame);
	if (loadgame < 0 || loadgame > 5)
		loadgame = -1;

	printf( "  player Num. (0 or 1) [%d]: ", player);
	gets(line);
	sscanf (line, "%d", &player);
	if (player < 0 || player > 1)
		player = 0;

	printf("\n");
}

void pickdirectory (void)
{
	char line [40];
	char *p;

	line[0]=39;      			//Limit to 38 chars + null
	p=cgets (line);
	strcpy(line, p);
	if (strcmp (line, "-1") == 0)
		directory [0] = EOS;
	else if (line [0] != EOS )
	{
		if( (line [strlen(line) - 1] != ':') && (line [strlen(line) -1] != '\\') )
			sprintf(directory, "%s\\", line);
		else
			strcpy (directory, line);
	}
}

void printselected ( int xpos)
{
	int i=0;
	int ypos;

	ypos=wherey();
	while (wadname [i][0] != EOS)	  /* Displays previous arguments */
	{
		if ( (wherex() + strlen(wadname[i])) > 78 )   //avoids line wrapping
		{
			cprintf("]\r\n");
			ypos=wherey();
			gotoxy(xpos,ypos);
			cprintf("[");
		}
		cprintf ("%s, ",wadname[i++]);
	}
	if ( i == 0 ) cprintf("]");
	else cprintf("\b\b]");
	ypos=wherey();
	gotoxy(xpos-4, ypos+1);
	cprintf("??? :");
}

void pickwads (void)
{
	char line [100];
	char ext[]=".WAD";

	kill_nonwads();
	wadname[9][0]=EOS;			//Limit to 9 Pwads in configure
	cprintf ("\r\n  Enter Pwads to play (-1 to clear, separate wads with a \"space\")\r\n"
			  "   Names (9 MAX) [");
	printselected(18);
	gets (line);
	if (strcmp (line, "-1") == 0)
	{
		wadname [0][0] = EOS;
		strcpy (devparm, add);
	}
	else if (line [0] != EOS)    //  Parse multiple wads
		parsewad( line, ext);
}

void parsewad (char *line, char *ext)
{
	int i=0;
	char *ptr;

	for (i = 0; i < 9; i++)
	{
		if ((ptr = strtok (i == 0 ? line : NULL, " ")) == NULL)
		{
			wadname [i][0] = EOS;
			return;
		}
		strcpy (wadname [i], ptr);
		if (strlen (wadname [i]) > 4)
		{
			if (strncmpi (
				&wadname [i][strlen (wadname [i]) - 4], ext, strlen (ext)) == 0)
			{
				/* Wad name already ends in given extension, so don't add it. */
				continue;
			}
		}
		/* Wad name doesn't end in given extension already, so add it. */
		strcat (wadname [i], ext);
	}
}

int checkwad ( int numwad, char *wad )
{
	char junk [55];
	int tempmapnum;
	int j=0;

	sprintf(junk,"%s%s\0",directory,wad);

	if (getwadlevel( junk) == TRUE)
	{
		while (levels[j][0] != EOS)
		{
			if (numwad == 0) map = 32;
			if (doom2)
				sscanf (&levels[j][3], "%d%d", &tempmapnum);
			else
				sscanf (&levels[j][3], "%d", &tempmapnum);
			if ( tempmapnum < map )
			{
				map=tempmapnum;
				if (!doom2)
					sscanf (&levels[j][1], "%d", &episode);
				textcolor(YELLOW);
				if (doom2)   // Doom 2 has no episode number
					cprintf("  Setting to Map %d\r\n",map);
				else
					cprintf("  Setting to Episode %d, Map %d\r\n",episode,map);
				textcolor(LIGHTGRAY);
				numwad++;
			}
		j++;
		}
	}
	else if ( getwadlevel ( junk) == 2)
	{
		printf("\a\nFile %s does not exist.\n\n",junk);
		sprintf(devparm," %s",junk);
		return 2;  // Nonexist file
	}
	else
	{
		printf("\a\nWadfile, %s, is corrupted\n",junk);
		return FALSE;
	}
	sprintf(devparm," %s",junk);
	return TRUE;
}

int getwadlevel ( char *wadname)
{
	FILE *WadFile;
	char pwad[6];
	long dirstart;
	int direntries;
	int i=0;
	int j=0;
	char name[17];

	if ((WadFile = fopen(wadname, "rb")) == NULL)
		return 2;    // 2 Non-Existant File
	if (fread ( &pwad, 1 , 4, WadFile) != 4)
		return FALSE;
	if ( (strncmp( pwad, "PWAD", 4)) && (strncmp( pwad, "IWAD", 4)) )
		return FALSE;
	if (fread ( &direntries , 1 , 4, WadFile) != 4)
		return FALSE;
	if (fread( &dirstart, 1, 4, WadFile) != 4)   /* Reads 4 byte pointer */
		return FALSE;
	fseek(WadFile, dirstart, 0);
	while ( i++ != direntries )
	{
		if (fread( &name, 1, 16, WadFile) != 16 )
			return FALSE;
		if (doom2)  //Doom 2 wad directory uses MAP01-MAP32
		{
			if (( name [8] == 'M' ) && ( name [9] == 'A' ) && ( name [10] == 'P'))
				strcpy(levels[j++], &name[8]);
			else levels[j][0]=EOS;
		}
		else      //Doom wad directory uses E1M1-E3M9
		{
			if (( name [8] == 'E' ) && ( name [10] == 'M' ))
				strcpy(levels[j++], &name[8]);
			else levels[j][0]=EOS;
		}
	}
	fclose(WadFile);
	return TRUE;
}

void wadleveldisplay (void)
{
	char line[40];
	char test[55];
	char ext[]=".WAD";
	int i=0;
	int j=0;
	int done_first;

	clrscr ();
	printf ("  Enter directory name [%s]: ", directory);
	pickdirectory ();
	kill_nonwads ();
	cprintf ("\r\n  Enter the Wadfile(s) to be scanned (separate wads with spaces)\r\n    [");
	printselected(5);
	gets(line);
	printf("\n");
	if (line [0] != EOS)
		parsewad ( line , ext);
	i=0;
	while (wadname[i][0] != EOS)
	{
		done_first = FALSE;
		sprintf( test, "%s%s\0", directory, wadname [i++]);
		if (getwadlevel( test) == TRUE)
		{
			printf("%s contains:\n  ", test);
			while ( levels[j][0] != EOS)
			{
				printf ("%s%s",
					done_first ? ", " : "",
					levels [j++]);
				done_first = TRUE;
			}
			j=0;
			printf("\n");
			pause();
		}
		else
			printf("Error reading %s", test);
		printf ("\n\n");
	}
}

void kill_nonwads ( void )     //removes non-wad files from the tag list
{
	int i=0;
	int j;

	while ( wadname [i][0] != EOS )
	{
		if ( strncmpi(&wadname[i][ strlen(wadname[i]) - 4], ".WAD", 4) != 0)
		{
			for (j=i; j<9; j++)
				strcpy(wadname[j], wadname[j+1]);  //moves all files up 1 position
			i--;     // Removed a file, we need to decriment to re-check 'i'
		}
		i++;
	}
}

int SendFile ( void )
{
	struct time t;
	char zipfile[13];			//Name of the Temperary Zipfile
	char ziplist[9];        //Name of the Temperary PKZIP response file
	char pkzip[35];			//Pkzip command line
	char dsz[130];          //DSZ command line
	char dszfiles[20][72];  //list of files to be 'strcat'ed to the DSZ command line
	char temp[55];				//what else...a temporary variable
	int i=0;                //just a counter
	int c;
	int runzip=FALSE;       //Do I run PKZIP ???
	int rundsz=FALSE;       //Do I run DSZ ???
	int dszloops=1;			//number of times to run dsz
	FILE *test;

	clrscr();
	if (wadname [0][0] == EOS )
	{
		printf("No Files were selected\n");   //duhh..
		pause();
		return FALSE;
	}
	for (c=0;c<4;c++)    //Initializes the files I just use a 'strcat' later
		dszfiles[c][0]=EOS;

	gettime(&t);         //I shouldn't ever overwrite a file this way :)
	sprintf(zipfile,"ID%02d%02d%02d.ZIP", t.ti_hour, t.ti_min, t.ti_sec);
	sprintf(ziplist,"ID%02d%02d%02d", t.ti_hour, t.ti_min, t.ti_sec);
	sprintf (pkzip,"pkzip -ex %s @%s", zipfile, ziplist);

	if ((test = fopen( ziplist, "w+t" )) == NULL )   // open a pkzip list file
		return FALSE;
	textcolor(YELLOW);
	while ( wadname[i][0] != EOS )
	{
		if (strncmpi( &wadname[i][ strlen(wadname[i]) - 4], ".ZIP", 4) != 0)
		{			//Adds non Zipfiles to the PKZIP response file
			fprintf(test, "%s%s\n", directory, wadname[i++]);
			runzip=TRUE;
		}
		else
		{			//Adds Pre Zipped files to lists for DSZ
			rundsz=TRUE;
			sprintf(temp, "%s%s ", directory, wadname[i++]);
			if ( (strlen(dszfiles[dszloops-1]) + strlen(temp)) >= 70)
				dszloops++;
			strcat(dszfiles[dszloops-1], temp);
		}
	}
	fclose(test);
	if (runzip == TRUE)
	{
		printf("Running Pkzip......\n");
		if (system ( pkzip));
		if (access(zipfile, 0) == 0)
		{			//Makes sure you didn't <CTRL>Break Pkzip
			rundsz=TRUE;
			if ( (strlen(dszfiles[dszloops-1]) + strlen(zipfile)) >= 70)
				dszloops++;
			strcat(dszfiles[dszloops-1], zipfile);
		}
		else	 //You must have killed PKZIP, check whether there are other files to send
			if (rundsz == FALSE)
				dszloops=0;
	}
	remove(ziplist);
	if (rundsz == TRUE)
	{
		clrscr();
		cprintf("<ALT>-N can be used to NUKE the DSZ transfer\r\n"
				 "but please don't unless you really screwed up :(\r\n\r\n");
	}
	if (dszloops > 1)
	{
		cprintf("Due to command line limitations with DSZ, DSZ needs to be called\r\n"
				  "%d times to send all flagged files.  Don't worry about the\r\n"
				  "negotiation, I handle the whole thing.  I Hope :)\r\n"
				  "BTW: You'll need to NUKE each call to DSZ to abort the transfer.\r\n\r\n",dszloops);
		delay(1000);
	}
	while (dszloops >= 1)
	{
		sprintf(dsz,"transfer\\dsz portx %x,%i speed %lu d ha on sz -rr %s", uart, irq, baud, dszfiles[dszloops-1] );
		if(system( dsz));
		if (--dszloops != 0)
		{
			cprintf("\r\n\r\nFile set complete.  Preparing for next set...\r\n");
			delay(3000);  //Delay for 3 seconds before writing negotiation byte
			c=0x19;
			write_buffer (&(char)c, 1);    //Sends command to reload DSZ
		}
		else
		{
			delay(3000);
			c=0x1A;
			write_buffer (&(char)c, 1);	 //Sends command to quit
		}
	}
	textcolor(LIGHTGRAY);
	clrscr();
	printf("File transfer complete.\n");
	pause();
	remove ( zipfile);
	return TRUE;
}

int ReceiveFile ( char *ziptodir)
{
	char zipfile[15];
	char output[130];
	char temp[50];
	unsigned int i=0;
	int done=FALSE;
	int nextset;
	int c;
	struct time t;
	int time;

	chdir("TRANSFER");
	sprintf(output,"dsz portx %x,%i speed %lu d ha on rz -rr", uart, irq, baud);

	clrscr();
	textcolor(YELLOW);
	cprintf("<ALT>-N can be used to NUKE the DSZ transfer\r\n"
			 "but please don't unless you really screwed up :(\r\n\r\n");

	cprintf("Press <ESC> to quit waiting.\r\n\r\nWaiting for Remote system to start sending");

	while (done != TRUE)
	{
		while ( bioskey(1) )  //Checks for the <ESC> key
		{
			if ( (bioskey (0) & 0xff) == ESC)
			{
				printf ("\n\nTransfer aborted.\n");
				while (read_byte () != -1);
				chdir("..");
				textcolor(LIGHTGRAY);
				return FALSE;
			}
		}

		if (i == 30000 )     //Prints dots so you know its waiting
		{
			textcolor(random(15) + 1);   //I felt like it OK :)
			i=0;
			cprintf(".");
		}
		i++;
		if ((c = read_byte ()) != -1)
		{
			if (c == 0x18)     //check for the ZMODEM autostart
			{
				printf("\n");
				if(system( output) );
				nextset=FALSE;
				gettime(&t);
				time=t.ti_sec+(t.ti_min*60);
				textcolor(YELLOW);
				while ( nextset != TRUE)
				{
					c = read_byte ();
					gettime(&t);
					if ( ((t.ti_min*60)+t.ti_sec - time) > 20)
					{
						cprintf("\r\nOops, I lost the other signal....Aborting\r\n");
						pause();
						nextset=TRUE;
						done=TRUE;
					}
					if (c == 0x19)   //character to re-load DSZ
					{
						nextset = TRUE;
						cprintf("\r\nDue to command line limitations with DSZ, the remote system\r\n"
								  "needed to re-load DSZ to send all of the files\r\n");
						cprintf("\nWaiting for next group of ZIP's");
					}
					if (c == 0x1A)		//character to quit
					{
						nextset=TRUE;
						done=TRUE;
					}
				}
			}           // Yes they really all end here
		}
	}
	chdir("..");
	textcolor(LIGHTGRAY);
	clrscr();
	printf("\nFiles transfered.\n\n");
	printf("Do you wish to FLAG zip's to be extracted to %s? [Yes/No/All]:", directory);
	for (EVER)
	{
		c = tolower (getch());
		switch (c)
		{
			case 'a'	:
			{
				sprintf(zipfile,"transfer\\*.zip");
				printf("\nRunning Pkunzip......\n");
				if (mkdir( ziptodir));
				sprintf(output,"pkunzip %s %s", zipfile, ziptodir);
				if (system( output));
				printf("\n\nDo you want to remove the Zipfiles [Yes/No]:\n");
				for (EVER)
				{
					c = tolower (getch());
					switch (c)
					{
						case 'y'	:
							sprintf(output,"del %s",zipfile);
							system(output);
							return TRUE;
						case 'n' :
							printf("\nOk, they're in the \".\\TRANSFER\\\" directory.\n");
							pause();
							return TRUE;
					}
				}
			}
			case 'y' :
			{
				strcpy(temp,ziptodir);		//dir call changes dir
				dir (DIR_TYPE_ZIP);
				i=0;
				if (mkdir( ziptodir));
				while ( wadname[i][0] != EOS)
				{
					sprintf(output, "pkunzip %s%s %s", directory, wadname[i++], temp);
					if (system( output));
				}
				strcpy(directory, temp);		//resets the directory to zip-output
				printf("\n\nDo you want to remove FLAGGED Zipfiles [Yes/No]:");
				for (EVER)
				{
					c = tolower (getch());
					switch (c)
					{
						case 'y'	:
							i=0;
							while ( wadname [i][0] != EOS)
							{
								sprintf(zipfile,"TRANSFER\\%s",wadname[i++]);
								remove (zipfile);
							}
							return TRUE;
						case 'n' :
							printf("\nOk, they're in the \".\\TRANSFER\\\" directory.\n");
							pause();
							return TRUE;
					}
				}
			}
			case 'n'	:
			{
				printf("\nOK, you can just shell to DOS and do it.\n"
						 "Look in the \".\\TRANSFER\\\" directory for them.\n");
				pause();
				return TRUE;
			}
		}
	}
}

void file_transfer ( void )
{
	char line[100];
	char ext[]="";
	char ch;

	clrscr();
	if ( access("TRANSFER\\DSZ.EXE", 0) != 0)
	{
		printf("\a\n  DSZ.EXE is not in the \".\\TRANSFER\\\" subdirectory\n"
				 "    You must install DSZ.EXE properly before use.  Please consult the\n"
				 "    SER6.TXT for installation notes.\n\n");
		pause();
		clrscr();
		return;
	}
	printf("Send or Receive [S/R]:");
	ch = tolower (getch ());
	switch (ch)
	{
		case 's'	:
			cprintf ("\r\n\n  Enter the directory of the files to be sent [%s]: ", directory);
			pickdirectory();
			printf ("\r\n  Enter the Files [");
			printselected(19);
			gets(line);
			printf("\n");
			if (line [0] != EOS)
				parsewad ( line, ext );
			if ( wadname[0][0] != EOS)
				SendFile ();
			break;

		case 'r'	:
			cprintf ("\r\n\nEnter directory for zipfile extraction [%s]: ", directory);
			pickdirectory();
			ReceiveFile ( directory );
			break;
	}
	InitPort ();        //Re-inits the port, incase DSZ screwed with it
	clrscr();
}

void dir ( int type )
{
	DIR *dir;
	struct dirent *ent;
	char *p;
	char wad[13];
	char wadfiles[301][13];  //300 Files Max or qsort() pukes
	char line[6];
	char extension[5];
	const void *a;
	const void *b;
	int numfiles=0;
	int numflagged=0;
	int i=0;
	int j=0;
	int k=0;
	int pages;
	int xpos;
	int ypos;
	int wildcard=0;

	clrscr ();

	for (i=0; i<301 ; i++)     //Initialize wadfiles
		wadfiles[i][0]=EOS;
	wadname[0][0]=EOS;

	if (type == DIR_TYPE_ZIP)			//Sets the default extension & directory
	{
		sprintf(extension,"ZIP");
		sprintf(directory, "TRANSFER\\");
		line[0]=EOS;
	}
	else
	{
		sprintf(extension,"WAD");
		printf("Enter file extension for search (* for all) [%s]: ",extension);
		line[0]=4;      			//Limit to 3 chars + null
		p=cgets (line);
		strcpy(line, p);
	}

	if (line [0] == EOS )
		strcpy (line, extension);
	sprintf(extension,".%s",line);   // Add a '.' to extension

	//Check for wildcard
	if ( (extension[1] == '*') && (extension[2] == EOS) )
		wildcard=3;		// '3' is the number that needs to be removed from the 'strncmpi' line below

	if (type == DIR_TYPE_NORMAL)
	{
		printf ("\n  Enter the directory to be displayed [%s]: ", directory);
		pickdirectory ();
	}

	if (( dir = opendir(directory)) != NULL)
	{
		while( ((ent = readdir(dir)) != NULL) &&  (numfiles < 300 ) )
		{
			if (strlen(ent->d_name) > 4)
			{
				if (strncmpi( &ent->d_name[strlen(ent->d_name) - 4], extension, (4-wildcard)) == 0)
					strcpy(wadfiles[numfiles++], ent->d_name);
			}
			wadfiles[numfiles][0]=EOS;
		}
		if (closedir(dir) != 0)
		{
			printf("\n\nUnable to close Directory\n\n");
			pause ();
		}
	}
	else
	{
		printf("\n\nUnable to open Directory\n\n");
		pause ();
		return;
	}
	/*  Sorts the files  */
	qsort((void *)wadfiles, numfiles, sizeof(wadfiles[0]), sort_function);
	pages = (numfiles / 68)+1;     //68 files per page
	numfiles = 0;
	for ( i = 0; i < pages; i++)
	{
		clrscr();
		printf("****** Page %i of %i of %s*%s *******\n\n",(i+1), pages, directory, extension);
		for (j = (0 + numfiles); j < (68 + numfiles); )
		{
			for ( k = 0; k < 4; k++)        // 4 columns per page
			{
				if (wadfiles[j][0] != EOS)
				{
					printf("[%3d] %+12s  ", (j+1), wadfiles[j]);
					j++;
				}
				else
				{
					printf("\n");
					k=4;
					j=68+numfiles;
				}
			}
		}
		numfiles = j;
		line [0] = ' ';
		cprintf("\r\nEnter the NUMBERS of the files to Flag. (20 FILES MAX)\r\n"
				 "Hit Enter to quit flagging or view next page\r\n"
				 "Files [");
		printselected(7);
		while ( (numflagged < 20 ) && (line[0] != EOS) )
		{
			xpos=wherex();
			ypos=wherey();
			gets(line);
			gotoxy( (xpos+strlen(line)) , ypos);  //Repositions cursor after linefeed
			if ( line[0] != EOS)
			{
				printf(", ");
				sscanf (line, "%d", &j);
				j--;
				if ( (j >= 0) && (j <= numfiles) )   //checks for a valid number
				{
					strcpy(wadname[numflagged],wadfiles[j]);
					numflagged++;
				}
			}
			else
				printf("\n\n");
		}
		wadname[numflagged][0]=EOS;  //Just to make sure the last entry is a EOS
	}
}

int sort_function(const void *a, const void *b)
{
	return( strcmp((char *)a, (char *)b) );
}

void stats (void)
{
	time_t hrs;
	time_t mins;
	time_t secs;
	struct tm *timeptr;

	clrscr ();
	if (starttime != 0)
	{
		timeptr = localtime (&starttime);
		printf ("                 Start Time: %2d:%02d:%02d",
			timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);

		playtime = endtime - starttime;
		hrs = playtime / 3600;
		mins = (playtime - (hrs * 3600)) / 60;
		secs = playtime - (hrs * 3600) - (mins * 60);
		printf ("	  Playing Time: %2d:%02d:%02d\n\n",
			(int) hrs, (int) mins, (int) secs);
	}
	showReadStats();
	showWriteStats();
	showUartErrors();
}

int good_char ( int ch )
{
	if ( isprint(ch) || (ch == BACKSPACE) || (ch == '\n') )
		return TRUE;
	else
		return FALSE;
}

#define LOCAL_Y_MIN 3
#define LOCAL_Y_MAX 13
#define REMOTE_Y_MIN 15
#define REMOTE_Y_MAX 25

void split_talk (void)
{
	int i;
	int c;
	int local_xpos=1;
	int local_ypos= LOCAL_Y_MIN + 1;
	int remote_xpos=1;
	int remote_ypos= REMOTE_Y_MIN + 1;
	int pos;
	int start = 0x02;    // A little bit of noise trapping
	int good = FALSE;

	clrscr ();
	printf ("Talk mode...  Press ESC to return");
	gotoxy(1, LOCAL_Y_MIN - 1);
	printf ("(Local)");
	gotoxy(1, REMOTE_Y_MIN - 2);
	/* Two lines here...  80 '=' signs plus the title. */
	printf ("ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ(Remote)");
	gotoxy(local_xpos, local_ypos);
	while (TRUE)
	{
		/* Check for keypress */
		if (bioskey (1))
		{
			if ((c = bioskey (0) & 0xff) == ESC)
			{
				/* ESC key -- Exit talk mode -- Flush both sides */
				while (read_byte () != -1)
					;
				while (bioskey (1))
					bioskey (0);
				clrscr();
				return;
			}
			if (c == '\r')		/* Change cr to crlf */
				c = '\n';
			if (good_char(c))
			{
				write_buffer (&(char)start, 1);
				write_buffer (&(char)c, 1);
				if((pos = wherey()) >= REMOTE_Y_MIN && pos <= REMOTE_Y_MAX)
				{
					/* We're on the remote side.  Save pos. and move to local. */
					remote_xpos=wherex();
					remote_ypos=pos;
					gotoxy(local_xpos, local_ypos);
				}
				printf ("%c", c);
			/* If we're now in the 1st column and we didn't backspace here... */
				if (( wherex() == 1) && ( c != BACKSPACE ) )
				{
					/* ... then we're on a new line, so increment ypos. */
					local_ypos++;
					/* If we're at the bottom of the local section, move to the top. */
					if (wherey() == LOCAL_Y_MAX)
					{
						local_ypos = LOCAL_Y_MIN;
						gotoxy ( 1, local_ypos);
					}
					/* Clear this line, and the next if not max. */
					clreol();
					if ( (local_ypos + 1) != LOCAL_Y_MAX )
					{
						gotoxy(1, local_ypos+1);
						clreol();
						gotoxy(1, local_ypos);
					}
				}
			}
		}
		/* Check for received byte */
		c = read_byte();
		if( (c != -1) && (good == TRUE) )
		{
			good = FALSE;
			if (good_char(c))
			{
				if ((pos = wherey()) >= LOCAL_Y_MIN && pos <= LOCAL_Y_MAX)
				{
					/* We're on the local side.  Save pos and move to remote. */
					local_xpos=wherex();
					local_ypos=wherey();
					gotoxy(remote_xpos, remote_ypos);
				}
				printf ("%c", c);
			/* If we're now in the 1st column and we didn't backspace here... */
				if (( wherex() == 1) && ( c != BACKSPACE ) )
				{
					/* ... then we're on a new line, so increment ypos. */
					remote_ypos++;
			/* If we're at the bottom of the remote section, move to the top. */
					if (wherey () == REMOTE_Y_MAX)
					{
						remote_ypos = REMOTE_Y_MIN;
						gotoxy ( 1, remote_ypos);
					}
					/* Clear this line, and the next if not max. */
					clreol();
					if ( (remote_ypos + 1) != REMOTE_Y_MAX )
					{
						gotoxy(1, remote_ypos+1);
						clreol();
						gotoxy(1, remote_ypos);
					}
				}
			}
		}
		if (c == start)
			good = TRUE;
	}
}

void reset_counters (void)
{
	/* showReadStats() counters. */
	writeBufferOverruns       = 0;
	bytesRead                 = 0;
	packetsRead               = 0;
	largestReadPacket         = 0;
	smallestReadPacket        = 0xFFFFFFFFl;
	readBufferOverruns        = 0;
	totalReadPacketBytes      = 0;
	oversizeReadPackets       = 0;
	largestOversizeReadPacket = 0;
	overReadPacketLen         = 0;

	/* showWriteStats() counters. */
	bytesWritten               = 0;
	packetsWrite               = 0;
	largestWritePacket         = 0;
	smallestWritePacket        = 0xFFFFFFFFl;
	totalWritePacketBytes      = 0;
	oversizeWritePackets       = 0;
	largestOversizeWritePacket = 0;

	/* showUartErrors() counters. */
	numBreak          = 0;
	numFramingError   = 0;
	numParityError    = 0;
	numOverrunError   = 0;
	numTxInterrupts   = 0;
	numRxInterrupts   = 0;
}

void title (void)
{
	clrscr();
	gotoxy (1, 4);
	cprintf("ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¸\r\n");
	cprintf("º                         ³\r\n");
	cprintf("º                         ³\r\n");
	cprintf("º                         ÃÄÄÄÄÄÄÄÄÄÄÄÄ¿\r\n");
	cprintf("º                         ³            ³\r\n");
	cprintf("º                         ³            ³\r\n");
	cprintf("ÓÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ            ³\r\n");
	cprintf("                         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r\n");
	cprintf("                         ³           %4.4s            ³\r\n",PROG_NAME);
	cprintf("                         ³ DOOM SERIAL DEVICE DRIVER ³\r\n");
	cprintf("                         ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r\n");
	cprintf("                                       ³            ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·\r");
	cprintf("                                       ³            ³                          º\r");
	cprintf("                                       ³            ³                          º\r");
	cprintf("                                       ÀÄÄÄÄÄÄÄÄÄÄÄÄ´                          º\r");
	cprintf("                                                    ³                          º\r");
	cprintf("                                                    ³                          º\r");
	cprintf("                                                    ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼\r");
	window(3,5,25,10);
	textcolor(DARKGRAY);
	DOOM();
	gotoxy(55,16);
	window(55,16,77,21);
	textcolor(GREEN);
	DOOM();
	window(1,1,80,25);
	textcolor(LIGHTGRAY);
	gotoxy(1,25);
	pause ();
}

void DOOM (void)
{
	cprintf("ÜÜÜÜ   ÜÜÜ   ÜÜÜ  Ü   Ü\r");
	cprintf("Û   Û Û   Û Û   Û ÛÛÜÛÛ\r");
	cprintf("Û   Û Û   Û Û   Û Û ß Û\r");
	cprintf("Û   Û Û   Û Û   Û Û   Û\r");
	cprintf("ßßßß   ßßß   ßßß  ß   ß\r");
}

void shell (void)
{
	char comspec[30];
	char oldprompt[128];
	static char prompt[128];   //setenv requires a static or global
	char path[80];
	int  drive;

	cprintf("\n\nDos Shell......\n\n");
	drive=getdisk();
	strcpy(path, "X:\\");      /* fill string with form of response: X:\ */
	path[0] = 'A' + getdisk();    /* replace X with current drive letter */
	getcurdir(0, path+3);  /* fill rest of string with current directory */

	strcpy(oldprompt,getenv("PROMPT"));
	sprintf(prompt,"PROMPT=Type 'EXIT' to return to %s$_%.80s",
		PROG_NAME, oldprompt);
	putenv(prompt);
	strcpy(comspec,getenv("COMSPEC"));
	if (comspec != NULL)
		spawnv(P_WAIT,comspec,NULL);
	else		//Just incase someone looses the COMSPEC env variable
	{
		printf("\nEnvironment Variable COMSPEC was not found!\nAssuming Command.Com\n");
		spawnvp(P_WAIT,"COMMAND.COM",NULL);
	}
	sprintf(prompt,"PROMPT=%.100s",oldprompt);  //resets environment
	putenv(prompt);
	setdisk(drive);          //returns to the DOOM dir.
	chdir(path);
	clrscr();
}

int promptdropdtr( void )
{
	char ch;

	printf("\n\nExit and Hangup the modem (Y)\n"
				  "Exit and Leave DTR up     (X)\n"
				  "Go Back to Main Menu      (N):");
	while(1)
	{
		ch = getch();
		tolower(ch);
		switch(ch)
		{
			case 'y':
				nodropdtr = FALSE;
				printf("\n\n");
				return TRUE;
			case 'n':
				printf("\n\n");
				return FALSE;
			case 'x':
				nodropdtr = TRUE;
				printf("\n\n");
				return TRUE;
			default :
				break;
		}
	}
}

void pause (void)
{
	printf("Press any key to continue...");
	getch();
}

void usage (void)
{
	clrscr ();
	printf (
		"\n\n\n\n\n%s is run without arguments.  Just type \"%s\"\n\n",
		PROG_EXE, PROG_NAME);
	printf (
		"To change port-related settings, edit the %s file,\n", PROG_CONFIG);
	printf (
		"and to change game settings, use the CONFIGURE option while running.\n\n"
		"Yes, this means setup.exe won't work, but don't sweat the small stuff. :-)\n\n");
	pause ();
}

/*
=================
=
= main
=
=================
*/

void main(void)
{
	int ch;

	if (_argc > 1)
		usage ();

	title();

	/* Setup the struct that we'll use to communicate with doom (via interrupt)*/
	doomcom.extratics = 1;			/* Make "extratics" default, it runs better. */
	doomcom.id = DOOMCOM_ID;		/* Used to verify that doomcom struct is ok. */
	doomcom.ticdup = 1;
	doomcom.numnodes = 2;			/* Don't waste your time trying 3 or 4. */
	doomcom.numplayers = 2;			/* Don't waste your time trying 3 or 4. */
	doomcom.drone = 0;
	doomcom.consoleplayer = 0;		/* Default - may be overridden later. */

	get_config_settings ();			/* Read config file and get uart & vector. */
	InitPort ();						/* Initialize the port. */
	print_config_settings ();		/* Print out current settings. */

	while (read_byte() != -1)		/* Flush any old stuff from uart buffer */
		;

	/* Get connected to the other computer. */
	while (TRUE)
	{
		printf ("\nSelect: [D] Dial     [A] Answer     [C] Connected already     [Q] Quit\n"
					 "        [U] Use Alternate BAUD and INIT");
		ch = tolower (getch ());
		printf ("\n");
		if (ch == 'd')
		{
			if (Dial_Modem ())
				break;
		}
		else if (ch == 'a')
		{
			if (Answer ())
				break;
		}
		else if (ch == 'q')
			Error (NULL);
		else if (ch == 'u')
			alt_init ();
		else if (ch == 'c')
			break;
	}

	/* Main menu. */
	clrscr();
	while (TRUE)
	{
		cprintf (
			"                               Main Menu\r\n\n");
		cprintf (
			"   [D] Play Doom     [C] Configure   [T] Talk to Remote    [Q] Quit\r\n"
			"   [F] File Dir      [W] Wad Levels  [Z] Zmodem File Xfer\r\n"
			"   [S] Serial Stats                                        [!] DOS Shell\r\n");
		ch = tolower (getch ());
		printf ("\n");
		switch (ch)
		{
			case 'd' :
				if (Connect ())
				{
					reset_counters ();
					time (&starttime);
					if (doomver1_2 == 1)
						LaunchDOOM1_2 ();
					else
						LaunchDOOM ();
					time (&endtime);
				}
				break;
			case 's' :
				stats ();
				break;
			case 'c' :
				configure ();
				break;
			case 't' :
				split_talk ();
				break;
			case 'q' :
				if (promptdropdtr() == TRUE)
					Error (NULL);
				break;
			case '!' :
				shell ();
				break;
			case 'w' :
				wadleveldisplay ();
				clrscr();
				break;
			case 'z' :
				file_transfer();
				break;
			case 'f' :
				dir (DIR_TYPE_NORMAL);
				clrscr();
				break;
		}
	}
}
