#include "macunpack.h" #ifdef ZMA #define ZMA_INTERNAL #include "zma.h" #include #include #include "globals.h" #include "crc.h" #include "../fileio/machdr.h" #include "../fileio/wrfile.h" #include "../fileio/kind.h" #include "../util/masks.h" #include "../util/transname.h" #include "../util/util.h" #include "de_lzah.h" /* We do allow for possible backpointing, so we allocate the archive in core */ static char *zma_archive; static char *zma_current; static char *zma_filestart; static uint32_t zma_length; static int32_t zma_archlength; static int zma_filehdr(struct zma_fileHdr *f, int skip); static void zma_folder(struct zma_fileHdr fhdr); static void zma_mooz(struct zma_fileHdr filehdr); static void zma_wrfile(uint32_t ibytes, uint32_t obytes, int type); static void zma_nocomp(uint32_t ibytes); static void zma_lzh(uint32_t ibytes); void zma (char *start, uint32_t length) { struct zma_fileHdr filehdr; int i, toread; if(length != 0) { if(zma_archlength < length) { if(zma_archlength == 0) { zma_archive = malloc((unsigned)length); } else { zma_archive = realloc(zma_archive, (unsigned)length); } zma_archlength = length; if(zma_archive == NULL) { (void)fprintf(stderr, "Insufficient memory, aborting\n"); exit(1); } } if(fread(zma_archive, 1, (int)length, infp) != length) { (void)fprintf(stderr, "Can't read archive.\n"); #ifdef SCAN do_error("macunpack: Can't read archive"); #endif /* SCAN */ exit(1); } zma_length = get4(zma_archive + ZMAHDRS + 1); if(zma_length != length) { (void)fprintf(stderr, "Archive length mismatch.\n"); #ifdef SCAN do_error("macunpack: Archive length mismatch"); #endif /* SCAN */ exit(1); } } else { zma_length = get4(start + ZMAHDRS + 1); if(zma_archlength < zma_length) { if(zma_archlength == 0) { zma_archive = malloc((unsigned)zma_length); } else { zma_archive = realloc(zma_archive, (unsigned)zma_length); } zma_archlength = zma_length; if(zma_archive == NULL) { (void)fprintf(stderr, "Insufficient memory, aborting\n"); exit(1); } } if(zma_archive == NULL) { (void)fprintf(stderr, "Insufficient memory, aborting\n"); exit(1); } for(i = 0; i <= ZMAHDRS2; i++) { zma_archive[i] = start[i]; } toread = zma_length - ZMAHDRS2 - 1; if(fread(zma_archive + ZMAHDRS2 + 1, 1, toread, infp) != toread) { (void)fprintf(stderr, "Can't read archive.\n"); #ifdef SCAN do_error("macunpack: Can't read archive"); #endif /* SCAN */ exit(1); } } /* Consistency checks */ if(zma_archive[0] != 0) { (void)fprintf(stderr, "Not a \"Zoom\" archive after all, aborting\n"); exit(1); } if(strncmp(zma_archive + 1, ZMAHDR, ZMAHDRS)) { (void)fprintf(stderr, "Not a \"Zoom\" archive after all, aborting\n"); exit(1); } zma_current = zma_archive + 8; updcrc = arc_updcrc; crcinit = arc_crcinit; while(zma_current != zma_archive) { if(zma_filehdr(&filehdr, 0) == -1) { (void)fprintf(stderr, "Can't find file header./n"); #ifdef SCAN do_error("macunpack: Can't find file header"); #endif /* SCAN */ exit(1); } zma_filestart = zma_current + filehdr.hlen; if(filehdr.what == z_dir) { zma_folder(filehdr); } else { zma_mooz(filehdr); } zma_current = zma_archive + filehdr.next; } } static int zma_filehdr (struct zma_fileHdr *f, int skip) { register int i; int n; char ftype[5], fauth[5]; if(zma_current - zma_archive + Z_HDRSIZE > zma_length) { return -1; } for(i = 0; i < INFOBYTES; i++) { info[i] = '\0'; } n = zma_current[Z_FNAME] & BYTEMASK; if(n > F_NAMELEN) { n = F_NAMELEN; } info[I_NAMEOFF] = n; copy(info + I_NAMEOFF + 1, zma_current + Z_FNAME + 1, n); transname(zma_current + Z_FNAME + 1, text, n); f->what = zma_current[Z_WHAT]; f->rsrcLength = get4(zma_current + Z_URLEN); f->dataLength = get4(zma_current + Z_UDLEN); f->compRLength = get4(zma_current + Z_CRLEN); f->compDLength = get4(zma_current + Z_CDLEN); f->rsrcCRC = get2(zma_current + Z_RCRC); f->dataCRC = get2(zma_current + Z_DCRC); f->hlen = zma_current[Z_HLEN]; f->next = get4(zma_current + Z_NEXT); if(f->what == z_dir) { /* A hack */ f->conts = get4(zma_current + Z_AUTH); } /* Set rsrc fork sizes correctly */ f->rsrcLength -= f->dataLength; f->compRLength -= f->compDLength; write_it = !skip; if(f->what & 0x80) { write_it = 0; f->what = -f->what; f->deleted = 1; return 0; } f->deleted = 0; if(list && !skip) { do_indent(indent); if(f->what == z_dir) { (void)fprintf(stderr, "folder=\"%s\"", text); } else { transname(zma_current + Z_TYPE, ftype, 4); transname(zma_current + Z_AUTH, fauth, 4); (void)fprintf(stderr, "name=\"%s\", type=%4.4s, author=%4.4s, data=%d, rsrc=%d", text, ftype, fauth, (int32_t)f->dataLength, (int32_t)f->rsrcLength); } switch(f->what) { case z_plug: (void)fputc('\n', stderr); (void)fprintf(stderr, "\tFile uses custom processing, cannot handle.\n"); write_it = 0; return 0; case z_dir: case z_file: case z_plain: break; default: (void)fputc('\n', stderr); (void)fprintf(stderr, "\tEh, do not understand this (%d); skipped.\n", f->what); write_it = 0; return 0; } if(info_only) { write_it = 0; } if(query) { write_it = do_query(); } else { (void)fputc('\n', stderr); } } if(write_it) { define_name(text); if(f->what != z_dir) { copy(info + I_TYPEOFF, zma_current + Z_TYPE, 4); copy(info + I_AUTHOFF, zma_current + Z_AUTH, 4); copy(info + I_FLAGOFF, zma_current + Z_FLAGS, 2); copy(info + I_DLENOFF, zma_current + Z_UDLEN, 4); put4(zma_current + Z_URLEN, f->rsrcLength); copy(info + I_RLENOFF, zma_current + Z_URLEN, 4); copy(info + I_CTIMOFF, zma_current + Z_MDATE, 4); copy(info + I_MTIMOFF, zma_current + Z_MDATE, 4); } } return 1; } static void zma_folder (struct zma_fileHdr fhdr) { int i; char loc_name[64]; struct zma_fileHdr filehdr; for(i = 0; i < 64; i++) { loc_name[i] = text[i]; } zma_current = zma_archive + fhdr.conts; if(write_it || info_only) { if(write_it) { do_mkdir(text, info); } indent++; while(zma_current != zma_archive) { if(zma_filehdr(&filehdr, 0) == -1) { (void)fprintf(stderr, "Can't find file header.\n"); #ifdef SCAN do_error("macunpack: Can't find file header"); #endif /* SCAN */ exit(1); } zma_filestart = zma_current + filehdr.hlen; if(filehdr.what == z_dir) { zma_folder(filehdr); } else { zma_mooz(filehdr); } zma_current = zma_archive + filehdr.next; } if(write_it) { enddir(); } indent--; if(list) { do_indent(indent); (void)fprintf(stderr, "leaving folder \"%s\"\n", loc_name); } } } static void zma_mooz (struct zma_fileHdr filehdr) { uint32_t crc; if(write_it) { start_info(info, filehdr.rsrcLength, filehdr.dataLength); } if(verbose) { (void)fprintf(stderr, "\tData: "); } if(write_it) { start_data(); } zma_wrfile(filehdr.compDLength, filehdr.dataLength, filehdr.what); if(write_it) { crc = (*updcrc)(INIT_CRC, (unsigned char*)out_buffer, filehdr.dataLength); if(filehdr.dataCRC != crc) { (void)fprintf(stderr, "CRC error on data fork: need 0x%04x, got 0x%04x\n", (int)filehdr.dataCRC, (int)crc); #ifdef SCAN do_error("macunpack: CRC error on data fork"); #endif /* SCAN */ exit(1); } } if(verbose) { (void)fprintf(stderr, ", Rsrc: "); } if(write_it) { start_rsrc(); } zma_wrfile(filehdr.compRLength, filehdr.rsrcLength, filehdr.what); if(write_it) { crc = (*updcrc)(INIT_CRC, (unsigned char*)out_buffer, filehdr.rsrcLength); if(filehdr.rsrcCRC != crc) { (void)fprintf(stderr, "CRC error on resource fork: need 0x%04x, got 0x%04x\n", (int)filehdr.rsrcCRC, (int)crc); #ifdef SCAN do_error("macunpack: CRC error on resource fork"); #endif /* SCAN */ exit(1); } end_file(); } if(verbose) { (void)fprintf(stderr, ".\n"); } } static void zma_wrfile (uint32_t ibytes, uint32_t obytes, int type) { if(ibytes == 0) { if(verbose) { (void)fprintf(stderr, "empty"); } return; } switch(type) { case z_plain: /* no compression */ if(verbose) { (void)fprintf(stderr, "No compression"); } if(write_it) { zma_nocomp(ibytes); } break; case z_file: /* lzh compression */ if(verbose) { (void)fprintf(stderr, "LZH compressed (%4.1f%%)", 100.0 * ibytes / obytes); } if(write_it) { zma_lzh(ibytes); } break; default: (void)fprintf(stderr, "Unknown compression method %2x\n", type); #ifdef SCAN do_idf("", UNKNOWN); #endif /* SCAN */ exit(1); } } /*---------------------------------------------------------------------------*/ /* No compression */ /*---------------------------------------------------------------------------*/ static void zma_nocomp (uint32_t ibytes) { int n = ibytes; char *ptr = out_buffer; while(n-- > 0) { *ptr++ = *zma_filestart++; } } /*---------------------------------------------------------------------------*/ /* LZ compression plus Huffman encoding */ /*---------------------------------------------------------------------------*/ static void zma_lzh (uint32_t ibytes) { /* Controlled by ibutes only */ de_lzh((int32_t)ibytes, (int32_t)(-1), &zma_filestart, 13); } #else /* ZMA */ int zma; /* keep lint and some compilers happy */ #endif /* ZMA */