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