1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
32 #define BSPFILE_ABSTRACT_C
42 /* -------------------------------------------------------------------------------
44 this file was copied out of the common directory in order to not break
45 compatibility with the q3map 1.x tree. it was moved out in order to support
46 the raven bsp format (RBSP) used in soldier of fortune 2 and jedi knight 2.
48 since each game has its own set of particular features, the data structures
49 below no longer directly correspond to the binary format of a particular game.
51 the translation will be done at bsp load/save time to keep any sort of
52 special-case code messiness out of the rest of the program.
54 ------------------------------------------------------------------------------- */
58 /* FIXME: remove the functions below that handle memory management of bsp file chunks */
60 int numBSPDrawVertsBuffer = 0;
64 if ( bspDrawVerts == 0 ) {
65 numBSPDrawVertsBuffer = 1024;
67 bspDrawVerts = safe_malloc_info( sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer, "IncDrawVerts" );
70 else if ( numBSPDrawVerts > numBSPDrawVertsBuffer ) {
71 bspDrawVert_t *newBspDrawVerts;
73 numBSPDrawVertsBuffer *= 3; // multiply by 1.5
74 numBSPDrawVertsBuffer /= 2;
76 newBspDrawVerts = realloc( bspDrawVerts, sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer );
78 if ( !newBspDrawVerts ) {
80 Error( "realloc() failed (IncDrawVerts)" );
83 bspDrawVerts = newBspDrawVerts;
86 memset( bspDrawVerts + ( numBSPDrawVerts - 1 ), 0, sizeof( bspDrawVert_t ) );
89 void SetDrawVerts( int n ){
90 if ( bspDrawVerts != 0 ) {
95 numBSPDrawVertsBuffer = numBSPDrawVerts;
97 bspDrawVerts = safe_malloc0_info( sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer, "IncDrawVerts" );
100 int numBSPDrawSurfacesBuffer = 0;
101 void SetDrawSurfacesBuffer(){
102 if ( bspDrawSurfaces != 0 ) {
103 free( bspDrawSurfaces );
106 numBSPDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
108 bspDrawSurfaces = safe_malloc0_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
111 void SetDrawSurfaces( int n ){
112 if ( bspDrawSurfaces != 0 ) {
113 free( bspDrawSurfaces );
116 numBSPDrawSurfaces = n;
117 numBSPDrawSurfacesBuffer = numBSPDrawSurfaces;
119 bspDrawSurfaces = safe_malloc0_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
122 void BSPFilesCleanup(){
123 if ( bspDrawVerts != 0 ) {
124 free( bspDrawVerts );
126 if ( bspDrawSurfaces != 0 ) {
127 free( bspDrawSurfaces );
129 if ( bspLightBytes != 0 ) {
130 free( bspLightBytes );
132 if ( bspGridPoints != 0 ) {
133 free( bspGridPoints );
144 if all values are 32 bits, this can be used to swap everything
147 void SwapBlock( int *block, int size ){
152 if ( block == NULL ) {
158 for ( i = 0; i < size; i++ )
159 block[ i ] = LittleLong( block[ i ] );
166 byte swaps all data in the abstract bsp
169 void SwapBSPFile( void ){
174 SwapBlock( (int*) bspModels, numBSPModels * sizeof( bspModels[ 0 ] ) );
176 /* shaders (don't swap the name) */
177 for ( i = 0; i < numBSPShaders ; i++ )
180 si = ShaderInfoForShader( bspShaders[ i ].shader );
181 if ( si->remapShader && si->remapShader[ 0 ] ) {
182 strcpy( bspShaders[ i ].shader, si->remapShader );
185 bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );
186 bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );
190 SwapBlock( (int*) bspPlanes, numBSPPlanes * sizeof( bspPlanes[ 0 ] ) );
193 SwapBlock( (int*) bspNodes, numBSPNodes * sizeof( bspNodes[ 0 ] ) );
196 SwapBlock( (int*) bspLeafs, numBSPLeafs * sizeof( bspLeafs[ 0 ] ) );
199 SwapBlock( (int*) bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
202 SwapBlock( (int*) bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
205 SwapBlock( (int*) bspBrushes, numBSPBrushes * sizeof( bspBrushes[ 0 ] ) );
208 SwapBlock( (int*) bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
211 ( (int*) &bspVisBytes )[ 0 ] = LittleLong( ( (int*) &bspVisBytes )[ 0 ] );
212 ( (int*) &bspVisBytes )[ 1 ] = LittleLong( ( (int*) &bspVisBytes )[ 1 ] );
214 /* drawverts (don't swap colors) */
215 for ( i = 0; i < numBSPDrawVerts; i++ )
217 bspDrawVerts[ i ].xyz[ 0 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 0 ] );
218 bspDrawVerts[ i ].xyz[ 1 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 1 ] );
219 bspDrawVerts[ i ].xyz[ 2 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 2 ] );
220 bspDrawVerts[ i ].normal[ 0 ] = LittleFloat( bspDrawVerts[ i ].normal[ 0 ] );
221 bspDrawVerts[ i ].normal[ 1 ] = LittleFloat( bspDrawVerts[ i ].normal[ 1 ] );
222 bspDrawVerts[ i ].normal[ 2 ] = LittleFloat( bspDrawVerts[ i ].normal[ 2 ] );
223 bspDrawVerts[ i ].st[ 0 ] = LittleFloat( bspDrawVerts[ i ].st[ 0 ] );
224 bspDrawVerts[ i ].st[ 1 ] = LittleFloat( bspDrawVerts[ i ].st[ 1 ] );
225 for ( j = 0; j < MAX_LIGHTMAPS; j++ )
227 bspDrawVerts[ i ].lightmap[ j ][ 0 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 0 ] );
228 bspDrawVerts[ i ].lightmap[ j ][ 1 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 1 ] );
233 SwapBlock( (int*) bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[0] ) );
236 /* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */
237 SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
240 for ( i = 0; i < numBSPFogs; i++ )
242 bspFogs[ i ].brushNum = LittleLong( bspFogs[ i ].brushNum );
243 bspFogs[ i ].visibleSide = LittleLong( bspFogs[ i ].visibleSide );
247 for ( i = 0; i < numBSPAds; i++ )
249 bspAds[ i ].cellId = LittleLong( bspAds[ i ].cellId );
250 bspAds[ i ].normal[ 0 ] = LittleFloat( bspAds[ i ].normal[ 0 ] );
251 bspAds[ i ].normal[ 1 ] = LittleFloat( bspAds[ i ].normal[ 1 ] );
252 bspAds[ i ].normal[ 2 ] = LittleFloat( bspAds[ i ].normal[ 2 ] );
254 for ( j = 0; j < 4; j++ )
256 bspAds[ i ].rect[j][ 0 ] = LittleFloat( bspAds[ i ].rect[j][ 0 ] );
257 bspAds[ i ].rect[j][ 1 ] = LittleFloat( bspAds[ i ].rect[j][ 1 ] );
258 bspAds[ i ].rect[j][ 2 ] = LittleFloat( bspAds[ i ].rect[j][ 2 ] );
261 //bspAds[ i ].model[ MAX_QPATH ];
267 gets the number of elements in a bsp lump
270 int GetLumpElements( bspHeader_t *header, int lump, int size ){
271 /* check for odd size */
272 if ( header->lumps[ lump ].length % size ) {
274 Sys_FPrintf( SYS_WRN, "WARNING: GetLumpElements: odd lump size (%d) in lump %d\n", header->lumps[ lump ].length, lump );
278 Error( "GetLumpElements: odd lump size (%d) in lump %d", header->lumps[ lump ].length, lump );
282 /* return element count */
283 return header->lumps[ lump ].length / size;
290 returns a pointer to the specified lump
293 void *GetLump( bspHeader_t *header, int lump ){
294 return (void*)( (byte*) header + header->lumps[ lump ].offset );
301 copies a bsp file lump into a destination buffer
304 int CopyLump( bspHeader_t *header, int lump, void *dest, int size ){
308 /* get lump length and offset */
309 length = header->lumps[ lump ].length;
310 offset = header->lumps[ lump ].offset;
312 /* handle erroneous cases */
316 if ( length % size ) {
318 Sys_FPrintf( SYS_WRN, "WARNING: CopyLump: odd lump size (%d) in lump %d\n", length, lump );
322 Error( "CopyLump: odd lump size (%d) in lump %d", length, lump );
326 /* copy block of memory and return */
327 memcpy( dest, (byte*) header + offset, length );
328 return length / size;
331 int CopyLump_Allocate( bspHeader_t *header, int lump, void **dest, int size, int *allocationVariable ){
332 /* get lump length and offset */
333 *allocationVariable = header->lumps[ lump ].length / size;
334 *dest = realloc( *dest, size * *allocationVariable );
335 return CopyLump( header, lump, *dest, size );
341 adds a lump to an outgoing bsp file
344 void AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length ){
347 /* add lump to bsp file header */
348 lump = &header->lumps[ lumpNum ];
349 lump->offset = LittleLong( ftell( file ) );
350 lump->length = LittleLong( length );
352 /* write lump to file */
353 SafeWrite( file, data, length );
355 /* write padding zeros */
356 SafeWrite( file, (const byte[3]){ 0, 0, 0 }, ( ( length + 3 ) & ~3 ) - length );
363 loads a bsp file into memory
366 void LoadBSPFile( const char *filename ){
368 if ( game == NULL || game->load == NULL ) {
369 Error( "LoadBSPFile: unsupported BSP file format" );
372 /* load it, then byte swap the in-memory version */
373 game->load( filename );
379 partially loads a bsp file into memory
383 void PartialLoadBSPFile( const char *filename ){
385 if ( game == NULL || game->load == NULL ) {
386 Error( "LoadBSPFile: unsupported BSP file format" );
389 /* load it, then byte swap the in-memory version */
390 //game->load( filename );
391 PartialLoadIBSPFile( filename );
393 /* PartialSwapBSPFile() */
396 /* shaders (don't swap the name) */
397 for ( i = 0; i < numBSPShaders ; i++ )
399 bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );
400 bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );
404 /* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */
405 SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
413 void WriteBSPFile( const char *filename ){
414 char tempname[ 1024 ];
419 if ( game == NULL || game->write == NULL ) {
420 Error( "WriteBSPFile: unsupported BSP file format" );
423 /* make fake temp name so existing bsp file isn't damaged in case write process fails */
425 sprintf( tempname, "%s.%08X", filename, (int) tm );
427 /* byteswap, write the bsp, then swap back so it can be manipulated further */
429 game->write( tempname );
432 /* replace existing bsp file */
434 rename( tempname, filename );
441 dumps info about current file
444 void PrintBSPFileSizes( void ){
445 /* parse entities first */
446 if ( numEntities <= 0 ) {
451 for ( s = bspDrawSurfaces; s != bspDrawSurfaces + numBSPDrawSurfaces; ++s ){
452 if ( s->surfaceType == MST_PATCH )
455 /* note that this is abstracted */
456 Sys_Printf( "Abstracted BSP file components (*actual sizes may differ)\n" );
458 /* print various and sundry bits */
459 Sys_Printf( "%9d models %9d\n",
460 numBSPModels, (int) ( numBSPModels * sizeof( bspModel_t ) ) );
461 Sys_Printf( "%9d shaders %9d\n",
462 numBSPShaders, (int) ( numBSPShaders * sizeof( bspShader_t ) ) );
463 Sys_Printf( "%9d brushes %9d\n",
464 numBSPBrushes, (int) ( numBSPBrushes * sizeof( bspBrush_t ) ) );
465 Sys_Printf( "%9d brushsides %9d *\n",
466 numBSPBrushSides, (int) ( numBSPBrushSides * sizeof( bspBrushSide_t ) ) );
467 Sys_Printf( "%9d fogs %9d\n",
468 numBSPFogs, (int) ( numBSPFogs * sizeof( bspFog_t ) ) );
469 Sys_Printf( "%9d planes %9d\n",
470 numBSPPlanes, (int) ( numBSPPlanes * sizeof( bspPlane_t ) ) );
471 Sys_Printf( "%9d entdata %9d\n",
472 numEntities, bspEntDataSize );
475 Sys_Printf( "%9d nodes %9d\n",
476 numBSPNodes, (int) ( numBSPNodes * sizeof( bspNode_t ) ) );
477 Sys_Printf( "%9d leafs %9d\n",
478 numBSPLeafs, (int) ( numBSPLeafs * sizeof( bspLeaf_t ) ) );
479 Sys_Printf( "%9d leafsurfaces %9d\n",
480 numBSPLeafSurfaces, (int) ( numBSPLeafSurfaces * sizeof( *bspLeafSurfaces ) ) );
481 Sys_Printf( "%9d leafbrushes %9d\n",
482 numBSPLeafBrushes, (int) ( numBSPLeafBrushes * sizeof( *bspLeafBrushes ) ) );
485 Sys_Printf( "%9d drawsurfaces %9d *\n",
486 numBSPDrawSurfaces, (int) ( numBSPDrawSurfaces * sizeof( *bspDrawSurfaces ) ) );
487 Sys_Printf( "%9d patchsurfaces \n",
489 Sys_Printf( "%9d drawverts %9d *\n",
490 numBSPDrawVerts, (int) ( numBSPDrawVerts * sizeof( *bspDrawVerts ) ) );
491 Sys_Printf( "%9d drawindexes %9d\n",
492 numBSPDrawIndexes, (int) ( numBSPDrawIndexes * sizeof( *bspDrawIndexes ) ) );
495 Sys_Printf( "%9d lightmaps %9d\n",
496 numBSPLightBytes / ( game->lightmapSize * game->lightmapSize * 3 ), numBSPLightBytes );
497 Sys_Printf( "%9d lightgrid %9d *\n",
498 numBSPGridPoints, (int) ( numBSPGridPoints * sizeof( *bspGridPoints ) ) );
499 Sys_Printf( " visibility %9d\n",
505 /* -------------------------------------------------------------------------------
509 ------------------------------------------------------------------------------- */
514 strips low byte chars off the end of a string
517 void StripTrailing( char *e ){
521 s = e + strlen( e ) - 1;
522 while ( s >= e && *s <= 32 )
533 parses a single quoted "key" "value" pair into an epair struct
536 epair_t *ParseEPair( void ){
540 /* allocate and clear new epair */
541 e = safe_malloc0( sizeof( epair_t ) );
544 if ( strlen( token ) >= ( MAX_KEY - 1 ) ) {
545 Error( "ParseEPair: token too long" );
548 e->key = copystring( token );
552 if ( strlen( token ) >= MAX_VALUE - 1 ) {
553 Error( "ParseEpar: token too long" );
555 e->value = copystring( token );
557 /* strip trailing spaces that sometimes get accidentally added in the editor */
558 StripTrailing( e->key );
559 StripTrailing( e->value );
569 parses an entity's epairs
572 qboolean ParseEntity( void ){
577 if ( !GetToken( qtrue ) ) {
580 if ( strcmp( token, "{" ) ) {
581 Error( "ParseEntity: { not found" );
583 AUTOEXPAND_BY_REALLOC( entities, numEntities, allocatedEntities, 32 );
585 /* create new entity */
586 mapEnt = &entities[ numEntities ];
588 memset( mapEnt, 0, sizeof( *mapEnt ) );
593 if ( !GetToken( qtrue ) ) {
594 Error( "ParseEntity: EOF without closing brace" );
596 if ( !EPAIR_STRCMP( token, "}" ) ) {
600 e->next = mapEnt->epairs;
604 /* return to sender */
612 parses the bsp entity data string into entities
615 void ParseEntities( void ){
617 ParseFromMemory( bspEntData, bspEntDataSize );
618 while ( ParseEntity() ) ;
620 /* ydnar: set number of bsp entities in case a map is loaded on top */
621 numBSPEntities = numEntities;
627 * must be called before UnparseEntities
629 void InjectCommandLine( char **argv, int beginArgs, int endArgs ){
630 const char *previousCommandLine;
631 char newCommandLine[1024];
633 char *outpos = newCommandLine;
634 char *sentinel = newCommandLine + sizeof( newCommandLine ) - 1;
640 previousCommandLine = ValueForKey( &entities[0], "_q3map2_cmdline" );
641 if ( previousCommandLine && *previousCommandLine ) {
642 inpos = previousCommandLine;
643 while ( outpos != sentinel && *inpos )
644 *outpos++ = *inpos++;
645 if ( outpos != sentinel ) {
648 if ( outpos != sentinel ) {
653 for ( i = beginArgs; i < endArgs; ++i )
655 if ( argv[i] == NULL ) {
658 if ( outpos != sentinel && i != beginArgs ) {
662 while ( outpos != sentinel && *inpos )
663 if ( *inpos != '\\' && *inpos != '"' && *inpos != ';' && (unsigned char) *inpos >= ' ' ) {
664 *outpos++ = *inpos++;
669 SetKeyValue( &entities[0], "_q3map2_cmdline", newCommandLine );
670 SetKeyValue( &entities[0], "_q3map2_version", Q3MAP_VERSION );
677 generates the dentdata string from all the entities.
678 this allows the utilities to add or remove key/value
679 pairs to the data created by the map editor
682 void UnparseEntities( void ){
687 char key[ 1024 ], value[ 1024 ];
692 AUTOEXPAND_BY_REALLOC( bspEntData, 0, allocatedBSPEntData, 1024 );
698 /* run through entity list */
699 for ( i = 0; i < numBSPEntities && i < numEntities; i++ )
703 AUTOEXPAND_BY_REALLOC( bspEntData, sz + 65536, allocatedBSPEntData, 1024 );
709 ep = entities[ i ].epairs;
711 continue; /* ent got removed */
714 /* ydnar: certain entities get stripped from bsp file */
715 value2 = ValueForKey( &entities[ i ], "classname" );
716 if ( !Q_stricmp( value2, "misc_model" ) ||
717 !Q_stricmp( value2, "_decal" ) ||
718 !Q_stricmp( value2, "_skybox" ) ) {
722 /* add beginning brace */
723 strcat( end, "{\n" );
726 /* walk epair list */
727 for ( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
730 strcpy( key, ep->key );
731 StripTrailing( key );
732 strcpy( value, ep->value );
733 StripTrailing( value );
736 sprintf( line, "\"%s\" \"%s\"\n", key, value );
738 end += strlen( line );
741 /* add trailing brace */
745 /* check for overflow */
746 if ( end > buf + allocatedBSPEntData ) {
747 Error( "Entity text too long" );
752 bspEntDataSize = end - buf + 1;
759 prints an entity's epairs to the console
762 void PrintEntity( const entity_t *ent ){
766 Sys_Printf( "------- entity %p -------\n", ent );
767 for ( ep = ent->epairs; ep != NULL; ep = ep->next )
768 Sys_Printf( "%s = %s\n", ep->key, ep->value );
776 sets an epair in an entity
779 void SetKeyValue( entity_t *ent, const char *key, const char *value ){
783 /* check for existing epair */
784 for ( ep = ent->epairs; ep != NULL; ep = ep->next )
786 if ( !EPAIR_STRCMP( ep->key, key ) ) {
788 ep->value = copystring( value );
793 /* create new epair */
794 ep = safe_malloc( sizeof( *ep ) );
795 ep->next = ent->epairs;
797 ep->key = copystring( key );
798 ep->value = copystring( value );
805 returns true if entity has this key
808 qboolean KeyExists( const entity_t *ent, const char *key ){
811 /* walk epair list */
812 for ( ep = ent->epairs; ep != NULL; ep = ep->next )
814 if ( !EPAIR_STRCMP( ep->key, key ) ) {
827 gets the value for an entity key
830 const char *ValueForKey( const entity_t *ent, const char *key ){
839 /* walk epair list */
840 for ( ep = ent->epairs; ep != NULL; ep = ep->next )
842 if ( !EPAIR_STRCMP( ep->key, key ) ) {
847 /* if no match, return empty string */
855 gets the integer point value for an entity key
858 int IntForKey( const entity_t *ent, const char *key ){
862 k = ValueForKey( ent, key );
870 gets the floating point value for an entity key
873 vec_t FloatForKey( const entity_t *ent, const char *key ){
877 k = ValueForKey( ent, key );
885 gets a 3-element vector value for an entity key
888 qboolean GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ){
894 k = ValueForKey( ent, key );
896 /* scanf into doubles, then assign, so it is vec_t size independent */
898 sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
903 /* true if the key is found, false otherwise */
911 finds an entity target
914 entity_t *FindTargetEntity( const char *target ){
919 /* walk entity list */
920 for ( i = 0; i < numEntities; i++ )
922 n = ValueForKey( &entities[ i ], "targetname" );
923 if ( !strcmp( n, target ) ) {
924 return &entities[ i ];
935 GetEntityShadowFlags() - ydnar
936 gets an entity's shadow flags
937 note: does not set them to defaults if the keys are not found!
940 void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows ){
943 /* get cast shadows */
944 if ( castShadows != NULL ) {
945 value = ValueForKey( ent, "_castShadows" );
946 if ( value[ 0 ] == '\0' ) {
947 value = ValueForKey( ent, "_cs" );
949 if ( value[ 0 ] == '\0' ) {
950 value = ValueForKey( ent2, "_castShadows" );
952 if ( value[ 0 ] == '\0' ) {
953 value = ValueForKey( ent2, "_cs" );
955 if ( value[ 0 ] != '\0' ) {
956 *castShadows = atoi( value );
961 if ( recvShadows != NULL ) {
962 value = ValueForKey( ent, "_receiveShadows" );
963 if ( value[ 0 ] == '\0' ) {
964 value = ValueForKey( ent, "_rs" );
966 if ( value[ 0 ] == '\0' ) {
967 value = ValueForKey( ent2, "_receiveShadows" );
969 if ( value[ 0 ] == '\0' ) {
970 value = ValueForKey( ent2, "_rs" );
972 if ( value[ 0 ] != '\0' ) {
973 *recvShadows = atoi( value );
977 /* vortex: game-specific default entity keys */
978 value = ValueForKey( ent, "classname" );
979 if ( !Q_stricmp( game->magic, "dq" ) || !Q_stricmp( game->magic, "prophecy" ) ) {
980 /* vortex: deluxe quake default shadow flags */
981 if ( !Q_stricmp( value, "func_wall" ) ) {
982 if ( recvShadows != NULL ) {
985 if ( castShadows != NULL ) {