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