]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/bspfile_abstract.c
ae2fec3091c7bb89916f85d12d03d3d485c6e371
[xonotic/netradiant.git] / tools / quake3 / q3map2 / bspfile_abstract.c
1 /*
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 ----------------------------------------------------------------------------------
22
23 This code has been altered significantly from its original form, to support
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."
25
26 ------------------------------------------------------------------------------- */
27
28
29
30 /* marker */
31 #define BSPFILE_ABSTRACT_C
32
33
34
35 /* dependencies */
36 #include "q3map2.h"
37
38
39
40
41 /* -------------------------------------------------------------------------------
42
43 this file was copied out of the common directory in order to not break
44 compatibility with the q3map 1.x tree. it was moved out in order to support
45 the raven bsp format (RBSP) used in soldier of fortune 2 and jedi knight 2.
46
47 since each game has its own set of particular features, the data structures
48 below no longer directly correspond to the binary format of a particular game.
49
50 the translation will be done at bsp load/save time to keep any sort of
51 special-case code messiness out of the rest of the program.
52
53 ------------------------------------------------------------------------------- */
54
55
56
57 /* FIXME: remove the functions below that handle memory management of bsp file chunks */
58
59 int numBSPDrawVertsBuffer = 0;
60 void IncDrawVerts()
61 {
62         numBSPDrawVerts++;
63
64         if(bspDrawVerts == 0)
65         {
66                 numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS / 37;
67                 
68                 bspDrawVerts = safe_malloc_info(sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer, "IncDrawVerts");
69
70         }
71         else if(numBSPDrawVerts > numBSPDrawVertsBuffer)
72         {
73                 numBSPDrawVertsBuffer *= 3; // multiply by 1.5
74                 numBSPDrawVertsBuffer /= 2;
75
76                 if(numBSPDrawVertsBuffer > MAX_MAP_DRAW_VERTS)
77                         numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS;
78
79                 bspDrawVerts = realloc(bspDrawVerts, sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer);
80
81                 if(!bspDrawVerts)
82                         Error( "realloc() failed (IncDrawVerts)");
83         }
84
85         memset(bspDrawVerts + (numBSPDrawVerts - 1), 0, sizeof(bspDrawVert_t));
86 }
87
88 void SetDrawVerts(int n)
89 {
90         if(bspDrawVerts != 0)
91                 free(bspDrawVerts);
92
93         numBSPDrawVerts = n;
94         numBSPDrawVertsBuffer = numBSPDrawVerts;
95
96         bspDrawVerts = safe_malloc_info(sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer, "IncDrawVerts");
97
98         memset(bspDrawVerts, 0, n * sizeof(bspDrawVert_t));
99 }
100
101 int numBSPDrawSurfacesBuffer = 0;
102 void SetDrawSurfacesBuffer()
103 {
104         if(bspDrawSurfaces != 0)
105                 free(bspDrawSurfaces);
106
107         numBSPDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
108
109         bspDrawSurfaces = safe_malloc_info(sizeof(bspDrawSurface_t) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces");
110
111         memset(bspDrawSurfaces, 0, MAX_MAP_DRAW_SURFS * sizeof(bspDrawVert_t));
112 }
113
114 void SetDrawSurfaces(int n)
115 {
116         if(bspDrawSurfaces != 0)
117                 free(bspDrawSurfaces);
118
119         numBSPDrawSurfaces = n;
120         numBSPDrawSurfacesBuffer = numBSPDrawSurfaces;
121
122         bspDrawSurfaces = safe_malloc_info(sizeof(bspDrawSurface_t) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces");
123
124         memset(bspDrawSurfaces, 0, n * sizeof(bspDrawVert_t));
125 }
126
127 void BSPFilesCleanup()
128 {
129         if(bspDrawVerts != 0)
130                 free(bspDrawVerts);
131         if(bspDrawSurfaces != 0)
132                 free(bspDrawSurfaces);
133         if(bspLightBytes != 0)
134                 free(bspLightBytes);
135         if(bspGridPoints != 0)
136                 free(bspGridPoints);
137 }
138
139
140
141
142
143
144 /*
145 SwapBlock()
146 if all values are 32 bits, this can be used to swap everything
147 */
148
149 void SwapBlock( int *block, int size )
150 {
151         int             i;
152         
153         
154         /* dummy check */
155         if( block == NULL )
156                 return;
157         
158         /* swap */
159         size >>= 2;
160         for( i = 0; i < size; i++ )
161                 block[ i ] = LittleLong( block[ i ] );
162 }
163
164
165
166 /*
167 SwapBSPFile()
168 byte swaps all data in the abstract bsp
169 */
170
171 void SwapBSPFile( void )
172 {
173         int             i, j;
174         
175         
176         /* models */
177         SwapBlock( (int*) bspModels, numBSPModels * sizeof( bspModels[ 0 ] ) );
178
179         /* shaders (don't swap the name) */
180         for( i = 0; i < numBSPShaders ; i++ )
181         {
182                 bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );
183                 bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );
184         }
185
186         /* planes */
187         SwapBlock( (int*) bspPlanes, numBSPPlanes * sizeof( bspPlanes[ 0 ] ) );
188         
189         /* nodes */
190         SwapBlock( (int*) bspNodes, numBSPNodes * sizeof( bspNodes[ 0 ] ) );
191
192         /* leafs */
193         SwapBlock( (int*) bspLeafs, numBSPLeafs * sizeof( bspLeafs[ 0 ] ) );
194
195         /* leaffaces */
196         SwapBlock( (int*) bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
197
198         /* leafbrushes */
199         SwapBlock( (int*) bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
200
201         // brushes
202         SwapBlock( (int*) bspBrushes, numBSPBrushes * sizeof( bspBrushes[ 0 ] ) );
203
204         // brushsides
205         SwapBlock( (int*) bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
206
207         // vis
208         ((int*) &bspVisBytes)[ 0 ] = LittleLong( ((int*) &bspVisBytes)[ 0 ] );
209         ((int*) &bspVisBytes)[ 1 ] = LittleLong( ((int*) &bspVisBytes)[ 1 ] );
210
211         /* drawverts (don't swap colors) */
212         for( i = 0; i < numBSPDrawVerts; i++ )
213         {
214                 bspDrawVerts[ i ].xyz[ 0 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 0 ] );
215                 bspDrawVerts[ i ].xyz[ 1 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 1 ] );
216                 bspDrawVerts[ i ].xyz[ 2 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 2 ] );
217                 bspDrawVerts[ i ].normal[ 0 ] = LittleFloat( bspDrawVerts[ i ].normal[ 0 ] );
218                 bspDrawVerts[ i ].normal[ 1 ] = LittleFloat( bspDrawVerts[ i ].normal[ 1 ] );
219                 bspDrawVerts[ i ].normal[ 2 ] = LittleFloat( bspDrawVerts[ i ].normal[ 2 ] );
220                 bspDrawVerts[ i ].st[ 0 ] = LittleFloat( bspDrawVerts[ i ].st[ 0 ] );
221                 bspDrawVerts[ i ].st[ 1 ] = LittleFloat( bspDrawVerts[ i ].st[ 1 ] );
222                 for( j = 0; j < MAX_LIGHTMAPS; j++ )
223                 {
224                         bspDrawVerts[ i ].lightmap[ j ][ 0 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 0 ] );
225                         bspDrawVerts[ i ].lightmap[ j ][ 1 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 1 ] );
226                 }
227         }
228         
229         /* drawindexes */
230         SwapBlock( (int*) bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[0] ) );
231
232         /* drawsurfs */
233         /* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */
234         SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
235
236         /* fogs */
237         for( i = 0; i < numBSPFogs; i++ )
238         {
239                 bspFogs[ i ].brushNum = LittleLong( bspFogs[ i ].brushNum );
240                 bspFogs[ i ].visibleSide = LittleLong( bspFogs[ i ].visibleSide );
241         }
242 }
243
244
245
246 /*
247 GetLumpElements()
248 gets the number of elements in a bsp lump
249 */
250
251 int GetLumpElements( bspHeader_t *header, int lump, int size )
252 {
253         /* check for odd size */
254         if( header->lumps[ lump ].length % size )
255         {
256                 if( force )
257                 {
258                         Sys_Printf( "WARNING: GetLumpElements: odd lump size (%d) in lump %d\n", header->lumps[ lump ].length, lump );
259                         return 0;
260                 }
261                 else
262                         Error( "GetLumpElements: odd lump size (%d) in lump %d", header->lumps[ lump ].length, lump );
263         }
264         
265         /* return element count */
266         return header->lumps[ lump ].length / size;
267 }
268
269
270
271 /*
272 GetLump()
273 returns a pointer to the specified lump
274 */
275
276 void *GetLump( bspHeader_t *header, int lump )
277 {
278         return (void*)( (byte*) header + header->lumps[ lump ].offset);
279 }
280
281
282
283 /*
284 CopyLump()
285 copies a bsp file lump into a destination buffer
286 */
287
288 int CopyLump( bspHeader_t *header, int lump, void *dest, int size )
289 {
290         int             length, offset;
291         
292         
293         /* get lump length and offset */
294         length = header->lumps[ lump ].length;
295         offset = header->lumps[ lump ].offset;
296         
297         /* handle erroneous cases */
298         if( length == 0 )
299                 return 0;
300         if( length % size )
301         {
302                 if( force )
303                 {
304                         Sys_Printf( "WARNING: CopyLump: odd lump size (%d) in lump %d\n", length, lump );
305                         return 0;
306                 }
307                 else
308                         Error( "CopyLump: odd lump size (%d) in lump %d", length, lump );
309         }
310         
311         /* copy block of memory and return */
312         memcpy( dest, (byte*) header + offset, length );
313         return length / size;
314 }
315
316
317
318 /*
319 AddLump()
320 adds a lump to an outgoing bsp file
321 */
322
323 void AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length )
324 {
325         bspLump_t       *lump;
326         
327         
328         /* add lump to bsp file header */
329         lump = &header->lumps[ lumpNum ];
330         lump->offset = LittleLong( ftell( file ) );
331         lump->length = LittleLong( length );
332         
333         /* write lump to file */
334         SafeWrite( file, data, (length + 3) & ~3 );
335 }
336
337
338
339 /*
340 LoadBSPFile()
341 loads a bsp file into memory
342 */
343
344 void LoadBSPFile( const char *filename )
345 {
346         /* dummy check */
347         if( game == NULL || game->load == NULL )
348                 Error( "LoadBSPFile: unsupported BSP file format" );
349         
350         /* load it, then byte swap the in-memory version */
351         game->load( filename );
352         SwapBSPFile();
353 }
354
355
356
357 /*
358 WriteBSPFile()
359 writes a bsp file
360 */
361
362 void WriteBSPFile( const char *filename )
363 {
364         char    tempname[ 1024 ];
365         time_t  tm;
366         
367         
368         /* dummy check */
369         if( game == NULL || game->write == NULL )
370                 Error( "WriteBSPFile: unsupported BSP file format" );
371         
372         /* make fake temp name so existing bsp file isn't damaged in case write process fails */
373         time( &tm );
374         sprintf( tempname, "%s.%08X", filename, (int) tm );
375         
376         /* byteswap, write the bsp, then swap back so it can be manipulated further */
377         SwapBSPFile();
378         game->write( tempname );
379         SwapBSPFile();
380         
381         /* replace existing bsp file */
382         remove( filename );
383         rename( tempname, filename );
384 }
385
386
387
388 /*
389 PrintBSPFileSizes()
390 dumps info about current file
391 */
392
393 void PrintBSPFileSizes( void )
394 {
395         /* parse entities first */
396         if( numEntities <= 0 )
397                 ParseEntities();
398         
399         /* note that this is abstracted */
400         Sys_Printf( "Abstracted BSP file components (*actual sizes may differ)\n" );
401         
402         /* print various and sundry bits */
403         Sys_Printf( "%9d models        %9d\n",
404                 numBSPModels, (int) (numBSPModels * sizeof( bspModel_t )) );
405         Sys_Printf( "%9d shaders       %9d\n",
406                 numBSPShaders, (int) (numBSPShaders * sizeof( bspShader_t )) );
407         Sys_Printf( "%9d brushes       %9d\n",
408                 numBSPBrushes, (int) (numBSPBrushes * sizeof( bspBrush_t )) );
409         Sys_Printf( "%9d brushsides    %9d *\n",
410                 numBSPBrushSides, (int) (numBSPBrushSides * sizeof( bspBrushSide_t )) );
411         Sys_Printf( "%9d fogs          %9d\n",
412                 numBSPFogs, (int) (numBSPFogs * sizeof( bspFog_t ) ) );
413         Sys_Printf( "%9d planes        %9d\n",
414                 numBSPPlanes, (int) (numBSPPlanes * sizeof( bspPlane_t )) );
415         Sys_Printf( "%9d entdata       %9d\n",
416                 numEntities, bspEntDataSize );
417         Sys_Printf( "\n");
418         
419         Sys_Printf( "%9d nodes         %9d\n",
420                 numBSPNodes, (int) (numBSPNodes * sizeof( bspNode_t)) );
421         Sys_Printf( "%9d leafs         %9d\n",
422                 numBSPLeafs, (int) (numBSPLeafs * sizeof( bspLeaf_t )) );
423         Sys_Printf( "%9d leafsurfaces  %9d\n",
424                 numBSPLeafSurfaces, (int) (numBSPLeafSurfaces * sizeof( *bspLeafSurfaces )) );
425         Sys_Printf( "%9d leafbrushes   %9d\n",
426                 numBSPLeafBrushes, (int) (numBSPLeafBrushes * sizeof( *bspLeafBrushes )) );
427         Sys_Printf( "\n");
428         
429         Sys_Printf( "%9d drawsurfaces  %9d *\n",
430                 numBSPDrawSurfaces, (int) (numBSPDrawSurfaces * sizeof( *bspDrawSurfaces )) );
431         Sys_Printf( "%9d drawverts     %9d *\n",
432                 numBSPDrawVerts, (int) (numBSPDrawVerts * sizeof( *bspDrawVerts )) );
433         Sys_Printf( "%9d drawindexes   %9d\n",
434                 numBSPDrawIndexes, (int) (numBSPDrawIndexes * sizeof( *bspDrawIndexes )) );
435         Sys_Printf( "\n");
436         
437         Sys_Printf( "%9d lightmaps     %9d\n",
438                 numBSPLightBytes / (LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3), numBSPLightBytes );
439         Sys_Printf( "%9d lightgrid     %9d *\n",
440                 numBSPGridPoints, (int) (numBSPGridPoints * sizeof( *bspGridPoints )) );
441         Sys_Printf( "          visibility    %9d\n",
442                 numBSPVisBytes );
443 }
444
445
446
447 /* -------------------------------------------------------------------------------
448
449 entity data handling
450
451 ------------------------------------------------------------------------------- */
452
453
454 /*
455 StripTrailing()
456 strips low byte chars off the end of a string
457 */
458
459 void StripTrailing( char *e )
460 {
461         char    *s;
462         
463         
464         s = e + strlen( e ) - 1;
465         while( s >= e && *s <= 32 )
466         {
467                 *s = 0;
468                 s--;
469         }
470 }
471
472
473
474 /*
475 ParseEpair()
476 parses a single quoted "key" "value" pair into an epair struct
477 */
478
479 epair_t *ParseEPair( void )
480 {
481         epair_t         *e;
482         
483         
484         /* allocate and clear new epair */
485         e = safe_malloc( sizeof( epair_t ) );
486         memset( e, 0, sizeof( epair_t ) );
487         
488         /* handle key */
489         if( strlen( token ) >= (MAX_KEY - 1) )
490                 Error( "ParseEPair: token too long" );
491         
492         e->key = copystring( token );
493         GetToken( qfalse );
494         
495         /* handle value */
496         if( strlen( token ) >= MAX_VALUE - 1 )
497                 Error( "ParseEpar: token too long" );
498         e->value = copystring( token );
499         
500         /* strip trailing spaces that sometimes get accidentally added in the editor */
501         StripTrailing( e->key );
502         StripTrailing( e->value );
503         
504         /* return it */
505         return e;
506 }
507
508
509
510 /*
511 ParseEntity()
512 parses an entity's epairs
513 */
514
515 qboolean ParseEntity( void )
516 {
517         epair_t         *e;
518         
519         
520         /* dummy check */
521         if( !GetToken( qtrue ) )
522                 return qfalse;
523         if( strcmp( token, "{" ) )
524                 Error( "ParseEntity: { not found" );
525         if( numEntities == MAX_MAP_ENTITIES )
526                 Error( "numEntities == MAX_MAP_ENTITIES" );
527         
528         /* create new entity */
529         mapEnt = &entities[ numEntities ];
530         numEntities++;
531         
532         /* parse */
533         while( 1 )
534         {
535                 if( !GetToken( qtrue ) )
536                         Error( "ParseEntity: EOF without closing brace" );
537                 if( !EPAIR_STRCMP( token, "}" ) )
538                         break;
539                 e = ParseEPair();
540                 e->next = mapEnt->epairs;
541                 mapEnt->epairs = e;
542         }
543         
544         /* return to sender */
545         return qtrue;
546 }
547
548
549
550 /*
551 ParseEntities()
552 parses the bsp entity data string into entities
553 */
554
555 void ParseEntities( void )
556 {
557         numEntities = 0;
558         ParseFromMemory( bspEntData, bspEntDataSize );
559         while( ParseEntity() );
560         
561         /* ydnar: set number of bsp entities in case a map is loaded on top */
562         numBSPEntities = numEntities;
563 }
564
565
566
567 /*
568 UnparseEntities()
569 generates the dentdata string from all the entities.
570 this allows the utilities to add or remove key/value
571 pairs to the data created by the map editor
572 */
573
574 void UnparseEntities( void )
575 {
576         int                     i;
577         char            *buf, *end;
578         epair_t         *ep;
579         char            line[ 2048 ];
580         char            key[ 1024 ], value[ 1024 ];
581         const char      *value2;
582         
583         
584         /* setup */
585         buf = bspEntData;
586         end = buf;
587         *end = 0;
588         
589         /* run through entity list */
590         for( i = 0; i < numBSPEntities && i < numEntities; i++ )
591         {
592                 /* get epair */
593                 ep = entities[ i ].epairs;
594                 if( ep == NULL )
595                         continue;       /* ent got removed */
596                 
597                 /* ydnar: certain entities get stripped from bsp file */
598                 value2 = ValueForKey( &entities[ i ], "classname" );
599                 if( !Q_stricmp( value2, "misc_model" ) ||
600                         !Q_stricmp( value2, "_decal" ) ||
601                         !Q_stricmp( value2, "_skybox" ) )
602                         continue;
603                 
604                 /* add beginning brace */
605                 strcat( end, "{\n" );
606                 end += 2;
607                 
608                 /* walk epair list */
609                 for( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
610                 {
611                         /* copy and clean */
612                         strcpy( key, ep->key );
613                         StripTrailing( key );
614                         strcpy( value, ep->value );
615                         StripTrailing( value );
616                         
617                         /* add to buffer */
618                         sprintf( line, "\"%s\" \"%s\"\n", key, value );
619                         strcat( end, line );
620                         end += strlen( line );
621                 }
622                 
623                 /* add trailing brace */
624                 strcat( end,"}\n" );
625                 end += 2;
626                 
627                 /* check for overflow */
628                 if( end > buf + MAX_MAP_ENTSTRING )
629                         Error( "Entity text too long" );
630         }
631         
632         /* set size */
633         bspEntDataSize = end - buf + 1;
634 }
635
636
637
638 /*
639 PrintEntity()
640 prints an entity's epairs to the console
641 */
642
643 void PrintEntity( const entity_t *ent )
644 {
645         epair_t *ep;
646         
647
648         Sys_Printf( "------- entity %p -------\n", ent );
649         for( ep = ent->epairs; ep != NULL; ep = ep->next )
650                 Sys_Printf( "%s = %s\n", ep->key, ep->value );
651
652 }
653
654
655
656 /*
657 SetKeyValue()
658 sets an epair in an entity
659 */
660
661 void SetKeyValue( entity_t *ent, const char *key, const char *value )
662 {
663         epair_t *ep;
664         
665         
666         /* check for existing epair */
667         for( ep = ent->epairs; ep != NULL; ep = ep->next )
668         {
669                 if( !EPAIR_STRCMP( ep->key, key ) )
670                 {
671                         free( ep->value );
672                         ep->value = copystring( value );
673                         return;
674                 }
675         }
676         
677         /* create new epair */
678         ep = safe_malloc( sizeof( *ep ) );
679         ep->next = ent->epairs;
680         ent->epairs = ep;
681         ep->key = copystring( key );
682         ep->value = copystring( value );
683 }
684
685
686
687 /*
688 ValueForKey()
689 gets the value for an entity key
690 */
691
692 const char *ValueForKey( const entity_t *ent, const char *key )
693 {
694         epair_t *ep;
695         
696         
697         /* dummy check */
698         if( ent == NULL )
699                 return "";
700         
701         /* walk epair list */
702         for( ep = ent->epairs; ep != NULL; ep = ep->next )
703         {
704                 if( !EPAIR_STRCMP( ep->key, key ) )
705                         return ep->value;
706         }
707         
708         /* if no match, return empty string */
709         return "";
710 }
711
712
713
714 /*
715 IntForKey()
716 gets the integer point value for an entity key
717 */
718
719 int IntForKey( const entity_t *ent, const char *key )
720 {
721         const char      *k;
722         
723         
724         k = ValueForKey( ent, key );
725         return atoi( k );
726 }
727
728
729
730 /*
731 FloatForKey()
732 gets the floating point value for an entity key
733 */
734
735 vec_t FloatForKey( const entity_t *ent, const char *key )
736 {
737         const char      *k;
738         
739         
740         k = ValueForKey( ent, key );
741         return atof( k );
742 }
743
744
745
746 /*
747 GetVectorForKey()
748 gets a 3-element vector value for an entity key
749 */
750
751 void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec )
752 {
753         const char      *k;
754         double          v1, v2, v3;
755         
756
757         /* get value */
758         k = ValueForKey( ent, key );
759         
760         /* scanf into doubles, then assign, so it is vec_t size independent */
761         v1 = v2 = v3 = 0.0;
762         sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
763         vec[ 0 ] = v1;
764         vec[ 1 ] = v2;
765         vec[ 2 ] = v3;
766 }
767
768
769
770 /*
771 FindTargetEntity()
772 finds an entity target
773 */
774
775 entity_t *FindTargetEntity( const char *target )
776 {
777         int                     i;
778         const char      *n;
779
780         
781         /* walk entity list */
782         for( i = 0; i < numEntities; i++ )
783         {
784                 n = ValueForKey( &entities[ i ], "targetname" );
785                 if ( !strcmp( n, target ) )
786                         return &entities[ i ];
787         }
788         
789         /* nada */
790         return NULL;
791 }
792
793
794
795 /*
796 GetEntityShadowFlags() - ydnar
797 gets an entity's shadow flags
798 note: does not set them to defaults if the keys are not found!
799 */
800
801 void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows )
802 {
803         const char      *value;
804         
805         
806         /* get cast shadows */
807         if( castShadows != NULL )
808         {
809                 value = ValueForKey( ent, "_castShadows" );
810                 if( value[ 0 ] == '\0' )
811                         value = ValueForKey( ent, "_cs" );
812                 if( value[ 0 ] == '\0' )
813                         value = ValueForKey( ent2, "_castShadows" );
814                 if( value[ 0 ] == '\0' )
815                         value = ValueForKey( ent2, "_cs" );
816                 if( value[ 0 ] != '\0' )
817                         *castShadows = atoi( value );
818         }
819         
820         /* receive */
821         if( recvShadows != NULL )
822         {
823                 value = ValueForKey( ent, "_receiveShadows" );
824                 if( value[ 0 ] == '\0' )
825                         value = ValueForKey( ent, "_rs" );
826                 if( value[ 0 ] == '\0' )
827                         value = ValueForKey( ent2, "_receiveShadows" );
828                 if( value[ 0 ] == '\0' )
829                         value = ValueForKey( ent2, "_rs" );
830                 if( value[ 0 ] != '\0' )
831                         *recvShadows = atoi( value );
832         }
833 }
834