// ipxsetup.c

#define DOOM2

#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <string.h>
#include <process.h>
#include <stdarg.h>
#include <bios.h>
#include <time.h>

#include "ipxnet.h"
#include "ipxstr.h"
//#include "ipx_frch.h"	// FRENCH VERSION
#include "ipxsetup.h"

int gameid;
int numnetnodes;
int socketid = 0x869c;        // 0x869c is the official DOOM socket
int myargc;
char **myargv;

setupdata_t    nodesetup[MAXNETNODES];

//my global
short master = 0;
short slave = 0;

char masterArgs[255];
/*
=================
=
= Error
=
= For abnormal program terminations
=
=================
*/

void Error (char *error, ...)
	{
	va_list argptr;

	if (vectorishooked)
		_dos_setvect (doomcom.intnum,olddoomvect);

	va_start (argptr,error);
	vprintf (error,argptr);
	va_end (argptr);
	printf ("\n");
	ShutdownNetwork ();
	exit (1);
	}


/*
=================
=
= CheckParm
=
= Checks for the given parameter in the program's command line arguments
=
= Returns the argument number (1 to argc-1) or 0 if not present
=
=================
*/

int CheckParm(char *parm, int argc, char *argv[])
	{
	int i;

	for(i = 1; i < argc; i++)
		if(stricmp(parm, argv[i]) == 0)
			return i;

	return 0;
	}

/*
=============
=
= NetISR
=
=============
*/

void interrupt NetISR (void)
	{
	if (doomcom.command == CMD_SEND)
		{
		local_time++;
		SendPacket (doomcom.remotenode);
		}
	else if (doomcom.command == CMD_GET)
		{
		GetPacket ();
		}
	}

/*
===================
=
= LookForNodes
=
= Finds all the nodes for the game and works out player numbers among 
them
=
= Exits with nodesetup[0..numnodes] and nodeadr[0..numnodes] filled in
===================
*/

