#include "globals.h"
#include "cpt.h"
#include "crc.h"

char *malloc();
unsigned long get4();
unsigned short get2();
void cpt_folder();
void cpt_uncompact();
unsigned char *cpt_data;
unsigned char cpt_LZbuff[CIRCSIZE];
unsigned int cpt_LZptr;
unsigned char *cpt_char;
unsigned long cpt_crc;
unsigned long cpt_inlength;
unsigned long cpt_outlength;

void cpt()
{
    struct cptHdr cpthdr;
    struct fileHdr filehdr;
    char *cptindex;
    char *cptptr;
    int i;

    updcrc = zip_updcrc;
    crcinit = zip_crcinit;
    cpt_crc = INIT_CRC;
    if(readcpthdr(&cpthdr) == 0) {
	fprintf(stderr, "Can't read archive header\n");
	exit(1);
    }

    cptindex = malloc((unsigned)(cpthdr.entries * FILEHDRSIZE));
    if(cptindex == NULL) {
	fprintf(stderr, "Insufficient memory, aborting\n");
	exit(1);
    }
    cptptr = cptindex;
    if(fread(cptptr, 1, (int)cpthdr.commentsize, infp) != cpthdr.commentsize) {
	fprintf(stderr, "Can't read comment.\n");
	exit(1);
    }
    cpt_crc = (*updcrc)(cpt_crc, cptptr, cpthdr.commentsize);

    for(i = 0; i < cpthdr.entries; i++) {
	*cptptr = getc(infp);
	cpt_crc = (*updcrc)(cpt_crc, cptptr, 1);
	if(*cptptr & 0x80) {
	    cptptr[F_FOLDER] = 1;
	    *cptptr &= 0x3f;
	} else {
	    cptptr[F_FOLDER] = 0;
	}
	if(fread(cptptr + 1, 1, *cptptr, infp) != *cptptr) {
	    fprintf(stderr, "Can't read file header #%d\n", i+1);
	    exit(1);
	}
	cpt_crc = (*updcrc)(cpt_crc, cptptr + 1, *cptptr);
	if(cptptr[F_FOLDER]) {
	    if(fread(cptptr + F_FOLDERSIZE, 1, 2, infp) != 2) {
		fprintf(stderr, "Can't read file header #%d\n", i+1);
		exit(1);
	    }
	    cpt_crc = (*updcrc)(cpt_crc, cptptr + F_FOLDERSIZE, 2);
	} else {
	    if(fread(cptptr + F_VOLUME, 1, FILEHDRSIZE - F_VOLUME, infp) !=
		FILEHDRSIZE - F_VOLUME) {
		fprintf(stderr, "Can't read file header #%d\n", i+1);
		exit(1);
	    }
	    cpt_crc = (*updcrc)(cpt_crc, cptptr + F_VOLUME, FILEHDRSIZE - F_VOLUME);
	}
	cptptr += FILEHDRSIZE;
    }
    if(cpt_crc != cpthdr.hdrcrc) {
	fprintf(stderr, "Header CRC mismatch: got 0x%08x, need 0x%08x\n",
		cpthdr.hdrcrc, cpt_crc);
    }

    cptptr = cptindex;
    for(i = 0; i < cpthdr.entries; i++) {
	if(cpt_filehdr(&filehdr, cptptr) == -1) {
	    fprintf(stderr, "Can't read file header #%d\n", i+1);
	    exit(1);
	}
	if(filehdr.folder) {
	    cpt_folder(text, filehdr, cptptr);
	    i += filehdr.foldersize;
	    cptptr += filehdr.foldersize * FILEHDRSIZE;
	} else {
	    cpt_uncompact(filehdr);
	}
	cptptr += FILEHDRSIZE;
    }
}

int readcpthdr(s)
struct cptHdr *s;
{
    char temp[CHDRSIZE];

    if(fread(temp, 1, CPTHDRSIZE, infp) != CPTHDRSIZE) {
	return 0;
    }

    if(temp[C_SIGNATURE] != 1) {
	fprintf(stderr, "Not a Compactor file\n");
	return 0;
    }

    s->offset = get4(temp + C_IOFFSET);

    cpt_data = (unsigned char *)malloc((unsigned)s->offset);
    if(cpt_data == NULL) {
	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, temp + CPTHDRSIZE + C_ENTRIES, 3);
    s->hdrcrc = get4(temp + CPTHDRSIZE + C_HDRCRC);
    s->entries = get2(temp + CPTHDRSIZE + C_ENTRIES);
    s->commentsize = temp[CPTHDRSIZE + C_COMMENT];

    return 1;
}

