/*
		===== IDBSP - id Software's BSP builder for DOOM ====
	DOS port (c) 1994 Ron Rossbach (ej070@cleveland.freenet.edu)

	This program is a mostly direct port of id's BSP compiler to MS-DOS.  In the
	words of John Carmack - "This code was written and extended ... so it's
	probably not the cleanest thing in the world."  Program was compiled with
	DJGPP (that's "DJ's port of GCC"), and is a 32-bit extended-DOS program.

	Inquiries, comments, etc. are always welcome at the above address.

	This program is provided with NO WARRANTY.

	Use and distribution of this code is permitted according to the terms in the
	accompanying IDBSP.DOC file.  WAD Editor authors are encouraged to bundle this
	utility with their distributions, either as an add-on or an incorporated
	feature.

	You may not use this code to develop modified levels for the shareware
	version of DOOM.

	DOOM and DOOM2 are registered trademarks of id Software, Inc.  This program
	is not a product of id Software, and id will not provide technical support for
	this program.


	Revision History:

	v1.0 (06/03/94)
		This version is faithful port of id's original source.

	v1.1 (08/15/94)
		This version contains the following fixes:
			1)	Removed alignment of THINGS on a 16x16 grid.

			2)	sector re-numbering and re-grouping is controlled via new command-line
					parameters(see discussion of the "-sectopt" parameter below)

			3) 	a new EMU387 is provided which actually works on machines without
					a math coprocessor

			4)	v1.0 would print a message saying it removed overlaid and
					zero-length LINEDEFS, but the LINEDEF was never actually removed.
					This is fixed.

			5)	v1.0 retained id's "slime column" bug (as seen on E1M1 in the
					original levels).  This has been fixed, thanks to an idea presented
					by Antony Suter	(antony@werple.apana.org.au).  A new command-line
					parameter	allows access to the original code, for purists. (see
					discussion of the "-radius" parameter below)

			6)	general clean-ups to the code.

		and the following new features:

			1)	Futher enhancements to the DWD format, to move closer to WIF; IDBSP
					now expects a "sectors:" section in each level in a DWD file.

			2)	Support for DOOM 2 (DOOM II, DOOM ][, DOOM HOE, or whatever)

			3)	Command-line parameters:
						-wad
								allows input directly from a WAD file, rather than a DWD file
								(DWD is the default without this parameter)  This parameter
								must immediately proceed the name of the WAD file on the
								command line.  For example:
									"idbsp -wad input.wad -radius=2.0 output.wad"

						-sectopt=[0, 1]
								allows selection of sector handling.  The choices are:
									0 = leave sectors alone.  This is the default.
									1 = handle sectors as in IDBSP v1.0 (remove duplicate sectors,
											do not allow "multi-extent" sectors, etc.)  Rising stairs
											must have sector tags which alternate as described in the
											Unofficial Doom Specs.

						-radius=x.xx
								allows override of the default radius used in the PointOnSide
								calculations.  id's original value of 2.0 causes the "slime
								column" bug found on E1M1.  Antony Suter's value of 1.4143
								(the default) fixes this bug in most cases, but causes
								mis-construction of some sectors in certain PWADs when used
								with -sectopt=1.  Experiment to find the value which best suits
								your needs.....but you may want to brush up on your trig
								first.

						-fullreject
								considers ALL sectors when building the REJECT resource.  The
								default skips sectors less than 64x64.

						-trace
								displays debugging information during processing
*/

#include "idbsp.h"

#define VERSION "1.1BETA"

WADFILE   wad_i;

boolean		wadin = false, fullreject = false, radius = false, duplines = false,
					trace = false;

float     pos_radius = 1.4143;

int       sectopt = 0;

