/* -------------------------------------------------------------------------------

Copyright (C) 1999-2007 id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.

This file is part of GtkRadiant.

GtkRadiant 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.

GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

----------------------------------------------------------------------------------

This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."

------------------------------------------------------------------------------- */



/* marker */
#define BSPFILE_RBSP_C



/* dependencies */
#include "q3map2.h"




/* -------------------------------------------------------------------------------

this file handles translating the bsp file format used by quake 3, rtcw, and ef
into the abstracted bsp file used by q3map2.

------------------------------------------------------------------------------- */

/* constants */
#define	LUMP_ENTITIES		0
#define	LUMP_SHADERS		1
#define	LUMP_PLANES			2
#define	LUMP_NODES			3
#define	LUMP_LEAFS			4
#define	LUMP_LEAFSURFACES	5
#define	LUMP_LEAFBRUSHES	6
#define	LUMP_MODELS			7
#define	LUMP_BRUSHES		8
#define	LUMP_BRUSHSIDES		9
#define	LUMP_DRAWVERTS		10
#define	LUMP_DRAWINDEXES	11
#define	LUMP_FOGS			12
#define	LUMP_SURFACES		13
#define	LUMP_LIGHTMAPS		14
#define	LUMP_LIGHTGRID		15
#define	LUMP_VISIBILITY		16
#define LUMP_LIGHTARRAY		17
#define	HEADER_LUMPS		18


/* types */
typedef struct
{
	char            ident[4];
	int             version;

	bspLump_t       lumps[HEADER_LUMPS];
}
rbspHeader_t;



/* light grid */
#define MAX_MAP_GRID		0xffff
#define MAX_MAP_GRIDARRAY	0x100000
#define LG_EPSILON			4


static void CopyLightGridLumps(rbspHeader_t * header)
{
	int             i;
	unsigned short *inArray;
	bspGridPoint_t *in, *out;


	/* get count */
	numBSPGridPoints = GetLumpElements((bspHeader_t *) header, LUMP_LIGHTARRAY, sizeof(*inArray));

	/* allocate buffer */
	bspGridPoints = safe_malloc(numBSPGridPoints * sizeof(*bspGridPoints));
	memset(bspGridPoints, 0, numBSPGridPoints * sizeof(*bspGridPoints));

	/* copy */
	inArray = GetLump((bspHeader_t *) header, LUMP_LIGHTARRAY);
	in = GetLump((bspHeader_t *) header, LUMP_LIGHTGRID);
	out = bspGridPoints;
	for(i = 0; i < numBSPGridPoints; i++)
	{
		memcpy(out, &in[*inArray], sizeof(*in));
		inArray++;
		out++;
	}
}


static void AddLightGridLumps(FILE * file, rbspHeader_t * header)
{
	int             i, j, k, c, d;
	int             numGridPoints, maxGridPoints;
	bspGridPoint_t *gridPoints, *in, *out;
	int             numGridArray;
	unsigned short *gridArray;
	qboolean        bad;


	/* allocate temporary buffers */
	maxGridPoints = (numBSPGridPoints < MAX_MAP_GRID) ? numBSPGridPoints : MAX_MAP_GRID;
	gridPoints = safe_malloc(maxGridPoints * sizeof(*gridPoints));
	gridArray = safe_malloc(numBSPGridPoints * sizeof(*gridArray));

	/* zero out */
	numGridPoints = 0;
	numGridArray = numBSPGridPoints;

	/* for each bsp grid point, find an approximate twin */
	Sys_Printf("Storing lightgrid: %d points\n", (int)numBSPGridPoints);
	for(i = 0; i < numGridArray; i++)
	{
		/* get points */
		in = &bspGridPoints[i];

		/* walk existing list */
		for(j = 0; j < numGridPoints; j++)
		{
			/* get point */
			out = &gridPoints[j];

			/* compare styles */
			if(*((unsigned int *)in->styles) != *((unsigned int *)out->styles))
				continue;

			/* compare direction */
			d = abs(in->latLong[0] - out->latLong[0]);
			if(d < (255 - LG_EPSILON) && d > LG_EPSILON)
				continue;
			d = abs(in->latLong[1] - out->latLong[1]);
			if(d < 255 - LG_EPSILON && d > LG_EPSILON)
				continue;

			/* compare light */
			bad = qfalse;
			for(k = 0; (k < MAX_LIGHTMAPS && bad == qfalse); k++)
			{
				for(c = 0; c < 3; c++)
				{
					if(abs((int)in->ambient[k][c] - (int)out->ambient[k][c]) > LG_EPSILON ||
					   abs((int)in->directed[k][c] - (int)out->directed[k][c]) > LG_EPSILON)
					{
						bad = qtrue;
						break;
					}
				}
			}

			/* failure */
			if(bad)
				continue;

			/* this sample is ok */
			break;
		}

		/* set sample index */
		gridArray[i] = (unsigned short)j;

		/* if no sample found, add a new one */
		if(j >= numGridPoints && numGridPoints < maxGridPoints)
		{
			out = &gridPoints[numGridPoints++];
			memcpy(out, in, sizeof(*in));
		}
	}

	/* swap array */
	for(i = 0; i < numGridArray; i++)
		gridArray[i] = LittleShort(gridArray[i]);

	/* write lumps */
	AddLump(file, (bspHeader_t *) header, LUMP_LIGHTGRID, gridPoints, (numGridPoints * sizeof(*gridPoints)));
	AddLump(file, (bspHeader_t *) header, LUMP_LIGHTARRAY, gridArray, (numGridArray * sizeof(*gridArray)));

	/* free buffers */
	free(gridPoints);
	free(gridArray);
}



