/*
	DOOMODEM
	Configuration utility for DOOM 1.2 modem-vs-modem play
	v1.0á  2/18/94

	Changes the default port and IRQ settings of the SERSETUP.EXE program!

	USE AT YOUR OWN RISK and back up your original SERSETUP.EXE first.
	I assume no responsibility for anything this program directly or
	indirectly does.

	JoSHua Lehan
	jlehan@galaxy.csc.calpoly.edu
*/

/*
	Borland C++ 3.1
	Set tab stops to 3 spaces to properly indent code
*/

/*
	written from 1:30 AM to 6:30 AM so there may be bugs...

	I have an 8250 UART, hopefully DOOM does not change the port
	address and IRQ's when referring to different types of UART's
	or any other system changes.  Here's the info, so if this program
	does not work for some reason, you can find what went wrong and
	fix it...  email me (jlehan@galaxy.csc.calpoly.edu) if you can
	improve this program!

	At offset 18378 (47CAh) in the SERSETUP.EXE program, the default
	port addresses and IRQ's are stored as 16-bit words (LSB first).
	This is the order they appear in:

				 1Port  2Port  3Port  4Port  1IRQ   2IRQ   3IRQ   4IRQ
	Offset    47CAh  47CCh  47CEh  47D0h  47D2h  47D4h  47D6h  47D8h
	Defaults  F8 03  F8 02  E8 03  E8 02  04 00  03 00  04 00  03 00

	All this program does is change these values, saving the user the
	trouble of using a disk editor.

	Why iD did not include a way to change these from within the
	DOOM setup program, I do not know.  The code to allow temporary
	changes on the command line seems to exist within SERSETUP.EXE
	(undocumented -irq and -port options), but I was completely unable to
	get these to work.  After downloading DOOM 1.2, I was really
	looking forward to modem-vs-modem "DOOMwars".  Imagine my frustration
	when the game started to use the wrong configuration and there was
	seemingly nothing I could do about it.  After staying up all night
	coding and hacking, I was playing ModemDOOM!  And by using this
	program, so can you...  :-)
*/

#include <stdio.h>

#define DRIVERNAME "SERSETUP.EXE"
#define DRIVERSIZE 36567l
#define HACKOFFSET 18378l

#undef DEBUG
/* #define DEBUG */

#ifdef DEBUG
	#define dprintf(x) printf("==-- %s --==\n",x)
#else
	#define dprintf(x) ;
#endif

unsigned Com, Port, Irq;
unsigned Settings[8];
FILE *Driver;

void PrintTitle(void)
{
	printf("\nDOOMODEM                     JoSHua Lehan\n");
	printf("v1.0á   2/18/94              jlehan@galaxy.csc.calpoly.edu\n\n");
}

void PrintHelp(void)
{
	printf("Changes the default port and IRQ settings of the SERSETUP.EXE program!\n\n");
	printf("  Usage:  DOOMODEM com port irq      All numbers must be _IN HEX_!!\n");
	printf("Example:  DOOMODEM 3 3e8 5\n          Sets -com3 to port address 3E8h, IRQ 5h\n\n");
	printf("This program is PUBLIC DOMAIN.  Use and distribute freely!\n");
}

void HackSettings(void)
{
	Settings[Com - 1] = Port;
	Settings[Com + 3] = Irq;
}

int Hexify(unsigned *Value, char Str[])
{
	char *ParsePtr = Str;
	char Nybble;
	unsigned Num = 0;

	while (*ParsePtr != 0)
	{
		dprintf(ParsePtr);
		Nybble = *ParsePtr;
		if (Nybble >= 'a' && Nybble <= 'f')
			Nybble -= 32;
		if (Nybble < '0' || Nybble > '9')
			if (Nybble < 'A' || Nybble > 'F')
			{
				printf("Character '%c' is not a valid hex digit!\n", Nybble);
				return 1;
			}
			else
				Nybble -= 55;
		else
			Nybble -= 48;

		Num <<= 4;
		Num += Nybble;
		ParsePtr ++;
	}

	*Value = Num;
	return 0;
}

