--- /dev/null
+/* -------------------------------------------------------------------------------
+
+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 = 1024;
+
+ bspDrawVerts = safe_malloc_info(sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer, "IncDrawVerts");
+
+ }
+ else if(numBSPDrawVerts > numBSPDrawVertsBuffer)
+ {
+ numBSPDrawVertsBuffer *= 3; // multiply by 1.5
+ numBSPDrawVertsBuffer /= 2;
+
+ 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;
+}
+
+int CopyLump_Allocate( bspHeader_t *header, int lump, void **dest, int size, int *allocationVariable )
+{
+ /* get lump length and offset */
+ *allocationVariable = header->lumps[ lump ].length / size;
+ *dest = realloc(*dest, size * *allocationVariable);
+ return CopyLump(header, lump, *dest, 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;
+}
+
+/*
+ * must be called before UnparseEntities
+ */
+void InjectCommandLine(char **argv, int beginArgs, int endArgs)
+{
+ const char *previousCommandLine;
+ char newCommandLine[1024];
+ const char *inpos;
+ char *outpos = newCommandLine;
+ char *sentinel = newCommandLine + sizeof(newCommandLine) - 1;
+ int i;
+
+ previousCommandLine = ValueForKey(&entities[0], "_q3map2_cmdline");
+ if(previousCommandLine && *previousCommandLine)
+ {
+ inpos = previousCommandLine;
+ while(outpos != sentinel && *inpos)
+ *outpos++ = *inpos++;
+ if(outpos != sentinel)
+ *outpos++ = ';';
+ if(outpos != sentinel)
+ *outpos++ = ' ';
+ }
+
+ for(i = beginArgs; i < endArgs; ++i)
+ {
+ if(outpos != sentinel && i != beginArgs)
+ *outpos++ = ' ';
+ inpos = argv[i];
+ while(outpos != sentinel && *inpos)
+ if(*inpos != '\\' && *inpos != '"' && *inpos != ';' && (unsigned char) *inpos >= ' ')
+ *outpos++ = *inpos++;
+ }
+
+ *outpos = 0;
+ SetKeyValue(&entities[0], "_q3map2_cmdline", newCommandLine);
+ SetKeyValue(&entities[0], "_q3map2_version", Q3MAP_VERSION);
+}
+
+/*
+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 */
+ AUTOEXPAND_BY_REALLOC(bspEntData, 0, allocatedBSPEntData, 1024);
+ buf = bspEntData;
+ end = buf;
+ *end = 0;
+
+
+ /* run through entity list */
+ for( i = 0; i < numBSPEntities && i < numEntities; i++ )
+ {
+ {
+ int sz = end - buf;
+ AUTOEXPAND_BY_REALLOC(bspEntData, sz + 65536, allocatedBSPEntData, 1024);
+ buf = bspEntData;
+ end = buf + sz;
+ }
+
+ /* 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 + allocatedBSPEntData )
+ 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 );
+}
+
+/*
+KeyExists()
+returns true if entity has this key
+*/
+
+qboolean KeyExists( const entity_t *ent, const char *key )
+{
+ epair_t *ep;
+
+ /* walk epair list */
+ for( ep = ent->epairs; ep != NULL; ep = ep->next )
+ {
+ if( !EPAIR_STRCMP( ep->key, key ) )
+ return qtrue;
+ }
+
+ /* no match */
+ return qfalse;
+}
+
+/*
+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 );
+ }
+
+ /* vortex: game-specific default eneity keys */
+ value = ValueForKey( ent, "classname" );
+ if (!Q_stricmp( game->magic, "dq" ) || !Q_stricmp( game->magic, "prophecy" ) )
+ {
+ /* vortex: deluxe quake default shadow flags */
+ if (!Q_stricmp( value, "func_wall" ) )
+ {
+ if( recvShadows != NULL )
+ *recvShadows = 1;
+ if( castShadows != NULL )
+ *castShadows = 1;
+ }
+ }
+}
+