]> git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/bspfile_abstract.c
Merge commit 'ed4c8c204443726e852a4c8927b3f8d2571cc522' 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         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
631 /*
632  * must be called before UnparseEntities
633  */
634 void InjectCommandLine( char **argv, int beginArgs, int endArgs ){
635         const char *previousCommandLine;
636         char newCommandLine[1024];
637         const char *inpos;
638         char *outpos = newCommandLine;
639         char *sentinel = newCommandLine + sizeof( newCommandLine ) - 1;
640         int i;
641
642         if ( nocmdline ){
643                 return;
644         }
645         previousCommandLine = ValueForKey( &entities[0], "_q3map2_cmdline" );
646         if ( previousCommandLine && *previousCommandLine ) {
647                 inpos = previousCommandLine;
648                 while ( outpos != sentinel && *inpos )
649                         *outpos++ = *inpos++;
650                 if ( outpos != sentinel ) {
651                         *outpos++ = ';';
652                 }
653                 if ( outpos != sentinel ) {
654                         *outpos++ = ' ';
655                 }
656         }
657
658         for ( i = beginArgs; i < endArgs; ++i )
659         {
660                 if ( outpos != sentinel && i != beginArgs ) {
661                         *outpos++ = ' ';
662                 }
663                 inpos = argv[i];
664                 while ( outpos != sentinel && *inpos )
665                         if ( *inpos != '\\' && *inpos != '"' && *inpos != ';' && (unsigned char) *inpos >= ' ' ) {
666                                 *outpos++ = *inpos++;
667                         }
668         }
669
670         *outpos = 0;
671         SetKeyValue( &entities[0], "_q3map2_cmdline", newCommandLine );
672         SetKeyValue( &entities[0], "_q3map2_version", Q3MAP_VERSION );
673 }
674
675
676
677 /*
678    UnparseEntities()
679    generates the dentdata string from all the entities.
680    this allows the utilities to add or remove key/value
681    pairs to the data created by the map editor
682  */
683
684 void UnparseEntities( void ){
685         int i;
686         char        *buf, *end;
687         epair_t     *ep;
688         char line[ 2048 ];
689         char key[ 1024 ], value[ 1024 ];
690         const char  *value2;
691
692
693         /* setup */
694         AUTOEXPAND_BY_REALLOC( bspEntData, 0, allocatedBSPEntData, 1024 );
695         buf = bspEntData;
696         end = buf;
697         *end = 0;
698
699
700         /* run through entity list */
701         for ( i = 0; i < numBSPEntities && i < numEntities; i++ )
702         {
703                 {
704                         int sz = end - buf;
705                         AUTOEXPAND_BY_REALLOC( bspEntData, sz + 65536, allocatedBSPEntData, 1024 );
706                         buf = bspEntData;
707                         end = buf + sz;
708                 }
709
710                 /* get epair */
711                 ep = entities[ i ].epairs;
712                 if ( ep == NULL ) {
713                         continue;   /* ent got removed */
714
715                 }
716                 /* ydnar: certain entities get stripped from bsp file */
717                 value2 = ValueForKey( &entities[ i ], "classname" );
718                 if ( !Q_stricmp( value2, "misc_model" ) ||
719                          !Q_stricmp( value2, "_decal" ) ||
720                          !Q_stricmp( value2, "_skybox" ) ) {
721                         continue;
722                 }
723
724                 /* add beginning brace */
725                 strcat( end, "{\n" );
726                 end += 2;
727
728                 /* walk epair list */
729                 for ( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
730                 {
731                         /* copy and clean */
732                         strcpy( key, ep->key );
733                         StripTrailing( key );
734                         strcpy( value, ep->value );
735                         StripTrailing( value );
736
737                         /* add to buffer */
738                         sprintf( line, "\"%s\" \"%s\"\n", key, value );
739                         strcat( end, line );
740                         end += strlen( line );
741                 }
742
743                 /* add trailing brace */
744                 strcat( end,"}\n" );
745                 end += 2;
746
747                 /* check for overflow */
748                 if ( end > buf + allocatedBSPEntData ) {
749                         Error( "Entity text too long" );
750                 }
751         }
752
753         /* set size */
754         bspEntDataSize = end - buf + 1;
755 }
756
757
758
759 /*
760    PrintEntity()
761    prints an entity's epairs to the console
762  */
763
764 void PrintEntity( const entity_t *ent ){
765         epair_t *ep;
766
767
768         Sys_Printf( "------- entity %p -------\n", ent );
769         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
770                 Sys_Printf( "%s = %s\n", ep->key, ep->value );
771
772 }
773
774
775
776 /*
777    SetKeyValue()
778    sets an epair in an entity
779  */
780
781 void SetKeyValue( entity_t *ent, const char *key, const char *value ){
782         epair_t *ep;
783
784
785         /* check for existing epair */
786         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
787         {
788                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
789                         free( ep->value );
790                         ep->value = copystring( value );
791                         return;
792                 }
793         }
794
795         /* create new epair */
796         ep = safe_malloc( sizeof( *ep ) );
797         ep->next = ent->epairs;
798         ent->epairs = ep;
799         ep->key = copystring( key );
800         ep->value = copystring( value );
801 }
802
803
804
805 /*
806    KeyExists()
807    returns true if entity has this key
808  */
809
810 qboolean KeyExists( const entity_t *ent, const char *key ){
811         epair_t *ep;
812
813         /* walk epair list */
814         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
815         {
816                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
817                         return qtrue;
818                 }
819         }
820
821         /* no match */
822         return qfalse;
823 }
824
825
826
827 /*
828    ValueForKey()
829    gets the value for an entity key
830  */
831
832 const char *ValueForKey( const entity_t *ent, const char *key ){
833         epair_t *ep;
834
835
836         /* dummy check */
837         if ( ent == NULL ) {
838                 return "";
839         }
840
841         /* walk epair list */
842         for ( ep = ent->epairs; ep != NULL; ep = ep->next )
843         {
844                 if ( !EPAIR_STRCMP( ep->key, key ) ) {
845                         return ep->value;
846                 }
847         }
848
849         /* if no match, return empty string */
850         return "";
851 }
852
853
854
855 /*
856    IntForKey()
857    gets the integer point value for an entity key
858  */
859
860 int IntForKey( const entity_t *ent, const char *key ){
861         const char  *k;
862
863
864         k = ValueForKey( ent, key );
865         return atoi( k );
866 }
867
868
869
870 /*
871    FloatForKey()
872    gets the floating point value for an entity key
873  */
874
875 vec_t FloatForKey( const entity_t *ent, const char *key ){
876         const char  *k;
877
878
879         k = ValueForKey( ent, key );
880         return atof( k );
881 }
882
883
884
885 /*
886    GetVectorForKey()
887    gets a 3-element vector value for an entity key
888  */
889
890 qboolean GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ){
891         const char  *k;
892         double v1, v2, v3;
893
894
895         /* get value */
896         k = ValueForKey( ent, key );
897
898         /* scanf into doubles, then assign, so it is vec_t size independent */
899         v1 = v2 = v3 = 0.0;
900         sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
901         vec[ 0 ] = v1;
902         vec[ 1 ] = v2;
903         vec[ 2 ] = v3;
904
905         /* true if the key is found, false otherwise */
906         return strlen( k );
907 }
908
909
910
911 /*
912    FindTargetEntity()
913    finds an entity target
914  */
915
916 entity_t *FindTargetEntity( const char *target ){
917         int i;
918         const char  *n;
919
920
921         /* walk entity list */
922         for ( i = 0; i < numEntities; i++ )
923         {
924                 n = ValueForKey( &entities[ i ], "targetname" );
925                 if ( !strcmp( n, target ) ) {
926                         return &entities[ i ];
927                 }
928         }
929
930         /* nada */
931         return NULL;
932 }
933
934
935
936 /*
937    GetEntityShadowFlags() - ydnar
938    gets an entity's shadow flags
939    note: does not set them to defaults if the keys are not found!
940  */
941
942 void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows ){
943         const char  *value;
944
945         /* get cast shadows */
946         if ( castShadows != NULL ) {
947                 value = ValueForKey( ent, "_castShadows" );
948                 if ( value[ 0 ] == '\0' ) {
949                         value = ValueForKey( ent, "_cs" );
950                 }
951                 if ( value[ 0 ] == '\0' ) {
952                         value = ValueForKey( ent2, "_castShadows" );
953                 }
954                 if ( value[ 0 ] == '\0' ) {
955                         value = ValueForKey( ent2, "_cs" );
956                 }
957                 if ( value[ 0 ] != '\0' ) {
958                         *castShadows = atoi( value );
959                 }
960         }
961
962         /* receive */
963         if ( recvShadows != NULL ) {
964                 value = ValueForKey( ent, "_receiveShadows" );
965                 if ( value[ 0 ] == '\0' ) {
966                         value = ValueForKey( ent, "_rs" );
967                 }
968                 if ( value[ 0 ] == '\0' ) {
969                         value = ValueForKey( ent2, "_receiveShadows" );
970                 }
971                 if ( value[ 0 ] == '\0' ) {
972                         value = ValueForKey( ent2, "_rs" );
973                 }
974                 if ( value[ 0 ] != '\0' ) {
975                         *recvShadows = atoi( value );
976                 }
977         }
978
979         /* vortex: game-specific default entity keys */
980         value = ValueForKey( ent, "classname" );
981         if ( !Q_stricmp( game->magic, "dq" ) || !Q_stricmp( game->magic, "prophecy" ) ) {
982                 /* vortex: deluxe quake default shadow flags */
983                 if ( !Q_stricmp( value, "func_wall" ) ) {
984                         if ( recvShadows != NULL ) {
985                                 *recvShadows = 1;
986                         }
987                         if ( castShadows != NULL ) {
988                                 *castShadows = 1;
989                         }
990                 }
991         }
992 }