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