]> git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/bspfile_abstract.c
Merge commit 'eef39952025db1375e33b6e314692ac76badb93c' into garux-merge
[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         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_FPrintf( SYS_WRN, "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_FPrintf( SYS_WRN, "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
592 /*
593  * must be called before UnparseEntities
594  */
595 void InjectCommandLine( char **argv, int beginArgs, int endArgs ){
596         const char *previousCommandLine;
597         char newCommandLine[1024];
598         const char *inpos;
599         char *outpos = newCommandLine;
600         char *sentinel = newCommandLine + sizeof( newCommandLine ) - 1;
601         int i;
602
603 if (nocmdline)
604 {
605         return;
606 }
607         previousCommandLine = ValueForKey( &entities[0], "_q3map2_cmdline" );
608         if ( previousCommandLine && *previousCommandLine ) {
609                 inpos = previousCommandLine;
610                 while ( outpos != sentinel && *inpos )
611                         *outpos++ = *inpos++;
612                 if ( outpos != sentinel ) {
613                         *outpos++ = ';';
614                 }
615                 if ( outpos != sentinel ) {
616                         *outpos++ = ' ';
617                 }
618         }
619
620         for ( i = beginArgs; i < endArgs; ++i )
621         {
622                 if ( outpos != sentinel && i != beginArgs ) {
623                         *outpos++ = ' ';
624                 }
625                 inpos = argv[i];
626                 while ( outpos != sentinel && *inpos )
627                         if ( *inpos != '\\' && *inpos != '"' && *inpos != ';' && (unsigned char) *inpos >= ' ' ) {
628                                 *outpos++ = *inpos++;
629                         }
630         }
631
632         *outpos = 0;
633         SetKeyValue( &entities[0], "_q3map2_cmdline", newCommandLine );
634         SetKeyValue( &entities[0], "_q3map2_version", Q3MAP_VERSION );
635 }
636
637
638
639 /*
640    UnparseEntities()
641    generates the dentdata string from all the entities.
642    this allows the utilities to add or remove key/value
643    pairs to the data created by the map editor
644  */
645
646 void UnparseEntities( void ){
647         int i;
648         char        *buf, *end;
649         epair_t     *ep;
650         char line[ 2048 ];
651         char key[ 1024 ], value[ 1024 ];
652         const char  *value2;
653
654
655         /* setup */
656         AUTOEXPAND_BY_REALLOC( bspEntData, 0, allocatedBSPEntData, 1024 );
657         buf = bspEntData;
658         end = buf;
659         *end = 0;
660
661
662         /* run through entity list */
663         for ( i = 0; i < numBSPEntities && i < numEntities; i++ )
664         {
665                 {
666                         int sz = end - buf;
667                         AUTOEXPAND_BY_REALLOC( bspEntData, sz + 65536, allocatedBSPEntData, 1024 );
668                         buf = bspEntData;
669                         end = buf + sz;
670                 }
671
672                 /* get epair */
673                 ep = entities[ i ].epairs;
674                 if ( ep == NULL ) {
675                         continue;   /* ent got removed */
676
677                 }
678                 /* ydnar: certain entities get stripped from bsp file */
679                 value2 = ValueForKey( &entities[ i ], "classname" );
680                 if ( !Q_stricmp( value2, "misc_model" ) ||
681                          !Q_stricmp( value2, "_decal" ) ||
682                          !Q_stricmp( value2, "_skybox" ) ) {
683                         continue;
684                 }
685
686                 /* add beginning brace */
687                 strcat( end, "{\n" );
688                 end += 2;
689
690                 /* walk epair list */
691                 for ( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
692                 {
693                         /* copy and clean */
694                         strcpy( key, ep->key );
695                         StripTrailing( key );
696                         strcpy( value, ep->value );
697                         StripTrailing( value );
698
699                         /* add to buffer */
700                         sprintf( line, "\"%s\" \"%s\"\n", key, value );
701                         strcat( end, line );
702                         end += strlen( line );
703                 }
704
705                 /* add trailing brace */
706                 strcat( end,"}\n" );
707                 end += 2;
708
709                 /* check for overflow */
710                 if ( end > buf + allocatedBSPEntData ) {
711                         Error( "Entity text too long" );
712                 }
713         }
714
715         /* set size */
716         bspEntDataSize = end - buf + 1;
717 }
718
719
720
721 /*
722    PrintEntity()
723    prints an entity's epairs to the console
724  */
725
726 void PrintEntity( const entity_t *ent ){
727         epair_t *ep;
728
729
730         Sys_Printf( "------- entity %p -------\n", ent );
731         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
732                 Sys_Printf( "%s = %s\n", ep->key, ep->value );
733
734 }
735
736
737
738 /*
739    SetKeyValue()
740    sets an epair in an entity
741  */
742
743 void SetKeyValue( entity_t *ent, const char *key, const char *value ){
744         epair_t *ep;
745
746
747         /* check for existing epair */
748         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
749         {
750                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
751                         free( ep->value );
752                         ep->value = copystring( value );
753                         return;
754                 }
755         }
756
757         /* create new epair */
758         ep = safe_malloc( sizeof( *ep ) );
759         ep->next = ent->epairs;
760         ent->epairs = ep;
761         ep->key = copystring( key );
762         ep->value = copystring( value );
763 }
764
765
766
767 /*
768    KeyExists()
769    returns true if entity has this key
770  */
771
772 qboolean KeyExists( const entity_t *ent, const char *key ){
773         epair_t *ep;
774
775         /* walk epair list */
776         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
777         {
778                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
779                         return qtrue;
780                 }
781         }
782
783         /* no match */
784         return qfalse;
785 }
786
787
788
789 /*
790    ValueForKey()
791    gets the value for an entity key
792  */
793
794 const char *ValueForKey( const entity_t *ent, const char *key ){
795         epair_t *ep;
796
797
798         /* dummy check */
799         if ( ent == NULL ) {
800                 return "";
801         }
802
803         /* walk epair list */
804         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
805         {
806                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
807                         return ep->value;
808                 }
809         }
810
811         /* if no match, return empty string */
812         return "";
813 }
814
815
816
817 /*
818    IntForKey()
819    gets the integer point value for an entity key
820  */
821
822 int IntForKey( const entity_t *ent, const char *key ){
823         const char  *k;
824
825
826         k = ValueForKey( ent, key );
827         return atoi( k );
828 }
829
830
831
832 /*
833    FloatForKey()
834    gets the floating point value for an entity key
835  */
836
837 vec_t FloatForKey( const entity_t *ent, const char *key ){
838         const char  *k;
839
840
841         k = ValueForKey( ent, key );
842         return atof( k );
843 }
844
845
846
847 /*
848    GetVectorForKey()
849    gets a 3-element vector value for an entity key
850  */
851
852 qboolean GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ){
853         const char  *k;
854         double v1, v2, v3;
855
856
857         /* get value */
858         k = ValueForKey( ent, key );
859
860         /* scanf into doubles, then assign, so it is vec_t size independent */
861         v1 = v2 = v3 = 0.0;
862         sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
863         vec[ 0 ] = v1;
864         vec[ 1 ] = v2;
865         vec[ 2 ] = v3;
866
867         /* true if the key is found, false otherwise */
868         return strlen( k );
869 }
870
871
872
873 /*
874    FindTargetEntity()
875    finds an entity target
876  */
877
878 entity_t *FindTargetEntity( const char *target ){
879         int i;
880         const char  *n;
881
882
883         /* walk entity list */
884         for ( i = 0; i < numEntities; i++ )
885         {
886                 n = ValueForKey( &entities[ i ], "targetname" );
887                 if ( !strcmp( n, target ) ) {
888                         return &entities[ i ];
889                 }
890         }
891
892         /* nada */
893         return NULL;
894 }
895
896
897
898 /*
899    GetEntityShadowFlags() - ydnar
900    gets an entity's shadow flags
901    note: does not set them to defaults if the keys are not found!
902  */
903
904 void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows ){
905         const char  *value;
906
907         /* get cast shadows */
908         if ( castShadows != NULL ) {
909                 value = ValueForKey( ent, "_castShadows" );
910                 if ( value[ 0 ] == '\0' ) {
911                         value = ValueForKey( ent, "_cs" );
912                 }
913                 if ( value[ 0 ] == '\0' ) {
914                         value = ValueForKey( ent2, "_castShadows" );
915                 }
916                 if ( value[ 0 ] == '\0' ) {
917                         value = ValueForKey( ent2, "_cs" );
918                 }
919                 if ( value[ 0 ] != '\0' ) {
920                         *castShadows = atoi( value );
921                 }
922         }
923
924         /* receive */
925         if ( recvShadows != NULL ) {
926                 value = ValueForKey( ent, "_receiveShadows" );
927                 if ( value[ 0 ] == '\0' ) {
928                         value = ValueForKey( ent, "_rs" );
929                 }
930                 if ( value[ 0 ] == '\0' ) {
931                         value = ValueForKey( ent2, "_receiveShadows" );
932                 }
933                 if ( value[ 0 ] == '\0' ) {
934                         value = ValueForKey( ent2, "_rs" );
935                 }
936                 if ( value[ 0 ] != '\0' ) {
937                         *recvShadows = atoi( value );
938                 }
939         }
940
941         /* vortex: game-specific default entity keys */
942         value = ValueForKey( ent, "classname" );
943         if ( !Q_stricmp( game->magic, "dq" ) || !Q_stricmp( game->magic, "prophecy" ) ) {
944                 /* vortex: deluxe quake default shadow flags */
945                 if ( !Q_stricmp( value, "func_wall" ) ) {
946                         if ( recvShadows != NULL ) {
947                                 *recvShadows = 1;
948                         }
949                         if ( castShadows != NULL ) {
950                                 *castShadows = 1;
951                         }
952                 }
953         }
954 }