/*
	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.

	LEVELS.C - Level loading and saving routines.
*/

#include "levels.h"
#include "things.h"


/*
	the global variables
*/
UBCINT NumThings = 0;      /* number of things */
TPtr      Things = NULL;   /* things data */
UBCINT NumLineDefs = 0;    /* number of line defs */
LDPtr     LineDefs = NULL; /* line defs data */
UBCINT NumSideDefs = 0;    /* number of side defs */
SDPtr     SideDefs = NULL; /* side defs data */
BCLNG  TotVertexes = 0;    /* total number of vertexes */
BCLNG  NumVertexes = 0;    /* number of used vertexes */
VPtr      Vertexes = NULL; /* vertex data */
/* UBCINT NumSegs = 0;        /* number of segments */
/* SEPtr     Segs = NULL;     /* list of segments */
/* UBCINT NumSSectors = 0;    /* number of subsectors */
/* SSPtr     SSectors = NULL; /* list of subsectors */
UBCINT NumSectors = 0;     /* number of sectors */
SPtr      Sectors = NULL;  /* sectors data */


/*
	read in the level data
*/
void ReadLevelData( WadPtr wadfile, char *name)
{
	MDirPtr dir, level;
	BCLNG n, m;
	BCINT val;
	Bool *VertexUsed;

	level = FindMasterDir( wadfile->masterdir, name);
	if (!level)
		ProgError( "level data not found for '%s'", name);

	/* read in the Things data */
	dir = FindLevelDir( level, "THINGS");
	if (dir)
	{
		if ((wadfile->gamevers & 0x80) == 0x80) // Doom64 format
			NumThings = (UBCINT) (dir->dir.size / sizeof(struct Thing64));
		else if ((wadfile->gamevers & 0xF0) == 0x40) // Hexen format
			NumThings = (UBCINT) (dir->dir.size / sizeof(struct ThingH));
		else // Doom format
			NumThings = (UBCINT) (dir->dir.size / sizeof(struct Thing));
	}
	else
		NumThings = 0;
	if (NumThings > 0)
	{
		Things = (TPtr) GetMemory( NumThings * sizeof(struct Thing));
		BasicWadSeek( wadfile, dir->dir.start);
		for (n = 0; n < NumThings; n++)
		{
			if ((wadfile->gamevers & 0xF0) == 0x40) // Hexen format, skip what's not needed
			{
				BasicWadRead( wadfile, &val, 2); // tid
			}
			BasicWadRead( wadfile, &(Things[n].xpos), 2);
			swapint( &(Things[n].xpos));
			BasicWadRead( wadfile, &(Things[n].ypos), 2);
			swapint( &(Things[n].ypos));
			if ((wadfile->gamevers & 0x40) == 0x40 || (wadfile->gamevers & 0x80) == 0x80) // Hexen/Doom64 format
			{
				BasicWadRead( wadfile, &val, 2); // zpos
			}
			BasicWadRead( wadfile, &(Things[n].angle), 2);
			swapint( &(Things[n].angle));
			BasicWadRead( wadfile, &(Things[n].type), 2);
			swapint( &(Things[n].type));
			BasicWadRead( wadfile, &(Things[n].when), 2);
			swapint( &(Things[n].when));
			if ((wadfile->gamevers & 0xF0) == 0x40) // Hexen format
			{
				BasicWadRead( wadfile, &val, 2); // special, arg1
				BasicWadRead( wadfile, &val, 2); // arg2, arg3
				BasicWadRead( wadfile, &val, 2); // arg4, arg5
			}
			if ((wadfile->gamevers & 0x80) == 0x80) // Doom64 format
				BasicWadRead( wadfile, &val, 2); // tid
		}
	}

	/* ignore all other lumps */

	return;

	/* get the number of Vertices */
	dir = FindLevelDir( level, "VERTEXES");
	if (dir)
		TotVertexes = (BCLNG) (dir->dir.size / sizeof(struct Vertex));
	else
		TotVertexes = 0;
	if (TotVertexes > 0)
	{
		VertexUsed = (Bool *) GetMemory( TotVertexes * sizeof(Bool));
		for (n = 0; n < TotVertexes; n++)
			VertexUsed[n] = FALSE;
	}

	/* read in the LineDef information */
	dir = FindLevelDir( level, "LINEDEFS");
	if (dir)
	{
		if ((wadfile->gamevers & 0xF0) == 0x40) // Hexen format
			NumLineDefs = (UBCINT) (dir->dir.size / sizeof(struct LineDefH));
		else
			NumLineDefs = (UBCINT) (dir->dir.size / sizeof(struct LineDef));
	}
	else
		NumLineDefs = 0;
	if (NumLineDefs > 0)
	{
		LineDefs = (LDPtr) GetMemory( NumLineDefs * sizeof(struct LineDef));
		BasicWadSeek( wadfile, dir->dir.start);
		for (n = 0; n < NumLineDefs; n++)
		{
			BasicWadRead( wadfile, &(LineDefs[n].start), 2);
			swapint( &(LineDefs[n].start));
			VertexUsed[LineDefs[n].start] = TRUE;
			BasicWadRead( wadfile, &(LineDefs[n].end), 2);
			swapint( &(LineDefs[n].end));
			VertexUsed[LineDefs[n].end] = TRUE;
			BasicWadRead( wadfile, &(LineDefs[n].flags), 2);
			swapint( &(LineDefs[n].flags));
			if ((wadfile->gamevers & 0xF0) == 0x40) // Hexen format, skip what's not needed
			{
				BasicWadRead( wadfile, &val, 2); // special, arg1
				BasicWadRead( wadfile, &val, 2); // arg2, arg3
				BasicWadRead( wadfile, &val, 2); // arg4, arg5
				LineDefs[n].type = 0;
				LineDefs[n].tag = 0;
			}
			else // Doom format
			{
				BasicWadRead( wadfile, &(LineDefs[n].type), 2);
				swapint( &(LineDefs[n].type));
				BasicWadRead( wadfile, &(LineDefs[n].tag), 2);
				swapint( &(LineDefs[n].tag));
			}
			BasicWadRead( wadfile, &(LineDefs[n].sidedef1), 2);
			swapint( &(LineDefs[n].sidedef1));
			BasicWadRead( wadfile, &(LineDefs[n].sidedef2), 2);
			swapint( &(LineDefs[n].sidedef2));
		}
	}

	/* read in the SideDef information */
	dir = FindLevelDir( level, "SIDEDEFS");
	if (dir)
		NumSideDefs = (UBCINT) (dir->dir.size / sizeof(struct SideDef));
	else
		NumSideDefs = 0;
	if (NumSideDefs > 0)
	{
		SideDefs = (SDPtr) GetMemory( NumSideDefs * sizeof(struct SideDef));
		BasicWadSeek( wadfile, dir->dir.start);
		for (n = 0; n < NumSideDefs; n++)
		{
			BasicWadRead( wadfile, &(SideDefs[n].xoff), 2);
			swapint( &(SideDefs[n].xoff));
			BasicWadRead( wadfile, &(SideDefs[n].yoff), 2);
			swapint( &(SideDefs[n].yoff));
			BasicWadRead( wadfile, &(SideDefs[n].tex1), 8);
			BasicWadRead( wadfile, &(SideDefs[n].tex2), 8);
			BasicWadRead( wadfile, &(SideDefs[n].tex3), 8);
			BasicWadRead( wadfile, &(SideDefs[n].sector), 2);
			swapint( &(SideDefs[n].sector));
		}
	}

	/* read in the Vertices which are all the corners of the level, but ignore the */
	/* Vertices not used in any LineDef (they usually are at the end of the list). */
	NumVertexes = 0;
	for (n = 0; n < TotVertexes; n++)
		if (VertexUsed[n])
			NumVertexes++;
	if (NumVertexes > 0)
	{
		Vertexes = (VPtr) GetMemory( NumVertexes * sizeof(struct Vertex));
		dir = FindLevelDir( level, "VERTEXES");
		BasicWadSeek( wadfile, dir->dir.start);
		m = 0;
		for (n = 0; n < TotVertexes; n++)
		{
			BasicWadRead( wadfile, &val, 2);
			swapint( &val);
			if (VertexUsed[n])
			{
				Vertexes[m].x = val;
			}
			BasicWadRead( wadfile, &val, 2);
			swapint( &val);
			if (VertexUsed[n])
			{
				Vertexes[m].y = val;
				m++;
			}
		}
		if (m != NumVertexes)
			ProgError("inconsistency in the Vertexes data");
	}

	if (TotVertexes > 0)
	{
		/* update the Vertex numbers in the LineDefs (not really necessary, but...) */
		m = 0;
		for (n = 0; n < TotVertexes; n++)
			if (VertexUsed[n])
				VertexUsed[n] = m++;
		for (n = 0; n < NumLineDefs; n++)
		{
			LineDefs[n].start = VertexUsed[LineDefs[n].start];
			LineDefs[n].end = VertexUsed[LineDefs[n].end];
		}
		FreeMemory( VertexUsed);
		VertexUsed = NULL;
	}

	/* ignore the Segs, SSectors and Nodes */

	/* read in the Sectors information */
	dir = FindLevelDir( level, "SECTORS");
	if (dir)
		NumSectors = (UBCINT) (dir->dir.size / sizeof(struct Sector));
	else
		NumSectors = 0;
	if (NumSectors > 0)
	{
		Sectors = (SPtr) GetMemory( NumSectors * sizeof(struct Sector));
		BasicWadSeek( wadfile, dir->dir.start);
		for (n = 0; n < NumSectors; n++)
		{
			BasicWadRead( wadfile, &(Sectors[n].floorh), 2);
			swapint( &(Sectors[n].floorh));
			BasicWadRead( wadfile, &(Sectors[n].ceilh), 2);
			swapint( &(Sectors[n].ceilh));
			BasicWadRead( wadfile, &(Sectors[n].floort), 8);
			BasicWadRead( wadfile, &(Sectors[n].ceilt), 8);
			BasicWadRead( wadfile, &(Sectors[n].light), 2);
			swapint( &(Sectors[n].light));
			BasicWadRead( wadfile, &(Sectors[n].special), 2);
			swapint( &(Sectors[n].special));
			BasicWadRead( wadfile, &(Sectors[n].tag), 2);
			swapint( &(Sectors[n].tag));
		}
	}

	/* ignore the Reject and BlockMap, and Hexen/other lumps */

	return;
}


