//-----------------------------------------------------------------------------
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
// DESCRIPTION:
//	Handles WAD file header, directory, lump I/O.
//
//-----------------------------------------------------------------------------

#include "vpo_local.h"

namespace vpo
{


typedef struct
{
	// Should be "IWAD" or "PWAD".
	char		identification[4];
	int			numlumps;
	int			infotableofs;

} PACKEDATTR wadinfo_t;


typedef struct
{
	int			filepos;
	int			size;
	char		name[8];

} PACKEDATTR filelump_t;

//
// GLOBALS
//

// Location of each lump on disk.

lumpinfo_t *lumpinfo;

int numlumps = 0;

static char * wad_filename;

static wad_file_t * current_file;



//
// LUMP BASED ROUTINES.
//

// andrewj: added this to find level names
//          returns 0 if not a level, 1 for DOOM, 2 for HEXEN format
static int CheckMapHeader(filelump_t *lumps, int num_after)
{
	static const char *level_lumps[] =
	{
		"THINGS", "LINEDEFS", "SIDEDEFS", "VERTEXES",
		"SEGS", "SSECTORS", "NODES", "SECTORS",
		"REJECT", "BLOCKMAP"
	};

	if (num_after < 10)
		return 0;

	for (int i = 0 ; i < 10 ; i++)
	{
		const char *name = level_lumps[i];

		// level lumps are never valid map header names
		if (strncmp(lumps[0].name, name, 8) == 0)
			return 0;

		// require a one-to-one correspondence
		// (anything else would crash DOOM)
		if (strncmp(lumps[1 + i].name, name, 8) != 0)
			return 0;
	}

	// hexen format is distinguished by a following BEHAVIOR lump
	if (num_after >= 11 && strncmp(lumps[11].name, "BEHAVIOR", 8) == 0)
		return 2;

	return 1;
}


//
// W_AddFile
//
// All files are optional, but at least one file must be
//  found (PWAD, if all required lumps are present).
// Files with a .wad extension are wadlink files
//  with multiple lumps.
// Other files are single lumps with the base filename
//  for the lump name.

bool W_AddFile (const char *filename)
{
	wadinfo_t header;
	lumpinfo_t *lump_p;
	wad_file_t *wad_file;

	int i;
	int length;
	int startlump;

	filelump_t *fileinfo;
	filelump_t *filerover;

	// open the file and add to directory

	wad_file = W_OpenFile(filename);

	if (wad_file == NULL)
	{
		return false;
	}

	startlump = numlumps;

	W_Read(wad_file, 0, &header, sizeof(header));

	if (strncmp(header.identification,"IWAD",4) != 0)
	{
		// Homebrew levels?
		if (strncmp(header.identification,"PWAD",4) != 0)
		{
///			I_Error ("Wad file %s doesn't have IWAD "
///					"or PWAD id\n", filename);

			W_CloseFile(wad_file);

			return false;
		}
	}

	header.numlumps = LONG(header.numlumps);
	header.infotableofs = LONG(header.infotableofs);

	length = header.numlumps * sizeof(filelump_t);

	fileinfo = new filelump_t[header.numlumps];

	W_Read(wad_file, header.infotableofs, fileinfo, length);
	numlumps += header.numlumps;


	// Fill in lumpinfo
	lumpinfo = (lumpinfo_t *)realloc(lumpinfo, numlumps * sizeof(lumpinfo_t));

	if (! lumpinfo)
		I_Error ("Out of memory -- could not realloc lumpinfo");

	lump_p = &lumpinfo[startlump];

	filerover = fileinfo;

	for (i=startlump; i < numlumps; ++i)
	{
		int map_header;

		lump_p->position = LONG(filerover->filepos);
		lump_p->size = LONG(filerover->size);

		strncpy(lump_p->name, filerover->name, 8);

		map_header = CheckMapHeader(filerover, numlumps - i - 1);

		lump_p->is_map_header = (map_header >= 1);
		lump_p->is_hexen      = (map_header == 2);

		++lump_p;
		++filerover;
	}

	delete[] fileinfo;

	// close the file now, we re-open it later to load the map
	W_CloseFile(wad_file);

	wad_filename = strdup(filename);

	if (! wad_filename)
		I_Error ("Out of memory -- could not strdup filename");

	return true;
}


void W_RemoveFile(void)
{
	if (wad_filename)
	{
		free(wad_filename);

		wad_filename = NULL;

		free(lumpinfo);

		lumpinfo = NULL;
		numlumps = 0;
	}
}


//
// W_NumLumps
//
int W_NumLumps (void)
{
	return numlumps;
}


//
// W_CheckNumForName
// Returns -1 if name not found.
//
int W_CheckNumForName (const char* name)
{
	int i;

	// scan backwards so patch lump files take precedence

	for (i=numlumps-1; i >= 0; --i)
	{
		if (strncasecmp(lumpinfo[i].name, name, 8) == 0)
		{
			return i;
		}
	}

	// TFB. Not found.

	return -1;
}


//
// W_LumpLength
// Returns the buffer size needed to load the given lump.
//
int W_LumpLength (int lumpnum)
{
	if (lumpnum >= numlumps)
	{
		I_Error ("W_LumpLength: %i >= numlumps", lumpnum);
	}

	return lumpinfo[lumpnum].size;
}


//
// W_ReadLump
// Loads the lump into the given buffer,
//  which must be >= W_LumpLength().
//
static void W_ReadLump(int lump, void *dest)
{
	int c;
	lumpinfo_t *l;

	if (lump >= numlumps)
	{
		I_Error ("W_ReadLump: %i >= numlumps", lump);
	}

	l = lumpinfo+lump;

	c = W_Read(current_file, l->position, dest, l->size);

	if (c < l->size)
	{
		I_Error ("W_ReadLump: only read %i of %i on lump %i", c, l->size, lump);
	}
}


//
// W_LoadLump
//
// Load a lump into memory and return a pointer to a buffer containing
// the lump data.
//
byte * W_LoadLump(int lumpnum)
{
	byte *result;

	if (lumpnum < 0 || lumpnum >= numlumps)
	{
		I_Error ("W_LoadLump: %i >= numlumps", lumpnum);
	}

	if (! current_file)
		I_Error ("W_LoadLump: no current file (W_BeginRead not called)");

	// load it now

	result = new byte[W_LumpLength(lumpnum) + 1];

	W_ReadLump (lumpnum, result);

	return result;
}


void W_FreeLump(byte * data)
{
	delete[] data;
}


void W_BeginRead(void)
{
	// check API usage
	if (! wad_filename)
		I_Error("W_BeginRead called without any wad file!");

	if (current_file)
		I_Error("W_BeginRead called twice without W_EndRead.");

	current_file = W_OpenFile(wad_filename);

	// it should normally succeed, as it is unlikely the file suddenly
	// disappears between reading the directory and loading a map.
	if (! current_file)
		I_Error("Could not re-open the wad file -- deleted?");
}


void W_EndRead()
{
	if (! current_file)
		I_Error("W_EndRead called without a previous W_BeginRead.");

	W_CloseFile(current_file);

	current_file = NULL;
}


} // namespace vpo

//--- editor settings ---
// vi:ts=4:sw=4:noexpandtab
// Emacs style mode select   -*- C++ -*-