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_malloc_info( sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer, "IncDrawVerts" );
99 memset( bspDrawVerts, 0, n * sizeof( bspDrawVert_t ) );
102 int numBSPDrawSurfacesBuffer = 0;
103 void SetDrawSurfacesBuffer(){
104 if ( bspDrawSurfaces != 0 ) {
105 free( bspDrawSurfaces );
108 numBSPDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
110 bspDrawSurfaces = safe_malloc_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
112 memset( bspDrawSurfaces, 0, MAX_MAP_DRAW_SURFS * sizeof( bspDrawSurface_t ) );
115 void SetDrawSurfaces( int n ){
116 if ( bspDrawSurfaces != 0 ) {
117 free( bspDrawSurfaces );
120 numBSPDrawSurfaces = n;
121 numBSPDrawSurfacesBuffer = numBSPDrawSurfaces;
123 bspDrawSurfaces = safe_malloc_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
125 memset( bspDrawSurfaces, 0, n * sizeof( bspDrawSurface_t ) );
128 void BSPFilesCleanup(){
129 if ( bspDrawVerts != 0 ) {
130 free( bspDrawVerts );
132 if ( bspDrawSurfaces != 0 ) {
133 free( bspDrawSurfaces );
135 if ( bspLightBytes != 0 ) {
136 free( bspLightBytes );
138 if ( bspGridPoints != 0 ) {
139 free( bspGridPoints );
150 if all values are 32 bits, this can be used to swap everything
153 void SwapBlock( int *block, int size ){
158 if ( block == NULL ) {
164 for ( i = 0; i < size; i++ )
165 block[ i ] = LittleLong( block[ i ] );
172 byte swaps all data in the abstract bsp
175 void SwapBSPFile( void ){
180 SwapBlock( (int*) bspModels, numBSPModels * sizeof( bspModels[ 0 ] ) );
182 /* shaders (don't swap the name) */
183 for ( i = 0; i < numBSPShaders ; i++ )
186 si = ShaderInfoForShader( bspShaders[ i ].shader );
187 if ( si->remapShader && si->remapShader[ 0 ] ) {
188 strcpy( bspShaders[ i ].shader, si->remapShader );
191 bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );
192 bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );
196 SwapBlock( (int*) bspPlanes, numBSPPlanes * sizeof( bspPlanes[ 0 ] ) );
199 SwapBlock( (int*) bspNodes, numBSPNodes * sizeof( bspNodes[ 0 ] ) );
202 SwapBlock( (int*) bspLeafs, numBSPLeafs * sizeof( bspLeafs[ 0 ] ) );
205 SwapBlock( (int*) bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
208 SwapBlock( (int*) bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
211 SwapBlock( (int*) bspBrushes, numBSPBrushes * sizeof( bspBrushes[ 0 ] ) );
214 SwapBlock( (int*) bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
217 ( (int*) &bspVisBytes )[ 0 ] = LittleLong( ( (int*) &bspVisBytes )[ 0 ] );
218 ( (int*) &bspVisBytes )[ 1 ] = LittleLong( ( (int*) &bspVisBytes )[ 1 ] );
220 /* drawverts (don't swap colors) */
221 for ( i = 0; i < numBSPDrawVerts; i++ )
223 bspDrawVerts[ i ].xyz[ 0 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 0 ] );
224 bspDrawVerts[ i ].xyz[ 1 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 1 ] );
225 bspDrawVerts[ i ].xyz[ 2 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 2 ] );
226 bspDrawVerts[ i ].normal[ 0 ] = LittleFloat( bspDrawVerts[ i ].normal[ 0 ] );
227 bspDrawVerts[ i ].normal[ 1 ] = LittleFloat( bspDrawVerts[ i ].normal[ 1 ] );
228 bspDrawVerts[ i ].normal[ 2 ] = LittleFloat( bspDrawVerts[ i ].normal[ 2 ] );
229 bspDrawVerts[ i ].st[ 0 ] = LittleFloat( bspDrawVerts[ i ].st[ 0 ] );
230 bspDrawVerts[ i ].st[ 1 ] = LittleFloat( bspDrawVerts[ i ].st[ 1 ] );
231 for ( j = 0; j < MAX_LIGHTMAPS; j++ )
233 bspDrawVerts[ i ].lightmap[ j ][ 0 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 0 ] );
234 bspDrawVerts[ i ].lightmap[ j ][ 1 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 1 ] );
239 SwapBlock( (int*) bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[0] ) );
242 /* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */
243 SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
246 for ( i = 0; i < numBSPFogs; i++ )
248 bspFogs[ i ].brushNum = LittleLong( bspFogs[ i ].brushNum );
249 bspFogs[ i ].visibleSide = LittleLong( bspFogs[ i ].visibleSide );
253 for ( i = 0; i < numBSPAds; i++ )
255 bspAds[ i ].cellId = LittleLong( bspAds[ i ].cellId );
256 bspAds[ i ].normal[ 0 ] = LittleFloat( bspAds[ i ].normal[ 0 ] );
257 bspAds[ i ].normal[ 1 ] = LittleFloat( bspAds[ i ].normal[ 1 ] );
258 bspAds[ i ].normal[ 2 ] = LittleFloat( bspAds[ i ].normal[ 2 ] );
260 for ( j = 0; j < 4; j++ )
262 bspAds[ i ].rect[j][ 0 ] = LittleFloat( bspAds[ i ].rect[j][ 0 ] );
263 bspAds[ i ].rect[j][ 1 ] = LittleFloat( bspAds[ i ].rect[j][ 1 ] );
264 bspAds[ i ].rect[j][ 2 ] = LittleFloat( bspAds[ i ].rect[j][ 2 ] );
267 //bspAds[ i ].model[ MAX_QPATH ];
273 gets the number of elements in a bsp lump
276 int GetLumpElements( bspHeader_t *header, int lump, int size ){
277 /* check for odd size */
278 if ( header->lumps[ lump ].length % size ) {
280 Sys_FPrintf( SYS_WRN, "WARNING: GetLumpElements: odd lump size (%d) in lump %d\n", header->lumps[ lump ].length, lump );
284 Error( "GetLumpElements: odd lump size (%d) in lump %d", header->lumps[ lump ].length, lump );
288 /* return element count */
289 return header->lumps[ lump ].length / size;
296 returns a pointer to the specified lump
299 void *GetLump( bspHeader_t *header, int lump ){
300 return (void*)( (byte*) header + header->lumps[ lump ].offset );
307 copies a bsp file lump into a destination buffer
310 int CopyLump( bspHeader_t *header, int lump, void *dest, int size ){
314 /* get lump length and offset */
315 length = header->lumps[ lump ].length;
316 offset = header->lumps[ lump ].offset;
318 /* handle erroneous cases */
322 if ( length % size ) {
324 Sys_FPrintf( SYS_WRN, "WARNING: CopyLump: odd lump size (%d) in lump %d\n", length, lump );
328 Error( "CopyLump: odd lump size (%d) in lump %d", length, lump );
332 /* copy block of memory and return */
333 memcpy( dest, (byte*) header + offset, length );
334 return length / size;
337 int CopyLump_Allocate( bspHeader_t *header, int lump, void **dest, int size, int *allocationVariable ){
338 /* get lump length and offset */
339 *allocationVariable = header->lumps[ lump ].length / size;
340 *dest = realloc( *dest, size * *allocationVariable );
341 return CopyLump( header, lump, *dest, size );
347 adds a lump to an outgoing bsp file
350 void AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length ){
354 /* add lump to bsp file header */
355 lump = &header->lumps[ lumpNum ];
356 lump->offset = LittleLong( ftell( file ) );
357 lump->length = LittleLong( length );
359 /* write lump to file */
360 SafeWrite( file, data, ( length + 3 ) & ~3 );
367 loads a bsp file into memory
370 void LoadBSPFile( const char *filename ){
372 if ( game == NULL || game->load == NULL ) {
373 Error( "LoadBSPFile: unsupported BSP file format" );
376 /* load it, then byte swap the in-memory version */
377 game->load( filename );
383 partially loads a bsp file into memory
387 void PartialLoadBSPFile( const char *filename ){
389 if ( game == NULL || game->load == NULL ) {
390 Error( "LoadBSPFile: unsupported BSP file format" );
393 /* load it, then byte swap the in-memory version */
394 //game->load( filename );
395 PartialLoadIBSPFile( filename );
397 /* PartialSwapBSPFile() */
400 /* shaders (don't swap the name) */
401 for ( i = 0; i < numBSPShaders ; i++ )
403 bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );
404 bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );
408 /* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */
409 SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
417 void WriteBSPFile( const char *filename ){
418 char tempname[ 1024 ];
423 if ( game == NULL || game->write == NULL ) {
424 Error( "WriteBSPFile: unsupported BSP file format" );
427 /* make fake temp name so existing bsp file isn't damaged in case write process fails */
429 sprintf( tempname, "%s.%08X", filename, (int) tm );
431 /* byteswap, write the bsp, then swap back so it can be manipulated further */
433 game->write( tempname );
436 /* replace existing bsp file */
438 rename( tempname, filename );
445 dumps info about current file
448 void PrintBSPFileSizes( void ){
449 /* parse entities first */
450 if ( numEntities <= 0 ) {
455 for ( s = bspDrawSurfaces; s != bspDrawSurfaces + numBSPDrawSurfaces; ++s ){
456 if ( s->surfaceType == MST_PATCH )
459 /* note that this is abstracted */
460 Sys_Printf( "Abstracted BSP file components (*actual sizes may differ)\n" );
462 /* print various and sundry bits */
463 Sys_Printf( "%9d models %9d\n",
464 numBSPModels, (int) ( numBSPModels * sizeof( bspModel_t ) ) );
465 Sys_Printf( "%9d shaders %9d\n",
466 numBSPShaders, (int) ( numBSPShaders * sizeof( bspShader_t ) ) );
467 Sys_Printf( "%9d brushes %9d\n",
468 numBSPBrushes, (int) ( numBSPBrushes * sizeof( bspBrush_t ) ) );
469 Sys_Printf( "%9d brushsides %9d *\n",
470 numBSPBrushSides, (int) ( numBSPBrushSides * sizeof( bspBrushSide_t ) ) );
471 Sys_Printf( "%9d fogs %9d\n",
472 numBSPFogs, (int) ( numBSPFogs * sizeof( bspFog_t ) ) );
473 Sys_Printf( "%9d planes %9d\n",
474 numBSPPlanes, (int) ( numBSPPlanes * sizeof( bspPlane_t ) ) );
475 Sys_Printf( "%9d entdata %9d\n",
476 numEntities, bspEntDataSize );
479 Sys_Printf( "%9d nodes %9d\n",
480 numBSPNodes, (int) ( numBSPNodes * sizeof( bspNode_t ) ) );
481 Sys_Printf( "%9d leafs %9d\n",
482 numBSPLeafs, (int) ( numBSPLeafs * sizeof( bspLeaf_t ) ) );
483 Sys_Printf( "%9d leafsurfaces %9d\n",
484 numBSPLeafSurfaces, (int) ( numBSPLeafSurfaces * sizeof( *bspLeafSurfaces ) ) );
485 Sys_Printf( "%9d leafbrushes %9d\n",
486 numBSPLeafBrushes, (int) ( numBSPLeafBrushes * sizeof( *bspLeafBrushes ) ) );
489 Sys_Printf( "%9d drawsurfaces %9d *\n",
490 numBSPDrawSurfaces, (int) ( numBSPDrawSurfaces * sizeof( *bspDrawSurfaces ) ) );
491 Sys_Printf( "%9d patchsurfaces \n",
493 Sys_Printf( "%9d drawverts %9d *\n",
494 numBSPDrawVerts, (int) ( numBSPDrawVerts * sizeof( *bspDrawVerts ) ) );
495 Sys_Printf( "%9d drawindexes %9d\n",
496 numBSPDrawIndexes, (int) ( numBSPDrawIndexes * sizeof( *bspDrawIndexes ) ) );
499 Sys_Printf( "%9d lightmaps %9d\n",
500 numBSPLightBytes / ( game->lightmapSize * game->lightmapSize * 3 ), numBSPLightBytes );
501 Sys_Printf( "%9d lightgrid %9d *\n",
502 numBSPGridPoints, (int) ( numBSPGridPoints * sizeof( *bspGridPoints ) ) );
503 Sys_Printf( " visibility %9d\n",
509 /* -------------------------------------------------------------------------------
513 ------------------------------------------------------------------------------- */
518 strips low byte chars off the end of a string
521 void StripTrailing( char *e ){
525 s = e + strlen( e ) - 1;
526 while ( s >= e && *s <= 32 )
537 parses a single quoted "key" "value" pair into an epair struct
540 epair_t *ParseEPair( void ){
544 /* allocate and clear new epair */
545 e = safe_malloc( sizeof( epair_t ) );
546 memset( e, 0, sizeof( epair_t ) );
549 if ( strlen( token ) >= ( MAX_KEY - 1 ) ) {
550 Error( "ParseEPair: token too long" );
553 e->key = copystring( token );
557 if ( strlen( token ) >= MAX_VALUE - 1 ) {
558 Error( "ParseEpar: token too long" );
560 e->value = copystring( token );
562 /* strip trailing spaces that sometimes get accidentally added in the editor */
563 StripTrailing( e->key );
564 StripTrailing( e->value );
574 parses an entity's epairs
577 qboolean ParseEntity( void ){
582 if ( !GetToken( qtrue ) ) {
585 if ( strcmp( token, "{" ) ) {
586 Error( "ParseEntity: { not found" );
588 AUTOEXPAND_BY_REALLOC( entities, numEntities, allocatedEntities, 32 );
590 /* create new entity */
591 mapEnt = &entities[ numEntities ];
593 memset( mapEnt, 0, sizeof( *mapEnt ) );
598 if ( !GetToken( qtrue ) ) {
599 Error( "ParseEntity: EOF without closing brace" );
601 if ( !EPAIR_STRCMP( token, "}" ) ) {
605 e->next = mapEnt->epairs;
609 /* return to sender */
617 parses the bsp entity data string into entities
620 void ParseEntities( void ){
622 ParseFromMemory( bspEntData, bspEntDataSize );
623 while ( ParseEntity() ) ;
625 /* ydnar: set number of bsp entities in case a map is loaded on top */
626 numBSPEntities = numEntities;
632 * must be called before UnparseEntities
634 void InjectCommandLine( char **argv, int beginArgs, int endArgs ){
635 const char *previousCommandLine;
636 char newCommandLine[1024];
638 char *outpos = newCommandLine;
639 char *sentinel = newCommandLine + sizeof( newCommandLine ) - 1;
645 previousCommandLine = ValueForKey( &entities[0], "_q3map2_cmdline" );
646 if ( previousCommandLine && *previousCommandLine ) {
647 inpos = previousCommandLine;
648 while ( outpos != sentinel && *inpos )
649 *outpos++ = *inpos++;
650 if ( outpos != sentinel ) {
653 if ( outpos != sentinel ) {
658 for ( i = beginArgs; i < endArgs; ++i )
660 if ( outpos != sentinel && i != beginArgs ) {
664 while ( outpos != sentinel && *inpos )
665 if ( *inpos != '\\' && *inpos != '"' && *inpos != ';' && (unsigned char) *inpos >= ' ' ) {
666 *outpos++ = *inpos++;
671 SetKeyValue( &entities[0], "_q3map2_cmdline", newCommandLine );
672 SetKeyValue( &entities[0], "_q3map2_version", Q3MAP_VERSION );
679 generates the dentdata string from all the entities.
680 this allows the utilities to add or remove key/value
681 pairs to the data created by the map editor
684 void UnparseEntities( void ){
689 char key[ 1024 ], value[ 1024 ];
694 AUTOEXPAND_BY_REALLOC( bspEntData, 0, allocatedBSPEntData, 1024 );
700 /* run through entity list */
701 for ( i = 0; i < numBSPEntities && i < numEntities; i++ )
705 AUTOEXPAND_BY_REALLOC( bspEntData, sz + 65536, allocatedBSPEntData, 1024 );
711 ep = entities[ i ].epairs;
713 continue; /* ent got removed */
716 /* ydnar: certain entities get stripped from bsp file */
717 value2 = ValueForKey( &entities[ i ], "classname" );
718 if ( !Q_stricmp( value2, "misc_model" ) ||
719 !Q_stricmp( value2, "_decal" ) ||
720 !Q_stricmp( value2, "_skybox" ) ) {
724 /* add beginning brace */
725 strcat( end, "{\n" );
728 /* walk epair list */
729 for ( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
732 strcpy( key, ep->key );
733 StripTrailing( key );
734 strcpy( value, ep->value );
735 StripTrailing( value );
738 sprintf( line, "\"%s\" \"%s\"\n", key, value );
740 end += strlen( line );
743 /* add trailing brace */
747 /* check for overflow */
748 if ( end > buf + allocatedBSPEntData ) {
749 Error( "Entity text too long" );
754 bspEntDataSize = end - buf + 1;
761 prints an entity's epairs to the console
764 void PrintEntity( const entity_t *ent ){
768 Sys_Printf( "------- entity %p -------\n", ent );
769 for ( ep = ent->epairs; ep != NULL; ep = ep->next )
770 Sys_Printf( "%s = %s\n", ep->key, ep->value );
778 sets an epair in an entity
781 void SetKeyValue( entity_t *ent, const char *key, const char *value ){
785 /* check for existing epair */
786 for ( ep = ent->epairs; ep != NULL; ep = ep->next )
788 if ( !EPAIR_STRCMP( ep->key, key ) ) {
790 ep->value = copystring( value );
795 /* create new epair */
796 ep = safe_malloc( sizeof( *ep ) );
797 ep->next = ent->epairs;
799 ep->key = copystring( key );
800 ep->value = copystring( value );
807 returns true if entity has this key
810 qboolean KeyExists( const entity_t *ent, const char *key ){
813 /* walk epair list */
814 for ( ep = ent->epairs; ep != NULL; ep = ep->next )
816 if ( !EPAIR_STRCMP( ep->key, key ) ) {
829 gets the value for an entity key
832 const char *ValueForKey( const entity_t *ent, const char *key ){
841 /* walk epair list */
842 for ( ep = ent->epairs; ep != NULL; ep = ep->next )
844 if ( !EPAIR_STRCMP( ep->key, key ) ) {
849 /* if no match, return empty string */
857 gets the integer point value for an entity key
860 int IntForKey( const entity_t *ent, const char *key ){
864 k = ValueForKey( ent, key );
872 gets the floating point value for an entity key
875 vec_t FloatForKey( const entity_t *ent, const char *key ){
879 k = ValueForKey( ent, key );
887 gets a 3-element vector value for an entity key
890 qboolean GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ){
896 k = ValueForKey( ent, key );
898 /* scanf into doubles, then assign, so it is vec_t size independent */
900 sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
905 /* true if the key is found, false otherwise */
913 finds an entity target
916 entity_t *FindTargetEntity( const char *target ){
921 /* walk entity list */
922 for ( i = 0; i < numEntities; i++ )
924 n = ValueForKey( &entities[ i ], "targetname" );
925 if ( !strcmp( n, target ) ) {
926 return &entities[ i ];
937 GetEntityShadowFlags() - ydnar
938 gets an entity's shadow flags
939 note: does not set them to defaults if the keys are not found!
942 void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows ){
945 /* get cast shadows */
946 if ( castShadows != NULL ) {
947 value = ValueForKey( ent, "_castShadows" );
948 if ( value[ 0 ] == '\0' ) {
949 value = ValueForKey( ent, "_cs" );
951 if ( value[ 0 ] == '\0' ) {
952 value = ValueForKey( ent2, "_castShadows" );
954 if ( value[ 0 ] == '\0' ) {
955 value = ValueForKey( ent2, "_cs" );
957 if ( value[ 0 ] != '\0' ) {
958 *castShadows = atoi( value );
963 if ( recvShadows != NULL ) {
964 value = ValueForKey( ent, "_receiveShadows" );
965 if ( value[ 0 ] == '\0' ) {
966 value = ValueForKey( ent, "_rs" );
968 if ( value[ 0 ] == '\0' ) {
969 value = ValueForKey( ent2, "_receiveShadows" );
971 if ( value[ 0 ] == '\0' ) {
972 value = ValueForKey( ent2, "_rs" );
974 if ( value[ 0 ] != '\0' ) {
975 *recvShadows = atoi( value );
979 /* vortex: game-specific default entity keys */
980 value = ValueForKey( ent, "classname" );
981 if ( !Q_stricmp( game->magic, "dq" ) || !Q_stricmp( game->magic, "prophecy" ) ) {
982 /* vortex: deluxe quake default shadow flags */
983 if ( !Q_stricmp( value, "func_wall" ) ) {
984 if ( recvShadows != NULL ) {
987 if ( castShadows != NULL ) {