/*
===============================================================================
=
= int main(int argc, char **argv)
=
===============================================================================
*/
int main(int argc, char **argv)
{
	char			szDWDName[81], szWADName[81] = { '\0' };
	FILE      *pDWD;
	boolean 	overwrite, firstfile = true;
	int       i;

	printf("**** IDBSP - id's node builder for DOOM, DOS port version %s ****"
				 "\n**** (c) 1994 Ron Rossbach (ej070@cleveland.freenet.edu)          ****"
				 "\n\nDOOM is a registered trademark of id Software, Inc."
				 "\nid will not provide technical support for this program."
				 "\nYou may not use this program to develop levels for the shareware"
				 "\nversion of DOOM."
				 "\nSee the accompanying README for terms of use, distribution, etc.",
				 VERSION);

	if (argc < 2 || argc > 9)
	{
		printf("\n\nUsage: \"idbsp [-fullreject | -radius=x.x | -sectopt=x "
					 "-duplines | -trace] [-wad] infile [outfile]\""
					 "\nDefault outfile is TMP.WAD if not specified");
		Error("\nExiting....");
	}

	for (i=1; i<argc; i++)
	{
		if (*argv[i] == '-')
		{
			if (!strcmp(argv[i],"-wad"))
				wadin = true;
			else
				if (!strcmp(argv[i],"-fullreject"))
					fullreject = true;
				else
					if (!strcmp(argv[i],"-duplines"))
						duplines = true;
					else
						if (!strcmp(argv[i],"-trace"))
							trace = true;
						else
							if (!strncmp(argv[i],"-radius",7))
							{
								if (sscanf(argv[i], "-radius=%f", &pos_radius) != 1)
									Error("\nNo value specified for radius argument\n");
								radius = true;
							}
							else
								if (!strncmp(argv[i],"-sectopt",8))
								{
									if (sscanf(argv[i], "-sectopt=%d", &sectopt) != 1)
										Error("\nNo value specified for sectopt argument\n");
									if (sectopt < 0 || sectopt > 1)
										Error("\nInvalid value for sectopt; must be 0 or 1\n");
								}
								else
									Error("\nUnrecognized argument %s",argv[i]);
		}
		else
		{
			if (firstfile)
			{
				firstfile = false;
				strcpy(szDWDName, argv[i]);
			}
			else
				strcpy(szWADName, argv[i]);
		}
	}

	if (!szWADName[0])
		strcpy(szWADName,"tmp.wad");

	if (!strcmp(szDWDName, szWADName))
		Error("\nCannot have same file as input and output\n");

	printf("\nDWD File: %s\nWAD File: %s\n", szDWDName, szWADName);

	if (trace)
	{
		printf("\n-wad: %d\n-fullreject: %d\n-duplines: %d\npos_radius = %f\n"
					 "-sectopt = %d\n",
					 wadin, fullreject, duplines, pos_radius, sectopt);
		printf("\n[RETURN] to continue.....");
		getchar();
		fflush(stdin);
	}

	switch (wadin)
	{
		case    true:

				if ((pDWD = fopen(szDWDName, "rb")) == NULL)
				{
					printf("\nCannot open input file %s", szDWDName);
					return 0;
				}
				break;

		case    false:

				if ((pDWD = fopen(szDWDName, "r")) == NULL)
				{
					printf("\nCannot open input file %s", szDWDName);
					return 0;
				}
				break;

		default:
				Error("Invalid wadin value; neither TRUE nor FALSE");
	}


	if ((wad_i.handle = fopen(szWADName, "rb+")) == NULL)
	{
		if ((wad_i.handle = fopen(szWADName, "wb")) == NULL)
			Error("\nCannot open output file %s",szWADName);
		overwrite = true;
	}
	else
	{
		printf("\n\nWAD file %s already exists.  Overwrite? (y/n): ", szWADName);
		if (toupper(getchar()) == 'Y')
		{
			overwrite = true;
			fclose(wad_i.handle);
			if ((wad_i.handle = fopen(szWADName, "wb")) == NULL)
				Error("\nCannot open output file %s", szWADName);
		}
		else
			overwrite = false;
	}

	wad_i.pathname = (char *)SafeMalloc(strlen(szWADName) + 1);

	if (wad_i.pathname == NULL)
	{
		printf("\nERROR: wad_i.pathname: malloc");
		fclose(pDWD);
		fclose(wad_i.handle);
		return 0;
	}

	strcpy(wad_i.pathname, szWADName);

	printf("\noverwrite = %d\n",overwrite);
	if (overwrite)
		initNew();
	else
		initFromFile();

	if (wadin)
		WADMain(pDWD);
	else
		DWDMain(pDWD);

	fclose(pDWD);
	fclose(wad_i.handle);

	return 0;
}


