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