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 numBSPDrawVertsBuffer *= 3; // multiply by 1.5
72 numBSPDrawVertsBuffer /= 2;
74 bspDrawVerts = realloc( bspDrawVerts, sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer );
76 if ( !bspDrawVerts ) {
77 Error( "realloc() failed (IncDrawVerts)" );
81 memset( bspDrawVerts + ( numBSPDrawVerts - 1 ), 0, sizeof( bspDrawVert_t ) );
84 void SetDrawVerts( int n ){
85 if ( bspDrawVerts != 0 ) {
90 numBSPDrawVertsBuffer = numBSPDrawVerts;
92 bspDrawVerts = safe_malloc_info( sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer, "IncDrawVerts" );
94 memset( bspDrawVerts, 0, n * sizeof( bspDrawVert_t ) );
97 int numBSPDrawSurfacesBuffer = 0;
98 void SetDrawSurfacesBuffer(){
99 if ( bspDrawSurfaces != 0 ) {
100 free( bspDrawSurfaces );
103 numBSPDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
105 bspDrawSurfaces = safe_malloc_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
107 memset( bspDrawSurfaces, 0, MAX_MAP_DRAW_SURFS * sizeof( bspDrawSurface_t ) );
110 void SetDrawSurfaces( int n ){
111 if ( bspDrawSurfaces != 0 ) {
112 free( bspDrawSurfaces );
115 numBSPDrawSurfaces = n;
116 numBSPDrawSurfacesBuffer = numBSPDrawSurfaces;
118 bspDrawSurfaces = safe_malloc_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
120 memset( bspDrawSurfaces, 0, n * sizeof( bspDrawSurface_t ) );
123 void BSPFilesCleanup(){
124 if ( bspDrawVerts != 0 ) {
125 free( bspDrawVerts );
127 if ( bspDrawSurfaces != 0 ) {
128 free( bspDrawSurfaces );
130 if ( bspLightBytes != 0 ) {
131 free( bspLightBytes );
133 if ( bspGridPoints != 0 ) {
134 free( bspGridPoints );
145 if all values are 32 bits, this can be used to swap everything
148 void SwapBlock( int *block, int size ){
153 if ( block == NULL ) {
159 for ( i = 0; i < size; i++ )
160 block[ i ] = LittleLong( block[ i ] );
167 byte swaps all data in the abstract bsp
170 void SwapBSPFile( void ){
175 SwapBlock( (int*) bspModels, numBSPModels * sizeof( bspModels[ 0 ] ) );
177 /* shaders (don't swap the name) */
178 for ( i = 0; i < numBSPShaders ; i++ )
181 si = ShaderInfoForShader( bspShaders[ i ].shader );
182 if ( si->remapShader && si->remapShader[ 0 ] ) {
183 strcpy( bspShaders[ i ].shader, si->remapShader );
186 bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );
187 bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );
191 SwapBlock( (int*) bspPlanes, numBSPPlanes * sizeof( bspPlanes[ 0 ] ) );
194 SwapBlock( (int*) bspNodes, numBSPNodes * sizeof( bspNodes[ 0 ] ) );
197 SwapBlock( (int*) bspLeafs, numBSPLeafs * sizeof( bspLeafs[ 0 ] ) );
200 SwapBlock( (int*) bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
203 SwapBlock( (int*) bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
206 SwapBlock( (int*) bspBrushes, numBSPBrushes * sizeof( bspBrushes[ 0 ] ) );
209 SwapBlock( (int*) bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
212 ( (int*) &bspVisBytes )[ 0 ] = LittleLong( ( (int*) &bspVisBytes )[ 0 ] );
213 ( (int*) &bspVisBytes )[ 1 ] = LittleLong( ( (int*) &bspVisBytes )[ 1 ] );
215 /* drawverts (don't swap colors) */
216 for ( i = 0; i < numBSPDrawVerts; i++ )
218 bspDrawVerts[ i ].xyz[ 0 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 0 ] );
219 bspDrawVerts[ i ].xyz[ 1 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 1 ] );
220 bspDrawVerts[ i ].xyz[ 2 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 2 ] );
221 bspDrawVerts[ i ].normal[ 0 ] = LittleFloat( bspDrawVerts[ i ].normal[ 0 ] );
222 bspDrawVerts[ i ].normal[ 1 ] = LittleFloat( bspDrawVerts[ i ].normal[ 1 ] );
223 bspDrawVerts[ i ].normal[ 2 ] = LittleFloat( bspDrawVerts[ i ].normal[ 2 ] );
224 bspDrawVerts[ i ].st[ 0 ] = LittleFloat( bspDrawVerts[ i ].st[ 0 ] );
225 bspDrawVerts[ i ].st[ 1 ] = LittleFloat( bspDrawVerts[ i ].st[ 1 ] );
226 for ( j = 0; j < MAX_LIGHTMAPS; j++ )
228 bspDrawVerts[ i ].lightmap[ j ][ 0 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 0 ] );
229 bspDrawVerts[ i ].lightmap[ j ][ 1 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 1 ] );
234 SwapBlock( (int*) bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[0] ) );
237 /* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */
238 SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
241 for ( i = 0; i < numBSPFogs; i++ )
243 bspFogs[ i ].brushNum = LittleLong( bspFogs[ i ].brushNum );
244 bspFogs[ i ].visibleSide = LittleLong( bspFogs[ i ].visibleSide );
248 for ( i = 0; i < numBSPAds; i++ )
250 bspAds[ i ].cellId = LittleLong( bspAds[ i ].cellId );
251 bspAds[ i ].normal[ 0 ] = LittleFloat( bspAds[ i ].normal[ 0 ] );
252 bspAds[ i ].normal[ 1 ] = LittleFloat( bspAds[ i ].normal[ 1 ] );
253 bspAds[ i ].normal[ 2 ] = LittleFloat( bspAds[ i ].normal[ 2 ] );
255 for ( j = 0; j < 4; j++ )
257 bspAds[ i ].rect[j][ 0 ] = LittleFloat( bspAds[ i ].rect[j][ 0 ] );
258 bspAds[ i ].rect[j][ 1 ] = LittleFloat( bspAds[ i ].rect[j][ 1 ] );
259 bspAds[ i ].rect[j][ 2 ] = LittleFloat( bspAds[ i ].rect[j][ 2 ] );
262 //bspAds[ i ].model[ MAX_QPATH ];
268 gets the number of elements in a bsp lump
271 int GetLumpElements( bspHeader_t *header, int lump, int size ){
272 /* check for odd size */
273 if ( header->lumps[ lump ].length % size ) {
275 Sys_FPrintf( SYS_WRN, "WARNING: GetLumpElements: odd lump size (%d) in lump %d\n", header->lumps[ lump ].length, lump );
279 Error( "GetLumpElements: odd lump size (%d) in lump %d", header->lumps[ lump ].length, lump );
283 /* return element count */
284 return header->lumps[ lump ].length / size;
291 returns a pointer to the specified lump
294 void *GetLump( bspHeader_t *header, int lump ){
295 return (void*)( (byte*) header + header->lumps[ lump ].offset );
302 copies a bsp file lump into a destination buffer
305 int CopyLump( bspHeader_t *header, int lump, void *dest, int size ){
309 /* get lump length and offset */
310 length = header->lumps[ lump ].length;
311 offset = header->lumps[ lump ].offset;
313 /* handle erroneous cases */
317 if ( length % size ) {
319 Sys_FPrintf( SYS_WRN, "WARNING: CopyLump: odd lump size (%d) in lump %d\n", length, lump );
323 Error( "CopyLump: odd lump size (%d) in lump %d", length, lump );
327 /* copy block of memory and return */
328 memcpy( dest, (byte*) header + offset, length );
329 return length / size;
332 int CopyLump_Allocate( bspHeader_t *header, int lump, void **dest, int size, int *allocationVariable ){
333 /* get lump length and offset */
334 *allocationVariable = header->lumps[ lump ].length / size;
335 *dest = realloc( *dest, size * *allocationVariable );
336 return CopyLump( header, lump, *dest, size );
342 adds a lump to an outgoing bsp file
345 void AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length ){
349 /* add lump to bsp file header */
350 lump = &header->lumps[ lumpNum ];
351 lump->offset = LittleLong( ftell( file ) );
352 lump->length = LittleLong( length );
354 /* write lump to file */
355 SafeWrite( file, data, ( length + 3 ) & ~3 );
362 loads a bsp file into memory
365 void LoadBSPFile( const char *filename ){
367 if ( game == NULL || game->load == NULL ) {
368 Error( "LoadBSPFile: unsupported BSP file format" );
371 /* load it, then byte swap the in-memory version */
372 game->load( filename );
378 partially loads a bsp file into memory
382 void PartialLoadBSPFile( const char *filename ){
384 if ( game == NULL || game->load == NULL ) {
385 Error( "LoadBSPFile: unsupported BSP file format" );
388 /* load it, then byte swap the in-memory version */
389 //game->load( filename );
390 PartialLoadIBSPFile( filename );
392 /* 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 ) {
450 /* note that this is abstracted */
451 Sys_Printf( "Abstracted BSP file components (*actual sizes may differ)\n" );
453 /* print various and sundry bits */
454 Sys_Printf( "%9d models %9d\n",
455 numBSPModels, (int) ( numBSPModels * sizeof( bspModel_t ) ) );
456 Sys_Printf( "%9d shaders %9d\n",
457 numBSPShaders, (int) ( numBSPShaders * sizeof( bspShader_t ) ) );
458 Sys_Printf( "%9d brushes %9d\n",
459 numBSPBrushes, (int) ( numBSPBrushes * sizeof( bspBrush_t ) ) );
460 Sys_Printf( "%9d brushsides %9d *\n",
461 numBSPBrushSides, (int) ( numBSPBrushSides * sizeof( bspBrushSide_t ) ) );
462 Sys_Printf( "%9d fogs %9d\n",
463 numBSPFogs, (int) ( numBSPFogs * sizeof( bspFog_t ) ) );
464 Sys_Printf( "%9d planes %9d\n",
465 numBSPPlanes, (int) ( numBSPPlanes * sizeof( bspPlane_t ) ) );
466 Sys_Printf( "%9d entdata %9d\n",
467 numEntities, bspEntDataSize );
470 Sys_Printf( "%9d nodes %9d\n",
471 numBSPNodes, (int) ( numBSPNodes * sizeof( bspNode_t ) ) );
472 Sys_Printf( "%9d leafs %9d\n",
473 numBSPLeafs, (int) ( numBSPLeafs * sizeof( bspLeaf_t ) ) );
474 Sys_Printf( "%9d leafsurfaces %9d\n",
475 numBSPLeafSurfaces, (int) ( numBSPLeafSurfaces * sizeof( *bspLeafSurfaces ) ) );
476 Sys_Printf( "%9d leafbrushes %9d\n",
477 numBSPLeafBrushes, (int) ( numBSPLeafBrushes * sizeof( *bspLeafBrushes ) ) );
480 Sys_Printf( "%9d drawsurfaces %9d *\n",
481 numBSPDrawSurfaces, (int) ( numBSPDrawSurfaces * sizeof( *bspDrawSurfaces ) ) );
482 Sys_Printf( "%9d drawverts %9d *\n",
483 numBSPDrawVerts, (int) ( numBSPDrawVerts * sizeof( *bspDrawVerts ) ) );
484 Sys_Printf( "%9d drawindexes %9d\n",
485 numBSPDrawIndexes, (int) ( numBSPDrawIndexes * sizeof( *bspDrawIndexes ) ) );
488 Sys_Printf( "%9d lightmaps %9d\n",
489 numBSPLightBytes / ( game->lightmapSize * game->lightmapSize * 3 ), numBSPLightBytes );
490 Sys_Printf( "%9d lightgrid %9d *\n",
491 numBSPGridPoints, (int) ( numBSPGridPoints * sizeof( *bspGridPoints ) ) );
492 Sys_Printf( " visibility %9d\n",
498 /* -------------------------------------------------------------------------------
502 ------------------------------------------------------------------------------- */
507 strips low byte chars off the end of a string
510 void StripTrailing( char *e ){
514 s = e + strlen( e ) - 1;
515 while ( s >= e && *s <= 32 )
526 parses a single quoted "key" "value" pair into an epair struct
529 epair_t *ParseEPair( void ){
533 /* allocate and clear new epair */
534 e = safe_malloc( sizeof( epair_t ) );
535 memset( e, 0, sizeof( epair_t ) );
538 if ( strlen( token ) >= ( MAX_KEY - 1 ) ) {
539 Error( "ParseEPair: token too long" );
542 e->key = copystring( token );
546 if ( strlen( token ) >= MAX_VALUE - 1 ) {
547 Error( "ParseEpar: token too long" );
549 e->value = copystring( token );
551 /* strip trailing spaces that sometimes get accidentally added in the editor */
552 StripTrailing( e->key );
553 StripTrailing( e->value );
563 parses an entity's epairs
566 qboolean ParseEntity( void ){
571 if ( !GetToken( qtrue ) ) {
574 if ( strcmp( token, "{" ) ) {
575 Error( "ParseEntity: { not found" );
577 AUTOEXPAND_BY_REALLOC( entities, numEntities, allocatedEntities, 32 );
579 /* create new entity */
580 mapEnt = &entities[ numEntities ];
582 memset( mapEnt, 0, sizeof( *mapEnt ) );
587 if ( !GetToken( qtrue ) ) {
588 Error( "ParseEntity: EOF without closing brace" );
590 if ( !EPAIR_STRCMP( token, "}" ) ) {
594 e->next = mapEnt->epairs;
598 /* return to sender */
606 parses the bsp entity data string into entities
609 void ParseEntities( void ){
611 ParseFromMemory( bspEntData, bspEntDataSize );
612 while ( ParseEntity() ) ;
614 /* ydnar: set number of bsp entities in case a map is loaded on top */
615 numBSPEntities = numEntities;
621 * must be called before UnparseEntities
623 void InjectCommandLine( char **argv, int beginArgs, int endArgs ){
624 const char *previousCommandLine;
625 char newCommandLine[1024];
627 char *outpos = newCommandLine;
628 char *sentinel = newCommandLine + sizeof( newCommandLine ) - 1;
635 previousCommandLine = ValueForKey( &entities[0], "_q3map2_cmdline" );
636 if ( previousCommandLine && *previousCommandLine ) {
637 inpos = previousCommandLine;
638 while ( outpos != sentinel && *inpos )
639 *outpos++ = *inpos++;
640 if ( outpos != sentinel ) {
643 if ( outpos != sentinel ) {
648 for ( i = beginArgs; i < endArgs; ++i )
650 if ( outpos != sentinel && i != beginArgs ) {
654 while ( outpos != sentinel && *inpos )
655 if ( *inpos != '\\' && *inpos != '"' && *inpos != ';' && (unsigned char) *inpos >= ' ' ) {
656 *outpos++ = *inpos++;
661 SetKeyValue( &entities[0], "_q3map2_cmdline", newCommandLine );
662 SetKeyValue( &entities[0], "_q3map2_version", Q3MAP_VERSION );
669 generates the dentdata string from all the entities.
670 this allows the utilities to add or remove key/value
671 pairs to the data created by the map editor
674 void UnparseEntities( void ){
679 char key[ 1024 ], value[ 1024 ];
684 AUTOEXPAND_BY_REALLOC( bspEntData, 0, allocatedBSPEntData, 1024 );
690 /* run through entity list */
691 for ( i = 0; i < numBSPEntities && i < numEntities; i++ )
695 AUTOEXPAND_BY_REALLOC( bspEntData, sz + 65536, allocatedBSPEntData, 1024 );
701 ep = entities[ i ].epairs;
703 continue; /* ent got removed */
706 /* ydnar: certain entities get stripped from bsp file */
707 value2 = ValueForKey( &entities[ i ], "classname" );
708 if ( !Q_stricmp( value2, "misc_model" ) ||
709 !Q_stricmp( value2, "_decal" ) ||
710 !Q_stricmp( value2, "_skybox" ) ) {
714 /* add beginning brace */
715 strcat( end, "{\n" );
718 /* walk epair list */
719 for ( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
722 strcpy( key, ep->key );
723 StripTrailing( key );
724 strcpy( value, ep->value );
725 StripTrailing( value );
728 sprintf( line, "\"%s\" \"%s\"\n", key, value );
730 end += strlen( line );
733 /* add trailing brace */
737 /* check for overflow */
738 if ( end > buf + allocatedBSPEntData ) {
739 Error( "Entity text too long" );
744 bspEntDataSize = end - buf + 1;
751 prints an entity's epairs to the console
754 void PrintEntity( const entity_t *ent ){
758 Sys_Printf( "------- entity %p -------\n", ent );
759 for ( ep = ent->epairs; ep != NULL; ep = ep->next )
760 Sys_Printf( "%s = %s\n", ep->key, ep->value );
768 sets an epair in an entity
771 void SetKeyValue( entity_t *ent, const char *key, const char *value ){
775 /* check for existing epair */
776 for ( ep = ent->epairs; ep != NULL; ep = ep->next )
778 if ( !EPAIR_STRCMP( ep->key, key ) ) {
780 ep->value = copystring( value );
785 /* create new epair */
786 ep = safe_malloc( sizeof( *ep ) );
787 ep->next = ent->epairs;
789 ep->key = copystring( key );
790 ep->value = copystring( value );
797 returns true if entity has this key
800 qboolean KeyExists( const entity_t *ent, const char *key ){
803 /* walk epair list */
804 for ( ep = ent->epairs; ep != NULL; ep = ep->next )
806 if ( !EPAIR_STRCMP( ep->key, key ) ) {
819 gets the value for an entity key
822 const char *ValueForKey( const entity_t *ent, const char *key ){
831 /* walk epair list */
832 for ( ep = ent->epairs; ep != NULL; ep = ep->next )
834 if ( !EPAIR_STRCMP( ep->key, key ) ) {
839 /* if no match, return empty string */
847 gets the integer point value for an entity key
850 int IntForKey( const entity_t *ent, const char *key ){
854 k = ValueForKey( ent, key );
862 gets the floating point value for an entity key
865 vec_t FloatForKey( const entity_t *ent, const char *key ){
869 k = ValueForKey( ent, key );
877 gets a 3-element vector value for an entity key
880 qboolean GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ){
886 k = ValueForKey( ent, key );
888 /* scanf into doubles, then assign, so it is vec_t size independent */
890 sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
895 /* true if the key is found, false otherwise */
903 finds an entity target
906 entity_t *FindTargetEntity( const char *target ){
911 /* walk entity list */
912 for ( i = 0; i < numEntities; i++ )
914 n = ValueForKey( &entities[ i ], "targetname" );
915 if ( !strcmp( n, target ) ) {
916 return &entities[ i ];
927 GetEntityShadowFlags() - ydnar
928 gets an entity's shadow flags
929 note: does not set them to defaults if the keys are not found!
932 void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows ){
935 /* get cast shadows */
936 if ( castShadows != NULL ) {
937 value = ValueForKey( ent, "_castShadows" );
938 if ( value[ 0 ] == '\0' ) {
939 value = ValueForKey( ent, "_cs" );
941 if ( value[ 0 ] == '\0' ) {
942 value = ValueForKey( ent2, "_castShadows" );
944 if ( value[ 0 ] == '\0' ) {
945 value = ValueForKey( ent2, "_cs" );
947 if ( value[ 0 ] != '\0' ) {
948 *castShadows = atoi( value );
953 if ( recvShadows != NULL ) {
954 value = ValueForKey( ent, "_receiveShadows" );
955 if ( value[ 0 ] == '\0' ) {
956 value = ValueForKey( ent, "_rs" );
958 if ( value[ 0 ] == '\0' ) {
959 value = ValueForKey( ent2, "_receiveShadows" );
961 if ( value[ 0 ] == '\0' ) {
962 value = ValueForKey( ent2, "_rs" );
964 if ( value[ 0 ] != '\0' ) {
965 *recvShadows = atoi( value );
969 /* vortex: game-specific default entity keys */
970 value = ValueForKey( ent, "classname" );
971 if ( !Q_stricmp( game->magic, "dq" ) || !Q_stricmp( game->magic, "prophecy" ) ) {
972 /* vortex: deluxe quake default shadow flags */
973 if ( !Q_stricmp( value, "func_wall" ) ) {
974 if ( recvShadows != NULL ) {
977 if ( castShadows != NULL ) {