/*
===============================================================================
=
= void DWDMain(FILE *)
=
=       Builds PWAD from the specified DWD file
===============================================================================
*/
void DWDMain(FILE *pDWD)
{
	int		version, episode, level;
	long  size;
	char  tmp[81],tmpname[81],dirname[9],*hold;

	if (fscanf (pDWD, "WorldServer version %d\n", &version) != 1)
		Error("\nInvalid input file; DWD header incorrect or missing");

	printf ( "Loading version %d doom map\n", version);

	while (fgets(tmp, 81, pDWD) != NULL)
	{
		if (sscanf(tmp,"level:E%dM%d",&episode,&level) == 2 ||
				sscanf(tmp,"level:MAP%d",&level) == 1)
		{
				/* Handle Map Level */
			hold=(char*)strchr(tmp,':') + 1;
			hold[strlen(hold) - 1] = '\0';
			printf("\nBuilding Map Level %s",hold);
			addName(hold,0,0);
			printf("\nLoading DWD file.....");
			LoadDoomMap(pDWD);
			printf("\nBuilding BSP....");
			DrawMap();
			BuildBSP();
			printf("\nSaving WAD file.....");
			SaveDoomMap();
			SaveBlocks();
				/* Free memory */
			FreeGlobalStorage();
		}
				/* Handle other resources */
		if (sscanf(tmp,"%s :%d",dirname,&size) == 2)
		{
			strcpy(tmp,dirname);
			strcpy(tmpname,tmp);
			strcat(tmpname,".lmp");
			printf("\nAdding resource %s",tmpname);
			AddFromFile(tmp, size, tmpname);
		}
	}

	printf("\nWriting directory.....");
	writeDirectory();

	return;
}


/*
===============================================================================
=
= void WADMain(FILE *)
=
=       Builds PWAD from the specified WAD file
===============================================================================
*/
void WADMain(FILE *pDWD)
{
	wadinfo_t       header;
	lumpinfo_t      *lumps;
	int             i, episode, map;
	char            tmp[9];
	void            *p;

		/* Read in the WAD header */
	if (fread(&header, sizeof(header), 1, pDWD) != 1)
		Error("\nCould not read WAD header\n");

		/* Read in the "lumps" (WAD directory) */
	lumps = (lumpinfo_t *)SafeCalloc(header.numlumps, sizeof(lumpinfo_t));
	fseek(pDWD, header.infotableofs, SEEK_SET);
	if (fread(lumps, sizeof(lumpinfo_t), header.numlumps, pDWD) != header.numlumps)
		Error("\nCould not read WAD directory\n");

	/* Process the directory entries */
	i = 0;
	while (i<header.numlumps)
	{
		strncpy(tmp, lumps->name, 8);
		tmp[8] = '\0';
		if (sscanf(tmp, "E%dM%d", &episode, &map) == 2 ||
				sscanf(tmp, "MAP%d", &map) == 1)
		{
				/* Handle Map Levels */
			printf("\nBuilding Level %s\n", tmp);
			addName(tmp, 0, 0);
			LoadWADMap(pDWD, lumps);
			printf("\nBuilding BSP....");
			DrawMap();
			BuildBSP();
			printf("\nSaving WAD file.....");
			SaveDoomMap();
			SaveBlocks();
				/* Free storage */
			FreeGlobalStorage();
			lumps += 11;
			i += 11;
			continue;
		}

		if (*tmp)
		{
				/* Handle other resources */
			printf("\nHandling resource %s\n",tmp);
			p = (void *)SafeMalloc(lumps->size);
			fseek(pDWD, lumps->filepos, SEEK_SET);
			fread(p, lumps->size, 1, pDWD);
			addName(tmp, p, lumps->size);
			free(p);
			i += 1;
			lumps += 1;
			continue;
		}

	i += 1;
	lumps += 1;
	}

	printf("\nWriting directory.....");
	writeDirectory();

	return;
}

/*
===============================================================================
=
= void AddFromFile(char *resname, int size, char *fname)
=
=       Adds a PWAD resource from a .LMP file
===============================================================================
*/
void AddFromFile(char *resname, int size, char *fname)
{
	void    *p;
	FILE    *f;

	if (!size)
	{
		addName(resname, 0, 0);
		return;
	}

	p = (void *)SafeMalloc(size);
	if ((f=fopen(fname,"rb")) == NULL)
		Error("\nAddFromFile: could not open file %s\n", fname);

	fread(p, size, 1, f);
	addName(resname, p, size);
	fclose(f);
	free(p);

	return;
}

