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