#include "macunpack.h" #ifdef DD #ifndef CPT #define CPT #endif /* CPT */ #endif /* DD */ #ifdef CPT #define CPT_INTERNAL #include "cpt.h" #include #include "globals.h" #include "crc.h" #include "../util/util.h" #include "../fileio/machdr.h" #include "../fileio/wrfile.h" #include "../fileio/kind.h" #include "../util/masks.h" #include "../util/transname.h" #include "huffman.h" #define ESC1 0x81 #define ESC2 0x82 #define NONESEEN 0 #define ESC1SEEN 1 #define ESC2SEEN 2 static unsigned char *cpt_data; static uint32_t cpt_datamax; static uint32_t cpt_datasize; static unsigned char cpt_LZbuff[CIRCSIZE]; static unsigned int cpt_LZptr; static unsigned char *cpt_char; static uint32_t cpt_crc; static uint32_t cpt_inlength; static uint32_t cpt_outlength; static int cpt_outstat; static unsigned char cpt_savechar; static uint32_t cpt_newbits; static int cpt_bitsavail; static int cpt_blocksize; /* Lengths is twice the max number of entries, and include slack. */ #define SLACK 6 static node cpt_Hufftree[512 + SLACK], cpt_LZlength[128 + SLACK], cpt_LZoffs[256 + SLACK]; static int readcpthdr(struct cptHdr *s); static int cpt_filehdr(struct cpt_fileHdr *f, char *hdr); static void cpt_folder(char *name, struct cpt_fileHdr fileh, char *cptptr); static void cpt_uncompact(struct cpt_fileHdr filehdr); static void cpt_wrfile(uint32_t ibytes, uint32_t obytes, int type); static void cpt_outch(int ch); static void cpt_rle(void); static void cpt_rle_lzh(void); static void cpt_readHuff(int size, struct node *Hufftree); static int cpt_get6bits(void); static int cpt_getbit(void); void cpt (void) { struct cptHdr cpthdr; struct cpt_fileHdr filehdr; char *cptindex; int cptindsize; char *cptptr; int i; updcrc = zip_updcrc; crcinit = zip_crcinit; cpt_crc = INIT_CRC; if(readcpthdr(&cpthdr) == 0) { (void)fprintf(stderr, "Can't read archive header\n"); #ifdef SCAN do_error("macunpack: Can't read archive header"); #endif /* SCAN */ exit(1); } cptindsize = cpthdr.entries * CPT_FILEHDRSIZE; if(cpthdr.commentsize > cptindsize) { cptindsize = cpthdr.commentsize; } cptindex = malloc((unsigned)cptindsize); if(cptindex == NULL) { (void)fprintf(stderr, "Insufficient memory, aborting\n"); exit(1); } cptptr = cptindex; if(fread(cptptr, 1, (int)cpthdr.commentsize, infp) != cpthdr.commentsize) { (void)fprintf(stderr, "Can't read comment.\n"); #ifdef SCAN do_error("macunpack: Can't read comment"); #endif /* SCAN */ exit(1); } cpt_crc = (*updcrc)(cpt_crc, (unsigned char*)cptptr, cpthdr.commentsize); for(i = 0; i < cpthdr.entries; i++) { *cptptr = getc(infp); cpt_crc = (*updcrc)(cpt_crc, (unsigned char*)cptptr, 1); if(*cptptr & 0x80) { cptptr[F_FOLDER] = 1; *cptptr &= 0x3f; } else { cptptr[F_FOLDER] = 0; } if(fread(cptptr + 1, 1, *cptptr, infp) != *cptptr) { (void)fprintf(stderr, "Can't read file header #%d\n", i+1); #ifdef SCAN do_error("macunpack: Can't read file header"); #endif /* SCAN */ exit(1); } cpt_crc = (*updcrc)(cpt_crc, (unsigned char*)(cptptr + 1), *cptptr); if(cptptr[F_FOLDER]) { if(fread(cptptr + F_FOLDERSIZE, 1, 2, infp) != 2) { (void)fprintf(stderr, "Can't read file header #%d\n", i+1); #ifdef SCAN do_error("macunpack: Can't read file header"); #endif /* SCAN */ exit(1); } cpt_crc = (*updcrc)(cpt_crc, (unsigned char*)(cptptr + F_FOLDERSIZE), 2); } else { if(fread(cptptr + F_VOLUME, 1, CPT_FILEHDRSIZE - F_VOLUME, infp) != CPT_FILEHDRSIZE - F_VOLUME) { (void)fprintf(stderr, "Can't read file header #%d\n", i+1); #ifdef SCAN do_error("macunpack: Can't read file header"); #endif /* SCAN */ exit(1); } cpt_crc = (*updcrc)(cpt_crc, (unsigned char*)(cptptr + F_VOLUME), CPT_FILEHDRSIZE - F_VOLUME); } cptptr += CPT_FILEHDRSIZE; } if(cpt_crc != cpthdr.hdrcrc) { (void)fprintf(stderr, "Header CRC mismatch: got 0x%08x, need 0x%08x\n", (int)cpthdr.hdrcrc, (int)cpt_crc); #ifdef SCAN do_error("macunpack: Header CRC mismatch"); #endif /* SCAN */ exit(1); } cptptr = cptindex; for(i = 0; i < cpthdr.entries; i++) { if(cpt_filehdr(&filehdr, cptptr) == -1) { (void)fprintf(stderr, "Can't read file header #%d\n", i+1); #ifdef SCAN do_error("macunpack: Can't read file header"); #endif /* SCAN */ exit(1); } if(filehdr.folder) { cpt_folder(text, filehdr, cptptr); i += filehdr.foldersize; cptptr += filehdr.foldersize * CPT_FILEHDRSIZE; } else { cpt_uncompact(filehdr); } cptptr += CPT_FILEHDRSIZE; } (void)free(cptindex); } static int readcpthdr (struct cptHdr *s) { char temp[CHDRSIZE]; if(fread(temp, 1, CPTHDRSIZE, infp) != CPTHDRSIZE) { return 0; } if(temp[C_SIGNATURE] != 1) { (void)fprintf(stderr, "Not a Compactor file\n"); return 0; } cpt_datasize = get4(temp + C_IOFFSET); s->offset = cpt_datasize; if(cpt_datasize > cpt_datamax) { if(cpt_datamax == 0) { cpt_data = (unsigned char *)malloc((unsigned)cpt_datasize); } else { cpt_data = (unsigned char *)realloc((char *)cpt_data, (unsigned)cpt_datasize); } cpt_datamax = cpt_datasize; } if(cpt_data == NULL) { (void)fprintf(stderr, "Insufficient memory, aborting\n"); exit(1); } if(fread((char *)(cpt_data + CPTHDRSIZE), 1, (int)s->offset - CPTHDRSIZE, infp) != s->offset - CPTHDRSIZE) { return 0; } if(fread(temp + CPTHDRSIZE, 1, CPTHDR2SIZE, infp) != CPTHDR2SIZE) { return 0; } cpt_crc = (*updcrc)(cpt_crc, (unsigned char*)(temp + CPTHDRSIZE + C_ENTRIES), 3); s->hdrcrc = get4(temp + CPTHDRSIZE + CPT_C_HDRCRC); s->entries = get2(temp + CPTHDRSIZE + C_ENTRIES); s->commentsize = temp[CPTHDRSIZE + C_COMMENT]; return 1; } static int cpt_filehdr (struct cpt_fileHdr *f, char *hdr) { register int i; int n; char ftype[5], fauth[5]; for(i = 0; i < INFOBYTES; i++) { info[i] = '\0'; } n = hdr[F_FNAME] & BYTEMASK; if(n > F_NAMELEN) { n = F_NAMELEN; } info[I_NAMEOFF] = n; copy(info + I_NAMEOFF + 1, hdr + F_FNAME + 1, n); transname(hdr + F_FNAME + 1, text, n); f->folder = hdr[F_FOLDER]; if(f->folder) { f->foldersize = get2(hdr + F_FOLDERSIZE); } else { f->cptFlag = get2(hdr + F_CPTFLAG); f->rsrcLength = get4(hdr + F_RSRCLENGTH); f->dataLength = get4(hdr + F_DATALENGTH); f->compRLength = get4(hdr + F_COMPRLENGTH); f->compDLength = get4(hdr + F_COMPDLENGTH); f->fileCRC = get4(hdr + F_FILECRC); f->FndrFlags = get2(hdr + F_FNDRFLAGS); f->filepos = get4(hdr + F_FILEPOS); f->volume = hdr[F_VOLUME]; } write_it = 1; if(list) { do_indent(indent); if(f->folder) { (void)fprintf(stderr, "folder=\"%s\"", text); } else { transname(hdr + F_FTYPE, ftype, 4); transname(hdr + F_CREATOR, 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); } 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->folder) { copy(info + I_TYPEOFF, hdr + F_FTYPE, 4); copy(info + I_AUTHOFF, hdr + F_CREATOR, 4); copy(info + I_FLAGOFF, hdr + F_FNDRFLAGS, 2); copy(info + I_DLENOFF, hdr + F_DATALENGTH, 4); copy(info + I_RLENOFF, hdr + F_RSRCLENGTH, 4); copy(info + I_CTIMOFF, hdr + F_CREATIONDATE, 4); copy(info + I_MTIMOFF, hdr + F_MODDATE, 4); } } return 1; } static void cpt_folder (char *name, struct cpt_fileHdr fileh, char *cptptr) { int i, nfiles; char loc_name[64]; struct cpt_fileHdr filehdr; for(i = 0; i < 64; i++) { loc_name[i] = name[i]; } if(write_it || info_only) { cptptr += CPT_FILEHDRSIZE; nfiles = fileh.foldersize; if(write_it) { do_mkdir(text, info); } indent++; for(i = 0; i < nfiles; i++) { if(cpt_filehdr(&filehdr, cptptr) == -1) { (void)fprintf(stderr, "Can't read file header #%d\n", i+1); #ifdef SCAN do_error("macunpack: Can't read file header"); #endif /* SCAN */ exit(1); } if(filehdr.folder) { cpt_folder(text, filehdr, cptptr); i += filehdr.foldersize; cptptr += filehdr.foldersize * CPT_FILEHDRSIZE; } else { cpt_uncompact(filehdr); } cptptr += CPT_FILEHDRSIZE; } if(write_it) { enddir(); } indent--; if(list) { do_indent(indent); (void)fprintf(stderr, "leaving folder \"%s\"\n", loc_name); } } } static void cpt_uncompact (struct cpt_fileHdr filehdr) { if(filehdr.cptFlag & 1) { (void)fprintf(stderr, "\tFile is password protected, skipping file\n"); #ifdef SCAN do_idf("", PROTECTED); #endif /* SCAN */ return; } if(write_it) { start_info(info, filehdr.rsrcLength, filehdr.dataLength); cpt_crc = INIT_CRC; cpt_char = cpt_data + filehdr.filepos; } if(verbose) { (void)fprintf(stderr, "\tRsrc: "); if(filehdr.compRLength == 0) { (void)fprintf(stderr, "empty"); } else if(filehdr.cptFlag & 2) { (void)fprintf(stderr, "RLE/LZH compressed (%4.1f%%)", 100.0 * filehdr.compRLength / filehdr.rsrcLength); } else { (void)fprintf(stderr, "RLE compressed (%4.1f%%)", 100.0 * filehdr.compRLength / filehdr.rsrcLength); } } if(write_it) { start_rsrc(); cpt_wrfile(filehdr.compRLength, filehdr.rsrcLength, filehdr.cptFlag & 2); cpt_char = cpt_data + filehdr.filepos + filehdr.compRLength; } if(verbose) { (void)fprintf(stderr, ", Data: "); if(filehdr.compDLength == 0) { (void)fprintf(stderr, "empty"); } else if(filehdr.cptFlag & 4) { (void)fprintf(stderr, "RLE/LZH compressed (%4.1f%%)", 100.0 * filehdr.compDLength / filehdr.dataLength); } else { (void)fprintf(stderr, "RLE compressed (%4.1f%%)", 100.0 * filehdr.compDLength / filehdr.dataLength); } } if(write_it) { start_data(); cpt_wrfile(filehdr.compDLength, filehdr.dataLength, filehdr.cptFlag & 4); if(filehdr.fileCRC != cpt_crc) { (void)fprintf(stderr, "CRC error on file: need 0x%08x, got 0x%08x\n", (int32_t)filehdr.fileCRC, (int32_t)cpt_crc); #ifdef SCAN do_error("macunpack: CRC error on file"); #endif /* SCAN */ exit(1); } end_file(); } if(verbose) { (void)fprintf(stderr, ".\n"); } } static void cpt_wrfile (uint32_t ibytes, uint32_t obytes, int type) { if(ibytes == 0) { return; } cpt_outstat = NONESEEN; cpt_inlength = ibytes; cpt_outlength = obytes; cpt_LZptr = 0; cpt_blocksize = 0x1fff0; if(type == 0) { cpt_rle(); } else { cpt_rle_lzh(); } cpt_crc = (*updcrc)(cpt_crc, (unsigned char*)out_buffer, obytes); } void cpt_wrfile1 (unsigned char *in_char, uint32_t ibytes, uint32_t obytes, int type, uint32_t blocksize) { cpt_char = in_char; if(ibytes == 0) { return; } cpt_outstat = NONESEEN; cpt_inlength = ibytes; cpt_outlength = obytes; cpt_LZptr = 0; cpt_blocksize = blocksize; if(type == 0) { cpt_rle(); } else { cpt_rle_lzh(); } } static void cpt_outch (int ch) { cpt_LZbuff[cpt_LZptr++ & (CIRCSIZE - 1)] = ch; switch(cpt_outstat) { case NONESEEN: if(ch == ESC1 && cpt_outlength != 1) { cpt_outstat = ESC1SEEN; } else { cpt_savechar = ch; *out_ptr++ = ch; cpt_outlength--; } break; case ESC1SEEN: if(ch == ESC2) { cpt_outstat = ESC2SEEN; } else { cpt_savechar = ESC1; *out_ptr++ = ESC1; cpt_outlength--; if(cpt_outlength == 0) { return; } if(ch == ESC1 && cpt_outlength != 1) { return; } cpt_outstat = NONESEEN; cpt_savechar = ch; *out_ptr++ = ch; cpt_outlength--; } break; case ESC2SEEN: cpt_outstat = NONESEEN; if(ch != 0) { while(--ch != 0) { *out_ptr++ = cpt_savechar; cpt_outlength--; if(cpt_outlength == 0) { return; } } } else { *out_ptr++ = ESC1; cpt_outlength--; if(cpt_outlength == 0) { return; } cpt_savechar = ESC2; *out_ptr++ = cpt_savechar; cpt_outlength--; } } } /*---------------------------------------------------------------------------*/ /* Run length encoding */ /*---------------------------------------------------------------------------*/ static void cpt_rle (void) { while(cpt_inlength-- > 0) { cpt_outch(*cpt_char++); } } /*---------------------------------------------------------------------------*/ /* Run length encoding plus LZ compression plus Huffman encoding */ /*---------------------------------------------------------------------------*/ static void cpt_rle_lzh (void) { int block_count; unsigned int bptr; int Huffchar, LZlength, LZoffs; get_bit = cpt_getbit; cpt_LZbuff[CIRCSIZE - 3] = 0; cpt_LZbuff[CIRCSIZE - 2] = 0; cpt_LZbuff[CIRCSIZE - 1] = 0; cpt_LZptr = 0; while(cpt_outlength != 0) { cpt_readHuff(256, cpt_Hufftree); cpt_readHuff(64, cpt_LZlength); cpt_readHuff(128, cpt_LZoffs); block_count = 0; cpt_newbits = (*cpt_char++ << 8); cpt_newbits = cpt_newbits | *cpt_char++; cpt_newbits = cpt_newbits << 16; cpt_bitsavail = 16; while(block_count < cpt_blocksize && cpt_outlength != 0) { if(cpt_getbit()) { Huffchar = gethuffbyte(cpt_Hufftree); cpt_outch((unsigned char)Huffchar); block_count += 2; } else { LZlength = gethuffbyte(cpt_LZlength); LZoffs = gethuffbyte(cpt_LZoffs); LZoffs = (LZoffs << 6) | cpt_get6bits(); bptr = cpt_LZptr - LZoffs; while(LZlength-- > 0) { cpt_outch(cpt_LZbuff[bptr++ & (CIRCSIZE - 1)]); } block_count += 3; } } } } /* Based on unimplod from unzip; difference are noted below. */ typedef struct sf_entry { int Value; int BitLength; } sf_entry; /* See routine LoadTree. The parameter tree (actually an array and two integers) are only used locally in this version and hence locally declared. The parameter nodes has been renamed Hufftree.... */ static void cpt_readHuff (int size, struct node *Hufftree) { sf_entry tree_entry[256 + SLACK]; /* maximal number of elements */ int tree_entries; int tree_MaxLength; /* finishes local declaration of tree */ int treeBytes, i, len; /* declarations from ReadLengths */ /* declarations from SortLengths */ sf_entry *ejm1; int j; sf_entry *entry; /* int i already above */ sf_entry tmp; int entries; unsigned a, b; /* declarations from GenerateTrees */ int codelen, lvlstart, next, parents; /* int i, j already above */ /* for Compactor */ int tree_count[32]; /* end declarations */ /* next paraphrased from ReadLengths with adaption for Compactor. */ treeBytes = *cpt_char++; if(size < treeBytes * 2) { /* too many entries, something is wrong! */ (void)fprintf(stderr, "Bytes is: %d, expected: %d\n", treeBytes, size / 2); #ifdef SCAN do_error("macunpack: error in coding tree"); #endif /* SCAN */ exit(1); } for(i = 0; i < 32; i++) { tree_count[i] = 0; } i = 0; tree_MaxLength = 0; tree_entries = 0; while(treeBytes-- > 0) { /* adaption for Compactor */ len = (*cpt_char) >> 4; if(len != 0) { /* only if length unequal zero */ if(len > tree_MaxLength) { tree_MaxLength = len; } tree_count[len]++; tree_entry[tree_entries].Value = i; tree_entry[tree_entries++].BitLength = len; } i++; len = *cpt_char++ & NIBBLEMASK; if(len != 0) { /* only if length unequal zero */ if(len > tree_MaxLength) { tree_MaxLength = len; } tree_count[len]++; tree_entry[tree_entries].Value = i; tree_entry[tree_entries++].BitLength = len; } i++; } /* Compactor allows unused trailing codes in its Huffman tree! */ j = 0; for(i = 0; i <= tree_MaxLength; i++) { j = (j << 1) + tree_count[i]; } j = (1 < 0) && ((a = (ejm1 = &(entry[j - 1]))->BitLength) >= b)) { if((a == b) && (ejm1->Value <= tmp.Value)) { break; } *(ejm1 + 1) = *ejm1; --j; } entry[j] = tmp; } /* Adapted from GenerateTrees */ i = tree_entries - 1; /* starting at the upper end (and reversing loop) because of Compactor */ lvlstart = next = size * 2 + SLACK - 1; /* slight adaption because of different node format used */ for(codelen = tree_MaxLength; codelen >= 1; --codelen) { while((i >= 0) && (tree_entry[i].BitLength == codelen)) { Hufftree[next].byte = tree_entry[i].Value; Hufftree[next].flag = 1; next--; i--; } parents = next; if(codelen > 1) { /* reversed loop */ for(j = lvlstart; j > parents + 1; j-= 2) { Hufftree[next].one = &(Hufftree[j]); Hufftree[next].zero = &(Hufftree[j - 1]); Hufftree[next].flag = 0; next--; } } lvlstart = parents; } Hufftree[0].one = &(Hufftree[next + 2]); Hufftree[0].zero = &(Hufftree[next + 1]); Hufftree[0].flag = 0; } static int cpt_get6bits (void) { int b = 0, cn; b = (cpt_newbits >> 26) & 0x3f; cpt_bitsavail -= 6; cpt_newbits <<= 6; if(cpt_bitsavail < 16) { cn = (*cpt_char++ << 8); cn |= *cpt_char++; cpt_newbits |= (cn << (16 - cpt_bitsavail)); cpt_bitsavail += 16; } return b; } static int cpt_getbit (void) { int b; b = (cpt_newbits >> 31) & 1; cpt_bitsavail--; if(cpt_bitsavail < 16) { cpt_newbits |= (*cpt_char++ << 8); cpt_newbits |= *cpt_char++; cpt_bitsavail += 16; } cpt_newbits <<= 1; return b; } #else /* CPT */ int cpt; /* keep lint and some compilers happy */ #endif /* CPT */