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