]> icculus.org git repositories - divverent/netradiant.git/blob - tools/quake3/q3map2/main.c
initial
[divverent/netradiant.git] / tools / quake3 / q3map2 / main.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 MAIN_C
33
34
35
36 /* dependencies */
37 #include "q3map2.h"
38
39
40
41 /*
42 Random()
43 returns a pseudorandom number between 0 and 1
44 */
45
46 vec_t Random( void )
47 {
48         return (vec_t) rand() / RAND_MAX;
49 }
50
51
52
53 /*
54 ExitQ3Map()
55 cleanup routine
56 */
57
58 static void ExitQ3Map( void )
59 {
60         BSPFilesCleanup();
61         if( mapDrawSurfs != NULL )
62                 free( mapDrawSurfs );
63 }
64
65
66
67 /*
68 MD4BlockChecksum()
69 calculates an md4 checksum for a block of data
70 */
71
72 static int MD4BlockChecksum( void *buffer, int length )
73 {
74         MHASH   mh;
75         int             digest[ 4 ], checksum;
76         
77         
78         /* make md4 hash */
79         mh = mhash_init( MHASH_MD4 );
80         if( !mh )
81                 Error( "Unable to initialize MD4 hash context" );
82         mhash( mh, buffer, length );
83         mhash_deinit( mh, digest );
84         
85         /* xor the bits and return */
86         checksum = digest[ 0 ] ^ digest[ 1 ] ^ digest[ 2 ] ^ digest[ 3 ];
87         return checksum;
88 }
89
90
91
92 /*
93 FixAAS()
94 resets an aas checksum to match the given BSP
95 */
96
97 int FixAAS( int argc, char **argv )
98 {
99         int                     length, checksum;
100         void            *buffer;
101         FILE            *file;
102         char            aas[ 1024 ], **ext;
103         char            *exts[] =
104                                 {
105                                         ".aas",
106                                         "_b0.aas",
107                                         "_b1.aas",
108                                         NULL
109                                 };
110         
111         
112         /* arg checking */
113         if( argc < 2 )
114         {
115                 Sys_Printf( "Usage: q3map -fixaas [-v] <mapname>\n" );
116                 return 0;
117         }
118         
119         /* do some path mangling */
120         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
121         StripExtension( source );
122         DefaultExtension( source, ".bsp" );
123         
124         /* note it */
125         Sys_Printf( "--- FixAAS ---\n" );
126         
127         /* load the bsp */
128         Sys_Printf( "Loading %s\n", source );
129         length = LoadFile( source, &buffer );
130         
131         /* create bsp checksum */
132         Sys_Printf( "Creating checksum...\n" );
133         checksum = LittleLong( MD4BlockChecksum( buffer, length ) );
134         
135         /* write checksum to aas */
136         ext = exts;
137         while( *ext )
138         {
139                 /* mangle name */
140                 strcpy( aas, source );
141                 StripExtension( aas );
142                 strcat( aas, *ext );
143                 Sys_Printf( "Trying %s\n", aas );
144                 ext++;
145                 
146                 /* fix it */
147                 file = fopen( aas, "r+b" );
148                 if( !file )
149                         continue;
150                 if( fwrite( &checksum, 4, 1, file ) != 1 )
151                         Error( "Error writing checksum to %s", aas );
152                 fclose( file );
153         }
154         
155         /* return to sender */
156         return 0;
157 }
158
159
160
161 /*
162 AnalyzeBSP() - ydnar
163 analyzes a Quake engine BSP file
164 */
165
166 typedef struct abspHeader_s
167 {
168         char                    ident[ 4 ];
169         int                             version;
170         
171         bspLump_t               lumps[ 1 ];     /* unknown size */
172 }
173 abspHeader_t;
174
175 typedef struct abspLumpTest_s
176 {
177         int                             radix, minCount;
178         char                    *name;
179 }
180 abspLumpTest_t;
181
182 int AnalyzeBSP( int argc, char **argv )
183 {
184         abspHeader_t                    *header;
185         int                                             size, i, version, offset, length, lumpInt, count;
186         char                                    ident[ 5 ];
187         void                                    *lump;
188         float                                   lumpFloat;
189         char                                    lumpString[ 1024 ], source[ 1024 ];
190         qboolean                                lumpSwap = qfalse;
191         abspLumpTest_t                  *lumpTest;
192         static abspLumpTest_t   lumpTests[] =
193                                                         {
194                                                                 { sizeof( bspPlane_t ),                 6,              "IBSP LUMP_PLANES" },
195                                                                 { sizeof( bspBrush_t ),                 1,              "IBSP LUMP_BRUSHES" },
196                                                                 { 8,                                                    6,              "IBSP LUMP_BRUSHSIDES" },
197                                                                 { sizeof( bspBrushSide_t ),             6,              "RBSP LUMP_BRUSHSIDES" },
198                                                                 { sizeof( bspModel_t ),                 1,              "IBSP LUMP_MODELS" },
199                                                                 { sizeof( bspNode_t ),                  2,              "IBSP LUMP_NODES" },
200                                                                 { sizeof( bspLeaf_t ),                  1,              "IBSP LUMP_LEAFS" },
201                                                                 { 104,                                                  3,              "IBSP LUMP_DRAWSURFS" },
202                                                                 { 44,                                                   3,              "IBSP LUMP_DRAWVERTS" },
203                                                                 { 4,                                                    6,              "IBSP LUMP_DRAWINDEXES" },
204                                                                 { 128 * 128 * 3,                                1,              "IBSP LUMP_LIGHTMAPS" },
205                                                                 { 256 * 256 * 3,                                1,              "IBSP LUMP_LIGHTMAPS (256 x 256)" },
206                                                                 { 512 * 512 * 3,                                1,              "IBSP LUMP_LIGHTMAPS (512 x 512)" },
207                                                                 { 0, 0, NULL }
208                                                         };
209         
210         
211         /* arg checking */
212         if( argc < 1 )
213         {
214                 Sys_Printf( "Usage: q3map -analyze [-lumpswap] [-v] <mapname>\n" );
215                 return 0;
216         }
217         
218         /* process arguments */
219         for( i = 1; i < (argc - 1); i++ )
220         {
221                 /* -format map|ase|... */
222                 if( !strcmp( argv[ i ],  "-lumpswap" ) )
223                 {
224                         Sys_Printf( "Swapped lump structs enabled\n" );
225                         lumpSwap = qtrue;
226                 }
227         }
228         
229         /* clean up map name */
230         strcpy( source, ExpandArg( argv[ i ] ) );
231         Sys_Printf( "Loading %s\n", source );
232         
233         /* load the file */
234         size = LoadFile( source, (void**) &header );
235         if( size == 0 || header == NULL )
236         {
237                 Sys_Printf( "Unable to load %s.\n", source );
238                 return -1;
239         }
240         
241         /* analyze ident/version */
242         memcpy( ident, header->ident, 4 );
243         ident[ 4 ] = '\0';
244         version = LittleLong( header->version );
245         
246         Sys_Printf( "Identity:      %s\n", ident );
247         Sys_Printf( "Version:       %d\n", version );
248         Sys_Printf( "---------------------------------------\n" );
249         
250         /* analyze each lump */
251         for( i = 0; i < 100; i++ )
252         {
253                 /* call of duty swapped lump pairs */
254                 if( lumpSwap )
255                 {
256                         offset = LittleLong( header->lumps[ i ].length );
257                         length = LittleLong( header->lumps[ i ].offset );
258                 }
259                 
260                 /* standard lump pairs */
261                 else
262                 {
263                         offset = LittleLong( header->lumps[ i ].offset );
264                         length = LittleLong( header->lumps[ i ].length );
265                 }
266                 
267                 /* extract data */
268                 lump = (byte*) header + offset;
269                 lumpInt = LittleLong( (int) *((int*) lump) );
270                 lumpFloat = LittleFloat( (float) *((float*) lump) );
271                 memcpy( lumpString, (char*) lump, (length < 1024 ? length : 1024) );
272                 lumpString[ 1024 ] = '\0';
273                 
274                 /* print basic lump info */
275                 Sys_Printf( "Lump:          %d\n", i );
276                 Sys_Printf( "Offset:        %d bytes\n", offset );
277                 Sys_Printf( "Length:        %d bytes\n", length );
278                 
279                 /* only operate on valid lumps */
280                 if( length > 0 )
281                 {
282                         /* print data in 4 formats */
283                         Sys_Printf( "As hex:        %08X\n", lumpInt );
284                         Sys_Printf( "As int:        %d\n", lumpInt );
285                         Sys_Printf( "As float:      %f\n", lumpFloat );
286                         Sys_Printf( "As string:     %s\n", lumpString );
287                         
288                         /* guess lump type */
289                         if( lumpString[ 0 ] == '{' && lumpString[ 2 ] == '"' )
290                                 Sys_Printf( "Type guess:    IBSP LUMP_ENTITIES\n" );
291                         else if( strstr( lumpString, "textures/" ) )
292                                 Sys_Printf( "Type guess:    IBSP LUMP_SHADERS\n" );
293                         else
294                         {
295                                 /* guess based on size/count */
296                                 for( lumpTest = lumpTests; lumpTest->radix > 0; lumpTest++ )
297                                 {
298                                         if( (length % lumpTest->radix) != 0 )
299                                                 continue;
300                                         count = length / lumpTest->radix;
301                                         if( count < lumpTest->minCount )
302                                                 continue;
303                                         Sys_Printf( "Type guess:    %s (%d x %d)\n", lumpTest->name, count, lumpTest->radix );
304                                 }
305                         }
306                 }
307                 
308                 Sys_Printf( "---------------------------------------\n" );
309                 
310                 /* end of file */
311                 if( offset + length >= size )
312                         break;
313         }
314         
315         /* last stats */
316         Sys_Printf( "Lump count:    %d\n", i + 1 );
317         Sys_Printf( "File size:     %d bytes\n", size );
318         
319         /* return to caller */
320         return 0;
321 }
322
323
324
325 /*
326 BSPInfo()
327 emits statistics about the bsp file
328 */
329
330 int BSPInfo( int count, char **fileNames )
331 {
332         int                     i;
333         char            source[ 1024 ], ext[ 64 ];
334         int                     size;
335         FILE            *f;
336         
337         
338         /* dummy check */
339         if( count < 1 )
340         {
341                 Sys_Printf( "No files to dump info for.\n");
342                 return -1;
343         }
344         
345         /* enable info mode */
346         infoMode = qtrue;
347         
348         /* walk file list */
349         for( i = 0; i < count; i++ )
350         {
351                 Sys_Printf( "---------------------------------\n" );
352                 
353                 /* mangle filename and get size */
354                 strcpy( source, fileNames[ i ] );
355                 ExtractFileExtension( source, ext );
356                 if( !Q_stricmp( ext, "map" ) )
357                         StripExtension( source );
358                 DefaultExtension( source, ".bsp" );
359                 f = fopen( source, "rb" );
360                 if( f )
361                 {
362                         size = Q_filelength (f);
363                         fclose( f );
364                 }
365                 else
366                         size = 0;
367                 
368                 /* load the bsp file and print lump sizes */
369                 Sys_Printf( "%s\n", source );
370                 LoadBSPFile( source );          
371                 PrintBSPFileSizes();
372                 
373                 /* print sizes */
374                 Sys_Printf( "\n" );
375                 Sys_Printf( "          total         %9d\n", size );
376                 Sys_Printf( "                        %9d KB\n", size / 1024 );
377                 Sys_Printf( "                        %9d MB\n", size / (1024 * 1024) );
378                 
379                 Sys_Printf( "---------------------------------\n" );
380         }
381         
382         /* return count */
383         return i;
384 }
385
386
387
388 /*
389 ScaleBSPMain()
390 amaze and confuse your enemies with wierd scaled maps!
391 */
392
393 int ScaleBSPMain( int argc, char **argv )
394 {
395         int                     i;
396         float           f, scale;
397         vec3_t          vec;
398         char            str[ 1024 ];
399         
400         
401         /* arg checking */
402         if( argc < 2 )
403         {
404                 Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
405                 return 0;
406         }
407         
408         /* get scale */
409         scale = atof( argv[ argc - 2 ] );
410         if( scale == 0.0f )
411         {
412                 Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
413                 Sys_Printf( "Non-zero scale value required.\n" );
414                 return 0;
415         }
416         
417         /* do some path mangling */
418         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
419         StripExtension( source );
420         DefaultExtension( source, ".bsp" );
421         
422         /* load the bsp */
423         Sys_Printf( "Loading %s\n", source );
424         LoadBSPFile( source );
425         ParseEntities();
426         
427         /* note it */
428         Sys_Printf( "--- ScaleBSP ---\n" );
429         Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
430         
431         /* scale entity keys */
432         for( i = 0; i < numBSPEntities && i < numEntities; i++ )
433         {
434                 /* scale origin */
435                 GetVectorForKey( &entities[ i ], "origin", vec );
436                 if( (vec[ 0 ] + vec[ 1 ] + vec[ 2 ]) )
437                 {
438                         VectorScale( vec, scale, vec );
439                         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
440                         SetKeyValue( &entities[ i ], "origin", str );
441                 }
442                 
443                 /* scale door lip */
444                 f = FloatForKey( &entities[ i ], "lip" );
445                 if( f )
446                 {
447                         f *= scale;
448                         sprintf( str, "%f", f );
449                         SetKeyValue( &entities[ i ], "lip", str );
450                 }
451         }
452         
453         /* scale models */
454         for( i = 0; i < numBSPModels; i++ )
455         {
456                 VectorScale( bspModels[ i ].mins, scale, bspModels[ i ].mins );
457                 VectorScale( bspModels[ i ].maxs, scale, bspModels[ i ].maxs );
458         }
459         
460         /* scale nodes */
461         for( i = 0; i < numBSPNodes; i++ )
462         {
463                 VectorScale( bspNodes[ i ].mins, scale, bspNodes[ i ].mins );
464                 VectorScale( bspNodes[ i ].maxs, scale, bspNodes[ i ].maxs );
465         }
466         
467         /* scale leafs */
468         for( i = 0; i < numBSPLeafs; i++ )
469         {
470                 VectorScale( bspLeafs[ i ].mins, scale, bspLeafs[ i ].mins );
471                 VectorScale( bspLeafs[ i ].maxs, scale, bspLeafs[ i ].maxs );
472         }
473         
474         /* scale drawverts */
475         for( i = 0; i < numBSPDrawVerts; i++ )
476                 VectorScale( bspDrawVerts[ i ].xyz, scale, bspDrawVerts[ i ].xyz );
477         
478         /* scale planes */
479         for( i = 0; i < numBSPPlanes; i++ )
480                 bspPlanes[ i ].dist *= scale;
481         
482         /* scale gridsize */
483         GetVectorForKey( &entities[ 0 ], "gridsize", vec );
484         if( (vec[ 0 ] + vec[ 1 ] + vec[ 2 ]) == 0.0f )
485                 VectorCopy( gridSize, vec );
486         VectorScale( vec, scale, vec );
487         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
488         SetKeyValue( &entities[ 0 ], "gridsize", str );
489         
490         /* write the bsp */
491         UnparseEntities();
492         StripExtension( source );
493         DefaultExtension( source, "_s.bsp" );
494         Sys_Printf( "Writing %s\n", source );
495         WriteBSPFile( source );
496         
497         /* return to sender */
498         return 0;
499 }
500
501
502
503 /*
504 ConvertBSPMain()
505 main argument processing function for bsp conversion
506 */
507
508 int ConvertBSPMain( int argc, char **argv )
509 {
510         int             i;
511         int             (*convertFunc)( char * );
512         game_t  *convertGame;
513         
514         
515         /* set default */
516         convertFunc = ConvertBSPToASE;
517         convertGame = NULL;
518         
519         /* arg checking */
520         if( argc < 1 )
521         {
522                 Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
523                 return 0;
524         }
525         
526         /* process arguments */
527         for( i = 1; i < (argc - 1); i++ )
528         {
529                 /* -format map|ase|... */
530                 if( !strcmp( argv[ i ],  "-format" ) )
531                 {
532                         i++;
533                         if( !Q_stricmp( argv[ i ], "ase" ) )
534                                 convertFunc = ConvertBSPToASE;
535                         else if( !Q_stricmp( argv[ i ], "map" ) )
536                                 convertFunc = ConvertBSPToMap;
537                         else
538                         {
539                                 convertGame = GetGame( argv[ i ] );
540                                 if( convertGame == NULL )
541                                         Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
542                         }
543                 }
544                 else if( !strcmp( argv[ i ],  "-ne" ) )
545                 {
546                         normalEpsilon = atof( argv[ i + 1 ] );
547                         i++;
548                         Sys_Printf( "Normal epsilon set to %f\n", normalEpsilon );
549                 }
550                 else if( !strcmp( argv[ i ],  "-de" ) )
551                 {
552                         distanceEpsilon = atof( argv[ i + 1 ] );
553                         i++;
554                         Sys_Printf( "Distance epsilon set to %f\n", distanceEpsilon );
555                 }
556         }
557         
558         /* clean up map name */
559         strcpy( source, ExpandArg( argv[ i ] ) );
560         StripExtension( source );
561         DefaultExtension( source, ".bsp" );
562         
563         LoadShaderInfo();
564         
565         Sys_Printf( "Loading %s\n", source );
566         
567         /* ydnar: load surface file */
568         //%     LoadSurfaceExtraFile( source );
569         
570         LoadBSPFile( source );
571         
572         /* parse bsp entities */
573         ParseEntities();
574         
575         /* bsp format convert? */
576         if( convertGame != NULL )
577         {
578                 /* set global game */
579                 game = convertGame;
580                 
581                 /* write bsp */
582                 StripExtension( source );
583                 DefaultExtension( source, "_c.bsp" );
584                 Sys_Printf( "Writing %s\n", source );
585                 WriteBSPFile( source );
586                 
587                 /* return to sender */
588                 return 0;
589         }
590         
591         /* normal convert */
592         return convertFunc( source );
593 }
594
595
596
597 /*
598 main()
599 q3map mojo...
600 */
601
602 int main( int argc, char **argv )
603 {
604         int             i, r;
605         double  start, end;
606         
607         
608         /* we want consistent 'randomness' */
609         srand( 0 );
610         
611         /* start timer */
612         start = I_FloatTime();
613
614         /* this was changed to emit version number over the network */
615         printf( Q3MAP_VERSION "\n" );
616         
617         /* set exit call */
618         atexit( ExitQ3Map );
619         
620         /* read general options first */
621         for( i = 1; i < argc; i++ )
622         {
623                 /* -connect */
624                 if( !strcmp( argv[ i ], "-connect" ) )
625                 {
626                         argv[ i ] = NULL;
627                         i++;
628                         Broadcast_Setup( argv[ i ] );
629                         argv[ i ] = NULL;
630                 }
631                 
632                 /* verbose */
633                 else if( !strcmp( argv[ i ], "-v" ) )
634                 {
635                         verbose = qtrue;
636                         argv[ i ] = NULL;
637                 }
638                 
639                 /* force */
640                 else if( !strcmp( argv[ i ], "-force" ) )
641                 {
642                         force = qtrue;
643                         argv[ i ] = NULL;
644                 }
645                 
646                 /* patch subdivisions */
647                 else if( !strcmp( argv[ i ], "-subdivisions" ) )
648                 {
649                         argv[ i ] = NULL;
650                         i++;
651                         patchSubdivisions = atoi( argv[ i ] );
652                         argv[ i ] = NULL;
653                         if( patchSubdivisions <= 0 )
654                                 patchSubdivisions = 1;
655                 }
656                 
657                 /* threads */
658                 else if( !strcmp( argv[ i ], "-threads" ) )
659                 {
660                         argv[ i ] = NULL;
661                         i++;
662                         numthreads = atoi( argv[ i ] );
663                         argv[ i ] = NULL;
664                 }
665         }
666         
667         /* init model library */
668         PicoInit();
669         PicoSetMallocFunc( safe_malloc );
670         PicoSetFreeFunc( free );
671         PicoSetPrintFunc( PicoPrintFunc );
672         PicoSetLoadFileFunc( PicoLoadFileFunc );
673         PicoSetFreeFileFunc( free );
674         
675         /* set number of threads */
676         ThreadSetDefault();
677         
678         /* generate sinusoid jitter table */
679         for( i = 0; i < MAX_JITTERS; i++ )
680         {
681                 jitters[ i ] = sin( i * 139.54152147 );
682                 //%     Sys_Printf( "Jitter %4d: %f\n", i, jitters[ i ] );
683         }
684         
685         /* we print out two versions, q3map's main version (since it evolves a bit out of GtkRadiant)
686            and we put the GtkRadiant version to make it easy to track with what version of Radiant it was built with */
687         
688         Sys_Printf( "Q3Map         - v1.0r (c) 1999 Id Software Inc.\n" );
689         Sys_Printf( "Q3Map (ydnar) - v" Q3MAP_VERSION "\n" );
690         Sys_Printf( "GtkRadiant    - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
691         Sys_Printf( "%s\n", Q3MAP_MOTD );
692         
693         /* ydnar: new path initialization */
694         InitPaths( &argc, argv );
695         
696         /* check if we have enough options left to attempt something */
697         if( argc < 2 )
698                 Error( "Usage: %s [general options] [options] mapfile", argv[ 0 ] );
699         
700         /* fixaas */
701         if( !strcmp( argv[ 1 ], "-fixaas" ) )
702                 r = FixAAS( argc - 1, argv + 1 );
703         
704         /* analyze */
705         else if( !strcmp( argv[ 1 ], "-analyze" ) )
706                 r = AnalyzeBSP( argc - 1, argv + 1 );
707         
708         /* info */
709         else if( !strcmp( argv[ 1 ], "-info" ) )
710                 r = BSPInfo( argc - 2, argv + 2 );
711         
712         /* vis */
713         else if( !strcmp( argv[ 1 ], "-vis" ) )
714                 r = VisMain( argc - 1, argv + 1 );
715         
716         /* light */
717         else if( !strcmp( argv[ 1 ], "-light" ) )
718                 r = LightMain( argc - 1, argv + 1 );
719         
720         /* vlight */
721         else if( !strcmp( argv[ 1 ], "-vlight" ) )
722         {
723                 Sys_Printf( "WARNING: VLight is no longer supported, defaulting to -light -fast instead\n\n" );
724                 argv[ 1 ] = "-fast";    /* eek a hack */
725                 r = LightMain( argc, argv );
726         }
727         
728         /* ydnar: lightmap export */
729         else if( !strcmp( argv[ 1 ], "-export" ) )
730                 r = ExportLightmapsMain( argc - 1, argv + 1 );
731         
732         /* ydnar: lightmap import */
733         else if( !strcmp( argv[ 1 ], "-import" ) )
734                 r = ImportLightmapsMain( argc - 1, argv + 1 );
735         
736         /* ydnar: bsp scaling */
737         else if( !strcmp( argv[ 1 ], "-scale" ) )
738                 r = ScaleBSPMain( argc - 1, argv + 1 );
739         
740         /* ydnar: bsp conversion */
741         else if( !strcmp( argv[ 1 ], "-convert" ) )
742                 r = ConvertBSPMain( argc - 1, argv + 1 );
743         
744         /* ydnar: otherwise create a bsp */
745         else
746                 r = BSPMain( argc, argv );
747         
748         /* emit time */
749         end = I_FloatTime();
750         Sys_Printf( "%9.0f seconds elapsed\n", end - start );
751         
752         /* shut down connection */
753         Broadcast_Shutdown();
754         
755         /* return any error code */
756         return r;
757 }