1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
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.
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.
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
22 -------------------------------------------------------------------------------
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."
27 ------------------------------------------------------------------------------- */
41 returns a pseudorandom number between 0 and 1
46 return (vec_t) rand() / RAND_MAX;
56 static void ExitQ3Map( void )
59 if( mapDrawSurfs != NULL )
63 static int MD4BlockChecksum( void * buffer, int length ) {
64 unsigned char digest[16];
67 md4_get_digest( buffer, length, digest );
68 /* I suppose it has to be done that way for legacy reasons? */
69 checksum = digest[0] & ( digest[1] << 8 ) & ( digest[2] << 16 ) & ( digest[3] << 24 );
70 checksum ^= digest[4] & ( digest[5] << 8 ) & ( digest[6] << 16 ) & ( digest[7] << 24 );
71 checksum ^= digest[8] & ( digest[9] << 8 ) & ( digest[10] << 16 ) & ( digest[11] << 24 );
72 checksum ^= digest[12] & ( digest[13] << 8 ) & ( digest[14] << 16 ) & ( digest[15] << 24 );
78 resets an aas checksum to match the given BSP
81 int FixAAS( int argc, char **argv )
86 char aas[ 1024 ], **ext;
99 Sys_Printf( "Usage: q3map -fixaas [-v] <mapname>\n" );
103 /* do some path mangling */
104 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
105 StripExtension( source );
106 DefaultExtension( source, ".bsp" );
109 Sys_Printf( "--- FixAAS ---\n" );
112 Sys_Printf( "Loading %s\n", source );
113 length = LoadFile( source, &buffer );
115 /* create bsp checksum */
116 Sys_Printf( "Creating checksum...\n" );
117 checksum = LittleLong( MD4BlockChecksum( buffer, length ) );
119 /* write checksum to aas */
124 strcpy( aas, source );
125 StripExtension( aas );
127 Sys_Printf( "Trying %s\n", aas );
131 file = fopen( aas, "r+b" );
134 if( fwrite( &checksum, 4, 1, file ) != 1 )
135 Error( "Error writing checksum to %s", aas );
139 /* return to sender */
147 analyzes a Quake engine BSP file
150 typedef struct abspHeader_s
155 bspLump_t lumps[ 1 ]; /* unknown size */
159 typedef struct abspLumpTest_s
166 int AnalyzeBSP( int argc, char **argv )
168 abspHeader_t *header;
169 int size, i, version, offset, length, lumpInt, count;
173 char lumpString[ 1024 ], source[ 1024 ];
174 qboolean lumpSwap = qfalse;
175 abspLumpTest_t *lumpTest;
176 static abspLumpTest_t lumpTests[] =
178 { sizeof( bspPlane_t ), 6, "IBSP LUMP_PLANES" },
179 { sizeof( bspBrush_t ), 1, "IBSP LUMP_BRUSHES" },
180 { 8, 6, "IBSP LUMP_BRUSHSIDES" },
181 { sizeof( bspBrushSide_t ), 6, "RBSP LUMP_BRUSHSIDES" },
182 { sizeof( bspModel_t ), 1, "IBSP LUMP_MODELS" },
183 { sizeof( bspNode_t ), 2, "IBSP LUMP_NODES" },
184 { sizeof( bspLeaf_t ), 1, "IBSP LUMP_LEAFS" },
185 { 104, 3, "IBSP LUMP_DRAWSURFS" },
186 { 44, 3, "IBSP LUMP_DRAWVERTS" },
187 { 4, 6, "IBSP LUMP_DRAWINDEXES" },
188 { 128 * 128 * 3, 1, "IBSP LUMP_LIGHTMAPS" },
189 { 256 * 256 * 3, 1, "IBSP LUMP_LIGHTMAPS (256 x 256)" },
190 { 512 * 512 * 3, 1, "IBSP LUMP_LIGHTMAPS (512 x 512)" },
198 Sys_Printf( "Usage: q3map -analyze [-lumpswap] [-v] <mapname>\n" );
202 /* process arguments */
203 for( i = 1; i < (argc - 1); i++ )
205 /* -format map|ase|... */
206 if( !strcmp( argv[ i ], "-lumpswap" ) )
208 Sys_Printf( "Swapped lump structs enabled\n" );
213 /* clean up map name */
214 strcpy( source, ExpandArg( argv[ i ] ) );
215 Sys_Printf( "Loading %s\n", source );
218 size = LoadFile( source, (void**) &header );
219 if( size == 0 || header == NULL )
221 Sys_Printf( "Unable to load %s.\n", source );
225 /* analyze ident/version */
226 memcpy( ident, header->ident, 4 );
228 version = LittleLong( header->version );
230 Sys_Printf( "Identity: %s\n", ident );
231 Sys_Printf( "Version: %d\n", version );
232 Sys_Printf( "---------------------------------------\n" );
234 /* analyze each lump */
235 for( i = 0; i < 100; i++ )
237 /* call of duty swapped lump pairs */
240 offset = LittleLong( header->lumps[ i ].length );
241 length = LittleLong( header->lumps[ i ].offset );
244 /* standard lump pairs */
247 offset = LittleLong( header->lumps[ i ].offset );
248 length = LittleLong( header->lumps[ i ].length );
252 lump = (byte*) header + offset;
253 lumpInt = LittleLong( (int) *((int*) lump) );
254 lumpFloat = LittleFloat( (float) *((float*) lump) );
255 memcpy( lumpString, (char*) lump, (length < 1024 ? length : 1024) );
256 lumpString[ 1024 ] = '\0';
258 /* print basic lump info */
259 Sys_Printf( "Lump: %d\n", i );
260 Sys_Printf( "Offset: %d bytes\n", offset );
261 Sys_Printf( "Length: %d bytes\n", length );
263 /* only operate on valid lumps */
266 /* print data in 4 formats */
267 Sys_Printf( "As hex: %08X\n", lumpInt );
268 Sys_Printf( "As int: %d\n", lumpInt );
269 Sys_Printf( "As float: %f\n", lumpFloat );
270 Sys_Printf( "As string: %s\n", lumpString );
272 /* guess lump type */
273 if( lumpString[ 0 ] == '{' && lumpString[ 2 ] == '"' )
274 Sys_Printf( "Type guess: IBSP LUMP_ENTITIES\n" );
275 else if( strstr( lumpString, "textures/" ) )
276 Sys_Printf( "Type guess: IBSP LUMP_SHADERS\n" );
279 /* guess based on size/count */
280 for( lumpTest = lumpTests; lumpTest->radix > 0; lumpTest++ )
282 if( (length % lumpTest->radix) != 0 )
284 count = length / lumpTest->radix;
285 if( count < lumpTest->minCount )
287 Sys_Printf( "Type guess: %s (%d x %d)\n", lumpTest->name, count, lumpTest->radix );
292 Sys_Printf( "---------------------------------------\n" );
295 if( offset + length >= size )
300 Sys_Printf( "Lump count: %d\n", i + 1 );
301 Sys_Printf( "File size: %d bytes\n", size );
303 /* return to caller */
311 emits statistics about the bsp file
314 int BSPInfo( int count, char **fileNames )
317 char source[ 1024 ], ext[ 64 ];
325 Sys_Printf( "No files to dump info for.\n");
329 /* enable info mode */
333 for( i = 0; i < count; i++ )
335 Sys_Printf( "---------------------------------\n" );
337 /* mangle filename and get size */
338 strcpy( source, fileNames[ i ] );
339 ExtractFileExtension( source, ext );
340 if( !Q_stricmp( ext, "map" ) )
341 StripExtension( source );
342 DefaultExtension( source, ".bsp" );
343 f = fopen( source, "rb" );
346 size = Q_filelength (f);
352 /* load the bsp file and print lump sizes */
353 Sys_Printf( "%s\n", source );
354 LoadBSPFile( source );
359 Sys_Printf( " total %9d\n", size );
360 Sys_Printf( " %9d KB\n", size / 1024 );
361 Sys_Printf( " %9d MB\n", size / (1024 * 1024) );
363 Sys_Printf( "---------------------------------\n" );
374 amaze and confuse your enemies with wierd scaled maps!
377 int ScaleBSPMain( int argc, char **argv )
388 Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
393 scale = atof( argv[ argc - 2 ] );
396 Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
397 Sys_Printf( "Non-zero scale value required.\n" );
401 /* do some path mangling */
402 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
403 StripExtension( source );
404 DefaultExtension( source, ".bsp" );
407 Sys_Printf( "Loading %s\n", source );
408 LoadBSPFile( source );
412 Sys_Printf( "--- ScaleBSP ---\n" );
413 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
415 /* scale entity keys */
416 for( i = 0; i < numBSPEntities && i < numEntities; i++ )
419 GetVectorForKey( &entities[ i ], "origin", vec );
420 if( (vec[ 0 ] + vec[ 1 ] + vec[ 2 ]) )
422 VectorScale( vec, scale, vec );
423 sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
424 SetKeyValue( &entities[ i ], "origin", str );
428 f = FloatForKey( &entities[ i ], "lip" );
432 sprintf( str, "%f", f );
433 SetKeyValue( &entities[ i ], "lip", str );
438 for( i = 0; i < numBSPModels; i++ )
440 VectorScale( bspModels[ i ].mins, scale, bspModels[ i ].mins );
441 VectorScale( bspModels[ i ].maxs, scale, bspModels[ i ].maxs );
445 for( i = 0; i < numBSPNodes; i++ )
447 VectorScale( bspNodes[ i ].mins, scale, bspNodes[ i ].mins );
448 VectorScale( bspNodes[ i ].maxs, scale, bspNodes[ i ].maxs );
452 for( i = 0; i < numBSPLeafs; i++ )
454 VectorScale( bspLeafs[ i ].mins, scale, bspLeafs[ i ].mins );
455 VectorScale( bspLeafs[ i ].maxs, scale, bspLeafs[ i ].maxs );
458 /* scale drawverts */
459 for( i = 0; i < numBSPDrawVerts; i++ )
460 VectorScale( bspDrawVerts[ i ].xyz, scale, bspDrawVerts[ i ].xyz );
463 for( i = 0; i < numBSPPlanes; i++ )
464 bspPlanes[ i ].dist *= scale;
467 GetVectorForKey( &entities[ 0 ], "gridsize", vec );
468 if( (vec[ 0 ] + vec[ 1 ] + vec[ 2 ]) == 0.0f )
469 VectorCopy( gridSize, vec );
470 VectorScale( vec, scale, vec );
471 sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
472 SetKeyValue( &entities[ 0 ], "gridsize", str );
476 StripExtension( source );
477 DefaultExtension( source, "_s.bsp" );
478 Sys_Printf( "Writing %s\n", source );
479 WriteBSPFile( source );
481 /* return to sender */
489 main argument processing function for bsp conversion
492 int ConvertBSPMain( int argc, char **argv )
495 int (*convertFunc)( char * );
500 convertFunc = ConvertBSPToASE;
506 Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
510 /* process arguments */
511 for( i = 1; i < (argc - 1); i++ )
513 /* -format map|ase|... */
514 if( !strcmp( argv[ i ], "-format" ) )
517 if( !Q_stricmp( argv[ i ], "ase" ) )
518 convertFunc = ConvertBSPToASE;
519 else if( !Q_stricmp( argv[ i ], "map" ) )
520 convertFunc = ConvertBSPToMap;
523 convertGame = GetGame( argv[ i ] );
524 if( convertGame == NULL )
525 Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
530 /* clean up map name */
531 strcpy( source, ExpandArg( argv[ i ] ) );
532 StripExtension( source );
533 DefaultExtension( source, ".bsp" );
537 Sys_Printf( "Loading %s\n", source );
539 /* ydnar: load surface file */
540 //% LoadSurfaceExtraFile( source );
542 LoadBSPFile( source );
544 /* parse bsp entities */
547 /* bsp format convert? */
548 if( convertGame != NULL )
550 /* set global game */
554 StripExtension( source );
555 DefaultExtension( source, "_c.bsp" );
556 Sys_Printf( "Writing %s\n", source );
557 WriteBSPFile( source );
559 /* return to sender */
564 return convertFunc( source );
574 int main( int argc, char **argv )
580 /* we want consistent 'randomness' */
584 start = I_FloatTime();
586 /* this was changed to emit version number over the network */
587 printf( Q3MAP_VERSION "\n" );
592 /* read general options first */
593 for( i = 1; i < argc; i++ )
596 if( !strcmp( argv[ i ], "-connect" ) )
600 Broadcast_Setup( argv[ i ] );
605 else if( !strcmp( argv[ i ], "-v" ) )
612 else if( !strcmp( argv[ i ], "-force" ) )
618 /* patch subdivisions */
619 else if( !strcmp( argv[ i ], "-subdivisions" ) )
623 patchSubdivisions = atoi( argv[ i ] );
625 if( patchSubdivisions <= 0 )
626 patchSubdivisions = 1;
630 else if( !strcmp( argv[ i ], "-threads" ) )
634 numthreads = atoi( argv[ i ] );
639 /* init model library */
641 PicoSetMallocFunc( safe_malloc );
642 PicoSetFreeFunc( free );
643 PicoSetPrintFunc( PicoPrintFunc );
644 PicoSetLoadFileFunc( PicoLoadFileFunc );
645 PicoSetFreeFileFunc( free );
647 /* set number of threads */
650 /* generate sinusoid jitter table */
651 for( i = 0; i < MAX_JITTERS; i++ )
653 jitters[ i ] = sin( i * 139.54152147 );
654 //% Sys_Printf( "Jitter %4d: %f\n", i, jitters[ i ] );
657 /* we print out two versions, q3map's main version (since it evolves a bit out of GtkRadiant)
658 and we put the GtkRadiant version to make it easy to track with what version of Radiant it was built with */
660 Sys_Printf( "Q3Map - v1.0r (c) 1999 Id Software Inc.\n" );
661 Sys_Printf( "Q3Map (ydnar) - v" Q3MAP_VERSION "\n" );
662 Sys_Printf( "GtkRadiant - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
663 Sys_Printf( "%s\n", Q3MAP_MOTD );
665 /* ydnar: new path initialization */
666 InitPaths( &argc, argv );
668 /* check if we have enough options left to attempt something */
670 Error( "Usage: %s [general options] [options] mapfile", argv[ 0 ] );
673 if( !strcmp( argv[ 1 ], "-fixaas" ) )
674 r = FixAAS( argc - 1, argv + 1 );
677 else if( !strcmp( argv[ 1 ], "-analyze" ) )
678 r = AnalyzeBSP( argc - 1, argv + 1 );
681 else if( !strcmp( argv[ 1 ], "-info" ) )
682 r = BSPInfo( argc - 2, argv + 2 );
685 else if( !strcmp( argv[ 1 ], "-vis" ) )
686 r = VisMain( argc - 1, argv + 1 );
689 else if( !strcmp( argv[ 1 ], "-light" ) )
690 r = LightMain( argc - 1, argv + 1 );
693 else if( !strcmp( argv[ 1 ], "-vlight" ) )
695 Sys_Printf( "WARNING: VLight is no longer supported, defaulting to -light -fast instead\n\n" );
696 argv[ 1 ] = "-fast"; /* eek a hack */
697 r = LightMain( argc, argv );
700 /* ydnar: lightmap export */
701 else if( !strcmp( argv[ 1 ], "-export" ) )
702 r = ExportLightmapsMain( argc - 1, argv + 1 );
704 /* ydnar: lightmap import */
705 else if( !strcmp( argv[ 1 ], "-import" ) )
706 r = ImportLightmapsMain( argc - 1, argv + 1 );
708 /* ydnar: bsp scaling */
709 else if( !strcmp( argv[ 1 ], "-scale" ) )
710 r = ScaleBSPMain( argc - 1, argv + 1 );
712 /* ydnar: bsp conversion */
713 else if( !strcmp( argv[ 1 ], "-convert" ) )
714 r = ConvertBSPMain( argc - 1, argv + 1 );
716 /* ydnar: otherwise create a bsp */
718 r = BSPMain( argc, argv );
722 Sys_Printf( "%9.0f seconds elapsed\n", end - start );
724 /* shut down connection */
725 Broadcast_Shutdown();
727 /* return any error code */