2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 ----------------------------------------------------------------------------------
23 This code has been altered significantly from its original form, to support
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."
26 ------------------------------------------------------------------------------- */
31 #define BSPFILE_ABSTRACT_C
41 /* -------------------------------------------------------------------------------
43 this file was copied out of the common directory in order to not break
44 compatibility with the q3map 1.x tree. it was moved out in order to support
45 the raven bsp format (RBSP) used in soldier of fortune 2 and jedi knight 2.
47 since each game has its own set of particular features, the data structures
48 below no longer directly correspond to the binary format of a particular game.
50 the translation will be done at bsp load/save time to keep any sort of
51 special-case code messiness out of the rest of the program.
53 ------------------------------------------------------------------------------- */
57 /* FIXME: remove the functions below that handle memory management of bsp file chunks */
59 int numBSPDrawVertsBuffer = 0;
66 numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS / 37;
68 bspDrawVerts = safe_malloc_info(sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer, "IncDrawVerts");
71 else if(numBSPDrawVerts > numBSPDrawVertsBuffer)
73 numBSPDrawVertsBuffer *= 3; // multiply by 1.5
74 numBSPDrawVertsBuffer /= 2;
76 if(numBSPDrawVertsBuffer > MAX_MAP_DRAW_VERTS)
77 numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS;
79 bspDrawVerts = realloc(bspDrawVerts, sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer);
82 Error( "realloc() failed (IncDrawVerts)");
85 memset(bspDrawVerts + (numBSPDrawVerts - 1), 0, sizeof(bspDrawVert_t));
88 void SetDrawVerts(int n)
94 numBSPDrawVertsBuffer = numBSPDrawVerts;
96 bspDrawVerts = safe_malloc_info(sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer, "IncDrawVerts");
98 memset(bspDrawVerts, 0, n * sizeof(bspDrawVert_t));
101 int numBSPDrawSurfacesBuffer = 0;
102 void SetDrawSurfacesBuffer()
104 if(bspDrawSurfaces != 0)
105 free(bspDrawSurfaces);
107 numBSPDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
109 bspDrawSurfaces = safe_malloc_info(sizeof(bspDrawSurface_t) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces");
111 memset(bspDrawSurfaces, 0, MAX_MAP_DRAW_SURFS * sizeof(bspDrawVert_t));
114 void SetDrawSurfaces(int n)
116 if(bspDrawSurfaces != 0)
117 free(bspDrawSurfaces);
119 numBSPDrawSurfaces = n;
120 numBSPDrawSurfacesBuffer = numBSPDrawSurfaces;
122 bspDrawSurfaces = safe_malloc_info(sizeof(bspDrawSurface_t) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces");
124 memset(bspDrawSurfaces, 0, n * sizeof(bspDrawVert_t));
127 void BSPFilesCleanup()
129 if(bspDrawVerts != 0)
131 if(bspDrawSurfaces != 0)
132 free(bspDrawSurfaces);
133 if(bspLightBytes != 0)
135 if(bspGridPoints != 0)
146 if all values are 32 bits, this can be used to swap everything
149 void SwapBlock( int *block, int size )
160 for( i = 0; i < size; i++ )
161 block[ i ] = LittleLong( block[ i ] );
168 byte swaps all data in the abstract bsp
171 void SwapBSPFile( void )
177 SwapBlock( (int*) bspModels, numBSPModels * sizeof( bspModels[ 0 ] ) );
179 /* shaders (don't swap the name) */
180 for( i = 0; i < numBSPShaders ; i++ )
182 bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );
183 bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );
187 SwapBlock( (int*) bspPlanes, numBSPPlanes * sizeof( bspPlanes[ 0 ] ) );
190 SwapBlock( (int*) bspNodes, numBSPNodes * sizeof( bspNodes[ 0 ] ) );
193 SwapBlock( (int*) bspLeafs, numBSPLeafs * sizeof( bspLeafs[ 0 ] ) );
196 SwapBlock( (int*) bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
199 SwapBlock( (int*) bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
202 SwapBlock( (int*) bspBrushes, numBSPBrushes * sizeof( bspBrushes[ 0 ] ) );
205 SwapBlock( (int*) bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
208 ((int*) &bspVisBytes)[ 0 ] = LittleLong( ((int*) &bspVisBytes)[ 0 ] );
209 ((int*) &bspVisBytes)[ 1 ] = LittleLong( ((int*) &bspVisBytes)[ 1 ] );
211 /* drawverts (don't swap colors) */
212 for( i = 0; i < numBSPDrawVerts; i++ )
214 bspDrawVerts[ i ].xyz[ 0 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 0 ] );
215 bspDrawVerts[ i ].xyz[ 1 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 1 ] );
216 bspDrawVerts[ i ].xyz[ 2 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 2 ] );
217 bspDrawVerts[ i ].normal[ 0 ] = LittleFloat( bspDrawVerts[ i ].normal[ 0 ] );
218 bspDrawVerts[ i ].normal[ 1 ] = LittleFloat( bspDrawVerts[ i ].normal[ 1 ] );
219 bspDrawVerts[ i ].normal[ 2 ] = LittleFloat( bspDrawVerts[ i ].normal[ 2 ] );
220 bspDrawVerts[ i ].st[ 0 ] = LittleFloat( bspDrawVerts[ i ].st[ 0 ] );
221 bspDrawVerts[ i ].st[ 1 ] = LittleFloat( bspDrawVerts[ i ].st[ 1 ] );
222 for( j = 0; j < MAX_LIGHTMAPS; j++ )
224 bspDrawVerts[ i ].lightmap[ j ][ 0 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 0 ] );
225 bspDrawVerts[ i ].lightmap[ j ][ 1 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 1 ] );
230 SwapBlock( (int*) bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[0] ) );
233 /* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */
234 SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
237 for( i = 0; i < numBSPFogs; i++ )
239 bspFogs[ i ].brushNum = LittleLong( bspFogs[ i ].brushNum );
240 bspFogs[ i ].visibleSide = LittleLong( bspFogs[ i ].visibleSide );
248 gets the number of elements in a bsp lump
251 int GetLumpElements( bspHeader_t *header, int lump, int size )
253 /* check for odd size */
254 if( header->lumps[ lump ].length % size )
258 Sys_Printf( "WARNING: GetLumpElements: odd lump size (%d) in lump %d\n", header->lumps[ lump ].length, lump );
262 Error( "GetLumpElements: odd lump size (%d) in lump %d", header->lumps[ lump ].length, lump );
265 /* return element count */
266 return header->lumps[ lump ].length / size;
273 returns a pointer to the specified lump
276 void *GetLump( bspHeader_t *header, int lump )
278 return (void*)( (byte*) header + header->lumps[ lump ].offset);
285 copies a bsp file lump into a destination buffer
288 int CopyLump( bspHeader_t *header, int lump, void *dest, int size )
293 /* get lump length and offset */
294 length = header->lumps[ lump ].length;
295 offset = header->lumps[ lump ].offset;
297 /* handle erroneous cases */
304 Sys_Printf( "WARNING: CopyLump: odd lump size (%d) in lump %d\n", length, lump );
308 Error( "CopyLump: odd lump size (%d) in lump %d", length, lump );
311 /* copy block of memory and return */
312 memcpy( dest, (byte*) header + offset, length );
313 return length / size;
320 adds a lump to an outgoing bsp file
323 void AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length )
328 /* add lump to bsp file header */
329 lump = &header->lumps[ lumpNum ];
330 lump->offset = LittleLong( ftell( file ) );
331 lump->length = LittleLong( length );
333 /* write lump to file */
334 SafeWrite( file, data, (length + 3) & ~3 );
341 loads a bsp file into memory
344 void LoadBSPFile( const char *filename )
347 if( game == NULL || game->load == NULL )
348 Error( "LoadBSPFile: unsupported BSP file format" );
350 /* load it, then byte swap the in-memory version */
351 game->load( filename );
362 void WriteBSPFile( const char *filename )
364 char tempname[ 1024 ];
369 if( game == NULL || game->write == NULL )
370 Error( "WriteBSPFile: unsupported BSP file format" );
372 /* make fake temp name so existing bsp file isn't damaged in case write process fails */
374 sprintf( tempname, "%s.%08X", filename, (int) tm );
376 /* byteswap, write the bsp, then swap back so it can be manipulated further */
378 game->write( tempname );
381 /* replace existing bsp file */
383 rename( tempname, filename );
390 dumps info about current file
393 void PrintBSPFileSizes( void )
395 /* parse entities first */
396 if( numEntities <= 0 )
399 /* note that this is abstracted */
400 Sys_Printf( "Abstracted BSP file components (*actual sizes may differ)\n" );
402 /* print various and sundry bits */
403 Sys_Printf( "%9d models %9d\n",
404 numBSPModels, (int) (numBSPModels * sizeof( bspModel_t )) );
405 Sys_Printf( "%9d shaders %9d\n",
406 numBSPShaders, (int) (numBSPShaders * sizeof( bspShader_t )) );
407 Sys_Printf( "%9d brushes %9d\n",
408 numBSPBrushes, (int) (numBSPBrushes * sizeof( bspBrush_t )) );
409 Sys_Printf( "%9d brushsides %9d *\n",
410 numBSPBrushSides, (int) (numBSPBrushSides * sizeof( bspBrushSide_t )) );
411 Sys_Printf( "%9d fogs %9d\n",
412 numBSPFogs, (int) (numBSPFogs * sizeof( bspFog_t ) ) );
413 Sys_Printf( "%9d planes %9d\n",
414 numBSPPlanes, (int) (numBSPPlanes * sizeof( bspPlane_t )) );
415 Sys_Printf( "%9d entdata %9d\n",
416 numEntities, bspEntDataSize );
419 Sys_Printf( "%9d nodes %9d\n",
420 numBSPNodes, (int) (numBSPNodes * sizeof( bspNode_t)) );
421 Sys_Printf( "%9d leafs %9d\n",
422 numBSPLeafs, (int) (numBSPLeafs * sizeof( bspLeaf_t )) );
423 Sys_Printf( "%9d leafsurfaces %9d\n",
424 numBSPLeafSurfaces, (int) (numBSPLeafSurfaces * sizeof( *bspLeafSurfaces )) );
425 Sys_Printf( "%9d leafbrushes %9d\n",
426 numBSPLeafBrushes, (int) (numBSPLeafBrushes * sizeof( *bspLeafBrushes )) );
429 Sys_Printf( "%9d drawsurfaces %9d *\n",
430 numBSPDrawSurfaces, (int) (numBSPDrawSurfaces * sizeof( *bspDrawSurfaces )) );
431 Sys_Printf( "%9d drawverts %9d *\n",
432 numBSPDrawVerts, (int) (numBSPDrawVerts * sizeof( *bspDrawVerts )) );
433 Sys_Printf( "%9d drawindexes %9d\n",
434 numBSPDrawIndexes, (int) (numBSPDrawIndexes * sizeof( *bspDrawIndexes )) );
437 Sys_Printf( "%9d lightmaps %9d\n",
438 numBSPLightBytes / (LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3), numBSPLightBytes );
439 Sys_Printf( "%9d lightgrid %9d *\n",
440 numBSPGridPoints, (int) (numBSPGridPoints * sizeof( *bspGridPoints )) );
441 Sys_Printf( " visibility %9d\n",
447 /* -------------------------------------------------------------------------------
451 ------------------------------------------------------------------------------- */
456 strips low byte chars off the end of a string
459 void StripTrailing( char *e )
464 s = e + strlen( e ) - 1;
465 while( s >= e && *s <= 32 )
476 parses a single quoted "key" "value" pair into an epair struct
479 epair_t *ParseEPair( void )
484 /* allocate and clear new epair */
485 e = safe_malloc( sizeof( epair_t ) );
486 memset( e, 0, sizeof( epair_t ) );
489 if( strlen( token ) >= (MAX_KEY - 1) )
490 Error( "ParseEPair: token too long" );
492 e->key = copystring( token );
496 if( strlen( token ) >= MAX_VALUE - 1 )
497 Error( "ParseEpar: token too long" );
498 e->value = copystring( token );
500 /* strip trailing spaces that sometimes get accidentally added in the editor */
501 StripTrailing( e->key );
502 StripTrailing( e->value );
512 parses an entity's epairs
515 qboolean ParseEntity( void )
521 if( !GetToken( qtrue ) )
523 if( strcmp( token, "{" ) )
524 Error( "ParseEntity: { not found" );
525 if( numEntities == MAX_MAP_ENTITIES )
526 Error( "numEntities == MAX_MAP_ENTITIES" );
528 /* create new entity */
529 mapEnt = &entities[ numEntities ];
535 if( !GetToken( qtrue ) )
536 Error( "ParseEntity: EOF without closing brace" );
537 if( !EPAIR_STRCMP( token, "}" ) )
540 e->next = mapEnt->epairs;
544 /* return to sender */
552 parses the bsp entity data string into entities
555 void ParseEntities( void )
558 ParseFromMemory( bspEntData, bspEntDataSize );
559 while( ParseEntity() );
561 /* ydnar: set number of bsp entities in case a map is loaded on top */
562 numBSPEntities = numEntities;
569 generates the dentdata string from all the entities.
570 this allows the utilities to add or remove key/value
571 pairs to the data created by the map editor
574 void UnparseEntities( void )
580 char key[ 1024 ], value[ 1024 ];
589 /* run through entity list */
590 for( i = 0; i < numBSPEntities && i < numEntities; i++ )
593 ep = entities[ i ].epairs;
595 continue; /* ent got removed */
597 /* ydnar: certain entities get stripped from bsp file */
598 value2 = ValueForKey( &entities[ i ], "classname" );
599 if( !Q_stricmp( value2, "misc_model" ) ||
600 !Q_stricmp( value2, "_decal" ) ||
601 !Q_stricmp( value2, "_skybox" ) )
604 /* add beginning brace */
605 strcat( end, "{\n" );
608 /* walk epair list */
609 for( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
612 strcpy( key, ep->key );
613 StripTrailing( key );
614 strcpy( value, ep->value );
615 StripTrailing( value );
618 sprintf( line, "\"%s\" \"%s\"\n", key, value );
620 end += strlen( line );
623 /* add trailing brace */
627 /* check for overflow */
628 if( end > buf + MAX_MAP_ENTSTRING )
629 Error( "Entity text too long" );
633 bspEntDataSize = end - buf + 1;
640 prints an entity's epairs to the console
643 void PrintEntity( const entity_t *ent )
648 Sys_Printf( "------- entity %p -------\n", ent );
649 for( ep = ent->epairs; ep != NULL; ep = ep->next )
650 Sys_Printf( "%s = %s\n", ep->key, ep->value );
658 sets an epair in an entity
661 void SetKeyValue( entity_t *ent, const char *key, const char *value )
666 /* check for existing epair */
667 for( ep = ent->epairs; ep != NULL; ep = ep->next )
669 if( !EPAIR_STRCMP( ep->key, key ) )
672 ep->value = copystring( value );
677 /* create new epair */
678 ep = safe_malloc( sizeof( *ep ) );
679 ep->next = ent->epairs;
681 ep->key = copystring( key );
682 ep->value = copystring( value );
689 gets the value for an entity key
692 const char *ValueForKey( const entity_t *ent, const char *key )
701 /* walk epair list */
702 for( ep = ent->epairs; ep != NULL; ep = ep->next )
704 if( !EPAIR_STRCMP( ep->key, key ) )
708 /* if no match, return empty string */
716 gets the integer point value for an entity key
719 int IntForKey( const entity_t *ent, const char *key )
724 k = ValueForKey( ent, key );
732 gets the floating point value for an entity key
735 vec_t FloatForKey( const entity_t *ent, const char *key )
740 k = ValueForKey( ent, key );
748 gets a 3-element vector value for an entity key
751 void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec )
758 k = ValueForKey( ent, key );
760 /* scanf into doubles, then assign, so it is vec_t size independent */
762 sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
772 finds an entity target
775 entity_t *FindTargetEntity( const char *target )
781 /* walk entity list */
782 for( i = 0; i < numEntities; i++ )
784 n = ValueForKey( &entities[ i ], "targetname" );
785 if ( !strcmp( n, target ) )
786 return &entities[ i ];
796 GetEntityShadowFlags() - ydnar
797 gets an entity's shadow flags
798 note: does not set them to defaults if the keys are not found!
801 void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows )
806 /* get cast shadows */
807 if( castShadows != NULL )
809 value = ValueForKey( ent, "_castShadows" );
810 if( value[ 0 ] == '\0' )
811 value = ValueForKey( ent, "_cs" );
812 if( value[ 0 ] == '\0' )
813 value = ValueForKey( ent2, "_castShadows" );
814 if( value[ 0 ] == '\0' )
815 value = ValueForKey( ent2, "_cs" );
816 if( value[ 0 ] != '\0' )
817 *castShadows = atoi( value );
821 if( recvShadows != NULL )
823 value = ValueForKey( ent, "_receiveShadows" );
824 if( value[ 0 ] == '\0' )
825 value = ValueForKey( ent, "_rs" );
826 if( value[ 0 ] == '\0' )
827 value = ValueForKey( ent2, "_receiveShadows" );
828 if( value[ 0 ] == '\0' )
829 value = ValueForKey( ent2, "_rs" );
830 if( value[ 0 ] != '\0' )
831 *recvShadows = atoi( value );