]> git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/bspfile_abstract.c
Q3map2:
[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_malloc_info( sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer, "IncDrawVerts" );
93
94         memset( bspDrawVerts, 0, n * sizeof( bspDrawVert_t ) );
95 }
96
97 int numBSPDrawSurfacesBuffer = 0;
98 void SetDrawSurfacesBuffer(){
99         if ( bspDrawSurfaces != 0 ) {
100                 free( bspDrawSurfaces );
101         }
102
103         numBSPDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
104
105         bspDrawSurfaces = safe_malloc_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
106
107         memset( bspDrawSurfaces, 0, MAX_MAP_DRAW_SURFS * sizeof( bspDrawVert_t ) );
108 }
109
110 void SetDrawSurfaces( int n ){
111         if ( bspDrawSurfaces != 0 ) {
112                 free( bspDrawSurfaces );
113         }
114
115         numBSPDrawSurfaces = n;
116         numBSPDrawSurfacesBuffer = numBSPDrawSurfaces;
117
118         bspDrawSurfaces = safe_malloc_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
119
120         memset( bspDrawSurfaces, 0, n * sizeof( bspDrawVert_t ) );
121 }
122
123 void BSPFilesCleanup(){
124         if ( bspDrawVerts != 0 ) {
125                 free( bspDrawVerts );
126         }
127         if ( bspDrawSurfaces != 0 ) {
128                 free( bspDrawSurfaces );
129         }
130         if ( bspLightBytes != 0 ) {
131                 free( bspLightBytes );
132         }
133         if ( bspGridPoints != 0 ) {
134                 free( bspGridPoints );
135         }
136 }
137
138
139
140
141
142
143 /*
144    SwapBlock()
145    if all values are 32 bits, this can be used to swap everything
146  */
147
148 void SwapBlock( int *block, int size ){
149         int i;
150
151
152         /* dummy check */
153         if ( block == NULL ) {
154                 return;
155         }
156
157         /* swap */
158         size >>= 2;
159         for ( i = 0; i < size; i++ )
160                 block[ i ] = LittleLong( block[ i ] );
161 }
162
163
164
165 /*
166    SwapBSPFile()
167    byte swaps all data in the abstract bsp
168  */
169
170 void SwapBSPFile( void ){
171         int i, j;
172         shaderInfo_t    *si;
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         if ( doingBSP ){
181                 si = ShaderInfoForShader( bspShaders[ i ].shader );
182                 if ( si->remapShader && si->remapShader[ 0 ] ) {
183                         strcpy( bspShaders[ i ].shader, si->remapShader );
184                 }
185         }
186                 bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );
187                 bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );
188         }
189
190         /* planes */
191         SwapBlock( (int*) bspPlanes, numBSPPlanes * sizeof( bspPlanes[ 0 ] ) );
192
193         /* nodes */
194         SwapBlock( (int*) bspNodes, numBSPNodes * sizeof( bspNodes[ 0 ] ) );
195
196         /* leafs */
197         SwapBlock( (int*) bspLeafs, numBSPLeafs * sizeof( bspLeafs[ 0 ] ) );
198
199         /* leaffaces */
200         SwapBlock( (int*) bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
201
202         /* leafbrushes */
203         SwapBlock( (int*) bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
204
205         // brushes
206         SwapBlock( (int*) bspBrushes, numBSPBrushes * sizeof( bspBrushes[ 0 ] ) );
207
208         // brushsides
209         SwapBlock( (int*) bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
210
211         // vis
212         ( (int*) &bspVisBytes )[ 0 ] = LittleLong( ( (int*) &bspVisBytes )[ 0 ] );
213         ( (int*) &bspVisBytes )[ 1 ] = LittleLong( ( (int*) &bspVisBytes )[ 1 ] );
214
215         /* drawverts (don't swap colors) */
216         for ( i = 0; i < numBSPDrawVerts; i++ )
217         {
218                 bspDrawVerts[ i ].xyz[ 0 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 0 ] );
219                 bspDrawVerts[ i ].xyz[ 1 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 1 ] );
220                 bspDrawVerts[ i ].xyz[ 2 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 2 ] );
221                 bspDrawVerts[ i ].normal[ 0 ] = LittleFloat( bspDrawVerts[ i ].normal[ 0 ] );
222                 bspDrawVerts[ i ].normal[ 1 ] = LittleFloat( bspDrawVerts[ i ].normal[ 1 ] );
223                 bspDrawVerts[ i ].normal[ 2 ] = LittleFloat( bspDrawVerts[ i ].normal[ 2 ] );
224                 bspDrawVerts[ i ].st[ 0 ] = LittleFloat( bspDrawVerts[ i ].st[ 0 ] );
225                 bspDrawVerts[ i ].st[ 1 ] = LittleFloat( bspDrawVerts[ i ].st[ 1 ] );
226                 for ( j = 0; j < MAX_LIGHTMAPS; j++ )
227                 {
228                         bspDrawVerts[ i ].lightmap[ j ][ 0 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 0 ] );
229                         bspDrawVerts[ i ].lightmap[ j ][ 1 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 1 ] );
230                 }
231         }
232
233         /* drawindexes */
234         SwapBlock( (int*) bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[0] ) );
235
236         /* drawsurfs */
237         /* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */
238         SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
239
240         /* fogs */
241         for ( i = 0; i < numBSPFogs; i++ )
242         {
243                 bspFogs[ i ].brushNum = LittleLong( bspFogs[ i ].brushNum );
244                 bspFogs[ i ].visibleSide = LittleLong( bspFogs[ i ].visibleSide );
245         }
246
247         /* advertisements */
248         for ( i = 0; i < numBSPAds; i++ )
249         {
250                 bspAds[ i ].cellId = LittleLong( bspAds[ i ].cellId );
251                 bspAds[ i ].normal[ 0 ] = LittleFloat( bspAds[ i ].normal[ 0 ] );
252                 bspAds[ i ].normal[ 1 ] = LittleFloat( bspAds[ i ].normal[ 1 ] );
253                 bspAds[ i ].normal[ 2 ] = LittleFloat( bspAds[ i ].normal[ 2 ] );
254
255                 for ( j = 0; j < 4; j++ )
256                 {
257                         bspAds[ i ].rect[j][ 0 ] = LittleFloat( bspAds[ i ].rect[j][ 0 ] );
258                         bspAds[ i ].rect[j][ 1 ] = LittleFloat( bspAds[ i ].rect[j][ 1 ] );
259                         bspAds[ i ].rect[j][ 2 ] = LittleFloat( bspAds[ i ].rect[j][ 2 ] );
260                 }
261
262                 //bspAds[ i ].model[ MAX_QPATH ];
263         }
264 }
265
266
267
268 /*
269    GetLumpElements()
270    gets the number of elements in a bsp lump
271  */
272
273 int GetLumpElements( bspHeader_t *header, int lump, int size ){
274         /* check for odd size */
275         if ( header->lumps[ lump ].length % size ) {
276                 if ( force ) {
277                         Sys_Printf( "WARNING: GetLumpElements: odd lump size (%d) in lump %d\n", header->lumps[ lump ].length, lump );
278                         return 0;
279                 }
280                 else{
281                         Error( "GetLumpElements: odd lump size (%d) in lump %d", header->lumps[ lump ].length, lump );
282                 }
283         }
284
285         /* return element count */
286         return header->lumps[ lump ].length / size;
287 }
288
289
290
291 /*
292    GetLump()
293    returns a pointer to the specified lump
294  */
295
296 void *GetLump( bspHeader_t *header, int lump ){
297         return (void*)( (byte*) header + header->lumps[ lump ].offset );
298 }
299
300
301
302 /*
303    CopyLump()
304    copies a bsp file lump into a destination buffer
305  */
306
307 int CopyLump( bspHeader_t *header, int lump, void *dest, int size ){
308         int length, offset;
309
310
311         /* get lump length and offset */
312         length = header->lumps[ lump ].length;
313         offset = header->lumps[ lump ].offset;
314
315         /* handle erroneous cases */
316         if ( length == 0 ) {
317                 return 0;
318         }
319         if ( length % size ) {
320                 if ( force ) {
321                         Sys_Printf( "WARNING: CopyLump: odd lump size (%d) in lump %d\n", length, lump );
322                         return 0;
323                 }
324                 else{
325                         Error( "CopyLump: odd lump size (%d) in lump %d", length, lump );
326                 }
327         }
328
329         /* copy block of memory and return */
330         memcpy( dest, (byte*) header + offset, length );
331         return length / size;
332 }
333
334 int CopyLump_Allocate( bspHeader_t *header, int lump, void **dest, int size, int *allocationVariable ){
335         /* get lump length and offset */
336         *allocationVariable = header->lumps[ lump ].length / size;
337         *dest = realloc( *dest, size * *allocationVariable );
338         return CopyLump( header, lump, *dest, size );
339 }
340
341
342 /*
343    AddLump()
344    adds a lump to an outgoing bsp file
345  */
346
347 void AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length ){
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         /* dummy check */
369         if ( game == NULL || game->load == NULL ) {
370                 Error( "LoadBSPFile: unsupported BSP file format" );
371         }
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         char tempname[ 1024 ];
387         time_t tm;
388
389
390         /* dummy check */
391         if ( game == NULL || game->write == NULL ) {
392                 Error( "WriteBSPFile: unsupported BSP file format" );
393         }
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         /* parse entities first */
418         if ( numEntities <= 0 ) {
419                 ParseEntities();
420         }
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         char    *s;
484
485
486         s = e + strlen( e ) - 1;
487         while ( s >= e && *s <= 32 )
488         {
489                 *s = 0;
490                 s--;
491         }
492 }
493
494
495
496 /*
497    ParseEpair()
498    parses a single quoted "key" "value" pair into an epair struct
499  */
500
501 epair_t *ParseEPair( void ){
502         epair_t     *e;
503
504
505         /* allocate and clear new epair */
506         e = safe_malloc( sizeof( epair_t ) );
507         memset( e, 0, sizeof( epair_t ) );
508
509         /* handle key */
510         if ( strlen( token ) >= ( MAX_KEY - 1 ) ) {
511                 Error( "ParseEPair: token too long" );
512         }
513
514         e->key = copystring( token );
515         GetToken( qfalse );
516
517         /* handle value */
518         if ( strlen( token ) >= MAX_VALUE - 1 ) {
519                 Error( "ParseEpar: token too long" );
520         }
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         epair_t     *e;
540
541
542         /* dummy check */
543         if ( !GetToken( qtrue ) ) {
544                 return qfalse;
545         }
546         if ( strcmp( token, "{" ) ) {
547                 Error( "ParseEntity: { not found" );
548         }
549         AUTOEXPAND_BY_REALLOC( entities, numEntities, allocatedEntities, 32 );
550
551         /* create new entity */
552         mapEnt = &entities[ numEntities ];
553         numEntities++;
554         memset( mapEnt, 0, sizeof( *mapEnt ) );
555
556         /* parse */
557         while ( 1 )
558         {
559                 if ( !GetToken( qtrue ) ) {
560                         Error( "ParseEntity: EOF without closing brace" );
561                 }
562                 if ( !EPAIR_STRCMP( token, "}" ) ) {
563                         break;
564                 }
565                 e = ParseEPair();
566                 e->next = mapEnt->epairs;
567                 mapEnt->epairs = e;
568         }
569
570         /* return to sender */
571         return qtrue;
572 }
573
574
575
576 /*
577    ParseEntities()
578    parses the bsp entity data string into entities
579  */
580
581 void ParseEntities( void ){
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         const char *previousCommandLine;
595         char newCommandLine[1024];
596         const char *inpos;
597         char *outpos = newCommandLine;
598         char *sentinel = newCommandLine + sizeof( newCommandLine ) - 1;
599         int i;
600
601 if (nocmdline)
602 {
603         return;
604 }
605         previousCommandLine = ValueForKey( &entities[0], "_q3map2_cmdline" );
606         if ( previousCommandLine && *previousCommandLine ) {
607                 inpos = previousCommandLine;
608                 while ( outpos != sentinel && *inpos )
609                         *outpos++ = *inpos++;
610                 if ( outpos != sentinel ) {
611                         *outpos++ = ';';
612                 }
613                 if ( outpos != sentinel ) {
614                         *outpos++ = ' ';
615                 }
616         }
617
618         for ( i = beginArgs; i < endArgs; ++i )
619         {
620                 if ( outpos != sentinel && i != beginArgs ) {
621                         *outpos++ = ' ';
622                 }
623                 inpos = argv[i];
624                 while ( outpos != sentinel && *inpos )
625                         if ( *inpos != '\\' && *inpos != '"' && *inpos != ';' && (unsigned char) *inpos >= ' ' ) {
626                                 *outpos++ = *inpos++;
627                         }
628         }
629
630         *outpos = 0;
631         SetKeyValue( &entities[0], "_q3map2_cmdline", newCommandLine );
632         SetKeyValue( &entities[0], "_q3map2_version", Q3MAP_VERSION );
633 }
634
635 /*
636    UnparseEntities()
637    generates the dentdata string from all the entities.
638    this allows the utilities to add or remove key/value
639    pairs to the data created by the map editor
640  */
641
642 void UnparseEntities( void ){
643         int i;
644         char        *buf, *end;
645         epair_t     *ep;
646         char line[ 2048 ];
647         char key[ 1024 ], value[ 1024 ];
648         const char  *value2;
649
650
651         /* setup */
652         AUTOEXPAND_BY_REALLOC( bspEntData, 0, allocatedBSPEntData, 1024 );
653         buf = bspEntData;
654         end = buf;
655         *end = 0;
656
657
658         /* run through entity list */
659         for ( i = 0; i < numBSPEntities && i < numEntities; i++ )
660         {
661                 {
662                         int sz = end - buf;
663                         AUTOEXPAND_BY_REALLOC( bspEntData, sz + 65536, allocatedBSPEntData, 1024 );
664                         buf = bspEntData;
665                         end = buf + sz;
666                 }
667
668                 /* get epair */
669                 ep = entities[ i ].epairs;
670                 if ( ep == NULL ) {
671                         continue;   /* ent got removed */
672
673                 }
674                 /* ydnar: certain entities get stripped from bsp file */
675                 value2 = ValueForKey( &entities[ i ], "classname" );
676                 if ( !Q_stricmp( value2, "misc_model" ) ||
677                          !Q_stricmp( value2, "_decal" ) ||
678                          !Q_stricmp( value2, "_skybox" ) ) {
679                         continue;
680                 }
681
682                 /* add beginning brace */
683                 strcat( end, "{\n" );
684                 end += 2;
685
686                 /* walk epair list */
687                 for ( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
688                 {
689                         /* copy and clean */
690                         strcpy( key, ep->key );
691                         StripTrailing( key );
692                         strcpy( value, ep->value );
693                         StripTrailing( value );
694
695                         /* add to buffer */
696                         sprintf( line, "\"%s\" \"%s\"\n", key, value );
697                         strcat( end, line );
698                         end += strlen( line );
699                 }
700
701                 /* add trailing brace */
702                 strcat( end,"}\n" );
703                 end += 2;
704
705                 /* check for overflow */
706                 if ( end > buf + allocatedBSPEntData ) {
707                         Error( "Entity text too long" );
708                 }
709         }
710
711         /* set size */
712         bspEntDataSize = end - buf + 1;
713 }
714
715
716
717 /*
718    PrintEntity()
719    prints an entity's epairs to the console
720  */
721
722 void PrintEntity( const entity_t *ent ){
723         epair_t *ep;
724
725
726         Sys_Printf( "------- entity %p -------\n", ent );
727         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
728                 Sys_Printf( "%s = %s\n", ep->key, ep->value );
729
730 }
731
732
733
734 /*
735    SetKeyValue()
736    sets an epair in an entity
737  */
738
739 void SetKeyValue( entity_t *ent, const char *key, const char *value ){
740         epair_t *ep;
741
742
743         /* check for existing epair */
744         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
745         {
746                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
747                         free( ep->value );
748                         ep->value = copystring( value );
749                         return;
750                 }
751         }
752
753         /* create new epair */
754         ep = safe_malloc( sizeof( *ep ) );
755         ep->next = ent->epairs;
756         ent->epairs = ep;
757         ep->key = copystring( key );
758         ep->value = copystring( value );
759 }
760
761 /*
762    KeyExists()
763    returns true if entity has this key
764  */
765
766 qboolean KeyExists( const entity_t *ent, const char *key ){
767         epair_t *ep;
768
769         /* walk epair list */
770         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
771         {
772                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
773                         return qtrue;
774                 }
775         }
776
777         /* no match */
778         return qfalse;
779 }
780
781 /*
782    ValueForKey()
783    gets the value for an entity key
784  */
785
786 const char *ValueForKey( const entity_t *ent, const char *key ){
787         epair_t *ep;
788
789
790         /* dummy check */
791         if ( ent == NULL ) {
792                 return "";
793         }
794
795         /* walk epair list */
796         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
797         {
798                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
799                         return ep->value;
800                 }
801         }
802
803         /* if no match, return empty string */
804         return "";
805 }
806
807
808
809 /*
810    IntForKey()
811    gets the integer point value for an entity key
812  */
813
814 int IntForKey( const entity_t *ent, const char *key ){
815         const char  *k;
816
817
818         k = ValueForKey( ent, key );
819         return atoi( k );
820 }
821
822
823
824 /*
825    FloatForKey()
826    gets the floating point value for an entity key
827  */
828
829 vec_t FloatForKey( const entity_t *ent, const char *key ){
830         const char  *k;
831
832
833         k = ValueForKey( ent, key );
834         return atof( k );
835 }
836
837
838
839 /*
840    GetVectorForKey()
841    gets a 3-element vector value for an entity key
842  */
843
844 void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ){
845         const char  *k;
846         double v1, v2, v3;
847
848
849         /* get value */
850         k = ValueForKey( ent, key );
851
852         /* scanf into doubles, then assign, so it is vec_t size independent */
853         v1 = v2 = v3 = 0.0;
854         sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
855         vec[ 0 ] = v1;
856         vec[ 1 ] = v2;
857         vec[ 2 ] = v3;
858 }
859
860
861
862 /*
863    FindTargetEntity()
864    finds an entity target
865  */
866
867 entity_t *FindTargetEntity( const char *target ){
868         int i;
869         const char  *n;
870
871
872         /* walk entity list */
873         for ( i = 0; i < numEntities; i++ )
874         {
875                 n = ValueForKey( &entities[ i ], "targetname" );
876                 if ( !strcmp( n, target ) ) {
877                         return &entities[ i ];
878                 }
879         }
880
881         /* nada */
882         return NULL;
883 }
884
885
886
887 /*
888    GetEntityShadowFlags() - ydnar
889    gets an entity's shadow flags
890    note: does not set them to defaults if the keys are not found!
891  */
892
893 void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows ){
894         const char  *value;
895
896         /* get cast shadows */
897         if ( castShadows != NULL ) {
898                 value = ValueForKey( ent, "_castShadows" );
899                 if ( value[ 0 ] == '\0' ) {
900                         value = ValueForKey( ent, "_cs" );
901                 }
902                 if ( value[ 0 ] == '\0' ) {
903                         value = ValueForKey( ent2, "_castShadows" );
904                 }
905                 if ( value[ 0 ] == '\0' ) {
906                         value = ValueForKey( ent2, "_cs" );
907                 }
908                 if ( value[ 0 ] != '\0' ) {
909                         *castShadows = atoi( value );
910                 }
911         }
912
913         /* receive */
914         if ( recvShadows != NULL ) {
915                 value = ValueForKey( ent, "_receiveShadows" );
916                 if ( value[ 0 ] == '\0' ) {
917                         value = ValueForKey( ent, "_rs" );
918                 }
919                 if ( value[ 0 ] == '\0' ) {
920                         value = ValueForKey( ent2, "_receiveShadows" );
921                 }
922                 if ( value[ 0 ] == '\0' ) {
923                         value = ValueForKey( ent2, "_rs" );
924                 }
925                 if ( value[ 0 ] != '\0' ) {
926                         *recvShadows = atoi( value );
927                 }
928         }
929
930         /* vortex: game-specific default entity keys */
931         value = ValueForKey( ent, "classname" );
932         if ( !Q_stricmp( game->magic, "dq" ) || !Q_stricmp( game->magic, "prophecy" ) ) {
933                 /* vortex: deluxe quake default shadow flags */
934                 if ( !Q_stricmp( value, "func_wall" ) ) {
935                         if ( recvShadows != NULL ) {
936                                 *recvShadows = 1;
937                         }
938                         if ( castShadows != NULL ) {
939                                 *castShadows = 1;
940                         }
941                 }
942         }
943 }