/*
	DooM/HereTic/HeXen/StriFe INFO.c processors, by Frans P. de Vries.

Derived from:

	DooM MaP StaTistics, by Frans P. de Vries.

	You are allowed to use any parts of this code in another program, as
	long as you give credits to the authors in the documentation and in
	the program itself.  Read the file README for more information.

	This program comes with absolutely no warranty.

	WADS.C - WAD files routines.
*/

#include <ctype.h>
#include "wads.h"


/*
	the global variables
*/
WadPtr WadFileList = NULL; /* linked list of Wad files */

/*
	the local function prototypes
*/
WadPtr BasicWadOpen( char *);
Bool IsLevelLump( char *);
void ListMasterDirectory( WadPtr);


/*
	swap routines to handle big/little endian machines
*/
#ifdef DM_BIG_ENDIAN
/* big endian machines (eg. Sun, SGI) need to swap bytes within int's/long's */

void swapint( BCINT *i)
{
	BCINT t;

	((char *) &t)[0] = ((char *) i)[1];
	((char *) &t)[1] = ((char *) i)[0];
	*i = t;
}

void swaplong( BCLNG *l)
{
	BCLNG t;

	((char *) &t)[0] = ((char *) l)[3];
	((char *) &t)[1] = ((char *) l)[2];
	((char *) &t)[2] = ((char *) l)[1];
	((char *) &t)[3] = ((char *) l)[0];
	*l = t;
}

#else
/* little endian machines (eg. IBM PC) need to do nothing */
#endif


/*
	open the IWAD file and read in its master directory
*/
void OpenIWad( char *filename)
{
	MDirPtr lastp, newp;
	WadPtr wad;
	BCLNG n;

	/* open the IWAD file */
	printf( "Loading IWAD file: %s...\n", filename);
	wad = BasicWadOpen( filename);
	if (strncmp( wad->type, "IWAD", 4) != 0)
		ProgError( "\"%s\" is not an IWAD file", filename);

	/* create the master directory */
	lastp = NULL;
	for (n = 0; n < wad->dirsize; n++)
	{
		newp = (MDirPtr) GetMemory( sizeof(struct MasterDirectory));
		newp->next = NULL;
		memcpy( &(newp->dir), &(wad->directory[n]), sizeof(struct Directory));
		if (wad->masterdir)
			lastp->next = newp;
		else
			wad->masterdir = newp;
		lastp = newp;
	}

	/* check for expansion/ultimate/commercial/registered version */
	/* if you change this, bad things will happen to you... */
	if (FindMasterDir( wad->masterdir, "MAP58") != NULL) // Hexen DDC
		wad->gamevers = 0x08;
	else if (FindMasterDir( wad->masterdir, "DENDBACK") != NULL) // Strife VE
		wad->gamevers = 0x08;
	else if (FindMasterDir( wad->masterdir, "ENDOFWAD") != NULL &&
	         FindMasterDir( wad->masterdir, "MAP40") != NULL) // Doom 64 tLL
		wad->gamevers = 0x08;
	else if (FindMasterDir( wad->masterdir, "E4M8") != NULL) // Doom ult, Heretic SSR
		wad->gamevers = 0x04;
	else if (FindMasterDir( wad->masterdir, "MAP28") != NULL) // Doom II, Strife, Hexen, Doom 64
		wad->gamevers = 0x02;
	else if (FindMasterDir( wad->masterdir, "E2M8") != NULL) // Doom reg, Heretic reg
		wad->gamevers = 0x01;
	else /* shareware, demo */
		wad->gamevers = 0x00;
	/* distinguish Final Doom */
	if (wad->gamevers == 0x02 && FindMasterDir( wad->masterdir, "ROMERO1") != NULL)
		wad->gamevers = 0x06;
	if (wad->gamevers == 0x02 && FindMasterDir( wad->masterdir, "POISON") != NULL)
		wad->gamevers = 0x07;

	/* check for Doom end screen */
	if (FindMasterDir( wad->masterdir, "ENDOOM") != NULL)
		wad->gamevers += 0x00;
	/* check for Heretic end screen */
	else if (FindMasterDir( wad->masterdir, "ENDTEXT") != NULL)
		wad->gamevers += 0x10;
	/* check for Strife end screen */
	else if (FindMasterDir( wad->masterdir, "ENDSTRF") != NULL)
		wad->gamevers += 0x20;
	/* check for Strife VE end screen */
	else if (FindMasterDir( wad->masterdir, "STRBACK") != NULL)
		wad->gamevers += 0x20;
	/* check for Hexen map lumps */
	else if (FindMasterDir( wad->masterdir, "BEHAVIOR") != NULL)
		wad->gamevers += 0x40;
	/* check for Doom 64 wad end marker */
	else if (FindMasterDir( wad->masterdir, "ENDOFWAD") != NULL)
		wad->gamevers += 0x80;
	else
		ProgError( "\"%s\" is an unsupported IWAD file", filename);

	/* check shareware/demo wad is first in the list */
	if (wad != WadFileList && (wad->gamevers & 0x0F) == 0)
		ProgError( "shareware/demo WAD \"%s\" must be first in -file", filename);
	if ((wad->gamevers & 0xF0) != (WadFileList->gamevers & 0xF0) &&
	    (wad->gamevers & 0xF0) != 0x80)
		ProgError( "additional IWAD is incompatible with initial IWAD");

	if (Verb)
		fprintf( stderr, "Main WAD version: 0x%02x.\n", wad->gamevers);
	//ListMasterDirectory( wad);
}

