]> icculus.org git repositories - divverent/netradiant.git/blob - tools/quake3/q3map2/bspfile_abstract.c
already apply some safe changes:
[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 int CopyLump_Allocate( bspHeader_t *header, int lump, void **dest, int size, int *allocationVariable )
333 {
334         int             length, offset;
335         
336         /* get lump length and offset */
337         *allocationVariable = header->lumps[ lump ].length / size;
338         *dest = realloc(*dest, size * *allocationVariable);
339         return CopyLump(header, lump, *dest, size);
340 }
341
342
343 /*
344 AddLump()
345 adds a lump to an outgoing bsp file
346 */
347
348 void AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length )
349 {
350         bspLump_t       *lump;
351         
352         
353         /* add lump to bsp file header */
354         lump = &header->lumps[ lumpNum ];
355         lump->offset = LittleLong( ftell( file ) );
356         lump->length = LittleLong( length );
357         
358         /* write lump to file */
359         SafeWrite( file, data, (length + 3) & ~3 );
360 }
361
362
363
364 /*
365 LoadBSPFile()
366 loads a bsp file into memory
367 */
368
369 void LoadBSPFile( const char *filename )
370 {
371         /* dummy check */
372         if( game == NULL || game->load == NULL )
373                 Error( "LoadBSPFile: unsupported BSP file format" );
374         
375         /* load it, then byte swap the in-memory version */
376         game->load( filename );
377         SwapBSPFile();
378 }
379
380
381
382 /*
383 WriteBSPFile()
384 writes a bsp file
385 */
386
387 void WriteBSPFile( const char *filename )
388 {
389         char    tempname[ 1024 ];
390         time_t  tm;
391         
392         
393         /* dummy check */
394         if( game == NULL || game->write == NULL )
395                 Error( "WriteBSPFile: unsupported BSP file format" );
396         
397         /* make fake temp name so existing bsp file isn't damaged in case write process fails */
398         time( &tm );
399         sprintf( tempname, "%s.%08X", filename, (int) tm );
400         
401         /* byteswap, write the bsp, then swap back so it can be manipulated further */
402         SwapBSPFile();
403         game->write( tempname );
404         SwapBSPFile();
405         
406         /* replace existing bsp file */
407         remove( filename );
408         rename( tempname, filename );
409 }
410
411
412
413 /*
414 PrintBSPFileSizes()
415 dumps info about current file
416 */
417
418 void PrintBSPFileSizes( void )
419 {
420         /* parse entities first */
421         if( numEntities <= 0 )
422                 ParseEntities();
423         
424         /* note that this is abstracted */
425         Sys_Printf( "Abstracted BSP file components (*actual sizes may differ)\n" );
426         
427         /* print various and sundry bits */
428         Sys_Printf( "%9d models        %9d\n",
429                 numBSPModels, (int) (numBSPModels * sizeof( bspModel_t )) );
430         Sys_Printf( "%9d shaders       %9d\n",
431                 numBSPShaders, (int) (numBSPShaders * sizeof( bspShader_t )) );
432         Sys_Printf( "%9d brushes       %9d\n",
433                 numBSPBrushes, (int) (numBSPBrushes * sizeof( bspBrush_t )) );
434         Sys_Printf( "%9d brushsides    %9d *\n",
435                 numBSPBrushSides, (int) (numBSPBrushSides * sizeof( bspBrushSide_t )) );
436         Sys_Printf( "%9d fogs          %9d\n",
437                 numBSPFogs, (int) (numBSPFogs * sizeof( bspFog_t ) ) );
438         Sys_Printf( "%9d planes        %9d\n",
439                 numBSPPlanes, (int) (numBSPPlanes * sizeof( bspPlane_t )) );
440         Sys_Printf( "%9d entdata       %9d\n",
441                 numEntities, bspEntDataSize );
442         Sys_Printf( "\n");
443         
444         Sys_Printf( "%9d nodes         %9d\n",
445                 numBSPNodes, (int) (numBSPNodes * sizeof( bspNode_t)) );
446         Sys_Printf( "%9d leafs         %9d\n",
447                 numBSPLeafs, (int) (numBSPLeafs * sizeof( bspLeaf_t )) );
448         Sys_Printf( "%9d leafsurfaces  %9d\n",
449                 numBSPLeafSurfaces, (int) (numBSPLeafSurfaces * sizeof( *bspLeafSurfaces )) );
450         Sys_Printf( "%9d leafbrushes   %9d\n",
451                 numBSPLeafBrushes, (int) (numBSPLeafBrushes * sizeof( *bspLeafBrushes )) );
452         Sys_Printf( "\n");
453         
454         Sys_Printf( "%9d drawsurfaces  %9d *\n",
455                 numBSPDrawSurfaces, (int) (numBSPDrawSurfaces * sizeof( *bspDrawSurfaces )) );
456         Sys_Printf( "%9d drawverts     %9d *\n",
457                 numBSPDrawVerts, (int) (numBSPDrawVerts * sizeof( *bspDrawVerts )) );
458         Sys_Printf( "%9d drawindexes   %9d\n",
459                 numBSPDrawIndexes, (int) (numBSPDrawIndexes * sizeof( *bspDrawIndexes )) );
460         Sys_Printf( "\n");
461         
462         Sys_Printf( "%9d lightmaps     %9d\n",
463                 numBSPLightBytes / (game->lightmapSize * game->lightmapSize * 3), numBSPLightBytes );
464         Sys_Printf( "%9d lightgrid     %9d *\n",
465                 numBSPGridPoints, (int) (numBSPGridPoints * sizeof( *bspGridPoints )) );
466         Sys_Printf( "          visibility    %9d\n",
467                 numBSPVisBytes );
468 }
469
470
471
472 /* -------------------------------------------------------------------------------
473
474 entity data handling
475
476 ------------------------------------------------------------------------------- */
477
478
479 /*
480 StripTrailing()
481 strips low byte chars off the end of a string
482 */
483
484 void StripTrailing( char *e )
485 {
486         char    *s;
487         
488         
489         s = e + strlen( e ) - 1;
490         while( s >= e && *s <= 32 )
491         {
492                 *s = 0;
493                 s--;
494         }
495 }
496
497
498
499 /*
500 ParseEpair()
501 parses a single quoted "key" "value" pair into an epair struct
502 */
503
504 epair_t *ParseEPair( void )
505 {
506         epair_t         *e;
507         
508         
509         /* allocate and clear new epair */
510         e = safe_malloc( sizeof( epair_t ) );
511         memset( e, 0, sizeof( epair_t ) );
512         
513         /* handle key */
514         if( strlen( token ) >= (MAX_KEY - 1) )
515                 Error( "ParseEPair: token too long" );
516         
517         e->key = copystring( token );
518         GetToken( qfalse );
519         
520         /* handle value */
521         if( strlen( token ) >= MAX_VALUE - 1 )
522                 Error( "ParseEpar: token too long" );
523         e->value = copystring( token );
524         
525         /* strip trailing spaces that sometimes get accidentally added in the editor */
526         StripTrailing( e->key );
527         StripTrailing( e->value );
528         
529         /* return it */
530         return e;
531 }
532
533
534
535 /*
536 ParseEntity()
537 parses an entity's epairs
538 */
539
540 qboolean ParseEntity( void )
541 {
542         epair_t         *e;
543         
544         
545         /* dummy check */
546         if( !GetToken( qtrue ) )
547                 return qfalse;
548         if( strcmp( token, "{" ) )
549                 Error( "ParseEntity: { not found" );
550         if( numEntities == MAX_MAP_ENTITIES )
551                 Error( "numEntities == MAX_MAP_ENTITIES" );
552         
553         /* create new entity */
554         mapEnt = &entities[ numEntities ];
555         numEntities++;
556         
557         /* parse */
558         while( 1 )
559         {
560                 if( !GetToken( qtrue ) )
561                         Error( "ParseEntity: EOF without closing brace" );
562                 if( !EPAIR_STRCMP( token, "}" ) )
563                         break;
564                 e = ParseEPair();
565                 e->next = mapEnt->epairs;
566                 mapEnt->epairs = e;
567         }
568         
569         /* return to sender */
570         return qtrue;
571 }
572
573
574
575 /*
576 ParseEntities()
577 parses the bsp entity data string into entities
578 */
579
580 void ParseEntities( void )
581 {
582         numEntities = 0;
583         ParseFromMemory( bspEntData, bspEntDataSize );
584         while( ParseEntity() );
585         
586         /* ydnar: set number of bsp entities in case a map is loaded on top */
587         numBSPEntities = numEntities;
588 }
589
590 /*
591  * must be called before UnparseEntities
592  */
593 void InjectCommandLine(char **argv, int beginArgs, int endArgs)
594 {
595         const char *previousCommandLine;
596         char newCommandLine[1024];
597         const char *inpos;
598         char *outpos = newCommandLine;
599         char *sentinel = newCommandLine + sizeof(newCommandLine) - 1;
600         int i;
601
602         previousCommandLine = ValueForKey(&entities[0], "_q3map2_cmdline");
603         if(previousCommandLine && *previousCommandLine)
604         {
605                 inpos = previousCommandLine;
606                 while(outpos != sentinel && *inpos)
607                         *outpos++ = *inpos++;
608                 if(outpos != sentinel)
609                         *outpos++ = ';';
610                 if(outpos != sentinel)
611                         *outpos++ = ' ';
612         }
613
614         for(i = beginArgs; i < endArgs; ++i)
615         {
616                 if(outpos != sentinel && i != beginArgs)
617                         *outpos++ = ' ';
618                 inpos = argv[i];
619                 while(outpos != sentinel && *inpos)
620                         if(*inpos != '\\' && *inpos != '"' && *inpos != ';' && (unsigned char) *inpos >= ' ')
621                                 *outpos++ = *inpos++;
622         }
623
624         *outpos = 0;
625         SetKeyValue(&entities[0], "_q3map2_cmdline", newCommandLine);
626         SetKeyValue(&entities[0], "_q3map2_version", Q3MAP_VERSION);
627 }
628
629 /*
630 UnparseEntities()
631 generates the dentdata string from all the entities.
632 this allows the utilities to add or remove key/value
633 pairs to the data created by the map editor
634 */
635
636 void UnparseEntities( void )
637 {
638         int                     i;
639         char            *buf, *end;
640         epair_t         *ep;
641         char            line[ 2048 ];
642         char            key[ 1024 ], value[ 1024 ];
643         const char      *value2;
644         
645         
646         /* setup */
647         buf = bspEntData;
648         end = buf;
649         *end = 0;
650         
651         /* run through entity list */
652         for( i = 0; i < numBSPEntities && i < numEntities; i++ )
653         {
654                 /* get epair */
655                 ep = entities[ i ].epairs;
656                 if( ep == NULL )
657                         continue;       /* ent got removed */
658                 
659                 /* ydnar: certain entities get stripped from bsp file */
660                 value2 = ValueForKey( &entities[ i ], "classname" );
661                 if( !Q_stricmp( value2, "misc_model" ) ||
662                         !Q_stricmp( value2, "_decal" ) ||
663                         !Q_stricmp( value2, "_skybox" ) )
664                         continue;
665                 
666                 /* add beginning brace */
667                 strcat( end, "{\n" );
668                 end += 2;
669                 
670                 /* walk epair list */
671                 for( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
672                 {
673                         /* copy and clean */
674                         strcpy( key, ep->key );
675                         StripTrailing( key );
676                         strcpy( value, ep->value );
677                         StripTrailing( value );
678                         
679                         /* add to buffer */
680                         sprintf( line, "\"%s\" \"%s\"\n", key, value );
681                         strcat( end, line );
682                         end += strlen( line );
683                 }
684                 
685                 /* add trailing brace */
686                 strcat( end,"}\n" );
687                 end += 2;
688                 
689                 /* check for overflow */
690                 if( end > buf + MAX_MAP_ENTSTRING )
691                         Error( "Entity text too long" );
692         }
693         
694         /* set size */
695         bspEntDataSize = end - buf + 1;
696 }
697
698
699
700 /*
701 PrintEntity()
702 prints an entity's epairs to the console
703 */
704
705 void PrintEntity( const entity_t *ent )
706 {
707         epair_t *ep;
708         
709
710         Sys_Printf( "------- entity %p -------\n", ent );
711         for( ep = ent->epairs; ep != NULL; ep = ep->next )
712                 Sys_Printf( "%s = %s\n", ep->key, ep->value );
713
714 }
715
716
717
718 /*
719 SetKeyValue()
720 sets an epair in an entity
721 */
722
723 void SetKeyValue( entity_t *ent, const char *key, const char *value )
724 {
725         epair_t *ep;
726         
727         
728         /* check for existing epair */
729         for( ep = ent->epairs; ep != NULL; ep = ep->next )
730         {
731                 if( !EPAIR_STRCMP( ep->key, key ) )
732                 {
733                         free( ep->value );
734                         ep->value = copystring( value );
735                         return;
736                 }
737         }
738         
739         /* create new epair */
740         ep = safe_malloc( sizeof( *ep ) );
741         ep->next = ent->epairs;
742         ent->epairs = ep;
743         ep->key = copystring( key );
744         ep->value = copystring( value );
745 }
746
747
748
749 /*
750 ValueForKey()
751 gets the value for an entity key
752 */
753
754 const char *ValueForKey( const entity_t *ent, const char *key )
755 {
756         epair_t *ep;
757         
758         
759         /* dummy check */
760         if( ent == NULL )
761                 return "";
762         
763         /* walk epair list */
764         for( ep = ent->epairs; ep != NULL; ep = ep->next )
765         {
766                 if( !EPAIR_STRCMP( ep->key, key ) )
767                         return ep->value;
768         }
769         
770         /* if no match, return empty string */
771         return "";
772 }
773
774
775
776 /*
777 IntForKey()
778 gets the integer point value for an entity key
779 */
780
781 int IntForKey( const entity_t *ent, const char *key )
782 {
783         const char      *k;
784         
785         
786         k = ValueForKey( ent, key );
787         return atoi( k );
788 }
789
790
791
792 /*
793 FloatForKey()
794 gets the floating point value for an entity key
795 */
796
797 vec_t FloatForKey( const entity_t *ent, const char *key )
798 {
799         const char      *k;
800         
801         
802         k = ValueForKey( ent, key );
803         return atof( k );
804 }
805
806
807
808 /*
809 GetVectorForKey()
810 gets a 3-element vector value for an entity key
811 */
812
813 void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec )
814 {
815         const char      *k;
816         double          v1, v2, v3;
817         
818
819         /* get value */
820         k = ValueForKey( ent, key );
821         
822         /* scanf into doubles, then assign, so it is vec_t size independent */
823         v1 = v2 = v3 = 0.0;
824         sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
825         vec[ 0 ] = v1;
826         vec[ 1 ] = v2;
827         vec[ 2 ] = v3;
828 }
829
830
831
832 /*
833 FindTargetEntity()
834 finds an entity target
835 */
836
837 entity_t *FindTargetEntity( const char *target )
838 {
839         int                     i;
840         const char      *n;
841
842         
843         /* walk entity list */
844         for( i = 0; i < numEntities; i++ )
845         {
846                 n = ValueForKey( &entities[ i ], "targetname" );
847                 if ( !strcmp( n, target ) )
848                         return &entities[ i ];
849         }
850         
851         /* nada */
852         return NULL;
853 }
854
855
856
857 /*
858 GetEntityShadowFlags() - ydnar
859 gets an entity's shadow flags
860 note: does not set them to defaults if the keys are not found!
861 */
862
863 void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows )
864 {
865         const char      *value;
866         
867         
868         /* get cast shadows */
869         if( castShadows != NULL )
870         {
871                 value = ValueForKey( ent, "_castShadows" );
872                 if( value[ 0 ] == '\0' )
873                         value = ValueForKey( ent, "_cs" );
874                 if( value[ 0 ] == '\0' )
875                         value = ValueForKey( ent2, "_castShadows" );
876                 if( value[ 0 ] == '\0' )
877                         value = ValueForKey( ent2, "_cs" );
878                 if( value[ 0 ] != '\0' )
879                         *castShadows = atoi( value );
880         }
881         
882         /* receive */
883         if( recvShadows != NULL )
884         {
885                 value = ValueForKey( ent, "_receiveShadows" );
886                 if( value[ 0 ] == '\0' )
887                         value = ValueForKey( ent, "_rs" );
888                 if( value[ 0 ] == '\0' )
889                         value = ValueForKey( ent2, "_receiveShadows" );
890                 if( value[ 0 ] == '\0' )
891                         value = ValueForKey( ent2, "_rs" );
892                 if( value[ 0 ] != '\0' )
893                         *recvShadows = atoi( value );
894         }
895 }
896