/*
===========================================================================

Return to Castle Wolfenstein single player GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 

This file is part of the Return to Castle Wolfenstein single player GPL Source Code (RTCW SP Source Code).  

RTCW SP Source Code 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 3 of the License, or
(at your option) any later version.

RTCW SP Source Code 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 RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

//===========================================================================
//
// Name:				BSP tool
// Function:
// Programmer:		id Software & Mr Elusive (MrElusive@demigod.demon.nl)
// Last update:	1997-12-04
// Tab Size:		3
// Notes:			Microsoft Visual C++ optimizations:
//						"global optimization" or "full optimization" results
//						in micro brushes??
//===========================================================================

#if defined( WIN32 ) || defined( _WIN32 )
#include <direct.h>
#include <windows.h>
#include <sys/types.h>
#include <sys/stat.h>
#else
#include <unistd.h>
#include <glob.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
#include "qbsp.h"
#include "l_mem.h"
//#include "l_qfiles.h"
#include "../botlib/aasfile.h"
#include "../botlib/be_aas_cluster.h"
#include "../botlib/be_aas_optimize.h"
#include "aas_create.h"
#include "aas_store.h"
#include "aas_file.h"
#include "aas_cfg.h"
#include "be_aas_bspc.h"

#define BSPC_VERSION        "2.1c-wolf"

// TTimo: some messy config things
#ifndef BSPC
  #define BSPC  // RF
#endif
#define stricmp strcasecmp

void AAS_InitBotImport();
void AAS_InitClustering();
void AAS_ShowTotals();


extern int use_nodequeue;       //brushbsp.c
extern int calcgrapplereach;    //be_aas_reach.c

float subdivide_size = 240;
char source[1024];
char name[1024];
vec_t microvolume = 1.0;
char outbase[32];
int entity_num;
aas_settings_t aassettings;

qboolean noprune;               //don't prune nodes (bspc.c)
qboolean glview;                //create a gl view
qboolean nodetail;              //don't use detail brushes (map.c)
qboolean fulldetail;            //use but don't mark detail brushes (map.c)
qboolean onlyents;              //only process the entities (bspc.c)
qboolean nomerge;               //don't merge bsp node faces (faces.c)
qboolean nowater;               //don't use the water brushes (map.c)
qboolean nocsg;                 //don't carve intersecting brushes (bspc.c)
qboolean noweld;                //use unique face vertexes (faces.c)
qboolean noshare;               //don't share bsp edges (faces.c)
qboolean nosubdiv;              //don't subdivide bsp node faces (faces.c)
qboolean notjunc;               //don't create tjunctions (edge melting) (faces.c)
qboolean optimize;              //enable optimisation
qboolean leaktest;              //perform a leak test
qboolean verboseentities;
qboolean freetree;              //free the bsp tree when not needed anymore
qboolean create_aas;            //create an .AAS file
qboolean nobrushmerge;          //don't merge brushes
qboolean lessbrushes;           //create less brushes instead of correct texture placement
qboolean cancelconversion;      //true if the conversion is being cancelled
qboolean noliquids;             //no liquids when writing map file
qboolean forcesidesvisible;     //force all brush sides to be visible when loaded from bsp
qboolean writeaasmap;           //
// qboolean	capsule_collision = true;	//use capsule collision
qboolean capsule_collision = false; // capsule collision// Ridah, allow to specify an extension for multiple AAS files per map

char aas_extension[64];
// done.

float VectorDistance( vec3_t v1, vec3_t v2 ) {
	vec3_t dir;

	VectorSubtract( v2, v1, dir );
	return VectorLength( dir );
}

/*
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void ProcessWorldModel (void)
{
	entity_t	*e;
	tree_t *tree;
	qboolean	leaked;
	int brush_start, brush_end;

	e = &entities[entity_num];

	brush_start = e->firstbrush;
	brush_end = brush_start + e->numbrushes;
	leaked = false;

	//process the whole world in one time
	tree = ProcessWorldBrushes(brush_start, brush_end);
	//create the bsp tree portals
	MakeTreePortals(tree);
	//mark all leafs that can be reached by entities
	if (FloodEntities(tree))
	{
		FillOutside(tree->headnode);
	} //end if
	else
	{
		Log_Print("**** leaked ****\n");
		leaked = true;
		LeakFile(tree);
		if (leaktest)
		{
			Log_Print("--- MAP LEAKED ---\n");
			exit(0);
		} //end if
	} //end else

	MarkVisibleSides (tree, brush_start, brush_end);

	FloodAreas (tree);

#ifndef ME
	if (glview) WriteGLView(tree, source);
#endif
	MakeFaces(tree->headnode);
	FixTjuncs(tree->headnode);

	//NOTE: Never prune the nodes because the portals
	//		are screwed when prunning is done and as
	//		a result portal writing will crash
	//if (!noprune) PruneNodes(tree->headnode);

	WriteBSP(tree->headnode);

	if (!leaked) WritePortalFile(tree);

	Tree_Free(tree);
} //end of the function ProcessWorldModel
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void ProcessSubModel (void)
{
	entity_t	*e;
	int start, end;
	tree_t *tree;
	bspbrush_t *list;
	vec3_t mins, maxs;

	e = &entities[entity_num];

	start = e->firstbrush;
	end = start + e->numbrushes;

	mins[0] = mins[1] = mins[2] = -4096;
	maxs[0] = maxs[1] = maxs[2] = 4096;
	list = MakeBspBrushList(start, end, mins, maxs);
	if (!nocsg) list = ChopBrushes (list);
	tree = BrushBSP (list, mins, maxs);
	MakeTreePortals (tree);
	MarkVisibleSides (tree, start, end);
	MakeFaces (tree->headnode);
	FixTjuncs (tree->headnode);
	WriteBSP (tree->headnode);
	Tree_Free (tree);
} //end of the function ProcessSubModel
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void ProcessModels (void)
{
	BeginBSPFile();

	for (entity_num = 0; entity_num < num_entities; entity_num++)
	{
		if (!entities[entity_num].numbrushes)
			continue;

		Log_Print("############### model %i ###############\n", nummodels);
		BeginModel();
		if (entity_num == 0) ProcessWorldModel();
		else ProcessSubModel();
		EndModel();

		if (!verboseentities)
			verbose = false;	// don't bother printing submodels
	} //end for
	EndBSPFile();
} //end of the function ProcessModels
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Win_Map2Bsp(char *bspfilename)
{
	double start, end;
	char path[1024];

	start = I_FloatTime();

	ThreadSetDefault();
	//yeah sure Carmack
	//numthreads = 1;		// multiple threads aren't helping...

	strcpy(source, ExpandArg(bspfilename));
	StripExtension(source);

	//delete portal and line files
	sprintf(path, "%s.prt", source);
	remove(path);
	sprintf(path, "%s.lin", source);
	remove(path);

	strcpy(name, ExpandArg(bspfilename));
	DefaultExtension(name, ".map");	// might be .reg

	Q2_AllocMaxBSP();
	//
	SetModelNumbers();
	SetLightStyles();
	ProcessModels();
	//write the BSP
	Q2_WriteBSPFile(bspfilename);

	Q2_FreeMaxBSP();

	end = I_FloatTime();
	Log_Print("%5.0f seconds elapsed\n", end-start);
} //end of the function Win_Map2Bsp
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Map2Bsp(char *mapfilename, char *outputfilename)
{
	double start, end;
	char path[1024];

	start = I_FloatTime ();

	ThreadSetDefault ();
	//yeah sure Carmack
	//numthreads = 1;		//multiple threads aren't helping...
	//SetQdirFromPath(bspfilename);

	strcpy(source, ExpandArg(mapfilename));
	StripExtension(source);

	// delete portal and line files
	sprintf(path, "%s.prt", source);
	remove(path);
	sprintf(path, "%s.lin", source);
	remove(path);

	strcpy(name, ExpandArg(mapfilename));
	DefaultExtension(name, ".map");	// might be .reg

	//
	// if onlyents, just grab the entites and resave
	//
	if (onlyents)
	{
		char out[1024];

		Q2_AllocMaxBSP();
		sprintf (out, "%s.bsp", source);
		Q2_LoadBSPFile(out, 0, 0);
		num_entities = 0;

		Q2_LoadMapFile(name);
		SetModelNumbers();
		SetLightStyles();

		Q2_UnparseEntities();

		Q2_WriteBSPFile(out);
		//
		Q2_FreeMaxBSP();
	} //end if
	else
	{
		//
		// start from scratch
		//
		Q2_AllocMaxBSP();
		//load the map
		Q2_LoadMapFile(name);
		//create the .bsp file
		SetModelNumbers();
		SetLightStyles();
		ProcessModels();
		//write the BSP
		Q2_WriteBSPFile(outputfilename);
		//
		Q2_FreeMaxBSP();
	} //end else

	end = I_FloatTime();
	Log_Print("%5.0f seconds elapsed\n", end-start);
} //end of the function Map2Bsp
*/
//===========================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
void AASOuputFile( quakefile_t *qf, char *outputpath, char *filename ) {
	char ext[MAX_PATH];

	//
	if ( strlen( outputpath ) ) {
		strcpy( filename, outputpath );
		//append the bsp file base
		AppendPathSeperator( filename, MAX_PATH );
		ExtractFileBase( qf->origname, &filename[strlen( filename )] );

		// Ridah, add extension
		strcat( filename, aas_extension );
		// done.

		//append .aas
		strcat( filename, ".aas" );
		return;
	} //end if
	  //
	ExtractFileExtension( qf->filename, ext );
	if ( !stricmp( ext, "pk3" ) || !stricmp( ext, "pak" ) || !stricmp( ext, "sin" ) ) {
		strcpy( filename, qf->filename );
		while ( strlen( filename ) &&
				filename[strlen( filename ) - 1] != '\\' &&
				filename[strlen( filename ) - 1] != '/' )
		{
			filename[strlen( filename ) - 1] = '\0';
		} //end while
		strcat( filename, "maps" );
		if ( access( filename, 0x04 ) ) {
			CreatePath( filename );
		}
		//append the bsp file base
		AppendPathSeperator( filename, MAX_PATH );
		ExtractFileBase( qf->origname, &filename[strlen( filename )] );

		// Ridah, add extension
		strcat( filename, aas_extension );
		// done.

		//append .aas
		strcat( filename, ".aas" );
	} //end if
	else
	{
		strcpy( filename, qf->filename );
		while ( strlen( filename ) &&
				filename[strlen( filename ) - 1] != '.' )
		{
			filename[strlen( filename ) - 1] = '\0';
		} //end while

		// Ridah, add extension
		strcat( filename, aas_extension );
		// done.

		strcat( filename, "aas" );
	} //end else
} //end of the function AASOutputFile
//===========================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
void CreateAASFilesForAllBSPFiles( char *quakepath ) {
#if defined( WIN32 ) | defined( _WIN32 )
	WIN32_FIND_DATA filedata;
	HWND handle;
	struct _stat statbuf;
#else
	glob_t globbuf;
	struct stat statbuf;
	int j;
#endif
	//int done; // TTimo: unused
	char filter[_MAX_PATH], bspfilter[_MAX_PATH], aasfilter[_MAX_PATH];
	char aasfile[_MAX_PATH], buf[_MAX_PATH], foldername[_MAX_PATH];
	quakefile_t *qf, *qf2, *files, *bspfiles, *aasfiles;

	strcpy( filter, quakepath );
	AppendPathSeperator( filter, sizeof( filter ) );
	strcat( filter, "*" );

#if defined( WIN32 ) | defined( _WIN32 )
	handle = FindFirstFile( filter, &filedata );
	done = ( handle == INVALID_HANDLE_VALUE );
	while ( !done )
	{
		_splitpath( filter, foldername, NULL, NULL, NULL );
		_splitpath( filter, NULL, &foldername[strlen( foldername )], NULL, NULL );
		AppendPathSeperator( foldername, _MAX_PATH );
		strcat( foldername, filedata.cFileName );
		_stat( foldername, &statbuf );
#else
	glob( filter, 0, NULL, &globbuf );
	for ( j = 0; j < globbuf.gl_pathc; j++ )
	{
		strcpy( foldername, globbuf.gl_pathv[j] );
		stat( foldername, &statbuf );
#endif
		//if it is a folder
		if ( statbuf.st_mode & S_IFDIR ) {
			//
			AppendPathSeperator( foldername, sizeof( foldername ) );
			//get all the bsp files
			strcpy( bspfilter, foldername );
			strcat( bspfilter, "maps/*.bsp" );
			files = FindQuakeFiles( bspfilter );
			strcpy( bspfilter, foldername );
			strcat( bspfilter, "*.pk3/maps/*.bsp" );
			bspfiles = FindQuakeFiles( bspfilter );
			for ( qf = bspfiles; qf; qf = qf->next ) if ( !qf->next ) {
					break;
				}
			if ( qf ) {
				qf->next = files;
			} else { bspfiles = files;}
			//get all the aas files
			strcpy( aasfilter, foldername );
			strcat( aasfilter, "maps/*.aas" );
			files = FindQuakeFiles( aasfilter );
			strcpy( aasfilter, foldername );
			strcat( aasfilter, "*.pk3/maps/*.aas" );
			aasfiles = FindQuakeFiles( aasfilter );
			for ( qf = aasfiles; qf; qf = qf->next ) if ( !qf->next ) {
					break;
				}
			if ( qf ) {
				qf->next = files;
			} else { aasfiles = files;}
			//
			for ( qf = bspfiles; qf; qf = qf->next )
			{
				sprintf( aasfile, "%s/%s", qf->pakfile, qf->origname );
				Log_Print( "found %s\n", aasfile );
				strcpy( &aasfile[strlen( aasfile ) - strlen( ".bsp" )], ".aas" );
				for ( qf2 = aasfiles; qf2; qf2 = qf2->next )
				{
					sprintf( buf, "%s/%s", qf2->pakfile, qf2->origname );
					if ( !stricmp( aasfile, buf ) ) {
						Log_Print( "found %s\n", buf );
						break;
					} //end if
				} //end for
			} //end for
		} //end if
#if defined( WIN32 ) | defined( _WIN32 )
		//find the next file
		done = !FindNextFile( handle, &filedata );
	} //end while
#else
	} //end for
	globfree( &globbuf );
#endif
} //end of the function CreateAASFilesForAllBSPFiles
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
quakefile_t *GetArgumentFiles( int argc, char *argv[], int *i, char *ext ) {
	quakefile_t *qfiles, *lastqf, *qf;
	int j;
	char buf[1024];

	qfiles = NULL;
	lastqf = NULL;
	for (; ( *i ) + 1 < argc && argv[( *i ) + 1][0] != '-'; ( *i )++ )
	{
		strcpy( buf, argv[( *i ) + 1] );
		for ( j = strlen( buf ) - 1; j >= strlen( buf ) - 4; j-- )
			if ( buf[j] == '.' ) {
				break;
			}
		if ( j >= strlen( buf ) - 4 ) {
			strcpy( &buf[j + 1], ext );
		}
		qf = FindQuakeFiles( buf );
		if ( !qf ) {
			continue;
		}
		if ( lastqf ) {
			lastqf->next = qf;
		} else { qfiles = qf;}
		lastqf = qf;
		while ( lastqf->next ) lastqf = lastqf->next;
	} //end for
	return qfiles;
} //end of the function GetArgumentFiles
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================

