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