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