/*
	close all IWad files, deallocating the data structures
*/
void CloseIWads( void)
{
	MDirPtr curd, nextd;
	WadPtr curw, nextw;

	curw = WadFileList;
	while (curw)
	{
		nextw = curw->next;
		fclose( curw->fileinfo);

		FreeMemory( curw->directory);
		curd = curw->masterdir;
		while (curd)
		{
			nextd = curd->next;
			FreeMemory( curd);
			curd = nextd;
		}
		FreeMemory( curw);
		curw = nextw;
	}
	WadFileList = NULL;
}


/*
	basic opening of WAD file and creation of node in Wad linked list
*/
WadPtr BasicWadOpen( char *filename)
{
	WadPtr curw, prevw;
#ifdef DM_BIG_ENDIAN
	BCLNG i;
#endif

	/* find the WAD file in the Wad file list */
	prevw = WadFileList;
	if (prevw)
	{
		curw = prevw->next;
		while (curw && strcmp( filename, curw->filename) != 0)
		{
			prevw = curw;
			curw = prevw->next;
		}
	}
	else
		curw = NULL;

	/* if this entry doesn't exist, add it to the WadFileList */
	if (!curw)
	{
		curw = (WadPtr) GetMemory( sizeof(struct WadFileInfo));
		if (!prevw)
			WadFileList = curw;
		else
			prevw->next = curw;
		curw->next = NULL;
		curw->filename = filename;
	}

	/* open the file */
	if ((curw->fileinfo = fopen( filename, "rb")) == NULL)
	{
		if (!prevw)
			WadFileList = NULL;
		else
			prevw->next = curw->next;
		FreeMemory( curw);
		ProgError( "error opening \"%s\"", filename);
	}

	/* read in the WAD directory info */
	BasicWadRead( curw, curw->type, 4);
	if (strncmp( curw->type, "IWAD", 4) != 0 && strncmp( curw->type, "PWAD", 4) != 0)
		ProgError( "\"%s\" is not a valid WAD file", filename);
	BasicWadRead( curw, &curw->dirsize, sizeof(curw->dirsize));
	swaplong( &(curw->dirsize));
	BasicWadRead( curw, &curw->dirstart, sizeof(curw->dirstart));
	swaplong( &(curw->dirstart));

	/* read in the WAD directory itself */
	curw->masterdir = NULL;
	curw->directory = (DirPtr) GetMemory( curw->dirsize * sizeof(struct Directory));
	BasicWadSeek( curw, curw->dirstart);
	BasicWadRead( curw, curw->directory, curw->dirsize * sizeof(struct Directory));
#ifdef DM_BIG_ENDIAN
	for (i = 0; i < curw->dirsize; i++)
	{
		DirPtr d = &(curw->directory[i]);
		swaplong( &(d->start));
		swaplong( &(d->size));
	}
#endif

	return curw;
}

