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 }