void LookForNodes (void)
	{
	int             i,j,k;
	int             netids[MAXNETNODES];
	int             netplayer[MAXNETNODES];
#ifndef _MSC_VER 
	struct time		time;
#else
	struct tm       *timer;
	time_t	     	now;
#endif
	int             oldsec;
	setupdata_t     *setup, *dest;
	char            str[80];
	int             total, console;
	short			masters = 0; //# of nodes wanting to be master

#ifdef DEBUG
	fprintf(stderr, "LookForNodes() - called\n");
#endif
//
// wait until we get [numnetnodes] packets, then start playing
// the playernumbers are assigned by netid
//
	if (slave)
		printf(STR_WAITMASTER); 
	else
		printf(STR_ATTEMPT, numnetnodes);

	printf (STR_LOOKING);

	oldsec = -1;
	setup = (setupdata_t *)&doomcom.data;
	local_time = -1;          // in setup time, not game time

//
// build local setup info
//
	nodesetup[0].nodesfound = 1;
	nodesetup[0].nodeswanted = numnetnodes;

//if console is Master then copy argumnets to pass to all nodes
	if (nodesetup[0].master = master)
		strcpy(nodesetup[0].args, masterArgs);

	doomcom.numnodes = 1;

	do
		{
//
// check for aborting
//
#ifndef _MSC_VER 	
		while ( bioskey(0) )
			{
			if ( (bioskey (1) & 0xff) == 27)
				Error ("\n\n"STR_NETABORT);
			}
#else
		while ( _bios_keybrd(_KEYBRD_READY) )
			{
			if ( (_bios_keybrd (_KEYBRD_READ) & 0xff) == 27)
				Error ("\n\n"STR_NETABORT);
			}
#endif

//
// listen to the network
//
		while (GetPacket ())
			{
#ifdef DEBUG
	fprintf(stderr, "GetPacket() returned TRUE\n");
#endif
			if (doomcom.remotenode == -1)
				dest = &nodesetup[doomcom.numnodes];
			else
				dest = &nodesetup[doomcom.remotenode];

			if (remote_time != -1)
				{    // an early game packet, not a setup packet
				if (doomcom.remotenode == -1)
			 		Error (STR_UNKNOWN);
				// if it allready started, it must have found all nodes
				dest->nodesfound = dest->nodeswanted;
				continue;
				}

	       // update setup ingo
			memcpy (dest, setup, sizeof(*dest) );

			if (doomcom.remotenode != -1)
				{
#ifdef DEBUG
	fprintf(stderr, "\tAlready know that node address\n");
#endif
				continue;           // allready know that node address
				}

	       //
	       // this is a new node
	       //
			memcpy (&nodeadr[doomcom.numnodes], &remoteadr, sizeof(nodeadr[doomcom.numnodes]) );

               //
               // if this node has a lower address, take all startup info
               //
			if (memcmp(&remoteadr, &nodeadr[0], sizeof(&remoteadr)) < 0 )
				{
				}

			doomcom.numnodes++;

			printf ("\n"STR_FOUND"\n");

			if (dest->master)
				{
				numnetnodes = nodesetup[0].nodeswanted = dest->nodeswanted;
				printf(STR_GOTMASTER"\n");
				printf(STR_ATTEMPT2"\n", numnetnodes);
				}

			if (doomcom.numnodes < numnetnodes)
				printf (STR_LOOKING);
			}
//
// we are done if all nodes have found all other nodes
//
		for (i=0 ; i<doomcom.numnodes ; i++)
			if (nodesetup[i].nodesfound != nodesetup[i].nodeswanted)
				break;

		if (i == nodesetup[0].nodeswanted)
			break;         // got them all

//
// send out a broadcast packet every second
//
#ifndef _MSC_VER 
		  gettime (&time);
		  if (time.ti_sec == oldsec)
			   continue;
		  oldsec = time.ti_sec;
#else
		time(&now);
		timer = localtime(&now);
		if (timer->tm_sec == oldsec)
			continue;
		oldsec = timer->tm_sec;
#endif

		printf (".");
		doomcom.datalength = sizeof(*setup);

		nodesetup[0].nodesfound = doomcom.numnodes;

		memcpy (&doomcom.data, &nodesetup[0], sizeof(*setup));

		SendPacket (MAXNETNODES);     // send to all

		} 
	while (1);

//
// count players
//
	total = 0;
	console = 0;

	for (i=0 ; i<numnetnodes ; i++)
		{
		if (nodesetup[i].drone)
			continue;
		total++;
		if (total > MAXPLAYERS)
			Error (STR_MORETHAN,MAXPLAYERS);
		if (memcmp (&nodeadr[i], &nodeadr[0], sizeof(nodeadr[0])) < 0)
			console++;

//if a Master is found copy the arguments passed to pass along to DOOM
		if (nodesetup[i].master)
			{
			strcpy(masterArgs, nodesetup[i].args);
			++masters; //count number of consoles wanting to be Master
			slave = 1; //this node is a Slave -- even if it is a Master
			}
		}

	if (!total)
		Error (STR_NONESPEC);

	if (masters > 1) //if more than 1 (one) Master then quit 
		Error ("\n"STR_MASTERS"\n");

	if (slave & !masters) //if no Master is found then quit
		Error ("\n"STR_NOMASTER"\n");

	doomcom.consoleplayer = console;
	doomcom.numplayers = total;

	printf (STR_CONSOLEIS"\n", console+1, total);

#ifdef DEBUG
	fprintf(stderr, "LookForNodes() - returning\n");
#endif
	}


