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