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