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