/* 31/01/01 Tinytim compression/decompression utility main source file Program author: Peter Armstrong Copyright info: zlib library code : deflate 1.1.3 Copyright 1995-1998 Jean-loup Gailly : inflate 1.1.3 Copyright 1995-1998 Mark Adler Huffman code : (C) David Bourgin */ /* includes */ #include "stdio.h" #include "string.h" #include "malloc.h" #include "conio.h" #include "zlib.h" #include "zconf.h" #include "tiny.h" /* externs */ extern u_char *srce_p; /* pointers from codhuff.c */ extern u_char *start_p; extern u_char *end_p; extern u_char *dest_p; extern u_char *srce2_p; /* pointers from dcodhuff.c */ extern u_char *end2_p; extern u_char *dest2_p; extern int buflen,buflen2; extern int bytecount,bytecount2; extern const char deflate_copyright[]; extern const char inflate_copyright[]; /* globals */ S_ZIP zip; /* compression/decompression data structure */ S_HDR zhdr; /* compressed file header structure */ void main(int argc, char *argv[]) { printf("\t\tTinytim v1.0 - File Compression Utility\n"); if(CheckParams(argc, argv)) /* validate command line parameters */ { if(OpenFiles()) /* open input and output files */ { if(zip.option <= 2) /* -z of -h, compression */ { Compress(); /* compress file data */ } else DeCompress(); /* decompress file */ fclose(zip.outfile_p); fclose(zip.infile_p); } } if(zip.errval) { ErrorReport(); /* print error to screen if there was one */ while(!kbhit()); } } /* Compression routines, read in a file and compress using either zlib or huffman compression */ void Compress(void) { int wsize; if(zhdr.orig_size = ReadRawFile()) /* malloc read/write memory and read in file */ { if(Checkid()) /* check file not already compressed */ { printf("Compressing file..."); if(zip.option == OPT_Z) /* use zlib */ { zhdr.id = ID_ZLIB; /* identify compression type */ wsize = zCompress(); } else /* huffman compression */ { zhdr.id = ID_HUFF; wsize = hufCompress(); } if(wsize > 0) /* if compression successful */ { zhdr.comp_size = wsize; /* store compressed data size in header */ if(Writefile(zip.outfile_p,(u_char *)&zhdr,sizeof(S_HDR))) /* write header info to file */ { Writefile(zip.outfile_p,zip.wbuf_p,wsize); /* write compressed data to file */ } } } FreeMem(); } if(!zip.errval) ShowInfo(); /* display file compression info */ } /* Compress file data using huffman encoding routine Returns compressed data length if successful, NG otherwise */ int hufCompress(void) { int rtn = NG; int status; start_p = srce_p = zip.rbuf_p; /* ptr to file data to compress */ dest_p = zip.wbuf_p; /* ptr to compression buffer */ end_p = (srce_p + zip.rsize) -1; /* ptr at end of data */ buflen = (int) (zip.wsize - 12); /* for overrun test in huffman code */ status = huffmanencoding(); switch(status) { case T_MEM_ERROR: /* malloc failure */ zip.errval = MALLOC_FAIL; break; case T_BUF_ERROR: zip.errval = BUF_OVERRUN; /* buffer overrun, compressed data larger than original */ break; default: rtn = status; /* compression successful */ } return rtn; } /* Compress file data into buffer using zlib compress() routine Returns compressed data length if successful, NG otherwise */ int zCompress(void) { int rtn = NG; int status; /* call zlib compression routine */ status = compress((Bytef *)zip.wbuf_p,(uLongf *)&zip.wsize,(Bytef *)zip.rbuf_p,(uLong)zip.rsize); switch(status) { case Z_OK: rtn = zip.wsize; /* compress() changes value to compressed data size */ break; case Z_MEM_ERROR: /* work space could not be allocated */ zip.errval = MALLOC_FAIL; break; case Z_BUF_ERROR: /* wbuf_p compression buffer too small */ zip.errval = BUF_OVERRUN; /* probably trying to compress data that's already compressed */ } return rtn; } /* Gets size of file in bytes, Returns file size if successful, NG otherwise */ int GetfileSize(void) { int rtn = NG; long fsize; fseek(zip.infile_p,0,SEEK_END); /* file end */ fsize = ftell(zip.infile_p); /* get file position, = length */ fseek(zip.infile_p,0,SEEK_SET); /* back to start of file */ switch(fsize) { case -1: /* file error */ zip.errval = FILE_READ_ERROR; break; case 0: /* file empty */ zip.errval = FILE_SIZE_ZERO; break; default: rtn = fsize; } return rtn; } /* Test if file data is already compressed, check first 4bytes of file data where header id would be, Returns OK if not compressed, NG if already compressed */ int Checkid(void) { int rtn = NG; u_int *ident_p; ident_p = (u_int *) zip.rbuf_p; /* get ptr to value at start of data */ if((*ident_p != ID_ZLIB) && (*ident_p != ID_HUFF)) rtn = OK; else zip.errval = FILE_ALREADY_COMPRESSED; return rtn; } /* Get file size, allocate space for file data and compression buffer, read in file data Returns file size if successful, NG otherwise */ int ReadRawFile(void) { int rtn = NG; u_int rsize,wsize; if(rsize = GetfileSize()) { /* allocate enough space for compressed data */ wsize = rsize + (int)(rsize * 0.001) + 32; if(AllocMem(rsize,wsize)) { if(Readfile(zip.infile_p,zip.rbuf_p,rsize)) { zip.wsize = wsize; rtn = zip.rsize = rsize; } else { FreeMem(); zip.errval = FILE_READ_ERROR; } } } return rtn; } /* Allocate memory read and write buffers, files are read in in one go, read/writebufsize - size of buffers to allocate Returns OK, NG otherwise */ int AllocMem(u_int readbufsize, u_int writebufsize) { int rtn = NG; if(zip.rbuf_p = malloc(readbufsize)) { if(zip.wbuf_p = malloc(writebufsize)) rtn = OK; else free(zip.rbuf_p); } if(!rtn) zip.errval = MALLOC_FAIL; return rtn; } /* Free read and write buffer space */ void FreeMem(void) { free(zip.rbuf_p); free(zip.wbuf_p); } /* read file data to buffer, file_p - file ptr, *rbuffer - ptr to data buffer, readsize - num of bytes to read, Returns OK if successful, NG otherwise */ int Readfile(FILE *file_p, u_char *rbuffer, u_int readsize) { int rtn = NG; if(fread(rbuffer,1,readsize,file_p) == readsize) rtn = OK; else zip.errval = FILE_READ_ERROR; return rtn; } /* write data to file, file_p - file ptr, *wbuffer - ptr to data buffer, writesize - num of bytes to write, Returns OK if successful, NG otherwise */ int Writefile(FILE *file_p, u_char *wbuffer, u_int writesize) { int rtn = NG; if(fwrite(wbuffer,1,writesize,file_p) == writesize) rtn = OK; else zip.errval = FILE_WRITE_ERROR; return rtn; } /* Decompression routine, read in a compressed file and output the uncompressed data file */ void DeCompress(void) { char decstr[8] = "file"; char *str_p = decstr; int wsize; if(ReadCompressedFile()) { if(strlen(zip.infile) < 20) /* display name of file if it fits */ str_p = zip.infile; printf("Decompressing %s...",str_p); if(zhdr.id == ID_ZLIB) wsize = zDeCompress(); else wsize = hufDeCompress(); if(wsize > 0) { Writefile(zip.outfile_p,zip.wbuf_p,wsize); printf(" done."); } FreeMem(); } } /* Decompress file data into buffer using zlib uncompress() routine, Returns uncompressed data length if successful, NG otherwise */ int zDeCompress(void) { int rtn = NG; int status; /* uncompress file data, zip.wsize changed to uncompressed file length (s/b the same as previous value) */ status = uncompress((Bytef *)zip.wbuf_p,(uLongf *)&zip.wsize,(Bytef *)zip.rbuf_p,(uLong)zip.rsize); switch(status) { case Z_OK: /* check uncompressed data length is the same as header value */ if(zip.wsize == zhdr.orig_size) rtn = zip.wsize; else zip.errval = LENGTH_MISREPORTED; break; case Z_MEM_ERROR: /* work space could not be allocated */ zip.errval = MALLOC_FAIL; break; case Z_BUF_ERROR: /* uncompressed data buffer too small */ zip.errval = BUF_OVERRUN; case Z_DATA_ERROR: /* compressed data corrupt */ zip.errval = FILE_CORRUPT; } return rtn; } /* Decompress file data using huffman decoding routine, Returns uncompressed data length if successful, NG if malloc fails */ int hufDeCompress(void) { int rtn = NG; int status; srce2_p = zip.rbuf_p; /* ptr to compressed data */ end2_p = (zip.rbuf_p + zip.rsize) -1; /* ptr to last byte of compressed data */ dest2_p = zip.wbuf_p; /* ptr to buffer for uncompressed data */ buflen2 = zhdr.orig_size; /* to check for buffer overrun */ status = huffmandecoding(); /* decode data */ switch(status) { case T_MEM_ERROR: zip.errval = MALLOC_FAIL; /* malloc failure */ break; case T_BUF_ERROR: zip.errval = BUF_OVERRUN; /* overrun, compressed data too large */ break; default: if(status == (int)zhdr.orig_size) /* check uncompressed length is as was reported */ rtn = status; /* success, return uncompressed data length */ else zip.errval = LENGTH_MISREPORTED; } return rtn; } /* Read in file header, allocate read and write buffer space, then read in file. Returns OK if successful, NG otherwise */ int ReadCompressedFile(void) { int rtn = NG; if(ReadHeader()) { if(AllocMem(zip.rsize,zip.wsize)) { if(Readfile(zip.infile_p,zip.rbuf_p,zip.rsize)) { rtn = OK; } } } return rtn; } /* Read header from compressed file, store length of compressed data and original file, Returns OK if successful, NG otherwise */ int ReadHeader(void) { int rtn = NG; u_int size; if(size = GetfileSize()) /* check file has a header */ { if(size > sizeof(S_HDR)) { if(Readfile(zip.infile_p,(u_char *)&zhdr,sizeof(S_HDR))) /* read header from file */ { if((zhdr.id == ID_ZLIB) || (zhdr.id == ID_HUFF)) /* check it's a compressed file */ { zip.rsize = zhdr.comp_size; /* length of compressed file data */ zip.wsize = zhdr.orig_size; /* length of original file */ rtn = OK; } else zip.errval = NOT_TINYTIM_FILE; /* not tinytim compressed file */ } } else zip.errval = NOT_TINYTIM_FILE; } return rtn; } /* Open input and output files Returns OK if successful, NG otherwise */ int OpenFiles(void) { int rtn = NG; if(zip.infile_p = fopen(zip.infile,"rb")) { if(zip.outfile_p = fopen(zip.outfile,"wb")) { rtn = OK; } else { fclose(zip.infile_p); zip.errval = INVALID_OUTFILE; } } else zip.errval = INVALID_INFILE; return rtn; } /* Test and store command line option Returns > 0 if successful, NG otherwise */ int GetOption(char *optstr) { int option = NG; char optchar; if((strlen(optstr) == 2) && (optstr[0] == '-')) /* check string is correct */ { optchar = optstr[1]; switch(optchar) /* test option value */ { case 'z': option = OPT_Z; break; case 'h': option = OPT_H; break; case 'd': option = OPT_D; break; default: zip.errval = INVALID_PARAM; /* other values rejected */ } } else zip.errval = INVALID_PARAM; return option; } /* Check command line parameters are valid Returns OK if option and file names present, NG otherwise */ int CheckParams(int argc, char *argv[]) { int rtn = NG; switch(argc) { case 1: /* no parameters, display help */ Help(); break; case 2: case 3: zip.errval = INSUF_PARAM; /* insufficient cmd line parameters */ break; default: if(zip.option = GetOption(argv[1])) /* test & store valid option */ { /* check file names aren't the same */ if(strcmp(argv[2],argv[3])) { strcpy(zip.infile,argv[2]); /* store file names */ strcpy(zip.outfile,argv[3]); return OK; } else zip.errval = DUP_FILENAME; } } return rtn; } /* Display file sizes and compression ratio on screen, only displays for a split second unless run from a batch file */ void ShowInfo(void) { char clrstr[] = "\t "; char infostr[32] = "File: "; char *str_p = clrstr; float orig,comp; float ratio; if(strlen(zip.infile) < 13) /* display file name if it will fit */ { strcat(infostr,zip.infile); str_p = infostr; } orig = (float) zhdr.orig_size; comp = (float) zhdr.comp_size + sizeof(S_HDR); /* work out ratio of compressed file size compared to the original */ //if(comp < orig) // ratio = (orig - comp) / orig * 100; // else ratio = (comp / orig) * 100; printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%s ",str_p); printf("Size: %d Compressed size: %d Ratio: %.0f%%\n",zhdr.orig_size, zhdr.comp_size+sizeof(S_HDR),ratio); } /* inform user of reason for error if one occurred */ void ErrorReport(void) { char *errstr = "\nError:"; char *errmsg[15] = { "", "Insufficient Parameters", "Invalid option parameter", "Duplicate input and output file names", "Cannot open input file", "Cannot open output file", "Error reading input file", "Input file empty", "Input file already compressed", "Cannot allocate required memory space", "Cannot compress file data", "Error writing to output file", "Cannot decompress, not a tinytim file", "Cannot decompress, file data corrupt", "Cannot decompress, uncompressed file length misreported"}; if(zip.errval) { printf("%s %s.\n",errstr,errmsg[zip.errval]); } } /* Display program syntax and copyright information */ void Help(void) { printf("\nSyntax:\t tiny