int CheckBounds(void)
{
	if (Com < 1 || Com > 4)
	{
		printf("COM ports only range from 1 to 4!  \"%X\" won't work.\n\n", Com);
		return 1;
	}
	if (Port != 0x3F8 && Port != 0x2F8 && Port != 0x3E8 && Port != 0x2E8)
	{
		printf("WARNING:  \"%X\" is a nonstandard port address!!\n", Port);
		printf("It might still work, but is not recommended...\n\n");
	}
	if (Irq != 3 && Irq != 4 && Irq != 5 && Irq != 7)
	{
		printf("WARNING:  \"%X\" is a nonstandard IRQ level!!\n", Irq);
		printf("DOOM will probably not like it...\n\n");
	}
	return 0;
}
int OpenDriver(void)
{
	long Size;

	Driver = fopen(DRIVERNAME, "r+b");
	if (Driver == NULL)
	{
		printf("Can't open file %s!\n", DRIVERNAME);
		printf("Make sure this program is ran in the \DOOM direcory!!\n\n");
		return 1;
	}
	else
	{
		fseek(Driver, 0, SEEK_END);
		Size = ftell(Driver);
		if (Size != DRIVERSIZE)
		{
			printf("This file %s isn't the DOOM 1.2 serial port driver!!\n", DRIVERNAME);
			printf("Was looking for file size %ld bytes, instead found %ld bytes!\n\n", DRIVERSIZE, Size);
			return 2;
		}
		else
			dprintf("File opened successfully!");
	}

	return 0;
}

void CloseDriver(void)
{
	fclose(Driver);
}

int ReadSettings(void)
{
	int Loop;

	fseek(Driver, HACKOFFSET, SEEK_SET);
	for (Loop = 0; Loop < 8; Loop ++)
	{
		if (!fread(&Settings[Loop], 2, 1, Driver))
		{
			printf("ERROR trying to read the file!!!\n");
			return 1;
		}
	}

	dprintf("Data read correctly!");
	return 0;
}

int WriteSettings(void)
{
	int Loop;

	fseek(Driver, HACKOFFSET, SEEK_SET);
	for (Loop = 0; Loop < 8; Loop ++)
	{
		if (!fwrite(&Settings[Loop], 2, 1, Driver))
		{
			printf("ERROR trying to write out the new settings!!!\n");
			return 1;
		}
	}

	dprintf("Data written correctly!");
	return 0;
}

void PrintSettings(void)
{
	int Loop;

	for(Loop = 0; Loop < 4; Loop ++)
		printf("COM%X:  port address %Xh, IRQ %Xh\n", Loop + 1, Settings[Loop], Settings[Loop + 4]);
}

int main(int argc, char *argv[])
{
	int Success = 0;

	PrintTitle();
	if (argc < 2)
	{
		PrintHelp();
		return 0;
	}
	else
		if (OpenDriver() || ReadSettings())
		{
			printf("PROGRAM ABORTED!  No changes to the file were made.\n");
			return 1;
		}

	if (argc == 4)
	{
		dprintf("Parse settings here:");
		if (Hexify(&Com, argv[1]) || Hexify(&Port, argv[2]) || Hexify(&Irq, argv[3]))
		{
			printf("VALUES NOT UNDERSTOOD!  Were they typed IN HEX?\n\n");
			printf("The old settings have not been changed:\n");
		}
		else
		{
			if (CheckBounds())
				printf("VALUES OUT OF RANGE!  Settings not changed:\n");
			else
			{
				HackSettings();
				if (WriteSettings() || ReadSettings())
					printf("UNABLE TO CHANGE THE FILE!  Here's the old settings:\n");
				else
				{
					printf("Settings of %s have been changed to:\n", DRIVERNAME);
					Success = 1;
				}
			}
		}
	}
	else
	{
		printf("Command line doesn't have 3 arguments so settings not changed.\n\n");
		printf("Current settings of %s are:\n", DRIVERNAME);
	}

	PrintSettings();
	CloseDriver();

	if (Success)
		printf("\nPROGRAM COMPLETE!  You are DOOMed!!!\n");
	else
		return 2;

	return 0;
}