split minimap stuff from q3map2
[xonotic/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         return (vec_t) rand() / RAND_MAX;
48 }
49
50
51 char *Q_strncpyz( char *dst, const char *src, size_t len ) {
52         if ( len == 0 ) {
53                 abort();
54         }
55
56         strncpy( dst, src, len );
57         dst[ len - 1 ] = '\0';
58         return dst;
59 }
60
61
62 char *Q_strcat( char *dst, size_t dlen, const char *src ) {
63         size_t n = strlen( dst  );
64
65         if ( n > dlen ) {
66                 abort(); /* buffer overflow */
67         }
68
69         return Q_strncpyz( dst + n, src, dlen - n );
70 }
71
72
73 char *Q_strncat( char *dst, size_t dlen, const char *src, size_t slen ) {
74         size_t n = strlen( dst );
75
76         if ( n > dlen ) {
77                 abort(); /* buffer overflow */
78         }
79
80         return Q_strncpyz( dst + n, src, MIN( slen, dlen - n ) );
81 }
82
83 /*
84    ExitQ3Map()
85    cleanup routine
86  */
87
88 static void ExitQ3Map( void ){
89         BSPFilesCleanup();
90         if ( mapDrawSurfs != NULL ) {
91                 free( mapDrawSurfs );
92         }
93 }
94
95 /*
96    MD4BlockChecksum()
97    calculates an md4 checksum for a block of data
98  */
99
100 static int MD4BlockChecksum( void *buffer, int length ){
101         return Com_BlockChecksum( buffer, length );
102 }
103
104 /*
105    FixAAS()
106    resets an aas checksum to match the given BSP
107  */
108
109 int FixAAS( int argc, char **argv ){
110         int length, checksum;
111         void        *buffer;
112         FILE        *file;
113         char aas[ 1024 ], **ext;
114         char        *exts[] =
115         {
116                 ".aas",
117                 "_b0.aas",
118                 "_b1.aas",
119                 NULL
120         };
121
122
123         /* arg checking */
124         if ( argc < 2 ) {
125                 Sys_Printf( "Usage: q3map -fixaas [-v] <mapname>\n" );
126                 return 0;
127         }
128
129         /* do some path mangling */
130         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
131         StripExtension( source );
132         DefaultExtension( source, ".bsp" );
133
134         /* note it */
135         Sys_Printf( "--- FixAAS ---\n" );
136
137         /* load the bsp */
138         Sys_Printf( "Loading %s\n", source );
139         length = LoadFile( source, &buffer );
140
141         /* create bsp checksum */
142         Sys_Printf( "Creating checksum...\n" );
143         checksum = LittleLong( MD4BlockChecksum( buffer, length ) );
144
145         /* write checksum to aas */
146         ext = exts;
147         while ( *ext )
148         {
149                 /* mangle name */
150                 strcpy( aas, source );
151                 StripExtension( aas );
152                 strcat( aas, *ext );
153                 Sys_Printf( "Trying %s\n", aas );
154                 ext++;
155
156                 /* fix it */
157                 file = fopen( aas, "r+b" );
158                 if ( !file ) {
159                         continue;
160                 }
161                 if ( fwrite( &checksum, 4, 1, file ) != 1 ) {
162                         Error( "Error writing checksum to %s", aas );
163                 }
164                 fclose( file );
165         }
166
167         /* return to sender */
168         return 0;
169 }
170
171
172
173 /*
174    AnalyzeBSP() - ydnar
175    analyzes a Quake engine BSP file
176  */
177
178 typedef struct abspHeader_s
179 {
180         char ident[ 4 ];
181         int version;
182
183         bspLump_t lumps[ 1 ];       /* unknown size */
184 }
185 abspHeader_t;
186
187 typedef struct abspLumpTest_s
188 {
189         int radix, minCount;
190         char            *name;
191 }
192 abspLumpTest_t;
193
194 int AnalyzeBSP( int argc, char **argv ){
195         abspHeader_t            *header;
196         int size, i, version, offset, length, lumpInt, count;
197         char ident[ 5 ];
198         void                    *lump;
199         float lumpFloat;
200         char lumpString[ 1024 ], source[ 1024 ];
201         qboolean lumpSwap = qfalse;
202         abspLumpTest_t          *lumpTest;
203         static abspLumpTest_t lumpTests[] =
204         {
205                 { sizeof( bspPlane_t ),         6,      "IBSP LUMP_PLANES" },
206                 { sizeof( bspBrush_t ),         1,      "IBSP LUMP_BRUSHES" },
207                 { 8,                            6,      "IBSP LUMP_BRUSHSIDES" },
208                 { sizeof( bspBrushSide_t ),     6,      "RBSP LUMP_BRUSHSIDES" },
209                 { sizeof( bspModel_t ),         1,      "IBSP LUMP_MODELS" },
210                 { sizeof( bspNode_t ),          2,      "IBSP LUMP_NODES" },
211                 { sizeof( bspLeaf_t ),          1,      "IBSP LUMP_LEAFS" },
212                 { 104,                          3,      "IBSP LUMP_DRAWSURFS" },
213                 { 44,                           3,      "IBSP LUMP_DRAWVERTS" },
214                 { 4,                            6,      "IBSP LUMP_DRAWINDEXES" },
215                 { 128 * 128 * 3,                1,      "IBSP LUMP_LIGHTMAPS" },
216                 { 256 * 256 * 3,                1,      "IBSP LUMP_LIGHTMAPS (256 x 256)" },
217                 { 512 * 512 * 3,                1,      "IBSP LUMP_LIGHTMAPS (512 x 512)" },
218                 { 0, 0, NULL }
219         };
220
221
222         /* arg checking */
223         if ( argc < 1 ) {
224                 Sys_Printf( "Usage: q3map -analyze [-lumpswap] [-v] <mapname>\n" );
225                 return 0;
226         }
227
228         /* process arguments */
229         for ( i = 1; i < ( argc - 1 ); i++ )
230         {
231                 /* -format map|ase|... */
232                 if ( !strcmp( argv[ i ],  "-lumpswap" ) ) {
233                         Sys_Printf( "Swapped lump structs enabled\n" );
234                         lumpSwap = qtrue;
235                 }
236         }
237
238         /* clean up map name */
239         strcpy( source, ExpandArg( argv[ i ] ) );
240         Sys_Printf( "Loading %s\n", source );
241
242         /* load the file */
243         size = LoadFile( source, (void**) &header );
244         if ( size == 0 || header == NULL ) {
245                 Sys_Printf( "Unable to load %s.\n", source );
246                 return -1;
247         }
248
249         /* analyze ident/version */
250         memcpy( ident, header->ident, 4 );
251         ident[ 4 ] = '\0';
252         version = LittleLong( header->version );
253
254         Sys_Printf( "Identity:      %s\n", ident );
255         Sys_Printf( "Version:       %d\n", version );
256         Sys_Printf( "---------------------------------------\n" );
257
258         /* analyze each lump */
259         for ( i = 0; i < 100; i++ )
260         {
261                 /* call of duty swapped lump pairs */
262                 if ( lumpSwap ) {
263                         offset = LittleLong( header->lumps[ i ].length );
264                         length = LittleLong( header->lumps[ i ].offset );
265                 }
266
267                 /* standard lump pairs */
268                 else
269                 {
270                         offset = LittleLong( header->lumps[ i ].offset );
271                         length = LittleLong( header->lumps[ i ].length );
272                 }
273
274                 /* extract data */
275                 lump = (byte*) header + offset;
276                 lumpInt = LittleLong( (int) *( (int*) lump ) );
277                 lumpFloat = LittleFloat( (float) *( (float*) lump ) );
278                 memcpy( lumpString, (char*) lump, ( (size_t)length < sizeof( lumpString ) ? (size_t)length : sizeof( lumpString ) - 1 ) );
279                 lumpString[ sizeof( lumpString ) - 1 ] = '\0';
280
281                 /* print basic lump info */
282                 Sys_Printf( "Lump:          %d\n", i );
283                 Sys_Printf( "Offset:        %d bytes\n", offset );
284                 Sys_Printf( "Length:        %d bytes\n", length );
285
286                 /* only operate on valid lumps */
287                 if ( length > 0 ) {
288                         /* print data in 4 formats */
289                         Sys_Printf( "As hex:        %08X\n", lumpInt );
290                         Sys_Printf( "As int:        %d\n", lumpInt );
291                         Sys_Printf( "As float:      %f\n", lumpFloat );
292                         Sys_Printf( "As string:     %s\n", lumpString );
293
294                         /* guess lump type */
295                         if ( lumpString[ 0 ] == '{' && lumpString[ 2 ] == '"' ) {
296                                 Sys_Printf( "Type guess:    IBSP LUMP_ENTITIES\n" );
297                         }
298                         else if ( strstr( lumpString, "textures/" ) ) {
299                                 Sys_Printf( "Type guess:    IBSP LUMP_SHADERS\n" );
300                         }
301                         else
302                         {
303                                 /* guess based on size/count */
304                                 for ( lumpTest = lumpTests; lumpTest->radix > 0; lumpTest++ )
305                                 {
306                                         if ( ( length % lumpTest->radix ) != 0 ) {
307                                                 continue;
308                                         }
309                                         count = length / lumpTest->radix;
310                                         if ( count < lumpTest->minCount ) {
311                                                 continue;
312                                         }
313                                         Sys_Printf( "Type guess:    %s (%d x %d)\n", lumpTest->name, count, lumpTest->radix );
314                                 }
315                         }
316                 }
317
318                 Sys_Printf( "---------------------------------------\n" );
319
320                 /* end of file */
321                 if ( offset + length >= size ) {
322                         break;
323                 }
324         }
325
326         /* last stats */
327         Sys_Printf( "Lump count:    %d\n", i + 1 );
328         Sys_Printf( "File size:     %d bytes\n", size );
329
330         /* return to caller */
331         return 0;
332 }
333
334
335
336 /*
337    BSPInfo()
338    emits statistics about the bsp file
339  */
340
341 int BSPInfo( int count, char **fileNames ){
342         int i;
343         char source[ 1024 ], ext[ 64 ];
344         int size;
345         FILE        *f;
346
347
348         /* dummy check */
349         if ( count < 1 ) {
350                 Sys_Printf( "No files to dump info for.\n" );
351                 return -1;
352         }
353
354         /* enable info mode */
355         infoMode = qtrue;
356
357         /* walk file list */
358         for ( i = 0; i < count; i++ )
359         {
360                 Sys_Printf( "---------------------------------\n" );
361
362                 /* mangle filename and get size */
363                 strcpy( source, fileNames[ i ] );
364                 ExtractFileExtension( source, ext );
365                 if ( !Q_stricmp( ext, "map" ) ) {
366                         StripExtension( source );
367                 }
368                 DefaultExtension( source, ".bsp" );
369                 f = fopen( source, "rb" );
370                 if ( f ) {
371                         size = Q_filelength( f );
372                         fclose( f );
373                 }
374                 else{
375                         size = 0;
376                 }
377
378                 /* load the bsp file and print lump sizes */
379                 Sys_Printf( "%s\n", source );
380                 LoadBSPFile( source );
381                 PrintBSPFileSizes();
382
383                 /* print sizes */
384                 Sys_Printf( "\n" );
385                 Sys_Printf( "          total         %9d\n", size );
386                 Sys_Printf( "                        %9d KB\n", size / 1024 );
387                 Sys_Printf( "                        %9d MB\n", size / ( 1024 * 1024 ) );
388
389                 Sys_Printf( "---------------------------------\n" );
390         }
391
392         /* return count */
393         return i;
394 }
395
396
397 static void ExtrapolateTexcoords( const float *axyz, const float *ast, const float *bxyz, const float *bst, const float *cxyz, const float *cst, const float *axyz_new, float *ast_out, const float *bxyz_new, float *bst_out, const float *cxyz_new, float *cst_out ){
398         vec4_t scoeffs, tcoeffs;
399         float md;
400         m4x4_t solvematrix;
401
402         vec3_t norm;
403         vec3_t dab, dac;
404         VectorSubtract( bxyz, axyz, dab );
405         VectorSubtract( cxyz, axyz, dac );
406         CrossProduct( dab, dac, norm );
407
408         // assume:
409         //   s = f(x, y, z)
410         //   s(v + norm) = s(v) when n ortho xyz
411
412         // s(v) = DotProduct(v, scoeffs) + scoeffs[3]
413
414         // solve:
415         //   scoeffs * (axyz, 1) == ast[0]
416         //   scoeffs * (bxyz, 1) == bst[0]
417         //   scoeffs * (cxyz, 1) == cst[0]
418         //   scoeffs * (norm, 0) == 0
419         // scoeffs * [axyz, 1 | bxyz, 1 | cxyz, 1 | norm, 0] = [ast[0], bst[0], cst[0], 0]
420         solvematrix[0] = axyz[0];
421         solvematrix[4] = axyz[1];
422         solvematrix[8] = axyz[2];
423         solvematrix[12] = 1;
424         solvematrix[1] = bxyz[0];
425         solvematrix[5] = bxyz[1];
426         solvematrix[9] = bxyz[2];
427         solvematrix[13] = 1;
428         solvematrix[2] = cxyz[0];
429         solvematrix[6] = cxyz[1];
430         solvematrix[10] = cxyz[2];
431         solvematrix[14] = 1;
432         solvematrix[3] = norm[0];
433         solvematrix[7] = norm[1];
434         solvematrix[11] = norm[2];
435         solvematrix[15] = 0;
436
437         md = m4_det( solvematrix );
438         if ( md * md < 1e-10 ) {
439                 Sys_Printf( "Cannot invert some matrix, some texcoords aren't extrapolated!" );
440                 return;
441         }
442
443         m4x4_invert( solvematrix );
444
445         scoeffs[0] = ast[0];
446         scoeffs[1] = bst[0];
447         scoeffs[2] = cst[0];
448         scoeffs[3] = 0;
449         m4x4_transform_vec4( solvematrix, scoeffs );
450         tcoeffs[0] = ast[1];
451         tcoeffs[1] = bst[1];
452         tcoeffs[2] = cst[1];
453         tcoeffs[3] = 0;
454         m4x4_transform_vec4( solvematrix, tcoeffs );
455
456         ast_out[0] = scoeffs[0] * axyz_new[0] + scoeffs[1] * axyz_new[1] + scoeffs[2] * axyz_new[2] + scoeffs[3];
457         ast_out[1] = tcoeffs[0] * axyz_new[0] + tcoeffs[1] * axyz_new[1] + tcoeffs[2] * axyz_new[2] + tcoeffs[3];
458         bst_out[0] = scoeffs[0] * bxyz_new[0] + scoeffs[1] * bxyz_new[1] + scoeffs[2] * bxyz_new[2] + scoeffs[3];
459         bst_out[1] = tcoeffs[0] * bxyz_new[0] + tcoeffs[1] * bxyz_new[1] + tcoeffs[2] * bxyz_new[2] + tcoeffs[3];
460         cst_out[0] = scoeffs[0] * cxyz_new[0] + scoeffs[1] * cxyz_new[1] + scoeffs[2] * cxyz_new[2] + scoeffs[3];
461         cst_out[1] = tcoeffs[0] * cxyz_new[0] + tcoeffs[1] * cxyz_new[1] + tcoeffs[2] * cxyz_new[2] + tcoeffs[3];
462 }
463
464 /*
465    ScaleBSPMain()
466    amaze and confuse your enemies with wierd scaled maps!
467  */
468
469 int ScaleBSPMain( int argc, char **argv ){
470         int i, j;
471         float f, a;
472         vec3_t scale;
473         vec3_t vec;
474         char str[ 1024 ];
475         int uniform, axis;
476         qboolean texscale;
477         float *old_xyzst = NULL;
478         float spawn_ref = 0;
479
480
481         /* arg checking */
482         if ( argc < 3 ) {
483                 Sys_Printf( "Usage: q3map [-v] -scale [-tex] [-spawn_ref <value>] <value> <mapname>\n" );
484                 return 0;
485         }
486
487         texscale = qfalse;
488         for ( i = 1; i < argc - 2; ++i )
489         {
490                 if ( !strcmp( argv[i], "-tex" ) ) {
491                         texscale = qtrue;
492                 }
493                 else if ( !strcmp( argv[i], "-spawn_ref" ) ) {
494                         spawn_ref = atof( argv[i + 1] );
495                         ++i;
496                 }
497                 else{
498                         break;
499                 }
500         }
501
502         /* get scale */
503         // if(argc-2 >= i) // always true
504         scale[2] = scale[1] = scale[0] = atof( argv[ argc - 2 ] );
505         if ( argc - 3 >= i ) {
506                 scale[1] = scale[0] = atof( argv[ argc - 3 ] );
507         }
508         if ( argc - 4 >= i ) {
509                 scale[0] = atof( argv[ argc - 4 ] );
510         }
511
512         uniform = ( ( scale[0] == scale[1] ) && ( scale[1] == scale[2] ) );
513
514         if ( scale[0] == 0.0f || scale[1] == 0.0f || scale[2] == 0.0f ) {
515                 Sys_Printf( "Usage: q3map [-v] -scale [-tex] [-spawn_ref <value>] <value> <mapname>\n" );
516                 Sys_Printf( "Non-zero scale value required.\n" );
517                 return 0;
518         }
519
520         /* do some path mangling */
521         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
522         StripExtension( source );
523         DefaultExtension( source, ".bsp" );
524
525         /* load the bsp */
526         Sys_Printf( "Loading %s\n", source );
527         LoadBSPFile( source );
528         ParseEntities();
529
530         /* note it */
531         Sys_Printf( "--- ScaleBSP ---\n" );
532         Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
533
534         /* scale entity keys */
535         for ( i = 0; i < numBSPEntities && i < numEntities; i++ )
536         {
537                 /* scale origin */
538                 GetVectorForKey( &entities[ i ], "origin", vec );
539                 if ( ( vec[ 0 ] || vec[ 1 ] || vec[ 2 ] ) ) {
540                         if ( !strncmp( ValueForKey( &entities[i], "classname" ), "info_player_", 12 ) ) {
541                                 vec[2] += spawn_ref;
542                         }
543                         vec[0] *= scale[0];
544                         vec[1] *= scale[1];
545                         vec[2] *= scale[2];
546                         if ( !strncmp( ValueForKey( &entities[i], "classname" ), "info_player_", 12 ) ) {
547                                 vec[2] -= spawn_ref;
548                         }
549                         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
550                         SetKeyValue( &entities[ i ], "origin", str );
551                 }
552
553                 a = FloatForKey( &entities[ i ], "angle" );
554                 if ( a == -1 || a == -2 ) { // z scale
555                         axis = 2;
556                 }
557                 else if ( fabs( sin( DEG2RAD( a ) ) ) < 0.707 ) {
558                         axis = 0;
559                 }
560                 else{
561                         axis = 1;
562                 }
563
564                 /* scale door lip */
565                 f = FloatForKey( &entities[ i ], "lip" );
566                 if ( f ) {
567                         f *= scale[axis];
568                         sprintf( str, "%f", f );
569                         SetKeyValue( &entities[ i ], "lip", str );
570                 }
571
572                 /* scale plat height */
573                 f = FloatForKey( &entities[ i ], "height" );
574                 if ( f ) {
575                         f *= scale[2];
576                         sprintf( str, "%f", f );
577                         SetKeyValue( &entities[ i ], "height", str );
578                 }
579
580                 // TODO maybe allow a definition file for entities to specify which values are scaled how?
581         }
582
583         /* scale models */
584         for ( i = 0; i < numBSPModels; i++ )
585         {
586                 bspModels[ i ].mins[0] *= scale[0];
587                 bspModels[ i ].mins[1] *= scale[1];
588                 bspModels[ i ].mins[2] *= scale[2];
589                 bspModels[ i ].maxs[0] *= scale[0];
590                 bspModels[ i ].maxs[1] *= scale[1];
591                 bspModels[ i ].maxs[2] *= scale[2];
592         }
593
594         /* scale nodes */
595         for ( i = 0; i < numBSPNodes; i++ )
596         {
597                 bspNodes[ i ].mins[0] *= scale[0];
598                 bspNodes[ i ].mins[1] *= scale[1];
599                 bspNodes[ i ].mins[2] *= scale[2];
600                 bspNodes[ i ].maxs[0] *= scale[0];
601                 bspNodes[ i ].maxs[1] *= scale[1];
602                 bspNodes[ i ].maxs[2] *= scale[2];
603         }
604
605         /* scale leafs */
606         for ( i = 0; i < numBSPLeafs; i++ )
607         {
608                 bspLeafs[ i ].mins[0] *= scale[0];
609                 bspLeafs[ i ].mins[1] *= scale[1];
610                 bspLeafs[ i ].mins[2] *= scale[2];
611                 bspLeafs[ i ].maxs[0] *= scale[0];
612                 bspLeafs[ i ].maxs[1] *= scale[1];
613                 bspLeafs[ i ].maxs[2] *= scale[2];
614         }
615
616         if ( texscale ) {
617                 Sys_Printf( "Using texture unlocking (and probably breaking texture alignment a lot)\n" );
618                 old_xyzst = safe_malloc( sizeof( *old_xyzst ) * numBSPDrawVerts * 5 );
619                 for ( i = 0; i < numBSPDrawVerts; i++ )
620                 {
621                         old_xyzst[5 * i + 0] = bspDrawVerts[i].xyz[0];
622                         old_xyzst[5 * i + 1] = bspDrawVerts[i].xyz[1];
623                         old_xyzst[5 * i + 2] = bspDrawVerts[i].xyz[2];
624                         old_xyzst[5 * i + 3] = bspDrawVerts[i].st[0];
625                         old_xyzst[5 * i + 4] = bspDrawVerts[i].st[1];
626                 }
627         }
628
629         /* scale drawverts */
630         for ( i = 0; i < numBSPDrawVerts; i++ )
631         {
632                 bspDrawVerts[i].xyz[0] *= scale[0];
633                 bspDrawVerts[i].xyz[1] *= scale[1];
634                 bspDrawVerts[i].xyz[2] *= scale[2];
635                 bspDrawVerts[i].normal[0] /= scale[0];
636                 bspDrawVerts[i].normal[1] /= scale[1];
637                 bspDrawVerts[i].normal[2] /= scale[2];
638                 VectorNormalize( bspDrawVerts[i].normal, bspDrawVerts[i].normal );
639         }
640
641         if ( texscale ) {
642                 for ( i = 0; i < numBSPDrawSurfaces; i++ )
643                 {
644                         switch ( bspDrawSurfaces[i].surfaceType )
645                         {
646                         case SURFACE_FACE:
647                         case SURFACE_META:
648                                 if ( bspDrawSurfaces[i].numIndexes % 3 ) {
649                                         Error( "Not a triangulation!" );
650                                 }
651                                 for ( j = bspDrawSurfaces[i].firstIndex; j < bspDrawSurfaces[i].firstIndex + bspDrawSurfaces[i].numIndexes; j += 3 )
652                                 {
653                                         int ia = bspDrawIndexes[j] + bspDrawSurfaces[i].firstVert, ib = bspDrawIndexes[j + 1] + bspDrawSurfaces[i].firstVert, ic = bspDrawIndexes[j + 2] + bspDrawSurfaces[i].firstVert;
654                                         bspDrawVert_t *a = &bspDrawVerts[ia], *b = &bspDrawVerts[ib], *c = &bspDrawVerts[ic];
655                                         float *oa = &old_xyzst[ia * 5], *ob = &old_xyzst[ib * 5], *oc = &old_xyzst[ic * 5];
656                                         // extrapolate:
657                                         //   a->xyz -> oa
658                                         //   b->xyz -> ob
659                                         //   c->xyz -> oc
660                                         ExtrapolateTexcoords(
661                                                 &oa[0], &oa[3],
662                                                 &ob[0], &ob[3],
663                                                 &oc[0], &oc[3],
664                                                 a->xyz, a->st,
665                                                 b->xyz, b->st,
666                                                 c->xyz, c->st );
667                                 }
668                                 break;
669                         }
670                 }
671         }
672
673         /* scale planes */
674         if ( uniform ) {
675                 for ( i = 0; i < numBSPPlanes; i++ )
676                 {
677                         bspPlanes[ i ].dist *= scale[0];
678                 }
679         }
680         else
681         {
682                 for ( i = 0; i < numBSPPlanes; i++ )
683                 {
684                         bspPlanes[ i ].normal[0] /= scale[0];
685                         bspPlanes[ i ].normal[1] /= scale[1];
686                         bspPlanes[ i ].normal[2] /= scale[2];
687                         f = 1 / VectorLength( bspPlanes[i].normal );
688                         VectorScale( bspPlanes[i].normal, f, bspPlanes[i].normal );
689                         bspPlanes[ i ].dist *= f;
690                 }
691         }
692
693         /* scale gridsize */
694         GetVectorForKey( &entities[ 0 ], "gridsize", vec );
695         if ( ( vec[ 0 ] + vec[ 1 ] + vec[ 2 ] ) == 0.0f ) {
696                 VectorCopy( gridSize, vec );
697         }
698         vec[0] *= scale[0];
699         vec[1] *= scale[1];
700         vec[2] *= scale[2];
701         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
702         SetKeyValue( &entities[ 0 ], "gridsize", str );
703
704         /* inject command line parameters */
705         InjectCommandLine( argv, 0, argc - 1 );
706
707         /* write the bsp */
708         UnparseEntities();
709         StripExtension( source );
710         DefaultExtension( source, "_s.bsp" );
711         Sys_Printf( "Writing %s\n", source );
712         WriteBSPFile( source );
713
714         /* return to sender */
715         return 0;
716 }
717
718
719 /*
720    PseudoCompileBSP()
721    a stripped down ProcessModels
722  */
723 void PseudoCompileBSP( qboolean need_tree ){
724         int models;
725         char modelValue[10];
726         entity_t *entity;
727         face_t *faces;
728         tree_t *tree;
729         node_t *node;
730         brush_t *brush;
731         side_t *side;
732         int i;
733
734         SetDrawSurfacesBuffer();
735         mapDrawSurfs = safe_malloc( sizeof( mapDrawSurface_t ) * MAX_MAP_DRAW_SURFS );
736         memset( mapDrawSurfs, 0, sizeof( mapDrawSurface_t ) * MAX_MAP_DRAW_SURFS );
737         numMapDrawSurfs = 0;
738
739         BeginBSPFile();
740         models = 1;
741         for ( mapEntityNum = 0; mapEntityNum < numEntities; mapEntityNum++ )
742         {
743                 /* get entity */
744                 entity = &entities[ mapEntityNum ];
745                 if ( entity->brushes == NULL && entity->patches == NULL ) {
746                         continue;
747                 }
748
749                 if ( mapEntityNum != 0 ) {
750                         sprintf( modelValue, "*%d", models++ );
751                         SetKeyValue( entity, "model", modelValue );
752                 }
753
754                 /* process the model */
755                 Sys_FPrintf( SYS_VRB, "############### model %i ###############\n", numBSPModels );
756                 BeginModel();
757
758                 entity->firstDrawSurf = numMapDrawSurfs;
759
760                 ClearMetaTriangles();
761                 PatchMapDrawSurfs( entity );
762
763                 if ( mapEntityNum == 0 && need_tree ) {
764                         faces = MakeStructuralBSPFaceList( entities[0].brushes );
765                         tree = FaceBSP( faces );
766                         node = tree->headnode;
767                 }
768                 else
769                 {
770                         node = AllocNode();
771                         node->planenum = PLANENUM_LEAF;
772                         tree = AllocTree();
773                         tree->headnode = node;
774                 }
775
776                 /* a minimized ClipSidesIntoTree */
777                 for ( brush = entity->brushes; brush; brush = brush->next )
778                 {
779                         /* walk the brush sides */
780                         for ( i = 0; i < brush->numsides; i++ )
781                         {
782                                 /* get side */
783                                 side = &brush->sides[ i ];
784                                 if ( side->winding == NULL ) {
785                                         continue;
786                                 }
787                                 /* shader? */
788                                 if ( side->shaderInfo == NULL ) {
789                                         continue;
790                                 }
791                                 /* save this winding as a visible surface */
792                                 DrawSurfaceForSide( entity, brush, side, side->winding );
793                         }
794                 }
795
796                 if ( meta ) {
797                         ClassifyEntitySurfaces( entity );
798                         MakeEntityDecals( entity );
799                         MakeEntityMetaTriangles( entity );
800                         SmoothMetaTriangles();
801                         MergeMetaTriangles();
802                 }
803                 FilterDrawsurfsIntoTree( entity, tree );
804
805                 FilterStructuralBrushesIntoTree( entity, tree );
806                 FilterDetailBrushesIntoTree( entity, tree );
807
808                 EmitBrushes( entity->brushes, &entity->firstBrush, &entity->numBrushes );
809                 EndModel( entity, node );
810         }
811         EndBSPFile( qfalse );
812 }
813
814 /*
815    ConvertBSPMain()
816    main argument processing function for bsp conversion
817  */
818
819 int ConvertBSPMain( int argc, char **argv ){
820         int i;
821         int ( *convertFunc )( char * );
822         game_t  *convertGame;
823         char ext[1024];
824         qboolean map_allowed, force_bsp, force_map;
825
826
827         /* set default */
828         convertFunc = ConvertBSPToASE;
829         convertGame = NULL;
830         map_allowed = qfalse;
831         force_bsp = qfalse;
832         force_map = qfalse;
833
834         /* arg checking */
835         if ( argc < 1 ) {
836                 Sys_Printf( "Usage: q3map -convert [-format <ase|obj|map_bp|map>] [-shadersasbitmap|-lightmapsastexcoord|-deluxemapsastexcoord] [-readbsp|-readmap [-meta|-patchmeta]] [-v] <mapname>\n" );
837                 return 0;
838         }
839
840         /* process arguments */
841         for ( i = 1; i < ( argc - 1 ); i++ )
842         {
843                 /* -format map|ase|... */
844                 if ( !strcmp( argv[ i ],  "-format" ) ) {
845                         i++;
846                         if ( !Q_stricmp( argv[ i ], "ase" ) ) {
847                                 convertFunc = ConvertBSPToASE;
848                                 map_allowed = qfalse;
849                         }
850                         else if ( !Q_stricmp( argv[ i ], "obj" ) ) {
851                                 convertFunc = ConvertBSPToOBJ;
852                                 map_allowed = qfalse;
853                         }
854                         else if ( !Q_stricmp( argv[ i ], "map_bp" ) ) {
855                                 convertFunc = ConvertBSPToMap_BP;
856                                 map_allowed = qtrue;
857                         }
858                         else if ( !Q_stricmp( argv[ i ], "map" ) ) {
859                                 convertFunc = ConvertBSPToMap;
860                                 map_allowed = qtrue;
861                         }
862                         else
863                         {
864                                 convertGame = GetGame( argv[ i ] );
865                                 map_allowed = qfalse;
866                                 if ( convertGame == NULL ) {
867                                         Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
868                                 }
869                         }
870                 }
871                 else if ( !strcmp( argv[ i ],  "-ne" ) ) {
872                         normalEpsilon = atof( argv[ i + 1 ] );
873                         i++;
874                         Sys_Printf( "Normal epsilon set to %f\n", normalEpsilon );
875                 }
876                 else if ( !strcmp( argv[ i ],  "-de" ) ) {
877                         distanceEpsilon = atof( argv[ i + 1 ] );
878                         i++;
879                         Sys_Printf( "Distance epsilon set to %f\n", distanceEpsilon );
880                 }
881                 else if ( !strcmp( argv[ i ],  "-shaderasbitmap" ) || !strcmp( argv[ i ],  "-shadersasbitmap" ) ) {
882                         shadersAsBitmap = qtrue;
883                 }
884                 else if ( !strcmp( argv[ i ],  "-lightmapastexcoord" ) || !strcmp( argv[ i ],  "-lightmapsastexcoord" ) ) {
885                         lightmapsAsTexcoord = qtrue;
886                 }
887                 else if ( !strcmp( argv[ i ],  "-deluxemapastexcoord" ) || !strcmp( argv[ i ],  "-deluxemapsastexcoord" ) ) {
888                         lightmapsAsTexcoord = qtrue;
889                         deluxemap = qtrue;
890                 }
891                 else if ( !strcmp( argv[ i ],  "-readbsp" ) ) {
892                         force_bsp = qtrue;
893                 }
894                 else if ( !strcmp( argv[ i ],  "-readmap" ) ) {
895                         force_map = qtrue;
896                 }
897                 else if ( !strcmp( argv[ i ],  "-meta" ) ) {
898                         meta = qtrue;
899                 }
900                 else if ( !strcmp( argv[ i ],  "-patchmeta" ) ) {
901                         meta = qtrue;
902                         patchMeta = qtrue;
903                 }
904         }
905
906         LoadShaderInfo();
907
908         /* clean up map name */
909         strcpy( source, ExpandArg( argv[i] ) );
910         ExtractFileExtension( source, ext );
911
912         if ( !map_allowed && !force_map ) {
913                 force_bsp = qtrue;
914         }
915
916         if ( force_map || ( !force_bsp && !Q_stricmp( ext, "map" ) && map_allowed ) ) {
917                 if ( !map_allowed ) {
918                         Sys_Printf( "WARNING: the requested conversion should not be done from .map files. Compile a .bsp first.\n" );
919                 }
920                 StripExtension( source );
921                 DefaultExtension( source, ".map" );
922                 Sys_Printf( "Loading %s\n", source );
923                 LoadMapFile( source, qfalse, convertGame == NULL );
924                 PseudoCompileBSP( convertGame != NULL );
925         }
926         else
927         {
928                 StripExtension( source );
929                 DefaultExtension( source, ".bsp" );
930                 Sys_Printf( "Loading %s\n", source );
931                 LoadBSPFile( source );
932                 ParseEntities();
933         }
934
935         /* bsp format convert? */
936         if ( convertGame != NULL ) {
937                 /* set global game */
938                 game = convertGame;
939
940                 /* write bsp */
941                 StripExtension( source );
942                 DefaultExtension( source, "_c.bsp" );
943                 Sys_Printf( "Writing %s\n", source );
944                 WriteBSPFile( source );
945
946                 /* return to sender */
947                 return 0;
948         }
949
950         /* normal convert */
951         return convertFunc( source );
952 }
953
954
955 struct HelpOption
956 {
957         const char* name;
958         const char* description;
959 };
960
961 void HelpOptions(const char* group_name, int indentation, int width, struct HelpOption* options, int count)
962 {
963         indentation *= 2;
964         char* indent = malloc(indentation+1);
965         memset(indent, ' ', indentation);
966         indent[indentation] = 0;
967         printf("%s%s:\n", indent, group_name);
968         indentation += 2;
969         indent = realloc(indent, indentation+1);
970         memset(indent, ' ', indentation);
971         indent[indentation] = 0;
972
973         int i;
974         for ( i = 0; i < count; i++ )
975         {
976                 int printed = printf("%s%-24s  ", indent, options[i].name);
977                 int descsz = strlen(options[i].description);
978                 int j = 0;
979                 while ( j < descsz && descsz-j > width - printed )
980                 {
981                         if ( j != 0 )
982                                 printf("%s%26c",indent,' ');
983                         int fragment = width - printed;
984                         while ( fragment > 0 && options[i].description[j+fragment-1] != ' ')
985                                         fragment--;
986                         j += fwrite(options[i].description+j, sizeof(char), fragment, stdout);
987                         putchar('\n');
988                         printed = indentation+26;
989                 }
990                 if ( j == 0 )
991                 {
992                         printf("%s\n",options[i].description+j);
993                 }
994                 else if ( j < descsz )
995                 {
996                         printf("%s%26c%s\n",indent,' ',options[i].description+j);
997                 }
998         }
999
1000         putchar('\n');
1001
1002         free(indent);
1003 }
1004
1005 void HelpBsp()
1006 {
1007         struct HelpOption bsp[] = {
1008                 {"-bsp <filename.map>", "Switch that enters this stage"},
1009                 {"-altsplit", "Alternate BSP tree splitting weights (should give more fps)"},
1010                 {"-celshader <shadername>", "Sets a global cel shader name"},
1011                 {"-custinfoparms", "Read scripts/custinfoparms.txt"},
1012                 {"-debuginset", "Push all triangle vertexes towards the triangle center"},
1013                 {"-debugportals", "Make BSP portals visible in the map"},
1014                 {"-debugsurfaces", "Color the vertexes according to the index of the surface"},
1015                 {"-deep", "Use detail brushes in the BSP tree, but at lowest priority (should give more fps)"},
1016                 {"-de <F>", "Distance epsilon for plane snapping etc."},
1017                 {"-fakemap", "Write fakemap.map containing all world brushes"},
1018                 {"-flares", "Turn on support for flares (TEST?)"},
1019                 {"-flat", "Enable flat shading (good for combining with -celshader)"},
1020                 {"-fulldetail", "Treat detail brushes as structural ones"},
1021                 {"-leaktest", "Abort if a leak was found"},
1022                 {"-meta", "Combine adjacent triangles of the same texture to surfaces (ALWAYS USE THIS)"},
1023                 {"-minsamplesize <N>", "Sets minimum lightmap resolution in luxels/qu"},
1024                 {"-mi <N>", "Sets the maximum number of indexes per surface"},
1025                 {"-mv <N>", "Sets the maximum number of vertices of a lightmapped surface"},
1026                 {"-ne <F>", "Normal epsilon for plane snapping etc."},
1027                 {"-nocurves", "Turn off support for patches"},
1028                 {"-nodetail", "Leave out detail brushes"},
1029                 {"-noflares", "Turn off support for flares"},
1030                 {"-nofog", "Turn off support for fog volumes"},
1031                 {"-nohint", "Turn off support for hint brushes"},
1032                 {"-nosubdivide", "Turn off support for `q3map_tessSize` (breaks water vertex deforms)"},
1033                 {"-notjunc", "Do not fix T-junctions (causes cracks between triangles, do not use)"},
1034                 {"-nowater", "Turn off support for water, slime or lava (Stef, this is for you)"},
1035                 {"-np <A>", "Force all surfaces to be nonplanar with a given shade angle"},
1036                 {"-onlyents", "Only update entities in the BSP"},
1037                 {"-patchmeta", "Turn patches into triangle meshes for display"},
1038                 {"-rename", "Append â€œbspâ€\9d suffix to miscmodel shaders (needed for SoF2)"},
1039                 {"-samplesize <N>", "Sets default lightmap resolution in luxels/qu"},
1040                 {"-skyfix", "Turn sky box into six surfaces to work around ATI problems"},
1041                 {"-snap <N>", "Snap brush bevel planes to the given number of units"},
1042                 {"-tempname <filename.map>", "Read the MAP file from the given file name"},
1043                 {"-texrange <N>", "Limit per-surface texture range to the given number of units, and subdivide surfaces like with `q3map_tessSize` if this is not met"},
1044                 {"-tmpout", "Write the BSP file to /tmp"},
1045                 {"-verboseentities", "Enable `-v` only for map entities, not for the world"},
1046         };
1047         HelpOptions("BSP Stage", 0, 80, bsp, sizeof(bsp)/sizeof(struct HelpOption));
1048 }
1049 void HelpVis()
1050 {
1051         struct HelpOption vis[] = {
1052                 {"-vis <filename.map>", "Switch that enters this stage"},
1053                 {"-fast", "Very fast and crude vis calculation"},
1054                 {"-mergeportals", "The less crude half of `-merge`, makes vis sometimes much faster but doesn't hurt fps usually"},
1055                 {"-merge", "Faster but still okay vis calculation"},
1056                 {"-nopassage", "Just use PortalFlow vis (usually less fps)"},
1057                 {"-nosort", "Do not sort the portals before calculating vis (usually slower)"},
1058                 {"-passageOnly", "Just use PassageFlow vis (usually less fps)"},
1059                 {"-saveprt", "Keep the PRT file after running vis (so you can run vis again)"},
1060                 {"-tmpin", "Use /tmp folder for input"},
1061                 {"-tmpout", "Use /tmp folder for output"},
1062         };
1063         HelpOptions("VIS Stage", 0, 80, vis, sizeof(vis)/sizeof(struct HelpOption));
1064 }
1065 void HelpLight()
1066 {
1067         struct HelpOption light[] = {
1068                 {"-light <filename.map>", "Switch that enters this stage"},
1069                 {"-vlight <filename.map>", "Deprecated alias for `-light -fast` ... filename.map"},
1070                 {"-approx <N>", "Vertex light approximation tolerance (never use in conjunction with deluxemapping)"},
1071                 {"-areascale <F, `-area` F>", "Scaling factor for area lights (surfacelight)"},
1072                 {"-border", "Add a red border to lightmaps for debugging"},
1073                 {"-bouncegrid", "Also compute radiosity on the light grid"},
1074                 {"-bounceonly", "Only compute radiosity"},
1075                 {"-bouncescale <F>", "Scaling factor for radiosity"},
1076                 {"-bounce <N>", "Number of bounces for radiosity"},
1077                 {"-cheapgrid", "Use `-cheap` style lighting for radiosity"},
1078                 {"-cheap", "Abort vertex light calculations when white is reached"},
1079                 {"-compensate <F>", "Lightmap compensate (darkening factor applied after everything else)"},
1080                 {"-cpma", "CPMA vertex lighting mode"},
1081                 {"-custinfoparms", "Read scripts/custinfoparms.txt"},
1082                 {"-dark", "Darken lightmap seams"},
1083                 {"-debugaxis", "Color the lightmaps according to the lightmap axis"},
1084                 {"-debugcluster", "Color the lightmaps according to the index of the cluster"},
1085                 {"-debugdeluxe", "Show deluxemaps on the lightmap"},
1086                 {"-debugnormals", "Color the lightmaps according to the direction of the surface normal"},
1087                 {"-debugorigin", "Color the lightmaps according to the origin of the luxels"},
1088                 {"-debugsurfaces, -debugsurface", "Color the lightmaps according to the index of the surface"},
1089                 {"-debugunused", "This option does nothing"},
1090                 {"-debug", "Mark the lightmaps according to the cluster: unmapped clusters get yellow, occluded ones get pink, flooded ones get blue overlay color, otherwise red"},
1091                 {"-deluxemode 0", "Use modelspace deluxemaps (DarkPlaces)"},
1092                 {"-deluxemode 1", "Use tangentspace deluxemaps"},
1093                 {"-deluxe, -deluxemap", "Enable deluxemapping (light direction maps)"},
1094                 {"-dirtdebug, -debugdirt", "Store the dirtmaps as lightmaps for debugging"},
1095                 {"-dirtdepth", "Dirtmapping depth"},
1096                 {"-dirtgain", "Dirtmapping exponent"},
1097                 {"-dirtmode 0", "Ordered direction dirtmapping"},
1098                 {"-dirtmode 1", "Randomized direction dirtmapping"},
1099                 {"-dirtscale", "Dirtmapping scaling factor"},
1100                 {"-dirty", "Enable dirtmapping"},
1101                 {"-dump", "Dump radiosity from `-bounce` into numbered MAP file prefabs"},
1102                 {"-export", "Export lightmaps when compile finished (like `-export` mode)"},
1103                 {"-exposure <F>", "Lightmap exposure to better support overbright spots"},
1104                 {"-external", "Force external lightmaps even if at size of internal lightmaps"},
1105                 {"-extravisnudge", "Broken feature to nudge the luxel origin to a better vis cluster"},
1106                 {"-extrawide", "Deprecated alias for `-super 2 -filter`"},
1107                 {"-extra", "Deprecated alias for `-super 2`"},
1108                 {"-fastbounce", "Use `-fast` style lighting for radiosity"},
1109                 {"-faster", "Use a faster falloff curve for lighting; also implies `-fast`"},
1110                 {"-fastgrid", "Use `-fast` style lighting for the light grid"},
1111                 {"-fast", "Ignore tiny light contributions"},
1112                 {"-filter", "Lightmap filtering"},
1113                 {"-floodlight", "Enable floodlight (zero-effort somewhat decent lighting)"},
1114                 {"-gamma <F>", "Lightmap gamma"},
1115                 {"-gridambientscale <F>", "Scaling factor for the light grid ambient components only"},
1116                 {"-gridscale <F>", "Scaling factor for the light grid only"},
1117                 {"-keeplights", "Keep light entities in the BSP file after compile"},
1118                 {"-lightmapdir <directory>", "Directory to store external lightmaps (default: same as map name without extension)"},
1119                 {"-lightmapsize <N>", "Size of lightmaps to generate (must be a power of two)"},
1120                 {"-lomem", "Low memory but slower lighting mode"},
1121                 {"-lowquality", "Low quality floodlight (appears to currently break floodlight)"},
1122                 {"-minsamplesize <N>", "Sets minimum lightmap resolution in luxels/qu"},
1123                 {"-nocollapse", "Do not collapse identical lightmaps"},
1124                 {"-nodeluxe, -nodeluxemap", "Disable deluxemapping"},
1125                 {"-nogrid", "Disable grid light calculation (makes all entities fullbright)"},
1126                 {"-nolightmapsearch", "Do not optimize lightmap packing for GPU memory usage (as doing so costs fps)"},
1127                 {"-normalmap", "Color the lightmaps according to the direction of the surface normal (TODO is this identical to `-debugnormals`?)"},
1128                 {"-nostyle, -nostyles", "Disable support for light styles"},
1129                 {"-nosurf", "Disable tracing against surfaces (only uses BSP nodes then)"},
1130                 {"-notrace", "Disable shadow occlusion"},
1131                 {"-novertex", "Disable vertex lighting"},
1132                 {"-patchshadows", "Cast shadows from patches"},
1133                 {"-pointscale <F, `-point` F>", "Scaling factor for point lights (light entities)"},
1134                 {"-q3", "Use nonlinear falloff curve by default (like Q3A)"},
1135                 {"-samplescale <F>", "Scales all lightmap resolutions"},
1136                 {"-samplesize <N>", "Sets default lightmap resolution in luxels/qu"},
1137                 {"-samples <N>", "Adaptive supersampling quality"},
1138                 {"-scale <F>", "Scaling factor for all light types"},
1139                 {"-shadeangle <A>", "Angle for phong shading"},
1140                 {"-shade", "Enable phong shading at default shade angle"},
1141                 {"-skyscale <F, `-sky` F>", "Scaling factor for sky and sun light"},
1142                 {"-smooth", "Deprecated alias for `-samples 2`"},
1143                 {"-style, -styles", "Enable support for light styles"},
1144                 {"-sunonly", "Only compute sun light"},
1145                 {"-super <N, `-supersample` N>", "Ordered grid supersampling quality"},
1146                 {"-thresh <F>", "Triangle subdivision threshold"},
1147                 {"-trianglecheck", "Broken check that should ensure luxels apply to the right triangle"},
1148                 {"-trisoup", "Convert brush faces to triangle soup"},
1149                 {"-wolf", "Use linear falloff curve by default (like W:ET)"},
1150         };
1151
1152         HelpOptions("Light Stage", 0, 80, light, sizeof(light)/sizeof(struct HelpOption));
1153 }
1154
1155 void HelpAnalize()
1156 {
1157         struct HelpOption analize[] = {
1158                 {"-analyze <filename.bsp>", "Switch that enters this mode"},
1159                 {"-lumpswap", "Swap byte order in the lumps"},
1160         };
1161
1162         HelpOptions("Analyzing BSP-like file structure", 0, 80, analize, sizeof(analize)/sizeof(struct HelpOption));
1163 }
1164 void HelpScale()
1165 {
1166         struct HelpOption scale[] = {
1167                 {"-scale <S filename.bsp>", "Scale uniformly"},
1168                 {"-scale <SX SY SZ filename.bsp>", "Scale non-uniformly"},
1169                 {"-scale -tex <S filename.bsp>", "Scale uniformly without texture lock"},
1170                 {"-scale -tex <SX SY SZ filename.bsp>", "Scale non-uniformly without texture lock"},
1171         };
1172         HelpOptions("Scaling", 0, 80, scale, sizeof(scale)/sizeof(struct HelpOption));
1173 }
1174 void HelpConvert()
1175 {
1176         struct HelpOption convert[] = {
1177                 {"-convert <filename.bsp>", "Switch that enters this mode"},
1178                 {"-de <number>", "Distance epsilon for the conversion"},
1179                 {"-format <converter>", "Select the converter (available: map, ase, or game names)"},
1180                 {"-ne <F>", "Normal epsilon for the conversion"},
1181                 {"-shadersasbitmap", "(only for ase) use the shader names as \\*BITMAP key so they work as prefabs"},
1182         };
1183
1184         HelpOptions("Converting & Decompiling", 0, 80, convert, sizeof(convert)/sizeof(struct HelpOption));
1185 }
1186
1187 void HelpExport()
1188 {
1189         struct HelpOption exportl[] = {
1190                 {"-export <filename.bsp>", "Copies lightmaps from the BSP to `filename/lightmap_0000.tga` ff"}
1191         };
1192
1193         HelpOptions("Exporting lightmaps", 0, 80, exportl, sizeof(exportl)/sizeof(struct HelpOption));
1194 }
1195
1196 void HelpFixaas()
1197 {
1198         struct HelpOption fixaas[] = {
1199                 {"-fixaas <filename.bsp>", "Switch that enters this mode"},
1200         };
1201
1202         HelpOptions("Fixing AAS checksum", 0, 80, fixaas, sizeof(fixaas)/sizeof(struct HelpOption));
1203 }
1204
1205 void HelpInfo()
1206 {
1207         struct HelpOption info[] = {
1208                 {"-info <filename.bsp>", "Switch that enters this mode"},
1209         };
1210
1211         HelpOptions("Get info about BSP file", 0, 80, info, sizeof(info)/sizeof(struct HelpOption));
1212 }
1213
1214 void HelpImport()
1215 {
1216         struct HelpOption import[] = {
1217                 {"-import <filename.bsp>", "Copies lightmaps from `filename/lightmap_0000.tga` ff into the BSP"},
1218         };
1219
1220         HelpOptions("Importing lightmaps", 0, 80, import, sizeof(import)/sizeof(struct HelpOption));
1221 }
1222
1223 void HelpMinimap()
1224 {
1225         struct HelpOption minimap[] = {
1226                 {"-minimap <filename.bsp>", "Creates a minimap of the BSP, by default writes to `../gfx/filename_mini.tga`"},
1227                 {"-black", "Write the minimap as a black-on-transparency RGBA32 image"},
1228                 {"-boost <F>", "Sets the contrast boost value (higher values make a brighter image); contrast boost is somewhat similar to gamma, but continuous even at zero"},
1229                 {"-border <F>", "Sets the amount of border pixels relative to the total image size"},
1230                 {"-gray", "Write the minimap as a white-on-black GRAY8 image"},
1231                 {"-keepaspect", "Ensure the aspect ratio is kept (the minimap is then letterboxed to keep aspect)"},
1232                 {"-minmax <xmin ymin zmin xmax ymax zmax>", "Forces specific map dimensions (note: the minimap actually uses these dimensions, scaled to the target size while keeping aspect with centering, and 1/64 of border appended to all sides)"},
1233                 {"-nokeepaspect", "Do not ensure the aspect ratio is kept (makes it easier to use the image in your code, but looks bad together with sharpening)"},
1234                 {"-o <filename.tga>", "Sets the output file name"},
1235                 {"-random <N>", "Sets the randomized supersampling count (cannot be combined with `-samples`)"},
1236                 {"-samples <N>", "Sets the ordered supersampling count (cannot be combined with `-random`)"},
1237                 {"-sharpen <F>", "Sets the sharpening coefficient"},
1238                 {"-size <N>", "Sets the width and height of the output image"},
1239                 {"-white", "Write the minimap as a white-on-transparency RGBA32 image"},
1240         };
1241
1242         HelpOptions("MiniMap", 0, 80, minimap, sizeof(minimap)/sizeof(struct HelpOption));
1243 }
1244
1245 void HelpCommon()
1246 {
1247         struct HelpOption common[] = {
1248                 {"-connect <address>", "Talk to a NetRadiant instance using a specific XML based protocol"},
1249                 {"-force", "Allow reading some broken/unsupported BSP files e.g. when decompiling, may also crash"},
1250                 {"-fs_basepath <path>", "Sets the given path as main directory of the game (can be used more than once to look in multiple paths)"},
1251                 {"-fs_game <gamename>", "Sets a different game directory name (default for Q3A: baseq3)"},
1252                 {"-fs_homebase <dir>", "Specifies where the user home directory name is on Linux (default for Q3A: .q3a)"},
1253                 {"-game <gamename>", "Load settings for the given game (default: quake3)"},
1254                 {"-subdivisions <F>", "multiplier for patch subdivisions quality"},
1255                 {"-threads <N>", "number of threads to use"},
1256                 {"-v", "Verbose mode"}
1257         };
1258
1259         HelpOptions("Common Options", 0, 80, common, sizeof(common)/sizeof(struct HelpOption));
1260
1261 }
1262
1263 void Help(const char* arg)
1264 {
1265         printf("Usage: q3map2 [stage] [common options...] [stage options...] [stage source file]\n");
1266         printf("       q3map2 -help [stage]\n\n");
1267
1268         HelpCommon();
1269
1270         struct HelpOption stages[] = {
1271                 {"-bsp", "BSP Stage"},
1272                 {"-vis", "VIS Stage"},
1273                 {"-light", "Light Stage"},
1274                 {"-analize", "Analyzing BSP-like file structure"},
1275                 {"-scale", "Scaling"},
1276                 {"-convert", "Converting & Decompiling"},
1277                 {"-export", "Exporting lightmaps"},
1278                 {"-fixaas", "Fixing AAS checksum"},
1279                 {"-info", "Get info about BSP file"},
1280                 {"-import", "Importing lightmaps"},
1281                 {"-minimap", "MiniMap"},
1282         };
1283         void(*help_funcs[])() = {
1284                 HelpBsp,
1285                 HelpVis,
1286                 HelpLight,
1287                 HelpAnalize,
1288                 HelpScale,
1289                 HelpConvert,
1290                 HelpExport,
1291                 HelpFixaas,
1292                 HelpInfo,
1293                 HelpImport,
1294                 HelpMinimap
1295         };
1296
1297         if ( arg && strlen(arg) > 0 )
1298         {
1299                 if ( arg[0] == '-' )
1300                         arg++;
1301
1302                 unsigned i;
1303                 for ( i = 0; i < sizeof(stages)/sizeof(struct HelpOption); i++ )
1304                         if ( strcmp(arg, stages[i].name+1) == 0 )
1305                         {
1306                                 help_funcs[i]();
1307                                 return;
1308                         }
1309         }
1310
1311         HelpOptions("Stages", 0, 80, stages, sizeof(stages)/sizeof(struct HelpOption));
1312 }
1313
1314 /*
1315    main()
1316    q3map mojo...
1317  */
1318
1319 int main( int argc, char **argv ){
1320         int i, r;
1321         double start, end;
1322
1323
1324         /* we want consistent 'randomness' */
1325         srand( 0 );
1326
1327         /* start timer */
1328         start = I_FloatTime();
1329
1330         /* this was changed to emit version number over the network */
1331         printf( Q3MAP_VERSION "\n" );
1332
1333         /* set exit call */
1334         atexit( ExitQ3Map );
1335
1336         /* read general options first */
1337         for ( i = 1; i < argc; i++ )
1338         {
1339                 /* -help */
1340                 if ( !strcmp( argv[ i ], "-h" ) || !strcmp( argv[ i ], "--help" )
1341                         || !strcmp( argv[ i ], "-help" ) ) {
1342                         Help(argv[i+1]);
1343                         return 0;
1344                 }
1345
1346                 /* -connect */
1347                 if ( !strcmp( argv[ i ], "-connect" ) ) {
1348                         argv[ i ] = NULL;
1349                         i++;
1350                         Broadcast_Setup( argv[ i ] );
1351                         argv[ i ] = NULL;
1352                 }
1353
1354                 /* verbose */
1355                 else if ( !strcmp( argv[ i ], "-v" ) ) {
1356                         if ( !verbose ) {
1357                                 verbose = qtrue;
1358                                 argv[ i ] = NULL;
1359                         }
1360                 }
1361
1362                 /* force */
1363                 else if ( !strcmp( argv[ i ], "-force" ) ) {
1364                         force = qtrue;
1365                         argv[ i ] = NULL;
1366                 }
1367
1368                 /* patch subdivisions */
1369                 else if ( !strcmp( argv[ i ], "-subdivisions" ) ) {
1370                         argv[ i ] = NULL;
1371                         i++;
1372                         patchSubdivisions = atoi( argv[ i ] );
1373                         argv[ i ] = NULL;
1374                         if ( patchSubdivisions <= 0 ) {
1375                                 patchSubdivisions = 1;
1376                         }
1377                 }
1378
1379                 /* threads */
1380                 else if ( !strcmp( argv[ i ], "-threads" ) ) {
1381                         argv[ i ] = NULL;
1382                         i++;
1383                         numthreads = atoi( argv[ i ] );
1384                         argv[ i ] = NULL;
1385                 }
1386         }
1387
1388         /* init model library */
1389         PicoInit();
1390         PicoSetMallocFunc( safe_malloc );
1391         PicoSetFreeFunc( free );
1392         PicoSetPrintFunc( PicoPrintFunc );
1393         PicoSetLoadFileFunc( PicoLoadFileFunc );
1394         PicoSetFreeFileFunc( free );
1395
1396         /* set number of threads */
1397         ThreadSetDefault();
1398
1399         /* generate sinusoid jitter table */
1400         for ( i = 0; i < MAX_JITTERS; i++ )
1401         {
1402                 jitters[ i ] = sin( i * 139.54152147 );
1403                 //%     Sys_Printf( "Jitter %4d: %f\n", i, jitters[ i ] );
1404         }
1405
1406         /* we print out two versions, q3map's main version (since it evolves a bit out of GtkRadiant)
1407            and we put the GtkRadiant version to make it easy to track with what version of Radiant it was built with */
1408
1409         Sys_Printf( "Q3Map         - v1.0r (c) 1999 Id Software Inc.\n" );
1410         Sys_Printf( "Q3Map (ydnar) - v" Q3MAP_VERSION "\n" );
1411         Sys_Printf( "NetRadiant    - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
1412         Sys_Printf( "%s\n", Q3MAP_MOTD );
1413
1414         /* ydnar: new path initialization */
1415         InitPaths( &argc, argv );
1416
1417         /* set game options */
1418         if ( !patchSubdivisions ) {
1419                 patchSubdivisions = game->patchSubdivisions;
1420         }
1421
1422         /* check if we have enough options left to attempt something */
1423         if ( argc < 2 ) {
1424                 Error( "Usage: %s [general options] [options] mapfile", argv[ 0 ] );
1425         }
1426
1427         /* fixaas */
1428         if ( !strcmp( argv[ 1 ], "-fixaas" ) ) {
1429                 r = FixAAS( argc - 1, argv + 1 );
1430         }
1431
1432         /* analyze */
1433         else if ( !strcmp( argv[ 1 ], "-analyze" ) ) {
1434                 r = AnalyzeBSP( argc - 1, argv + 1 );
1435         }
1436
1437         /* info */
1438         else if ( !strcmp( argv[ 1 ], "-info" ) ) {
1439                 r = BSPInfo( argc - 2, argv + 2 );
1440         }
1441
1442         /* vis */
1443         else if ( !strcmp( argv[ 1 ], "-vis" ) ) {
1444                 r = VisMain( argc - 1, argv + 1 );
1445         }
1446
1447         /* light */
1448         else if ( !strcmp( argv[ 1 ], "-light" ) ) {
1449                 r = LightMain( argc - 1, argv + 1 );
1450         }
1451
1452         /* vlight */
1453         else if ( !strcmp( argv[ 1 ], "-vlight" ) ) {
1454                 Sys_Printf( "WARNING: VLight is no longer supported, defaulting to -light -fast instead\n\n" );
1455                 argv[ 1 ] = "-fast";    /* eek a hack */
1456                 r = LightMain( argc, argv );
1457         }
1458
1459         /* ydnar: lightmap export */
1460         else if ( !strcmp( argv[ 1 ], "-export" ) ) {
1461                 r = ExportLightmapsMain( argc - 1, argv + 1 );
1462         }
1463
1464         /* ydnar: lightmap import */
1465         else if ( !strcmp( argv[ 1 ], "-import" ) ) {
1466                 r = ImportLightmapsMain( argc - 1, argv + 1 );
1467         }
1468
1469         /* ydnar: bsp scaling */
1470         else if ( !strcmp( argv[ 1 ], "-scale" ) ) {
1471                 r = ScaleBSPMain( argc - 1, argv + 1 );
1472         }
1473
1474         /* ydnar: bsp conversion */
1475         else if ( !strcmp( argv[ 1 ], "-convert" ) ) {
1476                 r = ConvertBSPMain( argc - 1, argv + 1 );
1477         }
1478
1479         /* div0: minimap */
1480         else if ( !strcmp( argv[ 1 ], "-minimap" ) ) {
1481                 r = MiniMapBSPMain( argc - 1, argv + 1 );
1482         }
1483
1484         /* ydnar: otherwise create a bsp */
1485         else{
1486                 r = BSPMain( argc, argv );
1487         }
1488
1489         /* emit time */
1490         end = I_FloatTime();
1491         Sys_Printf( "%9.0f seconds elapsed\n", end - start );
1492
1493         /* shut down connection */
1494         Broadcast_Shutdown();
1495
1496         /* return any error code */
1497         return r;
1498 }