/*
	forget the level data
*/
void ForgetLevelData( void)
{
	/* forget the Things */
	if (Things)
		FreeMemory( Things);
	Things = NULL;
	NumThings = 0;

	/* forget the Vertices */
	if (Vertexes)
		FreeMemory( Vertexes);
	Vertexes = NULL;
	NumVertexes = 0;

	/* forget the LineDefs */
	if (LineDefs)
		FreeMemory( LineDefs);
	LineDefs = NULL;
	NumLineDefs = 0;

	/* forget the SideDefs */
	if (SideDefs)
		FreeMemory( SideDefs);
	SideDefs = NULL;
	NumSideDefs = 0;

	/* forget the Sectors */
	if (Sectors)
		FreeMemory( Sectors);
	Sectors = NULL;
	NumSectors = 0;
}


/*
	search levels in IWAD according to pattern until thing is found
*/
Bool SearchLevels( WadPtr wadfile, char *pattern, UBCINT type)
{
	MDirPtr level;

	if (Verb)
		printf("%s: %s\n", wadfile->filename, pattern);

	level = FindNextLump( wadfile->masterdir, pattern, TRUE);
	while (level)
	{
		ReadLevelData( wadfile, level->dir.name);
		if (FindThingInLevel( wadfile, type))
		{
			ForgetLevelData();
			if (Verb)
				fprintf( stderr, "%s:\t%5d\tmap    %s\n",
				         wadfile->filename, type, level->dir.name);
			return TRUE;
		}
		ForgetLevelData();
		level = FindNextLump( level, pattern, FALSE);
	}

	return FALSE;
}

/*
	find the thing on the current level in single-player mode
*/
Bool FindThingInLevel( WadPtr wadfile, UBCINT type)
{
	int n;

	for (n = 0; n < NumThings; n++)
	{
		if ((wadfile->gamevers & 0xF0) == 0x40) // Hexen format
		{
			if (((Things[n].when & TF_HXNSP) != 0 || (Things[n].when & TF_HXNCP) != 0) && type == Things[n].type)
				return TRUE;
		}
		else // Doom format
		{
			if ((Things[n].when & TF_NOTSP) == 0 && type == Things[n].type)
				return TRUE;
			// deathmatch start in any mode
			if (type == 11 && type == Things[n].type)
				return TRUE;
		}
	}

	return FALSE;
}

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