/*
LoadRBSPFile()
loads a raven bsp file into memory
*/

void LoadRBSPFile(const char *filename)
{
	rbspHeader_t   *header;


	/* load the file header */
	LoadFile(filename, (void **)&header);

	/* swap the header (except the first 4 bytes) */
	SwapBlock((int *)((byte *) header + sizeof(int)), sizeof(*header) - sizeof(int));

	/* make sure it matches the format we're trying to load */
	if(force == qfalse && *((int *)header->ident) != *((int *)game->bspIdent))
		Error("%s is not a %s file", filename, game->bspIdent);
	if(force == qfalse && header->version != game->bspVersion)
		Error("%s is version %d, not %d", filename, header->version, game->bspVersion);

	/* load/convert lumps */
	numBSPShaders =
		CopyLump_Allocate((bspHeader_t *) header, LUMP_SHADERS, (void **)&bspShaders, sizeof(bspShader_t), &allocatedBSPShaders);

	numBSPModels =
		CopyLump_Allocate((bspHeader_t *) header, LUMP_MODELS, (void **)&bspModels, sizeof(bspModel_t), &allocatedBSPModels);

	numBSPPlanes =
		CopyLump_Allocate((bspHeader_t *) header, LUMP_PLANES, (void **)&bspPlanes, sizeof(bspPlane_t), &allocatedBSPPlanes);

	numBSPLeafs = CopyLump((bspHeader_t *) header, LUMP_LEAFS, bspLeafs, sizeof(bspLeaf_t));

	numBSPNodes =
		CopyLump_Allocate((bspHeader_t *) header, LUMP_NODES, (void **)&bspNodes, sizeof(bspNode_t), &allocatedBSPNodes);

	numBSPLeafSurfaces =
		CopyLump_Allocate((bspHeader_t *) header, LUMP_LEAFSURFACES, (void **)&bspLeafSurfaces, sizeof(bspLeafSurfaces[0]),
						  &allocatedBSPLeafSurfaces);

	numBSPLeafBrushes =
		CopyLump_Allocate((bspHeader_t *) header, LUMP_LEAFBRUSHES, (void **)&bspLeafBrushes, sizeof(bspLeafBrushes[0]),
						  &allocatedBSPLeafBrushes);

	numBSPBrushes =
		CopyLump_Allocate((bspHeader_t *) header, LUMP_BRUSHES, (void **)&bspBrushes, sizeof(bspBrush_t),
						  &allocatedBSPLeafBrushes);

	numBSPBrushSides =
		CopyLump_Allocate((bspHeader_t *) header, LUMP_BRUSHSIDES, (void **)&bspBrushSides, sizeof(bspBrushSide_t),
						  &allocatedBSPBrushSides);

	numBSPDrawVerts = GetLumpElements((bspHeader_t *) header, LUMP_DRAWVERTS, sizeof(bspDrawVerts[0]));
	SetDrawVerts(numBSPDrawVerts);
	CopyLump((bspHeader_t *) header, LUMP_DRAWVERTS, bspDrawVerts, sizeof(bspDrawVerts[0]));

	numBSPDrawSurfaces = GetLumpElements((bspHeader_t *) header, LUMP_SURFACES, sizeof(bspDrawSurfaces[0]));
	SetDrawSurfaces(numBSPDrawSurfaces);
	CopyLump((bspHeader_t *) header, LUMP_SURFACES, bspDrawSurfaces, sizeof(bspDrawSurfaces[0]));

	numBSPFogs = CopyLump((bspHeader_t *) header, LUMP_FOGS, bspFogs, sizeof(bspFogs[0]));

	numBSPDrawIndexes = CopyLump((bspHeader_t *) header, LUMP_DRAWINDEXES, bspDrawIndexes, sizeof(bspDrawIndexes[0]));

	numBSPVisBytes = CopyLump((bspHeader_t *) header, LUMP_VISIBILITY, bspVisBytes, 1);

	numBSPLightBytes = GetLumpElements((bspHeader_t *) header, LUMP_LIGHTMAPS, 1);
	bspLightBytes = safe_malloc(numBSPLightBytes);
	CopyLump((bspHeader_t *) header, LUMP_LIGHTMAPS, bspLightBytes, 1);

	bspEntDataSize = CopyLump_Allocate((bspHeader_t *) header, LUMP_ENTITIES, (void **)&bspEntData, 1, &allocatedBSPEntData);

	CopyLightGridLumps(header);

	/* free the file buffer */
	free(header);
}



