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