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