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