/*
WriteRBSPFile()
writes a raven bsp file
*/

void WriteRBSPFile(const char *filename)
{
	rbspHeader_t    outheader, *header;
	FILE           *file;
	time_t          t;
	char            marker[1024];
	int             size;


	/* set header */
	header = &outheader;
	memset(header, 0, sizeof(*header));

	//% Swapfile();

	/* set up header */
	*((int *)(bspHeader_t *) header->ident) = *((int *)game->bspIdent);
	header->version = LittleLong(game->bspVersion);

	/* write initial header */
	file = SafeOpenWrite(filename);
	SafeWrite(file, (bspHeader_t *) header, sizeof(*header));	/* overwritten later */

	/* add marker lump */
	time(&t);
	sprintf(marker, "I LOVE MY Q3MAP2 %s on %s)", Q3MAP_VERSION, asctime(localtime(&t)));
	AddLump(file, (bspHeader_t *) header, 0, marker, strlen(marker) + 1);

	/* add lumps */
	AddLump(file, (bspHeader_t *) header, LUMP_SHADERS, bspShaders, numBSPShaders * sizeof(bspShader_t));
	AddLump(file, (bspHeader_t *) header, LUMP_PLANES, bspPlanes, numBSPPlanes * sizeof(bspPlane_t));
	AddLump(file, (bspHeader_t *) header, LUMP_LEAFS, bspLeafs, numBSPLeafs * sizeof(bspLeaf_t));
	AddLump(file, (bspHeader_t *) header, LUMP_NODES, bspNodes, numBSPNodes * sizeof(bspNode_t));
	AddLump(file, (bspHeader_t *) header, LUMP_BRUSHES, bspBrushes, numBSPBrushes * sizeof(bspBrush_t));
	AddLump(file, (bspHeader_t *) header, LUMP_BRUSHSIDES, bspBrushSides, numBSPBrushSides * sizeof(bspBrushSides[0]));
	AddLump(file, (bspHeader_t *) header, LUMP_LEAFSURFACES, bspLeafSurfaces, numBSPLeafSurfaces * sizeof(bspLeafSurfaces[0]));
	AddLump(file, (bspHeader_t *) header, LUMP_LEAFBRUSHES, bspLeafBrushes, numBSPLeafBrushes * sizeof(bspLeafBrushes[0]));
	AddLump(file, (bspHeader_t *) header, LUMP_MODELS, bspModels, numBSPModels * sizeof(bspModel_t));
	AddLump(file, (bspHeader_t *) header, LUMP_DRAWVERTS, bspDrawVerts, numBSPDrawVerts * sizeof(bspDrawVerts[0]));
	AddLump(file, (bspHeader_t *) header, LUMP_SURFACES, bspDrawSurfaces, numBSPDrawSurfaces * sizeof(bspDrawSurfaces[0]));
	AddLump(file, (bspHeader_t *) header, LUMP_VISIBILITY, bspVisBytes, numBSPVisBytes);
	AddLump(file, (bspHeader_t *) header, LUMP_LIGHTMAPS, bspLightBytes, numBSPLightBytes);
	AddLightGridLumps(file, header);
	AddLump(file, (bspHeader_t *) header, LUMP_ENTITIES, bspEntData, bspEntDataSize);
	AddLump(file, (bspHeader_t *) header, LUMP_FOGS, bspFogs, numBSPFogs * sizeof(bspFog_t));
	AddLump(file, (bspHeader_t *) header, LUMP_DRAWINDEXES, bspDrawIndexes, numBSPDrawIndexes * sizeof(bspDrawIndexes[0]));

	/* emit bsp size */
	size = ftell(file);
	Sys_Printf("Wrote %.1f MB (%d bytes)\n", (float)size / (1024 * 1024), size);

	/* write the completed header */
	fseek(file, 0, SEEK_SET);
	SafeWrite(file, header, sizeof(*header));

	/* close the file */
	fclose(file);
}