#define COMP_BSP2MAP        1
#define COMP_BSP2AAS        2
#define COMP_REACH          3
#define COMP_CLUSTER        4
#define COMP_AASOPTIMIZE    5
#define COMP_AASINFO        6
#define COMP_TETRA          7

//#define GTKRADIANT

#ifdef GTKRADIANT
// TTimo: comment this out and use a bogus RADIANT_VERSION if necessary
#include "include/version.h"
#endif

int main( int argc, char **argv ) {
	int i, comp = 0;
	char outputpath[MAX_PATH] = "";
	char filename[MAX_PATH] = "unknown";
	quakefile_t *qfiles = NULL, *qf; // TTimo: init

	// Ridah, allow to specify an extension for multiple AAS files per map
	int has_ext = 0;
	// done.

	// Ridah, set the world pointer up for reachabilities
	aasworld = aasworlds;
	AAS_SetWorldPointer( &( *aasworld ) );
	// done.

	myargc = argc;
	myargv = argv;

	Log_Open( "bspc.log" );        //open a log file
  #ifdef GTKRADIANT
	Log_Print( "BSPC version " BSPC_VERSION " by Mr Elusive\n" );
	Log_Print( "GtkRadiant " RADIANT_VERSION " %s\n", __DATE__ );
  #else
	Log_Print( "BSPC version " BSPC_VERSION ", %s %s by Mr Elusive\n", __DATE__, __TIME__ );
  #endif

	DefaultCfg();
	for ( i = 1; i < argc; i++ )
	{
		if ( !stricmp( argv[i],"-threads" ) ) {
			if ( i + 1 >= argc ) {
				i = 0; break;
			}
			numthreads = atoi( argv[++i] );
			Log_Print( "threads = %d\n", numthreads );
		} //end if
		else if ( !stricmp( argv[i], "-noverbose" ) ) {
			Log_Print( "verbose = false\n" );
			verbose = false;
		} //end else if
		else if ( !stricmp( argv[i], "-nocsg" ) ) {
			Log_Print( "nocsg = true\n" );
			nocsg = true;
		} //end else if
		else if ( !stricmp( argv[i], "-optimize" ) ) {
			Log_Print( "optimize = true\n" );
			optimize = true;
		} //end else if
		  /*
		  else if (!stricmp(argv[i],"-glview"))
		  {
			  glview = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-draw"))
		  {
			  Log_Print("drawflag = true\n");
			  drawflag = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-noweld"))
		  {
			  Log_Print("noweld = true\n");
			  noweld = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-noshare"))
		  {
			  Log_Print("noshare = true\n");
			  noshare = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-notjunc"))
		  {
			  Log_Print("notjunc = true\n");
			  notjunc = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-nowater"))
		  {
			  Log_Print("nowater = true\n");
			  nowater = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-noprune"))
		  {
			  Log_Print("noprune = true\n");
			  noprune = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-nomerge"))
		  {
			  Log_Print("nomerge = true\n");
			  nomerge = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-nosubdiv"))
		  {
			  Log_Print("nosubdiv = true\n");
			  nosubdiv = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-nodetail"))
		  {
			  Log_Print("nodetail = true\n");
			  nodetail = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-fulldetail"))
		  {
			  Log_Print("fulldetail = true\n");
			  fulldetail = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-onlyents"))
		  {
			  Log_Print("onlyents = true\n");
			  onlyents = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-micro"))
		  {
			  if (i + 1 >= argc) {i = 0; break;}
			  microvolume = atof(argv[++i]);
			  Log_Print("microvolume = %f\n", microvolume);
		  } //end else if
		  else if (!stricmp(argv[i], "-leaktest"))
		  {
			  Log_Print("leaktest = true\n");
			  leaktest = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-verboseentities"))
		  {
			  Log_Print("verboseentities = true\n");
			  verboseentities = true;
		  } //end else if
		  else if (!stricmp(argv[i], "-chop"))
		  {
			  if (i + 1 >= argc) {i = 0; break;}
			  subdivide_size = atof(argv[++i]);
			  Log_Print("subdivide_size = %f\n", subdivide_size);
		  } //end else if
		  else if (!stricmp (argv[i], "-tmpout"))
		  {
			  strcpy (outbase, "/tmp");
			  Log_Print("temp output\n");
		  } //end else if
		  */
#ifdef ME
		else if ( !stricmp( argv[i], "-freetree" ) ) {
			freetree = true;
			Log_Print( "freetree = true\n" );
		} //end else if
		else if ( !stricmp( argv[i], "-grapplereach" ) ) {
			calcgrapplereach = true;
			Log_Print( "grapplereach = true\n" );
		} //end else if
		else if ( !stricmp( argv[i], "-nobrushmerge" ) ) {
			nobrushmerge = true;
			Log_Print( "nobrushmerge = true\n" );
		} //end else if
		else if ( !stricmp( argv[i], "-noliquids" ) ) {
			noliquids = true;
			Log_Print( "noliquids = true\n" );
		} //end else if
		else if ( !stricmp( argv[i], "-forcesidesvisible" ) ) {
			forcesidesvisible = true;
			Log_Print( "forcesidesvisible = true\n" );
		} //end else if
		else if ( !stricmp( argv[i], "-output" ) ) {
			if ( i + 1 >= argc ) {
				i = 0; break;
			}
			if ( access( argv[i + 1], 0x04 ) ) {
				Warning( "the folder %s does not exist", argv[i + 1] );
			}
			strcpy( outputpath, argv[++i] );
		} //end else if
		else if ( !stricmp( argv[i], "-breadthfirst" ) ) {
			use_nodequeue = true;
			Log_Print( "breadthfirst = true\n" );
		} //end else if
		else if ( !stricmp( argv[i], "-cfg" ) ) {
			if ( i + 1 >= argc ) {
				i = 0; break;
			}
			if ( !LoadCfgFile( argv[++i] ) ) {
				exit( 0 );
			}
		} //end else if
		else if ( !stricmp( argv[i], "-bsp2map" ) ) {
			if ( i + 1 >= argc ) {
				i = 0; break;
			}
			comp = COMP_BSP2MAP;
			qfiles = GetArgumentFiles( argc, argv, &i, "bsp" );
		} //end else if
		else if ( !stricmp( argv[i], "-bsp2aas" ) ) {
			if ( i + 1 >= argc ) {
				i = 0; break;
			}
			comp = COMP_BSP2AAS;
			qfiles = GetArgumentFiles( argc, argv, &i, "bsp" );
		} //end else if
		else if ( !stricmp( argv[i], "-aasall" ) ) {
			if ( i + 1 >= argc ) {
				i = 0; break;
			}
			CreateAASFilesForAllBSPFiles( argv[++i] );
		} //end else if
		else if ( !stricmp( argv[i], "-reach" ) ) {
			if ( i + 1 >= argc ) {
				i = 0; break;
			}
			comp = COMP_REACH;
			qfiles = GetArgumentFiles( argc, argv, &i, "bsp" );
		} //end else if
		else if ( !stricmp( argv[i], "-cluster" ) ) {
			if ( i + 1 >= argc ) {
				i = 0; break;
			}
			comp = COMP_CLUSTER;
			qfiles = GetArgumentFiles( argc, argv, &i, "bsp" );
		} //end else if
		else if ( !stricmp( argv[i], "-aasinfo" ) ) {
			if ( i + 1 >= argc ) {
				i = 0; break;
			}
			comp = COMP_AASINFO;
			qfiles = GetArgumentFiles( argc, argv, &i, "aas" );
		} //end else if
		else if ( !stricmp( argv[i], "-aasopt" ) ) {
			if ( i + 1 >= argc ) {
				i = 0; break;
			}
			comp = COMP_AASOPTIMIZE;
			qfiles = GetArgumentFiles( argc, argv, &i, "aas" );
		} //end else if
		else if ( !stricmp( argv[i], "-tetra" ) ) {
			if ( i + 1 >= argc ) {
				i = 0; break;
			}
			comp = COMP_TETRA;
			qfiles = GetArgumentFiles( argc, argv, &i, "aas" );
		} //end else if
		else if ( !stricmp( argv[i], "-writeaasmap" ) ) {
			writeaasmap = true;
			Log_Print( "writeaasmap = true\n" );
		}
		// Ridah, allow to specify an extension for multiple AAS files per map
		else if ( !stricmp( argv[i], "-ext" ) ) {
			if ( i + 1 >= argc ) {
				i = 0; break;
			}
			strcpy( aas_extension, argv[++i] );
			has_ext = 1;

		} //end else if
		  // done.

#endif //ME
		else
		{
			Log_Print( "unknown parameter %s\n", argv[i] );
			break;
		} //end else
	} //end for

	//if there are parameters and there's no mismatch in one of the parameters
	if ( argc > 1 && i == argc ) {
		switch ( comp )
		{
		case COMP_BSP2MAP:
		{
			if ( !qfiles ) {
				Log_Print( "no files found\n" );
			}
			for ( qf = qfiles; qf; qf = qf->next )
			{
				//copy the output path
				strcpy( filename, outputpath );
				//append the bsp file base
				AppendPathSeperator( filename, MAX_PATH );
				ExtractFileBase( qf->origname, &filename[strlen( filename )] );
				//append .map
				strcat( filename, ".map" );
				//
				Log_Print( "bsp2map: %s to %s\n", qf->origname, filename );
				if ( qf->type != QFILETYPE_BSP ) {
					Warning( "%s is probably not a BSP file\n", qf->origname );
				}
				//
				LoadMapFromBSP( qf );
				//write the map file
				WriteMapFile( filename );
			}     //end for
			break;
		}     //end case
		case COMP_BSP2AAS:
		{
			if ( !qfiles ) {
				Log_Print( "no files found\n" );
			}
			for ( qf = qfiles; qf; qf = qf->next )
			{
				AASOuputFile( qf, outputpath, filename );
				//
				Log_Print( "bsp2aas: %s to %s\n", qf->origname, filename );
				if ( qf->type != QFILETYPE_BSP ) {
					Warning( "%s is probably not a BSP file\n", qf->origname );
				}
				//set before map loading
				create_aas = 1;
				LoadMapFromBSP( qf );
				//create the AAS file
				AAS_Create( filename );
				//if it's a Quake3 map calculate the reachabilities and clusters
				if ( loadedmaptype == MAPTYPE_QUAKE3 ) {
					AAS_CalcReachAndClusters( qf );
				}
				//
				if ( optimize ) {
					AAS_Optimize();
				}
				//
				//write out the stored AAS file
				if ( !AAS_WriteAASFile( filename ) ) {
					Error( "error writing %s\n", filename );
				}     //end if
					  //deallocate memory
				AAS_FreeMaxAAS();
			}     //end for
			break;
		}     //end case
		case COMP_REACH:
		{
			if ( !qfiles ) {
				Log_Print( "no files found\n" );
			}
			for ( qf = qfiles; qf; qf = qf->next )
			{
				AASOuputFile( qf, outputpath, filename );
				//
				Log_Print( "reach: %s to %s\n", qf->origname, filename );
				if ( qf->type != QFILETYPE_BSP ) {
					Warning( "%s is probably not a BSP file\n", qf->origname );
				}
				//if the AAS file exists in the output directory
				if ( !access( filename, 0x04 ) ) {
					if ( !AAS_LoadAASFile( filename, 0, 0 ) ) {
						Error( "error loading aas file %s\n", filename );
					}     //end if
						  //assume it's a Quake3 BSP file
					loadedmaptype = MAPTYPE_QUAKE3;
				}     //end if
				else
				{
					Warning( "AAS file %s not found in output folder\n", filename );
					Log_Print( "creating %s...\n", filename );
					//set before map loading
					create_aas = 1;
					LoadMapFromBSP( qf );
					//create the AAS file
					AAS_Create( filename );
				}     //end else
					  //if it's a Quake3 map calculate the reachabilities and clusters
				if ( loadedmaptype == MAPTYPE_QUAKE3 ) {
					AAS_CalcReachAndClusters( qf );
				}     //end if
					  //
				if ( optimize ) {
					AAS_Optimize();
				}
				//write out the stored AAS file
				if ( !AAS_WriteAASFile( filename ) ) {
					Error( "error writing %s\n", filename );
				}     //end if
					  //deallocate memory
				AAS_FreeMaxAAS();
			}     //end for
			break;
		}     //end case
		case COMP_CLUSTER:
		{
			if ( !qfiles ) {
				Log_Print( "no files found\n" );
			}
			for ( qf = qfiles; qf; qf = qf->next )
			{
				AASOuputFile( qf, outputpath, filename );
				//
				Log_Print( "cluster: %s to %s\n", qf->origname, filename );
				if ( qf->type != QFILETYPE_BSP ) {
					Warning( "%s is probably not a BSP file\n", qf->origname );
				}
				//if the AAS file exists in the output directory
				if ( !access( filename, 0x04 ) ) {
					if ( !AAS_LoadAASFile( filename, 0, 0 ) ) {
						Error( "error loading aas file %s\n", filename );
					}     //end if
						  //assume it's a Quake3 BSP file
					loadedmaptype = MAPTYPE_QUAKE3;
					//if it's a Quake3 map calculate the clusters
					if ( loadedmaptype == MAPTYPE_QUAKE3 ) {
						( *aasworld ).numclusters = 0;
						AAS_InitBotImport();
						AAS_InitClustering();
					}     //end if
				}     //end if
				else
				{
					Warning( "AAS file %s not found in output folder\n", filename );
					Log_Print( "creating %s...\n", filename );
					//set before map loading
					create_aas = 1;
					LoadMapFromBSP( qf );
					//create the AAS file
					AAS_Create( filename );
					//if it's a Quake3 map calculate the reachabilities and clusters
					if ( loadedmaptype == MAPTYPE_QUAKE3 ) {
						AAS_CalcReachAndClusters( qf );
					}
				}     //end else
					  //
				if ( optimize ) {
					AAS_Optimize();
				}
				//write out the stored AAS file
				if ( !AAS_WriteAASFile( filename ) ) {
					Error( "error writing %s\n", filename );
				}     //end if
					  //deallocate memory
				AAS_FreeMaxAAS();
			}     //end for
			break;
		}     //end case
		case COMP_AASOPTIMIZE:
		{
			if ( !qfiles ) {
				Log_Print( "no files found\n" );
			}
			for ( qf = qfiles; qf; qf = qf->next )
			{
				AASOuputFile( qf, outputpath, filename );
				//
				Log_Print( "optimizing: %s to %s\n", qf->origname, filename );
				if ( qf->type != QFILETYPE_AAS ) {
					Warning( "%s is probably not a AAS file\n", qf->origname );
				}
				//
				AAS_InitBotImport();
				//
				if ( !AAS_LoadAASFile( qf->filename, qf->offset, qf->length ) ) {
					Error( "error loading aas file %s\n", qf->filename );
				}     //end if
				AAS_Optimize();
				//write out the stored AAS file
				if ( !AAS_WriteAASFile( filename ) ) {
					Error( "error writing %s\n", filename );
				}     //end if
					  //deallocate memory
				AAS_FreeMaxAAS();
			}     //end for
			break;
		}     //end case
		case COMP_AASINFO:
		{
			if ( !qfiles ) {
				Log_Print( "no files found\n" );
			}
			for ( qf = qfiles; qf; qf = qf->next )
			{
				AASOuputFile( qf, outputpath, filename );
				//
				Log_Print( "aas info for: %s\n", filename );
				if ( qf->type != QFILETYPE_AAS ) {
					Warning( "%s is probably not a AAS file\n", qf->origname );
				}
				//
				AAS_InitBotImport();
				//
				if ( !AAS_LoadAASFile( qf->filename, qf->offset, qf->length ) ) {
					Error( "error loading aas file %s\n", qf->filename );
				}     //end if
				AAS_ShowTotals();
			}     //end for
		}     //end case
		case COMP_TETRA:
		{
			if ( !qfiles ) {
				Log_Print( "no files found\n" );
			}
			for ( qf = qfiles; qf; qf = qf->next )
			{
				//TH_AASToTetrahedrons(qf->filename);
			}     //end for
			break;
		}     //end case
		default:
		{
			Log_Print( "don't know what to do\n" );
			break;
		}     //end default
		} //end switch
	} //end if
	else
	{
		Log_Print( "Usage:   bspc [-<switch> [-<switch> ...]]\n"
#if defined( WIN32 ) || defined( _WIN32 )
				   "Example 1: bspc -bsp2aas d:\\quake3\\baseq3\\maps\\mymap?.bsp\n"
				   "Example 2: bspc -bsp2aas d:\\quake3\\baseq3\\pak0.pk3\\maps/q3dm*.bsp\n"
#else
				   "Example 1: bspc -bsp2aas /quake3/baseq3/maps/mymap?.bsp\n"
				   "Example 2: bspc -bsp2aas /quake3/baseq3/pak0.pk3/maps/q3dm*.bsp\n"
#endif
				   "\n"
				   "Switches:\n"
				   //"   bsp2map  <[pakfilter/]filter.bsp>    = convert BSP to MAP\n"
				   "   bsp2aas  <[pakfilter/]filter.bsp>    = convert BSP to AAS\n"
				   //"   aasall   <quake3folder>              = create AAS files for all BSPs\n"
				   "   reach    <filter.bsp>                = compute reachability & clusters\n"
				   "   cluster  <filter.aas>                = compute clusters\n"
				   "   aasopt   <filter.aas>                = optimize aas file\n"
				   //"   tetra    <filter.aas>                = tetrahedral decomposition\n"
				   "   output   <output path>               = set output path\n"
				   "   threads  <X>                         = set number of threads to X\n"
				   "   cfg      <filename>                  = use this cfg file\n"
				   "   optimize                             = enable optimization\n"
				   "   noverbose                            = disable verbose output\n"
				   "   breadthfirst                         = breadth first bsp building\n"
				   "   nobrushmerge                         = don't merge brushes\n"
				   "   noliquids                            = don't write liquids to map\n"
				   "   freetree                             = free the bsp tree\n"
				   "   nocsg                                = disables brush chopping\n"
				   "   forcesidesvisible                    = force all sides to be visible\n"
				   "   grapplereach                         = calculate grapple reachabilities\n"
				   "   writeaasmap                          = write the map the AI sees\n"

/*			"   glview     = output a GL view\n"
			"   draw       = enables drawing\n"
			"   noweld     = disables weld\n"
			"   noshare    = disables sharing\n"
			"   notjunc    = disables juncs\n"
			"   nowater    = disables water brushes\n"
			"   noprune    = disables node prunes\n"
			"   nomerge    = disables face merging\n"
			"   nosubdiv   = disables subdeviding\n"
			"   nodetail   = disables detail brushes\n"
			"   fulldetail = enables full detail\n"
			"   onlyents   = only compile entities with bsp\n"
			"   micro <volume>\n"
			"              = sets the micro volume to the given float\n"
			"   leaktest   = perform a leak test\n"
			"   verboseentities\n"
			"              = enable entity verbose mode\n"
			"   chop <subdivide_size>\n"
			"              = sets the subdivide size to the given float\n"*/
				   "\n" );
	} //end else
	Log_Close();                        //close the log file
	return 0;
} //end of the function main


