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