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