]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/bspfile_abstract.c
Q3map2:
[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_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;
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         int patchCount = 0;
454         bspDrawSurface_t *s;
455         for ( s = bspDrawSurfaces; s != bspDrawSurfaces + numBSPDrawSurfaces; ++s ){
456                 if ( s->surfaceType == MST_PATCH )
457                         ++patchCount;
458         }
459         /* note that this is abstracted */
460         Sys_Printf( "Abstracted BSP file components (*actual sizes may differ)\n" );
461
462         /* print various and sundry bits */
463         Sys_Printf( "%9d models        %9d\n",
464                                 numBSPModels, (int) ( numBSPModels * sizeof( bspModel_t ) ) );
465         Sys_Printf( "%9d shaders       %9d\n",
466                                 numBSPShaders, (int) ( numBSPShaders * sizeof( bspShader_t ) ) );
467         Sys_Printf( "%9d brushes       %9d\n",
468                                 numBSPBrushes, (int) ( numBSPBrushes * sizeof( bspBrush_t ) ) );
469         Sys_Printf( "%9d brushsides    %9d *\n",
470                                 numBSPBrushSides, (int) ( numBSPBrushSides * sizeof( bspBrushSide_t ) ) );
471         Sys_Printf( "%9d fogs          %9d\n",
472                                 numBSPFogs, (int) ( numBSPFogs * sizeof( bspFog_t ) ) );
473         Sys_Printf( "%9d planes        %9d\n",
474                                 numBSPPlanes, (int) ( numBSPPlanes * sizeof( bspPlane_t ) ) );
475         Sys_Printf( "%9d entdata       %9d\n",
476                                 numEntities, bspEntDataSize );
477         Sys_Printf( "\n" );
478
479         Sys_Printf( "%9d nodes         %9d\n",
480                                 numBSPNodes, (int) ( numBSPNodes * sizeof( bspNode_t ) ) );
481         Sys_Printf( "%9d leafs         %9d\n",
482                                 numBSPLeafs, (int) ( numBSPLeafs * sizeof( bspLeaf_t ) ) );
483         Sys_Printf( "%9d leafsurfaces  %9d\n",
484                                 numBSPLeafSurfaces, (int) ( numBSPLeafSurfaces * sizeof( *bspLeafSurfaces ) ) );
485         Sys_Printf( "%9d leafbrushes   %9d\n",
486                                 numBSPLeafBrushes, (int) ( numBSPLeafBrushes * sizeof( *bspLeafBrushes ) ) );
487         Sys_Printf( "\n" );
488
489         Sys_Printf( "%9d drawsurfaces  %9d *\n",
490                                 numBSPDrawSurfaces, (int) ( numBSPDrawSurfaces * sizeof( *bspDrawSurfaces ) ) );
491         Sys_Printf( "%9d patchsurfaces       \n",
492                                 patchCount );
493         Sys_Printf( "%9d drawverts     %9d *\n",
494                                 numBSPDrawVerts, (int) ( numBSPDrawVerts * sizeof( *bspDrawVerts ) ) );
495         Sys_Printf( "%9d drawindexes   %9d\n",
496                                 numBSPDrawIndexes, (int) ( numBSPDrawIndexes * sizeof( *bspDrawIndexes ) ) );
497         Sys_Printf( "\n" );
498
499         Sys_Printf( "%9d lightmaps     %9d\n",
500                                 numBSPLightBytes / ( game->lightmapSize * game->lightmapSize * 3 ), numBSPLightBytes );
501         Sys_Printf( "%9d lightgrid     %9d *\n",
502                                 numBSPGridPoints, (int) ( numBSPGridPoints * sizeof( *bspGridPoints ) ) );
503         Sys_Printf( "          visibility    %9d\n",
504                                 numBSPVisBytes );
505 }
506
507
508
509 /* -------------------------------------------------------------------------------
510
511    entity data handling
512
513    ------------------------------------------------------------------------------- */
514
515
516 /*
517    StripTrailing()
518    strips low byte chars off the end of a string
519  */
520
521 void StripTrailing( char *e ){
522         char    *s;
523
524
525         s = e + strlen( e ) - 1;
526         while ( s >= e && *s <= 32 )
527         {
528                 *s = 0;
529                 s--;
530         }
531 }
532
533
534
535 /*
536    ParseEpair()
537    parses a single quoted "key" "value" pair into an epair struct
538  */
539
540 epair_t *ParseEPair( void ){
541         epair_t     *e;
542
543
544         /* allocate and clear new epair */
545         e = safe_malloc( sizeof( epair_t ) );
546         memset( e, 0, sizeof( epair_t ) );
547
548         /* handle key */
549         if ( strlen( token ) >= ( MAX_KEY - 1 ) ) {
550                 Error( "ParseEPair: token too long" );
551         }
552
553         e->key = copystring( token );
554         GetToken( qfalse );
555
556         /* handle value */
557         if ( strlen( token ) >= MAX_VALUE - 1 ) {
558                 Error( "ParseEpar: token too long" );
559         }
560         e->value = copystring( token );
561
562         /* strip trailing spaces that sometimes get accidentally added in the editor */
563         StripTrailing( e->key );
564         StripTrailing( e->value );
565
566         /* return it */
567         return e;
568 }
569
570
571
572 /*
573    ParseEntity()
574    parses an entity's epairs
575  */
576
577 qboolean ParseEntity( void ){
578         epair_t     *e;
579
580
581         /* dummy check */
582         if ( !GetToken( qtrue ) ) {
583                 return qfalse;
584         }
585         if ( strcmp( token, "{" ) ) {
586                 Error( "ParseEntity: { not found" );
587         }
588         AUTOEXPAND_BY_REALLOC( entities, numEntities, allocatedEntities, 32 );
589
590         /* create new entity */
591         mapEnt = &entities[ numEntities ];
592         numEntities++;
593         memset( mapEnt, 0, sizeof( *mapEnt ) );
594
595         /* parse */
596         while ( 1 )
597         {
598                 if ( !GetToken( qtrue ) ) {
599                         Error( "ParseEntity: EOF without closing brace" );
600                 }
601                 if ( !EPAIR_STRCMP( token, "}" ) ) {
602                         break;
603                 }
604                 e = ParseEPair();
605                 e->next = mapEnt->epairs;
606                 mapEnt->epairs = e;
607         }
608
609         /* return to sender */
610         return qtrue;
611 }
612
613
614
615 /*
616    ParseEntities()
617    parses the bsp entity data string into entities
618  */
619
620 void ParseEntities( void ){
621         numEntities = 0;
622         ParseFromMemory( bspEntData, bspEntDataSize );
623         while ( ParseEntity() ) ;
624
625         /* ydnar: set number of bsp entities in case a map is loaded on top */
626         numBSPEntities = numEntities;
627 }
628
629 /*
630  * must be called before UnparseEntities
631  */
632 void InjectCommandLine( char **argv, int beginArgs, int endArgs ){
633         const char *previousCommandLine;
634         char newCommandLine[1024];
635         const char *inpos;
636         char *outpos = newCommandLine;
637         char *sentinel = newCommandLine + sizeof( newCommandLine ) - 1;
638         int i;
639
640         if ( nocmdline ){
641                 return;
642         }
643         previousCommandLine = ValueForKey( &entities[0], "_q3map2_cmdline" );
644         if ( previousCommandLine && *previousCommandLine ) {
645                 inpos = previousCommandLine;
646                 while ( outpos != sentinel && *inpos )
647                         *outpos++ = *inpos++;
648                 if ( outpos != sentinel ) {
649                         *outpos++ = ';';
650                 }
651                 if ( outpos != sentinel ) {
652                         *outpos++ = ' ';
653                 }
654         }
655
656         for ( i = beginArgs; i < endArgs; ++i )
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    UnparseEntities()
675    generates the dentdata string from all the entities.
676    this allows the utilities to add or remove key/value
677    pairs to the data created by the map editor
678  */
679
680 void UnparseEntities( void ){
681         int i;
682         char        *buf, *end;
683         epair_t     *ep;
684         char line[ 2048 ];
685         char key[ 1024 ], value[ 1024 ];
686         const char  *value2;
687
688
689         /* setup */
690         AUTOEXPAND_BY_REALLOC( bspEntData, 0, allocatedBSPEntData, 1024 );
691         buf = bspEntData;
692         end = buf;
693         *end = 0;
694
695
696         /* run through entity list */
697         for ( i = 0; i < numBSPEntities && i < numEntities; i++ )
698         {
699                 {
700                         int sz = end - buf;
701                         AUTOEXPAND_BY_REALLOC( bspEntData, sz + 65536, allocatedBSPEntData, 1024 );
702                         buf = bspEntData;
703                         end = buf + sz;
704                 }
705
706                 /* get epair */
707                 ep = entities[ i ].epairs;
708                 if ( ep == NULL ) {
709                         continue;   /* ent got removed */
710
711                 }
712                 /* ydnar: certain entities get stripped from bsp file */
713                 value2 = ValueForKey( &entities[ i ], "classname" );
714                 if ( !Q_stricmp( value2, "misc_model" ) ||
715                          !Q_stricmp( value2, "_decal" ) ||
716                          !Q_stricmp( value2, "_skybox" ) ) {
717                         continue;
718                 }
719
720                 /* add beginning brace */
721                 strcat( end, "{\n" );
722                 end += 2;
723
724                 /* walk epair list */
725                 for ( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
726                 {
727                         /* copy and clean */
728                         strcpy( key, ep->key );
729                         StripTrailing( key );
730                         strcpy( value, ep->value );
731                         StripTrailing( value );
732
733                         /* add to buffer */
734                         sprintf( line, "\"%s\" \"%s\"\n", key, value );
735                         strcat( end, line );
736                         end += strlen( line );
737                 }
738
739                 /* add trailing brace */
740                 strcat( end,"}\n" );
741                 end += 2;
742
743                 /* check for overflow */
744                 if ( end > buf + allocatedBSPEntData ) {
745                         Error( "Entity text too long" );
746                 }
747         }
748
749         /* set size */
750         bspEntDataSize = end - buf + 1;
751 }
752
753
754
755 /*
756    PrintEntity()
757    prints an entity's epairs to the console
758  */
759
760 void PrintEntity( const entity_t *ent ){
761         epair_t *ep;
762
763
764         Sys_Printf( "------- entity %p -------\n", ent );
765         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
766                 Sys_Printf( "%s = %s\n", ep->key, ep->value );
767
768 }
769
770
771
772 /*
773    SetKeyValue()
774    sets an epair in an entity
775  */
776
777 void SetKeyValue( entity_t *ent, const char *key, const char *value ){
778         epair_t *ep;
779
780
781         /* check for existing epair */
782         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
783         {
784                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
785                         free( ep->value );
786                         ep->value = copystring( value );
787                         return;
788                 }
789         }
790
791         /* create new epair */
792         ep = safe_malloc( sizeof( *ep ) );
793         ep->next = ent->epairs;
794         ent->epairs = ep;
795         ep->key = copystring( key );
796         ep->value = copystring( value );
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    ValueForKey()
821    gets the value for an entity key
822  */
823
824 const char *ValueForKey( const entity_t *ent, const char *key ){
825         epair_t *ep;
826
827
828         /* dummy check */
829         if ( ent == NULL ) {
830                 return "";
831         }
832
833         /* walk epair list */
834         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
835         {
836                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
837                         return ep->value;
838                 }
839         }
840
841         /* if no match, return empty string */
842         return "";
843 }
844
845
846
847 /*
848    IntForKey()
849    gets the integer point value for an entity key
850  */
851
852 int IntForKey( const entity_t *ent, const char *key ){
853         const char  *k;
854
855
856         k = ValueForKey( ent, key );
857         return atoi( k );
858 }
859
860
861
862 /*
863    FloatForKey()
864    gets the floating point value for an entity key
865  */
866
867 vec_t FloatForKey( const entity_t *ent, const char *key ){
868         const char  *k;
869
870
871         k = ValueForKey( ent, key );
872         return atof( k );
873 }
874
875
876
877 /*
878    GetVectorForKey()
879    gets a 3-element vector value for an entity key
880  */
881
882 void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ){
883         const char  *k;
884         double v1, v2, v3;
885
886
887         /* get value */
888         k = ValueForKey( ent, key );
889
890         /* scanf into doubles, then assign, so it is vec_t size independent */
891         v1 = v2 = v3 = 0.0;
892         sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
893         vec[ 0 ] = v1;
894         vec[ 1 ] = v2;
895         vec[ 2 ] = v3;
896 }
897
898
899
900 /*
901    FindTargetEntity()
902    finds an entity target
903  */
904
905 entity_t *FindTargetEntity( const char *target ){
906         int i;
907         const char  *n;
908
909
910         /* walk entity list */
911         for ( i = 0; i < numEntities; i++ )
912         {
913                 n = ValueForKey( &entities[ i ], "targetname" );
914                 if ( !strcmp( n, target ) ) {
915                         return &entities[ i ];
916                 }
917         }
918
919         /* nada */
920         return NULL;
921 }
922
923
924
925 /*
926    GetEntityShadowFlags() - ydnar
927    gets an entity's shadow flags
928    note: does not set them to defaults if the keys are not found!
929  */
930
931 void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows ){
932         const char  *value;
933
934         /* get cast shadows */
935         if ( castShadows != NULL ) {
936                 value = ValueForKey( ent, "_castShadows" );
937                 if ( value[ 0 ] == '\0' ) {
938                         value = ValueForKey( ent, "_cs" );
939                 }
940                 if ( value[ 0 ] == '\0' ) {
941                         value = ValueForKey( ent2, "_castShadows" );
942                 }
943                 if ( value[ 0 ] == '\0' ) {
944                         value = ValueForKey( ent2, "_cs" );
945                 }
946                 if ( value[ 0 ] != '\0' ) {
947                         *castShadows = atoi( value );
948                 }
949         }
950
951         /* receive */
952         if ( recvShadows != NULL ) {
953                 value = ValueForKey( ent, "_receiveShadows" );
954                 if ( value[ 0 ] == '\0' ) {
955                         value = ValueForKey( ent, "_rs" );
956                 }
957                 if ( value[ 0 ] == '\0' ) {
958                         value = ValueForKey( ent2, "_receiveShadows" );
959                 }
960                 if ( value[ 0 ] == '\0' ) {
961                         value = ValueForKey( ent2, "_rs" );
962                 }
963                 if ( value[ 0 ] != '\0' ) {
964                         *recvShadows = atoi( value );
965                 }
966         }
967
968         /* vortex: game-specific default entity keys */
969         value = ValueForKey( ent, "classname" );
970         if ( !Q_stricmp( game->magic, "dq" ) || !Q_stricmp( game->magic, "prophecy" ) ) {
971                 /* vortex: deluxe quake default shadow flags */
972                 if ( !Q_stricmp( value, "func_wall" ) ) {
973                         if ( recvShadows != NULL ) {
974                                 *recvShadows = 1;
975                         }
976                         if ( castShadows != NULL ) {
977                                 *castShadows = 1;
978                         }
979                 }
980         }
981 }