//========================================================
//
//	Find a Response File
//
//========================================================
void FindResponseFile (void)
	{
	int		i;
	#define	MAXARGVS	100

	for (i = 1;i < myargc;i++)
		if (myargv[i][0] == '@')
			{
			FILE *		handle;
			int		size;
			int		k;
			int		index;
			int		indexinfile;
			char	*infile;
			char	*file;
			char	*moreargs[20];
			char	*firstargv;

			// READ THE RESPONSE FILE INTO MEMORY
			handle = fopen (&myargv[i][1],"rb");
			if (!handle)
				Error (STR_NORESP);
			printf(STR_FOUNDRESP" \"%s\"!\n",strupr(&myargv[i][1]));
			fseek (handle,0,SEEK_END);
			size = ftell(handle);
			fseek (handle,0,SEEK_SET);
			file = malloc (size);
			fread (file,size,1,handle);
			fclose (handle);

			// KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG
			for (index = 0,k = i+1; k < myargc; k++)
				moreargs[index++] = myargv[k];

			firstargv = myargv[0];
			myargv = malloc(sizeof(char *)*MAXARGVS);
			memset(myargv,0,sizeof(char *)*MAXARGVS);
			myargv[0] = firstargv;

			infile = file;
			indexinfile = k = 0;
			indexinfile++;	// SKIP PAST ARGV[0] (KEEP IT)
			do
				{
				myargv[indexinfile++] = infile+k;

				if (stricmp(myargv[indexinfile-2], PARM_MSG) != 0)
					{
					// Treat rest of line as message
					while(k < size && ((*(infile+k)>= ' '+1) && (*(infile+k)<='z')))
						k++;
					}
				else
					{
					while(k < size && (*(infile+k) != '\n'))
						k++;
					}
				*(infile+k) = 0;
				while(k < size && ((*(infile+k)<= ' ') || (*(infile+k)>'z')))
					k++;

				}
			while(k < size);

			for (k = 0;k < index;k++)
				myargv[indexinfile++] = moreargs[k];
			myargc = indexinfile;

			// DISPLAY ARGS
/*
			printf("%d command-line args:\n",myargc);
			for (k=1;k<myargc;k++)
				printf("%s\n",myargv[k]);
*/
			break;
		}
	}

void ShiftArgsRight(int i)
	{
	int j;

	myargv[myargc+1] = 0;
	for (j = myargc++; j > i; --j)
		myargv[j] = myargv[j-1];	
	myargv[i+1] = &myargv[i][2];
	}

