macutils/macunpack/cpt.c

717 lines
18 KiB
C

#include "macunpack.h"
#ifdef DD
#ifndef CPT
#define CPT
#endif /* CPT */
#endif /* DD */
#ifdef CPT
#define CPT_INTERNAL
#include "cpt.h"
#include <stdlib.h>
#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 <<tree_MaxLength) - j;
/* Insert the unused entries for sorting purposes. */
for(i = 0; i < j; i++) {
tree_entry[tree_entries].Value = size;
tree_entry[tree_entries++].BitLength = tree_MaxLength;
}
/* adaption from SortLengths */
entry = &(tree_entry[0]);
entries = tree_entries;
for(i = 0; ++i < entries;) {
tmp = entry[i];
b = tmp.BitLength;
j = i;
while((j > 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 */