/*
	read bytes from a file and store it into an address with error checking
*/
void BasicWadRead( WadPtr wadfile, void *addr, BCLNG size)
{
	if (fread( addr, 1, size, wadfile->fileinfo) != size)
		ProgError( "error reading from \"%s\"", wadfile->filename);
}

/*
	go to offset of a file with error checking
*/
void BasicWadSeek( WadPtr wadfile, BCLNG offset)
{
	if (fseek( wadfile->fileinfo, offset, 0))
		ProgError( "error reading from \"%s\"", wadfile->filename);
}


/*
	find an entry in the master directory
*/
MDirPtr FindMasterDir( MDirPtr from, char *name)
{
	while (from)
	{
		if (strncmp( from->dir.name, name, 8) == 0)
			break;
		from = from->next;
	}
	return from;
}

/*
	find a lump in the master directory after the current entry
	that matches a pattern (like E#M# or MAP##)
*/
MDirPtr FindNextLump( MDirPtr from, char *pattern, Bool start)
{
	int c;
	Bool same;

	if (!start && from)
		from = from->next;
	while (from)
	{
		same = TRUE;
		for (c = 0; c < 8; c++)
		{
			/* check for end of either string */
			if (from->dir.name[c] == '\0' || pattern[c] == '\0')
			{
				/* check for both ends of string */
				if (!(from->dir.name[c] == '\0' && pattern[c] == '\0'))
					same = FALSE;
				break;
			}
			/* check for different char */
			if (from->dir.name[c] != pattern[c])
			{
				if (pattern[c] != '#')
				{
					same = FALSE;
					break;
				}
				/* match digits to # */
				else if (isdigit( from->dir.name[c]) == 0)
				{
					same = FALSE;
					break;
				}
			}
		}
		if (same)
			break;
		from = from->next;
	}
	return from;
}

/*
	find an entry in this level's directory section
*/
MDirPtr FindLevelDir( MDirPtr from, char *name)
{
	/* start from lump after level marker */
	if (from)
		from = from->next;
	while (from)
	{
		if (!IsLevelLump( from->dir.name))
			return NULL;
		/* check for desired lump type */
		if (strncmp( from->dir.name, name, 8) == 0)
			break;
		from = from->next;
	}
	return from;
}

/*
	count the number of sprite lumps starting with prefix
*/
int CountSpriteLumps( MDirPtr from, char *prefix)
{
	int count = 0;

	/* search for start of sprites */
	while (from)
		if (strncmp( from->dir.name, "S_START", strlen( "S_START")) == 0)
			break;
		else
			from = from->next;

	/* count until end of sprites */
	while (from)
	{
		if (strncmp( from->dir.name, prefix, strlen( prefix)) == 0)
			count++;
		else if (strncmp( from->dir.name, "S_END", strlen( "S_END")) == 0)
			break;
		from = from->next;
	}
	return count;
}

#define NUMLUMPS 15

/*
	check whether name is level-related lump
*/
Bool IsLevelLump( char *name)
{
	BCINT n;
	char *llumps[NUMLUMPS] = {
		"THINGS",
		"VERTEXES",
		"LINEDEFS",
		"SIDEDEFS",
		"SECTORS",
		"SEGS",
		"SSECTORS",
		"NODES",
		"BLOCKMAP",
		"REJECT",
		"SCRIPTS",
		"BEHAVIOR",
		"LEAFS",
		"LIGHTS",
		"MACROS"
	};

	/* check known level lump types */
	for (n = 0; n < NUMLUMPS; n++)
		if (strncmp( name, llumps[n], 8) == 0)
			return TRUE;

	return FALSE;
}

/*
	list the master directory
*/
void ListMasterDirectory( WadPtr wad)
{
	MDirPtr dir;
	char dataname[ 9];

	dataname[ 8] = '\0';
	printf( "\nThe Master Directory\n");
	printf( "====================\n");
	printf( "\nWAD: %s\n\n", wad->filename);
	printf( "NAME____  SIZE__  START____\n");
	for (dir = wad->masterdir; dir; dir = dir->next)
	{
		strncpy( dataname, dir->dir.name, 8);
		printf( "%-8s  %6d  x%08x\n", dataname,
		        dir->dir.size, dir->dir.start);
	}
}

/* vim:set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