void ConvertParms(void)
	{
	int i;

	for (i = 1; i < myargc; ++i)
		{
		if (strcmp(myargv[i], "-m") == 0) 		// -master
			myargv[i] = PARM_MASTER;
#ifdef SLAVE
		else if (strcmp(myargv[i], "-e") == 0)	// -slave
			myargv[i] = PARM_SLAVE;
#endif
		else if (strcmp(myargv[i], "-b") == 0)	// -msg
			myargv[i] = PARM_MSG;
		else if (strcmp(myargv[i], "-h") == 0)	// -dehack
			myargv[i] = PARM_DEHACK;
		else if ((strncmp(myargv[i], "-h", 2) == 0) && (stricmp(myargv[i], PARM_DEHACK) != 0))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_DEHACK;
			}
		else if (strcmp(myargv[i], "-f") == 0)	// -file
			myargv[i] = PARM_FILE;
		else if ((strncmp(myargv[i], "-f", 2) == 0) && (stricmp(myargv[i], PARM_FILE) != 0))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_FILE;
			}
		else if (strcmp(myargv[i], "-n") == 0)	// -nodes
			myargv[i] = PARM_NODES;
		else if ((strncmp(myargv[i], "-n", 2) == 0) && (isdigit(myargv[i][2])))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_NODES;
			}
		else if (strcmp(myargv[i], "-p") == 0)	// -port
			myargv[i] = PARM_PORT;
		else if ((strncmp(myargv[i], "-p", 2) == 0) && (isdigit(myargv[i][2])))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_PORT;
			}
		else if (strcmp(myargv[i], "-v") == 0)	// -vector
			myargv[i] = PARM_VECTOR;
		else if ((strncmp(myargv[i], "-v", 2) == 0) && (isdigit(myargv[i][2])))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_VECTOR;
			}
		else if (strcmp(myargv[i], "-l") == 0)	// -loadgame
			myargv[i] = PARM_LOADGAME;
		else if ((strncmp(myargv[i], "-l", 2) == 0) && (stricmp(myargv[i], PARM_LOADGAME) != 0))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_LOADGAME;
			}
		else if (strcmp(myargv[i], "-d") == 0)	// -deathmatch
			myargv[i] = PARM_DEATHMATCH;
		else if (strcmp(myargv[i], "-a") == 0)	// -altdeath
			myargv[i] = PARM_ALTDEATH;
		else if (strcmp(myargv[i], "-s") == 0)	// -skill
			myargv[i] = PARM_SKILL;
		else if ((strncmp(myargv[i], "-s", 2) == 0) && (isdigit(myargv[i][2])))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_SKILL;
			}
		else if (strcmp(myargv[i], "-c") == 0)	// -config
			myargv[i] = PARM_CONFIG;
		else if ((strncmp(myargv[i], "-c", 2) == 0) && (stricmp(myargv[i], PARM_CONFIG) != 0))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_CONFIG;
			}
		else if (strcmp(myargv[i], "-x") == 0)	// -nomonsters
			myargv[i] = PARM_NOMONSTERS;
		else if (strcmp(myargv[i], "-r") == 0)	// -respawn
			myargv[i] = PARM_RESPAWN;
		else if (strcmp(myargv[i], "-T") == 0)	// -turbo
			myargv[i] = PARM_TURBO;
		else if ((strncmp(myargv[i], "-T", 2) == 0) && (isdigit(myargv[i][2])))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_TURBO;
			}
		else if (strcmp(myargv[i], "-F") == 0)	// -fast
			myargv[i] = PARM_FAST;
		else if (strcmp(myargv[i], "-t") == 0)	// -timer
			myargv[i] = PARM_TIMER;
		else if ((strncmp(myargv[i], "-t", 2) == 0) && (isdigit(myargv[i][2])))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_TIMER;
			}
		else if (strcmp(myargv[i], "-w") == 0)	// -warp
			myargv[i] = PARM_WARP;
		else if ((strncmp(myargv[i], "-w", 2) == 0) && (isdigit(myargv[i][2])))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_WARP;
			}
		else if (strcmp(myargv[i], "-R") == 0)	// -record
			myargv[i] = PARM_RECORD;
		else if ((strncmp(myargv[i], "-R", 2) == 0) && (stricmp(myargv[i], PARM_RECORD) != 0))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_RECORD;
			}
		else if (strcmp(myargv[i], "-P") == 0)	// -playdemo
			myargv[i] = PARM_PLAYDEMO;
		else if ((strncmp(myargv[i], "-P", 2) == 0) && (stricmp(myargv[i], PARM_PLAYDEMO) != 0))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_PLAYDEMO;
			}
		else if (strcmp(myargv[i], "-X") == 0)	// -maxdemo
			myargv[i] = PARM_MAXDEMO;
		else if ((strncmp(myargv[i], "-X", 2) == 0) && (isdigit(myargv[i][2])))
			{
			ShiftArgsRight(i);
			myargv[i++] = PARM_MAXDEMO;
			}
		else if (strcmp(myargv[i], "-J") == 0)	// -nojoy
			myargv[i] = PARM_NOJOY;
		else if (strcmp(myargv[i], "-M") == 0)	// -nomouse
			myargv[i] = PARM_NOMOUSE;
		else if (strcmp(myargv[i], "-S") == 0)	// -nosound
			myargv[i] = PARM_NOSOUND;
		else if (strcmp(myargv[i], "-U") == 0)	// -nomusic
			myargv[i] = PARM_NOMUSIC;
		else if (strcmp(myargv[i], "-Y") == 0)	// -nosfx
			myargv[i] = PARM_NOSFX;
		}
/*
	printf("%d command-line args:\n",myargc);
		for (i=1;i<myargc;i++)
			printf("%s\n",myargv[i]);
*/
	}
/*
=============
=
= main
=
=============
*/

#ifndef _MSC_VER
void main (void)
#else
//MSC does nat have global command line arguments vaiables