/*
===============================================================================
=
= void FreeGlobalStorage(void)
=
=       Frees allocated memory
===============================================================================
*/
void FreeGlobalStorage(void)
{
	free(sectorlist);
	free(sectorsavelist->data);
	free(sectorsavelist);
	free(linestore_i->data);
	free(thingstore_i->data);
	free(secstore_i->data);
	free(mapvertexstore_i->data);
	free(subsecstore_i->data);
	free(maplinestore_i->data);
	free(nodestore_i->data);
	free(mapthingstore_i->data);
	free(ldefstore_i->data);
	free(sdefstore_i->data);
	free(linestore_i);
	free(thingstore_i);
	free(secstore_i);
	free(mapvertexstore_i);
	free(subsecstore_i);
	free(maplinestore_i);
	free(nodestore_i);
	free(mapthingstore_i);
	free(ldefstore_i);
	free(sdefstore_i);
}

/*
===============================================================================
=
= void Error (char *error, ...)
=
=       Displays message and exits program
===============================================================================
*/
void Error (char *error, ...)
{
	va_list         argptr;

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

/*
===============================================================================
=
= void *SafeMalloc(unsigned size)
=
=       Allocates memory of specified size, aborts program on error
===============================================================================
*/
void *SafeMalloc(unsigned size)
{
	void    *ret = (void *)malloc(size);

	if (!ret)
		Error("\nSafeMalloc: Failed to allocate %u bytes",size);

	return ret;
}

/*
===============================================================================
=
= void *SafeCalloc(unsigned num, unsigned size)
=
=       Allocates memory of specified size, aborts program on error
===============================================================================
*/
void *SafeCalloc(unsigned num, unsigned size)
{
	void *ret = (void *)calloc(num,size);

	if (!ret)
		Error("\nSafeCalloc: Failed to allocate %u of %u bytes",num,size);

	return ret;
}

/*
===============================================================================
=
= void *SafeRealloc(void *p, unsigned size)
=
=       Resizes previously allocated chunk of memory to specified size,
=				aborts on error
===============================================================================
*/
void *SafeRealloc(void *p, unsigned size)
{
	void *ret = (void *)realloc(p, size);

	if (!ret)
		Error("\nSafeRealloc: Failed to resize %p to size %u", p, size);

	return ret;
}

/*
	The following functions are from id's original source.  They perform any
	byte-swapping needed for the target architecture.  For example, if id ran
	the bsp builder under NextStep on some RISC box, but wanted to build a WAD
	targeted for Intel x86 machines....

	This obviously isn't needed for our use, since we stay within a single
	architecture.  I'm leaving the code commented out, rather than deleting it,
	to preserve for posterity.  :-)
*/
#if 0
#ifdef __BIG_ENDIAN__
short   LittleShort (short l)
{
				byte    b1,b2;
				b1 = l&255;
				b2 = (l>>8)&255;
				return (b1<<8) + b2;
}

short   BigShort (short l)
{
				return l;
}

long    LittleLong (long l)
{
				byte    b1,b2,b3,b4;
				b1 = l&255;
				b2 = (l>>8)&255;
				b3 = (l>>16)&255;
				b4 = (l>>24)&255;
				return ((long)b1<<24) + ((long)b2<<16) + ((long)b3<<8) + b4;
}

long    BigLong (long l)
{
				return l;
}

#else

short   BigShort (short l)
{
        byte    b1,b2;
        b1 = l&255;
        b2 = (l>>8)&255;
        return (b1<<8) + b2;
}

short   LittleShort (short l)
{
        return l;
}

long    BigLong (long l)
{
        byte    b1,b2,b3,b4;
        b1 = l&255;
        b2 = (l>>8)&255;
        b3 = (l>>16)&255;
        b4 = (l>>24)&255;
        return ((long)b1<<24) + ((long)b2<<16) + ((long)b3<<8) + b4;
}

long    LittleLong (long l)
{
        return l;
}

#endif
#endif

