1 #include "dzip.h"
2 #include "dzipcon.h"
3 
4 char flag[NUM_SWITCHES];
5 char AbortOp, *dzname;
6 uInt dzsize;
7 FILE *infile, *outfile, *dzfile;
8 
9 /* read from file being compressed */
Infile_Read(void * buf,uInt num)10 void Infile_Read (void *buf, uInt num)
11 {
12 	if (fread(buf, 1, num, infile) != num)
13 	{
14 		error(": error reading from input: %s", strerror(errno));
15 		AbortOp = 3;
16 	}
17 	make_crc(buf, num);
18 }
19 
20 /* change file pointer in file being compressed (used for .pak) */
Infile_Seek(uInt dest)21 void Infile_Seek (uInt dest)
22 {
23 	fseek(infile, dest, SEEK_SET);
24 }
25 
26 /* called when compression was unsuccessful (negative ratio) */
Infile_Store(uInt size)27 void Infile_Store (uInt size)
28 {
29 	void *buf = Dzip_malloc(32768);
30 	int s;
31 
32 	Infile_Seek(ftell(infile) - size);
33 	while (size && !AbortOp)
34 	{
35 		s = (size > 32768) ? 32768 : size;
36 		Infile_Read(buf, s);
37 		dzFile_Write(buf, s);
38 		size -= s;
39 	}
40 	free(buf);
41 }
42 
43 /* write to the file being extracted */
Outfile_Write(void * buf,uInt num)44 void Outfile_Write (void *buf, uInt num)
45 {
46 	if (!flag[SW_VERIFY])
47 	if (fwrite(buf, 1, num, outfile) != num)
48 	{
49 		error(": error writing to output: %s", strerror(errno));
50 		AbortOp = 1;
51 	}
52 	make_crc(buf, num);
53 }
54 
55 /* read from dz */
dzFile_Read(void * buf,uInt num)56 void dzFile_Read (void *buf, uInt num)
57 {
58 	if (fread(buf, 1, num, dzfile) != num)
59 	{
60 		error(": error reading from %s: %s", dzname, strerror(errno));
61 		AbortOp = 1;
62 	}
63 }
64 
65 /* write to dz */
dzFile_Write(void * buf,uInt num)66 void dzFile_Write (void *buf, uInt num)
67 {
68 	if (fwrite(buf, 1, num, dzfile) != num)
69 	{
70 		error(": error writing to %s: %s", dzname, strerror(errno));
71 		AbortOp = 1;
72 	}
73 }
74 
75 /*	get filesize of dz, returns zero if it could not
76 	be determined, most likely because it was >= 4GB */
dzFile_Size(void)77 uInt dzFile_Size(void)
78 {	/* size was determined in DoFiles */
79 	return dzsize;
80 }
81 
82 /* change file pointer of dz */
dzFile_Seek(uInt dest)83 void dzFile_Seek (uInt dest)
84 {
85 #ifdef _MSC_VER
86 	/* do this so 2-4GB files will work for windows version */
87 	int hi = 0;
88 	fseek(dzfile, 0, SEEK_CUR); /* can't just seek out from under crt's nose */
89 	SetFilePointer(_get_osfhandle(fileno(dzfile)), dest, &hi, FILE_BEGIN);
90 #else
91 	fseek(dzfile, dest, SEEK_SET);
92 #endif
93 }
94 
95 /* chop off end of dz; this is implementation dependent
96    and may need adjusting for other platforms */
dzFile_Truncate(void)97 void dzFile_Truncate(void)
98 {
99 #ifdef _MSC_VER
100 	SetEndOfFile(_get_osfhandle(fileno(dzfile)));
101 #else
102 	ftruncate(fileno(dzfile), ftell(dzfile));
103 #endif
104 }
105 
106 /* free directory and close current dz */
dzClose(void)107 void dzClose(void)
108 {
109 	int i;
110 	for (i = 0; i < numfiles; free(directory[i++].name));
111 	free(directory);
112 	fclose(dzfile);
113 }
114 
115 /* open a dz file, return 0 on failure */
dzOpen(char * src,int needwrite)116 int dzOpen (char *src, int needwrite)
117 {
118 	dzname = src;
119 	dzfile = fopen(src, needwrite ? "rb+" : "rb");
120 	if (!dzfile)
121 	{
122 		error(needwrite ? "could not open %s for writing: %s"
123 			: "could not open %s: %s", src, strerror(errno));
124 		return 0;
125 	}
126 
127 	if (dzReadDirectory(src))
128 		return 1;
129 
130 	fclose(dzfile);
131 	return 0;
132 }
133 
open_create(char * src)134 FILE *open_create (char *src)
135 {
136 	FILE *f;
137 
138 	if (!flag[SW_FORCE])
139 		if ((f = fopen(src, "r")))
140 		{
141 			error("%s exists; will not overwrite", src);
142 			return NULL;
143 		}
144 
145 	f = fopen(src,"wb+");
146 	if (!f) error("could not create %s: %s",src, strerror(errno));
147 	return f;
148 }
149 
150 /* safe set of allocation functions */
Dzip_malloc(uInt size)151 void *Dzip_malloc (uInt size)
152 {
153 	void *m = malloc(size);
154 	if (!m)	errorFatal("Out of memory!");
155 	return m;
156 }
157 
Dzip_realloc(void * ptr,uInt size)158 void *Dzip_realloc (void *ptr, uInt size)
159 {
160 	void *m = realloc(ptr, size);
161 	if (!m)	errorFatal("Out of memory!");
162 	return m;
163 }
164 
Dzip_strdup(const char * str)165 char *Dzip_strdup (const char *str)
166 {
167 	char *m = strdup(str);
168 	if (!m)	errorFatal("Out of memory!");
169 	return m;
170 }
171 
Dzip_calloc(uInt size)172 void *Dzip_calloc (uInt size)
173 {
174 	return memset(Dzip_malloc(size), 0, size);
175 }
176 
177 #if BYTE_ORDER == BIG_ENDIAN
178 /* byte swapping on big endian machines */
getshort(uchar * c)179 short getshort (uchar *c)
180 {
181 	return (c[1] << 8) + c[0];
182 }
183 
getlong(uchar * c)184 long getlong (uchar *c)
185 {
186 	return (c[3] << 24) + (c[2] << 16) + (c[1] << 8) + c[0];
187 }
188 
getfloat(uchar * c)189 float getfloat (uchar *c)
190 {
191 	long l = getlong(c);
192 	return *(float*)(&l);
193 }
194 
195 #endif
196 
197 /* does not return if -e was specified on cmd line */
error(const char * msg,...)198 void error (const char *msg, ...)
199 {
200 	va_list	the_args;
201 	va_start(the_args,msg);
202 	if (flag[SW_HALT]) {
203 		if (*msg == ':')
204 		{
205 			fprintf(stderr, "\nERROR: ");
206 			msg += 2;
207 		}
208 		else
209 			fprintf(stderr,"ERROR: ");
210 	} /* stupid compilers bitch about ambigious else */
211 
212 	vfprintf(stderr,msg,the_args);
213 	fprintf(stderr,"\n");
214 	va_end(the_args);
215 	if (flag[SW_HALT])
216 		exit(1);
217 }
218 
219 /* does not return ever */
errorFatal(const char * msg,...)220 void errorFatal (const char *msg, ...)
221 {
222 	va_list	the_args;
223 	va_start(the_args,msg);
224 	fprintf(stderr,"ERROR: ");
225 	vfprintf(stderr,msg,the_args);
226 	fprintf(stderr,"\n");
227 	va_end(the_args);
228 	exit(1);
229 }
230 
231 struct { char *name; int flag; }
232 optname[] =
233 	{ { "-l", SW_LIST },
234 	  { "-x", SW_EXTRACT },
235 	  { "-v", SW_VERIFY },
236 	  { "-a", SW_ADD },
237 	  { "-d", SW_DELETE },
238 	  { "-f", SW_FORCE },
239 	  { "-e", SW_HALT },
240 	  { NULL, 0 }
241 	};
242 
parse_switch(char * arg)243 void parse_switch (char *arg)
244 {
245 	int i;
246 
247 	if (strlen(arg) == 2 && arg[1] >= '0' && arg[1] <= '9')
248 	{
249 		zlevel = arg[1] - '0';
250 		return;
251 	}
252 
253 	for (i = 0; optname[i].name; i++)
254 		if (!strcmp(optname[i].name,arg))
255 		{
256 			flag[optname[i].flag]++;
257 			return;
258 		}
259 	errorFatal("unknown switch %s",arg);
260 }
261 
usage(void)262 void usage(void)
263 {
264     fprintf(stderr,
265 	"Dzip v%u.%u: specializing in Quake demo compression\n\n"
266 
267 	"Compression:         dzip <filenames> [-o <outputfile>]\n"
268 	"Add to existing dz:  dzip -a <dzfile> <filenames>\n"
269 	"Delete from a dz:    dzip -d <dzfile> <filenames>\n"
270 	"Decompression:       dzip -x <filenames>\n"
271 	"Verify dz file:      dzip -v <filenames>\n"
272 	"List dz file:        dzip -l <filenames>\n\n"
273 
274 	"Options:\n"
275 	"  -0 to -9: set compression level\n"
276 	"  -e: quit program on first error\n"
277 	"  -f: overwrite existing files\n\n"
278 
279 	"Copyright 2000-2002 Stefan Schwoon, Nolan Pflug\n",
280 	MAJOR_VERSION, MINOR_VERSION
281     );
282 
283     exit(1);
284 }
285 
286 void DoFiles (char *fname, void (*func)(char *));
287 
DoDirectory(char * dname)288 void DoDirectory (char *dname)
289 {
290 	char fullname[260], *ptr;
291 
292 	if (!strcmp(dname, ".") || !strcmp(dname, ".."))
293 		return;
294 	ptr = &fullname[sprintf(fullname, "%s\\", dname)];
295 	dzAddFolder(fullname);
296 
297 	/* scan directory, more system specific stuff */
298 	{
299 #ifdef WIN32
300 	HANDLE f;
301 	WIN32_FIND_DATA fd;
302 
303 	*(short *)ptr = '*';
304 	f = FindFirstFile(fullname, &fd);
305 	if (f == INVALID_HANDLE_VALUE)
306 		return;	/* happens if user doesn't have browse permission */
307 
308 	do {
309 		if (!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, ".."))
310 			continue;
311 		strcpy(ptr, fd.cFileName);
312 		DoFiles(fullname, NULL);
313 	} while (!AbortOp && FindNextFile(f, &fd));
314 
315 	FindClose(f);
316 #else
317 	struct dirent *e;
318 	DIR *d = opendir(dname);
319 	if (!d) return;
320 	ptr[-1] = '/';
321 
322 	while (!AbortOp && (e = readdir(d)))
323 	{
324 		if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
325 			continue;
326 		strcpy(ptr, e->d_name);
327 		DoFiles(fullname, NULL);
328 	}
329 
330 	closedir(d);
331 #endif
332 	}
333 }
334 
DoFiles(char * fname,void (* func)(char *))335 void DoFiles (char *fname, void (*func)(char *))
336 {
337 	uInt filetime;
338 
339 /* handle wildcards for Win32, assume that
340    other systems will handle them for me! */
341 #ifdef WIN32
342 	HANDLE f;
343 	WIN32_FIND_DATA fd;
344 	char fullname[260];
345 	int p = GetFileFromPath(fname) - fname;
346 
347 	f = FindFirstFile(fname, &fd);
348 	if (f == INVALID_HANDLE_VALUE)
349 	{	/* file not found, try adding .dem if no wildcards */
350 		if (!*FileExtension(fname) && !strpbrk(fname, "*?"))
351 		{
352 			strcat(fname, ".dem");
353 			f = FindFirstFile(fname, &fd);
354 			if (f == INVALID_HANDLE_VALUE)
355 				*FileExtension(fname) = 0;
356 		}
357 		if (f == INVALID_HANDLE_VALUE)
358 		{
359 			error("could not open %s", fname);
360 			return;
361 		}
362 	}
363 
364 	do
365 	{
366 		strncpy(fullname, fname, p);
367 		strcpy(fullname + p, fd.cFileName);
368 
369 		if (func)
370 		{	/* either dzList or dzUncompress */
371 			if (fd.nFileSizeHigh)
372 				dzsize = 0;
373 			else
374 				dzsize = fd.nFileSizeLow;
375 			func(fullname);
376 			continue;
377 		}
378 
379 		if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
380 		{
381 			DoDirectory(fullname);
382 			continue;
383 		}
384 
385 		/* cant add the current file to itself */
386 		if (!strcasecmp(fullname, dzname))
387 			continue;
388 
389 		if (fd.nFileSizeHigh)
390 		{
391 			error("%s is 4GB or more and can't be compressed", fullname);
392 			return;
393 		}
394 
395 		infile = fopen(fullname, "rb");
396 		if (!infile)
397 		{
398 			error("could not open %s: %s", fullname, strerror(errno));
399 			continue;
400 		}
401 
402 		FileTimeToLocalFileTime(&fd.ftLastWriteTime, &fd.ftLastAccessTime);
403 		FileTimeToDosDateTime(&fd.ftLastAccessTime, (short *)&filetime + 1, (short *)&filetime);
404 
405 		dzCompressFile(fullname, fd.nFileSizeLow, filetime - (1 << 21));
406 		fclose(infile);
407 		if (AbortOp == 3)	/* had a problem reading from infile */
408 			AbortOp = 0;	/* but still try the rest of the files */
409 
410 	} while (!AbortOp && FindNextFile(f, &fd));
411 	FindClose(f);
412 
413 #else
414 
415 	struct stat64 filestats;
416 	struct tm *trec;
417 
418 	if (stat64(fname, &filestats))
419 	{	/* file probably doesn't exist if this failed */
420 		error("could not open %s: %s", fname, strerror(errno));
421 		return;
422 	}
423 
424 	if (func)
425 	{	/* either dzList or dzUncompress */
426 		if (filestats.st_size > 0xFFFFFFFF)
427 			dzsize = 0;
428 		else
429 			dzsize = filestats.st_size;
430 		func(fname);
431 		return;
432 	}
433 
434 	if (filestats.st_mode & S_IFDIR)
435 	{
436 		DoDirectory(fname);
437 		return;
438 	}
439 
440 	/* cant add the current file to itself */
441 	if (!strcasecmp(fname, dzname))
442 		return;
443 
444 	if (filestats.st_size > 0xFFFFFFFF)
445 	{
446 		error("%s is 4GB or more and can't be compressed", fname);
447 		return;
448 	}
449 
450 	infile = fopen(fname, "rb");
451 	if (!infile)
452 	{
453 		error("could not open %s: %s", fname, strerror(errno));
454 		return;
455 	}
456 
457 	trec = localtime(&filestats.st_mtime);
458 	filetime = (trec->tm_sec >> 1) + (trec->tm_min << 5)
459 		+ (trec->tm_hour << 11) + (trec->tm_mday << 16)
460 		+ (trec->tm_mon << 21) + ((trec->tm_year - 80) << 25);
461 
462 	dzCompressFile(fname, filestats.st_size, filetime);
463 	fclose(infile);
464 	if (AbortOp == 3)	/* had a problem reading from infile */
465 		AbortOp = 0;	/* but still try the rest of the files */
466 
467 #endif
468 }
469 
main(int argc,char ** argv)470 int main(int argc, char **argv)
471 {
472 	int i, j, oflag = 0;
473 	int fileargs = 0;
474 	char *optr = NULL;
475 	char **files;
476 	char *fname;
477 
478 	if (argc < 2) usage();
479 
480 	for (i = 1; i < argc; i++)
481 		if (!strcmp(argv[i],"-o"))
482 			oflag = 1;
483 		else if (*argv[i] == '-')
484 			parse_switch(argv[i]);
485 		else if (oflag)
486 			optr = argv[i];
487 		else
488 			fileargs++;
489 
490 	if (!fileargs) errorFatal("no input files");
491 	j = !!flag[SW_LIST] + !!flag[SW_EXTRACT]
492 		+ !!flag[SW_VERIFY] + !!flag[SW_ADD] + !!flag[SW_DELETE];
493 	if (j > 1) errorFatal("conflicting switches");
494 	if (oflag && j == 1) errorFatal("-o invalid with other switches");
495 	if (oflag && !optr) errorFatal("-o without argument");
496 
497 	crc_init();
498 	inblk = Dzip_malloc(p_blocksize * 3);
499 	outblk = inblk + p_blocksize;
500 	tmpblk = outblk + p_blocksize / 2;
501 	zbuf = tmpblk + p_blocksize / 2;
502 
503 	files = Dzip_malloc(fileargs * 4);
504 	for (i = 1, j = 0; i < argc; i++)
505 	{
506 		if (*argv[i] != '-') files[j++] = argv[i];
507 		if (!strcmp(argv[i],"-o")) i++;
508 	}
509 
510 	zs.zalloc = Dzip_calloc;
511 	zs.zfree = free;
512 
513 	if (flag[SW_LIST] || flag[SW_EXTRACT] || flag[SW_VERIFY])
514 	{
515 		void (*func)(char *) = flag[SW_LIST] ? dzList : dzUncompress;
516 		for (i = 0; i < fileargs && !AbortOp; i++)
517 		{
518 			fname = Dzip_malloc(strlen(files[i])+4);
519 			strcpy(fname,files[i]);
520 #ifdef WIN32 /* only add extension automatically on windows */
521 			if (!*FileExtension(fname))
522 				strcat(fname, ".dz");
523 #endif
524 			DoFiles(fname, func);
525 			free(fname);
526 		}
527 		exit(0);
528 	}
529 
530 	if (!flag[SW_ADD] && !flag[SW_DELETE])
531 	{	/* create a new dz and compress files */
532 		if (!optr)
533 		{
534 			optr = Dzip_malloc(strlen(files[0]) + 4);
535 			strcpy(optr, files[0]);
536 			strcpy(FileExtension(optr), ".dz");
537 		}
538 	#ifdef WIN32
539 		else if (!*FileExtension(optr))
540 		{
541 			char *t = Dzip_malloc(strlen(optr) + 4);
542 			sprintf(t, "%s.dz", optr);
543 			optr = t;
544 		}
545 	#endif
546 		dzname = optr;
547 		dzfile = open_create(optr);
548 		if (!dzfile) exit(1);
549 		i = 'D' + ('Z' << 8) + (MAJOR_VERSION<<16) + (MINOR_VERSION<<24);
550 		i = cnvlong(i);
551 		dzFile_Write(&i, 12);
552 		totalsize = 12;
553 
554 		directory = Dzip_malloc(sizeof(direntry_t));
555 
556 		for (i = 0; i < fileargs; i++)
557 		{
558 			fname = Dzip_malloc(strlen(files[i])+5);
559 			strcpy(fname,files[i]);
560 			DoFiles(fname, NULL);
561 			free(fname);
562 		}
563 
564 		free(files);
565 		dzWriteDirectory();
566 		dzClose();
567 		if (!numfiles)
568 			remove(dzname);
569 		exit(0);
570 	}
571 
572 	/* add or delete */
573 	if (fileargs < 2)
574 		errorFatal("no input files");
575 	fname = Dzip_malloc(strlen(files[0])+4);
576 	strcpy(fname,files[0]);
577 #ifdef WIN32
578 	if (!*FileExtension(fname))
579 		strcat(fname, ".dz");
580 #endif
581 	dzsize = 0;
582 	/* figure out dzsize */
583 	{
584 #ifdef WIN32
585 	WIN32_FIND_DATA fd;
586 
587 	FindClose(FindFirstFile(fname, &fd));
588 	if (!fd.nFileSizeHigh)
589 		dzsize = fd.nFileSizeLow;
590 #else
591 	struct stat64 filestats;
592 
593 	if (!stat64(fname, &filestats))
594 		if (!(filestats.st_size > 0xFFFFFFFF))
595 			dzsize = filestats.st_size;
596 #endif
597 	}
598 
599 	if (!dzOpen(fname, 1))	/* add & delete need write access */
600 		exit(0);
601 
602 	if (flag[SW_ADD])
603 	{
604 		dzFile_Seek(4);
605 		dzFile_Read(&totalsize, 4);
606 		dzFile_Seek(totalsize);
607 		for (i = 1; i < fileargs; i++)
608 		{
609 			fname = Dzip_malloc(strlen(files[i])+5);
610 			strcpy(fname,files[i]);
611 			DoFiles(fname, NULL);
612 			free(fname);
613 		}
614 		dzWriteDirectory();
615 		dzClose();
616 	}
617 	else
618 		dzDeleteFiles_MakeList(files + 1, fileargs - 1);
619 	free(files);
620 	exit(0);
621 }