int newargc;
char *newargv[99];
char newargs[255];

void main (int argc, char *argv[])
#endif
	{
	unsigned char far *vector;
	int i;

#ifdef _MSC_VER
//less recoding this way
#endif

	master = 0;
	slave = 0;
//
// determine game parameters
//
	gameid = 0;
	numnetnodes = 2;
	doomcom.ticdup = 1;
	doomcom.extratics = 1;
	doomcom.episode = 1;
	doomcom.map = 1;
	doomcom.skill = 2;
	doomcom.deathmatch = 0;

	printf("\n"
		"----------------------------------\n"
	#ifdef DOOM2
		STR_DOOMNETDRV"\n"
	#else
		"DOOM NETWORK DEVICE DRIVER\n"
	#endif
		"v1.22 (Master/Slave by Micah Nutt)\n"
		"----------------------------------\n");

	myargc = argc;
	myargv = argv;

	FindResponseFile();
	
	ConvertParms();
	
// Check if this node is the Master
// and if so copy the command line arguments to pass to the other nodes
	if((i = CheckParm(PARM_MASTER, myargc, myargv)) != 0)
		{
		int nArg;
		char *pArgs;

		master = 1; //set Master flag

		nodesetup[0].args[0] = 0;
		pArgs = masterArgs;
		for (nArg = 1; nArg < myargc; ++nArg)
			{
			sprintf(pArgs, "%s ", myargv[nArg]);
			pArgs += strlen(myargv[nArg])+1;
			if (stricmp(myargv[nArg], PARM_MSG) == 0)
				if (nArg+1 == myargc)
					Error(STR_NOMSG);
				else
					{
					sprintf(pArgs, "\"%s\" ", myargv[++nArg]);
					pArgs += strlen(myargv[nArg])+3;
					}
			}
		*pArgs = 0; //NULL terminator
		}
#ifndef SLAVE
	else
		{
		slave = 1;
		numnetnodes = MAXPLAYERS+1;
		}

	if((i = CheckParm(PARM_NODES, myargc, myargv)) != 0)
		numnetnodes = atoi(myargv[i+1]);
#else
	if ((i = CheckParm(PARM_SLAVE, myargc, myargv)) != 0)
		{
		if (master)
			Error ("\n"STR_CONFLICT"\n");
		slave = 1;
		// Master will determine how many nodes
		numnetnodes = MAXPLAYERS+1;
		}	
	else if((i = CheckParm(PARM_NODES, myargc, myargv)) != 0)
		numnetnodes = atoi(myargv[i+1]);
#endif

	if((i = CheckParm(PARM_VECTOR, myargc, myargv)) != 0)
		{
		//doomcom.intnum = sscanf ("0x%x",myargv[i+1]);
		sscanf (myargv[i+1], "0x%x", &doomcom.intnum);
		vector = *(char far * far *)(doomcom.intnum*4);
		if(vector != NULL && *vector != 0xcf)
		//if(vector == NULL || *vector != 0xcf)
			{			  
			printf(STR_VECTSPEC"\n", doomcom.intnum);
			exit(-1);
			}
		}
	else
		{
		for(doomcom.intnum = 0x60 ; doomcom.intnum <= 0x66 ; doomcom.intnum++)
			{
			vector = *(char far * far *)(doomcom.intnum*4);
			if(vector == NULL || *vector == 0xcf)
				break;
			}
		if(doomcom.intnum == 0x67)
			{
			printf(STR_NONULL"\n");
			exit(-1);
			}
		}
	printf(STR_COMMVECT"\n",doomcom.intnum);

	if((i = CheckParm(PARM_PORT, myargc, myargv)) != 0)
		{
		socketid = atoi (myargv[i+1]);
		printf (STR_USEALT"\n", socketid);
		}

	InitNetwork ();

	LookForNodes ();

	local_time = 0;

	LaunchDOOM ();

	ShutdownNetwork ();

	if (vectorishooked)
		_dos_setvect (doomcom.intnum,olddoomvect);

	exit(0);
	}
