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