-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
-\r
-----------------------------------------------------------------------------------\r
-\r
-This code has been altered significantly from its original form, to support\r
-several games based on the Quake III Arena engine, in the form of "Q3Map2."\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-\r
-\r
-/* marker */\r
-#define BSPFILE_ABSTRACT_C\r
-\r
-\r
-\r
-/* dependencies */\r
-#include "q3map2.h"\r
-\r
-\r
-\r
-\r
-/* -------------------------------------------------------------------------------\r
-\r
-this file was copied out of the common directory in order to not break\r
-compatibility with the q3map 1.x tree. it was moved out in order to support\r
-the raven bsp format (RBSP) used in soldier of fortune 2 and jedi knight 2.\r
-\r
-since each game has its own set of particular features, the data structures\r
-below no longer directly correspond to the binary format of a particular game.\r
-\r
-the translation will be done at bsp load/save time to keep any sort of\r
-special-case code messiness out of the rest of the program.\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-\r
-\r
-/* FIXME: remove the functions below that handle memory management of bsp file chunks */\r
-\r
-int numBSPDrawVertsBuffer = 0;\r
-void IncDrawVerts()\r
-{\r
- numBSPDrawVerts++;\r
-\r
- if(bspDrawVerts == 0)\r
- {\r
- numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS / 37;\r
- \r
- bspDrawVerts = safe_malloc_info(sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer, "IncDrawVerts");\r
-\r
- }\r
- else if(numBSPDrawVerts > numBSPDrawVertsBuffer)\r
- {\r
- numBSPDrawVertsBuffer *= 3; // multiply by 1.5\r
- numBSPDrawVertsBuffer /= 2;\r
-\r
- if(numBSPDrawVertsBuffer > MAX_MAP_DRAW_VERTS)\r
- numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS;\r
-\r
- bspDrawVerts = realloc(bspDrawVerts, sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer);\r
-\r
- if(!bspDrawVerts)\r
- Error( "realloc() failed (IncDrawVerts)");\r
- }\r
-\r
- memset(bspDrawVerts + (numBSPDrawVerts - 1), 0, sizeof(bspDrawVert_t));\r
-}\r
-\r
-void SetDrawVerts(int n)\r
-{\r
- if(bspDrawVerts != 0)\r
- free(bspDrawVerts);\r
-\r
- numBSPDrawVerts = n;\r
- numBSPDrawVertsBuffer = numBSPDrawVerts;\r
-\r
- bspDrawVerts = safe_malloc_info(sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer, "IncDrawVerts");\r
-\r
- memset(bspDrawVerts, 0, n * sizeof(bspDrawVert_t));\r
-}\r
-\r
-int numBSPDrawSurfacesBuffer = 0;\r
-void SetDrawSurfacesBuffer()\r
-{\r
- if(bspDrawSurfaces != 0)\r
- free(bspDrawSurfaces);\r
-\r
- numBSPDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;\r
-\r
- bspDrawSurfaces = safe_malloc_info(sizeof(bspDrawSurface_t) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces");\r
-\r
- memset(bspDrawSurfaces, 0, MAX_MAP_DRAW_SURFS * sizeof(bspDrawVert_t));\r
-}\r
-\r
-void SetDrawSurfaces(int n)\r
-{\r
- if(bspDrawSurfaces != 0)\r
- free(bspDrawSurfaces);\r
-\r
- numBSPDrawSurfaces = n;\r
- numBSPDrawSurfacesBuffer = numBSPDrawSurfaces;\r
-\r
- bspDrawSurfaces = safe_malloc_info(sizeof(bspDrawSurface_t) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces");\r
-\r
- memset(bspDrawSurfaces, 0, n * sizeof(bspDrawVert_t));\r
-}\r
-\r
-void BSPFilesCleanup()\r
-{\r
- if(bspDrawVerts != 0)\r
- free(bspDrawVerts);\r
- if(bspDrawSurfaces != 0)\r
- free(bspDrawSurfaces);\r
- if(bspLightBytes != 0)\r
- free(bspLightBytes);\r
- if(bspGridPoints != 0)\r
- free(bspGridPoints);\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-/*\r
-SwapBlock()\r
-if all values are 32 bits, this can be used to swap everything\r
-*/\r
-\r
-void SwapBlock( int *block, int size )\r
-{\r
- int i;\r
- \r
- \r
- /* dummy check */\r
- if( block == NULL )\r
- return;\r
- \r
- /* swap */\r
- size >>= 2;\r
- for( i = 0; i < size; i++ )\r
- block[ i ] = LittleLong( block[ i ] );\r
-}\r
-\r
-\r
-\r
-/*\r
-SwapBSPFile()\r
-byte swaps all data in the abstract bsp\r
-*/\r
-\r
-void SwapBSPFile( void )\r
-{\r
- int i, j;\r
- \r
- \r
- /* models */\r
- SwapBlock( (int*) bspModels, numBSPModels * sizeof( bspModels[ 0 ] ) );\r
-\r
- /* shaders (don't swap the name) */\r
- for( i = 0; i < numBSPShaders ; i++ )\r
- {\r
- bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );\r
- bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );\r
- }\r
-\r
- /* planes */\r
- SwapBlock( (int*) bspPlanes, numBSPPlanes * sizeof( bspPlanes[ 0 ] ) );\r
- \r
- /* nodes */\r
- SwapBlock( (int*) bspNodes, numBSPNodes * sizeof( bspNodes[ 0 ] ) );\r
-\r
- /* leafs */\r
- SwapBlock( (int*) bspLeafs, numBSPLeafs * sizeof( bspLeafs[ 0 ] ) );\r
-\r
- /* leaffaces */\r
- SwapBlock( (int*) bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );\r
-\r
- /* leafbrushes */\r
- SwapBlock( (int*) bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );\r
-\r
- // brushes\r
- SwapBlock( (int*) bspBrushes, numBSPBrushes * sizeof( bspBrushes[ 0 ] ) );\r
-\r
- // brushsides\r
- SwapBlock( (int*) bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );\r
-\r
- // vis\r
- ((int*) &bspVisBytes)[ 0 ] = LittleLong( ((int*) &bspVisBytes)[ 0 ] );\r
- ((int*) &bspVisBytes)[ 1 ] = LittleLong( ((int*) &bspVisBytes)[ 1 ] );\r
-\r
- /* drawverts (don't swap colors) */\r
- for( i = 0; i < numBSPDrawVerts; i++ )\r
- {\r
- bspDrawVerts[ i ].xyz[ 0 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 0 ] );\r
- bspDrawVerts[ i ].xyz[ 1 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 1 ] );\r
- bspDrawVerts[ i ].xyz[ 2 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 2 ] );\r
- bspDrawVerts[ i ].normal[ 0 ] = LittleFloat( bspDrawVerts[ i ].normal[ 0 ] );\r
- bspDrawVerts[ i ].normal[ 1 ] = LittleFloat( bspDrawVerts[ i ].normal[ 1 ] );\r
- bspDrawVerts[ i ].normal[ 2 ] = LittleFloat( bspDrawVerts[ i ].normal[ 2 ] );\r
- bspDrawVerts[ i ].st[ 0 ] = LittleFloat( bspDrawVerts[ i ].st[ 0 ] );\r
- bspDrawVerts[ i ].st[ 1 ] = LittleFloat( bspDrawVerts[ i ].st[ 1 ] );\r
- for( j = 0; j < MAX_LIGHTMAPS; j++ )\r
- {\r
- bspDrawVerts[ i ].lightmap[ j ][ 0 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 0 ] );\r
- bspDrawVerts[ i ].lightmap[ j ][ 1 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 1 ] );\r
- }\r
- }\r
- \r
- /* drawindexes */\r
- SwapBlock( (int*) bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[0] ) );\r
-\r
- /* drawsurfs */\r
- /* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */\r
- SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );\r
-\r
- /* fogs */\r
- for( i = 0; i < numBSPFogs; i++ )\r
- {\r
- bspFogs[ i ].brushNum = LittleLong( bspFogs[ i ].brushNum );\r
- bspFogs[ i ].visibleSide = LittleLong( bspFogs[ i ].visibleSide );\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-GetLumpElements()\r
-gets the number of elements in a bsp lump\r
-*/\r
-\r
-int GetLumpElements( bspHeader_t *header, int lump, int size )\r
-{\r
- /* check for odd size */\r
- if( header->lumps[ lump ].length % size )\r
- {\r
- if( force )\r
- {\r
- Sys_Printf( "WARNING: GetLumpElements: odd lump size (%d) in lump %d\n", header->lumps[ lump ].length, lump );\r
- return 0;\r
- }\r
- else\r
- Error( "GetLumpElements: odd lump size (%d) in lump %d", header->lumps[ lump ].length, lump );\r
- }\r
- \r
- /* return element count */\r
- return header->lumps[ lump ].length / size;\r
-}\r
-\r
-\r
-\r
-/*\r
-GetLump()\r
-returns a pointer to the specified lump\r
-*/\r
-\r
-void *GetLump( bspHeader_t *header, int lump )\r
-{\r
- return (void*)( (byte*) header + header->lumps[ lump ].offset);\r
-}\r
-\r
-\r
-\r
-/*\r
-CopyLump()\r
-copies a bsp file lump into a destination buffer\r
-*/\r
-\r
-int CopyLump( bspHeader_t *header, int lump, void *dest, int size )\r
-{\r
- int length, offset;\r
- \r
- \r
- /* get lump length and offset */\r
- length = header->lumps[ lump ].length;\r
- offset = header->lumps[ lump ].offset;\r
- \r
- /* handle erroneous cases */\r
- if( length == 0 )\r
- return 0;\r
- if( length % size )\r
- {\r
- if( force )\r
- {\r
- Sys_Printf( "WARNING: CopyLump: odd lump size (%d) in lump %d\n", length, lump );\r
- return 0;\r
- }\r
- else\r
- Error( "CopyLump: odd lump size (%d) in lump %d", length, lump );\r
- }\r
- \r
- /* copy block of memory and return */\r
- memcpy( dest, (byte*) header + offset, length );\r
- return length / size;\r
-}\r
-\r
-\r
-\r
-/*\r
-AddLump()\r
-adds a lump to an outgoing bsp file\r
-*/\r
-\r
-void AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length )\r
-{\r
- bspLump_t *lump;\r
- \r
- \r
- /* add lump to bsp file header */\r
- lump = &header->lumps[ lumpNum ];\r
- lump->offset = LittleLong( ftell( file ) );\r
- lump->length = LittleLong( length );\r
- \r
- /* write lump to file */\r
- SafeWrite( file, data, (length + 3) & ~3 );\r
-}\r
-\r
-\r
-\r
-/*\r
-LoadBSPFile()\r
-loads a bsp file into memory\r
-*/\r
-\r
-void LoadBSPFile( const char *filename )\r
-{\r
- /* dummy check */\r
- if( game == NULL || game->load == NULL )\r
- Error( "LoadBSPFile: unsupported BSP file format" );\r
- \r
- /* load it, then byte swap the in-memory version */\r
- game->load( filename );\r
- SwapBSPFile();\r
-}\r
-\r
-\r
-\r
-/*\r
-WriteBSPFile()\r
-writes a bsp file\r
-*/\r
-\r
-void WriteBSPFile( const char *filename )\r
-{\r
- char tempname[ 1024 ];\r
- time_t tm;\r
- \r
- \r
- /* dummy check */\r
- if( game == NULL || game->write == NULL )\r
- Error( "WriteBSPFile: unsupported BSP file format" );\r
- \r
- /* make fake temp name so existing bsp file isn't damaged in case write process fails */\r
- time( &tm );\r
- sprintf( tempname, "%s.%08X", filename, (int) tm );\r
- \r
- /* byteswap, write the bsp, then swap back so it can be manipulated further */\r
- SwapBSPFile();\r
- game->write( tempname );\r
- SwapBSPFile();\r
- \r
- /* replace existing bsp file */\r
- remove( filename );\r
- rename( tempname, filename );\r
-}\r
-\r
-\r
-\r
-/*\r
-PrintBSPFileSizes()\r
-dumps info about current file\r
-*/\r
-\r
-void PrintBSPFileSizes( void )\r
-{\r
- /* parse entities first */\r
- if( numEntities <= 0 )\r
- ParseEntities();\r
- \r
- /* note that this is abstracted */\r
- Sys_Printf( "Abstracted BSP file components (*actual sizes may differ)\n" );\r
- \r
- /* print various and sundry bits */\r
- Sys_Printf( "%9d models %9d\n",\r
- numBSPModels, (int) (numBSPModels * sizeof( bspModel_t )) );\r
- Sys_Printf( "%9d shaders %9d\n",\r
- numBSPShaders, (int) (numBSPShaders * sizeof( bspShader_t )) );\r
- Sys_Printf( "%9d brushes %9d\n",\r
- numBSPBrushes, (int) (numBSPBrushes * sizeof( bspBrush_t )) );\r
- Sys_Printf( "%9d brushsides %9d *\n",\r
- numBSPBrushSides, (int) (numBSPBrushSides * sizeof( bspBrushSide_t )) );\r
- Sys_Printf( "%9d fogs %9d\n",\r
- numBSPFogs, (int) (numBSPFogs * sizeof( bspFog_t ) ) );\r
- Sys_Printf( "%9d planes %9d\n",\r
- numBSPPlanes, (int) (numBSPPlanes * sizeof( bspPlane_t )) );\r
- Sys_Printf( "%9d entdata %9d\n",\r
- numEntities, bspEntDataSize );\r
- Sys_Printf( "\n");\r
- \r
- Sys_Printf( "%9d nodes %9d\n",\r
- numBSPNodes, (int) (numBSPNodes * sizeof( bspNode_t)) );\r
- Sys_Printf( "%9d leafs %9d\n",\r
- numBSPLeafs, (int) (numBSPLeafs * sizeof( bspLeaf_t )) );\r
- Sys_Printf( "%9d leafsurfaces %9d\n",\r
- numBSPLeafSurfaces, (int) (numBSPLeafSurfaces * sizeof( *bspLeafSurfaces )) );\r
- Sys_Printf( "%9d leafbrushes %9d\n",\r
- numBSPLeafBrushes, (int) (numBSPLeafBrushes * sizeof( *bspLeafBrushes )) );\r
- Sys_Printf( "\n");\r
- \r
- Sys_Printf( "%9d drawsurfaces %9d *\n",\r
- numBSPDrawSurfaces, (int) (numBSPDrawSurfaces * sizeof( *bspDrawSurfaces )) );\r
- Sys_Printf( "%9d drawverts %9d *\n",\r
- numBSPDrawVerts, (int) (numBSPDrawVerts * sizeof( *bspDrawVerts )) );\r
- Sys_Printf( "%9d drawindexes %9d\n",\r
- numBSPDrawIndexes, (int) (numBSPDrawIndexes * sizeof( *bspDrawIndexes )) );\r
- Sys_Printf( "\n");\r
- \r
- Sys_Printf( "%9d lightmaps %9d\n",\r
- numBSPLightBytes / (LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3), numBSPLightBytes );\r
- Sys_Printf( "%9d lightgrid %9d *\n",\r
- numBSPGridPoints, (int) (numBSPGridPoints * sizeof( *bspGridPoints )) );\r
- Sys_Printf( " visibility %9d\n",\r
- numBSPVisBytes );\r
-}\r
-\r
-\r
-\r
-/* -------------------------------------------------------------------------------\r
-\r
-entity data handling\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-\r
-/*\r
-StripTrailing()\r
-strips low byte chars off the end of a string\r
-*/\r
-\r
-void StripTrailing( char *e )\r
-{\r
- char *s;\r
- \r
- \r
- s = e + strlen( e ) - 1;\r
- while( s >= e && *s <= 32 )\r
- {\r
- *s = 0;\r
- s--;\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-ParseEpair()\r
-parses a single quoted "key" "value" pair into an epair struct\r
-*/\r
-\r
-epair_t *ParseEPair( void )\r
-{\r
- epair_t *e;\r
- \r
- \r
- /* allocate and clear new epair */\r
- e = safe_malloc( sizeof( epair_t ) );\r
- memset( e, 0, sizeof( epair_t ) );\r
- \r
- /* handle key */\r
- if( strlen( token ) >= (MAX_KEY - 1) )\r
- Error( "ParseEPair: token too long" );\r
- \r
- e->key = copystring( token );\r
- GetToken( qfalse );\r
- \r
- /* handle value */\r
- if( strlen( token ) >= MAX_VALUE - 1 )\r
- Error( "ParseEpar: token too long" );\r
- e->value = copystring( token );\r
- \r
- /* strip trailing spaces that sometimes get accidentally added in the editor */\r
- StripTrailing( e->key );\r
- StripTrailing( e->value );\r
- \r
- /* return it */\r
- return e;\r
-}\r
-\r
-\r
-\r
-/*\r
-ParseEntity()\r
-parses an entity's epairs\r
-*/\r
-\r
-qboolean ParseEntity( void )\r
-{\r
- epair_t *e;\r
- \r
- \r
- /* dummy check */\r
- if( !GetToken( qtrue ) )\r
- return qfalse;\r
- if( strcmp( token, "{" ) )\r
- Error( "ParseEntity: { not found" );\r
- if( numEntities == MAX_MAP_ENTITIES )\r
- Error( "numEntities == MAX_MAP_ENTITIES" );\r
- \r
- /* create new entity */\r
- mapEnt = &entities[ numEntities ];\r
- numEntities++;\r
- \r
- /* parse */\r
- while( 1 )\r
- {\r
- if( !GetToken( qtrue ) )\r
- Error( "ParseEntity: EOF without closing brace" );\r
- if( !EPAIR_STRCMP( token, "}" ) )\r
- break;\r
- e = ParseEPair();\r
- e->next = mapEnt->epairs;\r
- mapEnt->epairs = e;\r
- }\r
- \r
- /* return to sender */\r
- return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-ParseEntities()\r
-parses the bsp entity data string into entities\r
-*/\r
-\r
-void ParseEntities( void )\r
-{\r
- numEntities = 0;\r
- ParseFromMemory( bspEntData, bspEntDataSize );\r
- while( ParseEntity() );\r
- \r
- /* ydnar: set number of bsp entities in case a map is loaded on top */\r
- numBSPEntities = numEntities;\r
-}\r
-\r
-\r
-\r
-/*\r
-UnparseEntities()\r
-generates the dentdata string from all the entities.\r
-this allows the utilities to add or remove key/value\r
-pairs to the data created by the map editor\r
-*/\r
-\r
-void UnparseEntities( void )\r
-{\r
- int i;\r
- char *buf, *end;\r
- epair_t *ep;\r
- char line[ 2048 ];\r
- char key[ 1024 ], value[ 1024 ];\r
- const char *value2;\r
- \r
- \r
- /* setup */\r
- buf = bspEntData;\r
- end = buf;\r
- *end = 0;\r
- \r
- /* run through entity list */\r
- for( i = 0; i < numBSPEntities && i < numEntities; i++ )\r
- {\r
- /* get epair */\r
- ep = entities[ i ].epairs;\r
- if( ep == NULL )\r
- continue; /* ent got removed */\r
- \r
- /* ydnar: certain entities get stripped from bsp file */\r
- value2 = ValueForKey( &entities[ i ], "classname" );\r
- if( !Q_stricmp( value2, "misc_model" ) ||\r
- !Q_stricmp( value2, "_decal" ) ||\r
- !Q_stricmp( value2, "_skybox" ) )\r
- continue;\r
- \r
- /* add beginning brace */\r
- strcat( end, "{\n" );\r
- end += 2;\r
- \r
- /* walk epair list */\r
- for( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )\r
- {\r
- /* copy and clean */\r
- strcpy( key, ep->key );\r
- StripTrailing( key );\r
- strcpy( value, ep->value );\r
- StripTrailing( value );\r
- \r
- /* add to buffer */\r
- sprintf( line, "\"%s\" \"%s\"\n", key, value );\r
- strcat( end, line );\r
- end += strlen( line );\r
- }\r
- \r
- /* add trailing brace */\r
- strcat( end,"}\n" );\r
- end += 2;\r
- \r
- /* check for overflow */\r
- if( end > buf + MAX_MAP_ENTSTRING )\r
- Error( "Entity text too long" );\r
- }\r
- \r
- /* set size */\r
- bspEntDataSize = end - buf + 1;\r
-}\r
-\r
-\r
-\r
-/*\r
-PrintEntity()\r
-prints an entity's epairs to the console\r
-*/\r
-\r
-void PrintEntity( const entity_t *ent )\r
-{\r
- epair_t *ep;\r
- \r
-\r
- Sys_Printf( "------- entity %p -------\n", ent );\r
- for( ep = ent->epairs; ep != NULL; ep = ep->next )\r
- Sys_Printf( "%s = %s\n", ep->key, ep->value );\r
-\r
-}\r
-\r
-\r
-\r
-/*\r
-SetKeyValue()\r
-sets an epair in an entity\r
-*/\r
-\r
-void SetKeyValue( entity_t *ent, const char *key, const char *value )\r
-{\r
- epair_t *ep;\r
- \r
- \r
- /* check for existing epair */\r
- for( ep = ent->epairs; ep != NULL; ep = ep->next )\r
- {\r
- if( !EPAIR_STRCMP( ep->key, key ) )\r
- {\r
- free( ep->value );\r
- ep->value = copystring( value );\r
- return;\r
- }\r
- }\r
- \r
- /* create new epair */\r
- ep = safe_malloc( sizeof( *ep ) );\r
- ep->next = ent->epairs;\r
- ent->epairs = ep;\r
- ep->key = copystring( key );\r
- ep->value = copystring( value );\r
-}\r
-\r
-\r
-\r
-/*\r
-ValueForKey()\r
-gets the value for an entity key\r
-*/\r
-\r
-const char *ValueForKey( const entity_t *ent, const char *key )\r
-{\r
- epair_t *ep;\r
- \r
- \r
- /* dummy check */\r
- if( ent == NULL )\r
- return "";\r
- \r
- /* walk epair list */\r
- for( ep = ent->epairs; ep != NULL; ep = ep->next )\r
- {\r
- if( !EPAIR_STRCMP( ep->key, key ) )\r
- return ep->value;\r
- }\r
- \r
- /* if no match, return empty string */\r
- return "";\r
-}\r
-\r
-\r
-\r
-/*\r
-IntForKey()\r
-gets the integer point value for an entity key\r
-*/\r
-\r
-int IntForKey( const entity_t *ent, const char *key )\r
-{\r
- const char *k;\r
- \r
- \r
- k = ValueForKey( ent, key );\r
- return atoi( k );\r
-}\r
-\r
-\r
-\r
-/*\r
-FloatForKey()\r
-gets the floating point value for an entity key\r
-*/\r
-\r
-vec_t FloatForKey( const entity_t *ent, const char *key )\r
-{\r
- const char *k;\r
- \r
- \r
- k = ValueForKey( ent, key );\r
- return atof( k );\r
-}\r
-\r
-\r
-\r
-/*\r
-GetVectorForKey()\r
-gets a 3-element vector value for an entity key\r
-*/\r
-\r
-void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec )\r
-{\r
- const char *k;\r
- double v1, v2, v3;\r
- \r
-\r
- /* get value */\r
- k = ValueForKey( ent, key );\r
- \r
- /* scanf into doubles, then assign, so it is vec_t size independent */\r
- v1 = v2 = v3 = 0.0;\r
- sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );\r
- vec[ 0 ] = v1;\r
- vec[ 1 ] = v2;\r
- vec[ 2 ] = v3;\r
-}\r
-\r
-\r
-\r
-/*\r
-FindTargetEntity()\r
-finds an entity target\r
-*/\r
-\r
-entity_t *FindTargetEntity( const char *target )\r
-{\r
- int i;\r
- const char *n;\r
-\r
- \r
- /* walk entity list */\r
- for( i = 0; i < numEntities; i++ )\r
- {\r
- n = ValueForKey( &entities[ i ], "targetname" );\r
- if ( !strcmp( n, target ) )\r
- return &entities[ i ];\r
- }\r
- \r
- /* nada */\r
- return NULL;\r
-}\r
-\r
-\r
-\r
-/*\r
-GetEntityShadowFlags() - ydnar\r
-gets an entity's shadow flags\r
-note: does not set them to defaults if the keys are not found!\r
-*/\r
-\r
-void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows )\r
-{\r
- const char *value;\r
- \r
- \r
- /* get cast shadows */\r
- if( castShadows != NULL )\r
- {\r
- value = ValueForKey( ent, "_castShadows" );\r
- if( value[ 0 ] == '\0' )\r
- value = ValueForKey( ent, "_cs" );\r
- if( value[ 0 ] == '\0' )\r
- value = ValueForKey( ent2, "_castShadows" );\r
- if( value[ 0 ] == '\0' )\r
- value = ValueForKey( ent2, "_cs" );\r
- if( value[ 0 ] != '\0' )\r
- *castShadows = atoi( value );\r
- }\r
- \r
- /* receive */\r
- if( recvShadows != NULL )\r
- {\r
- value = ValueForKey( ent, "_receiveShadows" );\r
- if( value[ 0 ] == '\0' )\r
- value = ValueForKey( ent, "_rs" );\r
- if( value[ 0 ] == '\0' )\r
- value = ValueForKey( ent2, "_receiveShadows" );\r
- if( value[ 0 ] == '\0' )\r
- value = ValueForKey( ent2, "_rs" );\r
- if( value[ 0 ] != '\0' )\r
- *recvShadows = atoi( value );\r
- }\r
-}\r
-\r
+/* -------------------------------------------------------------------------------
+
+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_ABSTRACT_C
+
+
+
+/* dependencies */
+#include "q3map2.h"
+
+
+
+
+/* -------------------------------------------------------------------------------
+
+this file was copied out of the common directory in order to not break
+compatibility with the q3map 1.x tree. it was moved out in order to support
+the raven bsp format (RBSP) used in soldier of fortune 2 and jedi knight 2.
+
+since each game has its own set of particular features, the data structures
+below no longer directly correspond to the binary format of a particular game.
+
+the translation will be done at bsp load/save time to keep any sort of
+special-case code messiness out of the rest of the program.
+
+------------------------------------------------------------------------------- */
+
+
+
+/* FIXME: remove the functions below that handle memory management of bsp file chunks */
+
+int numBSPDrawVertsBuffer = 0;
+void IncDrawVerts()
+{
+ numBSPDrawVerts++;
+
+ if(bspDrawVerts == 0)
+ {
+ numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS / 37;
+
+ bspDrawVerts = safe_malloc_info(sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer, "IncDrawVerts");
+
+ }
+ else if(numBSPDrawVerts > numBSPDrawVertsBuffer)
+ {
+ numBSPDrawVertsBuffer *= 3; // multiply by 1.5
+ numBSPDrawVertsBuffer /= 2;
+
+ if(numBSPDrawVertsBuffer > MAX_MAP_DRAW_VERTS)
+ numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS;
+
+ bspDrawVerts = realloc(bspDrawVerts, sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer);
+
+ if(!bspDrawVerts)
+ Error( "realloc() failed (IncDrawVerts)");
+ }
+
+ memset(bspDrawVerts + (numBSPDrawVerts - 1), 0, sizeof(bspDrawVert_t));
+}
+
+void SetDrawVerts(int n)
+{
+ if(bspDrawVerts != 0)
+ free(bspDrawVerts);
+
+ numBSPDrawVerts = n;
+ numBSPDrawVertsBuffer = numBSPDrawVerts;
+
+ bspDrawVerts = safe_malloc_info(sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer, "IncDrawVerts");
+
+ memset(bspDrawVerts, 0, n * sizeof(bspDrawVert_t));
+}
+
+int numBSPDrawSurfacesBuffer = 0;
+void SetDrawSurfacesBuffer()
+{
+ if(bspDrawSurfaces != 0)
+ free(bspDrawSurfaces);
+
+ numBSPDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
+
+ bspDrawSurfaces = safe_malloc_info(sizeof(bspDrawSurface_t) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces");
+
+ memset(bspDrawSurfaces, 0, MAX_MAP_DRAW_SURFS * sizeof(bspDrawVert_t));
+}
+
+void SetDrawSurfaces(int n)
+{
+ if(bspDrawSurfaces != 0)
+ free(bspDrawSurfaces);
+
+ numBSPDrawSurfaces = n;
+ numBSPDrawSurfacesBuffer = numBSPDrawSurfaces;
+
+ bspDrawSurfaces = safe_malloc_info(sizeof(bspDrawSurface_t) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces");
+
+ memset(bspDrawSurfaces, 0, n * sizeof(bspDrawVert_t));
+}
+
+void BSPFilesCleanup()
+{
+ if(bspDrawVerts != 0)
+ free(bspDrawVerts);
+ if(bspDrawSurfaces != 0)
+ free(bspDrawSurfaces);
+ if(bspLightBytes != 0)
+ free(bspLightBytes);
+ if(bspGridPoints != 0)
+ free(bspGridPoints);
+}
+
+
+
+
+
+
+/*
+SwapBlock()
+if all values are 32 bits, this can be used to swap everything
+*/
+
+void SwapBlock( int *block, int size )
+{
+ int i;
+
+
+ /* dummy check */
+ if( block == NULL )
+ return;
+
+ /* swap */
+ size >>= 2;
+ for( i = 0; i < size; i++ )
+ block[ i ] = LittleLong( block[ i ] );
+}
+
+
+
+/*
+SwapBSPFile()
+byte swaps all data in the abstract bsp
+*/
+
+void SwapBSPFile( void )
+{
+ int i, j;
+
+
+ /* models */
+ SwapBlock( (int*) bspModels, numBSPModels * sizeof( bspModels[ 0 ] ) );
+
+ /* shaders (don't swap the name) */
+ for( i = 0; i < numBSPShaders ; i++ )
+ {
+ bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );
+ bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );
+ }
+
+ /* planes */
+ SwapBlock( (int*) bspPlanes, numBSPPlanes * sizeof( bspPlanes[ 0 ] ) );
+
+ /* nodes */
+ SwapBlock( (int*) bspNodes, numBSPNodes * sizeof( bspNodes[ 0 ] ) );
+
+ /* leafs */
+ SwapBlock( (int*) bspLeafs, numBSPLeafs * sizeof( bspLeafs[ 0 ] ) );
+
+ /* leaffaces */
+ SwapBlock( (int*) bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
+
+ /* leafbrushes */
+ SwapBlock( (int*) bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
+
+ // brushes
+ SwapBlock( (int*) bspBrushes, numBSPBrushes * sizeof( bspBrushes[ 0 ] ) );
+
+ // brushsides
+ SwapBlock( (int*) bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
+
+ // vis
+ ((int*) &bspVisBytes)[ 0 ] = LittleLong( ((int*) &bspVisBytes)[ 0 ] );
+ ((int*) &bspVisBytes)[ 1 ] = LittleLong( ((int*) &bspVisBytes)[ 1 ] );
+
+ /* drawverts (don't swap colors) */
+ for( i = 0; i < numBSPDrawVerts; i++ )
+ {
+ bspDrawVerts[ i ].xyz[ 0 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 0 ] );
+ bspDrawVerts[ i ].xyz[ 1 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 1 ] );
+ bspDrawVerts[ i ].xyz[ 2 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 2 ] );
+ bspDrawVerts[ i ].normal[ 0 ] = LittleFloat( bspDrawVerts[ i ].normal[ 0 ] );
+ bspDrawVerts[ i ].normal[ 1 ] = LittleFloat( bspDrawVerts[ i ].normal[ 1 ] );
+ bspDrawVerts[ i ].normal[ 2 ] = LittleFloat( bspDrawVerts[ i ].normal[ 2 ] );
+ bspDrawVerts[ i ].st[ 0 ] = LittleFloat( bspDrawVerts[ i ].st[ 0 ] );
+ bspDrawVerts[ i ].st[ 1 ] = LittleFloat( bspDrawVerts[ i ].st[ 1 ] );
+ for( j = 0; j < MAX_LIGHTMAPS; j++ )
+ {
+ bspDrawVerts[ i ].lightmap[ j ][ 0 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 0 ] );
+ bspDrawVerts[ i ].lightmap[ j ][ 1 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 1 ] );
+ }
+ }
+
+ /* drawindexes */
+ SwapBlock( (int*) bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[0] ) );
+
+ /* drawsurfs */
+ /* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */
+ SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
+
+ /* fogs */
+ for( i = 0; i < numBSPFogs; i++ )
+ {
+ bspFogs[ i ].brushNum = LittleLong( bspFogs[ i ].brushNum );
+ bspFogs[ i ].visibleSide = LittleLong( bspFogs[ i ].visibleSide );
+ }
+
+ /* advertisements */
+ for( i = 0; i < numBSPAds; i++ )
+ {
+ bspAds[ i ].cellId = LittleLong( bspAds[ i ].cellId );
+ bspAds[ i ].normal[ 0 ] = LittleFloat( bspAds[ i ].normal[ 0 ] );
+ bspAds[ i ].normal[ 1 ] = LittleFloat( bspAds[ i ].normal[ 1 ] );
+ bspAds[ i ].normal[ 2 ] = LittleFloat( bspAds[ i ].normal[ 2 ] );
+
+ for( j = 0; j < 4; j++ )
+ {
+ bspAds[ i ].rect[j][ 0 ] = LittleFloat( bspAds[ i ].rect[j][ 0 ] );
+ bspAds[ i ].rect[j][ 1 ] = LittleFloat( bspAds[ i ].rect[j][ 1 ] );
+ bspAds[ i ].rect[j][ 2 ] = LittleFloat( bspAds[ i ].rect[j][ 2 ] );
+ }
+
+ //bspAds[ i ].model[ MAX_QPATH ];
+ }
+}
+
+
+
+/*
+GetLumpElements()
+gets the number of elements in a bsp lump
+*/
+
+int GetLumpElements( bspHeader_t *header, int lump, int size )
+{
+ /* check for odd size */
+ if( header->lumps[ lump ].length % size )
+ {
+ if( force )
+ {
+ Sys_Printf( "WARNING: GetLumpElements: odd lump size (%d) in lump %d\n", header->lumps[ lump ].length, lump );
+ return 0;
+ }
+ else
+ Error( "GetLumpElements: odd lump size (%d) in lump %d", header->lumps[ lump ].length, lump );
+ }
+
+ /* return element count */
+ return header->lumps[ lump ].length / size;
+}
+
+
+
+/*
+GetLump()
+returns a pointer to the specified lump
+*/
+
+void *GetLump( bspHeader_t *header, int lump )
+{
+ return (void*)( (byte*) header + header->lumps[ lump ].offset);
+}
+
+
+
+/*
+CopyLump()
+copies a bsp file lump into a destination buffer
+*/
+
+int CopyLump( bspHeader_t *header, int lump, void *dest, int size )
+{
+ int length, offset;
+
+
+ /* get lump length and offset */
+ length = header->lumps[ lump ].length;
+ offset = header->lumps[ lump ].offset;
+
+ /* handle erroneous cases */
+ if( length == 0 )
+ return 0;
+ if( length % size )
+ {
+ if( force )
+ {
+ Sys_Printf( "WARNING: CopyLump: odd lump size (%d) in lump %d\n", length, lump );
+ return 0;
+ }
+ else
+ Error( "CopyLump: odd lump size (%d) in lump %d", length, lump );
+ }
+
+ /* copy block of memory and return */
+ memcpy( dest, (byte*) header + offset, length );
+ return length / size;
+}
+
+
+
+/*
+AddLump()
+adds a lump to an outgoing bsp file
+*/
+
+void AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length )
+{
+ bspLump_t *lump;
+
+
+ /* add lump to bsp file header */
+ lump = &header->lumps[ lumpNum ];
+ lump->offset = LittleLong( ftell( file ) );
+ lump->length = LittleLong( length );
+
+ /* write lump to file */
+ SafeWrite( file, data, (length + 3) & ~3 );
+}
+
+
+
+/*
+LoadBSPFile()
+loads a bsp file into memory
+*/
+
+void LoadBSPFile( const char *filename )
+{
+ /* dummy check */
+ if( game == NULL || game->load == NULL )
+ Error( "LoadBSPFile: unsupported BSP file format" );
+
+ /* load it, then byte swap the in-memory version */
+ game->load( filename );
+ SwapBSPFile();
+}
+
+
+
+/*
+WriteBSPFile()
+writes a bsp file
+*/
+
+void WriteBSPFile( const char *filename )
+{
+ char tempname[ 1024 ];
+ time_t tm;
+
+
+ /* dummy check */
+ if( game == NULL || game->write == NULL )
+ Error( "WriteBSPFile: unsupported BSP file format" );
+
+ /* make fake temp name so existing bsp file isn't damaged in case write process fails */
+ time( &tm );
+ sprintf( tempname, "%s.%08X", filename, (int) tm );
+
+ /* byteswap, write the bsp, then swap back so it can be manipulated further */
+ SwapBSPFile();
+ game->write( tempname );
+ SwapBSPFile();
+
+ /* replace existing bsp file */
+ remove( filename );
+ rename( tempname, filename );
+}
+
+
+
+/*
+PrintBSPFileSizes()
+dumps info about current file
+*/
+
+void PrintBSPFileSizes( void )
+{
+ /* parse entities first */
+ if( numEntities <= 0 )
+ ParseEntities();
+
+ /* note that this is abstracted */
+ Sys_Printf( "Abstracted BSP file components (*actual sizes may differ)\n" );
+
+ /* print various and sundry bits */
+ Sys_Printf( "%9d models %9d\n",
+ numBSPModels, (int) (numBSPModels * sizeof( bspModel_t )) );
+ Sys_Printf( "%9d shaders %9d\n",
+ numBSPShaders, (int) (numBSPShaders * sizeof( bspShader_t )) );
+ Sys_Printf( "%9d brushes %9d\n",
+ numBSPBrushes, (int) (numBSPBrushes * sizeof( bspBrush_t )) );
+ Sys_Printf( "%9d brushsides %9d *\n",
+ numBSPBrushSides, (int) (numBSPBrushSides * sizeof( bspBrushSide_t )) );
+ Sys_Printf( "%9d fogs %9d\n",
+ numBSPFogs, (int) (numBSPFogs * sizeof( bspFog_t ) ) );
+ Sys_Printf( "%9d planes %9d\n",
+ numBSPPlanes, (int) (numBSPPlanes * sizeof( bspPlane_t )) );
+ Sys_Printf( "%9d entdata %9d\n",
+ numEntities, bspEntDataSize );
+ Sys_Printf( "\n");
+
+ Sys_Printf( "%9d nodes %9d\n",
+ numBSPNodes, (int) (numBSPNodes * sizeof( bspNode_t)) );
+ Sys_Printf( "%9d leafs %9d\n",
+ numBSPLeafs, (int) (numBSPLeafs * sizeof( bspLeaf_t )) );
+ Sys_Printf( "%9d leafsurfaces %9d\n",
+ numBSPLeafSurfaces, (int) (numBSPLeafSurfaces * sizeof( *bspLeafSurfaces )) );
+ Sys_Printf( "%9d leafbrushes %9d\n",
+ numBSPLeafBrushes, (int) (numBSPLeafBrushes * sizeof( *bspLeafBrushes )) );
+ Sys_Printf( "\n");
+
+ Sys_Printf( "%9d drawsurfaces %9d *\n",
+ numBSPDrawSurfaces, (int) (numBSPDrawSurfaces * sizeof( *bspDrawSurfaces )) );
+ Sys_Printf( "%9d drawverts %9d *\n",
+ numBSPDrawVerts, (int) (numBSPDrawVerts * sizeof( *bspDrawVerts )) );
+ Sys_Printf( "%9d drawindexes %9d\n",
+ numBSPDrawIndexes, (int) (numBSPDrawIndexes * sizeof( *bspDrawIndexes )) );
+ Sys_Printf( "\n");
+
+ Sys_Printf( "%9d lightmaps %9d\n",
+ numBSPLightBytes / (game->lightmapSize * game->lightmapSize * 3), numBSPLightBytes );
+ Sys_Printf( "%9d lightgrid %9d *\n",
+ numBSPGridPoints, (int) (numBSPGridPoints * sizeof( *bspGridPoints )) );
+ Sys_Printf( " visibility %9d\n",
+ numBSPVisBytes );
+}
+
+
+
+/* -------------------------------------------------------------------------------
+
+entity data handling
+
+------------------------------------------------------------------------------- */
+
+
+/*
+StripTrailing()
+strips low byte chars off the end of a string
+*/
+
+void StripTrailing( char *e )
+{
+ char *s;
+
+
+ s = e + strlen( e ) - 1;
+ while( s >= e && *s <= 32 )
+ {
+ *s = 0;
+ s--;
+ }
+}
+
+
+
+/*
+ParseEpair()
+parses a single quoted "key" "value" pair into an epair struct
+*/
+
+epair_t *ParseEPair( void )
+{
+ epair_t *e;
+
+
+ /* allocate and clear new epair */
+ e = safe_malloc( sizeof( epair_t ) );
+ memset( e, 0, sizeof( epair_t ) );
+
+ /* handle key */
+ if( strlen( token ) >= (MAX_KEY - 1) )
+ Error( "ParseEPair: token too long" );
+
+ e->key = copystring( token );
+ GetToken( qfalse );
+
+ /* handle value */
+ if( strlen( token ) >= MAX_VALUE - 1 )
+ Error( "ParseEpar: token too long" );
+ e->value = copystring( token );
+
+ /* strip trailing spaces that sometimes get accidentally added in the editor */
+ StripTrailing( e->key );
+ StripTrailing( e->value );
+
+ /* return it */
+ return e;
+}
+
+
+
+/*
+ParseEntity()
+parses an entity's epairs
+*/
+
+qboolean ParseEntity( void )
+{
+ epair_t *e;
+
+
+ /* dummy check */
+ if( !GetToken( qtrue ) )
+ return qfalse;
+ if( strcmp( token, "{" ) )
+ Error( "ParseEntity: { not found" );
+ if( numEntities == MAX_MAP_ENTITIES )
+ Error( "numEntities == MAX_MAP_ENTITIES" );
+
+ /* create new entity */
+ mapEnt = &entities[ numEntities ];
+ numEntities++;
+
+ /* parse */
+ while( 1 )
+ {
+ if( !GetToken( qtrue ) )
+ Error( "ParseEntity: EOF without closing brace" );
+ if( !EPAIR_STRCMP( token, "}" ) )
+ break;
+ e = ParseEPair();
+ e->next = mapEnt->epairs;
+ mapEnt->epairs = e;
+ }
+
+ /* return to sender */
+ return qtrue;
+}
+
+
+
+/*
+ParseEntities()
+parses the bsp entity data string into entities
+*/
+
+void ParseEntities( void )
+{
+ numEntities = 0;
+ ParseFromMemory( bspEntData, bspEntDataSize );
+ while( ParseEntity() );
+
+ /* ydnar: set number of bsp entities in case a map is loaded on top */
+ numBSPEntities = numEntities;
+}
+
+
+
+/*
+UnparseEntities()
+generates the dentdata string from all the entities.
+this allows the utilities to add or remove key/value
+pairs to the data created by the map editor
+*/
+
+void UnparseEntities( void )
+{
+ int i;
+ char *buf, *end;
+ epair_t *ep;
+ char line[ 2048 ];
+ char key[ 1024 ], value[ 1024 ];
+ const char *value2;
+
+
+ /* setup */
+ buf = bspEntData;
+ end = buf;
+ *end = 0;
+
+
+ /* run through entity list */
+ for( i = 0; i < numBSPEntities && i < numEntities; i++ )
+ {
+ /* get epair */
+ ep = entities[ i ].epairs;
+ if( ep == NULL )
+ continue; /* ent got removed */
+
+ /* ydnar: certain entities get stripped from bsp file */
+ value2 = ValueForKey( &entities[ i ], "classname" );
+ if( !Q_stricmp( value2, "misc_model" ) ||
+ !Q_stricmp( value2, "_decal" ) ||
+ !Q_stricmp( value2, "_skybox" ) )
+ continue;
+
+ /* add beginning brace */
+ strcat( end, "{\n" );
+ end += 2;
+
+ /* walk epair list */
+ for( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
+ {
+ /* copy and clean */
+ strcpy( key, ep->key );
+ StripTrailing( key );
+ strcpy( value, ep->value );
+ StripTrailing( value );
+
+ /* add to buffer */
+ sprintf( line, "\"%s\" \"%s\"\n", key, value );
+ strcat( end, line );
+ end += strlen( line );
+ }
+
+ /* add trailing brace */
+ strcat( end,"}\n" );
+ end += 2;
+
+ /* check for overflow */
+ if( end > buf + MAX_MAP_ENTSTRING )
+ Error( "Entity text too long" );
+ }
+
+ /* set size */
+ bspEntDataSize = end - buf + 1;
+}
+
+
+
+/*
+PrintEntity()
+prints an entity's epairs to the console
+*/
+
+void PrintEntity( const entity_t *ent )
+{
+ epair_t *ep;
+
+
+ Sys_Printf( "------- entity %p -------\n", ent );
+ for( ep = ent->epairs; ep != NULL; ep = ep->next )
+ Sys_Printf( "%s = %s\n", ep->key, ep->value );
+
+}
+
+
+
+/*
+SetKeyValue()
+sets an epair in an entity
+*/
+
+void SetKeyValue( entity_t *ent, const char *key, const char *value )
+{
+ epair_t *ep;
+
+
+ /* check for existing epair */
+ for( ep = ent->epairs; ep != NULL; ep = ep->next )
+ {
+ if( !EPAIR_STRCMP( ep->key, key ) )
+ {
+ free( ep->value );
+ ep->value = copystring( value );
+ return;
+ }
+ }
+
+ /* create new epair */
+ ep = safe_malloc( sizeof( *ep ) );
+ ep->next = ent->epairs;
+ ent->epairs = ep;
+ ep->key = copystring( key );
+ ep->value = copystring( value );
+}
+
+
+
+/*
+ValueForKey()
+gets the value for an entity key
+*/
+
+const char *ValueForKey( const entity_t *ent, const char *key )
+{
+ epair_t *ep;
+
+
+ /* dummy check */
+ if( ent == NULL )
+ return "";
+
+ /* walk epair list */
+ for( ep = ent->epairs; ep != NULL; ep = ep->next )
+ {
+ if( !EPAIR_STRCMP( ep->key, key ) )
+ return ep->value;
+ }
+
+ /* if no match, return empty string */
+ return "";
+}
+
+
+
+/*
+IntForKey()
+gets the integer point value for an entity key
+*/
+
+int IntForKey( const entity_t *ent, const char *key )
+{
+ const char *k;
+
+
+ k = ValueForKey( ent, key );
+ return atoi( k );
+}
+
+
+
+/*
+FloatForKey()
+gets the floating point value for an entity key
+*/
+
+vec_t FloatForKey( const entity_t *ent, const char *key )
+{
+ const char *k;
+
+
+ k = ValueForKey( ent, key );
+ return atof( k );
+}
+
+
+
+/*
+GetVectorForKey()
+gets a 3-element vector value for an entity key
+*/
+
+void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec )
+{
+ const char *k;
+ double v1, v2, v3;
+
+
+ /* get value */
+ k = ValueForKey( ent, key );
+
+ /* scanf into doubles, then assign, so it is vec_t size independent */
+ v1 = v2 = v3 = 0.0;
+ sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
+ vec[ 0 ] = v1;
+ vec[ 1 ] = v2;
+ vec[ 2 ] = v3;
+}
+
+
+
+/*
+FindTargetEntity()
+finds an entity target
+*/
+
+entity_t *FindTargetEntity( const char *target )
+{
+ int i;
+ const char *n;
+
+
+ /* walk entity list */
+ for( i = 0; i < numEntities; i++ )
+ {
+ n = ValueForKey( &entities[ i ], "targetname" );
+ if ( !strcmp( n, target ) )
+ return &entities[ i ];
+ }
+
+ /* nada */
+ return NULL;
+}
+
+
+
+/*
+GetEntityShadowFlags() - ydnar
+gets an entity's shadow flags
+note: does not set them to defaults if the keys are not found!
+*/
+
+void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows )
+{
+ const char *value;
+
+ /* get cast shadows */
+ if( castShadows != NULL )
+ {
+ value = ValueForKey( ent, "_castShadows" );
+ if( value[ 0 ] == '\0' )
+ value = ValueForKey( ent, "_cs" );
+ if( value[ 0 ] == '\0' )
+ value = ValueForKey( ent2, "_castShadows" );
+ if( value[ 0 ] == '\0' )
+ value = ValueForKey( ent2, "_cs" );
+ if( value[ 0 ] != '\0' )
+ *castShadows = atoi( value );
+ }
+
+ /* receive */
+ if( recvShadows != NULL )
+ {
+ value = ValueForKey( ent, "_receiveShadows" );
+ if( value[ 0 ] == '\0' )
+ value = ValueForKey( ent, "_rs" );
+ if( value[ 0 ] == '\0' )
+ value = ValueForKey( ent2, "_receiveShadows" );
+ if( value[ 0 ] == '\0' )
+ value = ValueForKey( ent2, "_rs" );
+ if( value[ 0 ] != '\0' )
+ *recvShadows = atoi( value );
+ }
+}
+