]> git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/bspfile_abstract.c
Merge commit 'bf6dd1f2d186c799adf11f1e744a1ff57aa8d335' into garux-merge
[xonotic/netradiant.git] / tools / quake3 / q3map2 / bspfile_abstract.c
1 /* -------------------------------------------------------------------------------
2
3    Copyright (C) 1999-2007 id Software, Inc. and contributors.
4    For a list of contributors, see the accompanying CONTRIBUTORS file.
5
6    This file is part of GtkRadiant.
7
8    GtkRadiant is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    GtkRadiant is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with GtkRadiant; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21
22    ----------------------------------------------------------------------------------
23
24    This code has been altered significantly from its original form, to support
25    several games based on the Quake III Arena engine, in the form of "Q3Map2."
26
27    ------------------------------------------------------------------------------- */
28
29
30
31 /* marker */
32 #define BSPFILE_ABSTRACT_C
33
34
35
36 /* dependencies */
37 #include "q3map2.h"
38
39
40
41
42 /* -------------------------------------------------------------------------------
43
44    this file was copied out of the common directory in order to not break
45    compatibility with the q3map 1.x tree. it was moved out in order to support
46    the raven bsp format (RBSP) used in soldier of fortune 2 and jedi knight 2.
47
48    since each game has its own set of particular features, the data structures
49    below no longer directly correspond to the binary format of a particular game.
50
51    the translation will be done at bsp load/save time to keep any sort of
52    special-case code messiness out of the rest of the program.
53
54    ------------------------------------------------------------------------------- */
55
56
57
58 /* FIXME: remove the functions below that handle memory management of bsp file chunks */
59
60 int numBSPDrawVertsBuffer = 0;
61 void IncDrawVerts(){
62         numBSPDrawVerts++;
63
64         if ( bspDrawVerts == 0 ) {
65                 numBSPDrawVertsBuffer = 1024;
66
67                 bspDrawVerts = safe_malloc_info( sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer, "IncDrawVerts" );
68
69         }
70         else if ( numBSPDrawVerts > numBSPDrawVertsBuffer ) {
71                 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( bspDrawSurface_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( bspDrawSurface_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_FPrintf( SYS_WRN, "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_FPrintf( SYS_WRN, "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;
399
400         /* shaders (don't swap the name) */
401         for ( i = 0; i < numBSPShaders ; i++ )
402         {
403                 bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );
404                 bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );
405         }
406
407         /* drawsurfs */
408         /* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */
409         SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
410 }
411
412 /*
413    WriteBSPFile()
414    writes a bsp file
415  */
416
417 void WriteBSPFile( const char *filename ){
418         char tempname[ 1024 ];
419         time_t tm;
420
421
422         /* dummy check */
423         if ( game == NULL || game->write == NULL ) {
424                 Error( "WriteBSPFile: unsupported BSP file format" );
425         }
426
427         /* make fake temp name so existing bsp file isn't damaged in case write process fails */
428         time( &tm );
429         sprintf( tempname, "%s.%08X", filename, (int) tm );
430
431         /* byteswap, write the bsp, then swap back so it can be manipulated further */
432         SwapBSPFile();
433         game->write( tempname );
434         SwapBSPFile();
435
436         /* replace existing bsp file */
437         remove( filename );
438         rename( tempname, filename );
439 }
440
441
442
443 /*
444    PrintBSPFileSizes()
445    dumps info about current file
446  */
447
448 void PrintBSPFileSizes( void ){
449         /* parse entities first */
450         if ( numEntities <= 0 ) {
451                 ParseEntities();
452         }
453
454         /* note that this is abstracted */
455         Sys_Printf( "Abstracted BSP file components (*actual sizes may differ)\n" );
456
457         /* print various and sundry bits */
458         Sys_Printf( "%9d models        %9d\n",
459                                 numBSPModels, (int) ( numBSPModels * sizeof( bspModel_t ) ) );
460         Sys_Printf( "%9d shaders       %9d\n",
461                                 numBSPShaders, (int) ( numBSPShaders * sizeof( bspShader_t ) ) );
462         Sys_Printf( "%9d brushes       %9d\n",
463                                 numBSPBrushes, (int) ( numBSPBrushes * sizeof( bspBrush_t ) ) );
464         Sys_Printf( "%9d brushsides    %9d *\n",
465                                 numBSPBrushSides, (int) ( numBSPBrushSides * sizeof( bspBrushSide_t ) ) );
466         Sys_Printf( "%9d fogs          %9d\n",
467                                 numBSPFogs, (int) ( numBSPFogs * sizeof( bspFog_t ) ) );
468         Sys_Printf( "%9d planes        %9d\n",
469                                 numBSPPlanes, (int) ( numBSPPlanes * sizeof( bspPlane_t ) ) );
470         Sys_Printf( "%9d entdata       %9d\n",
471                                 numEntities, bspEntDataSize );
472         Sys_Printf( "\n" );
473
474         Sys_Printf( "%9d nodes         %9d\n",
475                                 numBSPNodes, (int) ( numBSPNodes * sizeof( bspNode_t ) ) );
476         Sys_Printf( "%9d leafs         %9d\n",
477                                 numBSPLeafs, (int) ( numBSPLeafs * sizeof( bspLeaf_t ) ) );
478         Sys_Printf( "%9d leafsurfaces  %9d\n",
479                                 numBSPLeafSurfaces, (int) ( numBSPLeafSurfaces * sizeof( *bspLeafSurfaces ) ) );
480         Sys_Printf( "%9d leafbrushes   %9d\n",
481                                 numBSPLeafBrushes, (int) ( numBSPLeafBrushes * sizeof( *bspLeafBrushes ) ) );
482         Sys_Printf( "\n" );
483
484         Sys_Printf( "%9d drawsurfaces  %9d *\n",
485                                 numBSPDrawSurfaces, (int) ( numBSPDrawSurfaces * sizeof( *bspDrawSurfaces ) ) );
486         Sys_Printf( "%9d drawverts     %9d *\n",
487                                 numBSPDrawVerts, (int) ( numBSPDrawVerts * sizeof( *bspDrawVerts ) ) );
488         Sys_Printf( "%9d drawindexes   %9d\n",
489                                 numBSPDrawIndexes, (int) ( numBSPDrawIndexes * sizeof( *bspDrawIndexes ) ) );
490         Sys_Printf( "\n" );
491
492         Sys_Printf( "%9d lightmaps     %9d\n",
493                                 numBSPLightBytes / ( game->lightmapSize * game->lightmapSize * 3 ), numBSPLightBytes );
494         Sys_Printf( "%9d lightgrid     %9d *\n",
495                                 numBSPGridPoints, (int) ( numBSPGridPoints * sizeof( *bspGridPoints ) ) );
496         Sys_Printf( "          visibility    %9d\n",
497                                 numBSPVisBytes );
498 }
499
500
501
502 /* -------------------------------------------------------------------------------
503
504    entity data handling
505
506    ------------------------------------------------------------------------------- */
507
508
509 /*
510    StripTrailing()
511    strips low byte chars off the end of a string
512  */
513
514 void StripTrailing( char *e ){
515         char    *s;
516
517
518         s = e + strlen( e ) - 1;
519         while ( s >= e && *s <= 32 )
520         {
521                 *s = 0;
522                 s--;
523         }
524 }
525
526
527
528 /*
529    ParseEpair()
530    parses a single quoted "key" "value" pair into an epair struct
531  */
532
533 epair_t *ParseEPair( void ){
534         epair_t     *e;
535
536
537         /* allocate and clear new epair */
538         e = safe_malloc( sizeof( epair_t ) );
539         memset( e, 0, sizeof( epair_t ) );
540
541         /* handle key */
542         if ( strlen( token ) >= ( MAX_KEY - 1 ) ) {
543                 Error( "ParseEPair: token too long" );
544         }
545
546         e->key = copystring( token );
547         GetToken( qfalse );
548
549         /* handle value */
550         if ( strlen( token ) >= MAX_VALUE - 1 ) {
551                 Error( "ParseEpar: token too long" );
552         }
553         e->value = copystring( token );
554
555         /* strip trailing spaces that sometimes get accidentally added in the editor */
556         StripTrailing( e->key );
557         StripTrailing( e->value );
558
559         /* return it */
560         return e;
561 }
562
563
564
565 /*
566    ParseEntity()
567    parses an entity's epairs
568  */
569
570 qboolean ParseEntity( void ){
571         epair_t     *e;
572
573
574         /* dummy check */
575         if ( !GetToken( qtrue ) ) {
576                 return qfalse;
577         }
578         if ( strcmp( token, "{" ) ) {
579                 Error( "ParseEntity: { not found" );
580         }
581         AUTOEXPAND_BY_REALLOC( entities, numEntities, allocatedEntities, 32 );
582
583         /* create new entity */
584         mapEnt = &entities[ numEntities ];
585         numEntities++;
586         memset( mapEnt, 0, sizeof( *mapEnt ) );
587
588         /* parse */
589         while ( 1 )
590         {
591                 if ( !GetToken( qtrue ) ) {
592                         Error( "ParseEntity: EOF without closing brace" );
593                 }
594                 if ( !EPAIR_STRCMP( token, "}" ) ) {
595                         break;
596                 }
597                 e = ParseEPair();
598                 e->next = mapEnt->epairs;
599                 mapEnt->epairs = e;
600         }
601
602         /* return to sender */
603         return qtrue;
604 }
605
606
607
608 /*
609    ParseEntities()
610    parses the bsp entity data string into entities
611  */
612
613 void ParseEntities( void ){
614         numEntities = 0;
615         ParseFromMemory( bspEntData, bspEntDataSize );
616         while ( ParseEntity() ) ;
617
618         /* ydnar: set number of bsp entities in case a map is loaded on top */
619         numBSPEntities = numEntities;
620 }
621
622
623
624 /*
625  * must be called before UnparseEntities
626  */
627 void InjectCommandLine( char **argv, int beginArgs, int endArgs ){
628         const char *previousCommandLine;
629         char newCommandLine[1024];
630         const char *inpos;
631         char *outpos = newCommandLine;
632         char *sentinel = newCommandLine + sizeof( newCommandLine ) - 1;
633         int i;
634
635 if (nocmdline)
636 {
637         return;
638 }
639         previousCommandLine = ValueForKey( &entities[0], "_q3map2_cmdline" );
640         if ( previousCommandLine && *previousCommandLine ) {
641                 inpos = previousCommandLine;
642                 while ( outpos != sentinel && *inpos )
643                         *outpos++ = *inpos++;
644                 if ( outpos != sentinel ) {
645                         *outpos++ = ';';
646                 }
647                 if ( outpos != sentinel ) {
648                         *outpos++ = ' ';
649                 }
650         }
651
652         for ( i = beginArgs; i < endArgs; ++i )
653         {
654                 if ( outpos != sentinel && i != beginArgs ) {
655                         *outpos++ = ' ';
656                 }
657                 inpos = argv[i];
658                 while ( outpos != sentinel && *inpos )
659                         if ( *inpos != '\\' && *inpos != '"' && *inpos != ';' && (unsigned char) *inpos >= ' ' ) {
660                                 *outpos++ = *inpos++;
661                         }
662         }
663
664         *outpos = 0;
665         SetKeyValue( &entities[0], "_q3map2_cmdline", newCommandLine );
666         SetKeyValue( &entities[0], "_q3map2_version", Q3MAP_VERSION );
667 }
668
669
670
671 /*
672    UnparseEntities()
673    generates the dentdata string from all the entities.
674    this allows the utilities to add or remove key/value
675    pairs to the data created by the map editor
676  */
677
678 void UnparseEntities( void ){
679         int i;
680         char        *buf, *end;
681         epair_t     *ep;
682         char line[ 2048 ];
683         char key[ 1024 ], value[ 1024 ];
684         const char  *value2;
685
686
687         /* setup */
688         AUTOEXPAND_BY_REALLOC( bspEntData, 0, allocatedBSPEntData, 1024 );
689         buf = bspEntData;
690         end = buf;
691         *end = 0;
692
693
694         /* run through entity list */
695         for ( i = 0; i < numBSPEntities && i < numEntities; i++ )
696         {
697                 {
698                         int sz = end - buf;
699                         AUTOEXPAND_BY_REALLOC( bspEntData, sz + 65536, allocatedBSPEntData, 1024 );
700                         buf = bspEntData;
701                         end = buf + sz;
702                 }
703
704                 /* get epair */
705                 ep = entities[ i ].epairs;
706                 if ( ep == NULL ) {
707                         continue;   /* ent got removed */
708
709                 }
710                 /* ydnar: certain entities get stripped from bsp file */
711                 value2 = ValueForKey( &entities[ i ], "classname" );
712                 if ( !Q_stricmp( value2, "misc_model" ) ||
713                          !Q_stricmp( value2, "_decal" ) ||
714                          !Q_stricmp( value2, "_skybox" ) ) {
715                         continue;
716                 }
717
718                 /* add beginning brace */
719                 strcat( end, "{\n" );
720                 end += 2;
721
722                 /* walk epair list */
723                 for ( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
724                 {
725                         /* copy and clean */
726                         strcpy( key, ep->key );
727                         StripTrailing( key );
728                         strcpy( value, ep->value );
729                         StripTrailing( value );
730
731                         /* add to buffer */
732                         sprintf( line, "\"%s\" \"%s\"\n", key, value );
733                         strcat( end, line );
734                         end += strlen( line );
735                 }
736
737                 /* add trailing brace */
738                 strcat( end,"}\n" );
739                 end += 2;
740
741                 /* check for overflow */
742                 if ( end > buf + allocatedBSPEntData ) {
743                         Error( "Entity text too long" );
744                 }
745         }
746
747         /* set size */
748         bspEntDataSize = end - buf + 1;
749 }
750
751
752
753 /*
754    PrintEntity()
755    prints an entity's epairs to the console
756  */
757
758 void PrintEntity( const entity_t *ent ){
759         epair_t *ep;
760
761
762         Sys_Printf( "------- entity %p -------\n", ent );
763         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
764                 Sys_Printf( "%s = %s\n", ep->key, ep->value );
765
766 }
767
768
769
770 /*
771    SetKeyValue()
772    sets an epair in an entity
773  */
774
775 void SetKeyValue( entity_t *ent, const char *key, const char *value ){
776         epair_t *ep;
777
778
779         /* check for existing epair */
780         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
781         {
782                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
783                         free( ep->value );
784                         ep->value = copystring( value );
785                         return;
786                 }
787         }
788
789         /* create new epair */
790         ep = safe_malloc( sizeof( *ep ) );
791         ep->next = ent->epairs;
792         ent->epairs = ep;
793         ep->key = copystring( key );
794         ep->value = copystring( value );
795 }
796
797
798
799 /*
800    KeyExists()
801    returns true if entity has this key
802  */
803
804 qboolean KeyExists( const entity_t *ent, const char *key ){
805         epair_t *ep;
806
807         /* walk epair list */
808         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
809         {
810                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
811                         return qtrue;
812                 }
813         }
814
815         /* no match */
816         return qfalse;
817 }
818
819
820
821 /*
822    ValueForKey()
823    gets the value for an entity key
824  */
825
826 const char *ValueForKey( const entity_t *ent, const char *key ){
827         epair_t *ep;
828
829
830         /* dummy check */
831         if ( ent == NULL ) {
832                 return "";
833         }
834
835         /* walk epair list */
836         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
837         {
838                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
839                         return ep->value;
840                 }
841         }
842
843         /* if no match, return empty string */
844         return "";
845 }
846
847
848
849 /*
850    IntForKey()
851    gets the integer point value for an entity key
852  */
853
854 int IntForKey( const entity_t *ent, const char *key ){
855         const char  *k;
856
857
858         k = ValueForKey( ent, key );
859         return atoi( k );
860 }
861
862
863
864 /*
865    FloatForKey()
866    gets the floating point value for an entity key
867  */
868
869 vec_t FloatForKey( const entity_t *ent, const char *key ){
870         const char  *k;
871
872
873         k = ValueForKey( ent, key );
874         return atof( k );
875 }
876
877
878
879 /*
880    GetVectorForKey()
881    gets a 3-element vector value for an entity key
882  */
883
884 qboolean GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ){
885         const char  *k;
886         double v1, v2, v3;
887
888
889         /* get value */
890         k = ValueForKey( ent, key );
891
892         /* scanf into doubles, then assign, so it is vec_t size independent */
893         v1 = v2 = v3 = 0.0;
894         sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
895         vec[ 0 ] = v1;
896         vec[ 1 ] = v2;
897         vec[ 2 ] = v3;
898
899         /* true if the key is found, false otherwise */
900         return strlen( k );
901 }
902
903
904
905 /*
906    FindTargetEntity()
907    finds an entity target
908  */
909
910 entity_t *FindTargetEntity( const char *target ){
911         int i;
912         const char  *n;
913
914
915         /* walk entity list */
916         for ( i = 0; i < numEntities; i++ )
917         {
918                 n = ValueForKey( &entities[ i ], "targetname" );
919                 if ( !strcmp( n, target ) ) {
920                         return &entities[ i ];
921                 }
922         }
923
924         /* nada */
925         return NULL;
926 }
927
928
929
930 /*
931    GetEntityShadowFlags() - ydnar
932    gets an entity's shadow flags
933    note: does not set them to defaults if the keys are not found!
934  */
935
936 void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows ){
937         const char  *value;
938
939         /* get cast shadows */
940         if ( castShadows != NULL ) {
941                 value = ValueForKey( ent, "_castShadows" );
942                 if ( value[ 0 ] == '\0' ) {
943                         value = ValueForKey( ent, "_cs" );
944                 }
945                 if ( value[ 0 ] == '\0' ) {
946                         value = ValueForKey( ent2, "_castShadows" );
947                 }
948                 if ( value[ 0 ] == '\0' ) {
949                         value = ValueForKey( ent2, "_cs" );
950                 }
951                 if ( value[ 0 ] != '\0' ) {
952                         *castShadows = atoi( value );
953                 }
954         }
955
956         /* receive */
957         if ( recvShadows != NULL ) {
958                 value = ValueForKey( ent, "_receiveShadows" );
959                 if ( value[ 0 ] == '\0' ) {
960                         value = ValueForKey( ent, "_rs" );
961                 }
962                 if ( value[ 0 ] == '\0' ) {
963                         value = ValueForKey( ent2, "_receiveShadows" );
964                 }
965                 if ( value[ 0 ] == '\0' ) {
966                         value = ValueForKey( ent2, "_rs" );
967                 }
968                 if ( value[ 0 ] != '\0' ) {
969                         *recvShadows = atoi( value );
970                 }
971         }
972
973         /* vortex: game-specific default entity keys */
974         value = ValueForKey( ent, "classname" );
975         if ( !Q_stricmp( game->magic, "dq" ) || !Q_stricmp( game->magic, "prophecy" ) ) {
976                 /* vortex: deluxe quake default shadow flags */
977                 if ( !Q_stricmp( value, "func_wall" ) ) {
978                         if ( recvShadows != NULL ) {
979                                 *recvShadows = 1;
980                         }
981                         if ( castShadows != NULL ) {
982                                 *castShadows = 1;
983                         }
984                 }
985         }
986 }