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 #include "qdata.h"
23 
24 void TK_Init();
25 
26 qboolean	g_compress_pak;
27 qboolean	g_release;			// don't grab, copy output data to new tree
28 qboolean	g_pak;				// if true, copy to pak instead of release
29 char		g_releasedir[1024];	// c:\quake2\baseq2, etc
30 qboolean	g_archive;			// don't grab, copy source data to new tree
31 qboolean	do3ds;
32 char		g_only[256];		// if set, only grab this cd
33 qboolean	g_skipmodel;		// set true when a cd is not g_only
34 int			g_forcemodel = MODEL_AUTO;
35 qboolean	g_verbose = false;
36 qboolean	g_allow_newskin = true;
37 qboolean	g_ignoreTriUV = false;
38 qboolean	g_publishOutput = false;
39 
40 char		*ext_3ds = "3ds";
41 char		*ext_tri= "tri";
42 char		*trifileext;
43 
44 char		g_materialFile[256] = "none";	// default for Heretic2
45 char		*g_outputDir;
46 extern char *g_publishDir;
47 
48 extern qboolean	g_nomkdir;
49 
50 /*
51 =======================================================
52 
53   PAK FILES
54 
55 =======================================================
56 */
57 
58 unsigned Com_BlockChecksum (void *buffer, int length);
59 
60 typedef struct
61 {
62 	char	name[56];
63 	int		filepos, filelen;
64 } packfile_t;
65 
66 typedef struct
67 {
68 	char	id[4];
69 	int		dirofs;
70 	int		dirlen;
71 } packheader_t;
72 
73 packfile_t		pfiles[16384];
74 FILE			*pakfile;
75 packfile_t		*pf;
76 packheader_t	pakheader;
77 
78 
79 
80 /*
81 ==============
82 BeginPak
83 ==============
84 */
BeginPak(char * outname)85 void BeginPak (char *outname)
86 {
87 	if (!g_pak)
88 		return;
89 
90 	pakfile = SafeOpenWrite (outname);
91 
92 	// leave space for header
93 	SafeWrite (pakfile, &pakheader, sizeof(pakheader));
94 
95 	pf = pfiles;
96 }
97 
98 
99 /*
100 ==============
101 ReleaseFile
102 
103 Filename should be gamedir reletive.
104 Either copies the file to the release dir, or adds it to
105 the pak file.
106 ==============
107 */
ReleaseFile(char * filename)108 void ReleaseFile (char *filename)
109 {
110 	int		len;
111 	byte	*buf;
112 	char	source[1024];
113 	char	dest[1024];
114 
115 	if (!g_release)
116 		return;
117 
118 	sprintf (source, "%s%s", gamedir, filename);
119 
120 	if (!g_pak)
121 	{	// copy it
122 		sprintf (dest, "%s/%s", g_releasedir, filename);
123 		printf ("copying to %s\n", dest);
124 		QCopyFile (source, dest);
125 		return;
126 	}
127 
128 	// pak it
129 	printf ("paking %s\n", filename);
130 	if (strlen(filename) >= sizeof(pf->name))
131 		Error ("Filename too long for pak: %s", filename);
132 
133 	len = LoadFile (source, (void **)&buf);
134 
135 	// segment moved to old.c
136 
137 	strcpy (pf->name, filename);
138 	pf->filepos = LittleLong(ftell(pakfile));
139 	pf->filelen = LittleLong(len);
140 	pf++;
141 
142 	SafeWrite (pakfile, buf, len);
143 
144 	free (buf);
145 }
146 
147 
148 /*
149 ==============
150 FinishPak
151 ==============
152 */
FinishPak(void)153 void FinishPak (void)
154 {
155 	int		dirlen;
156 	int		d;
157 	int		i;
158 	unsigned	checksum;
159 
160 	if (!g_pak)
161 		return;
162 
163 	pakheader.id[0] = 'P';
164 	pakheader.id[1] = 'A';
165 	pakheader.id[2] = 'C';
166 	pakheader.id[3] = 'K';
167 	dirlen = (byte *)pf - (byte *)pfiles;
168 	pakheader.dirofs = LittleLong(ftell(pakfile));
169 	pakheader.dirlen = LittleLong(dirlen);
170 
171 	checksum = Com_BlockChecksum ( (void *)pfiles, dirlen );
172 
173 	SafeWrite (pakfile, pfiles, dirlen);
174 
175 	i = ftell (pakfile);
176 
177 	fseek (pakfile, 0, SEEK_SET);
178 	SafeWrite (pakfile, &pakheader, sizeof(pakheader));
179 	fclose (pakfile);
180 
181 	d = pf - pfiles;
182 	printf ("%i files packed in %i bytes\n",d, i);
183 	printf ("checksum: 0x%x\n", checksum);
184 }
185 
186 
187 /*
188 ===============
189 Cmd_File
190 
191 This is only used to cause a file to be copied during a release
192 build (default.cfg, maps, etc)
193 ===============
194 */
Cmd_File(void)195 void Cmd_File (void)
196 {
197 	GetScriptToken (false);
198 	ReleaseFile (token);
199 }
200 
201 /*
202 ===============
203 PackDirectory_r
204 
205 ===============
206 */
207 #ifdef _WIN32
208 #include "io.h"
PackDirectory_r(char * dir)209 void PackDirectory_r (char *dir)
210 {
211 	struct _finddata_t fileinfo;
212 	int		handle;
213 	char	dirstring[1024];
214 	char	filename[1024];
215 
216 	sprintf (dirstring, "%s%s/*.*", gamedir, dir);
217 
218 	handle = _findfirst (dirstring, &fileinfo);
219 	if (handle == -1)
220 		return;
221 
222 	do
223 	{
224 		sprintf (filename, "%s/%s", dir, fileinfo.name);
225 		if (fileinfo.attrib & _A_SUBDIR)
226 		{	// directory
227 			if (fileinfo.name[0] != '.')	// don't pak . and ..
228 				PackDirectory_r (filename);
229 			continue;
230 		}
231 		// copy or pack the file
232 		ReleaseFile (filename);
233 	} while (_findnext( handle, &fileinfo ) != -1);
234 
235 	_findclose (handle);
236 }
237 #else
238 
239 #include <sys/types.h>
240 #ifdef NeXT
241 #include <sys/dir.h>
242 #else
243 #include <dirent.h>
244 #endif
245 
PackDirectory_r(char * dir)246 void PackDirectory_r (char *dir)
247 {
248 #ifdef NeXT
249 	struct direct **namelist, *ent;
250 #else
251 	struct dirent **namelist, *ent;
252 #endif
253 	int		count;
254 	struct stat st;
255 	int			i;
256 	int			len;
257 	char		fullname[1024];
258 	char		dirstring[1024];
259 	char		*name;
260 
261 	sprintf (dirstring, "%s%s", gamedir, dir);
262 	count = scandir(dirstring, &namelist, NULL, NULL);
263 
264 	for (i=0 ; i<count ; i++)
265 	{
266 		ent = namelist[i];
267 		name = ent->d_name;
268 
269 		if (name[0] == '.')
270 			continue;
271 
272 		sprintf (fullname, "%s/%s", dir, name);
273 		sprintf (dirstring, "%s%s/%s", gamedir, dir, name);
274 
275 		if (stat (dirstring, &st) == -1)
276 			Error ("fstating %s", pf->name);
277 		if (st.st_mode & S_IFDIR)
278 		{	// directory
279 			PackDirectory_r (fullname);
280 			continue;
281 		}
282 
283 		// copy or pack the file
284 		ReleaseFile (fullname);
285 	}
286 }
287 #endif
288 
289 
290 /*
291 ===============
292 Cmd_Dir
293 
294 This is only used to cause a directory to be copied during a
295 release build (sounds, etc)
296 ===============
297 */
Cmd_Dir(void)298 void Cmd_Dir (void)
299 {
300 	GetScriptToken (false);
301 	PackDirectory_r (token);
302 }
303 
304 //========================================================================
305 
306 #define	MAX_RTEX	16384
307 int		numrtex;
308 char	rtex[MAX_RTEX][64];
309 
ReleaseTexture(char * name)310 void ReleaseTexture (char *name)
311 {
312 	int		i;
313 	char	path[1024];
314 
315 	for (i=0 ; i<numrtex ; i++)
316 		if (!Q_strcasecmp(name, rtex[i]))
317 			return;
318 
319 	if (numrtex == MAX_RTEX)
320 		Error ("numrtex == MAX_RTEX");
321 
322 	strcpy (rtex[i], name);
323 	numrtex++;
324 
325 	sprintf (path, "textures/%s.wal", name);
326 	ReleaseFile (path);
327 }
328 
329 /*
330 ===============
331 Cmd_Maps
332 
333 Only relevent for release and pak files.
334 Releases the .bsp files for the maps, and scans all of the files to
335 build a list of all textures used, which are then released.
336 ===============
337 */
Cmd_Maps(void)338 void Cmd_Maps (void)
339 {
340 	char	map[1024];
341 	int		i;
342 
343 	while (ScriptTokenAvailable ())
344 	{
345 		GetScriptToken (false);
346 		sprintf (map, "maps/%s.bsp", token);
347 		ReleaseFile (map);
348 
349 		if (!g_release)
350 			continue;
351 
352 		// get all the texture references
353 		sprintf (map, "%smaps/%s.bsp", gamedir, token);
354 		LoadBSPFileTexinfo (map);
355 		for (i=0 ; i<numtexinfo ; i++)
356 			ReleaseTexture (texinfo[i].texture);
357 	}
358 }
359 
360 
361 //==============================================================
362 
363 /*
364 ===============
365 ParseScript
366 ===============
367 */
ParseScript(void)368 void ParseScript (void)
369 {
370 	while (1)
371 	{
372 		do
373 		{	// look for a line starting with a $ command
374 			GetScriptToken (true);
375 			if (endofscript)
376 				return;
377 			if (token[0] == '$')
378 				break;
379 			while (ScriptTokenAvailable())
380 				GetScriptToken (false);
381 		} while (1);
382 
383 		//
384 		// model commands
385 		//
386 		if (!strcmp (token, "$modelname"))
387 			MODELCMD_Modelname (MODEL_MD2);
388 		else if (!strcmp (token, "$cd"))
389 			MODELCMD_Cd (MODEL_MD2);
390 		else if (!strcmp (token, "$origin"))
391 			MODELCMD_Origin (MODEL_MD2);
392 		else if (!strcmp (token, "$cluster"))
393 			MODELCMD_Cluster (MODEL_MD2);
394 		else if (!strcmp (token, "$base"))
395 			MODELCMD_Base (MODEL_MD2);
396 		else if (!strcmp (token, "$scale"))
397 			MODELCMD_ScaleUp (MODEL_MD2);
398 		else if (!strcmp (token, "$frame"))
399 			MODELCMD_Frame (MODEL_MD2);
400 		else if (!strcmp (token, "$skin"))
401 			MODELCMD_Skin (MODEL_MD2);
402 		else if (!strcmp (token, "$skinsize"))
403 			MODELCMD_Skinsize (MODEL_MD2);
404 		//
405 		// flexible model commands
406 		//
407 		else if (!strcmp (token, "$fm_modelname"))
408 			MODELCMD_Modelname (MODEL_FM);
409 		else if (!strcmp (token, "$fm_base"))
410 			MODELCMD_Base (MODEL_FM);
411 		else if (!strcmp (token, "$fm_basest"))
412 			MODELCMD_BaseST (MODEL_FM);
413 		else if (!strcmp (token, "$fm_cd"))
414 			MODELCMD_Cd (MODEL_FM);
415 		else if (!strcmp (token, "$fm_origin"))
416 			MODELCMD_Origin (MODEL_FM);
417 		else if (!strcmp (token, "$fm_cluster"))
418 			MODELCMD_Cluster (MODEL_FM);
419 		else if (!strcmp (token, "$fm_skeleton"))
420 			MODELCMD_Skeleton (MODEL_FM);
421 		else if (!strcmp (token, "$fm_scale"))
422 			MODELCMD_ScaleUp (MODEL_FM);
423 		else if (!strcmp (token, "$fm_frame"))
424 			MODELCMD_Frame (MODEL_FM);
425 		else if (!strcmp (token, "$fm_skeletal_frame"))	// left in for compadibility with qdt already using fm_skeletal_frame
426 			MODELCMD_Frame (MODEL_FM);
427 		else if (!strcmp (token, "$fm_skin"))
428 			MODELCMD_Skin (MODEL_FM);
429 		else if (!strcmp (token, "$fm_skinsize"))
430 			MODELCMD_Skinsize (MODEL_FM);
431 		else if (!strcmp (token, "$fm_begin_group"))
432 			MODELCMD_BeginGroup(MODEL_FM);
433 		else if (!strcmp (token, "$fm_end_group"))
434 			MODELCMD_EndGroup(MODEL_FM);
435 		else if (!strcmp (token, "$fm_referenced"))
436 			MODELCMD_Referenced(MODEL_FM);
437 		else if (!strcmp (token, "$fm_node_order"))
438 			MODELCMD_NodeOrder(MODEL_FM);
439 
440 		//
441 		// sprite commands
442 		//
443 		else if (!strcmp (token, "$spritename"))
444 			Cmd_SpriteName ();
445 		else if (!strcmp (token, "$sprdir"))
446 			Cmd_Sprdir ();
447 		else if (!strcmp (token, "$load"))
448 			Cmd_Load ();
449 		else if (!strcmp (token, "$spriteframe"))
450 			Cmd_SpriteFrame ();
451 		//
452 		// image commands
453 		//
454 		else if (!strcmpi (token, "$grab"))
455 			Cmd_Grab ();
456 		else if (!strcmpi (token, "$raw"))
457 			Cmd_Raw ();
458 		else if (!strcmpi (token, "$colormap"))
459 			Cmd_Colormap ();
460 		else if (!strcmpi (token, "$mippal"))
461 			Cmd_Mippal ();
462 		else if (!strcmpi (token, "$mipdir"))
463 			Cmd_Mipdir ();
464 		else if (!strcmpi (token, "$mip"))
465 			Cmd_Mip ();
466 		else if (!strcmp (token, "$environment"))
467 			Cmd_Environment ();
468 		//
469 		// pics
470 		//
471 		else if (!strcmp (token, "$picdir"))
472 			Cmd_Picdir ();
473 		else if (!strcmp (token, "$pic"))
474 			Cmd_Pic ();
475 		//
476 		// book
477 		//
478 		else if (!strcmp (token, "$bookdir"))
479 			Cmd_Bookdir ();
480 		else if (!strcmp (token, "$book"))
481 			Cmd_Book ();
482 		//
483 		// tmix
484 		//
485 		else if (!strcmp (token, "$texturemix"))
486 			Cmd_TextureMix ();
487 		//
488 		// video
489 		//
490 		else if (!strcmp (token, "$video"))
491 			Cmd_Video ();
492 		//
493 		// misc
494 		//
495 		else if (!strcmp (token, "$file"))
496 			Cmd_File ();
497 		else if (!strcmp (token, "$dir"))
498 			Cmd_Dir ();
499 		else if (!strcmp (token, "$maps"))
500 			Cmd_Maps ();
501 		else if (!strcmp (token, "$alphalight"))
502 			Cmd_Alphalight ();
503 		else if (!strcmp (token, "$inverse16table" ))
504 			Cmd_Inverse16Table();
505 		else
506 			Error ("bad command %s\n", token);
507 	}
508 }
509 
510 //=======================================================
511 
512 /*
513 ==============
514 main
515 ==============
516 */
main(int argc,char ** argv)517 int main (int argc, char **argv)
518 {
519 	int			i;
520 	char		path[1024];
521 	char		*basedir;
522 	double		starttime, endtime;
523 
524 	printf ("Qdata Plus : "__TIME__" "__DATE__"\n");
525 
526 	starttime = I_FloatTime();
527 	basedir = NULL;
528 
529 	TK_Init();
530 	ExpandWildcards (&argc, &argv);
531 
532 	for (i=1 ; i<argc ; i++)
533 	{
534 		if (!strcmp(argv[i], "-archive"))
535 		{
536 			// -archive f:/quake2/release/dump_11_30
537 			archive = true;
538 			strcpy (archivedir, argv[i+1]);
539 			printf ("Archiving source to: %s\n", archivedir);
540 			i++;
541 		}
542 		else if (!strcmp(argv[i], "-release"))
543 		{
544 			g_release = true;
545 			strcpy (g_releasedir, argv[i+1]);
546 			printf ("Copy output to: %s\n", g_releasedir);
547 			i++;
548 		}
549 		else if (!strcmp(argv[i], "-base"))
550 		{
551 			i++;
552 			basedir = argv[i];
553 		}
554 		else if (!strcmp(argv[i], "-compress"))
555 		{
556 			g_compress_pak = true;
557 			printf ("Compressing pakfile\n");
558 		}
559 		else if (!strcmp(argv[i], "-pak"))
560 		{
561 			g_release = true;
562 			g_pak = true;
563 			printf ("Building pakfile: %s\n", argv[i+1]);
564 			BeginPak (argv[i+1]);
565 			i++;
566 		}
567 		else if (!strcmp(argv[i], "-only"))
568 		{
569 			strcpy (g_only, argv[i+1]);
570 			printf ("Only grabbing %s\n", g_only);
571 			i++;
572 		}
573 		else if (!strcmpi(argv[i], "-keypress"))
574 		{
575 			g_dokeypress = true;
576 		}
577 		else if (!strcmp(argv[i], "-3ds"))
578 		{
579 			do3ds = true;
580 			printf ("loading .3ds files\n");
581 		}
582 		else if (!strcmp(argv[i], "-materialfile"))
583 		{
584 			strcpy(g_materialFile, argv[i+1]);
585 			printf("Setting material file to %s\n", g_materialFile);
586 			i++;
587 		}
588 /*		else if (!strcmpi(argv[i], "-newgen"))
589 		{
590 			if (i < argc-4)
591 			{
592 				printf("run new triangle grouping routine here\n");
593 				NewGen(argv[i+1],argv[i+2],atoi(argv[i+3]),atoi(argv[i+4]));
594 			}
595 			else
596 			{
597 				printf("qdata -newskin <base.hrc> <skin.pcx> width height\n");
598 			}
599 			return 0;
600 		}
601 */		else if (!strcmpi(argv[i], "-genskin"))
602 		{
603 			i++;
604 			if (i < argc-3)
605 			{
606 				GenSkin(argv[i],argv[i+1],atol(argv[i+2]),atol(argv[i+3]));
607 			}
608 			else
609 			{
610 				printf("qdata -genskin <base.hrc> <skin.pcx> <desired width> <desired height>\n");
611 			}
612 			return 0;
613 
614 		}
615 		else if (!strcmpi(argv[i], "-noopts"))
616 		{
617 			g_no_opimizations = true;
618 			printf("not performing optimizations\n");
619 		}
620 		else if (!strcmpi(argv[i], "-md2"))
621 		{
622 			g_forcemodel = MODEL_MD2;
623 		}
624 		else if (!strcmpi(argv[i], "-fm"))
625 		{
626 			g_forcemodel = MODEL_FM;
627 		}
628 		else if (!strcmpi(argv[i], "-verbose"))
629 		{
630 			g_verbose = true;
631 		}
632 		else if (!strcmpi(argv[i], "-oldskin"))
633 		{
634 			g_allow_newskin = false;
635 		}
636 		else if (!strcmpi(argv[i], "-ignoreUV"))
637 		{
638 			g_ignoreTriUV = true;
639 		}
640 		else if (!strcmpi(argv[i], "-publish"))
641 		{
642 			g_publishOutput = true;
643 		}
644 		else if (!strcmpi(argv[i], "-nomkdir"))
645 		{
646 			g_nomkdir = true;
647 		}
648 		else if (argv[i][0] == '-')
649 			Error ("Unknown option \"%s\"", argv[i]);
650 		else
651 			break;
652 	}
653 
654 	if (i >= argc)
655 	{
656 		Error ("usage: qdata [-archive <directory>]\n"
657 			"             [-release <directory>]\n"
658 			"             [-base <directory>]\n"
659 			"             [-compress]\n"
660 			"             [-pak <file>]\n"
661 			"             [-only <model>]\n"
662 			"             [-keypress]\n"
663 			"             [-3ds]\n"
664 			"             [-materialfile <file>]\n"
665 			"             [-noopts]\n"
666 			"             [-md2]\n"
667 			"             [-fm]\n"
668 			"             [-verbose]\n"
669 			"             [-ignoreUV]\n"
670 			"             [-oldskin]\n"
671 			"             [-publish]\n"
672 			"             [-nomkdir]\n"
673 			"             file.qdt\n"
674 			"or\n"
675 			"       qdata -genskin <base.hrc> <skin.pcx> <desired width> <desired height>");
676 	}
677 
678 	if (do3ds)
679 		trifileext = ext_3ds;
680 	else
681 		trifileext = ext_tri;
682 
683 	for ( ; i<argc ; i++)
684 	{
685 		printf ("--------------- %s ---------------\n", argv[i]);
686 		// load the script
687 		strcpy (path, argv[i]);
688 		DefaultExtension (path, ".qdt");
689 		DefaultExtension(g_materialFile, ".mat");
690 		SetQdirFromPath (path);
691 
692 		printf("workingdir='%s'\n", gamedir);
693 		if (basedir)
694 		{
695 			qdir[0] = 0;
696 			g_outputDir = basedir;
697 		}
698 
699 		printf("outputdir='%s'\n", g_outputDir);
700 
701 		QFile_ReadMaterialTypes(g_materialFile);
702 		LoadScriptFile (ExpandArg(path));
703 
704 		//
705 		// parse it
706 		//
707 		ParseScript ();
708 
709 		// write out the last model
710 		FinishModel ();
711 		FMFinishModel ();
712 		FinishSprite ();
713 	}
714 
715 	if (total_textures)
716 	{
717 		printf("\n");
718 		printf("Total textures processed: %d\n",total_textures);
719 		printf("Average size: %d x %d\n",total_x / total_textures, total_y / total_textures);
720 	}
721 
722 	if (g_pak)
723 		FinishPak ();
724 
725 	endtime = I_FloatTime();
726 	printf("Time elapsed:  %f\n", endtime-starttime);
727 
728 	if (g_dokeypress)
729 	{
730 		printf("Success! ... Hit a key: ");
731 		getchar();
732 	}
733 
734 	return 0;
735 }
736 
737