]>
Commit | Line | Data |
---|---|---|
f6bcfd97 BP |
1 | /* ------------------------------------------------------------------------ */ |
2 | /* */ | |
3 | /* Main file of public UNACE. */ | |
4 | /* */ | |
5 | /* ------------------------------------------------------------------------ */ | |
6 | ||
7 | ||
8 | //--------------- include general files ------------------------------------// | |
9 | #include <ctype.h> // tolower() | |
10 | #include <fcntl.h> // open() | |
11 | #include <stdio.h> // printf() sprintf() remove() | |
12 | #include <stdlib.h> // malloc() | |
13 | #include <string.h> // str*() | |
14 | #include <sys/types.h> | |
15 | #include <sys/stat.h> // S_I* AMIGA: fstat() | |
16 | ||
17 | #define DIRSEP '\\' | |
18 | ||
19 | #if (!defined(__EMX__) && !defined(__OS2__) && !defined(WINNT) && !defined(WIN32)) || defined(__CYGWIN__) | |
20 | #include <sys/errno.h> | |
21 | #endif | |
22 | ||
23 | ||
24 | //--------------- include unace specific header files ----------------------// | |
25 | #include "os.h" | |
26 | ||
27 | #include "globals.h" | |
28 | #include "portable.h" | |
29 | #include "uac_comm.h" | |
30 | #include "uac_crc.h" | |
31 | #include "uac_crt.h" | |
32 | #include "uac_dcpr.h" | |
33 | #include "uac_sys.h" | |
34 | ||
35 | #ifdef CRYPT | |
36 | #include "unace_ps.h" | |
37 | #endif /* CRYPT */ | |
38 | int files=0; | |
39 | ||
40 | //--------------- BEGIN OF UNACE ROUTINES ----------------------------------// | |
41 | ||
42 | int pipeit(char *format, ...) { | |
43 | /* Do nothing ... perhaps pipe this somewhere in the future */ | |
44 | return 0; | |
45 | } | |
46 | ||
47 | void init_unace(void) // initializes unace | |
48 | { | |
49 | buf_rd =malloc(size_rdb * sizeof(ULONG)); // Allocate buffers: increase | |
50 | buf =malloc(size_buf); // sizes when possible to speed | |
51 | buf_wr =malloc(size_wrb); // up the program | |
52 | readbuf=malloc(size_headrdb); | |
53 | ||
54 | if (buf_rd ==NULL || | |
55 | buf ==NULL || | |
56 | buf_wr ==NULL || | |
57 | readbuf==NULL ) | |
58 | f_err = ERR_MEM; | |
59 | ||
60 | make_crctable(); // initialize CRC table | |
61 | dcpr_init(); // initialize decompression | |
62 | ||
63 | set_handler(); // ctrl+break etc. | |
64 | } | |
65 | ||
66 | void done_unace(void) | |
67 | { | |
68 | if (buf_rd ) free(buf_rd ); | |
69 | if (buf ) free(buf ); | |
70 | if (buf_wr ) free(buf_wr ); | |
71 | if (readbuf ) free(readbuf ); | |
72 | if (dcpr_text) free(dcpr_text); | |
73 | } | |
74 | ||
75 | INT read_header(INT print_err) // reads any header from archive | |
76 | { | |
77 | USHORT rd, | |
78 | head_size, | |
79 | crc_ok; | |
80 | LONG crc; | |
81 | UCHAR *tp=readbuf; | |
82 | ||
83 | lseek(archan, skipsize, SEEK_CUR); // skip ADDSIZE block | |
84 | ||
85 | if (read(archan, &head, 4)<4) | |
86 | return (0); // read CRC and header size | |
87 | ||
88 | #ifdef HI_LO_BYTE_ORDER | |
89 | WORDswap(&head.HEAD_CRC); | |
90 | WORDswap(&head.HEAD_SIZE); | |
91 | #endif | |
92 | // read size_headrdb bytes into | |
93 | head_size = head.HEAD_SIZE; // header structure | |
94 | rd = (head_size > size_headrdb) ? size_headrdb : head_size; | |
95 | if (read(archan, readbuf, rd) < rd) | |
96 | return (0); | |
97 | head_size -= rd; | |
98 | crc = getcrc(CRC_MASK, readbuf, rd); | |
99 | ||
100 | while (head_size) // skip rest of header | |
101 | { | |
102 | rd = (head_size > size_buf) ? size_buf : head_size; | |
103 | if (read(archan, buf, rd) < rd) | |
104 | return (0); | |
105 | head_size -= rd; | |
106 | crc = getcrc(crc, (UCHAR *)buf, rd); | |
107 | } | |
108 | ||
109 | head.HEAD_TYPE =*tp++; // generic buffer to head conversion | |
110 | head.HEAD_FLAGS=BUFP2WORD(tp); | |
111 | ||
112 | if (head.HEAD_FLAGS & ACE_ADDSIZE) | |
113 | skipsize = head.ADDSIZE = BUF2LONG(tp); // get ADDSIZE | |
114 | else | |
115 | skipsize = 0; | |
116 | ||
117 | // check header CRC | |
118 | if (!(crc_ok = head.HEAD_CRC == (crc & 0xffff)) && print_err) | |
119 | pipeit("\nError: archive is broken\n"); | |
120 | else | |
121 | switch (head.HEAD_TYPE) // specific buffer to head conversion | |
122 | { | |
123 | case MAIN_BLK: | |
124 | memcpy(mhead.ACESIGN, tp, acesign_len); tp+=acesign_len; | |
125 | mhead.VER_MOD=*tp++; | |
126 | mhead.VER_CR =*tp++; | |
127 | mhead.HOST_CR=*tp++; | |
128 | mhead.VOL_NUM=*tp++; | |
129 | mhead.TIME_CR=BUFP2LONG(tp); | |
130 | mhead.RES1 =BUFP2WORD(tp); | |
131 | mhead.RES2 =BUFP2WORD(tp); | |
132 | mhead.RES =BUFP2LONG(tp); | |
133 | mhead.AV_SIZE=*tp++; | |
134 | memcpy(mhead.AV, tp, rd-(USHORT)(tp-readbuf)); | |
135 | break; | |
136 | case FILE_BLK: | |
137 | fhead.PSIZE =BUFP2LONG(tp); | |
138 | fhead.SIZE =BUFP2LONG(tp); | |
139 | fhead.FTIME =BUFP2LONG(tp); | |
140 | fhead.ATTR =BUFP2LONG(tp); | |
141 | fhead.CRC32 =BUFP2LONG(tp); | |
142 | fhead.TECH.TYPE =*tp++; | |
143 | fhead.TECH.QUAL =*tp++; | |
144 | fhead.TECH.PARM =BUFP2WORD(tp); | |
145 | fhead.RESERVED =BUFP2WORD(tp); | |
146 | fhead.FNAME_SIZE=BUFP2WORD(tp); | |
147 | memcpy(fhead.FNAME, tp, rd-(USHORT)(tp-readbuf)); | |
148 | break; | |
149 | // default: (REC_BLK and future things): | |
150 | // do nothing 'cause isn't needed for extraction | |
151 | } | |
152 | ||
153 | return (crc_ok); | |
154 | } | |
155 | // maximum SFX module size | |
156 | #define max_sfx_size 65536 // (needed by read_arc_head) | |
157 | ||
158 | INT read_arc_head(void) // searches for the archive header and reads it | |
159 | { | |
160 | INT i, | |
161 | flags, | |
162 | buf_pos = 0; | |
163 | LONG arc_head_pos, | |
164 | old_fpos, | |
165 | fpos = 0; | |
166 | struct stat st; | |
167 | ||
168 | fstat(archan, &st); | |
169 | ||
170 | memset(buf, 0, size_buf); | |
171 | ||
172 | #if !defined(__EMX__) && !defined(__OS2__) | |
173 | while (ftell(farchan)<st.st_size && fpos < max_sfx_size) | |
174 | #else | |
175 | while (tell(archan)<st.st_size && fpos < max_sfx_size) | |
176 | #endif | |
177 | { | |
178 | old_fpos = fpos; | |
179 | fpos += read(archan, &buf[buf_pos], size_buf - buf_pos); | |
180 | ||
181 | for (i = 0; i < size_buf; i++) // look for the acesign | |
182 | { | |
183 | if (!memcmp(acesign, &buf[i], acesign_len)) | |
184 | { | |
185 | // seek to the probable begin | |
186 | // of the archive | |
187 | arc_head_pos = old_fpos + i - buf_pos - bytes_before_acesign; | |
188 | lseek(archan, arc_head_pos, SEEK_SET); | |
189 | if (read_header(0)) // try to read archive header | |
190 | { | |
191 | flags = mhead.HEAD_FLAGS; | |
192 | adat.sol = (flags & ACE_SOLID) > 0; | |
193 | adat.vol = (flags & ACE_MULT_VOL) > 0; | |
194 | adat.vol_num = mhead.VOL_NUM; | |
195 | adat.time_cr = mhead.TIME_CR; | |
196 | return (1); | |
197 | } | |
198 | } | |
199 | } | |
200 | // was no archive header, | |
201 | // continue search | |
202 | lseek(archan, fpos, SEEK_SET); | |
203 | memcpy(buf, &buf[size_buf - 512], 512); | |
204 | buf_pos = 512; // keep 512 old bytes | |
205 | } | |
206 | return (0); | |
207 | } | |
208 | ||
209 | INT open_archive(INT print_err) // opens archive (or volume) | |
210 | { | |
211 | CHAR av_str[80]; | |
212 | ||
213 | ||
214 | #if defined(__OS2_) || defined(__EMX__) || defined(WIN32) | |
215 | archan = open(aname, O_RDONLY | O_BINARY); // open file | |
216 | #else | |
217 | archan = open(aname, O_RDONLY); // open file | |
218 | #endif | |
219 | #if !defined(__EMX__) && !defined(__OS2__) | |
220 | farchan = fdopen(archan, "rb"); | |
221 | #endif | |
222 | if (archan == -1) | |
223 | { | |
224 | pipeit("\nError opening file %s", aname); | |
225 | return (0); | |
226 | } | |
227 | if (!read_arc_head()) // read archive header | |
228 | { | |
229 | if (print_err) | |
230 | pipeit("\nInvalid archive file: %s\n", aname); | |
231 | #if !defined(__EMX__) && !defined(__OS2__) | |
232 | fclose(farchan); | |
233 | #endif | |
234 | close(archan); | |
235 | return (0); | |
236 | } | |
237 | ||
238 | pipeit("\nProcessing archive: %s\n\n", aname); | |
239 | if (head.HEAD_FLAGS & ACE_AV) | |
240 | { | |
241 | pipeit("Authenticity Verification:"); // print the AV | |
242 | sprintf(av_str, "\ncreated on %d.%d.%d by ", | |
243 | ts_day(adat.time_cr), ts_month(adat.time_cr), ts_year(adat.time_cr)); | |
244 | pipeit(av_str); | |
245 | strncpy(av_str, (char *)mhead.AV, mhead.AV_SIZE); | |
246 | av_str[mhead.AV_SIZE] = 0; | |
247 | pipeit("%s\n\n", av_str); | |
248 | } | |
249 | comment_out("Main comment:"); // print main comment | |
250 | return (1); | |
251 | } | |
252 | ||
253 | void get_next_volname(void) // get file name of next volume | |
254 | { | |
255 | CHAR *cp; | |
256 | INT num; | |
257 | ||
258 | if ((cp = (CHAR *) strrchr(aname, '.')) == NULL || !*(cp + 1)) | |
259 | num = -1; | |
260 | else | |
261 | { | |
262 | cp++; | |
263 | num = (*(cp + 1) - '0') * 10 + *(cp + 2) - '0'; | |
264 | if (!in(num, 0, 99)) | |
265 | num = -1; | |
266 | if (in(*cp, '0', '9')) | |
267 | num += (*cp - '0') * 100; | |
268 | } | |
269 | num++; | |
270 | ||
271 | if (num < 100) | |
272 | *cp = 'C'; | |
273 | else | |
274 | *cp = num / 100 + '0'; | |
275 | *(cp + 1) = (num / 10) % 10 + '0'; | |
276 | *(cp + 2) = num % 10 + '0'; | |
277 | } | |
278 | ||
279 | INT proc_vol(void) // opens volume | |
280 | { | |
281 | INT i; | |
282 | CHAR s[80]; | |
283 | ||
284 | if (!fileexists(aname) || !f_allvol_pr) | |
285 | { | |
286 | do | |
287 | { | |
288 | sprintf(s, "Ready to process %s?", aname); | |
289 | #if !defined(__MINGW32__) | |
290 | beep(); | |
291 | #else | |
292 | beep(500,500); | |
293 | #endif | |
294 | i = wrask(s); // ask whether ready or not | |
295 | f_allvol_pr = (i == 1); // "Always" --> process all volumes | |
296 | if (i >= 2) | |
297 | { | |
298 | f_err = ERR_FOUND; | |
299 | return (0); | |
300 | } | |
301 | } | |
302 | while (!fileexists(aname)); | |
303 | } | |
304 | ||
305 | if (!open_archive(1)) // open volume | |
306 | { | |
307 | pipeit("\nError while opening archive. File not found or archive broken.\n"); | |
308 | f_err = ERR_OPEN; | |
309 | return (0); | |
310 | } | |
311 | ||
312 | return (1); | |
313 | } | |
314 | ||
315 | INT proc_next_vol(void) // opens next volume to process | |
316 | { | |
317 | #if !defined(__EMX__) && !defined(__OS2__) | |
318 | fclose(farchan); | |
319 | #endif | |
320 | close(archan); // close handle | |
321 | get_next_volname(); // get file name of next volume | |
322 | ||
323 | if (!proc_vol()) // try to open volume, read archive header | |
324 | return 0; | |
325 | if (!read_header(1)) // read 2nd header | |
326 | { | |
327 | f_err=ERR_READ; | |
328 | return 0; | |
329 | } | |
330 | return 1; | |
331 | } | |
332 | ||
333 | INT read_adds_blk(CHAR * buffer, INT len) // reads part of ADD_SIZE block | |
334 | { | |
335 | INT rd = 0, | |
336 | l = len; | |
337 | LONG i; | |
338 | ||
339 | #ifdef CRYPT | |
340 | char *cbuffer=buffer; | |
341 | ||
342 | if (head.HEAD_TYPE == FILE_BLK && (head.HEAD_FLAGS & ACE_PASSW)) | |
343 | len = crypt_len(len); | |
344 | #endif /* CRYPT */ | |
345 | while (!f_err && len && skipsize) | |
346 | { | |
347 | i = (skipsize > len) ? len : skipsize; | |
348 | skipsize -= i; | |
349 | ||
350 | /* How do I check error condition when comping -mno-cygwin? */ | |
351 | #if !defined(__MINGW32__) | |
352 | errno = 0; | |
353 | #endif | |
354 | rd += read(archan, buffer, i); | |
355 | #if !defined(__MINGW32__) | |
356 | if (errno) | |
357 | { | |
358 | pipeit("\nRead error\n"); | |
359 | f_err = ERR_READ; | |
360 | } | |
361 | #endif | |
362 | ||
363 | buffer += i; | |
364 | len -= i; | |
365 | ||
366 | if (!skipsize) // if block is continued on next volume | |
367 | if (head.HEAD_FLAGS & ACE_SP_AFTER && !proc_next_vol()) | |
368 | break; | |
369 | } | |
370 | #ifdef CRYPT | |
371 | if (head.HEAD_TYPE == FILE_BLK && (head.HEAD_FLAGS & ACE_PASSW)) | |
372 | decrypt(cbuffer, rd); | |
373 | #endif /* CRYPT */ | |
374 | ||
375 | return (rd > l ? l : rd); | |
376 | } | |
377 | ||
378 | void crc_print(void) // checks CRC, prints message | |
379 | { | |
380 | INT crc_not_ok = rd_crc != fhead.CRC32; /* check CRC of file */ | |
381 | ||
382 | if (!f_err) // print message | |
383 | { | |
384 | pipeit(crc_not_ok ? " CRC-check error" : " CRC OK"); | |
385 | flush; | |
386 | } | |
387 | } | |
388 | ||
389 | void analyze_file(void) // analyzes one file (for solid archives) | |
390 | { | |
391 | pipeit("\n Analyzing"); | |
392 | flush; | |
393 | while (!cancel() && (dcpr_adds_blk(buf_wr, size_wrb))) // decompress only | |
394 | ; | |
395 | crc_print(); | |
396 | } | |
397 | ||
398 | void extract_file(void) // extracts one file | |
399 | { | |
400 | INT rd; | |
401 | ||
402 | pipeit("\n Extracting"); | |
403 | flush; // decompress block | |
404 | while (!cancel() && (rd = dcpr_adds_blk(buf_wr, size_wrb))) | |
405 | { | |
406 | if (write(wrhan, buf_wr, rd) != rd) // write block | |
407 | { | |
408 | pipeit("\nWrite error\n"); | |
409 | f_err = ERR_WRITE; | |
410 | } | |
411 | } | |
412 | crc_print(); | |
413 | } | |
414 | ||
415 | /* extracts or tests all files of the archive | |
416 | */ | |
417 | void extract_files(int nopath, int test) | |
418 | { | |
419 | CHAR file[PATH_MAX]; | |
420 | ||
421 | while (!cancel() && read_header(1)) | |
422 | { | |
423 | if (head.HEAD_TYPE == FILE_BLK) | |
424 | { | |
425 | comment_out("File comment:"); // show file comment | |
426 | ace_fname(file, &head, nopath); // get file name | |
427 | pipeit("\n%s", file); | |
428 | flush; | |
429 | dcpr_init_file(); // initialize decompression of file | |
430 | if (!f_err) | |
431 | { | |
432 | if (test || | |
433 | (wrhan = create_dest_file(file, (INT) fhead.ATTR))<0) | |
434 | { | |
435 | if (test || adat.sol) | |
436 | analyze_file(); // analyze file | |
437 | } | |
438 | else | |
439 | { | |
440 | extract_file(); // extract it | |
441 | #ifdef DOS // set file time | |
442 | _dos_setftime(wrhan, (USHORT) (fhead.FTIME >> 16), (USHORT) fhead.FTIME); | |
443 | #endif | |
444 | close(wrhan); | |
445 | #ifdef DOS // set file attributes | |
446 | _dos_setfileattr(file, (UINT) fhead.ATTR); | |
447 | #endif | |
448 | #ifdef AMIGA | |
449 | { // set file date and time | |
450 | struct DateTime dt; | |
451 | char Date[9], Time[9]; | |
452 | ULONG tstamp=fhead.FTIME; | |
453 | ||
454 | sprintf(Date, "%02d-%02d-%02d", ts_year(tstamp)-1900, ts_month(tstamp), ts_day(tstamp)); | |
455 | sprintf(Time, "%02d:%02d:%02d", ts_hour(tstamp), ts_min(tstamp), ts_sec(tstamp)); | |
456 | ||
457 | dt.dat_Format = FORMAT_INT; | |
458 | dt.dat_Flags = 0; | |
459 | dt.dat_StrDate= Date; | |
460 | dt.dat_StrTime= Time; | |
461 | ||
462 | if (StrToDate(&dt)) | |
463 | SetFileDate(file, &dt.dat_Stamp); | |
464 | } | |
465 | #endif | |
466 | if (f_err) | |
467 | remove(file); | |
468 | } | |
469 | } | |
470 | } | |
471 | } | |
472 | } | |
473 | ||
474 | unsigned percentage(ULONG p, ULONG d) | |
475 | { | |
476 | return (unsigned)( d ? (d/2+p*100)/d : 100 ); | |
477 | } | |
478 | ||
479 | void list_files(int verbose) | |
480 | { | |
481 | ULONG size =0, | |
482 | psize=0, | |
483 | tpsize; | |
484 | CHAR file[PATH_MAX]; | |
485 | ||
486 | pipeit("Date |Time |Packed |Size |Ratio|File\n"); | |
487 | ||
488 | while (!cancel() && read_header(1)) | |
489 | { | |
490 | if (head.HEAD_TYPE == FILE_BLK) | |
491 | { | |
492 | ULONG ti=fhead.FTIME; | |
493 | ace_fname(file, &head, verbose ? 0 : 1); // get file name | |
494 | ||
495 | size += fhead.SIZE; | |
496 | psize += | |
497 | tpsize = fhead.PSIZE; | |
498 | files++; | |
499 | ||
500 | while (head.HEAD_FLAGS & ACE_SP_AFTER) | |
501 | { | |
502 | skipsize=0; | |
503 | if (!proc_next_vol()) | |
504 | break; | |
505 | psize += fhead.PSIZE; | |
506 | tpsize+= fhead.PSIZE; | |
507 | } | |
508 | if (!f_err) | |
509 | pipeit("%02u.%02u.%02u|%02u:%02u|%c%c%9lu|%9lu|%4u%%|%c%s\n", | |
510 | ts_day (ti), ts_month(ti), ts_year(ti)%100, | |
511 | ts_hour(ti), ts_min (ti), | |
512 | fhead.HEAD_FLAGS & ACE_SP_BEF ? '<' : ' ', | |
513 | fhead.HEAD_FLAGS & ACE_SP_AFTER ? '>' : ' ', | |
514 | tpsize, fhead.SIZE, percentage(tpsize, fhead.SIZE), | |
515 | fhead.HEAD_FLAGS & ACE_PASSW ? '*' : ' ', | |
516 | file | |
517 | ); | |
518 | } | |
519 | } | |
520 | if (!f_err) | |
521 | { | |
522 | pipeit("\n %9lu|%9lu|%4u%%| %u file%s", | |
523 | psize, | |
524 | size, | |
525 | percentage(psize, size), | |
526 | files, | |
527 | (char*)(files == 1 ? "" : "s") | |
528 | ); | |
529 | } | |
530 | } | |
531 | ||
532 | void showhelp(void) | |
533 | { | |
534 | pipeit("\n" | |
535 | "Usage: UNACE <command> <archive[.ace]>\n" | |
536 | "\n" | |
537 | "Where <command> is one of:\n" | |
538 | "\n" | |
539 | " e Extract files\n" | |
540 | " l List archive\n" | |
541 | " t Test archive integrity\n" | |
542 | " v List archive (verbose)\n" | |
543 | " x Extract files with full path" | |
544 | ); | |
545 | } | |
546 | ||
547 | int include_unpack(char *bleah) // processes the archive | |
548 | { | |
549 | CHAR *s; | |
550 | ||
551 | strcpy(aname, bleah); | |
552 | ||
553 | init_unace(); // initialize unace | |
554 | ||
555 | if (!(s = (CHAR *) strrchr(aname, DIRSEP))) | |
556 | s = aname; | |
557 | if (!strrchr(s, '.')) | |
558 | strcat(aname, ".ACE"); | |
559 | ||
560 | if (open_archive(1)) // open archive to process | |
561 | { | |
562 | if (adat.vol_num) | |
563 | pipeit("\nFirst volume of archive required!\n"); | |
564 | else | |
565 | list_files (0 ); | |
566 | ||
567 | #if !defined(__EMX__) && !defined(__OS2__) | |
568 | fclose(farchan); | |
569 | #endif | |
570 | close(archan); | |
571 | if (f_err) | |
572 | { | |
573 | pipeit("\nError occurred\n"); | |
574 | if (f_criterr) | |
575 | pipeit("Critical error on drive %c\n", f_criterr); | |
576 | } | |
577 | } | |
578 | else | |
579 | f_err = ERR_CLINE; | |
580 | ||
581 | done_unace(); | |
582 | return (f_err); | |
583 | } | |
584 |