reduce more diff noise
[xonotic/netradiant.git] / tools / quake3 / q3data / q3data.c
1 /*
2    Copyright (C) 1999-2007 id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "globaldefs.h"
23 #if GDEF_OS_WINDOWS
24 #include <io.h>
25 #endif
26 #include "q3data.h"
27 #include "md3lib.h"
28
29 #include "vfs.h"
30
31 qboolean g_verbose;
32 qboolean g_stripify = qtrue;
33 qboolean g_release;             // don't grab, copy output data to new tree
34 char g_releasedir[1024];        // c:\quake2\baseq2, etc
35 qboolean g_archive;             // don't grab, copy source data to new tree
36 char g_only[256];               // if set, only grab this cd
37 qboolean g_skipmodel;           // set true when a cd is not g_only
38
39 // bogus externs for some TA hacks (common/ using them against q3map)
40 char *moddir = NULL;
41 // some old defined that was in cmdlib lost during merge
42 char writedir[1024];
43
44 #if GDEF_OS_LINUX || GDEF_OS_MACOS
45 #define strlwr strlower
46 #endif
47
48 /*
49    =======================================================
50
51    PAK FILES
52
53    =======================================================
54  */
55
56 unsigned Com_BlockChecksum( void *buffer, int length );
57
58 typedef struct
59 {
60         char name[56];
61         int filepos, filelen;
62 } packfile_t;
63
64 typedef struct
65 {
66         char id[4];
67         int dirofs;
68         int dirlen;
69 } packheader_t;
70
71 packfile_t pfiles[16384];
72 FILE            *pakfile;
73 packfile_t      *pf;
74 packheader_t pakheader;
75
76 /*
77    ==============
78    ReleaseFile
79
80    Filename should be gamedir reletive.
81    Either copies the file to the release dir, or adds it to
82    the pak file.
83    ==============
84  */
85 void ReleaseFile( char *filename ){
86         char source[1024];
87         char dest[1024];
88
89         if ( !g_release ) {
90                 return;
91         }
92
93         sprintf( source, "%s%s", gamedir, filename );
94         sprintf( dest, "%s/%s", g_releasedir, filename );
95         printf( "copying to %s\n", dest );
96         QCopyFile( source, dest );
97         return;
98 }
99
100 typedef struct
101 {
102         // shader
103         // opaque
104         // opaque 2
105         // blend
106         // blend 2
107         char names[5][1024];
108         int num;
109 } ShaderFiles_t;
110
111 ShaderFiles_t s_shaderFiles;
112
113 void FindShaderFiles( char *filename ){
114         char buffer[1024];
115         char stripped[1024];
116         char linebuffer[1024];
117         int len, i;
118         char *buf;
119         char *diffuseExtensions[] =
120         {
121                 ".TGA",
122                 ".WAL",
123                 ".PCX",
124                 0
125         };
126         char *otherExtensions[] =
127         {
128                 ".specular.TGA",
129                 ".blend.TGA",
130                 ".alpha.TGA",
131                 0
132         };
133
134         s_shaderFiles.num = 0;
135
136         strcpy( stripped, filename );
137         if ( strrchr( stripped, '.' ) ) {
138                 *strrchr( stripped, '.' ) = 0;
139         }
140         strcat( stripped, ".shader" );
141
142         if ( FileExists( stripped ) ) {
143                 char *p;
144                 char mapa[512], mapb[512];
145
146                 strcpy( s_shaderFiles.names[s_shaderFiles.num], stripped );
147                 s_shaderFiles.num++;
148
149                 // load and parse
150                 len = LoadFile( stripped, (void **)&buf );
151
152                 p = buf;
153
154                 while ( p - buf < len )
155                 {
156                         i = 0;
157
158                         // skip spaces
159                         while ( *p == ' ' || *p == '\n' || *p == '\t' )
160                                 p++;
161
162                         // grab rest of the line
163                         while ( *p != 0 && *p != '\n' )
164                         {
165                                 linebuffer[i] = *p;
166                                 i++;
167                                 p++;
168                         }
169                         if ( *p == '\n' ) {
170                                 p++;
171                         }
172                         linebuffer[i] = 0;
173
174                         strlwr( linebuffer );
175
176                         // see if the line specifies an opaque map or blendmap
177                         if ( strstr( linebuffer, "opaquemap" ) == linebuffer ||
178                                  strstr( linebuffer, "blendmap" ) == linebuffer ) {
179                                 int j;
180
181                                 i = 0;
182
183                                 mapa[0] = mapb[0] = 0;
184
185                                 // skip past the keyword
186                                 while ( linebuffer[i] != ' ' && linebuffer[i] != '\t' && linebuffer[i] )
187                                         i++;
188                                 // skip past spaces
189                                 while ( ( linebuffer[i] == ' ' || linebuffer[i] == '\t' ) && linebuffer[i] )
190                                         i++;
191
192                                 // grab first map name
193                                 j = 0;
194                                 while ( linebuffer[i] != ' ' && linebuffer[i] != '\t' && linebuffer[i] )
195                                 {
196                                         mapa[j] = linebuffer[i];
197                                         j++;
198                                         i++;
199                                 }
200                                 mapa[j] = 0;
201
202                                 // skip past spaces
203                                 while ( ( linebuffer[i] == ' ' || linebuffer[i] == '\t' ) && linebuffer[i] )
204                                         i++;
205
206                                 // grab second map name
207                                 j = 0;
208                                 while ( linebuffer[i] != ' ' && linebuffer[i] != '\t' && linebuffer[i] )
209                                 {
210                                         mapb[j] = linebuffer[i];
211                                         j++;
212                                         i++;
213                                 }
214                                 mapb[j] = 0;
215
216                                 // store map names
217                                 if ( mapa[0] != 0 && mapa[0] != '-' ) {
218                                         sprintf( s_shaderFiles.names[s_shaderFiles.num], "%s%s", gamedir, mapa );
219                                         s_shaderFiles.num++;
220                                 }
221                                 if ( mapb[0] != 0 && mapb[0] != '-' && mapb[0] != '^' && mapb[0] != '*' ) {
222                                         sprintf( s_shaderFiles.names[s_shaderFiles.num], "%s%s", gamedir, mapb );
223                                         s_shaderFiles.num++;
224                                 }
225                         }
226                 }
227         }
228         else
229         {
230                 if ( strrchr( stripped, '.' ) ) {
231                         *strrchr( stripped, '.' ) = 0;
232                 }
233
234                 // look for diffuse maps
235                 for ( i = 0; i < 3; i++ )
236                 {
237                         strcpy( buffer, stripped );
238                         strcat( buffer, diffuseExtensions[i] );
239                         if ( FileExists( buffer ) ) {
240                                 strcpy( s_shaderFiles.names[s_shaderFiles.num], buffer );
241                                 s_shaderFiles.num++;
242                                 break;
243                         }
244                 }
245                 for ( i = 0; i < 3; i++ )
246                 {
247                         strcpy( buffer, stripped );
248                         strcat( buffer, otherExtensions[i] );
249                         if ( FileExists( buffer ) ) {
250                                 strcpy( s_shaderFiles.names[s_shaderFiles.num], buffer );
251                                 s_shaderFiles.num++;
252                         }
253                 }
254         }
255 }
256
257 /*
258    ==============
259    ReleaseShader
260
261    Copies all needed files for a shader to the release directory
262    ==============
263  */
264 void ReleaseShader( char *filename ){
265         char fullpath[1024];
266         char dest[1024];
267         char stripped[1024];
268         int i;
269
270         sprintf( fullpath, "%s%s", gamedir, filename );
271
272         FindShaderFiles( fullpath );
273
274         for ( i = 0; i < s_shaderFiles.num; i++ )
275         {
276                 strcpy( stripped, s_shaderFiles.names[i] );
277                 if ( strstr( stripped, gamedir ) ) {
278                         memmove( stripped, stripped + strlen( gamedir ), strlen( stripped ) );
279                 }
280                 sprintf( dest, "%s/%s", g_releasedir, stripped );
281                 printf( "copying to %s\n", dest );
282                 QCopyFile( s_shaderFiles.names[i], dest );
283         }
284 }
285
286 /*
287    ===============
288    Cmd_File
289
290    This is only used to cause a file to be copied during a release
291    build (default.cfg, maps, etc)
292    ===============
293  */
294 void Cmd_File( void ){
295         GetToken( qfalse );
296         ReleaseFile( token );
297 }
298
299 /*
300    ===============
301    PackDirectory_r
302
303    ===============
304  */
305 #if GDEF_OS_WINDOWS
306 #include "io.h"
307 void PackDirectory_r( char *dir ){
308         struct _finddata_t fileinfo;
309         int handle;
310         char dirstring[1024];
311         char filename[1024];
312
313         sprintf( dirstring, "%s%s/*.*", gamedir, dir );
314
315         handle = _findfirst( dirstring, &fileinfo );
316         if ( handle == -1 ) {
317                 return;
318         }
319
320         do
321         {
322                 sprintf( filename, "%s/%s", dir, fileinfo.name );
323                 if ( fileinfo.attrib & _A_SUBDIR ) { // directory
324                         if ( fileinfo.name[0] != '.' ) {  // don't pak . and ..
325                                 PackDirectory_r( filename );
326                         }
327                         continue;
328                 }
329                 // copy or pack the file
330                 ReleaseFile( filename );
331         } while ( _findnext( handle, &fileinfo ) != -1 );
332
333         _findclose( handle );
334 }
335 #else
336
337 #include <sys/types.h>
338 #if !GDEF_OS_WINDOWS
339 #include <sys/dir.h>
340 #else
341 #include <sys/dirent.h>
342 #endif
343
344 void PackDirectory_r( char *dir ){
345 #ifdef NeXT
346         struct direct **namelist, *ent;
347 #else
348         struct dirent **namelist, *ent;
349 #endif
350         int count;
351         struct stat st;
352         int i;
353         int len;
354         char fullname[1024];
355         char dirstring[1024];
356         char        *name;
357
358         sprintf( dirstring, "%s%s", gamedir, dir );
359         count = scandir( dirstring, &namelist, NULL, NULL );
360
361         for ( i = 0 ; i < count ; i++ )
362         {
363                 ent = namelist[i];
364                 name = ent->d_name;
365
366                 if ( name[0] == '.' ) {
367                         continue;
368                 }
369
370                 sprintf( fullname, "%s/%s", dir, name );
371                 sprintf( dirstring, "%s%s/%s", gamedir, dir, name );
372
373                 if ( stat( dirstring, &st ) == -1 ) {
374                         Error( "fstating %s", pf->name );
375                 }
376                 if ( st.st_mode & S_IFDIR ) { // directory
377                         PackDirectory_r( fullname );
378                         continue;
379                 }
380
381                 // copy or pack the file
382                 ReleaseFile( fullname );
383         }
384 }
385 #endif
386
387
388 /*
389    ===============
390    Cmd_Dir
391
392    This is only used to cause a directory to be copied during a
393    release build (sounds, etc)
394    ===============
395  */
396 void Cmd_Dir( void ){
397         GetToken( qfalse );
398         PackDirectory_r( token );
399 }
400
401 //========================================================================
402
403 #define MAX_RTEX    16384
404 int numrtex;
405 char rtex[MAX_RTEX][64];
406
407 void ReleaseTexture( char *name ){
408         int i;
409         char path[1024];
410
411         for ( i = 0 ; i < numrtex ; i++ )
412                 if ( !Q_stricmp( name, rtex[i] ) ) {
413                         return;
414                 }
415
416         if ( numrtex == MAX_RTEX ) {
417                 Error( "numrtex == MAX_RTEX" );
418         }
419
420         strcpy( rtex[i], name );
421         numrtex++;
422
423         sprintf( path, "textures/%s.wal", name );
424         ReleaseFile( path );
425 }
426
427 /*
428    ===============
429    Cmd_Maps
430
431    Only relevent for release and pak files.
432    Releases the .bsp files for the maps, and scans all of the files to
433    build a list of all textures used, which are then released.
434    ===============
435  */
436 void Cmd_Maps( void ){
437         char map[1024];
438
439         while ( TokenAvailable() )
440         {
441                 GetToken( qfalse );
442                 sprintf( map, "maps/%s.bsp", token );
443                 ReleaseFile( map );
444
445                 if ( !g_release ) {
446                         continue;
447                 }
448
449                 // get all the texture references
450                 sprintf( map, "%smaps/%s.bsp", gamedir, token );
451                 LoadBSPFile( map );
452         }
453 }
454
455
456 //==============================================================
457
458 /*
459    ===============
460    ParseScript
461    ===============
462  */
463 void ParseScript( void ){
464         while ( 1 )
465         {
466                 do
467                 {   // look for a line starting with a $ command
468                         GetToken( qtrue );
469                         if ( endofscript ) {
470                                 return;
471                         }
472                         if ( token[0] == '$' ) {
473                                 break;
474                         }
475                         while ( TokenAvailable() )
476                                 GetToken( qfalse );
477                 } while ( 1 );
478
479                 //
480                 // model commands
481                 //
482                 if ( !strcmp( token, "$modelname" ) ) {
483                         Cmd_Modelname();
484                 }
485                 else if ( !strcmp( token, "$base" ) ) {
486                         Cmd_Base();
487                 }
488                 else if ( !strcmp( token, "$exit" ) ) {
489                         break;
490                 }
491                 else if ( !strcmp( token, "$3dsconvert" ) ) {
492                         Cmd_3DSConvert();
493                 }
494                 else if ( !strcmp( token, "$spritebase" ) ) {
495                         Cmd_SpriteBase();
496                 }
497                 else if ( !strcmp( token, "$cd" ) ) {
498                         Cmd_Cd();
499                 }
500                 else if ( !strcmp( token, "$origin" ) ) {
501                         Cmd_Origin();
502                 }
503                 else if ( !strcmp( token, "$scale" ) ) {
504                         Cmd_ScaleUp();
505                 }
506                 else if ( !strcmp( token, "$frame" ) ) {
507                         Cmd_Frame();
508                 }
509                 else if ( !strcmp( token, "$skin" ) ) {
510                         Cmd_Skin();
511                 }
512                 else if ( !strcmp( token, "$spriteshader" ) ) {
513                         Cmd_SpriteShader();
514                 }
515                 else if ( !strcmp( token, "$aseconvert" ) ) {
516                         Cmd_ASEConvert( qfalse );
517                 }
518                 else if ( !strcmp( token, "$aseanimconvert" ) ) {
519                         Cmd_ASEConvert( qtrue );
520                 }
521
522                 //
523                 // image commands
524                 //
525                 else if ( !strcmp( token, "$grab" ) ) {
526                         Cmd_Grab();
527                 }
528                 else if ( !strcmp( token, "$raw" ) ) {
529                         Cmd_Raw();
530                 }
531                 else if ( !strcmp( token, "$colormap" ) ) {
532                         Cmd_Colormap();
533                 }
534                 else if ( !strcmp( token, "$environment" ) ) {
535                         Cmd_Environment();
536                 }
537
538                 //
539                 // video
540                 //
541                 else if ( !strcmp( token, "$video" ) ) {
542                         Cmd_Video();
543                 }
544                 //
545                 // misc
546                 //
547                 else if ( !strcmp( token, "$file" ) ) {
548                         Cmd_File();
549                 }
550                 else if ( !strcmp( token, "$dir" ) ) {
551                         Cmd_Dir();
552                 }
553                 else if ( !strcmp( token, "$maps" ) ) {
554                         Cmd_Maps();
555                 }
556                 else{
557                         Error( "bad command %s\n", token );
558                 }
559         }
560 }
561
562 //=======================================================
563
564 #include "version.h"
565
566 /*
567    ==============
568    main
569    ==============
570  */
571 int main( int argc, char **argv ){
572         static int i;           // VC4.2 compiler bug if auto...
573         char path[1024];
574
575         // using GtkRadiant's versioning next to Id's versioning
576         printf( "Q3Data      - (c) 1999 Id Software Inc.\n" );
577         printf( "NetRadiant  - v" RADIANT_VERSION " " __DATE__ "\n" );
578
579         ExpandWildcards( &argc, &argv );
580
581         for ( i = 1 ; i < argc ; i++ )
582         {
583                 if ( !strcmp( argv[i], "-archive" ) ) {
584                         archive = qtrue;
585                         strcpy( archivedir, argv[i + 1] );
586                         printf( "Archiving source to: %s\n", archivedir );
587                         i++;
588                 }
589                 else if ( !strcmp( argv[i], "-release" ) ) {
590                         g_release = qtrue;
591                         strcpy( g_releasedir, argv[i + 1] );
592                         printf( "Copy output to: %s\n", g_releasedir );
593                         i++;
594                 }
595                 else if ( !strcmp( argv[i], "-nostrips" ) ) {
596                         g_stripify = qfalse;
597                         printf( "Not optimizing for strips\n" );
598                 }
599                 else if ( !strcmp( argv[i], "-writedir" ) ) {
600                         strcpy( writedir, argv[i + 1] );
601                         printf( "Write output to: %s\n", writedir );
602                         i++;
603                 }
604                 else if ( !strcmp( argv[i], "-verbose" ) ) {
605                         g_verbose = qtrue;
606                 }
607                 else if ( !strcmp( argv[i], "-dump" ) ) {
608                         printf( "Dumping contents of: '%s'\n", argv[i + 1] );
609                         if ( strstr( argv[i + 1], ".md3" ) ) {
610                                 MD3_Dump( argv[i + 1] );
611                         }
612                         else
613                         {
614                                 Error( "Do not know how to dump the contents of '%s'\n", argv[i + 1] );
615                         }
616                         i++;
617                 }
618                 else if ( !strcmp( argv[i], "-3dsconvert" ) ) {
619                         // NOTE TTimo this is broken, tried on a sample .3ds
620                         // what happens .. it calls the Convert3DStoMD3,
621                         // which calls the scriptlib function in non initialized state .. and crashes
622                         printf( "Converting %s.3DS to %s.MD3\n", argv[i + 1], argv[i + 1] );
623                         SetQdirFromPath( argv[i + 1] );
624                         vfsInitDirectory( gamedir );
625                         Convert3DStoMD3( argv[i + 1] );
626                         i++;
627                 }
628                 else if ( !strcmp( argv[i], "-only" ) ) {
629                         strcpy( g_only, argv[i + 1] );
630                         printf( "Only grabbing %s\n", g_only );
631                         i++;
632                 }
633                 else if ( !strcmp( argv[i], "-gamedir" ) ) {
634                         strcpy( gamedir, argv[i + 1] );
635                         i++;
636                 }
637                 else if ( argv[i][0] == '-' ) {
638                         Error( "Unknown option \"%s\"", argv[i] );
639                 }
640                 else{
641                         break;
642                 }
643         }
644
645         if ( i == argc ) {
646                 Error( "usage: q3data [-archive <directory>] [-dump <file.md3>] [-release <directory>] [-only <model>] [-3dsconvert <file.3ds>] [-verbose] [file.qdt]" );
647         }
648
649         for ( ; i < argc ; i++ )
650         {
651                 printf( "--------------- %s ---------------\n", argv[i] );
652                 // load the script
653                 strcpy( path, argv[i] );
654                 DefaultExtension( path, ".qdt" );
655                 if ( !gamedir[0] ) {
656                         SetQdirFromPath( path );
657                 }
658                 // NOTE TTimo
659                 // q3data went through a partial conversion to use the vfs
660                 // it was never actually tested before 1.1.1
661                 // the code is still mostly using direct file access calls
662                 vfsInitDirectory( gamedir );
663                 LoadScriptFile( ExpandArg( path ), -1 );
664
665                 //
666                 // parse it
667                 //
668                 ParseScript();
669
670                 // write out the last model
671                 FinishModel( TYPE_UNKNOWN );
672         }
673
674         return 0;
675 }