1 /*
2 ===========================================================================
3 Copyright (C) 1997-2006 Id Software, Inc.
4
5 This file is part of Quake 2 Tools source code.
6
7 Quake 2 Tools source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake 2 Tools source code is distributed in the hope that it will be
13 useful, 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 Quake 2 Tools source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22
23 #include "qdata.h"
24
25 qboolean g_compress_pak;
26 qboolean g_release; // don't grab, copy output data to new tree
27 qboolean g_pak; // if true, copy to pak instead of release
28 char g_releasedir[1024]; // c:\quake2\baseq2, etc
29 qboolean g_archive; // don't grab, copy source data to new tree
30 qboolean do3ds;
31 char g_only[256]; // if set, only grab this cd
32 qboolean g_skipmodel; // set true when a cd is not g_only
33
34 char *ext_3ds = "3ds";
35 char *ext_tri= "tri";
36 char *trifileext;
37
38 /*
39 =======================================================
40
41 PAK FILES
42
43 =======================================================
44 */
45
46 unsigned Com_BlockChecksum (void *buffer, int length);
47
48 typedef struct
49 {
50 char name[56];
51 int filepos, filelen;
52 } packfile_t;
53
54 typedef struct
55 {
56 char id[4];
57 int dirofs;
58 int dirlen;
59 } packheader_t;
60
61 packfile_t pfiles[16384];
62 FILE *pakfile;
63 packfile_t *pf;
64 packheader_t pakheader;
65
66
67
68 /*
69 ==============
70 BeginPak
71 ==============
72 */
BeginPak(char * outname)73 void BeginPak (char *outname)
74 {
75 if (!g_pak)
76 return;
77
78 pakfile = SafeOpenWrite (outname);
79
80 // leave space for header
81 SafeWrite (pakfile, &pakheader, sizeof(pakheader));
82
83 pf = pfiles;
84 }
85
86
87 /*
88 ==============
89 ReleaseFile
90
91 Filename should be gamedir reletive.
92 Either copies the file to the release dir, or adds it to
93 the pak file.
94 ==============
95 */
ReleaseFile(char * filename)96 void ReleaseFile (char *filename)
97 {
98 int len;
99 byte *buf;
100 char source[1024];
101 char dest[1024];
102
103 if (!g_release)
104 return;
105
106 sprintf (source, "%s%s", gamedir, filename);
107
108 if (!g_pak)
109 { // copy it
110 sprintf (dest, "%s/%s", g_releasedir, filename);
111 printf ("copying to %s\n", dest);
112 QCopyFile (source, dest);
113 return;
114 }
115
116 // pak it
117 printf ("paking %s\n", filename);
118 if (strlen(filename) >= sizeof(pf->name))
119 Error ("Filename too long for pak: %s", filename);
120
121 len = LoadFile (source, (void **)&buf);
122
123 if (g_compress_pak && len < 4096*1024 )
124 {
125 cblock_t in, out;
126 cblock_t Huffman (cblock_t in);
127
128 in.count = len;
129 in.data = buf;
130
131 out = Huffman (in);
132
133 if (out.count < in.count)
134 {
135 printf (" compressed from %i to %i\n", in.count, out.count);
136 free (in.data);
137 buf = out.data;
138 len = out.count;
139 }
140 else
141 free (out.data);
142 }
143
144 strcpy (pf->name, filename);
145 pf->filepos = LittleLong(ftell(pakfile));
146 pf->filelen = LittleLong(len);
147 pf++;
148
149 SafeWrite (pakfile, buf, len);
150
151 free (buf);
152 }
153
154
155 /*
156 ==============
157 FinishPak
158 ==============
159 */
FinishPak(void)160 void FinishPak (void)
161 {
162 int dirlen;
163 int d;
164 int i;
165 unsigned checksum;
166
167 if (!g_pak)
168 return;
169
170 pakheader.id[0] = 'P';
171 pakheader.id[1] = 'A';
172 pakheader.id[2] = 'C';
173 pakheader.id[3] = 'K';
174 dirlen = (byte *)pf - (byte *)pfiles;
175 pakheader.dirofs = LittleLong(ftell(pakfile));
176 pakheader.dirlen = LittleLong(dirlen);
177
178 checksum = Com_BlockChecksum ( (void *)pfiles, dirlen );
179
180 SafeWrite (pakfile, pfiles, dirlen);
181
182 i = ftell (pakfile);
183
184 fseek (pakfile, 0, SEEK_SET);
185 SafeWrite (pakfile, &pakheader, sizeof(pakheader));
186 fclose (pakfile);
187
188 d = pf - pfiles;
189 printf ("%i files packed in %i bytes\n",d, i);
190 printf ("checksum: 0x%x\n", checksum);
191 }
192
193
194 /*
195 ===============
196 Cmd_File
197
198 This is only used to cause a file to be copied during a release
199 build (default.cfg, maps, etc)
200 ===============
201 */
Cmd_File(void)202 void Cmd_File (void)
203 {
204 GetToken (false);
205 ReleaseFile (token);
206 }
207
208 /*
209 ===============
210 PackDirectory_r
211
212 ===============
213 */
214 #ifdef _WIN32
215 #include "io.h"
PackDirectory_r(char * dir)216 void PackDirectory_r (char *dir)
217 {
218 struct _finddata_t fileinfo;
219 int handle;
220 char dirstring[1024];
221 char filename[1024];
222
223 sprintf (dirstring, "%s%s/*.*", gamedir, dir);
224
225 handle = _findfirst (dirstring, &fileinfo);
226 if (handle == -1)
227 return;
228
229 do
230 {
231 sprintf (filename, "%s/%s", dir, fileinfo.name);
232 if (fileinfo.attrib & _A_SUBDIR)
233 { // directory
234 if (fileinfo.name[0] != '.') // don't pak . and ..
235 PackDirectory_r (filename);
236 continue;
237 }
238 // copy or pack the file
239 ReleaseFile (filename);
240 } while (_findnext( handle, &fileinfo ) != -1);
241
242 _findclose (handle);
243 }
244 #else
245
246 #include <sys/types.h>
247 #ifdef NeXT
248 #include <sys/dir.h>
249 #else
250 #include <sys/dirent.h>
251 #endif
252
PackDirectory_r(char * dir)253 void PackDirectory_r (char *dir)
254 {
255 #ifdef NeXT
256 struct direct **namelist, *ent;
257 #else
258 struct dirent **namelist, *ent;
259 #endif
260 int count;
261 struct stat st;
262 int i;
263 int len;
264 char fullname[1024];
265 char dirstring[1024];
266 char *name;
267
268 sprintf (dirstring, "%s%s", gamedir, dir);
269 count = scandir(dirstring, &namelist, NULL, NULL);
270
271 for (i=0 ; i<count ; i++)
272 {
273 ent = namelist[i];
274 name = ent->d_name;
275
276 if (name[0] == '.')
277 continue;
278
279 sprintf (fullname, "%s/%s", dir, name);
280 sprintf (dirstring, "%s%s/%s", gamedir, dir, name);
281
282 if (stat (dirstring, &st) == -1)
283 Error ("fstating %s", pf->name);
284 if (st.st_mode & S_IFDIR)
285 { // directory
286 PackDirectory_r (fullname);
287 continue;
288 }
289
290 // copy or pack the file
291 ReleaseFile (fullname);
292 }
293 }
294 #endif
295
296
297 /*
298 ===============
299 Cmd_Dir
300
301 This is only used to cause a directory to be copied during a
302 release build (sounds, etc)
303 ===============
304 */
Cmd_Dir(void)305 void Cmd_Dir (void)
306 {
307 GetToken (false);
308 PackDirectory_r (token);
309 }
310
311 //========================================================================
312
313 #define MAX_RTEX 16384
314 int numrtex;
315 char rtex[MAX_RTEX][64];
316
ReleaseTexture(char * name)317 void ReleaseTexture (char *name)
318 {
319 int i;
320 char path[1024];
321
322 for (i=0 ; i<numrtex ; i++)
323 if (!Q_strcasecmp(name, rtex[i]))
324 return;
325
326 if (numrtex == MAX_RTEX)
327 Error ("numrtex == MAX_RTEX");
328
329 strcpy (rtex[i], name);
330 numrtex++;
331
332 sprintf (path, "textures/%s.wal", name);
333 ReleaseFile (path);
334 }
335
336 /*
337 ===============
338 Cmd_Maps
339
340 Only relevent for release and pak files.
341 Releases the .bsp files for the maps, and scans all of the files to
342 build a list of all textures used, which are then released.
343 ===============
344 */
Cmd_Maps(void)345 void Cmd_Maps (void)
346 {
347 char map[1024];
348 int i;
349
350 while (TokenAvailable ())
351 {
352 GetToken (false);
353 sprintf (map, "maps/%s.bsp", token);
354 ReleaseFile (map);
355
356 if (!g_release)
357 continue;
358
359 // get all the texture references
360 sprintf (map, "%smaps/%s.bsp", gamedir, token);
361 LoadBSPFileTexinfo (map);
362 for (i=0 ; i<numtexinfo ; i++)
363 ReleaseTexture (texinfo[i].texture);
364 }
365 }
366
367
368 //==============================================================
369
370 /*
371 ===============
372 ParseScript
373 ===============
374 */
ParseScript(void)375 void ParseScript (void)
376 {
377 while (1)
378 {
379 do
380 { // look for a line starting with a $ command
381 GetToken (true);
382 if (endofscript)
383 return;
384 if (token[0] == '$')
385 break;
386 while (TokenAvailable())
387 GetToken (false);
388 } while (1);
389
390 //
391 // model commands
392 //
393 if (!strcmp (token, "$modelname"))
394 Cmd_Modelname ();
395 else if (!strcmp (token, "$base"))
396 Cmd_Base ();
397 else if (!strcmp (token, "$cd"))
398 Cmd_Cd ();
399 else if (!strcmp (token, "$origin"))
400 Cmd_Origin ();
401 else if (!strcmp (token, "$scale"))
402 Cmd_ScaleUp ();
403 else if (!strcmp (token, "$frame"))
404 Cmd_Frame ();
405 else if (!strcmp (token, "$skin"))
406 Cmd_Skin ();
407 else if (!strcmp (token, "$skinsize"))
408 Cmd_Skinsize ();
409 //
410 // sprite commands
411 //
412 else if (!strcmp (token, "$spritename"))
413 Cmd_SpriteName ();
414 else if (!strcmp (token, "$load"))
415 Cmd_Load ();
416 else if (!strcmp (token, "$spriteframe"))
417 Cmd_SpriteFrame ();
418 //
419 // image commands
420 //
421 else if (!strcmp (token, "$grab"))
422 Cmd_Grab ();
423 else if (!strcmp (token, "$raw"))
424 Cmd_Raw ();
425 else if (!strcmp (token, "$colormap"))
426 Cmd_Colormap ();
427 else if (!strcmp (token, "$mippal"))
428 Cmd_Mippal ();
429 else if (!strcmp (token, "$mipdir"))
430 Cmd_Mipdir ();
431 else if (!strcmp (token, "$mip"))
432 Cmd_Mip ();
433 else if (!strcmp (token, "$environment"))
434 Cmd_Environment ();
435 //
436 // video
437 //
438 else if (!strcmp (token, "$video"))
439 Cmd_Video ();
440 //
441 // misc
442 //
443 else if (!strcmp (token, "$file"))
444 Cmd_File ();
445 else if (!strcmp (token, "$dir"))
446 Cmd_Dir ();
447 else if (!strcmp (token, "$maps"))
448 Cmd_Maps ();
449 else if (!strcmp (token, "$alphalight"))
450 Cmd_Alphalight ();
451 else if (!strcmp (token, "$inverse16table" ))
452 Cmd_Inverse16Table();
453 else
454 Error ("bad command %s\n", token);
455 }
456 }
457
458 //=======================================================
459
460 /*
461 ==============
462 main
463 ==============
464 */
main(int argc,char ** argv)465 int main (int argc, char **argv)
466 {
467 static int i; // VC4.2 compiler bug if auto...
468 char path[1024];
469
470 ExpandWildcards (&argc, &argv);
471
472 for (i=1 ; i<argc ; i++)
473 {
474 if (!strcmp(argv[i], "-archive"))
475 {
476 // -archive f:/quake2/release/dump_11_30
477 archive = true;
478 strcpy (archivedir, argv[i+1]);
479 printf ("Archiving source to: %s\n", archivedir);
480 i++;
481 }
482 else if (!strcmp(argv[i], "-release"))
483 {
484 g_release = true;
485 strcpy (g_releasedir, argv[i+1]);
486 printf ("Copy output to: %s\n", g_releasedir);
487 i++;
488 }
489 else if (!strcmp(argv[i], "-compress"))
490 {
491 g_compress_pak = true;
492 printf ("Compressing pakfile\n");
493 }
494 else if (!strcmp(argv[i], "-pak"))
495 {
496 g_release = true;
497 g_pak = true;
498 printf ("Building pakfile: %s\n", argv[i+1]);
499 BeginPak (argv[i+1]);
500 i++;
501 }
502 else if (!strcmp(argv[i], "-only"))
503 {
504 strcpy (g_only, argv[i+1]);
505 printf ("Only grabbing %s\n", g_only);
506 i++;
507 }
508 else if (!strcmp(argv[i], "-3ds"))
509 {
510 do3ds = true;
511 printf ("loading .3ds files\n");
512 }
513 else if (argv[i][0] == '-')
514 Error ("Unknown option \"%s\"", argv[i]);
515 else
516 break;
517 }
518
519 if (i >= argc)
520 Error ("usage: qgrab [-archive <directory>] [-release <directory>] [-only <model>] [-3ds] file.qgr");
521
522 if (do3ds)
523 trifileext = ext_3ds;
524 else
525 trifileext = ext_tri;
526
527 for ( ; i<argc ; i++)
528 {
529 printf ("--------------- %s ---------------\n", argv[i]);
530 // load the script
531 strcpy (path, argv[i]);
532 DefaultExtension (path, ".qdt");
533 SetQdirFromPath (path);
534 LoadScriptFile (ExpandArg(path));
535
536 //
537 // parse it
538 //
539 ParseScript ();
540
541 // write out the last model
542 FinishModel ();
543 FinishSprite ();
544 }
545
546 if (g_pak)
547 FinishPak ();
548
549 return 0;
550 }
551
552