]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/main.c
make minimap parameters game dependent
[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 {
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 /* minimap stuff */
68
69 /* borrowed from light.c */
70 void WriteTGA24( char *filename, byte *data, int width, int height, qboolean flip );
71 typedef struct minimap_s
72 {
73         bspModel_t *model;
74         int width;
75         int height;
76         int samples;
77         float *sample_offsets;
78         float sharpen_boxmult;
79         float sharpen_centermult;
80         float *data1f;
81         float *sharpendata1f;
82         vec3_t mins, size;
83 }
84 minimap_t;
85 static minimap_t minimap;
86
87 qboolean BrushIntersectionWithLine(bspBrush_t *brush, vec3_t start, vec3_t dir, float *t_in, float *t_out)
88 {
89         int i;
90         qboolean in = qfalse, out = qfalse;
91         bspBrushSide_t *sides = &bspBrushSides[brush->firstSide];
92
93         for(i = 0; i < brush->numSides; ++i)
94         {
95                 bspPlane_t *p = &bspPlanes[sides[i].planeNum];
96                 float sn = DotProduct(start, p->normal);
97                 float dn = DotProduct(dir, p->normal);
98                 if(dn == 0)
99                 {
100                         if(sn > p->dist)
101                                 return qfalse; // outside!
102                 }
103                 else
104                 {
105                         float t = (p->dist - sn) / dn;
106                         if(dn < 0)
107                         {
108                                 if(!in || t > *t_in)
109                                 {
110                                         *t_in = t;
111                                         in = qtrue;
112                                         // as t_in can only increase, and t_out can only decrease, early out
113                                         if(out && *t_in >= *t_out)
114                                                 return qfalse;
115                                 }
116                         }
117                         else
118                         {
119                                 if(!out || t < *t_out)
120                                 {
121                                         *t_out = t;
122                                         out = qtrue;
123                                         // as t_in can only increase, and t_out can only decrease, early out
124                                         if(in && *t_in >= *t_out)
125                                                 return qfalse;
126                                 }
127                         }
128                 }
129         }
130         return in && out;
131 }
132
133 static float MiniMapSample(float x, float y)
134 {
135         vec3_t org, dir;
136         int i, bi;
137         float t0, t1;
138         float samp;
139         bspBrush_t *b;
140         bspBrushSide_t *s;
141         int cnt;
142
143         org[0] = x;
144         org[1] = y;
145         org[2] = 0;
146         dir[0] = 0;
147         dir[1] = 0;
148         dir[2] = 1;
149
150         cnt = 0;
151         samp = 0;
152         for(i = 0; i < minimap.model->numBSPBrushes; ++i)
153         {
154                 bi = minimap.model->firstBSPBrush + i;
155                 if(opaqueBrushes[bi >> 3] & (1 << (bi & 7)))
156                 {
157                         b = &bspBrushes[bi];
158
159                         // sort out mins/maxs of the brush
160                         s = &bspBrushSides[b->firstSide];
161                         if(x < -bspPlanes[s[0].planeNum].dist)
162                                 continue;
163                         if(x > +bspPlanes[s[1].planeNum].dist)
164                                 continue;
165                         if(y < -bspPlanes[s[2].planeNum].dist)
166                                 continue;
167                         if(y > +bspPlanes[s[3].planeNum].dist)
168                                 continue;
169
170                         if(BrushIntersectionWithLine(b, org, dir, &t0, &t1))
171                         {
172                                 samp += t1 - t0;
173                                 ++cnt;
174                         }
175                 }
176         }
177
178         return samp;
179 }
180
181 void RandomVector2f(float v[2])
182 {
183         do
184         {
185                 v[0] = 2 * Random() - 1;
186                 v[1] = 2 * Random() - 1;
187         }
188         while(v[0] * v[0] + v[1] * v[1] > 1);
189 }
190
191 static void MiniMapRandomlySupersampled(int y)
192 {
193         int x, i;
194         float *p = &minimap.data1f[y * minimap.width];
195         float ymin = minimap.mins[1] + minimap.size[1] * (y / (float) minimap.height);
196         float dx   =                   minimap.size[0]      / (float) minimap.width;
197         float dy   =                   minimap.size[1]      / (float) minimap.height;
198         float uv[2];
199         float thisval;
200
201         for(x = 0; x < minimap.width; ++x)
202         {
203                 float xmin = minimap.mins[0] + minimap.size[0] * (x / (float) minimap.width);
204                 float val = 0;
205
206                 for(i = 0; i < minimap.samples; ++i)
207                 {
208                         RandomVector2f(uv);
209                         thisval = MiniMapSample(
210                                 xmin + (uv[0] + 0.5) * dx, /* exaggerated random pattern for better results */
211                                 ymin + (uv[1] + 0.5) * dy  /* exaggerated random pattern for better results */
212                         );
213                         val += thisval;
214                 }
215                 val /= minimap.samples * minimap.size[2];
216                 *p++ = val;
217         }
218 }
219
220 static void MiniMapSupersampled(int y)
221 {
222         int x, i;
223         float *p = &minimap.data1f[y * minimap.width];
224         float ymin = minimap.mins[1] + minimap.size[1] * (y / (float) minimap.height);
225         float dx   =                   minimap.size[0]      / (float) minimap.width;
226         float dy   =                   minimap.size[1]      / (float) minimap.height;
227
228         for(x = 0; x < minimap.width; ++x)
229         {
230                 float xmin = minimap.mins[0] + minimap.size[0] * (x / (float) minimap.width);
231                 float val = 0;
232
233                 for(i = 0; i < minimap.samples; ++i)
234                 {
235                         float thisval = MiniMapSample(
236                                 xmin + minimap.sample_offsets[2*i+0] * dx,
237                                 ymin + minimap.sample_offsets[2*i+1] * dy
238                         );
239                         val += thisval;
240                 }
241                 val /= minimap.samples * minimap.size[2];
242                 *p++ = val;
243         }
244 }
245
246 static void MiniMapNoSupersampling(int y)
247 {
248         int x;
249         float *p = &minimap.data1f[y * minimap.width];
250         float ymin = minimap.mins[1] + minimap.size[1] * ((y + 0.5) / (float) minimap.height);
251
252         for(x = 0; x < minimap.width; ++x)
253         {
254                 float xmin = minimap.mins[0] + minimap.size[0] * ((x + 0.5) / (float) minimap.width);
255                 *p++ = MiniMapSample(xmin, ymin) / minimap.size[2];
256         }
257 }
258
259 static void MiniMapSharpen(int y)
260 {
261         int x;
262         qboolean up = (y > 0);
263         qboolean down = (y < minimap.height - 1);
264         float *p = &minimap.data1f[y * minimap.width];
265         float *q = &minimap.sharpendata1f[y * minimap.width];
266
267         for(x = 0; x < minimap.width; ++x)
268         {
269                 qboolean left = (x > 0);
270                 qboolean right = (x < minimap.width - 1);
271                 float val = p[0] * minimap.sharpen_centermult;
272
273                 if(left && up)
274                         val += p[-1 -minimap.width] * minimap.sharpen_boxmult;
275                 if(left && down)
276                         val += p[-1 +minimap.width] * minimap.sharpen_boxmult;
277                 if(right && up)
278                         val += p[+1 -minimap.width] * minimap.sharpen_boxmult;
279                 if(right && down)
280                         val += p[+1 +minimap.width] * minimap.sharpen_boxmult;
281                         
282                 if(left)
283                         val += p[-1] * minimap.sharpen_boxmult;
284                 if(right)
285                         val += p[+1] * minimap.sharpen_boxmult;
286                 if(up)
287                         val += p[-minimap.width] * minimap.sharpen_boxmult;
288                 if(down)
289                         val += p[+minimap.width] * minimap.sharpen_boxmult;
290
291                 ++p;
292                 *q++ = val;
293         }
294 }
295
296 void MiniMapMakeMinsMaxs(vec3_t mins_in, vec3_t maxs_in, float border)
297 {
298         vec3_t mins, maxs, extend;
299         VectorCopy(mins_in, mins);
300         VectorCopy(maxs_in, maxs);
301
302         // line compatible to nexuiz mapinfo
303         Sys_Printf("size %f %f %f %f %f %f\n", mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);
304
305         VectorSubtract(maxs, mins, extend);
306
307         if(extend[1] > extend[0])
308         {
309                 mins[0] -= (extend[1] - extend[0]) * 0.5;
310                 maxs[0] += (extend[1] - extend[0]) * 0.5;
311         }
312         else
313         {
314                 mins[1] -= (extend[0] - extend[1]) * 0.5;
315                 maxs[1] += (extend[0] - extend[1]) * 0.5;
316         }
317
318         /* border: amount of black area around the image */
319         /* input: border, 1-2*border, border but we need border/(1-2*border) */
320
321         VectorSubtract(maxs, mins, extend);
322         VectorScale(extend, border / (1 - 2 * border), extend);
323
324         VectorSubtract(mins, extend, mins);
325         VectorAdd(maxs, extend, maxs);
326
327         VectorCopy(mins, minimap.mins);
328         VectorSubtract(maxs, mins, minimap.size);
329
330         // line compatible to nexuiz mapinfo
331         Sys_Printf("size_texcoords %f %f %f %f %f %f\n", mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);
332 }
333
334 /*
335 MiniMapSetupBrushes()
336 determines solid non-sky brushes in the world
337 */
338
339 void MiniMapSetupBrushes( void )
340 {
341         int                             i, b, compileFlags;
342         bspBrush_t              *brush;
343         bspShader_t             *shader;
344         shaderInfo_t    *si;
345         
346         
347         /* note it */
348         Sys_FPrintf( SYS_VRB, "--- MiniMapSetupBrushes ---\n" );
349         
350         /* allocate */
351         if( opaqueBrushes == NULL )
352                 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
353         
354         /* clear */
355         memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
356         numOpaqueBrushes = 0;
357         
358         /* walk the list of worldspawn brushes */
359         for( i = 0; i < minimap.model->numBSPBrushes; i++ )
360         {
361                 /* get brush */
362                 b = minimap.model->firstBSPBrush + i;
363                 brush = &bspBrushes[ b ];
364                 
365 #if 0
366                 /* check all sides */
367                 compileFlags = 0;
368                 for( j = 0; j < brush->numSides; j++ )
369                 {
370                         /* do bsp shader calculations */
371                         side = &bspBrushSides[ brush->firstSide + j ];
372                         shader = &bspShaders[ side->shaderNum ];
373                         
374                         /* get shader info */
375                         si = ShaderInfoForShader( shader->shader );
376                         if( si == NULL )
377                                 continue;
378                         
379                         /* or together compile flags */
380                         compileFlags |= si->compileFlags;
381                 }
382 #else
383                 shader = &bspShaders[ brush->shaderNum ];
384                 si = ShaderInfoForShader( shader->shader );
385                 if( si == NULL )
386                         compileFlags = 0;
387                 else
388                         compileFlags = si->compileFlags;
389 #endif
390                 
391                 /* determine if this brush is solid */
392                 if( (compileFlags & (C_SOLID | C_SKY)) == C_SOLID )
393                 {
394                         opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
395                         numOpaqueBrushes++;
396                         maxOpaqueBrush = i;
397                 }
398         }
399         
400         /* emit some statistics */
401         Sys_FPrintf( SYS_VRB, "%9d solid brushes\n", numOpaqueBrushes );
402 }
403
404 qboolean MiniMapEvaluateSampleOffsets(int *bestj, int *bestk, float *bestval)
405 {
406         float val, dx, dy;
407         int j, k;
408
409         *bestj = *bestk = -1;
410         *bestval = 3; /* max possible val is 2 */
411
412         for(j = 0; j < minimap.samples; ++j)
413                 for(k = j + 1; k < minimap.samples; ++k)
414                 {
415                         dx = minimap.sample_offsets[2*j+0] - minimap.sample_offsets[2*k+0];
416                         dy = minimap.sample_offsets[2*j+1] - minimap.sample_offsets[2*k+1];
417                         if(dx > +0.5) dx -= 1;
418                         if(dx < -0.5) dx += 1;
419                         if(dy > +0.5) dy -= 1;
420                         if(dy < -0.5) dy += 1;
421                         val = dx * dx + dy * dy;
422                         if(val < *bestval)
423                         {
424                                 *bestj = j;
425                                 *bestk = k;
426                                 *bestval = val;
427                         }
428                 }
429         
430         return *bestval < 3;
431 }
432
433 void MiniMapMakeSampleOffsets()
434 {
435         int i, j, k, jj, kk;
436         float val, valj, valk, sx, sy, rx, ry;
437
438         Sys_Printf( "Generating good sample offsets (this may take a while)...\n" );
439
440         /* start with entirely random samples */
441         for(i = 0; i < minimap.samples; ++i)
442         {
443                 minimap.sample_offsets[2*i+0] = Random();
444                 minimap.sample_offsets[2*i+1] = Random();
445         }
446
447         for(i = 0; i < 1000; ++i)
448         {
449                 if(MiniMapEvaluateSampleOffsets(&j, &k, &val))
450                 {
451                         sx = minimap.sample_offsets[2*j+0];
452                         sy = minimap.sample_offsets[2*j+1];
453                         minimap.sample_offsets[2*j+0] = rx = Random();
454                         minimap.sample_offsets[2*j+1] = ry = Random();
455                         if(!MiniMapEvaluateSampleOffsets(&jj, &kk, &valj))
456                                 valj = -1;
457                         minimap.sample_offsets[2*j+0] = sx;
458                         minimap.sample_offsets[2*j+1] = sy;
459
460                         sx = minimap.sample_offsets[2*k+0];
461                         sy = minimap.sample_offsets[2*k+1];
462                         minimap.sample_offsets[2*k+0] = rx;
463                         minimap.sample_offsets[2*k+1] = ry;
464                         if(!MiniMapEvaluateSampleOffsets(&jj, &kk, &valk))
465                                 valk = -1;
466                         minimap.sample_offsets[2*k+0] = sx;
467                         minimap.sample_offsets[2*k+1] = sy;
468
469                         if(valj > valk)
470                         {
471                                 if(valj > val)
472                                 {
473                                         /* valj is the greatest */
474                                         minimap.sample_offsets[2*j+0] = rx;
475                                         minimap.sample_offsets[2*j+1] = ry;
476                                         i = -1;
477                                 }
478                                 else
479                                 {
480                                         /* valj is the greater and it is useless - forget it */
481                                 }
482                         }
483                         else
484                         {
485                                 if(valk > val)
486                                 {
487                                         /* valk is the greatest */
488                                         minimap.sample_offsets[2*k+0] = rx;
489                                         minimap.sample_offsets[2*k+1] = ry;
490                                         i = -1;
491                                 }
492                                 else
493                                 {
494                                         /* valk is the greater and it is useless - forget it */
495                                 }
496                         }
497                 }
498                 else
499                         break;
500         }
501 }
502
503 void MergeRelativePath(char *out, const char *absolute, const char *relative)
504 {
505         const char *endpos = absolute + strlen(absolute);
506         while(endpos != absolute && (endpos[-1] == '/' || endpos[-1] == '\\'))
507                 --endpos;
508         while(relative[0] == '.' && relative[1] == '.' && (relative[2] == '/' || relative[2] == '\\'))
509         {
510                 relative += 3;
511                 while(endpos != absolute)
512                 {
513                         --endpos;
514                         if(*endpos == '/' || *endpos == '\\')
515                                 break;
516                 }
517                 while(endpos != absolute && (endpos[-1] == '/' || endpos[-1] == '\\'))
518                         --endpos;
519         }
520         memcpy(out, absolute, endpos - absolute);
521         out[endpos - absolute] = '/';
522         strcpy(out + (endpos - absolute + 1), relative);
523 }
524
525 int MiniMapBSPMain( int argc, char **argv )
526 {
527         char minimapFilename[1024];
528         char basename[1024];
529         char path[1024];
530         char relativeMinimapFilename[1024];
531         float minimapSharpen;
532         float border;
533         byte *data3b, *p;
534         float *q;
535         int x, y;
536         int i;
537         vec3_t mins, maxs;
538
539         /* arg checking */
540         if( argc < 2 )
541         {
542                 Sys_Printf( "Usage: q3map [-v] -minimap [-size n] [-sharpen f] [-samples n | -random n] [-o filename.tga] [-minmax Xmin Ymin Zmin Xmax Ymax Zmax] <mapname>\n" );
543                 return 0;
544         }
545
546         /* load the BSP first */
547         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
548         StripExtension( source );
549         DefaultExtension( source, ".bsp" );
550         Sys_Printf( "Loading %s\n", source );
551         BeginMapShaderFile( source );
552         LoadShaderInfo();
553         LoadBSPFile( source );
554
555         minimap.model = &bspModels[0];
556         VectorCopy(minimap.model->mins, mins);
557         VectorCopy(minimap.model->maxs, maxs);
558
559         *minimapFilename = 0;
560         minimapSharpen = game->miniMapSharpen;
561         minimap.width = minimap.height = game->miniMapSize;
562         border = game->miniMapBorder;
563
564         minimap.samples = 1;
565         minimap.sample_offsets = NULL;
566
567         /* process arguments */
568         for( i = 1; i < (argc - 1); i++ )
569         {
570                 if( !strcmp( argv[ i ],  "-size" ) )
571                 {
572                         minimap.width = minimap.height = atoi(argv[i + 1]);
573                         i++;
574                         Sys_Printf( "Image size set to %i\n", minimap.width );
575                 }
576                 else if( !strcmp( argv[ i ],  "-sharpen" ) )
577                 {
578                         minimapSharpen = atof(argv[i + 1]);
579                         i++;
580                         Sys_Printf( "Sharpening coefficient set to %f\n", minimapSharpen );
581                 }
582                 else if( !strcmp( argv[ i ],  "-samples" ) )
583                 {
584                         minimap.samples = atoi(argv[i + 1]);
585                         i++;
586                         Sys_Printf( "Samples set to %i\n", minimap.samples );
587                         if(minimap.sample_offsets)
588                                 free(minimap.sample_offsets);
589                         minimap.sample_offsets = malloc(2 * sizeof(*minimap.sample_offsets) * minimap.samples);
590                         MiniMapMakeSampleOffsets();
591                 }
592                 else if( !strcmp( argv[ i ],  "-random" ) )
593                 {
594                         minimap.samples = atoi(argv[i + 1]);
595                         i++;
596                         Sys_Printf( "Random samples set to %i\n", minimap.samples );
597                         if(minimap.sample_offsets)
598                                 free(minimap.sample_offsets);
599                         minimap.sample_offsets = NULL;
600                 }
601                 else if( !strcmp( argv[ i ],  "-border" ) )
602                 {
603                         border = atof(argv[i + 1]);
604                         i++;
605                         Sys_Printf( "Border set to %f\n", border );
606                 }
607                 else if( !strcmp( argv[ i ],  "-o" ) )
608                 {
609                         strcpy(minimapFilename, argv[i + 1]);
610                         i++;
611                         Sys_Printf( "Output file name set to %s\n", minimapFilename );
612                 }
613                 else if( !strcmp( argv[ i ],  "-minmax" ) && i < (argc - 7) )
614                 {
615                         mins[0] = atof(argv[i + 1]);
616                         mins[1] = atof(argv[i + 2]);
617                         mins[2] = atof(argv[i + 3]);
618                         maxs[0] = atof(argv[i + 4]);
619                         maxs[1] = atof(argv[i + 5]);
620                         maxs[2] = atof(argv[i + 6]);
621                         i += 6;
622                         Sys_Printf( "Map mins/maxs overridden\n" );
623                 }
624         }
625
626         MiniMapMakeMinsMaxs(mins, maxs, border);
627
628         if(!*minimapFilename)
629         {
630                 ExtractFileBase(source, basename);
631                 ExtractFilePath(source, path);
632                 sprintf(relativeMinimapFilename, game->miniMapNameFormat, basename);
633                 MergeRelativePath(minimapFilename, path, relativeMinimapFilename);
634                 Sys_Printf("Output file name automatically set to %s\n", minimapFilename);
635         }
636
637         if(minimapSharpen >= 0)
638         {
639                 minimap.sharpen_centermult = 8 * minimapSharpen + 1;
640                 minimap.sharpen_boxmult    =    -minimapSharpen;
641         }
642
643         minimap.data1f = safe_malloc(minimap.width * minimap.height * sizeof(*minimap.data1f));
644         data3b = safe_malloc(minimap.width * minimap.height * 3);
645         if(minimapSharpen >= 0)
646                 minimap.sharpendata1f = safe_malloc(minimap.width * minimap.height * sizeof(*minimap.data1f));
647
648         MiniMapSetupBrushes();
649
650         if(minimap.samples <= 1)
651         {
652                 Sys_Printf( "\n--- MiniMapNoSupersampling (%d) ---\n", minimap.height );
653                 RunThreadsOnIndividual(minimap.height, qtrue, MiniMapNoSupersampling);
654         }
655         else
656         {
657                 if(minimap.sample_offsets)
658                 {
659                         Sys_Printf( "\n--- MiniMapSupersampled (%d) ---\n", minimap.height );
660                         RunThreadsOnIndividual(minimap.height, qtrue, MiniMapSupersampled);
661                 }
662                 else
663                 {
664                         Sys_Printf( "\n--- MiniMapRandomlySupersampled (%d) ---\n", minimap.height );
665                         RunThreadsOnIndividual(minimap.height, qtrue, MiniMapRandomlySupersampled);
666                 }
667         }
668
669         if(minimap.sharpendata1f)
670         {
671                 Sys_Printf( "\n--- MiniMapSharpen (%d) ---\n", minimap.height );
672                 RunThreadsOnIndividual(minimap.height, qtrue, MiniMapSharpen);
673                 q = minimap.sharpendata1f;
674         }
675         else
676         {
677                 q = minimap.data1f;
678         }
679
680         Sys_Printf( "\nConverting...");
681         p = data3b;
682         for(y = 0; y < minimap.height; ++y)
683                 for(x = 0; x < minimap.width; ++x)
684                 {
685                         byte b;
686                         float v = *q++;
687                         if(v < 0) v = 0;
688                         if(v > 255.0/256.0) v = 255.0/256.0;
689                         b = v * 256;
690                         *p++ = b;
691                         *p++ = b;
692                         *p++ = b;
693                 }
694
695         Sys_Printf( " writing to %s...", minimapFilename );
696         WriteTGA24(minimapFilename, data3b, minimap.width, minimap.height, qfalse);
697
698         Sys_Printf( " done.\n" );
699
700         /* return to sender */
701         return 0;
702 }
703
704
705
706
707
708 /*
709 MD4BlockChecksum()
710 calculates an md4 checksum for a block of data
711 */
712
713 static int MD4BlockChecksum( void *buffer, int length )
714 {
715         return Com_BlockChecksum(buffer, length);
716 }
717
718 /*
719 FixAAS()
720 resets an aas checksum to match the given BSP
721 */
722
723 int FixAAS( int argc, char **argv )
724 {
725         int                     length, checksum;
726         void            *buffer;
727         FILE            *file;
728         char            aas[ 1024 ], **ext;
729         char            *exts[] =
730                                 {
731                                         ".aas",
732                                         "_b0.aas",
733                                         "_b1.aas",
734                                         NULL
735                                 };
736         
737         
738         /* arg checking */
739         if( argc < 2 )
740         {
741                 Sys_Printf( "Usage: q3map -fixaas [-v] <mapname>\n" );
742                 return 0;
743         }
744         
745         /* do some path mangling */
746         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
747         StripExtension( source );
748         DefaultExtension( source, ".bsp" );
749         
750         /* note it */
751         Sys_Printf( "--- FixAAS ---\n" );
752         
753         /* load the bsp */
754         Sys_Printf( "Loading %s\n", source );
755         length = LoadFile( source, &buffer );
756         
757         /* create bsp checksum */
758         Sys_Printf( "Creating checksum...\n" );
759         checksum = LittleLong( MD4BlockChecksum( buffer, length ) );
760         
761         /* write checksum to aas */
762         ext = exts;
763         while( *ext )
764         {
765                 /* mangle name */
766                 strcpy( aas, source );
767                 StripExtension( aas );
768                 strcat( aas, *ext );
769                 Sys_Printf( "Trying %s\n", aas );
770                 ext++;
771                 
772                 /* fix it */
773                 file = fopen( aas, "r+b" );
774                 if( !file )
775                         continue;
776                 if( fwrite( &checksum, 4, 1, file ) != 1 )
777                         Error( "Error writing checksum to %s", aas );
778                 fclose( file );
779         }
780         
781         /* return to sender */
782         return 0;
783 }
784
785
786
787 /*
788 AnalyzeBSP() - ydnar
789 analyzes a Quake engine BSP file
790 */
791
792 typedef struct abspHeader_s
793 {
794         char                    ident[ 4 ];
795         int                             version;
796         
797         bspLump_t               lumps[ 1 ];     /* unknown size */
798 }
799 abspHeader_t;
800
801 typedef struct abspLumpTest_s
802 {
803         int                             radix, minCount;
804         char                    *name;
805 }
806 abspLumpTest_t;
807
808 int AnalyzeBSP( int argc, char **argv )
809 {
810         abspHeader_t                    *header;
811         int                                             size, i, version, offset, length, lumpInt, count;
812         char                                    ident[ 5 ];
813         void                                    *lump;
814         float                                   lumpFloat;
815         char                                    lumpString[ 1024 ], source[ 1024 ];
816         qboolean                                lumpSwap = qfalse;
817         abspLumpTest_t                  *lumpTest;
818         static abspLumpTest_t   lumpTests[] =
819                                                         {
820                                                                 { sizeof( bspPlane_t ),                 6,              "IBSP LUMP_PLANES" },
821                                                                 { sizeof( bspBrush_t ),                 1,              "IBSP LUMP_BRUSHES" },
822                                                                 { 8,                                                    6,              "IBSP LUMP_BRUSHSIDES" },
823                                                                 { sizeof( bspBrushSide_t ),             6,              "RBSP LUMP_BRUSHSIDES" },
824                                                                 { sizeof( bspModel_t ),                 1,              "IBSP LUMP_MODELS" },
825                                                                 { sizeof( bspNode_t ),                  2,              "IBSP LUMP_NODES" },
826                                                                 { sizeof( bspLeaf_t ),                  1,              "IBSP LUMP_LEAFS" },
827                                                                 { 104,                                                  3,              "IBSP LUMP_DRAWSURFS" },
828                                                                 { 44,                                                   3,              "IBSP LUMP_DRAWVERTS" },
829                                                                 { 4,                                                    6,              "IBSP LUMP_DRAWINDEXES" },
830                                                                 { 128 * 128 * 3,                                1,              "IBSP LUMP_LIGHTMAPS" },
831                                                                 { 256 * 256 * 3,                                1,              "IBSP LUMP_LIGHTMAPS (256 x 256)" },
832                                                                 { 512 * 512 * 3,                                1,              "IBSP LUMP_LIGHTMAPS (512 x 512)" },
833                                                                 { 0, 0, NULL }
834                                                         };
835         
836         
837         /* arg checking */
838         if( argc < 1 )
839         {
840                 Sys_Printf( "Usage: q3map -analyze [-lumpswap] [-v] <mapname>\n" );
841                 return 0;
842         }
843         
844         /* process arguments */
845         for( i = 1; i < (argc - 1); i++ )
846         {
847                 /* -format map|ase|... */
848                 if( !strcmp( argv[ i ],  "-lumpswap" ) )
849                 {
850                         Sys_Printf( "Swapped lump structs enabled\n" );
851                         lumpSwap = qtrue;
852                 }
853         }
854         
855         /* clean up map name */
856         strcpy( source, ExpandArg( argv[ i ] ) );
857         Sys_Printf( "Loading %s\n", source );
858         
859         /* load the file */
860         size = LoadFile( source, (void**) &header );
861         if( size == 0 || header == NULL )
862         {
863                 Sys_Printf( "Unable to load %s.\n", source );
864                 return -1;
865         }
866         
867         /* analyze ident/version */
868         memcpy( ident, header->ident, 4 );
869         ident[ 4 ] = '\0';
870         version = LittleLong( header->version );
871         
872         Sys_Printf( "Identity:      %s\n", ident );
873         Sys_Printf( "Version:       %d\n", version );
874         Sys_Printf( "---------------------------------------\n" );
875         
876         /* analyze each lump */
877         for( i = 0; i < 100; i++ )
878         {
879                 /* call of duty swapped lump pairs */
880                 if( lumpSwap )
881                 {
882                         offset = LittleLong( header->lumps[ i ].length );
883                         length = LittleLong( header->lumps[ i ].offset );
884                 }
885                 
886                 /* standard lump pairs */
887                 else
888                 {
889                         offset = LittleLong( header->lumps[ i ].offset );
890                         length = LittleLong( header->lumps[ i ].length );
891                 }
892                 
893                 /* extract data */
894                 lump = (byte*) header + offset;
895                 lumpInt = LittleLong( (int) *((int*) lump) );
896                 lumpFloat = LittleFloat( (float) *((float*) lump) );
897                 memcpy( lumpString, (char*) lump, (length < 1024 ? length : 1024) );
898                 lumpString[ 1024 ] = '\0';
899                 
900                 /* print basic lump info */
901                 Sys_Printf( "Lump:          %d\n", i );
902                 Sys_Printf( "Offset:        %d bytes\n", offset );
903                 Sys_Printf( "Length:        %d bytes\n", length );
904                 
905                 /* only operate on valid lumps */
906                 if( length > 0 )
907                 {
908                         /* print data in 4 formats */
909                         Sys_Printf( "As hex:        %08X\n", lumpInt );
910                         Sys_Printf( "As int:        %d\n", lumpInt );
911                         Sys_Printf( "As float:      %f\n", lumpFloat );
912                         Sys_Printf( "As string:     %s\n", lumpString );
913                         
914                         /* guess lump type */
915                         if( lumpString[ 0 ] == '{' && lumpString[ 2 ] == '"' )
916                                 Sys_Printf( "Type guess:    IBSP LUMP_ENTITIES\n" );
917                         else if( strstr( lumpString, "textures/" ) )
918                                 Sys_Printf( "Type guess:    IBSP LUMP_SHADERS\n" );
919                         else
920                         {
921                                 /* guess based on size/count */
922                                 for( lumpTest = lumpTests; lumpTest->radix > 0; lumpTest++ )
923                                 {
924                                         if( (length % lumpTest->radix) != 0 )
925                                                 continue;
926                                         count = length / lumpTest->radix;
927                                         if( count < lumpTest->minCount )
928                                                 continue;
929                                         Sys_Printf( "Type guess:    %s (%d x %d)\n", lumpTest->name, count, lumpTest->radix );
930                                 }
931                         }
932                 }
933                 
934                 Sys_Printf( "---------------------------------------\n" );
935                 
936                 /* end of file */
937                 if( offset + length >= size )
938                         break;
939         }
940         
941         /* last stats */
942         Sys_Printf( "Lump count:    %d\n", i + 1 );
943         Sys_Printf( "File size:     %d bytes\n", size );
944         
945         /* return to caller */
946         return 0;
947 }
948
949
950
951 /*
952 BSPInfo()
953 emits statistics about the bsp file
954 */
955
956 int BSPInfo( int count, char **fileNames )
957 {
958         int                     i;
959         char            source[ 1024 ], ext[ 64 ];
960         int                     size;
961         FILE            *f;
962         
963         
964         /* dummy check */
965         if( count < 1 )
966         {
967                 Sys_Printf( "No files to dump info for.\n");
968                 return -1;
969         }
970         
971         /* enable info mode */
972         infoMode = qtrue;
973         
974         /* walk file list */
975         for( i = 0; i < count; i++ )
976         {
977                 Sys_Printf( "---------------------------------\n" );
978                 
979                 /* mangle filename and get size */
980                 strcpy( source, fileNames[ i ] );
981                 ExtractFileExtension( source, ext );
982                 if( !Q_stricmp( ext, "map" ) )
983                         StripExtension( source );
984                 DefaultExtension( source, ".bsp" );
985                 f = fopen( source, "rb" );
986                 if( f )
987                 {
988                         size = Q_filelength (f);
989                         fclose( f );
990                 }
991                 else
992                         size = 0;
993                 
994                 /* load the bsp file and print lump sizes */
995                 Sys_Printf( "%s\n", source );
996                 LoadBSPFile( source );          
997                 PrintBSPFileSizes();
998                 
999                 /* print sizes */
1000                 Sys_Printf( "\n" );
1001                 Sys_Printf( "          total         %9d\n", size );
1002                 Sys_Printf( "                        %9d KB\n", size / 1024 );
1003                 Sys_Printf( "                        %9d MB\n", size / (1024 * 1024) );
1004                 
1005                 Sys_Printf( "---------------------------------\n" );
1006         }
1007         
1008         /* return count */
1009         return i;
1010 }
1011
1012
1013 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)
1014 {
1015         vec4_t scoeffs, tcoeffs;
1016         float md;
1017         m4x4_t solvematrix;
1018
1019         vec3_t norm;
1020         vec3_t dab, dac;
1021         VectorSubtract(bxyz, axyz, dab);
1022         VectorSubtract(cxyz, axyz, dac);
1023         CrossProduct(dab, dac, norm);
1024         
1025         // assume:
1026         //   s = f(x, y, z)
1027         //   s(v + norm) = s(v) when n ortho xyz
1028         
1029         // s(v) = DotProduct(v, scoeffs) + scoeffs[3]
1030
1031         // solve:
1032         //   scoeffs * (axyz, 1) == ast[0]
1033         //   scoeffs * (bxyz, 1) == bst[0]
1034         //   scoeffs * (cxyz, 1) == cst[0]
1035         //   scoeffs * (norm, 0) == 0
1036         // scoeffs * [axyz, 1 | bxyz, 1 | cxyz, 1 | norm, 0] = [ast[0], bst[0], cst[0], 0]
1037         solvematrix[0] = axyz[0];
1038         solvematrix[4] = axyz[1];
1039         solvematrix[8] = axyz[2];
1040         solvematrix[12] = 1;
1041         solvematrix[1] = bxyz[0];
1042         solvematrix[5] = bxyz[1];
1043         solvematrix[9] = bxyz[2];
1044         solvematrix[13] = 1;
1045         solvematrix[2] = cxyz[0];
1046         solvematrix[6] = cxyz[1];
1047         solvematrix[10] = cxyz[2];
1048         solvematrix[14] = 1;
1049         solvematrix[3] = norm[0];
1050         solvematrix[7] = norm[1];
1051         solvematrix[11] = norm[2];
1052         solvematrix[15] = 0;
1053
1054         md = m4_det(solvematrix);
1055         if(md*md < 1e-10)
1056         {
1057                 Sys_Printf("Cannot invert some matrix, some texcoords aren't extrapolated!");
1058                 return;
1059         }
1060
1061         m4x4_invert(solvematrix);
1062
1063         scoeffs[0] = ast[0];
1064         scoeffs[1] = bst[0];
1065         scoeffs[2] = cst[0];
1066         scoeffs[3] = 0;
1067         m4x4_transform_vec4(solvematrix, scoeffs);
1068         tcoeffs[0] = ast[1];
1069         tcoeffs[1] = bst[1];
1070         tcoeffs[2] = cst[1];
1071         tcoeffs[3] = 0;
1072         m4x4_transform_vec4(solvematrix, tcoeffs);
1073
1074         ast_out[0] = scoeffs[0] * axyz_new[0] + scoeffs[1] * axyz_new[1] + scoeffs[2] * axyz_new[2] + scoeffs[3];
1075         ast_out[1] = tcoeffs[0] * axyz_new[0] + tcoeffs[1] * axyz_new[1] + tcoeffs[2] * axyz_new[2] + tcoeffs[3];
1076         bst_out[0] = scoeffs[0] * bxyz_new[0] + scoeffs[1] * bxyz_new[1] + scoeffs[2] * bxyz_new[2] + scoeffs[3];
1077         bst_out[1] = tcoeffs[0] * bxyz_new[0] + tcoeffs[1] * bxyz_new[1] + tcoeffs[2] * bxyz_new[2] + tcoeffs[3];
1078         cst_out[0] = scoeffs[0] * cxyz_new[0] + scoeffs[1] * cxyz_new[1] + scoeffs[2] * cxyz_new[2] + scoeffs[3];
1079         cst_out[1] = tcoeffs[0] * cxyz_new[0] + tcoeffs[1] * cxyz_new[1] + tcoeffs[2] * cxyz_new[2] + tcoeffs[3];
1080 }
1081
1082 /*
1083 ScaleBSPMain()
1084 amaze and confuse your enemies with wierd scaled maps!
1085 */
1086
1087 int ScaleBSPMain( int argc, char **argv )
1088 {
1089         int                     i, j;
1090         float           f, a;
1091         vec3_t scale;
1092         vec3_t          vec;
1093         char            str[ 1024 ];
1094         int uniform, axis;
1095         qboolean texscale;
1096         float *old_xyzst = NULL;
1097         
1098         
1099         /* arg checking */
1100         if( argc < 3 )
1101         {
1102                 Sys_Printf( "Usage: q3map [-v] -scale [-tex] <value> <mapname>\n" );
1103                 return 0;
1104         }
1105         
1106         /* get scale */
1107         scale[2] = scale[1] = scale[0] = atof( argv[ argc - 2 ] );
1108         if(argc >= 4)
1109                 scale[1] = scale[0] = atof( argv[ argc - 3 ] );
1110         if(argc >= 5)
1111                 scale[0] = atof( argv[ argc - 4 ] );
1112
1113         texscale = !strcmp(argv[1], "-tex");
1114         
1115         uniform = ((scale[0] == scale[1]) && (scale[1] == scale[2]));
1116
1117         if( scale[0] == 0.0f || scale[1] == 0.0f || scale[2] == 0.0f )
1118         {
1119                 Sys_Printf( "Usage: q3map [-v] -scale [-tex] <value> <mapname>\n" );
1120                 Sys_Printf( "Non-zero scale value required.\n" );
1121                 return 0;
1122         }
1123         
1124         /* do some path mangling */
1125         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
1126         StripExtension( source );
1127         DefaultExtension( source, ".bsp" );
1128         
1129         /* load the bsp */
1130         Sys_Printf( "Loading %s\n", source );
1131         LoadBSPFile( source );
1132         ParseEntities();
1133         
1134         /* note it */
1135         Sys_Printf( "--- ScaleBSP ---\n" );
1136         Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1137         
1138         /* scale entity keys */
1139         for( i = 0; i < numBSPEntities && i < numEntities; i++ )
1140         {
1141                 /* scale origin */
1142                 GetVectorForKey( &entities[ i ], "origin", vec );
1143                 if( (vec[ 0 ] || vec[ 1 ] || vec[ 2 ]) )
1144                 {
1145                         vec[0] *= scale[0];
1146                         vec[1] *= scale[1];
1147                         vec[2] *= scale[2];
1148                         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
1149                         SetKeyValue( &entities[ i ], "origin", str );
1150                 }
1151
1152                 a = FloatForKey( &entities[ i ], "angle" );
1153                 if(a == -1 || a == -2) // z scale
1154                         axis = 2;
1155                 else if(fabs(sin(DEG2RAD(a))) < 0.707)
1156                         axis = 0;
1157                 else
1158                         axis = 1;
1159                 
1160                 /* scale door lip */
1161                 f = FloatForKey( &entities[ i ], "lip" );
1162                 if( f )
1163                 {
1164                         f *= scale[axis];
1165                         sprintf( str, "%f", f );
1166                         SetKeyValue( &entities[ i ], "lip", str );
1167                 }
1168                 
1169                 /* scale plat height */
1170                 f = FloatForKey( &entities[ i ], "height" );
1171                 if( f )
1172                 {
1173                         f *= scale[2];
1174                         sprintf( str, "%f", f );
1175                         SetKeyValue( &entities[ i ], "height", str );
1176                 }
1177
1178                 // TODO maybe allow a definition file for entities to specify which values are scaled how?
1179         }
1180         
1181         /* scale models */
1182         for( i = 0; i < numBSPModels; i++ )
1183         {
1184                 bspModels[ i ].mins[0] *= scale[0];
1185                 bspModels[ i ].mins[1] *= scale[1];
1186                 bspModels[ i ].mins[2] *= scale[2];
1187                 bspModels[ i ].maxs[0] *= scale[0];
1188                 bspModels[ i ].maxs[1] *= scale[1];
1189                 bspModels[ i ].maxs[2] *= scale[2];
1190         }
1191         
1192         /* scale nodes */
1193         for( i = 0; i < numBSPNodes; i++ )
1194         {
1195                 bspNodes[ i ].mins[0] *= scale[0];
1196                 bspNodes[ i ].mins[1] *= scale[1];
1197                 bspNodes[ i ].mins[2] *= scale[2];
1198                 bspNodes[ i ].maxs[0] *= scale[0];
1199                 bspNodes[ i ].maxs[1] *= scale[1];
1200                 bspNodes[ i ].maxs[2] *= scale[2];
1201         }
1202         
1203         /* scale leafs */
1204         for( i = 0; i < numBSPLeafs; i++ )
1205         {
1206                 bspLeafs[ i ].mins[0] *= scale[0];
1207                 bspLeafs[ i ].mins[1] *= scale[1];
1208                 bspLeafs[ i ].mins[2] *= scale[2];
1209                 bspLeafs[ i ].maxs[0] *= scale[0];
1210                 bspLeafs[ i ].maxs[1] *= scale[1];
1211                 bspLeafs[ i ].maxs[2] *= scale[2];
1212         }
1213         
1214         if(texscale)
1215         {
1216                 Sys_Printf("Using texture unlocking (and probably breaking texture alignment a lot)\n");
1217                 old_xyzst = safe_malloc(sizeof(*old_xyzst) * numBSPDrawVerts * 5);
1218                 for(i = 0; i < numBSPDrawVerts; i++)
1219                 {
1220                         old_xyzst[5*i+0] = bspDrawVerts[i].xyz[0];
1221                         old_xyzst[5*i+1] = bspDrawVerts[i].xyz[1];
1222                         old_xyzst[5*i+2] = bspDrawVerts[i].xyz[2];
1223                         old_xyzst[5*i+3] = bspDrawVerts[i].st[0];
1224                         old_xyzst[5*i+4] = bspDrawVerts[i].st[1];
1225                 }
1226         }
1227
1228         /* scale drawverts */
1229         for( i = 0; i < numBSPDrawVerts; i++ )
1230         {
1231                 bspDrawVerts[i].xyz[0] *= scale[0];
1232                 bspDrawVerts[i].xyz[1] *= scale[1];
1233                 bspDrawVerts[i].xyz[2] *= scale[2];
1234                 bspDrawVerts[i].normal[0] /= scale[0];
1235                 bspDrawVerts[i].normal[1] /= scale[1];
1236                 bspDrawVerts[i].normal[2] /= scale[2];
1237                 VectorNormalize(bspDrawVerts[i].normal, bspDrawVerts[i].normal);
1238         }
1239
1240         if(texscale)
1241         {
1242                 for(i = 0; i < numBSPDrawSurfaces; i++)
1243                 {
1244                         switch(bspDrawSurfaces[i].surfaceType)
1245                         {
1246                                 case SURFACE_FACE:
1247                                 case SURFACE_META:
1248                                         if(bspDrawSurfaces[i].numIndexes % 3)
1249                                                 Error("Not a triangulation!");
1250                                         for(j = bspDrawSurfaces[i].firstIndex; j < bspDrawSurfaces[i].firstIndex + bspDrawSurfaces[i].numIndexes; j += 3)
1251                                         {
1252                                                 int ia = bspDrawIndexes[j] + bspDrawSurfaces[i].firstVert, ib = bspDrawIndexes[j+1] + bspDrawSurfaces[i].firstVert, ic = bspDrawIndexes[j+2] + bspDrawSurfaces[i].firstVert;
1253                                                 bspDrawVert_t *a = &bspDrawVerts[ia], *b = &bspDrawVerts[ib], *c = &bspDrawVerts[ic];
1254                                                 float *oa = &old_xyzst[ia*5], *ob = &old_xyzst[ib*5], *oc = &old_xyzst[ic*5];
1255                                                 // extrapolate:
1256                                                 //   a->xyz -> oa
1257                                                 //   b->xyz -> ob
1258                                                 //   c->xyz -> oc
1259                                                 ExtrapolateTexcoords(
1260                                                         &oa[0], &oa[3],
1261                                                         &ob[0], &ob[3],
1262                                                         &oc[0], &oc[3],
1263                                                         a->xyz, a->st,
1264                                                         b->xyz, b->st,
1265                                                         c->xyz, c->st);
1266                                         }
1267                                         break;
1268                         }
1269                 }
1270         }
1271         
1272         /* scale planes */
1273         if(uniform)
1274         {
1275                 for( i = 0; i < numBSPPlanes; i++ )
1276                 {
1277                         bspPlanes[ i ].dist *= scale[0];
1278                 }
1279         }
1280         else
1281         {
1282                 for( i = 0; i < numBSPPlanes; i++ )
1283                 {
1284                         bspPlanes[ i ].normal[0] /= scale[0];
1285                         bspPlanes[ i ].normal[1] /= scale[1];
1286                         bspPlanes[ i ].normal[2] /= scale[2];
1287                         f = 1/VectorLength(bspPlanes[i].normal);
1288                         VectorScale(bspPlanes[i].normal, f, bspPlanes[i].normal);
1289                         bspPlanes[ i ].dist *= f;
1290                 }
1291         }
1292         
1293         /* scale gridsize */
1294         GetVectorForKey( &entities[ 0 ], "gridsize", vec );
1295         if( (vec[ 0 ] + vec[ 1 ] + vec[ 2 ]) == 0.0f )
1296                 VectorCopy( gridSize, vec );
1297         vec[0] *= scale[0];
1298         vec[1] *= scale[1];
1299         vec[2] *= scale[2];
1300         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
1301         SetKeyValue( &entities[ 0 ], "gridsize", str );
1302
1303         /* inject command line parameters */
1304         InjectCommandLine(argv, 0, argc - 1);
1305         
1306         /* write the bsp */
1307         UnparseEntities();
1308         StripExtension( source );
1309         DefaultExtension( source, "_s.bsp" );
1310         Sys_Printf( "Writing %s\n", source );
1311         WriteBSPFile( source );
1312         
1313         /* return to sender */
1314         return 0;
1315 }
1316
1317
1318
1319 /*
1320 ConvertBSPMain()
1321 main argument processing function for bsp conversion
1322 */
1323
1324 int ConvertBSPMain( int argc, char **argv )
1325 {
1326         int             i;
1327         int             (*convertFunc)( char * );
1328         game_t  *convertGame;
1329         
1330         
1331         /* set default */
1332         convertFunc = ConvertBSPToASE;
1333         convertGame = NULL;
1334         
1335         /* arg checking */
1336         if( argc < 1 )
1337         {
1338                 Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
1339                 return 0;
1340         }
1341         
1342         /* process arguments */
1343         for( i = 1; i < (argc - 1); i++ )
1344         {
1345                 /* -format map|ase|... */
1346                 if( !strcmp( argv[ i ],  "-format" ) )
1347                 {
1348                         i++;
1349                         if( !Q_stricmp( argv[ i ], "ase" ) )
1350                                 convertFunc = ConvertBSPToASE;
1351                         else if( !Q_stricmp( argv[ i ], "map" ) )
1352                                 convertFunc = ConvertBSPToMap;
1353                         else
1354                         {
1355                                 convertGame = GetGame( argv[ i ] );
1356                                 if( convertGame == NULL )
1357                                         Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
1358                         }
1359                 }
1360                 else if( !strcmp( argv[ i ],  "-ne" ) )
1361                 {
1362                         normalEpsilon = atof( argv[ i + 1 ] );
1363                         i++;
1364                         Sys_Printf( "Normal epsilon set to %f\n", normalEpsilon );
1365                 }
1366                 else if( !strcmp( argv[ i ],  "-de" ) )
1367                 {
1368                         distanceEpsilon = atof( argv[ i + 1 ] );
1369                         i++;
1370                         Sys_Printf( "Distance epsilon set to %f\n", distanceEpsilon );
1371                 }
1372                 else if( !strcmp( argv[ i ],  "-shadersasbitmap" ) )
1373                         shadersAsBitmap = qtrue;
1374         }
1375         
1376         /* clean up map name */
1377         strcpy( source, ExpandArg( argv[ i ] ) );
1378         StripExtension( source );
1379         DefaultExtension( source, ".bsp" );
1380         
1381         LoadShaderInfo();
1382         
1383         Sys_Printf( "Loading %s\n", source );
1384         
1385         /* ydnar: load surface file */
1386         //%     LoadSurfaceExtraFile( source );
1387         
1388         LoadBSPFile( source );
1389         
1390         /* parse bsp entities */
1391         ParseEntities();
1392         
1393         /* bsp format convert? */
1394         if( convertGame != NULL )
1395         {
1396                 /* set global game */
1397                 game = convertGame;
1398                 
1399                 /* write bsp */
1400                 StripExtension( source );
1401                 DefaultExtension( source, "_c.bsp" );
1402                 Sys_Printf( "Writing %s\n", source );
1403                 WriteBSPFile( source );
1404                 
1405                 /* return to sender */
1406                 return 0;
1407         }
1408         
1409         /* normal convert */
1410         return convertFunc( source );
1411 }
1412
1413
1414
1415 /*
1416 main()
1417 q3map mojo...
1418 */
1419
1420 int main( int argc, char **argv )
1421 {
1422         int             i, r;
1423         double  start, end;
1424         
1425         
1426         /* we want consistent 'randomness' */
1427         srand( 0 );
1428         
1429         /* start timer */
1430         start = I_FloatTime();
1431
1432         /* this was changed to emit version number over the network */
1433         printf( Q3MAP_VERSION "\n" );
1434         
1435         /* set exit call */
1436         atexit( ExitQ3Map );
1437
1438         /* read general options first */
1439         for( i = 1; i < argc; i++ )
1440         {
1441                 /* -connect */
1442                 if( !strcmp( argv[ i ], "-connect" ) )
1443                 {
1444                         argv[ i ] = NULL;
1445                         i++;
1446                         Broadcast_Setup( argv[ i ] );
1447                         argv[ i ] = NULL;
1448                 }
1449                 
1450                 /* verbose */
1451                 else if( !strcmp( argv[ i ], "-v" ) )
1452                 {
1453                         if(!verbose)
1454                         {
1455                                 verbose = qtrue;
1456                                 argv[ i ] = NULL;
1457                         }
1458                 }
1459                 
1460                 /* force */
1461                 else if( !strcmp( argv[ i ], "-force" ) )
1462                 {
1463                         force = qtrue;
1464                         argv[ i ] = NULL;
1465                 }
1466                 
1467                 /* patch subdivisions */
1468                 else if( !strcmp( argv[ i ], "-subdivisions" ) )
1469                 {
1470                         argv[ i ] = NULL;
1471                         i++;
1472                         patchSubdivisions = atoi( argv[ i ] );
1473                         argv[ i ] = NULL;
1474                         if( patchSubdivisions <= 0 )
1475                                 patchSubdivisions = 1;
1476                 }
1477                 
1478                 /* threads */
1479                 else if( !strcmp( argv[ i ], "-threads" ) )
1480                 {
1481                         argv[ i ] = NULL;
1482                         i++;
1483                         numthreads = atoi( argv[ i ] );
1484                         argv[ i ] = NULL;
1485                 }
1486         }
1487         
1488         /* init model library */
1489         PicoInit();
1490         PicoSetMallocFunc( safe_malloc );
1491         PicoSetFreeFunc( free );
1492         PicoSetPrintFunc( PicoPrintFunc );
1493         PicoSetLoadFileFunc( PicoLoadFileFunc );
1494         PicoSetFreeFileFunc( free );
1495         
1496         /* set number of threads */
1497         ThreadSetDefault();
1498         
1499         /* generate sinusoid jitter table */
1500         for( i = 0; i < MAX_JITTERS; i++ )
1501         {
1502                 jitters[ i ] = sin( i * 139.54152147 );
1503                 //%     Sys_Printf( "Jitter %4d: %f\n", i, jitters[ i ] );
1504         }
1505         
1506         /* we print out two versions, q3map's main version (since it evolves a bit out of GtkRadiant)
1507            and we put the GtkRadiant version to make it easy to track with what version of Radiant it was built with */
1508         
1509         Sys_Printf( "Q3Map         - v1.0r (c) 1999 Id Software Inc.\n" );
1510         Sys_Printf( "Q3Map (ydnar) - v" Q3MAP_VERSION "\n" );
1511         Sys_Printf( "NetRadiant    - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
1512         Sys_Printf( "%s\n", Q3MAP_MOTD );
1513         
1514         /* ydnar: new path initialization */
1515         InitPaths( &argc, argv );
1516
1517         /* set game options */
1518         if (!patchSubdivisions)
1519                 patchSubdivisions = game->patchSubdivisions;
1520         
1521         /* check if we have enough options left to attempt something */
1522         if( argc < 2 )
1523                 Error( "Usage: %s [general options] [options] mapfile", argv[ 0 ] );
1524         
1525         /* fixaas */
1526         if( !strcmp( argv[ 1 ], "-fixaas" ) )
1527                 r = FixAAS( argc - 1, argv + 1 );
1528         
1529         /* analyze */
1530         else if( !strcmp( argv[ 1 ], "-analyze" ) )
1531                 r = AnalyzeBSP( argc - 1, argv + 1 );
1532         
1533         /* info */
1534         else if( !strcmp( argv[ 1 ], "-info" ) )
1535                 r = BSPInfo( argc - 2, argv + 2 );
1536         
1537         /* vis */
1538         else if( !strcmp( argv[ 1 ], "-vis" ) )
1539                 r = VisMain( argc - 1, argv + 1 );
1540         
1541         /* light */
1542         else if( !strcmp( argv[ 1 ], "-light" ) )
1543                 r = LightMain( argc - 1, argv + 1 );
1544         
1545         /* vlight */
1546         else if( !strcmp( argv[ 1 ], "-vlight" ) )
1547         {
1548                 Sys_Printf( "WARNING: VLight is no longer supported, defaulting to -light -fast instead\n\n" );
1549                 argv[ 1 ] = "-fast";    /* eek a hack */
1550                 r = LightMain( argc, argv );
1551         }
1552         
1553         /* ydnar: lightmap export */
1554         else if( !strcmp( argv[ 1 ], "-export" ) )
1555                 r = ExportLightmapsMain( argc - 1, argv + 1 );
1556         
1557         /* ydnar: lightmap import */
1558         else if( !strcmp( argv[ 1 ], "-import" ) )
1559                 r = ImportLightmapsMain( argc - 1, argv + 1 );
1560         
1561         /* ydnar: bsp scaling */
1562         else if( !strcmp( argv[ 1 ], "-scale" ) )
1563                 r = ScaleBSPMain( argc - 1, argv + 1 );
1564         
1565         /* ydnar: bsp conversion */
1566         else if( !strcmp( argv[ 1 ], "-convert" ) )
1567                 r = ConvertBSPMain( argc - 1, argv + 1 );
1568         
1569         /* div0: minimap */
1570         else if( !strcmp( argv[ 1 ], "-minimap" ) )
1571                 r = MiniMapBSPMain(argc - 1, argv + 1);
1572
1573         /* ydnar: otherwise create a bsp */
1574         else
1575                 r = BSPMain( argc, argv );
1576         
1577         /* emit time */
1578         end = I_FloatTime();
1579         Sys_Printf( "%9.0f seconds elapsed\n", end - start );
1580         
1581         /* shut down connection */
1582         Broadcast_Shutdown();
1583         
1584         /* return any error code */
1585         return r;
1586 }