From 729b6e4f33419489415a6111bb98e5d95221a338 Mon Sep 17 00:00:00 2001 From: Dominik Riebeling Date: Mon, 8 Jun 2020 21:44:02 +0200 Subject: rbutil: Update libmspack to 0.10.1alpha. Update to the most recent release. Fix name / include clashes, as has been done before. Change-Id: Ia712bb2b5f4b9018b65a46b8bdd04ba42363be8b --- rbutil/rbutilqt/mspack/README.ROCKBOX | 4 +- rbutil/rbutilqt/mspack/cab.h | 27 +- rbutil/rbutilqt/mspack/cabd.c | 541 ++++++++++++---------- rbutil/rbutilqt/mspack/chmd.c | 667 ++++++++++++++------------- rbutil/rbutilqt/mspack/kwajd.c | 365 +++++++-------- rbutil/rbutilqt/mspack/lzss.h | 8 +- rbutil/rbutilqt/mspack/lzssd.c | 84 ++-- rbutil/rbutilqt/mspack/lzx.h | 61 ++- rbutil/rbutilqt/mspack/lzxd.c | 793 ++++++++++++++++++++------------- rbutil/rbutilqt/mspack/mspack.h | 316 ++++++++++--- rbutil/rbutilqt/mspack/mszip.h | 17 +- rbutil/rbutilqt/mspack/mszipd.c | 268 ++++++----- rbutil/rbutilqt/mspack/qtm.h | 8 +- rbutil/rbutilqt/mspack/qtmd.c | 235 +++++----- rbutil/rbutilqt/mspack/readbits.h | 104 ++--- rbutil/rbutilqt/mspack/readhuff.h | 129 +++--- rbutil/rbutilqt/mspack/system-mspack.c | 2 +- rbutil/rbutilqt/mspack/system-mspack.h | 4 +- rbutil/rbutilqt/mspack/szddd.c | 70 +-- 19 files changed, 2121 insertions(+), 1582 deletions(-) (limited to 'rbutil/rbutilqt') diff --git a/rbutil/rbutilqt/mspack/README.ROCKBOX b/rbutil/rbutilqt/mspack/README.ROCKBOX index db5ba7f482..220691af2c 100644 --- a/rbutil/rbutilqt/mspack/README.ROCKBOX +++ b/rbutil/rbutilqt/mspack/README.ROCKBOX @@ -1,6 +1,6 @@ This folder contains the mspack project for MS files compression/decompression. These files are distributed under the LGPL. -The source files have been last synced with libmspack-0.3alpha -http://sourceforge.net/projects/libmspack/on January 28, 2013 +The source files have been last synced with libmspack-0.10.1alpha +https://www.cabextract.org.uk/libmspack/ on June 8, 2020 diff --git a/rbutil/rbutilqt/mspack/cab.h b/rbutil/rbutilqt/mspack/cab.h index 78ec8e60db..79d9951252 100644 --- a/rbutil/rbutilqt/mspack/cab.h +++ b/rbutil/rbutilqt/mspack/cab.h @@ -1,5 +1,5 @@ /* This file is part of libmspack. - * (C) 2003-2004 Stuart Caie. + * (C) 2003-2018 Stuart Caie. * * libmspack is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License (LGPL) version 2.1 @@ -10,10 +10,6 @@ #ifndef MSPACK_CAB_H #define MSPACK_CAB_H 1 -#include "mszip.h" -#include "qtm.h" -#include "lzx.h" - /* generic CAB definitions */ /* structure offsets */ @@ -70,6 +66,22 @@ #define CAB_BLOCKMAX (32768) #define CAB_INPUTMAX (CAB_BLOCKMAX+6144) +/* input buffer needs to be CAB_INPUTMAX + 1 byte to allow for max-sized block + * plus 1 trailer byte added by cabd_sys_read_block() for Quantum alignment. + * + * When MSCABD_PARAM_SALVAGE is set, block size is not checked so can be + * up to 65535 bytes, so max input buffer size needed is 65535 + 1 + */ +#define CAB_INPUTMAX_SALVAGE (65535) +#define CAB_INPUTBUF (CAB_INPUTMAX_SALVAGE + 1) + +/* There are no more than 65535 data blocks per folder, so a folder cannot + * be more than 32768*65535 bytes in length. As files cannot span more than + * one folder, this is also their max offset, length and offset+length limit. + */ +#define CAB_FOLDERMAX (65535) +#define CAB_LENGTHMAX (CAB_BLOCKMAX * CAB_FOLDERMAX) + /* CAB compression definitions */ struct mscab_compressor_p { @@ -85,6 +97,7 @@ struct mscabd_decompress_state { struct mscabd_folder_data *data; /* current folder split we're in */ unsigned int offset; /* uncompressed offset within folder */ unsigned int block; /* which block are we decompressing? */ + off_t outlen; /* cumulative sum of block output sizes */ struct mspack_system sys; /* special I/O code for decompressor */ int comp_type; /* type of compression used by folder */ int (*decompress)(void *, off_t); /* decompressor code */ @@ -93,14 +106,14 @@ struct mscabd_decompress_state { struct mspack_file *infh; /* input file handle */ struct mspack_file *outfh; /* output file handle */ unsigned char *i_ptr, *i_end; /* input data consumed, end */ - unsigned char input[CAB_INPUTMAX]; /* one input block of data */ + unsigned char input[CAB_INPUTBUF]; /* one input block of data */ }; struct mscab_decompressor_p { struct mscab_decompressor base; struct mscabd_decompress_state *d; struct mspack_system *system; - int param[3]; /* !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!! */ + int buf_size, searchbuf_size, fix_mszip, salvage; /* params */ int error, read_error; }; diff --git a/rbutil/rbutilqt/mspack/cabd.c b/rbutil/rbutilqt/mspack/cabd.c index 6549d7b8cf..ae66769b24 100644 --- a/rbutil/rbutilqt/mspack/cabd.c +++ b/rbutil/rbutilqt/mspack/cabd.c @@ -1,5 +1,5 @@ /* This file is part of libmspack. - * (C) 2003-2011 Stuart Caie. + * (C) 2003-2018 Stuart Caie. * * libmspack is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License (LGPL) version 2.1 @@ -23,7 +23,9 @@ #include "system-mspack.h" #include "cab.h" -#include +#include "mszip.h" +#include "lzx.h" +#include "qtm.h" /* Notes on compliance with cabinet specification: * @@ -72,10 +74,9 @@ static void cabd_close( struct mscab_decompressor *base, struct mscabd_cabinet *origcab); static int cabd_read_headers( struct mspack_system *sys, struct mspack_file *fh, - struct mscabd_cabinet_p *cab, off_t offset, int quiet); + struct mscabd_cabinet_p *cab, off_t offset, int salvage, int quiet); static char *cabd_read_string( - struct mspack_system *sys, struct mspack_file *fh, - struct mscabd_cabinet_p *cab, int *error); + struct mspack_system *sys, struct mspack_file *fh, int *error); static struct mscabd_cabinet *cabd_search( struct mscab_decompressor *base, const char *filename); @@ -110,7 +111,7 @@ static int cabd_sys_write( struct mspack_file *file, void *buffer, int bytes); static int cabd_sys_read_block( struct mspack_system *sys, struct mscabd_decompress_state *d, int *out, - int ignore_cksum); + int ignore_cksum, int ignore_blocksize); static unsigned int cabd_checksum( unsigned char *data, unsigned int bytes, unsigned int cksum); static struct noned_state *noned_init( @@ -155,9 +156,10 @@ struct mscab_decompressor * self->d = NULL; self->error = MSPACK_ERR_OK; - self->param[MSCABD_PARAM_SEARCHBUF] = 32768; - self->param[MSCABD_PARAM_FIXMSZIP] = 0; - self->param[MSCABD_PARAM_DECOMPBUF] = 4096; + self->searchbuf_size = 32768; + self->fix_mszip = 0; + self->buf_size = 4096; + self->salvage = 0; } return (struct mscab_decompressor *) self; } @@ -171,9 +173,9 @@ void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) { struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; if (self) { struct mspack_system *sys = self->system; - cabd_free_decomp(self); if (self->d) { if (self->d->infh) sys->close(self->d->infh); + cabd_free_decomp(self); sys->free(self->d); } sys->free(self); @@ -187,7 +189,7 @@ void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) { * opens a file and tries to read it as a cabinet file */ static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, - const char *filename) + const char *filename) { struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; struct mscabd_cabinet_p *cab = NULL; @@ -201,10 +203,10 @@ static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { cab->base.filename = filename; - error = cabd_read_headers(sys, fh, cab, (off_t) 0, 0); + error = cabd_read_headers(sys, fh, cab, (off_t) 0, self->salvage, 0); if (error) { - cabd_close(base, (struct mscabd_cabinet *) cab); - cab = NULL; + cabd_close(base, (struct mscabd_cabinet *) cab); + cab = NULL; } self->error = error; } @@ -225,7 +227,7 @@ static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, * frees all memory associated with a given mscabd_cabinet. */ static void cabd_close(struct mscab_decompressor *base, - struct mscabd_cabinet *origcab) + struct mscabd_cabinet *origcab) { struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; struct mscabd_folder_data *dat, *ndat; @@ -253,16 +255,16 @@ static void cabd_close(struct mscab_decompressor *base, /* free folder decompression state if it has been decompressed */ if (self->d && (self->d->folder == (struct mscabd_folder_p *) fol)) { - if (self->d->infh) sys->close(self->d->infh); - cabd_free_decomp(self); - sys->free(self->d); - self->d = NULL; + if (self->d->infh) sys->close(self->d->infh); + cabd_free_decomp(self); + sys->free(self->d); + self->d = NULL; } /* free folder data segments */ for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) { - ndat = dat->next; - sys->free(dat); + ndat = dat->next; + sys->free(dat); } sys->free(fol); } @@ -304,11 +306,11 @@ static void cabd_close(struct mscab_decompressor *base, * for folders and files as necessary */ static int cabd_read_headers(struct mspack_system *sys, - struct mspack_file *fh, - struct mscabd_cabinet_p *cab, - off_t offset, int quiet) + struct mspack_file *fh, + struct mscabd_cabinet_p *cab, + off_t offset, int salvage, int quiet) { - int num_folders, num_files, folder_resv, i, x; + int num_folders, num_files, folder_resv, i, x, err, fidx; struct mscabd_folder_p *fol, *linkfol = NULL; struct mscabd_file *file, *linkfile = NULL; unsigned char buf[64]; @@ -364,6 +366,7 @@ static int cabd_read_headers(struct mspack_system *sys, /* read the reserved-sizes part of header, if present */ cab->base.flags = EndGetI16(&buf[cfhead_Flags]); + if (cab->base.flags & cfheadRESERVE_PRESENT) { if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) { return MSPACK_ERR_READ; @@ -379,7 +382,7 @@ static int cabd_read_headers(struct mspack_system *sys, /* skip the reserved header */ if (cab->base.header_resv) { if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) { - return MSPACK_ERR_SEEK; + return MSPACK_ERR_SEEK; } } } @@ -391,14 +394,18 @@ static int cabd_read_headers(struct mspack_system *sys, /* read name and info of preceeding cabinet in set, if present */ if (cab->base.flags & cfheadPREV_CABINET) { - cab->base.prevname = cabd_read_string(sys, fh, cab, &x); if (x) return x; - cab->base.previnfo = cabd_read_string(sys, fh, cab, &x); if (x) return x; + cab->base.prevname = cabd_read_string(sys, fh, &err); + if (err) return err; + cab->base.previnfo = cabd_read_string(sys, fh, &err); + if (err) return err; } /* read name and info of next cabinet in set, if present */ if (cab->base.flags & cfheadNEXT_CABINET) { - cab->base.nextname = cabd_read_string(sys, fh, cab, &x); if (x) return x; - cab->base.nextinfo = cabd_read_string(sys, fh, cab, &x); if (x) return x; + cab->base.nextname = cabd_read_string(sys, fh, &err); + if (err) return err; + cab->base.nextinfo = cabd_read_string(sys, fh, &err); + if (err) return err; } /* read folders */ @@ -408,7 +415,7 @@ static int cabd_read_headers(struct mspack_system *sys, } if (folder_resv) { if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) { - return MSPACK_ERR_SEEK; + return MSPACK_ERR_SEEK; } } @@ -447,45 +454,44 @@ static int cabd_read_headers(struct mspack_system *sys, file->offset = EndGetI32(&buf[cffile_FolderOffset]); /* set folder pointer */ - x = EndGetI16(&buf[cffile_FolderIndex]); - if (x < cffileCONTINUED_FROM_PREV) { - /* normal folder index; count up to the correct folder. the folder - * pointer will be NULL if folder index is invalid */ - struct mscabd_folder *ifol = cab->base.folders; - while (x--) if (ifol) ifol = ifol->next; - file->folder = ifol; - - if (!ifol) { - sys->free(file); - D(("invalid folder index")) - return MSPACK_ERR_DATAFORMAT; + fidx = EndGetI16(&buf[cffile_FolderIndex]); + if (fidx < cffileCONTINUED_FROM_PREV) { + /* normal folder index; count up to the correct folder */ + if (fidx < num_folders) { + struct mscabd_folder *ifol = cab->base.folders; + while (fidx--) if (ifol) ifol = ifol->next; + file->folder = ifol; + } + else { + D(("invalid folder index")) + file->folder = NULL; } } else { /* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or * CONTINUED_PREV_AND_NEXT */ - if ((x == cffileCONTINUED_TO_NEXT) || - (x == cffileCONTINUED_PREV_AND_NEXT)) + if ((fidx == cffileCONTINUED_TO_NEXT) || + (fidx == cffileCONTINUED_PREV_AND_NEXT)) { - /* get last folder */ - struct mscabd_folder *ifol = cab->base.folders; - while (ifol->next) ifol = ifol->next; - file->folder = ifol; - - /* set "merge next" pointer */ - fol = (struct mscabd_folder_p *) ifol; - if (!fol->merge_next) fol->merge_next = file; + /* get last folder */ + struct mscabd_folder *ifol = cab->base.folders; + while (ifol->next) ifol = ifol->next; + file->folder = ifol; + + /* set "merge next" pointer */ + fol = (struct mscabd_folder_p *) ifol; + if (!fol->merge_next) fol->merge_next = file; } - if ((x == cffileCONTINUED_FROM_PREV) || - (x == cffileCONTINUED_PREV_AND_NEXT)) + if ((fidx == cffileCONTINUED_FROM_PREV) || + (fidx == cffileCONTINUED_PREV_AND_NEXT)) { - /* get first folder */ - file->folder = cab->base.folders; + /* get first folder */ + file->folder = cab->base.folders; - /* set "merge prev" pointer */ - fol = (struct mscabd_folder_p *) file->folder; - if (!fol->merge_prev) fol->merge_prev = file; + /* set "merge prev" pointer */ + fol = (struct mscabd_folder_p *) file->folder; + if (!fol->merge_prev) fol->merge_prev = file; } } @@ -502,10 +508,14 @@ static int cabd_read_headers(struct mspack_system *sys, file->date_y = (x >> 9) + 1980; /* get filename */ - file->filename = cabd_read_string(sys, fh, cab, &x); - if (x) { + file->filename = cabd_read_string(sys, fh, &err); + + /* if folder index or filename are bad, either skip it or fail */ + if (err || !file->folder) { + sys->free(file->filename); sys->free(file); - return x; + if (salvage) continue; + return err ? err : MSPACK_ERR_DATAFORMAT; } /* link file entry into file list */ @@ -514,23 +524,34 @@ static int cabd_read_headers(struct mspack_system *sys, linkfile = file; } + if (cab->base.files == NULL) { + /* We never actually added any files to the file list. Something went wrong. + * The file header may have been invalid */ + D(("No files found, even though header claimed to have %d files", num_files)) + return MSPACK_ERR_DATAFORMAT; + } + return MSPACK_ERR_OK; } static char *cabd_read_string(struct mspack_system *sys, - struct mspack_file *fh, - struct mscabd_cabinet_p *cab, int *error) + struct mspack_file *fh, int *error) { off_t base = sys->tell(fh); char buf[256], *str; - unsigned int len, i, ok; - (void)cab; + int len, i, ok; /* read up to 256 bytes */ - len = sys->read(fh, &buf[0], 256); + if ((len = sys->read(fh, &buf[0], 256)) <= 0) { + *error = MSPACK_ERR_READ; + return NULL; + } /* search for a null terminator in the buffer */ for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; } + /* reject empty strings */ + if (i == 0) ok = 0; + if (!ok) { *error = MSPACK_ERR_DATAFORMAT; return NULL; @@ -566,7 +587,7 @@ static char *cabd_read_string(struct mspack_system *sys, * break out of the loop and be sure that all resources are freed */ static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, - const char *filename) + const char *filename) { struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; struct mscabd_cabinet_p *cab = NULL; @@ -579,7 +600,7 @@ static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, sys = self->system; /* allocate a search buffer */ - search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->param[MSCABD_PARAM_SEARCHBUF]); + search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->searchbuf_size); if (!search_buf) { self->error = MSPACK_ERR_NOMEMORY; return NULL; @@ -589,21 +610,21 @@ static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { if (!(self->error = mspack_sys_filelen(sys, fh, &filelen))) { self->error = cabd_find(self, search_buf, fh, filename, - filelen, &firstlen, &cab); + filelen, &firstlen, &cab); } /* truncated / extraneous data warning: */ if (firstlen && (firstlen != filelen) && - (!cab || (cab->base.base_offset == 0))) + (!cab || (cab->base.base_offset == 0))) { if (firstlen < filelen) { - sys->message(fh, "WARNING; possible %" LD - " extra bytes at end of file.", - filelen - firstlen); + sys->message(fh, "WARNING; possible %" LD + " extra bytes at end of file.", + filelen - firstlen); } else { - sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.", - firstlen - filelen); + sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.", + firstlen - filelen); } } @@ -620,8 +641,8 @@ static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, } static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, - struct mspack_file *fh, const char *filename, off_t flen, - off_t *firstlen, struct mscabd_cabinet_p **firstcab) + struct mspack_file *fh, const char *filename, off_t flen, + off_t *firstlen, struct mscabd_cabinet_p **firstcab) { struct mscabd_cabinet_p *cab, *link = NULL; off_t caboff, offset, length; @@ -630,7 +651,7 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, unsigned int cablen_u32 = 0, foffset_u32 = 0; int false_cabs = 0; -#ifndef LARGEFILE_SUPPORT +#if !LARGEFILE_SUPPORT /* detect 32-bit off_t overflow */ if (flen < 0) { sys->message(fh, largefile_msg); @@ -643,8 +664,8 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, /* search length is either the full length of the search buffer, or the * amount of data remaining to the end of the file, whichever is less. */ length = flen - offset; - if (length > self->param[MSCABD_PARAM_SEARCHBUF]) { - length = self->param[MSCABD_PARAM_SEARCHBUF]; + if (length > self->searchbuf_size) { + length = self->searchbuf_size; } /* fill the search buffer with data from disk */ @@ -654,22 +675,21 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, /* FAQ avoidance strategy */ if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) { - sys->message(fh, "WARNING; found InstallShield header. " - "This is probably an InstallShield file. " - "Use UNSHIELD from www.synce.org to unpack it."); + sys->message(fh, "WARNING; found InstallShield header. Use unshield " + "(https://github.com/twogood/unshield) to unpack this file"); } /* read through the entire buffer. */ for (p = &buf[0], pend = &buf[length]; p < pend; ) { switch (state) { - /* starting state */ + /* starting state */ case 0: - /* we spend most of our time in this while loop, looking for - * a leading 'M' of the 'MSCF' signature */ - while (p < pend && *p != 0x4D) p++; - /* if we found tht 'M', advance state */ - if (p++ < pend) state = 1; - break; + /* we spend most of our time in this while loop, looking for + * a leading 'M' of the 'MSCF' signature */ + while (p < pend && *p != 0x4D) p++; + /* if we found tht 'M', advance state */ + if (p++ < pend) state = 1; + break; /* verify that the next 3 bytes are 'S', 'C' and 'F' */ case 1: state = (*p++ == 0x53) ? 2 : 0; break; @@ -691,70 +711,71 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, case 17: foffset_u32 |= *p++ << 8; state++; break; case 18: foffset_u32 |= *p++ << 16; state++; break; case 19: foffset_u32 |= *p++ << 24; - /* now we have recieved 20 bytes of potential cab header. work out - * the offset in the file of this potential cabinet */ - caboff = offset + (p - &buf[0]) - 20; - - /* should reading cabinet fail, restart search just after 'MSCF' */ - offset = caboff + 4; - - /* capture the "length of cabinet" field if there is a cabinet at - * offset 0 in the file, regardless of whether the cabinet can be - * read correctly or not */ - if (caboff == 0) *firstlen = (off_t) cablen_u32; - - /* check that the files offset is less than the alleged length of - * the cabinet, and that the offset + the alleged length are - * 'roughly' within the end of overall file length */ - if ((foffset_u32 < cablen_u32) && - ((caboff + (off_t) foffset_u32) < (flen + 32)) && - ((caboff + (off_t) cablen_u32) < (flen + 32)) ) - { - /* likely cabinet found -- try reading it */ - if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { - return MSPACK_ERR_NOMEMORY; - } - cab->base.filename = filename; - if (cabd_read_headers(sys, fh, cab, caboff, 1)) { - /* destroy the failed cabinet */ - cabd_close((struct mscab_decompressor *) self, - (struct mscabd_cabinet *) cab); - false_cabs++; - } - else { - /* cabinet read correctly! */ - - /* link the cab into the list */ - if (!link) *firstcab = cab; - else link->base.next = (struct mscabd_cabinet *) cab; - link = cab; - - /* cause the search to restart after this cab's data. */ - offset = caboff + (off_t) cablen_u32; - -#ifndef LARGEFILE_SUPPORT - /* detect 32-bit off_t overflow */ - if (offset < caboff) { - sys->message(fh, largefile_msg); - return MSPACK_ERR_OK; - } -#endif - } - } - - /* restart search */ - if (offset >= flen) return MSPACK_ERR_OK; - if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { - return MSPACK_ERR_SEEK; - } - length = 0; - p = pend; - state = 0; - break; + /* now we have recieved 20 bytes of potential cab header. work out + * the offset in the file of this potential cabinet */ + caboff = offset + (p - &buf[0]) - 20; + + /* should reading cabinet fail, restart search just after 'MSCF' */ + offset = caboff + 4; + + /* capture the "length of cabinet" field if there is a cabinet at + * offset 0 in the file, regardless of whether the cabinet can be + * read correctly or not */ + if (caboff == 0) *firstlen = (off_t) cablen_u32; + + /* check that the files offset is less than the alleged length of + * the cabinet, and that the offset + the alleged length are + * 'roughly' within the end of overall file length. In salvage + * mode, don't check the alleged length, allow it to be garbage */ + if ((foffset_u32 < cablen_u32) && + ((caboff + (off_t) foffset_u32) < (flen + 32)) && + (((caboff + (off_t) cablen_u32) < (flen + 32)) || self->salvage)) + { + /* likely cabinet found -- try reading it */ + if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { + return MSPACK_ERR_NOMEMORY; + } + cab->base.filename = filename; + if (cabd_read_headers(sys, fh, cab, caboff, self->salvage, 1)) { + /* destroy the failed cabinet */ + cabd_close((struct mscab_decompressor *) self, + (struct mscabd_cabinet *) cab); + false_cabs++; + } + else { + /* cabinet read correctly! */ + + /* link the cab into the list */ + if (!link) *firstcab = cab; + else link->base.next = (struct mscabd_cabinet *) cab; + link = cab; + + /* cause the search to restart after this cab's data. */ + offset = caboff + (off_t) cablen_u32; + +#if !LARGEFILE_SUPPORT + /* detect 32-bit off_t overflow */ + if (offset < caboff) { + sys->message(fh, largefile_msg); + return MSPACK_ERR_OK; + } +#endif + } + } + + /* restart search */ + if (offset >= flen) return MSPACK_ERR_OK; + if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { + return MSPACK_ERR_SEEK; + } + length = 0; + p = pend; + state = 0; + break; /* for bytes 4-7 and 12-15, just advance state/pointer */ default: - p++, state++; + p++, state++; } /* switch(state) */ } /* for (... p < pend ...) */ } /* for (... offset < length ...) */ @@ -765,7 +786,7 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, return MSPACK_ERR_OK; } - + /*************************************** * CABD_MERGE, CABD_PREPEND, CABD_APPEND *************************************** @@ -775,22 +796,22 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, * merged folder's data parts list. */ static int cabd_prepend(struct mscab_decompressor *base, - struct mscabd_cabinet *cab, - struct mscabd_cabinet *prevcab) + struct mscabd_cabinet *cab, + struct mscabd_cabinet *prevcab) { return cabd_merge(base, prevcab, cab); } static int cabd_append(struct mscab_decompressor *base, - struct mscabd_cabinet *cab, - struct mscabd_cabinet *nextcab) + struct mscabd_cabinet *cab, + struct mscabd_cabinet *nextcab) { return cabd_merge(base, cab, nextcab); } static int cabd_merge(struct mscab_decompressor *base, - struct mscabd_cabinet *lcab, - struct mscabd_cabinet *rcab) + struct mscabd_cabinet *lcab, + struct mscabd_cabinet *rcab) { struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; struct mscabd_folder_data *data, *ndata; @@ -880,7 +901,7 @@ static int cabd_merge(struct mscab_decompressor *base, * instead */ lfol->base.num_blocks += rfol->base.num_blocks - 1; if ((rfol->merge_next == NULL) || - (rfol->merge_next->folder != (struct mscabd_folder *) rfol)) + (rfol->merge_next->folder != (struct mscabd_folder *) rfol)) { lfol->merge_next = rfol->merge_next; } @@ -903,9 +924,9 @@ static int cabd_merge(struct mscab_decompressor *base, rfi = fi->next; /* if file's folder matches the merge folder, unlink and free it */ if (fi->folder == (struct mscabd_folder *) rfol) { - if (lfi) lfi->next = rfi; else lcab->files = rfi; - sys->free(fi->filename); - sys->free(fi); + if (lfi) lfi->next = rfi; else lcab->files = rfi; + sys->free(fi->filename); + sys->free(fi); } else lfi = fi; } @@ -940,6 +961,12 @@ static int cabd_can_merge_folders(struct mspack_system *sys, return 0; } + /* check there are not too many data blocks after merging */ + if ((lfol->base.num_blocks + rfol->base.num_blocks) > CAB_FOLDERMAX) { + D(("folder merge: too many data blocks in merged folders")) + return 0; + } + if (!(lfi = lfol->merge_next) || !(rfi = rfol->merge_prev)) { D(("folder merge: one cabinet has no files to merge")) return 0; @@ -950,10 +977,10 @@ static int cabd_can_merge_folders(struct mspack_system *sys, * should be identical in number and order. to verify this, check the * offset and length of each file. */ for (l=lfi, r=rfi; l; l=l->next, r=r->next) { - if (!r || (l->offset != r->offset) || (l->length != r->length)) { - matching = 0; - break; - } + if (!r || (l->offset != r->offset) || (l->length != r->length)) { + matching = 0; + break; + } } if (matching) return 1; @@ -963,9 +990,9 @@ static int cabd_can_merge_folders(struct mspack_system *sys, * the merge with a warning about missing files. */ matching = 0; for (l = lfi; l; l = l->next) { - for (r = rfi; r; r = r->next) { - if (l->offset == r->offset && l->length == r->length) break; - } + for (r = rfi; r; r = r->next) { + if (l->offset == r->offset && l->length == r->length) break; + } if (r) matching = 1; else sys->message(NULL, "WARNING; merged file %s not listed in both cabinets", l->filename); } @@ -985,6 +1012,7 @@ static int cabd_extract(struct mscab_decompressor *base, struct mscabd_folder_p *fol; struct mspack_system *sys; struct mspack_file *fh; + off_t filelen; if (!self) return MSPACK_ERR_ARGS; if (!file) return self->error = MSPACK_ERR_ARGS; @@ -992,15 +1020,43 @@ static int cabd_extract(struct mscab_decompressor *base, sys = self->system; fol = (struct mscabd_folder_p *) file->folder; - /* check if file can be extracted */ - if ((!fol) || (fol->merge_prev) || - (((file->offset + file->length) / CAB_BLOCKMAX) > fol->base.num_blocks)) - { - sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " - "cabinet set is incomplete.", file->filename); + /* if offset is beyond 2GB, nothing can be extracted */ + if (file->offset > CAB_LENGTHMAX) { return self->error = MSPACK_ERR_DATAFORMAT; } + /* if file claims to go beyond 2GB either error out, + * or in salvage mode reduce file length so it fits 2GB limit + */ + filelen = file->length; + if (filelen > CAB_LENGTHMAX || (file->offset + filelen) > CAB_LENGTHMAX) { + if (self->salvage) { + filelen = CAB_LENGTHMAX - file->offset; + } + else { + return self->error = MSPACK_ERR_DATAFORMAT; + } + } + + /* extraction impossible if no folder, or folder needs predecessor */ + if (!fol || fol->merge_prev) { + sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " + "cabinet set is incomplete", file->filename); + return self->error = MSPACK_ERR_DECRUNCH; + } + + /* if file goes beyond what can be decoded, given an error. + * In salvage mode, don't assume block sizes, just try decoding + */ + if (!self->salvage) { + off_t maxlen = fol->base.num_blocks * CAB_BLOCKMAX; + if ((file->offset + filelen) > maxlen) { + sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " + "cabinet set is incomplete", file->filename); + return self->error = MSPACK_ERR_DECRUNCH; + } + } + /* allocate generic decompression state */ if (!self->d) { self->d = (struct mscabd_decompress_state *) sys->alloc(sys, sizeof(struct mscabd_decompress_state)); @@ -1016,14 +1072,19 @@ static int cabd_extract(struct mscab_decompressor *base, } /* do we need to change folder or reset the current folder? */ - if ((self->d->folder != fol) || (self->d->offset > file->offset)) { + if ((self->d->folder != fol) || (self->d->offset > file->offset) || + !self->d->state) + { + /* free any existing decompressor */ + cabd_free_decomp(self); + /* do we need to open a new cab file? */ if (!self->d->infh || (fol->data.cab != self->d->incab)) { /* close previous file handle if from a different cab */ if (self->d->infh) sys->close(self->d->infh); self->d->incab = fol->data.cab; self->d->infh = sys->open(sys, fol->data.cab->base.filename, - MSPACK_SYS_OPEN_READ); + MSPACK_SYS_OPEN_READ); if (!self->d->infh) return self->error = MSPACK_ERR_OPEN; } /* seek to start of data blocks */ @@ -1041,6 +1102,7 @@ static int cabd_extract(struct mscab_decompressor *base, self->d->data = &fol->data; self->d->offset = 0; self->d->block = 0; + self->d->outlen = 0; self->d->i_ptr = self->d->i_end = &self->d->input[0]; /* read_error lasts for the lifetime of a decompressor */ @@ -1055,7 +1117,7 @@ static int cabd_extract(struct mscab_decompressor *base, self->error = MSPACK_ERR_OK; /* if file has more than 0 bytes */ - if (file->length) { + if (filelen) { off_t bytes; int error; /* get to correct offset. @@ -1065,14 +1127,14 @@ static int cabd_extract(struct mscab_decompressor *base, */ self->d->outfh = NULL; if ((bytes = file->offset - self->d->offset)) { - error = self->d->decompress(self->d->state, bytes); - self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; + error = self->d->decompress(self->d->state, bytes); + self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; } /* if getting to the correct offset was error free, unpack file */ if (!self->error) { self->d->outfh = fh; - error = self->d->decompress(self->d->state, (off_t) file->length); + error = self->d->decompress(self->d->state, filelen); self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; } } @@ -1098,34 +1160,27 @@ static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct) { struct mspack_file *fh = (struct mspack_file *) self; - assert(self && self->d); - - /* free any existing decompressor */ - cabd_free_decomp(self); - self->d->comp_type = ct; switch (ct & cffoldCOMPTYPE_MASK) { case cffoldCOMPTYPE_NONE: self->d->decompress = (int (*)(void *, off_t)) &noned_decompress; - self->d->state = noned_init(&self->d->sys, fh, fh, - self->param[MSCABD_PARAM_DECOMPBUF]); + self->d->state = noned_init(&self->d->sys, fh, fh, self->buf_size); break; case cffoldCOMPTYPE_MSZIP: self->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress; - self->d->state = mszipd_init(&self->d->sys, fh, fh, - self->param[MSCABD_PARAM_DECOMPBUF], - self->param[MSCABD_PARAM_FIXMSZIP]); + self->d->state = mszipd_init(&self->d->sys, fh, fh, self->buf_size, + self->fix_mszip); break; case cffoldCOMPTYPE_QUANTUM: self->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress; self->d->state = qtmd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, - self->param[MSCABD_PARAM_DECOMPBUF]); + self->buf_size); break; case cffoldCOMPTYPE_LZX: self->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress; self->d->state = lzxd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, 0, - self->param[MSCABD_PARAM_DECOMPBUF], (off_t) 0); + self->buf_size, (off_t)0,0); break; default: return self->error = MSPACK_ERR_DATAFORMAT; @@ -1134,7 +1189,7 @@ static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct) } static void cabd_free_decomp(struct mscab_decompressor_p *self) { - if (!self || !self->d || !self->d->folder || !self->d->state) return; + if (!self || !self->d || !self->d->state) return; switch (self->d->comp_type & cffoldCOMPTYPE_MASK) { case cffoldCOMPTYPE_NONE: noned_free((struct noned_state *) self->d->state); break; @@ -1162,10 +1217,12 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) { struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; unsigned char *buf = (unsigned char *) buffer; struct mspack_system *sys = self->system; - int avail, todo, outlen, ignore_cksum; + int avail, todo, outlen, ignore_cksum, ignore_blocksize; - ignore_cksum = self->param[MSCABD_PARAM_FIXMSZIP] && - ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP); + ignore_cksum = self->salvage || + (self->fix_mszip && + ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP)); + ignore_blocksize = self->salvage; todo = bytes; while (todo > 0) { @@ -1185,37 +1242,35 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) { /* check if we're out of input blocks, advance block counter */ if (self->d->block++ >= self->d->folder->base.num_blocks) { - self->read_error = MSPACK_ERR_DATAFORMAT; - break; + if (!self->salvage) { + self->read_error = MSPACK_ERR_DATAFORMAT; + } + else { + D(("Ran out of CAB input blocks prematurely")) + } + break; } /* read a block */ - self->read_error = cabd_sys_read_block(sys, self->d, &outlen, ignore_cksum); + self->read_error = cabd_sys_read_block(sys, self->d, &outlen, + ignore_cksum, ignore_blocksize); if (self->read_error) return -1; + self->d->outlen += outlen; /* special Quantum hack -- trailer byte to allow the decompressor * to realign itself. CAB Quantum blocks, unlike LZX blocks, can have * anything from 0 to 4 trailing null bytes. */ if ((self->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) { - *self->d->i_end++ = 0xFF; + *self->d->i_end++ = 0xFF; } /* is this the last block? */ if (self->d->block >= self->d->folder->base.num_blocks) { - /* last block */ - if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) { - /* special LZX hack -- on the last block, inform LZX of the - * size of the output data stream. */ - lzxd_set_output_length((struct lzxd_stream *) self->d->state, (off_t) - ((self->d->block-1) * CAB_BLOCKMAX + outlen)); - } - } - else { - /* not the last block */ - if (outlen != CAB_BLOCKMAX) { - self->system->message(self->d->infh, - "WARNING; non-maximal data block"); - } + if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) { + /* special LZX hack -- on the last block, inform LZX of the + * size of the output data stream. */ + lzxd_set_output_length((struct lzxd_stream *) self->d->state, self->d->outlen); + } } } /* if (avail) */ } /* while (todo > 0) */ @@ -1238,12 +1293,13 @@ static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) { * one cab file, if it does then the fragments will be reassembled */ static int cabd_sys_read_block(struct mspack_system *sys, - struct mscabd_decompress_state *d, - int *out, int ignore_cksum) + struct mscabd_decompress_state *d, + int *out, int ignore_cksum, + int ignore_blocksize) { unsigned char hdr[cfdata_SIZEOF]; unsigned int cksum; - int len; + int len, full_len; /* reset the input block pointer and end of block pointer */ d->i_ptr = d->i_end = &d->input[0]; @@ -1256,23 +1312,27 @@ static int cabd_sys_read_block(struct mspack_system *sys, /* skip any reserved block headers */ if (d->data->cab->block_resv && - sys->seek(d->infh, (off_t) d->data->cab->block_resv, - MSPACK_SYS_SEEK_CUR)) + sys->seek(d->infh, (off_t) d->data->cab->block_resv, + MSPACK_SYS_SEEK_CUR)) { return MSPACK_ERR_SEEK; } /* blocks must not be over CAB_INPUTMAX in size */ len = EndGetI16(&hdr[cfdata_CompressedSize]); - if (((d->i_end - d->i_ptr) + len) > CAB_INPUTMAX) { - D(("block size > CAB_INPUTMAX (%ld + %d)", d->i_end - d->i_ptr, len)) - return MSPACK_ERR_DATAFORMAT; + full_len = (d->i_end - d->i_ptr) + len; /* include cab-spanning blocks */ + if (full_len > CAB_INPUTMAX) { + D(("block size %d > CAB_INPUTMAX", full_len)); + /* in salvage mode, blocks can be 65535 bytes but no more than that */ + if (!ignore_blocksize || full_len > CAB_INPUTMAX_SALVAGE) { + return MSPACK_ERR_DATAFORMAT; + } } /* blocks must not expand to more than CAB_BLOCKMAX */ if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) { D(("block size > CAB_BLOCKMAX")) - return MSPACK_ERR_DATAFORMAT; + if (!ignore_blocksize) return MSPACK_ERR_DATAFORMAT; } /* read the block data */ @@ -1284,8 +1344,8 @@ static int cabd_sys_read_block(struct mspack_system *sys, if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) { unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0); if (cabd_checksum(&hdr[4], 4, sum2) != cksum) { - if (!ignore_cksum) return MSPACK_ERR_CHECKSUM; - sys->message(d->infh, "WARNING; bad block checksum found"); + if (!ignore_cksum) return MSPACK_ERR_CHECKSUM; + sys->message(d->infh, "WARNING; bad block checksum found"); } } @@ -1310,14 +1370,14 @@ static int cabd_sys_read_block(struct mspack_system *sys, /* advance to next member in the cabinet set */ if (!(d->data = d->data->next)) { - D(("ran out of splits in cabinet set")) + sys->message(d->infh, "WARNING; ran out of cabinets in set. Are any missing?"); return MSPACK_ERR_DATAFORMAT; } /* open next cab file */ d->incab = d->data->cab; if (!(d->infh = sys->open(sys, d->incab->base.filename, - MSPACK_SYS_OPEN_READ))) + MSPACK_SYS_OPEN_READ))) { return MSPACK_ERR_OPEN; } @@ -1333,7 +1393,7 @@ static int cabd_sys_read_block(struct mspack_system *sys, } static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes, - unsigned int cksum) + unsigned int cksum) { unsigned int len, ul = 0; @@ -1342,8 +1402,8 @@ static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes, } switch (bytes & 3) { - case 3: ul |= *data++ << 16; - case 2: ul |= *data++ << 8; + case 3: ul |= *data++ << 16; /*@fallthrough@*/ + case 2: ul |= *data++ << 8; /*@fallthrough@*/ case 1: ul |= *data; } cksum ^= ul; @@ -1365,9 +1425,9 @@ struct noned_state { }; static struct noned_state *noned_init(struct mspack_system *sys, - struct mspack_file *in, - struct mspack_file *out, - int bufsize) + struct mspack_file *in, + struct mspack_file *out, + int bufsize) { struct noned_state *state = (struct noned_state *) sys->alloc(sys, sizeof(struct noned_state)); unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) bufsize); @@ -1419,14 +1479,17 @@ static int cabd_param(struct mscab_decompressor *base, int param, int value) { switch (param) { case MSCABD_PARAM_SEARCHBUF: if (value < 4) return MSPACK_ERR_ARGS; - self->param[MSCABD_PARAM_SEARCHBUF] = value; + self->searchbuf_size = value; break; case MSCABD_PARAM_FIXMSZIP: - self->param[MSCABD_PARAM_FIXMSZIP] = value; + self->fix_mszip = value; break; case MSCABD_PARAM_DECOMPBUF: if (value < 4) return MSPACK_ERR_ARGS; - self->param[MSCABD_PARAM_DECOMPBUF] = value; + self->buf_size = value; + break; + case MSCABD_PARAM_SALVAGE: + self->salvage = value; break; default: return MSPACK_ERR_ARGS; diff --git a/rbutil/rbutilqt/mspack/chmd.c b/rbutil/rbutilqt/mspack/chmd.c index 416156e742..6c8481db14 100644 --- a/rbutil/rbutilqt/mspack/chmd.c +++ b/rbutil/rbutilqt/mspack/chmd.c @@ -1,5 +1,5 @@ /* This file is part of libmspack. - * (C) 2003-2011 Stuart Caie. + * (C) 2003-2018 Stuart Caie. * * libmspack is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License (LGPL) version 2.1 @@ -44,7 +44,7 @@ static int chmd_init_decomp( struct mschm_decompressor_p *self, struct mschmd_file *file); static int read_reset_table( struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec, - int entry, off_t *length_ptr, off_t *offset_ptr); + unsigned int entry, off_t *length_ptr, off_t *offset_ptr); static int read_spaninfo( struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec, off_t *length_ptr); @@ -121,7 +121,7 @@ void mspack_destroy_chm_decompressor(struct mschm_decompressor *base) { * Calls chmd_real_open() with entire=1. */ static struct mschmd_header *chmd_open(struct mschm_decompressor *base, - const char *filename) + const char *filename) { return chmd_real_open(base, filename, 1); } @@ -133,7 +133,7 @@ static struct mschmd_header *chmd_open(struct mschm_decompressor *base, * the file headers. Calls chmd_real_open() with entire=0 */ static struct mschmd_header *chmd_fast_open(struct mschm_decompressor *base, - const char *filename) + const char *filename) { return chmd_real_open(base, filename, 0); } @@ -146,7 +146,7 @@ static struct mschmd_header *chmd_fast_open(struct mschm_decompressor *base, * either read all headers, or a bare mininum. */ static struct mschmd_header *chmd_real_open(struct mschm_decompressor *base, - const char *filename, int entire) + const char *filename, int entire) { struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; struct mschmd_header *chm = NULL; @@ -162,16 +162,16 @@ static struct mschmd_header *chmd_real_open(struct mschm_decompressor *base, chm->filename = filename; error = chmd_read_headers(sys, fh, chm, entire); if (error) { - /* if the error is DATAFORMAT, and there are some results, return - * partial results with a warning, rather than nothing */ - if (error == MSPACK_ERR_DATAFORMAT && (chm->files || chm->sysfiles)) { - sys->message(fh, "WARNING; contents are corrupt"); - error = MSPACK_ERR_OK; - } - else { - chmd_close(base, chm); - chm = NULL; - } + /* if the error is DATAFORMAT, and there are some results, return + * partial results with a warning, rather than nothing */ + if (error == MSPACK_ERR_DATAFORMAT && (chm->files || chm->sysfiles)) { + sys->message(fh, "WARNING; contents are corrupt"); + error = MSPACK_ERR_OK; + } + else { + chmd_close(base, chm); + chm = NULL; + } } self->error = error; } @@ -192,7 +192,7 @@ static struct mschmd_header *chmd_real_open(struct mschm_decompressor *base, * frees all memory associated with a given mschmd_header */ static void chmd_close(struct mschm_decompressor *base, - struct mschmd_header *chm) + struct mschmd_header *chm) { struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; struct mschmd_file *fi, *nfi; @@ -251,16 +251,16 @@ static const unsigned char guids[32] = { /* reads an encoded integer into a variable; 7 bits of data per byte, * the high bit is used to indicate that there is another byte */ -#define READ_ENCINT(var) do { \ - (var) = 0; \ - do { \ - if (p > end) goto chunk_end; \ - (var) = ((var) << 7) | (*p & 0x7F); \ - } while (*p++ & 0x80); \ +#define READ_ENCINT(var) do { \ + (var) = 0; \ + do { \ + if (p >= end) goto chunk_end; \ + (var) = ((var) << 7) | (*p & 0x7F); \ + } while (*p++ & 0x80); \ } while (0) static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, - struct mschmd_header *chm, int entire) + struct mschmd_header *chm, int entire) { unsigned int section, name_len, x, errors, num_chunks; unsigned char buf[0x54], *chunk = NULL, *name, *p, *end; @@ -292,7 +292,7 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, } /* check both header GUIDs */ - if (mspack_memcmp(&buf[chmhead_GUID1], &guids[0], 32L) != 0) { + if (memcmp(&buf[chmhead_GUID1], &guids[0], 32L) != 0) { D(("incorrect GUIDs")) return MSPACK_ERR_SIGNATURE; } @@ -356,8 +356,53 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, chm->sec0.offset = chm->dir_offset + (chm->chunk_size * chm->num_chunks); } - /* ensure chunk size is large enough for signature and num_entries */ + /* check if content offset or file size is wrong */ + if (chm->sec0.offset > chm->length) { + D(("content section begins after file has ended")) + return MSPACK_ERR_DATAFORMAT; + } + + /* ensure there are chunks and that chunk size is + * large enough for signature and num_entries */ if (chm->chunk_size < (pmgl_Entries + 2)) { + D(("chunk size not large enough")) + return MSPACK_ERR_DATAFORMAT; + } + if (chm->num_chunks == 0) { + D(("no chunks")) + return MSPACK_ERR_DATAFORMAT; + } + + /* The chunk_cache data structure is not great; large values for num_chunks + * or num_chunks*chunk_size can exhaust all memory. Until a better chunk + * cache is implemented, put arbitrary limits on num_chunks and chunk size. + */ + if (chm->num_chunks > 100000) { + D(("more than 100,000 chunks")) + return MSPACK_ERR_DATAFORMAT; + } + if (chm->chunk_size > 8192) { + D(("chunk size over 8192 (get in touch if this is valid)")) + return MSPACK_ERR_DATAFORMAT; + } + if ((off_t)chm->chunk_size * (off_t)chm->num_chunks > chm->length) { + D(("chunks larger than entire file")) + return MSPACK_ERR_DATAFORMAT; + } + + /* common sense checks on header section 1 fields */ + if (chm->chunk_size != 4096) { + sys->message(fh, "WARNING; chunk size is not 4096"); + } + if (chm->first_pmgl != 0) { + sys->message(fh, "WARNING; first PMGL chunk is not zero"); + } + if (chm->first_pmgl > chm->last_pmgl) { + D(("first pmgl chunk is after last pmgl chunk")) + return MSPACK_ERR_DATAFORMAT; + } + if (chm->index_root != 0xFFFFFFFF && chm->index_root >= chm->num_chunks) { + D(("index_root outside valid range")) return MSPACK_ERR_DATAFORMAT; } @@ -394,7 +439,7 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, sys->message(fh, "WARNING; PMGL quickref area is too small"); } if (EndGetI32(&chunk[pmgl_QuickRefSize]) > - ((int)chm->chunk_size - pmgl_Entries)) + (chm->chunk_size - pmgl_Entries)) { sys->message(fh, "WARNING; PMGL quickref area is too large"); } @@ -404,60 +449,63 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, num_entries = EndGetI16(end); while (num_entries--) { - READ_ENCINT(name_len); name = p; p += name_len; + READ_ENCINT(name_len); + if (name_len > (unsigned int) (end - p)) goto chunk_end; + name = p; p += name_len; READ_ENCINT(section); READ_ENCINT(offset); READ_ENCINT(length); + /* ignore blank or one-char (e.g. "/") filenames we'd return as blank */ + if (name_len < 2 || !name[0] || !name[1]) continue; + /* empty files and directory names are stored as a file entry at * offset 0 with length 0. We want to keep empty files, but not * directory names, which end with a "/" */ if ((offset == 0) && (length == 0)) { - if ((name_len > 0) && (name[name_len-1] == '/')) continue; + if ((name_len > 0) && (name[name_len-1] == '/')) continue; } if (section > 1) { - sys->message(fh, "invalid section number '%u'.", section); - continue; + sys->message(fh, "invalid section number '%u'.", section); + continue; } if (!(fi = (struct mschmd_file *) sys->alloc(sys, sizeof(struct mschmd_file) + name_len + 1))) { - sys->free(chunk); - return MSPACK_ERR_NOMEMORY; + sys->free(chunk); + return MSPACK_ERR_NOMEMORY; } fi->next = NULL; fi->filename = (char *) &fi[1]; fi->section = ((section == 0) ? (struct mschmd_section *) (&chm->sec0) - : (struct mschmd_section *) (&chm->sec1)); + : (struct mschmd_section *) (&chm->sec1)); fi->offset = offset; fi->length = length; sys->copy(name, fi->filename, (size_t) name_len); fi->filename[name_len] = '\0'; if (name[0] == ':' && name[1] == ':') { - /* system file */ - if (mspack_memcmp(&name[2], &content_name[2], 31L) == 0) { - if (mspack_memcmp(&name[33], &content_name[33], 8L) == 0) { - chm->sec1.content = fi; - } - else if (mspack_memcmp(&name[33], &control_name[33], 11L) == 0) { - chm->sec1.control = fi; - } - else if (mspack_memcmp(&name[33], &spaninfo_name[33], 8L) == 0) { - chm->sec1.spaninfo = fi; - } - else if (mspack_memcmp(&name[33], &rtable_name[33], 72L) == 0) { - chm->sec1.rtable = fi; - } - } - fi->next = chm->sysfiles; - chm->sysfiles = fi; + /* system file */ + if (name_len == 40 && memcmp(name, content_name, 40) == 0) { + chm->sec1.content = fi; + } + else if (name_len == 44 && memcmp(name, control_name, 44) == 0) { + chm->sec1.control = fi; + } + else if (name_len == 41 && memcmp(name, spaninfo_name, 41) == 0) { + chm->sec1.spaninfo = fi; + } + else if (name_len == 105 && memcmp(name, rtable_name, 105) == 0) { + chm->sec1.rtable = fi; + } + fi->next = chm->sysfiles; + chm->sysfiles = fi; } else { - /* normal file */ - if (link) link->next = fi; else chm->files = fi; - link = fi; + /* normal file */ + if (link) link->next = fi; else chm->files = fi; + link = fi; } } @@ -481,21 +529,24 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh, * directly from the on-disk index. * * TODO: protect against infinite loops in chunks (where pgml_NextChunk - * or a PGMI index entry point to an already visited chunk) + * or a PMGI index entry point to an already visited chunk) */ static int chmd_fast_find(struct mschm_decompressor *base, - struct mschmd_header *chm, const char *filename, - struct mschmd_file *f_ptr, int f_size) + struct mschmd_header *chm, const char *filename, + struct mschmd_file *f_ptr, int f_size) { struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; struct mspack_system *sys; struct mspack_file *fh; - const unsigned char *chunk, *p, *end; + /* p and end are initialised to prevent MSVC warning about "potentially" + * uninitialised usage. This is provably untrue, but MS won't fix: + * https://developercommunity.visualstudio.com/content/problem/363489/c4701-false-positive-warning.html */ + const unsigned char *chunk, *p = NULL, *end = NULL; int err = MSPACK_ERR_OK, result = -1; unsigned int n, sec; if (!self || !chm || !f_ptr || (f_size != sizeof(struct mschmd_file))) { - return MSPACK_ERR_ARGS; + return MSPACK_ERR_ARGS; } sys = self->system; @@ -503,54 +554,59 @@ static int chmd_fast_find(struct mschm_decompressor *base, memset(f_ptr, 0, f_size); if (!(fh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ))) { - return MSPACK_ERR_OPEN; + return MSPACK_ERR_OPEN; } /* go through PMGI chunk hierarchy to reach PMGL chunk */ if (chm->index_root < chm->num_chunks) { - n = chm->index_root; - for (;;) { - if (!(chunk = read_chunk(self, chm, fh, n))) { - sys->close(fh); - return self->error; - } - - /* search PMGI/PMGL chunk. exit early if no entry found */ - if ((result = search_chunk(chm, chunk, filename, &p, &end)) <= 0) { - break; - } - - /* found result. loop around for next chunk if this is PMGI */ - if (chunk[3] == 0x4C) break; else READ_ENCINT(n); - } + n = chm->index_root; + for (;;) { + if (!(chunk = read_chunk(self, chm, fh, n))) { + sys->close(fh); + return self->error; + } + + /* search PMGI/PMGL chunk. exit early if no entry found */ + if ((result = search_chunk(chm, chunk, filename, &p, &end)) <= 0) { + break; + } + + /* found result. loop around for next chunk if this is PMGI */ + if (chunk[3] == 0x4C) break; else READ_ENCINT(n); + } } else { - /* PMGL chunks only, search from first_pmgl to last_pmgl */ - for (n = chm->first_pmgl; n <= chm->last_pmgl; - n = EndGetI32(&chunk[pmgl_NextChunk])) - { - if (!(chunk = read_chunk(self, chm, fh, n))) { - err = self->error; - break; - } - - /* search PMGL chunk. exit if file found */ - if ((result = search_chunk(chm, chunk, filename, &p, &end)) > 0) { - break; - } - } + /* PMGL chunks only, search from first_pmgl to last_pmgl */ + for (n = chm->first_pmgl; n <= chm->last_pmgl; + n = EndGetI32(&chunk[pmgl_NextChunk])) + { + if (!(chunk = read_chunk(self, chm, fh, n))) { + err = self->error; + break; + } + + /* search PMGL chunk. exit if file found */ + if ((result = search_chunk(chm, chunk, filename, &p, &end)) > 0) { + break; + } + + /* stop simple infinite loops: can't visit the same chunk twice */ + if (n == EndGetI32(&chunk[pmgl_NextChunk])) { + break; + } + } } /* if we found a file, read it */ if (result > 0) { - READ_ENCINT(sec); - f_ptr->section = (sec == 0) ? (struct mschmd_section *) &chm->sec0 - : (struct mschmd_section *) &chm->sec1; - READ_ENCINT(f_ptr->offset); - READ_ENCINT(f_ptr->length); + READ_ENCINT(sec); + f_ptr->section = (sec == 0) ? (struct mschmd_section *) &chm->sec0 + : (struct mschmd_section *) &chm->sec1; + READ_ENCINT(f_ptr->offset); + READ_ENCINT(f_ptr->length); } else if (result < 0) { - err = MSPACK_ERR_DATAFORMAT; + err = MSPACK_ERR_DATAFORMAT; } sys->close(fh); @@ -566,24 +622,24 @@ static int chmd_fast_find(struct mschm_decompressor *base, * so it doesn't need to be read from disk more than once */ static unsigned char *read_chunk(struct mschm_decompressor_p *self, - struct mschmd_header *chm, - struct mspack_file *fh, - unsigned int chunk_num) + struct mschmd_header *chm, + struct mspack_file *fh, + unsigned int chunk_num) { struct mspack_system *sys = self->system; unsigned char *buf; /* check arguments - most are already checked by chmd_fast_find */ - if (chunk_num > chm->num_chunks) return NULL; + if (chunk_num >= chm->num_chunks) return NULL; /* ensure chunk cache is available */ if (!chm->chunk_cache) { - size_t size = sizeof(unsigned char *) * chm->num_chunks; - if (!(chm->chunk_cache = (unsigned char **) sys->alloc(sys, size))) { - self->error = MSPACK_ERR_NOMEMORY; - return NULL; - } - memset(chm->chunk_cache, 0, size); + size_t size = sizeof(unsigned char *) * chm->num_chunks; + if (!(chm->chunk_cache = (unsigned char **) sys->alloc(sys, size))) { + self->error = MSPACK_ERR_NOMEMORY; + return NULL; + } + memset(chm->chunk_cache, 0, size); } /* try to answer out of chunk cache */ @@ -591,31 +647,31 @@ static unsigned char *read_chunk(struct mschm_decompressor_p *self, /* need to read chunk - allocate memory for it */ if (!(buf = (unsigned char *) sys->alloc(sys, chm->chunk_size))) { - self->error = MSPACK_ERR_NOMEMORY; - return NULL; + self->error = MSPACK_ERR_NOMEMORY; + return NULL; } /* seek to block and read it */ if (sys->seek(fh, (off_t) (chm->dir_offset + (chunk_num * chm->chunk_size)), - MSPACK_SYS_SEEK_START)) + MSPACK_SYS_SEEK_START)) { - self->error = MSPACK_ERR_SEEK; - sys->free(buf); - return NULL; + self->error = MSPACK_ERR_SEEK; + sys->free(buf); + return NULL; } if (sys->read(fh, buf, (int)chm->chunk_size) != (int)chm->chunk_size) { - self->error = MSPACK_ERR_READ; - sys->free(buf); - return NULL; + self->error = MSPACK_ERR_READ; + sys->free(buf); + return NULL; } /* check the signature. Is is PMGL or PMGI? */ if (!((buf[0] == 0x50) && (buf[1] == 0x4D) && (buf[2] == 0x47) && - ((buf[3] == 0x4C) || (buf[3] == 0x49)))) + ((buf[3] == 0x4C) || (buf[3] == 0x49)))) { - self->error = MSPACK_ERR_SEEK; - sys->free(buf); - return NULL; + self->error = MSPACK_ERR_SEEK; + sys->free(buf); + return NULL; } /* all OK. Store chunk in cache and return it */ @@ -633,14 +689,14 @@ static unsigned char *read_chunk(struct mschm_decompressor_p *self, * chunk that may eventually contain that entry has been found. */ static int search_chunk(struct mschmd_header *chm, - const unsigned char *chunk, - const char *filename, - const unsigned char **result, - const unsigned char **result_end) + const unsigned char *chunk, + const char *filename, + const unsigned char **result, + const unsigned char **result_end) { const unsigned char *start, *end, *p; unsigned int qr_size, num_entries, qr_entries, qr_density, name_len; - unsigned int L, R, M, sec, fname_len, entries_off, is_pmgl; + unsigned int L, R, M, fname_len, entries_off, is_pmgl; int cmp; fname_len = strlen(filename); @@ -648,12 +704,12 @@ static int search_chunk(struct mschmd_header *chm, /* PMGL chunk or PMGI chunk? (note: read_chunk() has already * checked the rest of the characters in the chunk signature) */ if (chunk[3] == 0x4C) { - is_pmgl = 1; - entries_off = pmgl_Entries; + is_pmgl = 1; + entries_off = pmgl_Entries; } else { - is_pmgl = 0; - entries_off = pmgi_Entries; + is_pmgl = 0; + entries_off = pmgi_Entries; } /* Step 1: binary search first filename of each QR entry @@ -674,55 +730,55 @@ static int search_chunk(struct mschmd_header *chm, qr_entries = (num_entries + qr_density-1) / qr_density; if (num_entries == 0) { - D(("chunk has no entries")) - return -1; + D(("chunk has no entries")) + return -1; } if (qr_size > chm->chunk_size) { - D(("quickref size > chunk size")) - return -1; + D(("quickref size > chunk size")) + return -1; } *result_end = end; if (((int)qr_entries * 2) > (start - end)) { - D(("WARNING; more quickrefs than quickref space")) - qr_entries = 0; /* but we can live with it */ + D(("WARNING; more quickrefs than quickref space")) + qr_entries = 0; /* but we can live with it */ } if (qr_entries > 0) { - L = 0; - R = qr_entries - 1; - do { - /* pick new midpoint */ - M = (L + R) >> 1; - - /* compare filename with entry QR points to */ - p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; - READ_ENCINT(name_len); - if (p + name_len > end) goto chunk_end; - cmp = compare(filename, (char *)p, fname_len, name_len); - - if (cmp == 0) break; - else if (cmp < 0) { if (M) R = M - 1; else return 0; } - else if (cmp > 0) L = M + 1; - } while (L <= R); - M = (L + R) >> 1; - - if (cmp == 0) { - /* exact match! */ - p += name_len; - *result = p; - return 1; - } - - /* otherwise, read the group of entries for QR entry M */ - p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; - num_entries -= (M * qr_density); - if (num_entries > qr_density) num_entries = qr_density; + L = 0; + R = qr_entries - 1; + do { + /* pick new midpoint */ + M = (L + R) >> 1; + + /* compare filename with entry QR points to */ + p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; + READ_ENCINT(name_len); + if (name_len > (unsigned int) (end - p)) goto chunk_end; + cmp = compare(filename, (char *)p, fname_len, name_len); + + if (cmp == 0) break; + else if (cmp < 0) { if (M) R = M - 1; else return 0; } + else if (cmp > 0) L = M + 1; + } while (L <= R); + M = (L + R) >> 1; + + if (cmp == 0) { + /* exact match! */ + p += name_len; + *result = p; + return 1; + } + + /* otherwise, read the group of entries for QR entry M */ + p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)]; + num_entries -= (M * qr_density); + if (num_entries > qr_density) num_entries = qr_density; } else { - p = &chunk[entries_off]; + p = &chunk[entries_off]; } /* Step 2: linear search through the set of entries reached in step 1. @@ -736,32 +792,32 @@ static int search_chunk(struct mschmd_header *chm, */ *result = NULL; while (num_entries-- > 0) { - READ_ENCINT(name_len); - if (p + name_len > end) goto chunk_end; - cmp = compare(filename, (char *)p, fname_len, name_len); - p += name_len; - - if (cmp == 0) { - /* entry found */ - *result = p; - return 1; - } - - if (cmp < 0) { - /* entry not found (PMGL) / maybe found (PMGI) */ - break; - } - - /* read and ignore the rest of this entry */ - if (is_pmgl) { - READ_ENCINT(R); /* skip section */ - READ_ENCINT(R); /* skip offset */ - READ_ENCINT(R); /* skip length */ - } - else { - *result = p; /* store potential final result */ - READ_ENCINT(R); /* skip chunk number */ - } + READ_ENCINT(name_len); + if (name_len > (unsigned int) (end - p)) goto chunk_end; + cmp = compare(filename, (char *)p, fname_len, name_len); + p += name_len; + + if (cmp == 0) { + /* entry found */ + *result = p; + return 1; + } + + if (cmp < 0) { + /* entry not found (PMGL) / maybe found (PMGI) */ + break; + } + + /* read and ignore the rest of this entry */ + if (is_pmgl) { + READ_ENCINT(R); /* skip section */ + READ_ENCINT(R); /* skip offset */ + READ_ENCINT(R); /* skip length */ + } + else { + *result = p; /* store potential final result */ + READ_ENCINT(R); /* skip chunk number */ + } } /* PMGL? not found. PMGI? maybe found */ @@ -773,66 +829,34 @@ static int search_chunk(struct mschmd_header *chm, } #if HAVE_TOWLOWER -# if HAVE_WCTYPE_H -# include -# endif +# include # define TOLOWER(x) towlower(x) -#elif HAVE_TOLOWER -# if HAVE_CTYPE_H -# include -# endif -# define TOLOWER(x) tolower(x) #else -# define TOLOWER(x) (((x)<0||(x)>256)?(x):mspack_tolower_map[(x)]) -/* Map of char -> lowercase char for the first 256 chars. Generated with: - * LC_CTYPE=en_GB.utf-8 perl -Mlocale -le 'print map{ord(lc chr).","} 0..255' - */ -static const unsigned char mspack_tolower_map[256] = { - 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27, - 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52, - 53,54,55,56,57,58,59,60,61,62,63,64,97,98,99,100,101,102,103,104,105,106, - 107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,92,93,94, - 95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114, - 115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133, - 134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152, - 153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171, - 172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190, - 191,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241, - 242,243,244,245,246,215,248,249,250,251,252,253,254,223,224,225,226,227,228, - 229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, - 248,249,250,251,252,253,254,255 -}; +# include +# define TOLOWER(x) tolower(x) #endif -/* decodes a UTF-8 character from s[] into c. Will not read past e. */ -#define GET_UTF8_CHAR(s, e, c) do { \ - unsigned char x = *s++; \ - if (x < 0x80) c = x; \ - else if (x < 0xC0) c = -1; \ - else if (x < 0xE0) { \ - c = (s >= e) ? -1 : ((x & 0x1F) << 6) | (*s++ & 0x3F); \ - } \ - else if (x < 0xF0) { \ - c = (s+2 > e) ? -1 : ((x & 0x0F) << 12) | ((s[0] & 0x3F) << 6) \ - | (s[1] & 0x3F); \ - s += 2; \ - } \ - else if (x < 0xF8) { \ - c = (s+3 > e) ? -1 : ((x & 0x07) << 18) | ((s[0] & 0x3F) << 12) \ - | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F); \ - s += 3; \ - } \ - else if (x < 0xFC) { \ - c = (s+4 > e) ? -1 : ((x & 0x03) << 24) | ((s[0] & 0x3F) << 18) \ - | ((s[1] & 0x3F) << 12)|((s[2] & 0x3F) << 6)|(s[3] & 0x3F); \ - s += 4; \ - } \ - else if (x < 0xFE) { \ - c = (s+5>e)?-1:((x&1)<<30)|((s[0]&0x3F)<<24)|((s[1]&0x3F)<<18)| \ - ((s[2] & 0x3F) << 12) | ((s[3] & 0x3F) << 6)|(s[4] & 0x3F); \ - s += 5; \ - } \ - else c = -1; \ +/* decodes a UTF-8 character from s[] into c. Will not read past e. + * doesn't test that extension bytes are %10xxxxxx. + * allows some overlong encodings. + */ +#define GET_UTF8_CHAR(s, e, c) do { \ + unsigned char x = *s++; \ + if (x < 0x80) c = x; \ + else if (x >= 0xC2 && x < 0xE0 && s < e) { \ + c = (x & 0x1F) << 6 | (*s++ & 0x3F); \ + } \ + else if (x >= 0xE0 && x < 0xF0 && s+1 < e) { \ + c = (x & 0x0F) << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F); \ + s += 2; \ + } \ + else if (x >= 0xF0 && x <= 0xF5 && s+2 < e) { \ + c = (x & 0x07) << 18 | (s[0] & 0x3F) << 12 | \ + (s[1] & 0x3F) << 6 | (s[2] & 0x3F); \ + if (c > 0x10FFFF) c = 0xFFFD; \ + s += 3; \ + } \ + else c = 0xFFFD; \ } while (0) /* case-insensitively compares two UTF8 encoded strings. String length for @@ -844,12 +868,12 @@ static inline int compare(const char *s1, const char *s2, int l1, int l2) { int c1, c2; while (p1 < e1 && p2 < e2) { - GET_UTF8_CHAR(p1, e1, c1); - GET_UTF8_CHAR(p2, e2, c2); - if (c1 == c2) continue; - c1 = TOLOWER(c1); - c2 = TOLOWER(c2); - if (c1 != c2) return c1 - c2; + GET_UTF8_CHAR(p1, e1, c1); + GET_UTF8_CHAR(p2, e2, c2); + if (c1 == c2) continue; + c1 = TOLOWER(c1); + c2 = TOLOWER(c2); + if (c1 != c2) return c1 - c2; } return l1 - l2; } @@ -861,7 +885,7 @@ static inline int compare(const char *s1, const char *s2, int l1, int l2) { * extracts a file from a CHM helpfile */ static int chmd_extract(struct mschm_decompressor *base, - struct mschmd_file *file, const char *filename) + struct mschmd_file *file, const char *filename) { struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; struct mspack_system *sys; @@ -915,7 +939,7 @@ static int chmd_extract(struct mschm_decompressor *base, case 0: /* Uncompressed section file */ /* simple seek + copy */ if (sys->seek(self->d->infh, file->section->chm->sec0.offset - + file->offset, MSPACK_SYS_SEEK_START)) + + file->offset, MSPACK_SYS_SEEK_START)) { self->error = MSPACK_ERR_SEEK; } @@ -923,17 +947,17 @@ static int chmd_extract(struct mschm_decompressor *base, unsigned char buf[512]; off_t length = file->length; while (length > 0) { - int run = sizeof(buf); - if ((off_t)run > length) run = (int)length; - if (sys->read(self->d->infh, &buf[0], run) != run) { - self->error = MSPACK_ERR_READ; - break; - } - if (sys->write(fh, &buf[0], run) != run) { - self->error = MSPACK_ERR_WRITE; - break; - } - length -= run; + int run = sizeof(buf); + if ((off_t)run > length) run = (int)length; + if (sys->read(self->d->infh, &buf[0], run) != run) { + self->error = MSPACK_ERR_READ; + break; + } + if (sys->write(fh, &buf[0], run) != run) { + self->error = MSPACK_ERR_WRITE; + break; + } + length -= run; } } break; @@ -944,8 +968,8 @@ static int chmd_extract(struct mschm_decompressor *base, */ if (!self->d->state || (file->offset < self->d->offset)) { if (self->d->state) { - lzxd_free(self->d->state); - self->d->state = NULL; + lzxd_free(self->d->state); + self->d->state = NULL; } if (chmd_init_decomp(self, file)) break; } @@ -1009,7 +1033,7 @@ static int chmd_sys_write(struct mspack_file *file, void *buffer, int bytes) { * file. */ static int chmd_init_decomp(struct mschm_decompressor_p *self, - struct mschmd_file *file) + struct mschmd_file *file) { int window_size, window_bits, reset_interval, entry, err; struct mspack_system *sys = self->system; @@ -1077,7 +1101,7 @@ static int chmd_init_decomp(struct mschm_decompressor_p *self, } /* validate reset_interval */ - if (reset_interval % LZX_FRAME_SIZE) { + if (reset_interval == 0 || reset_interval % LZX_FRAME_SIZE) { D(("bad controldata reset interval")) return self->error = MSPACK_ERR_DATAFORMAT; } @@ -1116,9 +1140,9 @@ static int chmd_init_decomp(struct mschm_decompressor_p *self, /* initialise LZX stream */ self->d->state = lzxd_init(&self->d->sys, self->d->infh, - (struct mspack_file *) self, window_bits, - reset_interval / LZX_FRAME_SIZE, - 4096, length); + (struct mspack_file *) self, window_bits, + reset_interval / LZX_FRAME_SIZE, + 4096, length, 0); if (!self->d->state) self->error = MSPACK_ERR_NOMEMORY; return self->error; } @@ -1131,12 +1155,13 @@ static int chmd_init_decomp(struct mschm_decompressor_p *self, * Returns non-zero for success, zero for failure. */ static int read_reset_table(struct mschm_decompressor_p *self, - struct mschmd_sec_mscompressed *sec, - int entry, off_t *length_ptr, off_t *offset_ptr) + struct mschmd_sec_mscompressed *sec, + unsigned int entry, + off_t *length_ptr, off_t *offset_ptr) { struct mspack_system *sys = self->system; unsigned char *data; - int pos, entrysize; + unsigned int pos, entrysize; /* do we have a ResetTable file? */ int err = find_sys_file(self, sec, &sec->rtable, rtable_name); @@ -1144,25 +1169,25 @@ static int read_reset_table(struct mschm_decompressor_p *self, /* read ResetTable file */ if (sec->rtable->length < lzxrt_headerSIZEOF) { - D(("ResetTable file is too short")) - return 0; + D(("ResetTable file is too short")) + return 0; } if (!(data = read_sys_file(self, sec->rtable))) { - D(("can't read reset table")) - return 0; + D(("can't read reset table")) + return 0; } /* check sanity of reset table */ if (EndGetI32(&data[lzxrt_FrameLen]) != LZX_FRAME_SIZE) { - D(("bad reset table frame length")) - sys->free(data); - return 0; + D(("bad reset table frame length")) + sys->free(data); + return 0; } /* get the uncompressed length of the LZX stream */ - if (read_off64(length_ptr, data, sys, self->d->infh)) { - sys->free(data); - return 0; + if (read_off64(length_ptr, &data[lzxrt_UncompLen], sys, self->d->infh)) { + sys->free(data); + return 0; } entrysize = EndGetI32(&data[lzxrt_EntrySize]); @@ -1170,25 +1195,25 @@ static int read_reset_table(struct mschm_decompressor_p *self, /* ensure reset table entry for this offset exists */ if (entry < EndGetI32(&data[lzxrt_NumEntries]) && - ((pos + entrysize) <= sec->rtable->length)) + pos <= (sec->rtable->length - entrysize)) { - switch (entrysize) { - case 4: - *offset_ptr = EndGetI32(&data[pos]); - err = 0; - break; - case 8: - err = read_off64(offset_ptr, &data[pos], sys, self->d->infh); - break; - default: - D(("reset table entry size neither 4 nor 8")) - err = 1; - break; - } + switch (entrysize) { + case 4: + *offset_ptr = EndGetI32(&data[pos]); + err = 0; + break; + case 8: + err = read_off64(offset_ptr, &data[pos], sys, self->d->infh); + break; + default: + D(("reset table entry size neither 4 nor 8")) + err = 1; + break; + } } else { - D(("bad reset interval")) - err = 1; + D(("bad reset interval")) + err = 1; } /* free the reset table */ @@ -1205,8 +1230,8 @@ static int read_reset_table(struct mschm_decompressor_p *self, * Returns zero for success or a non-zero error code for failure. */ static int read_spaninfo(struct mschm_decompressor_p *self, - struct mschmd_sec_mscompressed *sec, - off_t *length_ptr) + struct mschmd_sec_mscompressed *sec, + off_t *length_ptr) { struct mspack_system *sys = self->system; unsigned char *data; @@ -1217,21 +1242,27 @@ static int read_spaninfo(struct mschm_decompressor_p *self, /* check it's large enough */ if (sec->spaninfo->length != 8) { - D(("SpanInfo file is wrong size")) - return MSPACK_ERR_DATAFORMAT; + D(("SpanInfo file is wrong size")) + return MSPACK_ERR_DATAFORMAT; } /* read the SpanInfo file */ if (!(data = read_sys_file(self, sec->spaninfo))) { - D(("can't read SpanInfo file")) - return self->error; + D(("can't read SpanInfo file")) + return self->error; } /* get the uncompressed length of the LZX stream */ err = read_off64(length_ptr, data, sys, self->d->infh); - sys->free(data); - return (err) ? MSPACK_ERR_DATAFORMAT : MSPACK_ERR_OK; + if (err) return MSPACK_ERR_DATAFORMAT; + + if (*length_ptr <= 0) { + D(("output length is invalid")) + return MSPACK_ERR_DATAFORMAT; + } + + return MSPACK_ERR_OK; } /*************************************** @@ -1242,8 +1273,8 @@ static int read_spaninfo(struct mschm_decompressor_p *self, * for success, non-zero for both failure and the file not existing. */ static int find_sys_file(struct mschm_decompressor_p *self, - struct mschmd_sec_mscompressed *sec, - struct mschmd_file **f_ptr, const char *name) + struct mschmd_sec_mscompressed *sec, + struct mschmd_file **f_ptr, const char *name) { struct mspack_system *sys = self->system; struct mschmd_file result; @@ -1254,13 +1285,13 @@ static int find_sys_file(struct mschm_decompressor_p *self, /* try using fast_find to find the file - return DATAFORMAT error if * it fails, or successfully doesn't find the file */ if (chmd_fast_find((struct mschm_decompressor *) self, sec->base.chm, - name, &result, (int)sizeof(result)) || !result.section) + name, &result, (int)sizeof(result)) || !result.section) { - return MSPACK_ERR_DATAFORMAT; + return MSPACK_ERR_DATAFORMAT; } if (!(*f_ptr = (struct mschmd_file *) sys->alloc(sys, sizeof(result)))) { - return MSPACK_ERR_NOMEMORY; + return MSPACK_ERR_NOMEMORY; } /* copy result */ @@ -1280,7 +1311,7 @@ static int find_sys_file(struct mschm_decompressor_p *self, * memory. */ static unsigned char *read_sys_file(struct mschm_decompressor_p *self, - struct mschmd_file *file) + struct mschmd_file *file) { struct mspack_system *sys = self->system; unsigned char *data = NULL; @@ -1298,7 +1329,7 @@ static unsigned char *read_sys_file(struct mschm_decompressor_p *self, return NULL; } if (sys->seek(self->d->infh, file->section->chm->sec0.offset - + file->offset, MSPACK_SYS_SEEK_START)) + + file->offset, MSPACK_SYS_SEEK_START)) { self->error = MSPACK_ERR_SEEK; sys->free(data); @@ -1331,15 +1362,15 @@ static int chmd_error(struct mschm_decompressor *base) { * are accepted, offsets beyond that cause an error message. */ static int read_off64(off_t *var, unsigned char *mem, - struct mspack_system *sys, struct mspack_file *fh) + struct mspack_system *sys, struct mspack_file *fh) { -#ifdef LARGEFILE_SUPPORT +#if LARGEFILE_SUPPORT *var = EndGetI64(mem); #else *var = EndGetI32(mem); if ((*var & 0x80000000) || EndGetI32(mem+4)) { - sys->message(fh, (char *)largefile_msg); - return 1; + sys->message(fh, (char *)largefile_msg); + return 1; } #endif return 0; diff --git a/rbutil/rbutilqt/mspack/kwajd.c b/rbutil/rbutilqt/mspack/kwajd.c index 440f1dcfdd..24e0b0613b 100644 --- a/rbutil/rbutilqt/mspack/kwajd.c +++ b/rbutil/rbutilqt/mspack/kwajd.c @@ -1,5 +1,5 @@ /* This file is part of libmspack. - * (C) 2003-2010 Stuart Caie. + * (C) 2003-2011 Stuart Caie. * * KWAJ is a format very similar to SZDD. KWAJ method 3 (LZH) was * written by Jeff Johnson. @@ -14,6 +14,7 @@ #include "system-mspack.h" #include "kwaj.h" +#include "mszip.h" /* prototypes */ static struct mskwajd_header *kwajd_open( @@ -40,7 +41,7 @@ static void lzh_free( static int lzh_read_lens( struct kwajd_stream *kwaj, unsigned int type, unsigned int numsyms, - unsigned char *lens, unsigned short *table); + unsigned char *lens); static int lzh_read_input( struct kwajd_stream *kwaj); @@ -79,8 +80,8 @@ void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *base) { struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; if (self) { - struct mspack_system *sys = self->system; - sys->free(self); + struct mspack_system *sys = self->system; + sys->free(self); } } @@ -90,7 +91,7 @@ void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *base) * opens a KWAJ file without decompressing, reads header */ static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base, - const char *filename) + const char *filename) { struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; struct mskwajd_header *hdr; @@ -103,18 +104,18 @@ static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base, fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ); hdr = (struct mskwajd_header *) sys->alloc(sys, sizeof(struct mskwajd_header_p)); if (fh && hdr) { - ((struct mskwajd_header_p *) hdr)->fh = fh; - self->error = kwajd_read_headers(sys, fh, hdr); + ((struct mskwajd_header_p *) hdr)->fh = fh; + self->error = kwajd_read_headers(sys, fh, hdr); } else { - if (!fh) self->error = MSPACK_ERR_OPEN; - if (!hdr) self->error = MSPACK_ERR_NOMEMORY; + if (!fh) self->error = MSPACK_ERR_OPEN; + if (!hdr) self->error = MSPACK_ERR_NOMEMORY; } if (self->error) { - if (fh) sys->close(fh); - if (hdr) sys->free(hdr); - hdr = NULL; + if (fh) sys->close(fh); + sys->free(hdr); + hdr = NULL; } return hdr; @@ -126,7 +127,7 @@ static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base, * closes a KWAJ file */ static void kwajd_close(struct mskwaj_decompressor *base, - struct mskwajd_header *hdr) + struct mskwajd_header *hdr) { struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; struct mskwajd_header_p *hdr_p = (struct mskwajd_header_p *) hdr; @@ -148,22 +149,22 @@ static void kwajd_close(struct mskwaj_decompressor *base, * reads the headers of a KWAJ format file */ static int kwajd_read_headers(struct mspack_system *sys, - struct mspack_file *fh, - struct mskwajd_header *hdr) + struct mspack_file *fh, + struct mskwajd_header *hdr) { unsigned char buf[16]; int i; /* read in the header */ if (sys->read(fh, &buf[0], kwajh_SIZEOF) != kwajh_SIZEOF) { - return MSPACK_ERR_READ; + return MSPACK_ERR_READ; } /* check for "KWAJ" signature */ if (((unsigned int) EndGetI32(&buf[kwajh_Signature1]) != 0x4A41574B) || - ((unsigned int) EndGetI32(&buf[kwajh_Signature2]) != 0xD127F088)) + ((unsigned int) EndGetI32(&buf[kwajh_Signature2]) != 0xD127F088)) { - return MSPACK_ERR_SIGNATURE; + return MSPACK_ERR_SIGNATURE; } /* basic header fields */ @@ -179,61 +180,67 @@ static int kwajd_read_headers(struct mspack_system *sys, /* 4 bytes: length of unpacked file */ if (hdr->headers & MSKWAJ_HDR_HASLENGTH) { - if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ; - hdr->length = EndGetI32(&buf[0]); + if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ; + hdr->length = EndGetI32(&buf[0]); } /* 2 bytes: unknown purpose */ if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN1) { - if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; + if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; } /* 2 bytes: length of section, then [length] bytes: unknown purpose */ if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN2) { - if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; - i = EndGetI16(&buf[0]); - if (sys->seek(fh, (off_t)i, MSPACK_SYS_SEEK_CUR)) return MSPACK_ERR_SEEK; + if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; + i = EndGetI16(&buf[0]); + if (sys->seek(fh, (off_t)i, MSPACK_SYS_SEEK_CUR)) return MSPACK_ERR_SEEK; } /* filename and extension */ if (hdr->headers & (MSKWAJ_HDR_HASFILENAME | MSKWAJ_HDR_HASFILEEXT)) { - off_t pos = sys->tell(fh); - char *fn = (char *) sys->alloc(sys, (size_t) 13); - - /* allocate memory for maximum length filename */ - if (! fn) return MSPACK_ERR_NOMEMORY; - hdr->filename = fn; - - /* copy filename if present */ - if (hdr->headers & MSKWAJ_HDR_HASFILENAME) { - if (sys->read(fh, &buf[0], 9) != 9) return MSPACK_ERR_READ; - for (i = 0; i < 9; i++, fn++) if (!(*fn = buf[i])) break; - pos += (i < 9) ? i+1 : 9; - if (sys->seek(fh, pos, MSPACK_SYS_SEEK_START)) - return MSPACK_ERR_SEEK; - } - - /* copy extension if present */ - if (hdr->headers & MSKWAJ_HDR_HASFILEEXT) { - *fn++ = '.'; - if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ; - for (i = 0; i < 4; i++, fn++) if (!(*fn = buf[i])) break; - pos += (i < 4) ? i+1 : 4; - if (sys->seek(fh, pos, MSPACK_SYS_SEEK_START)) - return MSPACK_ERR_SEEK; - } - *fn = '\0'; + int len; + /* allocate memory for maximum length filename */ + char *fn = (char *) sys->alloc(sys, (size_t) 13); + if (!(hdr->filename = fn)) return MSPACK_ERR_NOMEMORY; + + /* copy filename if present */ + if (hdr->headers & MSKWAJ_HDR_HASFILENAME) { + /* read and copy up to 9 bytes of a null terminated string */ + if ((len = sys->read(fh, &buf[0], 9)) < 2) return MSPACK_ERR_READ; + for (i = 0; i < len; i++) if (!(*fn++ = buf[i])) break; + /* if string was 9 bytes with no null terminator, reject it */ + if (i == 9 && buf[8] != '\0') return MSPACK_ERR_DATAFORMAT; + /* seek to byte after string ended in file */ + if (sys->seek(fh, (off_t)(i + 1 - len), MSPACK_SYS_SEEK_CUR)) + return MSPACK_ERR_SEEK; + fn--; /* remove the null terminator */ + } + + /* copy extension if present */ + if (hdr->headers & MSKWAJ_HDR_HASFILEEXT) { + *fn++ = '.'; + /* read and copy up to 4 bytes of a null terminated string */ + if ((len = sys->read(fh, &buf[0], 4)) < 2) return MSPACK_ERR_READ; + for (i = 0; i < len; i++) if (!(*fn++ = buf[i])) break; + /* if string was 4 bytes with no null terminator, reject it */ + if (i == 4 && buf[3] != '\0') return MSPACK_ERR_DATAFORMAT; + /* seek to byte after string ended in file */ + if (sys->seek(fh, (off_t)(i + 1 - len), MSPACK_SYS_SEEK_CUR)) + return MSPACK_ERR_SEEK; + fn--; /* remove the null terminator */ + } + *fn = '\0'; } /* 2 bytes: extra text length then [length] bytes of extra text data */ if (hdr->headers & MSKWAJ_HDR_HASEXTRATEXT) { - if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; - i = EndGetI16(&buf[0]); - hdr->extra = (char *) sys->alloc(sys, (size_t)i+1); - if (! hdr->extra) return MSPACK_ERR_NOMEMORY; - if (sys->read(fh, hdr->extra, i) != i) return MSPACK_ERR_READ; - hdr->extra[i] = '\0'; - hdr->extra_length = i; + if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; + i = EndGetI16(&buf[0]); + hdr->extra = (char *) sys->alloc(sys, (size_t)i+1); + if (! hdr->extra) return MSPACK_ERR_NOMEMORY; + if (sys->read(fh, hdr->extra, i) != i) return MSPACK_ERR_READ; + hdr->extra[i] = '\0'; + hdr->extra_length = i; } return MSPACK_ERR_OK; } @@ -244,7 +251,7 @@ static int kwajd_read_headers(struct mspack_system *sys, * decompresses a KWAJ file */ static int kwajd_extract(struct mskwaj_decompressor *base, - struct mskwajd_header *hdr, const char *filename) + struct mskwajd_header *hdr, const char *filename) { struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; struct mspack_system *sys; @@ -258,51 +265,56 @@ static int kwajd_extract(struct mskwaj_decompressor *base, /* seek to the compressed data */ if (sys->seek(fh, hdr->data_offset, MSPACK_SYS_SEEK_START)) { - return self->error = MSPACK_ERR_SEEK; + return self->error = MSPACK_ERR_SEEK; } /* open file for output */ if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { - return self->error = MSPACK_ERR_OPEN; + return self->error = MSPACK_ERR_OPEN; } self->error = MSPACK_ERR_OK; /* decompress based on format */ if (hdr->comp_type == MSKWAJ_COMP_NONE || - hdr->comp_type == MSKWAJ_COMP_XOR) + hdr->comp_type == MSKWAJ_COMP_XOR) { - /* NONE is a straight copy. XOR is a copy xored with 0xFF */ - unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) KWAJ_INPUT_SIZE); - if (buf) { - int read, i; - while ((read = sys->read(fh, buf, KWAJ_INPUT_SIZE)) > 0) { - if (hdr->comp_type == MSKWAJ_COMP_XOR) { - for (i = 0; i < read; i++) buf[i] ^= 0xFF; - } - if (sys->write(outfh, buf, read) != read) { - self->error = MSPACK_ERR_WRITE; - break; - } - } - if (read < 0) self->error = MSPACK_ERR_READ; - sys->free(buf); - } - else { - self->error = MSPACK_ERR_NOMEMORY; - } + /* NONE is a straight copy. XOR is a copy xored with 0xFF */ + unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) KWAJ_INPUT_SIZE); + if (buf) { + int read, i; + while ((read = sys->read(fh, buf, KWAJ_INPUT_SIZE)) > 0) { + if (hdr->comp_type == MSKWAJ_COMP_XOR) { + for (i = 0; i < read; i++) buf[i] ^= 0xFF; + } + if (sys->write(outfh, buf, read) != read) { + self->error = MSPACK_ERR_WRITE; + break; + } + } + if (read < 0) self->error = MSPACK_ERR_READ; + sys->free(buf); + } + else { + self->error = MSPACK_ERR_NOMEMORY; + } } else if (hdr->comp_type == MSKWAJ_COMP_SZDD) { - self->error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE, - LZSS_MODE_EXPAND); + self->error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE, + LZSS_MODE_EXPAND); } else if (hdr->comp_type == MSKWAJ_COMP_LZH) { - struct kwajd_stream *lzh = lzh_init(sys, fh, outfh); - self->error = (lzh) ? lzh_decompress(lzh) : MSPACK_ERR_NOMEMORY; - lzh_free(lzh); + struct kwajd_stream *lzh = lzh_init(sys, fh, outfh); + self->error = (lzh) ? lzh_decompress(lzh) : MSPACK_ERR_NOMEMORY; + lzh_free(lzh); + } + else if (hdr->comp_type == MSKWAJ_COMP_MSZIP) { + struct mszipd_stream *zip = mszipd_init(sys,fh,outfh,KWAJ_INPUT_SIZE,0); + self->error = (zip) ? mszipd_decompress_kwaj(zip) : MSPACK_ERR_NOMEMORY; + mszipd_free(zip); } else { - self->error = MSPACK_ERR_DATAFORMAT; + self->error = MSPACK_ERR_DATAFORMAT; } /* close output file */ @@ -317,7 +329,7 @@ static int kwajd_extract(struct mskwaj_decompressor *base, * unpacks directly from input to output */ static int kwajd_decompress(struct mskwaj_decompressor *base, - const char *input, const char *output) + const char *input, const char *output) { struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; struct mskwajd_header *hdr; @@ -353,15 +365,15 @@ static int kwajd_error(struct mskwaj_decompressor *base) #define BITS_VAR lzh #define BITS_ORDER_MSB #define BITS_NO_READ_INPUT -#define READ_BYTES do { \ - if (i_ptr >= i_end) { \ - if ((err = lzh_read_input(lzh))) return err; \ - i_ptr = lzh->i_ptr; \ - i_end = lzh->i_end; \ - } \ - INJECT_BITS(*i_ptr++, 8); \ +#define READ_BYTES do { \ + if (i_ptr >= i_end) { \ + if ((err = lzh_read_input(lzh))) return err; \ + i_ptr = lzh->i_ptr; \ + i_end = lzh->i_end; \ + } \ + INJECT_BITS(*i_ptr++, 8); \ } while (0) -#include "readbits.h" +#include /* import huffman-reading macros and code */ #define TABLEBITS(tbl) KWAJ_TABLEBITS @@ -369,7 +381,7 @@ static int kwajd_error(struct mskwaj_decompressor *base) #define HUFF_TABLE(tbl,idx) lzh->tbl##_table[idx] #define HUFF_LEN(tbl,idx) lzh->tbl##_len[idx] #define HUFF_ERROR return MSPACK_ERR_DATAFORMAT -#include "readhuff.h" +#include /* In the KWAJ LZH format, there is no special 'eof' marker, it just * ends. Depending on how many bits are left in the final byte when @@ -381,31 +393,30 @@ static int kwajd_error(struct mskwaj_decompressor *base) * isn't how the default readbits.h read_input() works (it simply lets * 2 fake bytes in then stops), so we implement our own. */ -#define READ_BITS_SAFE(val, n) do { \ - READ_BITS(val, n); \ - if (lzh->input_end && bits_left < lzh->input_end) \ - return MSPACK_ERR_OK; \ +#define READ_BITS_SAFE(val, n) do { \ + READ_BITS(val, n); \ + if (lzh->input_end && bits_left < lzh->input_end) \ + return MSPACK_ERR_OK; \ } while (0) -#define READ_HUFFSYM_SAFE(tbl, val) do { \ - READ_HUFFSYM(tbl, val); \ - if (lzh->input_end && bits_left < lzh->input_end) \ - return MSPACK_ERR_OK; \ +#define READ_HUFFSYM_SAFE(tbl, val) do { \ + READ_HUFFSYM(tbl, val); \ + if (lzh->input_end && bits_left < lzh->input_end) \ + return MSPACK_ERR_OK; \ } while (0) -#define BUILD_TREE(tbl, type) \ - STORE_BITS; \ - err = lzh_read_lens(lzh, type, MAXSYMBOLS(tbl), \ - &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0)); \ - if (err) return err; \ - RESTORE_BITS; \ - if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ - &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ - return MSPACK_ERR_DATAFORMAT; - -#define WRITE_BYTE do { \ - if (lzh->sys->write(lzh->output, &lzh->window[pos], 1) != 1) \ - return MSPACK_ERR_WRITE; \ +#define BUILD_TREE(tbl, type) \ + STORE_BITS; \ + err = lzh_read_lens(lzh, type, MAXSYMBOLS(tbl), &HUFF_LEN(tbl,0)); \ + if (err) return err; \ + RESTORE_BITS; \ + if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ + &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ + return MSPACK_ERR_DATAFORMAT; + +#define WRITE_BYTE do { \ + if (lzh->sys->write(lzh->output, &lzh->window[pos], 1) != 1) \ + return MSPACK_ERR_WRITE; \ } while (0) static struct kwajd_stream *lzh_init(struct mspack_system *sys, @@ -447,33 +458,33 @@ static int lzh_decompress(struct kwajd_stream *lzh) BUILD_TREE(LITERAL, types[4]); while (!lzh->input_end) { - if (lit_run) READ_HUFFSYM_SAFE(MATCHLEN2, len); - else READ_HUFFSYM_SAFE(MATCHLEN1, len); - - if (len > 0) { - len += 2; - lit_run = 0; /* not the end of a literal run */ - READ_HUFFSYM_SAFE(OFFSET, j); offset = j << 6; - READ_BITS_SAFE(j, 6); offset |= j; - - /* copy match as output and into the ring buffer */ - while (len-- > 0) { - lzh->window[pos] = lzh->window[(pos+4096-offset) & 4095]; - WRITE_BYTE; - pos++; pos &= 4095; - } - } - else { - READ_HUFFSYM_SAFE(LITLEN, len); len++; - lit_run = (len == 32) ? 0 : 1; /* end of a literal run? */ - while (len-- > 0) { - READ_HUFFSYM_SAFE(LITERAL, j); - /* copy as output and into the ring buffer */ - lzh->window[pos] = j; - WRITE_BYTE; - pos++; pos &= 4095; - } - } + if (lit_run) READ_HUFFSYM_SAFE(MATCHLEN2, len); + else READ_HUFFSYM_SAFE(MATCHLEN1, len); + + if (len > 0) { + len += 2; + lit_run = 0; /* not the end of a literal run */ + READ_HUFFSYM_SAFE(OFFSET, j); offset = j << 6; + READ_BITS_SAFE(j, 6); offset |= j; + + /* copy match as output and into the ring buffer */ + while (len-- > 0) { + lzh->window[pos] = lzh->window[(pos+4096-offset) & 4095]; + WRITE_BYTE; + pos++; pos &= 4095; + } + } + else { + READ_HUFFSYM_SAFE(LITLEN, len); len++; + lit_run = (len == 32) ? 0 : 1; /* end of a literal run? */ + while (len-- > 0) { + READ_HUFFSYM_SAFE(LITERAL, j); + /* copy as output and into the ring buffer */ + lzh->window[pos] = j; + WRITE_BYTE; + pos++; pos &= 4095; + } + } } return MSPACK_ERR_OK; } @@ -487,8 +498,8 @@ static void lzh_free(struct kwajd_stream *lzh) } static int lzh_read_lens(struct kwajd_stream *lzh, - unsigned int type, unsigned int numsyms, - unsigned char *lens, unsigned short *table) + unsigned int type, unsigned int numsyms, + unsigned char *lens) { register unsigned int bit_buffer; register int bits_left; @@ -499,33 +510,33 @@ static int lzh_read_lens(struct kwajd_stream *lzh, RESTORE_BITS; switch (type) { case 0: - i = numsyms; c = (i==16)?4: (i==32)?5: (i==64)?6: (i==256)?8 :0; - for (i = 0; i < numsyms; i++) lens[i] = c; - break; + i = numsyms; c = (i==16)?4: (i==32)?5: (i==64)?6: (i==256)?8 :0; + for (i = 0; i < numsyms; i++) lens[i] = c; + break; case 1: - READ_BITS_SAFE(c, 4); lens[0] = c; - for (i = 1; i < numsyms; i++) { - READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = c; - else { READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = ++c; - else { READ_BITS_SAFE(c, 4); lens[i] = c; }} - } - break; + READ_BITS_SAFE(c, 4); lens[0] = c; + for (i = 1; i < numsyms; i++) { + READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = c; + else { READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = ++c; + else { READ_BITS_SAFE(c, 4); lens[i] = c; }} + } + break; case 2: - READ_BITS_SAFE(c, 4); lens[0] = c; - for (i = 1; i < numsyms; i++) { - READ_BITS_SAFE(sel, 2); - if (sel == 3) READ_BITS_SAFE(c, 4); else c += (char) sel-1; - lens[i] = c; - } - break; + READ_BITS_SAFE(c, 4); lens[0] = c; + for (i = 1; i < numsyms; i++) { + READ_BITS_SAFE(sel, 2); + if (sel == 3) READ_BITS_SAFE(c, 4); else c += (char) sel-1; + lens[i] = c; + } + break; case 3: - for (i = 0; i < numsyms; i++) { - READ_BITS_SAFE(c, 4); lens[i] = c; - } - break; + for (i = 0; i < numsyms; i++) { + READ_BITS_SAFE(c, 4); lens[i] = c; + } + break; } STORE_BITS; return MSPACK_ERR_OK; @@ -534,18 +545,18 @@ static int lzh_read_lens(struct kwajd_stream *lzh, static int lzh_read_input(struct kwajd_stream *lzh) { int read; if (lzh->input_end) { - lzh->input_end += 8; - lzh->inbuf[0] = 0; - read = 1; + lzh->input_end += 8; + lzh->inbuf[0] = 0; + read = 1; } else { - read = lzh->sys->read(lzh->input, &lzh->inbuf[0], KWAJ_INPUT_SIZE); - if (read < 0) return MSPACK_ERR_READ; - if (read == 0) { - lzh->input_end = 8; - lzh->inbuf[0] = 0; - read = 1; - } + read = lzh->sys->read(lzh->input, &lzh->inbuf[0], KWAJ_INPUT_SIZE); + if (read < 0) return MSPACK_ERR_READ; + if (read == 0) { + lzh->input_end = 8; + lzh->inbuf[0] = 0; + read = 1; + } } /* update i_ptr and i_end */ diff --git a/rbutil/rbutilqt/mspack/lzss.h b/rbutil/rbutilqt/mspack/lzss.h index 55e761b5bf..aa946e52ae 100644 --- a/rbutil/rbutilqt/mspack/lzss.h +++ b/rbutil/rbutilqt/mspack/lzss.h @@ -54,10 +54,10 @@ extern "C" { * @return an error code, or MSPACK_ERR_OK if successful */ extern int lzss_decompress(struct mspack_system *system, - struct mspack_file *input, - struct mspack_file *output, - int input_buffer_size, - int mode); + struct mspack_file *input, + struct mspack_file *output, + int input_buffer_size, + int mode); #ifdef __cplusplus } diff --git a/rbutil/rbutilqt/mspack/lzssd.c b/rbutil/rbutilqt/mspack/lzssd.c index f1a47c7a01..63716d414a 100644 --- a/rbutil/rbutilqt/mspack/lzssd.c +++ b/rbutil/rbutilqt/mspack/lzssd.c @@ -14,31 +14,31 @@ #include "system-mspack.h" #include "lzss.h" -#define ENSURE_BYTES do { \ - if (i_ptr >= i_end) { \ - read = system->read(input, &inbuf[0], \ - input_buffer_size); \ - if (read <= 0) { \ - system->free(window); \ - return (read < 0) ? MSPACK_ERR_READ \ - : MSPACK_ERR_OK; \ - } \ - i_ptr = &inbuf[0]; i_end = &inbuf[read]; \ - } \ +#define ENSURE_BYTES do { \ + if (i_ptr >= i_end) { \ + read = system->read(input, &inbuf[0], \ + input_buffer_size); \ + if (read <= 0) { \ + system->free(window); \ + return (read < 0) ? MSPACK_ERR_READ \ + : MSPACK_ERR_OK; \ + } \ + i_ptr = &inbuf[0]; i_end = &inbuf[read]; \ + } \ } while (0) -#define WRITE_BYTE do { \ - if (system->write(output, &window[pos], 1) != 1) { \ - system->free(window); \ - return MSPACK_ERR_WRITE; \ - } \ +#define WRITE_BYTE do { \ + if (system->write(output, &window[pos], 1) != 1) { \ + system->free(window); \ + return MSPACK_ERR_WRITE; \ + } \ } while (0) int lzss_decompress(struct mspack_system *system, - struct mspack_file *input, - struct mspack_file *output, - int input_buffer_size, - int mode) + struct mspack_file *input, + struct mspack_file *output, + int input_buffer_size, + int mode) { unsigned char *window, *inbuf, *i_ptr, *i_end; unsigned int pos, i, c, invert, mpos, len; @@ -48,7 +48,7 @@ int lzss_decompress(struct mspack_system *system, if (!system || input_buffer_size < 1 || (mode != LZSS_MODE_EXPAND && mode != LZSS_MODE_MSHELP && mode != LZSS_MODE_QBASIC)) { - return MSPACK_ERR_ARGS; + return MSPACK_ERR_ARGS; } /* allocate memory */ @@ -64,27 +64,27 @@ int lzss_decompress(struct mspack_system *system, /* loop forever; exit condition is in ENSURE_BYTES macro */ for (;;) { - ENSURE_BYTES; c = *i_ptr++ ^ invert; - for (i = 0x01; i & 0xFF; i <<= 1) { - if (c & i) { - /* literal */ - ENSURE_BYTES; window[pos] = *i_ptr++; - WRITE_BYTE; - pos++; pos &= LZSS_WINDOW_SIZE - 1; - } - else { - /* match */ - ENSURE_BYTES; mpos = *i_ptr++; - ENSURE_BYTES; mpos |= (*i_ptr & 0xF0) << 4; - len = (*i_ptr++ & 0x0F) + 3; - while (len--) { - window[pos] = window[mpos]; - WRITE_BYTE; - pos++; pos &= LZSS_WINDOW_SIZE - 1; - mpos++; mpos &= LZSS_WINDOW_SIZE - 1; - } - } - } + ENSURE_BYTES; c = *i_ptr++ ^ invert; + for (i = 0x01; i & 0xFF; i <<= 1) { + if (c & i) { + /* literal */ + ENSURE_BYTES; window[pos] = *i_ptr++; + WRITE_BYTE; + pos++; pos &= LZSS_WINDOW_SIZE - 1; + } + else { + /* match */ + ENSURE_BYTES; mpos = *i_ptr++; + ENSURE_BYTES; mpos |= (*i_ptr & 0xF0) << 4; + len = (*i_ptr++ & 0x0F) + 3; + while (len--) { + window[pos] = window[mpos]; + WRITE_BYTE; + pos++; pos &= LZSS_WINDOW_SIZE - 1; + mpos++; mpos &= LZSS_WINDOW_SIZE - 1; + } + } + } } /* not reached */ diff --git a/rbutil/rbutilqt/mspack/lzx.h b/rbutil/rbutilqt/mspack/lzx.h index fc69928c96..a6152f622b 100644 --- a/rbutil/rbutilqt/mspack/lzx.h +++ b/rbutil/rbutilqt/mspack/lzx.h @@ -1,5 +1,5 @@ /* This file is part of libmspack. - * (C) 2003-2004 Stuart Caie. + * (C) 2003-2013 Stuart Caie. * * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted * by Microsoft Corporation. @@ -35,7 +35,7 @@ extern "C" { /* LZX huffman defines: tweak tablebits as desired */ #define LZX_PRETREE_MAXSYMBOLS (LZX_PRETREE_NUM_ELEMENTS) #define LZX_PRETREE_TABLEBITS (6) -#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 50*8) +#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 290*8) #define LZX_MAINTREE_TABLEBITS (12) #define LZX_LENGTH_MAXSYMBOLS (LZX_NUM_SECONDARY_LENGTHS+1) #define LZX_LENGTH_TABLEBITS (12) @@ -55,6 +55,8 @@ struct lzxd_stream { unsigned char *window; /* decoding window */ unsigned int window_size; /* window size */ + unsigned int ref_data_size; /* LZX DELTA reference data size */ + unsigned int num_offsets; /* number of match_offset entries in table */ unsigned int window_posn; /* decompression offset within window */ unsigned int frame_posn; /* current frame offset within in window */ unsigned int frame; /* the number of 32kb frames processed */ @@ -70,8 +72,8 @@ struct lzxd_stream { unsigned char intel_started; /* has intel E8 decoding started? */ unsigned char block_type; /* type of the current block */ unsigned char header_read; /* have we started decoding at all yet? */ - unsigned char posn_slots; /* how many posn slots in stream? */ unsigned char input_end; /* have we reached the end of input? */ + unsigned char is_delta; /* does stream follow LZX DELTA spec? */ int error; @@ -87,13 +89,13 @@ struct lzxd_stream { /* huffman decoding tables */ unsigned short PRETREE_table [(1 << LZX_PRETREE_TABLEBITS) + - (LZX_PRETREE_MAXSYMBOLS * 2)]; + (LZX_PRETREE_MAXSYMBOLS * 2)]; unsigned short MAINTREE_table[(1 << LZX_MAINTREE_TABLEBITS) + - (LZX_MAINTREE_MAXSYMBOLS * 2)]; + (LZX_MAINTREE_MAXSYMBOLS * 2)]; unsigned short LENGTH_table [(1 << LZX_LENGTH_TABLEBITS) + - (LZX_LENGTH_MAXSYMBOLS * 2)]; + (LZX_LENGTH_MAXSYMBOLS * 2)]; unsigned short ALIGNED_table [(1 << LZX_ALIGNED_TABLEBITS) + - (LZX_ALIGNED_MAXSYMBOLS * 2)]; + (LZX_ALIGNED_MAXSYMBOLS * 2)]; unsigned char LENGTH_empty; /* this is used purely for doing the intel E8 transform */ @@ -114,12 +116,14 @@ struct lzxd_stream { * @param input an input stream with the LZX data. * @param output an output stream to write the decoded data to. * @param window_bits the size of the decoding window, which must be - * between 15 and 21 inclusive. + * between 15 and 21 inclusive for regular LZX + * data, or between 17 and 25 inclusive for + * LZX DELTA data. * @param reset_interval the interval at which the LZX bitstream is * reset, in multiples of LZX frames (32678 * bytes), e.g. a value of 2 indicates the input * stream resets after every 65536 output bytes. - * A value of 0 indicates that the bistream never + * A value of 0 indicates that the bitstream never * resets, such as in CAB LZX streams. * @param input_buffer_size the number of bytes to use as an input * bitstream buffer. @@ -135,26 +139,49 @@ struct lzxd_stream { * lzxd_set_output_length() once it is * known. If never set, 4 of the final 6 bytes * of the output stream may be incorrect. + * @param is_delta should be zero for all regular LZX data, + * non-zero for LZX DELTA encoded data. * @return a pointer to an initialised lzxd_stream structure, or NULL if * there was not enough memory or parameters to the function were wrong. */ extern struct lzxd_stream *lzxd_init(struct mspack_system *system, - struct mspack_file *input, - struct mspack_file *output, - int window_bits, - int reset_interval, - int input_buffer_size, - off_t output_length); + struct mspack_file *input, + struct mspack_file *output, + int window_bits, + int reset_interval, + int input_buffer_size, + off_t output_length, + char is_delta); /* see description of output_length in lzxd_init() */ extern void lzxd_set_output_length(struct lzxd_stream *lzx, - off_t output_length); + off_t output_length); + +/** + * Reads LZX DELTA reference data into the window and allows + * lzxd_decompress() to reference it. + * + * Call this before the first call to lzxd_decompress(). + + * @param lzx the LZX stream to apply this reference data to + * @param system an mspack_system implementation to use with the + * input param. Only read() will be called. + * @param input an input file handle to read reference data using + * system->read(). + * @param length the length of the reference data. Cannot be longer + * than the LZX window size. + * @return an error code, or MSPACK_ERR_OK if successful + */ +extern int lzxd_set_reference_data(struct lzxd_stream *lzx, + struct mspack_system *system, + struct mspack_file *input, + unsigned int length); /** * Decompresses entire or partial LZX streams. * * The number of bytes of data that should be decompressed is given as the - * out_bytes parameter. If more bytes are decoded than are needed, they + * out_bytes parameter. If more bytes are decoded than are needed, they * will be kept over for a later invocation. * * The output bytes will be passed to the system->write() function given in diff --git a/rbutil/rbutilqt/mspack/lzxd.c b/rbutil/rbutilqt/mspack/lzxd.c index 9b26bac3e0..88cfd90c2a 100644 --- a/rbutil/rbutilqt/mspack/lzxd.c +++ b/rbutil/rbutilqt/mspack/lzxd.c @@ -1,5 +1,5 @@ /* This file is part of libmspack. - * (C) 2003-2004 Stuart Caie. + * (C) 2003-2013 Stuart Caie. * * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted * by Microsoft Corporation. @@ -70,6 +70,10 @@ * The maximum window size has increased from 2MB to 32MB. This also * increases the maximum number of position slots, etc. * + * If the match length is 257 (the maximum possible), this signals + * a further length decoding step, that allows for matches up to + * 33024 bytes long. + * * The format now allows for "reference data", supplied by the caller. * If match offsets go further back than the number of bytes * decompressed so far, that is them accessing the reference data. @@ -79,11 +83,11 @@ #define BITS_TYPE struct lzxd_stream #define BITS_VAR lzx #define BITS_ORDER_MSB -#define READ_BYTES do { \ - unsigned char b0, b1; \ - READ_IF_NEEDED; b0 = *i_ptr++; \ - READ_IF_NEEDED; b1 = *i_ptr++; \ - INJECT_BITS((b1 << 8) | b0, 16); \ +#define READ_BYTES do { \ + unsigned char b0, b1; \ + READ_IF_NEEDED; b0 = *i_ptr++; \ + READ_IF_NEEDED; b1 = *i_ptr++; \ + INJECT_BITS((b1 << 8) | b0, 16); \ } while (0) #include "readbits.h" @@ -96,43 +100,43 @@ #include "readhuff.h" /* BUILD_TABLE(tbl) builds a huffman lookup table from code lengths */ -#define BUILD_TABLE(tbl) \ - if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ - &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ - { \ - D(("failed to build %s table", #tbl)) \ - return lzx->error = MSPACK_ERR_DECRUNCH; \ +#define BUILD_TABLE(tbl) \ + if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ + &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ + { \ + D(("failed to build %s table", #tbl)) \ + return lzx->error = MSPACK_ERR_DECRUNCH; \ } -#define BUILD_TABLE_MAYBE_EMPTY(tbl) do { \ - lzx->tbl##_empty = 0; \ - if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ - &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ - { \ - for (i = 0; i < MAXSYMBOLS(tbl); i++) { \ - if (HUFF_LEN(tbl, i) > 0) { \ - D(("failed to build %s table", #tbl)) \ - return lzx->error = MSPACK_ERR_DECRUNCH; \ - } \ - } \ - /* empty tree - allow it, but don't decode symbols with it */ \ - lzx->tbl##_empty = 1; \ - } \ +#define BUILD_TABLE_MAYBE_EMPTY(tbl) do { \ + lzx->tbl##_empty = 0; \ + if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ + &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ + { \ + for (i = 0; i < MAXSYMBOLS(tbl); i++) { \ + if (HUFF_LEN(tbl, i) > 0) { \ + D(("failed to build %s table", #tbl)) \ + return lzx->error = MSPACK_ERR_DECRUNCH; \ + } \ + } \ + /* empty tree - allow it, but don't decode symbols with it */ \ + lzx->tbl##_empty = 1; \ + } \ } while (0) /* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols * first to last in the given table. The code lengths are stored in their * own special LZX way. */ -#define READ_LENGTHS(tbl, first, last) do { \ - STORE_BITS; \ - if (lzxd_read_lens(lzx, &HUFF_LEN(tbl, 0), (first), \ - (unsigned int)(last))) return lzx->error; \ - RESTORE_BITS; \ +#define READ_LENGTHS(tbl, first, last) do { \ + STORE_BITS; \ + if (lzxd_read_lens(lzx, &HUFF_LEN(tbl, 0), (first), \ + (unsigned int)(last))) return lzx->error; \ + RESTORE_BITS; \ } while (0) static int lzxd_read_lens(struct lzxd_stream *lzx, unsigned char *lens, - unsigned int first, unsigned int last) + unsigned int first, unsigned int last) { /* bit buffer and huffman symbol decode variables */ register unsigned int bit_buffer; @@ -189,27 +193,70 @@ static int lzxd_read_lens(struct lzxd_stream *lzx, unsigned char *lens, * a small 'position slot' number and a small offset from that slot are * encoded instead of one large offset. * + * The number of slots is decided by how many are needed to encode the + * largest offset for a given window size. This is easy when the gap between + * slots is less than 128Kb, it's a linear relationship. But when extra_bits + * reaches its limit of 17 (because LZX can only ensure reading 17 bits of + * data at a time), we can only jump 128Kb at a time and have to start + * using more and more position slots as each window size doubles. + * * position_base[] is an index to the position slot bases * * extra_bits[] states how many bits of offset-from-base data is needed. * - * They are generated like so: - * for (i = 0; i < 4; i++) extra_bits[i] = 0; - * for (i = 4, j = 0; i < 36; i+=2) extra_bits[i] = extra_bits[i+1] = j++; - * for (i = 36; i < 51; i++) extra_bits[i] = 17; - * for (i = 0, j = 0; i < 51; j += 1 << extra_bits[i++]) position_base[i] = j; + * They are calculated as follows: + * extra_bits[i] = 0 where i < 4 + * extra_bits[i] = floor(i/2)-1 where i >= 4 && i < 36 + * extra_bits[i] = 17 where i >= 36 + * position_base[0] = 0 + * position_base[i] = position_base[i-1] + (1 << extra_bits[i-1]) */ -static const unsigned int position_base[51] = { - 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, - 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, - 16384, 24576, 32768, 49152, 65536, 98304, 131072, 196608, 262144, - 393216, 524288, 655360, 786432, 917504, 1048576, 1179648, 1310720, - 1441792, 1572864, 1703936, 1835008, 1966080, 2097152 +static const unsigned int position_slots[11] = { + 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290 +}; +static const unsigned char extra_bits[36] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16 }; -static const unsigned char extra_bits[51] = { - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, - 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 +static const unsigned int position_base[290] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, + 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, + 49152, 65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360, + 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936, + 1835008, 1966080, 2097152, 2228224, 2359296, 2490368, 2621440, 2752512, + 2883584, 3014656, 3145728, 3276800, 3407872, 3538944, 3670016, 3801088, + 3932160, 4063232, 4194304, 4325376, 4456448, 4587520, 4718592, 4849664, + 4980736, 5111808, 5242880, 5373952, 5505024, 5636096, 5767168, 5898240, + 6029312, 6160384, 6291456, 6422528, 6553600, 6684672, 6815744, 6946816, + 7077888, 7208960, 7340032, 7471104, 7602176, 7733248, 7864320, 7995392, + 8126464, 8257536, 8388608, 8519680, 8650752, 8781824, 8912896, 9043968, + 9175040, 9306112, 9437184, 9568256, 9699328, 9830400, 9961472, 10092544, + 10223616, 10354688, 10485760, 10616832, 10747904, 10878976, 11010048, + 11141120, 11272192, 11403264, 11534336, 11665408, 11796480, 11927552, + 12058624, 12189696, 12320768, 12451840, 12582912, 12713984, 12845056, + 12976128, 13107200, 13238272, 13369344, 13500416, 13631488, 13762560, + 13893632, 14024704, 14155776, 14286848, 14417920, 14548992, 14680064, + 14811136, 14942208, 15073280, 15204352, 15335424, 15466496, 15597568, + 15728640, 15859712, 15990784, 16121856, 16252928, 16384000, 16515072, + 16646144, 16777216, 16908288, 17039360, 17170432, 17301504, 17432576, + 17563648, 17694720, 17825792, 17956864, 18087936, 18219008, 18350080, + 18481152, 18612224, 18743296, 18874368, 19005440, 19136512, 19267584, + 19398656, 19529728, 19660800, 19791872, 19922944, 20054016, 20185088, + 20316160, 20447232, 20578304, 20709376, 20840448, 20971520, 21102592, + 21233664, 21364736, 21495808, 21626880, 21757952, 21889024, 22020096, + 22151168, 22282240, 22413312, 22544384, 22675456, 22806528, 22937600, + 23068672, 23199744, 23330816, 23461888, 23592960, 23724032, 23855104, + 23986176, 24117248, 24248320, 24379392, 24510464, 24641536, 24772608, + 24903680, 25034752, 25165824, 25296896, 25427968, 25559040, 25690112, + 25821184, 25952256, 26083328, 26214400, 26345472, 26476544, 26607616, + 26738688, 26869760, 27000832, 27131904, 27262976, 27394048, 27525120, + 27656192, 27787264, 27918336, 28049408, 28180480, 28311552, 28442624, + 28573696, 28704768, 28835840, 28966912, 29097984, 29229056, 29360128, + 29491200, 29622272, 29753344, 29884416, 30015488, 30146560, 30277632, + 30408704, 30539776, 30670848, 30801920, 30932992, 31064064, 31195136, + 31326208, 31457280, 31588352, 31719424, 31850496, 31981568, 32112640, + 32243712, 32374784, 32505856, 32636928, 32768000, 32899072, 33030144, + 33161216, 33292288, 33423360 }; static void lzxd_reset_state(struct lzxd_stream *lzx) { @@ -230,23 +277,37 @@ static void lzxd_reset_state(struct lzxd_stream *lzx) { /*-------- main LZX code --------*/ struct lzxd_stream *lzxd_init(struct mspack_system *system, - struct mspack_file *input, - struct mspack_file *output, - int window_bits, - int reset_interval, - int input_buffer_size, - off_t output_length) + struct mspack_file *input, + struct mspack_file *output, + int window_bits, + int reset_interval, + int input_buffer_size, + off_t output_length, + char is_delta) { unsigned int window_size = 1 << window_bits; struct lzxd_stream *lzx; if (!system) return NULL; - /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */ - if (window_bits < 15 || window_bits > 21) return NULL; + /* LZX DELTA window sizes are between 2^17 (128KiB) and 2^25 (32MiB), + * regular LZX windows are between 2^15 (32KiB) and 2^21 (2MiB) + */ + if (is_delta) { + if (window_bits < 17 || window_bits > 25) return NULL; + } + else { + if (window_bits < 15 || window_bits > 21) return NULL; + } + if (reset_interval < 0 || output_length < 0) { + D(("reset interval or output length < 0")) + return NULL; + } + + /* round up input buffer size to multiple of two */ input_buffer_size = (input_buffer_size + 1) & -2; - if (!input_buffer_size) return NULL; + if (input_buffer_size < 2) return NULL; /* allocate decompression state */ if (!(lzx = (struct lzxd_stream *) system->alloc(system, sizeof(struct lzxd_stream)))) { @@ -272,6 +333,7 @@ struct lzxd_stream *lzxd_init(struct mspack_system *system, lzx->inbuf_size = input_buffer_size; lzx->window_size = 1 << window_bits; + lzx->ref_data_size = 0; lzx->window_posn = 0; lzx->frame_posn = 0; lzx->frame = 0; @@ -280,11 +342,8 @@ struct lzxd_stream *lzxd_init(struct mspack_system *system, lzx->intel_curpos = 0; lzx->intel_started = 0; lzx->error = MSPACK_ERR_OK; - - /* window bits: 15 16 17 18 19 20 21 - * position slots: 30 32 34 36 38 42 50 */ - lzx->posn_slots = ((window_bits == 21) ? 50 : - ((window_bits == 20) ? 42 : (window_bits << 1))); + lzx->num_offsets = position_slots[window_bits - 15] << 3; + lzx->is_delta = is_delta; lzx->o_ptr = lzx->o_end = &lzx->e8_buf[0]; lzxd_reset_state(lzx); @@ -292,8 +351,44 @@ struct lzxd_stream *lzxd_init(struct mspack_system *system, return lzx; } +int lzxd_set_reference_data(struct lzxd_stream *lzx, + struct mspack_system *system, + struct mspack_file *input, + unsigned int length) +{ + if (!lzx) return MSPACK_ERR_ARGS; + + if (!lzx->is_delta) { + D(("only LZX DELTA streams support reference data")) + return MSPACK_ERR_ARGS; + } + if (lzx->offset) { + D(("too late to set reference data after decoding starts")) + return MSPACK_ERR_ARGS; + } + if (length > lzx->window_size) { + D(("reference length (%u) is longer than the window", length)) + return MSPACK_ERR_ARGS; + } + if (length > 0 && (!system || !input)) { + D(("length > 0 but no system or input")) + return MSPACK_ERR_ARGS; + } + + lzx->ref_data_size = length; + if (length > 0) { + /* copy reference data */ + unsigned char *pos = &lzx->window[lzx->window_size - length]; + int bytes = system->read(input, pos, length); + /* length can't be more than 2^25, so no signedness problem */ + if (bytes < (int)length) return MSPACK_ERR_READ; + } + lzx->ref_data_size = length; + return MSPACK_ERR_OK; +} + void lzxd_set_output_length(struct lzxd_stream *lzx, off_t out_bytes) { - if (lzx) lzx->length = out_bytes; + if (lzx && out_bytes > 0) lzx->length = out_bytes; } int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { @@ -304,7 +399,7 @@ int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { register unsigned short sym; int match_length, length_footer, extra, verbatim_bits, bytes_todo; - int this_run, main_element, aligned_bits, j; + int this_run, main_element, aligned_bits, j, warned = 0; unsigned char *window, *runsrc, *rundest, buf[12]; unsigned int frame_size=0, end_frame, match_offset, window_posn; unsigned int R0, R1, R2; @@ -340,8 +435,12 @@ int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { /* have we reached the reset interval? (if there is one?) */ if (lzx->reset_interval && ((lzx->frame % lzx->reset_interval) == 0)) { if (lzx->block_remaining) { - D(("%d bytes remaining at reset interval", lzx->block_remaining)) - return lzx->error = MSPACK_ERR_DECRUNCH; + /* this is a file format error, we can make a best effort to extract what we can */ + D(("%d bytes remaining at reset interval", lzx->block_remaining)) + if (!warned) { + lzx->sys->message(NULL, "WARNING; invalid reset interval detected during LZX decompression"); + warned++; + } } /* re-read the intel header and reset the huffman lengths */ @@ -351,6 +450,12 @@ int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { R2 = lzx->R2; } + /* LZX DELTA format has chunk_size, not present in LZX format */ + if (lzx->is_delta) { + ENSURE_BITS(16); + REMOVE_BITS(16); + } + /* read header if necessary */ if (!lzx->header_read) { /* read 1 bit. if bit=0, intel filesize = 0. @@ -373,62 +478,61 @@ int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { while (bytes_todo > 0) { /* initialise new block, if one is needed */ if (lzx->block_remaining == 0) { - /* realign if previous block was an odd-sized UNCOMPRESSED block */ - if ((lzx->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) && - (lzx->block_length & 1)) - { - READ_IF_NEEDED; - i_ptr++; - } - - /* read block type (3 bits) and block length (24 bits) */ - READ_BITS(lzx->block_type, 3); - READ_BITS(i, 16); READ_BITS(j, 8); - lzx->block_remaining = lzx->block_length = (i << 8) | j; - /*D(("new block t%d len %u", lzx->block_type, lzx->block_length))*/ - - /* read individual block headers */ - switch (lzx->block_type) { - case LZX_BLOCKTYPE_ALIGNED: - /* read lengths of and build aligned huffman decoding tree */ - for (i = 0; i < 8; i++) { READ_BITS(j, 3); lzx->ALIGNED_len[i] = j; } - BUILD_TABLE(ALIGNED); - /* no break -- rest of aligned header is same as verbatim */ - case LZX_BLOCKTYPE_VERBATIM: - /* read lengths of and build main huffman decoding tree */ - READ_LENGTHS(MAINTREE, 0, 256); - READ_LENGTHS(MAINTREE, 256, LZX_NUM_CHARS + (lzx->posn_slots << 3)); - BUILD_TABLE(MAINTREE); - /* if the literal 0xE8 is anywhere in the block... */ - if (lzx->MAINTREE_len[0xE8] != 0) lzx->intel_started = 1; - /* read lengths of and build lengths huffman decoding tree */ - READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS); - BUILD_TABLE_MAYBE_EMPTY(LENGTH); - break; - - case LZX_BLOCKTYPE_UNCOMPRESSED: - /* because we can't assume otherwise */ - lzx->intel_started = 1; - - /* read 1-16 (not 0-15) bits to align to bytes */ - ENSURE_BITS(16); - if (bits_left > 16) i_ptr -= 2; - bits_left = 0; bit_buffer = 0; - - /* read 12 bytes of stored R0 / R1 / R2 values */ - for (rundest = &buf[0], i = 0; i < 12; i++) { - READ_IF_NEEDED; - *rundest++ = *i_ptr++; - } - R0 = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - R1 = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); - R2 = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24); - break; - - default: - D(("bad block type")) - return lzx->error = MSPACK_ERR_DECRUNCH; - } + /* realign if previous block was an odd-sized UNCOMPRESSED block */ + if ((lzx->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) && + (lzx->block_length & 1)) + { + READ_IF_NEEDED; + i_ptr++; + } + + /* read block type (3 bits) and block length (24 bits) */ + READ_BITS(lzx->block_type, 3); + READ_BITS(i, 16); READ_BITS(j, 8); + lzx->block_remaining = lzx->block_length = (i << 8) | j; + /*D(("new block t%d len %u", lzx->block_type, lzx->block_length))*/ + + /* read individual block headers */ + switch (lzx->block_type) { + case LZX_BLOCKTYPE_ALIGNED: + /* read lengths of and build aligned huffman decoding tree */ + for (i = 0; i < 8; i++) { READ_BITS(j, 3); lzx->ALIGNED_len[i] = j; } + BUILD_TABLE(ALIGNED); + /* rest of aligned header is same as verbatim */ /*@fallthrough@*/ + case LZX_BLOCKTYPE_VERBATIM: + /* read lengths of and build main huffman decoding tree */ + READ_LENGTHS(MAINTREE, 0, 256); + READ_LENGTHS(MAINTREE, 256, LZX_NUM_CHARS + lzx->num_offsets); + BUILD_TABLE(MAINTREE); + /* if the literal 0xE8 is anywhere in the block... */ + if (lzx->MAINTREE_len[0xE8] != 0) lzx->intel_started = 1; + /* read lengths of and build lengths huffman decoding tree */ + READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS); + BUILD_TABLE_MAYBE_EMPTY(LENGTH); + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + /* because we can't assume otherwise */ + lzx->intel_started = 1; + + /* read 1-16 (not 0-15) bits to align to bytes */ + if (bits_left == 0) ENSURE_BITS(16); + bits_left = 0; bit_buffer = 0; + + /* read 12 bytes of stored R0 / R1 / R2 values */ + for (rundest = &buf[0], i = 0; i < 12; i++) { + READ_IF_NEEDED; + *rundest++ = *i_ptr++; + } + R0 = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + R1 = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); + R2 = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24); + break; + + default: + D(("bad block type")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } } /* decode more of the block: @@ -443,208 +547,270 @@ int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { /* decode at least this_run bytes */ switch (lzx->block_type) { case LZX_BLOCKTYPE_VERBATIM: - while (this_run > 0) { - READ_HUFFSYM(MAINTREE, main_element); - if (main_element < LZX_NUM_CHARS) { - /* literal: 0 to LZX_NUM_CHARS-1 */ - window[window_posn++] = main_element; - this_run--; - } - else { - /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ - main_element -= LZX_NUM_CHARS; - - /* get match length */ - match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; - if (match_length == LZX_NUM_PRIMARY_LENGTHS) { - if (lzx->LENGTH_empty) { + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } + else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + /* get match length */ + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + if (lzx->LENGTH_empty) { D(("LENGTH symbol needed but tree is empty")) return lzx->error = MSPACK_ERR_DECRUNCH; } - READ_HUFFSYM(LENGTH, length_footer); - match_length += length_footer; - } - match_length += LZX_MIN_MATCH; - - /* get match offset */ - switch ((match_offset = (main_element >> 3))) { - case 0: match_offset = R0; break; - case 1: match_offset = R1; R1=R0; R0 = match_offset; break; - case 2: match_offset = R2; R2=R0; R0 = match_offset; break; - case 3: match_offset = 1; R2=R1; R1=R0; R0 = match_offset; break; - default: - extra = extra_bits[match_offset]; - READ_BITS(verbatim_bits, extra); - match_offset = position_base[match_offset] - 2 + verbatim_bits; - R2 = R1; R1 = R0; R0 = match_offset; - } - - if ((window_posn + match_length) > lzx->window_size) { - D(("match ran over window wrap")) - return lzx->error = MSPACK_ERR_DECRUNCH; - } - - /* copy match */ - rundest = &window[window_posn]; - i = match_length; - /* does match offset wrap the window? */ - if (match_offset > window_posn) { - /* j = length from match offset to end of window */ - j = match_offset - window_posn; - if (j > (int) lzx->window_size) { - D(("match offset beyond window boundaries")) - return lzx->error = MSPACK_ERR_DECRUNCH; - } - runsrc = &window[lzx->window_size - j]; - if (j < i) { - /* if match goes over the window edge, do two copy runs */ - i -= j; while (j-- > 0) *rundest++ = *runsrc++; - runsrc = window; - } - while (i-- > 0) *rundest++ = *runsrc++; - } - else { - runsrc = rundest - match_offset; - while (i-- > 0) *rundest++ = *runsrc++; - } - - this_run -= match_length; - window_posn += match_length; - } - } /* while (this_run > 0) */ - break; + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + /* get match offset */ + switch ((match_offset = (main_element >> 3))) { + case 0: match_offset = R0; break; + case 1: match_offset = R1; R1=R0; R0 = match_offset; break; + case 2: match_offset = R2; R2=R0; R0 = match_offset; break; + case 3: match_offset = 1; R2=R1; R1=R0; R0 = match_offset; break; + default: + extra = (match_offset >= 36) ? 17 : extra_bits[match_offset]; + READ_BITS(verbatim_bits, extra); + match_offset = position_base[match_offset] - 2 + verbatim_bits; + R2 = R1; R1 = R0; R0 = match_offset; + } + + /* LZX DELTA uses max match length to signal even longer match */ + if (match_length == LZX_MAX_MATCH && lzx->is_delta) { + int extra_len = 0; + ENSURE_BITS(3); /* 4 entry huffman tree */ + if (PEEK_BITS(1) == 0) { + REMOVE_BITS(1); /* '0' -> 8 extra length bits */ + READ_BITS(extra_len, 8); + } + else if (PEEK_BITS(2) == 2) { + REMOVE_BITS(2); /* '10' -> 10 extra length bits + 0x100 */ + READ_BITS(extra_len, 10); + extra_len += 0x100; + } + else if (PEEK_BITS(3) == 6) { + REMOVE_BITS(3); /* '110' -> 12 extra length bits + 0x500 */ + READ_BITS(extra_len, 12); + extra_len += 0x500; + } + else { + REMOVE_BITS(3); /* '111' -> 15 extra length bits */ + READ_BITS(extra_len, 15); + } + match_length += extra_len; + } + + if ((window_posn + match_length) > lzx->window_size) { + D(("match ran over window wrap")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* copy match */ + rundest = &window[window_posn]; + i = match_length; + /* does match offset wrap the window? */ + if (match_offset > window_posn) { + if (match_offset > lzx->offset && + (match_offset - window_posn) > lzx->ref_data_size) + { + D(("match offset beyond LZX stream")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + /* j = length from match offset to end of window */ + j = match_offset - window_posn; + if (j > (int) lzx->window_size) { + D(("match offset beyond window boundaries")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + runsrc = &window[lzx->window_size - j]; + if (j < i) { + /* if match goes over the window edge, do two copy runs */ + i -= j; while (j-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + while (i-- > 0) *rundest++ = *runsrc++; + } + else { + runsrc = rundest - match_offset; + while (i-- > 0) *rundest++ = *runsrc++; + } + + this_run -= match_length; + window_posn += match_length; + } + } /* while (this_run > 0) */ + break; case LZX_BLOCKTYPE_ALIGNED: - while (this_run > 0) { - READ_HUFFSYM(MAINTREE, main_element); - if (main_element < LZX_NUM_CHARS) { - /* literal: 0 to LZX_NUM_CHARS-1 */ - window[window_posn++] = main_element; - this_run--; - } - else { - /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ - main_element -= LZX_NUM_CHARS; - - /* get match length */ - match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; - if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } + else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + /* get match length */ + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { if (lzx->LENGTH_empty) { D(("LENGTH symbol needed but tree is empty")) return lzx->error = MSPACK_ERR_DECRUNCH; } - READ_HUFFSYM(LENGTH, length_footer); - match_length += length_footer; - } - match_length += LZX_MIN_MATCH; - - /* get match offset */ - switch ((match_offset = (main_element >> 3))) { - case 0: match_offset = R0; break; - case 1: match_offset = R1; R1 = R0; R0 = match_offset; break; - case 2: match_offset = R2; R2 = R0; R0 = match_offset; break; - default: - extra = extra_bits[match_offset]; - match_offset = position_base[match_offset] - 2; - if (extra > 3) { - /* verbatim and aligned bits */ - extra -= 3; - READ_BITS(verbatim_bits, extra); - match_offset += (verbatim_bits << 3); - READ_HUFFSYM(ALIGNED, aligned_bits); - match_offset += aligned_bits; - } - else if (extra == 3) { - /* aligned bits only */ - READ_HUFFSYM(ALIGNED, aligned_bits); - match_offset += aligned_bits; - } - else if (extra > 0) { /* extra==1, extra==2 */ - /* verbatim bits only */ - READ_BITS(verbatim_bits, extra); - match_offset += verbatim_bits; - } - else /* extra == 0 */ { - /* ??? not defined in LZX specification! */ - match_offset = 1; - } - /* update repeated offset LRU queue */ - R2 = R1; R1 = R0; R0 = match_offset; - } - - if ((window_posn + match_length) > lzx->window_size) { - D(("match ran over window wrap")) - return lzx->error = MSPACK_ERR_DECRUNCH; - } - - /* copy match */ - rundest = &window[window_posn]; - i = match_length; - /* does match offset wrap the window? */ - if (match_offset > window_posn) { - /* j = length from match offset to end of window */ - j = match_offset - window_posn; - if (j > (int) lzx->window_size) { - D(("match offset beyond window boundaries")) - return lzx->error = MSPACK_ERR_DECRUNCH; - } - runsrc = &window[lzx->window_size - j]; - if (j < i) { - /* if match goes over the window edge, do two copy runs */ - i -= j; while (j-- > 0) *rundest++ = *runsrc++; - runsrc = window; - } - while (i-- > 0) *rundest++ = *runsrc++; - } - else { - runsrc = rundest - match_offset; - while (i-- > 0) *rundest++ = *runsrc++; - } - - this_run -= match_length; - window_posn += match_length; - } - } /* while (this_run > 0) */ - break; + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + /* get match offset */ + switch ((match_offset = (main_element >> 3))) { + case 0: match_offset = R0; break; + case 1: match_offset = R1; R1 = R0; R0 = match_offset; break; + case 2: match_offset = R2; R2 = R0; R0 = match_offset; break; + default: + extra = (match_offset >= 36) ? 17 : extra_bits[match_offset]; + match_offset = position_base[match_offset] - 2; + if (extra > 3) { + /* verbatim and aligned bits */ + extra -= 3; + READ_BITS(verbatim_bits, extra); + match_offset += (verbatim_bits << 3); + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } + else if (extra == 3) { + /* aligned bits only */ + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } + else if (extra > 0) { /* extra==1, extra==2 */ + /* verbatim bits only */ + READ_BITS(verbatim_bits, extra); + match_offset += verbatim_bits; + } + else /* extra == 0 */ { + /* ??? not defined in LZX specification! */ + match_offset = 1; + } + /* update repeated offset LRU queue */ + R2 = R1; R1 = R0; R0 = match_offset; + } + + /* LZX DELTA uses max match length to signal even longer match */ + if (match_length == LZX_MAX_MATCH && lzx->is_delta) { + int extra_len = 0; + ENSURE_BITS(3); /* 4 entry huffman tree */ + if (PEEK_BITS(1) == 0) { + REMOVE_BITS(1); /* '0' -> 8 extra length bits */ + READ_BITS(extra_len, 8); + } + else if (PEEK_BITS(2) == 2) { + REMOVE_BITS(2); /* '10' -> 10 extra length bits + 0x100 */ + READ_BITS(extra_len, 10); + extra_len += 0x100; + } + else if (PEEK_BITS(3) == 6) { + REMOVE_BITS(3); /* '110' -> 12 extra length bits + 0x500 */ + READ_BITS(extra_len, 12); + extra_len += 0x500; + } + else { + REMOVE_BITS(3); /* '111' -> 15 extra length bits */ + READ_BITS(extra_len, 15); + } + match_length += extra_len; + } + + if ((window_posn + match_length) > lzx->window_size) { + D(("match ran over window wrap")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* copy match */ + rundest = &window[window_posn]; + i = match_length; + /* does match offset wrap the window? */ + if (match_offset > window_posn) { + if (match_offset > lzx->offset && + (match_offset - window_posn) > lzx->ref_data_size) + { + D(("match offset beyond LZX stream")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + /* j = length from match offset to end of window */ + j = match_offset - window_posn; + if (j > (int) lzx->window_size) { + D(("match offset beyond window boundaries")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + runsrc = &window[lzx->window_size - j]; + if (j < i) { + /* if match goes over the window edge, do two copy runs */ + i -= j; while (j-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + while (i-- > 0) *rundest++ = *runsrc++; + } + else { + runsrc = rundest - match_offset; + while (i-- > 0) *rundest++ = *runsrc++; + } + + this_run -= match_length; + window_posn += match_length; + } + } /* while (this_run > 0) */ + break; case LZX_BLOCKTYPE_UNCOMPRESSED: - /* as this_run is limited not to wrap a frame, this also means it - * won't wrap the window (as the window is a multiple of 32k) */ - rundest = &window[window_posn]; - window_posn += this_run; - while (this_run > 0) { - if ((i = i_end - i_ptr) == 0) { - READ_IF_NEEDED; - } - else { - if (i > this_run) i = this_run; - lzx->sys->copy(i_ptr, rundest, (size_t) i); - rundest += i; - i_ptr += i; - this_run -= i; - } - } - break; + /* as this_run is limited not to wrap a frame, this also means it + * won't wrap the window (as the window is a multiple of 32k) */ + rundest = &window[window_posn]; + window_posn += this_run; + while (this_run > 0) { + if ((i = i_end - i_ptr) == 0) { + READ_IF_NEEDED; + } + else { + if (i > this_run) i = this_run; + lzx->sys->copy(i_ptr, rundest, (size_t) i); + rundest += i; + i_ptr += i; + this_run -= i; + } + } + break; default: - return lzx->error = MSPACK_ERR_DECRUNCH; /* might as well */ + return lzx->error = MSPACK_ERR_DECRUNCH; /* might as well */ } /* did the final match overrun our desired this_run length? */ if (this_run < 0) { - if ((unsigned int)(-this_run) > lzx->block_remaining) { - D(("overrun went past end of block by %d (%d remaining)", - -this_run, lzx->block_remaining )) - return lzx->error = MSPACK_ERR_DECRUNCH; - } - lzx->block_remaining -= -this_run; + if ((unsigned int)(-this_run) > lzx->block_remaining) { + D(("overrun went past end of block by %d (%d remaining)", + -this_run, lzx->block_remaining )) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + lzx->block_remaining -= -this_run; } } /* while (bytes_todo > 0) */ /* streams don't extend over frame boundaries */ if ((window_posn - lzx->frame_posn) != frame_size) { D(("decode beyond output frame limits! %d != %d", - window_posn - lzx->frame_posn, frame_size)) + window_posn - lzx->frame_posn, frame_size)) return lzx->error = MSPACK_ERR_DECRUNCH; } @@ -654,13 +820,14 @@ int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { /* check that we've used all of the previous frame first */ if (lzx->o_ptr != lzx->o_end) { - D(("%ld avail bytes, new %d frame", lzx->o_end-lzx->o_ptr, frame_size)) + D(("%ld avail bytes, new %d frame", + (long)(lzx->o_end - lzx->o_ptr), frame_size)) return lzx->error = MSPACK_ERR_DECRUNCH; } /* does this intel block _really_ need decoding? */ if (lzx->intel_started && lzx->intel_filesize && - (lzx->frame <= 32768) && (frame_size > 10)) + (lzx->frame <= 32768) && (frame_size > 10)) { unsigned char *data = &lzx->e8_buf[0]; unsigned char *dataend = &lzx->e8_buf[frame_size - 10]; @@ -673,17 +840,17 @@ int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { lzx->sys->copy(&lzx->window[lzx->frame_posn], data, frame_size); while (data < dataend) { - if (*data++ != 0xE8) { curpos++; continue; } - abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); - if ((abs_off >= -curpos) && (abs_off < filesize)) { - rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; - data[0] = (unsigned char) rel_off; - data[1] = (unsigned char) (rel_off >> 8); - data[2] = (unsigned char) (rel_off >> 16); - data[3] = (unsigned char) (rel_off >> 24); - } - data += 4; - curpos += 5; + if (*data++ != 0xE8) { curpos++; continue; } + abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); + if ((abs_off >= -curpos) && (abs_off < filesize)) { + rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; + data[0] = (unsigned char) rel_off; + data[1] = (unsigned char) (rel_off >> 8); + data[2] = (unsigned char) (rel_off >> 16); + data[3] = (unsigned char) (rel_off >> 24); + } + data += 4; + curpos += 5; } lzx->intel_curpos += frame_size; } diff --git a/rbutil/rbutilqt/mspack/mspack.h b/rbutil/rbutilqt/mspack/mspack.h index 7f6bdf1465..3e99624463 100644 --- a/rbutil/rbutilqt/mspack/mspack.h +++ b/rbutil/rbutilqt/mspack/mspack.h @@ -1,5 +1,5 @@ /* libmspack -- a library for working with Microsoft compression formats. - * (C) 2003-2011 Stuart Caie + * (C) 2003-2019 Stuart Caie * * libmspack is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License (LGPL) version 2.1 @@ -30,6 +30,7 @@ * - .CAB (MS Cabinet) files, which use deflate, LZX or Quantum compression * - .CHM (HTML Help) files, which use LZX compression * - .LIT (MS EBook) files, which use LZX compression and DES encryption + * - .LZX (Exchange Offline Addressbook) files, which use LZX compression * * To determine the capabilities of the library, and the binary * compatibility version of any particular compressor or decompressor, use @@ -60,6 +61,7 @@ * - mspack_create_hlp_compressor() creates a mshlp_compressor * - mspack_create_szdd_compressor() creates a msszdd_compressor * - mspack_create_kwaj_compressor() creates a mskwaj_compressor + * - mspack_create_oab_compressor() creates a msoab_compressor * * For decompression: * - mspack_create_cab_decompressor() creates a mscab_decompressor @@ -68,6 +70,7 @@ * - mspack_create_hlp_decompressor() creates a mshlp_decompressor * - mspack_create_szdd_decompressor() creates a msszdd_decompressor * - mspack_create_kwaj_decompressor() creates a mskwaj_decompressor + * - mspack_create_oab_decompressor() creates a msoab_decompressor * * Once finished working with a format, each kind of * compressor/decompressor has its own specific destructor: @@ -83,6 +86,8 @@ * - mspack_destroy_szdd_decompressor() * - mspack_destroy_kwaj_compressor() * - mspack_destroy_kwaj_decompressor() + * - mspack_destroy_oab_compressor() + * - mspack_destroy_oab_decompressor() * * Destroying a compressor or decompressor does not destroy any objects, * structures or handles that have been created using that compressor or @@ -208,6 +213,8 @@ extern int mspack_sys_selftest_internal(int); * - #MSPACK_VER_MSSZDDC: the msszdd_compressor interface * - #MSPACK_VER_MSKWAJD: the mskwaj_decompressor interface * - #MSPACK_VER_MSKWAJC: the mskwaj_compressor interface + * - #MSPACK_VER_MSOABD: the msoab_decompressor interface + * - #MSPACK_VER_MSOABC: the msoab_compressor interface * * The result of the function should be interpreted as follows: * - -1: this interface is completely unknown to the library @@ -249,6 +256,10 @@ extern int mspack_version(int entity); #define MSPACK_VER_MSKWAJD (12) /** Pass to mspack_version() to get the mskwaj_compressor version */ #define MSPACK_VER_MSKWAJC (13) +/** Pass to mspack_version() to get the msoab_decompressor version */ +#define MSPACK_VER_MSOABD (14) +/** Pass to mspack_version() to get the msoab_compressor version */ +#define MSPACK_VER_MSOABC (15) /* --- file I/O abstraction ------------------------------------------------ */ @@ -297,8 +308,8 @@ struct mspack_system { * @see close(), read(), write(), seek(), tell(), message() */ struct mspack_file * (*open)(struct mspack_system *self, - const char *filename, - int mode); + const char *filename, + int mode); /** * Closes a previously opened file. If any memory was allocated for this @@ -317,12 +328,14 @@ struct mspack_system { * @param bytes the number of bytes to read from the file. * @return the number of bytes successfully read (this can be less than * the number requested), zero to mark the end of file, or less - * than zero to indicate an error. + * than zero to indicate an error. The library does not "retry" + * reads and assumes short reads are due to EOF, so you should + * avoid returning short reads because of transient errors. * @see open(), write() */ int (*read)(struct mspack_file *file, - void *buffer, - int bytes); + void *buffer, + int bytes); /** * Writes a given number of bytes to an open file. @@ -338,8 +351,8 @@ struct mspack_system { * @see open(), read() */ int (*write)(struct mspack_file *file, - void *buffer, - int bytes); + void *buffer, + int bytes); /** * Seeks to a specific file offset within an open file. @@ -365,8 +378,8 @@ struct mspack_system { * @see open(), tell() */ int (*seek)(struct mspack_file *file, - off_t offset, - int mode); + off_t offset, + int mode); /** * Returns the current file position (in bytes) of the given file. @@ -392,8 +405,8 @@ struct mspack_system { * @see open() */ void (*message)(struct mspack_file *file, - const char *format, - ...); + const char *format, + ...); /** * Allocates memory. @@ -406,12 +419,12 @@ struct mspack_system { * @see free() */ void * (*alloc)(struct mspack_system *self, - size_t bytes); + size_t bytes); /** * Frees memory. * - * @param ptr the memory to be freed. + * @param ptr the memory to be freed. NULL is accepted and ignored. * @see alloc() */ void (*free)(void *ptr); @@ -429,8 +442,8 @@ struct mspack_system { * @param bytes the size of the memory region, in bytes */ void (*copy)(void *src, - void *dest, - size_t bytes); + void *dest, + size_t bytes); /** * A null pointer to mark the end of mspack_system. It must equal NULL. @@ -645,6 +658,31 @@ extern void mspack_destroy_kwaj_compressor(struct mskwaj_compressor *self); extern void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *self); +/** Creates a new OAB compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #msoab_compressor or NULL + */ +extern struct msoab_compressor * + mspack_create_oab_compressor(struct mspack_system *sys); + +/** Creates a new OAB decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #msoab_decompressor or NULL + */ +extern struct msoab_decompressor * + mspack_create_oab_decompressor(struct mspack_system *sys); + +/** Destroys an existing OAB compressor. + * @param self the #msoab_compressor to destroy + */ +extern void mspack_destroy_oab_compressor(struct msoab_compressor *self); + +/** Destroys an existing OAB decompressor. + * @param self the #msoab_decompressor to destroy + */ +extern void mspack_destroy_oab_decompressor(struct msoab_decompressor *self); + + /* --- support for .CAB (MS Cabinet) file format --------------------------- */ /** @@ -896,6 +934,13 @@ struct mscabd_file { #define MSCABD_PARAM_FIXMSZIP (1) /** mscab_decompressor::set_param() parameter: size of decompression buffer */ #define MSCABD_PARAM_DECOMPBUF (2) +/** mscab_decompressor::set_param() parameter: salvage data from bad cabinets? + * If enabled, open() will skip file with bad folder indices or filenames + * rather than reject the whole cabinet, and extract() will limit rather than + * reject files with invalid offsets and lengths, and bad data block checksums + * will be ignored. Available only in CAB decoder version 2 and above. + */ +#define MSCABD_PARAM_SALVAGE (3) /** TODO */ struct mscab_compressor { @@ -931,7 +976,7 @@ struct mscab_decompressor { * @see close(), search(), last_error() */ struct mscabd_cabinet * (*open) (struct mscab_decompressor *self, - const char *filename); + const char *filename); /** * Closes a previously opened cabinet or cabinet set. @@ -963,7 +1008,7 @@ struct mscab_decompressor { * @see open(), search(), append(), prepend() */ void (*close)(struct mscab_decompressor *self, - struct mscabd_cabinet *cab); + struct mscabd_cabinet *cab); /** * Searches a regular file for embedded cabinets. @@ -1000,7 +1045,7 @@ struct mscab_decompressor { * @see close(), open(), last_error() */ struct mscabd_cabinet * (*search) (struct mscab_decompressor *self, - const char *filename); + const char *filename); /** * Appends one mscabd_cabinet to another, forming or extending a cabinet @@ -1043,8 +1088,8 @@ struct mscab_decompressor { * @see prepend(), open(), close() */ int (*append) (struct mscab_decompressor *self, - struct mscabd_cabinet *cab, - struct mscabd_cabinet *nextcab); + struct mscabd_cabinet *cab, + struct mscabd_cabinet *nextcab); /** * Prepends one mscabd_cabinet to another, forming or extending a @@ -1065,8 +1110,8 @@ struct mscab_decompressor { * @see append(), open(), close() */ int (*prepend) (struct mscab_decompressor *self, - struct mscabd_cabinet *cab, - struct mscabd_cabinet *prevcab); + struct mscabd_cabinet *cab, + struct mscabd_cabinet *prevcab); /** * Extracts a file from a cabinet or cabinet set. @@ -1091,8 +1136,8 @@ struct mscab_decompressor { * @return an error code, or MSPACK_ERR_OK if successful */ int (*extract)(struct mscab_decompressor *self, - struct mscabd_file *file, - const char *filename); + struct mscabd_file *file, + const char *filename); /** * Sets a CAB decompression engine parameter. @@ -1117,8 +1162,8 @@ struct mscab_decompressor { * @see search(), extract() */ int (*set_param)(struct mscab_decompressor *self, - int param, - int value); + int param, + int value); /** * Returns the error code set by the most recently called method. @@ -1403,8 +1448,8 @@ struct mschm_compressor { * @see use_temporary_file() set_param() */ int (*generate)(struct mschm_compressor *self, - struct mschmc_file file_list[], - const char *output_file); + struct mschmc_file file_list[], + const char *output_file); /** * Specifies whether a temporary file is used during CHM generation. @@ -1460,8 +1505,8 @@ struct mschm_compressor { * @see generate() */ int (*use_temporary_file)(struct mschm_compressor *self, - int use_temp_file, - const char *temp_file); + int use_temp_file, + const char *temp_file); /** * Sets a CHM compression engine parameter. * @@ -1508,8 +1553,8 @@ struct mschm_compressor { * @see generate() */ int (*set_param)(struct mschm_compressor *self, - int param, - unsigned int value); + int param, + int value); /** * Returns the error code set by the most recently called method. @@ -1551,7 +1596,7 @@ struct mschm_decompressor { * @see close() */ struct mschmd_header *(*open)(struct mschm_decompressor *self, - const char *filename); + const char *filename); /** * Closes a previously opened CHM helpfile. @@ -1571,7 +1616,7 @@ struct mschm_decompressor { * @see open(), fast_open() */ void (*close)(struct mschm_decompressor *self, - struct mschmd_header *chm); + struct mschmd_header *chm); /** * Extracts a file from a CHM helpfile. @@ -1592,8 +1637,8 @@ struct mschm_decompressor { * @return an error code, or MSPACK_ERR_OK if successful */ int (*extract)(struct mschm_decompressor *self, - struct mschmd_file *file, - const char *filename); + struct mschmd_file *file, + const char *filename); /** * Returns the error code set by the most recently called method. @@ -1631,7 +1676,7 @@ struct mschm_decompressor { * @see open(), close(), fast_find(), extract() */ struct mschmd_header *(*fast_open)(struct mschm_decompressor *self, - const char *filename); + const char *filename); /** * Finds file details quickly. @@ -1672,10 +1717,10 @@ struct mschm_decompressor { * @see open(), close(), fast_find(), extract() */ int (*fast_find)(struct mschm_decompressor *self, - struct mschmd_header *chm, - const char *filename, - struct mschmd_file *f_ptr, - int f_size); + struct mschmd_header *chm, + const char *filename, + struct mschmd_file *f_ptr, + int f_size); }; /* --- support for .LIT (EBook) file format -------------------------------- */ @@ -1781,9 +1826,9 @@ struct msszdd_compressor { * @see set_param() */ int (*compress)(struct msszdd_compressor *self, - const char *input, - const char *output, - off_t length); + const char *input, + const char *output, + off_t length); /** * Sets an SZDD compression engine parameter. @@ -1807,8 +1852,8 @@ struct msszdd_compressor { * @see compress() */ int (*set_param)(struct msszdd_compressor *self, - int param, - unsigned int value); + int param, + int value); /** * Returns the error code set by the most recently called method. @@ -1849,7 +1894,7 @@ struct msszdd_decompressor { * @see close() */ struct msszddd_header *(*open)(struct msszdd_decompressor *self, - const char *filename); + const char *filename); /** * Closes a previously opened SZDD file. @@ -1865,7 +1910,7 @@ struct msszdd_decompressor { * @see open() */ void (*close)(struct msszdd_decompressor *self, - struct msszddd_header *szdd); + struct msszddd_header *szdd); /** * Extracts the compressed data from a SZDD file. @@ -1881,8 +1926,8 @@ struct msszdd_decompressor { * @return an error code, or MSPACK_ERR_OK if successful */ int (*extract)(struct msszdd_decompressor *self, - struct msszddd_header *szdd, - const char *filename); + struct msszddd_header *szdd, + const char *filename); /** * Decompresses an SZDD file to an output file in one step. @@ -1902,8 +1947,8 @@ struct msszdd_decompressor { * @return an error code, or MSPACK_ERR_OK if successful */ int (*decompress)(struct msszdd_decompressor *self, - const char *input, - const char *output); + const char *input, + const char *output); /** * Returns the error code set by the most recently called method. @@ -1937,6 +1982,8 @@ struct msszdd_decompressor { #define MSKWAJ_COMP_SZDD (2) /** KWAJ compression type: LZ+Huffman compression */ #define MSKWAJ_COMP_LZH (3) +/** KWAJ compression type: MSZIP */ +#define MSKWAJ_COMP_MSZIP (4) /** KWAJ optional header flag: decompressed file length is included */ #define MSKWAJ_HDR_HASLENGTH (0x01) @@ -2015,9 +2062,9 @@ struct mskwaj_compressor { * @see set_param() */ int (*compress)(struct mskwaj_compressor *self, - const char *input, - const char *output, - off_t length); + const char *input, + const char *output, + off_t length); /** * Sets an KWAJ compression engine parameter. @@ -2043,8 +2090,8 @@ struct mskwaj_compressor { * @see generate() */ int (*set_param)(struct mskwaj_compressor *self, - int param, - unsigned int value); + int param, + int value); /** @@ -2065,7 +2112,7 @@ struct mskwaj_compressor { * filename is too long */ int (*set_filename)(struct mskwaj_compressor *self, - const char *filename); + const char *filename); /** * Sets arbitrary data that will be stored in the header of the @@ -2085,8 +2132,8 @@ struct mskwaj_compressor { * is too long */ int (*set_extra_data)(struct mskwaj_compressor *self, - void *data, - size_t bytes); + void *data, + size_t bytes); /** * Returns the error code set by the most recently called method. @@ -2127,7 +2174,7 @@ struct mskwaj_decompressor { * @see close() */ struct mskwajd_header *(*open)(struct mskwaj_decompressor *self, - const char *filename); + const char *filename); /** * Closes a previously opened KWAJ file. @@ -2142,7 +2189,7 @@ struct mskwaj_decompressor { * @see open() */ void (*close)(struct mskwaj_decompressor *self, - struct mskwajd_header *kwaj); + struct mskwajd_header *kwaj); /** * Extracts the compressed data from a KWAJ file. @@ -2158,8 +2205,8 @@ struct mskwaj_decompressor { * @return an error code, or MSPACK_ERR_OK if successful */ int (*extract)(struct mskwaj_decompressor *self, - struct mskwajd_header *kwaj, - const char *filename); + struct mskwajd_header *kwaj, + const char *filename); /** * Decompresses an KWAJ file to an output file in one step. @@ -2179,8 +2226,8 @@ struct mskwaj_decompressor { * @return an error code, or MSPACK_ERR_OK if successful */ int (*decompress)(struct mskwaj_decompressor *self, - const char *input, - const char *output); + const char *input, + const char *output); /** * Returns the error code set by the most recently called method. @@ -2196,6 +2243,141 @@ struct mskwaj_decompressor { int (*last_error)(struct mskwaj_decompressor *self); }; +/* --- support for .LZX (Offline Address Book) file format ----------------- */ + +/** + * A compressor for the Offline Address Book (OAB) format. + * + * All fields are READ ONLY. + * + * @see mspack_create_oab_compressor(), mspack_destroy_oab_compressor() + */ +struct msoab_compressor { + /** + * Compress a full OAB file. + * + * The input file will be read and the compressed contents written to the + * output file. + * + * @param self a self-referential pointer to the msoab_decompressor + * instance being called + * @param input the filename of the input file. This is passed + * directly to mspack_system::open(). + * @param output the filename of the output file. This is passed + * directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*compress) (struct msoab_compressor *self, + const char *input, + const char *output); + + /** + * Generate a compressed incremental OAB patch file. + * + * The two uncompressed files "input" and "base" will be read, and an + * incremental patch to generate "input" from "base" will be written to + * the output file. + * + * @param self a self-referential pointer to the msoab_compressor + * instance being called + * @param input the filename of the input file containing the new + * version of its contents. This is passed directly + * to mspack_system::open(). + * @param base the filename of the original base file containing + * the old version of its contents, against which the + * incremental patch shall generated. This is passed + * directly to mspack_system::open(). + * @param output the filename of the output file. This is passed + * directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*compress_incremental) (struct msoab_compressor *self, + const char *input, + const char *base, + const char *output); +}; + +/** + * A decompressor for .LZX (Offline Address Book) files + * + * All fields are READ ONLY. + * + * @see mspack_create_oab_decompressor(), mspack_destroy_oab_decompressor() + */ +struct msoab_decompressor { + /** + * Decompresses a full Offline Address Book file. + * + * If the input file is a valid compressed Offline Address Book file, + * it will be read and the decompressed contents will be written to + * the output file. + * + * @param self a self-referential pointer to the msoab_decompressor + * instance being called + * @param input the filename of the input file. This is passed + * directly to mspack_system::open(). + * @param output the filename of the output file. This is passed + * directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*decompress) (struct msoab_decompressor *self, + const char *input, + const char *output); + + /** + * Decompresses an Offline Address Book with an incremental patch file. + * + * This requires both a full UNCOMPRESSED Offline Address Book file to + * act as the "base", and a compressed incremental patch file as input. + * If the input file is valid, it will be decompressed with reference to + * the base file, and the decompressed contents will be written to the + * output file. + * + * There is no way to tell what the right base file is for the given + * incremental patch, but if you get it wrong, this will usually result + * in incorrect data being decompressed, which will then fail a checksum + * test. + * + * @param self a self-referential pointer to the msoab_decompressor + * instance being called + * @param input the filename of the input file. This is passed + * directly to mspack_system::open(). + * @param base the filename of the base file to which the + * incremental patch shall be applied. This is passed + * directly to mspack_system::open(). + * @param output the filename of the output file. This is passed + * directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*decompress_incremental) (struct msoab_decompressor *self, + const char *input, + const char *base, + const char *output); + + /** + * Sets an OAB decompression engine parameter. Available only in OAB + * decompressor version 2 and above. + * + * - #MSOABD_PARAM_DECOMPBUF: How many bytes should be used as an input + * buffer by decompressors? The minimum value is 16. The default value + * is 4096. + * + * @param self a self-referential pointer to the msoab_decompressor + * instance being called + * @param param the parameter to set + * @param value the value to set the parameter to + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there + * is a problem with either parameter or value. + */ + int (*set_param)(struct msoab_decompressor *self, + int param, + int value); + +}; + +/** msoab_decompressor::set_param() parameter: size of decompression buffer */ +#define MSOABD_PARAM_DECOMPBUF (0) + #ifdef __cplusplus } #endif diff --git a/rbutil/rbutilqt/mspack/mszip.h b/rbutil/rbutilqt/mspack/mszip.h index 13c9542ee6..2cd608234e 100644 --- a/rbutil/rbutilqt/mspack/mszip.h +++ b/rbutil/rbutilqt/mspack/mszip.h @@ -32,14 +32,14 @@ extern "C" { # define MSZIP_LITERAL_TABLESIZE (MSZIP_LITERAL_MAXSYMBOLS * 4) #else # define MSZIP_LITERAL_TABLESIZE ((1 << MSZIP_LITERAL_TABLEBITS) + \ - (MSZIP_LITERAL_MAXSYMBOLS * 2)) + (MSZIP_LITERAL_MAXSYMBOLS * 2)) #endif #if (1 << MSZIP_DISTANCE_TABLEBITS) < (MSZIP_DISTANCE_MAXSYMBOLS * 2) # define MSZIP_DISTANCE_TABLESIZE (MSZIP_DISTANCE_MAXSYMBOLS * 4) #else # define MSZIP_DISTANCE_TABLESIZE ((1 << MSZIP_DISTANCE_TABLEBITS) + \ - (MSZIP_DISTANCE_MAXSYMBOLS * 2)) + (MSZIP_DISTANCE_MAXSYMBOLS * 2)) #endif struct mszipd_stream { @@ -83,10 +83,10 @@ struct mszipd_stream { * a partial recovery of erroneous data. */ extern struct mszipd_stream *mszipd_init(struct mspack_system *system, - struct mspack_file *input, - struct mspack_file *output, - int input_buffer_size, - int repair_mode); + struct mspack_file *input, + struct mspack_file *output, + int input_buffer_size, + int repair_mode); /* decompresses, or decompresses more of, an MS-ZIP stream. * @@ -108,6 +108,11 @@ extern struct mszipd_stream *mszipd_init(struct mspack_system *system, */ extern int mszipd_decompress(struct mszipd_stream *zip, off_t out_bytes); +/* decompresses an entire MS-ZIP stream in a KWAJ file. Acts very much + * like mszipd_decompress(), but doesn't take an out_bytes parameter + */ +extern int mszipd_decompress_kwaj(struct mszipd_stream *zip); + /* frees all stream associated with an MS-ZIP data stream * * - calls system->free() using the system pointer given in mszipd_init() diff --git a/rbutil/rbutilqt/mspack/mszipd.c b/rbutil/rbutilqt/mspack/mszipd.c index 3c158fbd4d..c1b02b1207 100644 --- a/rbutil/rbutilqt/mspack/mszipd.c +++ b/rbutil/rbutilqt/mspack/mszipd.c @@ -20,9 +20,9 @@ #define BITS_VAR zip #define BITS_ORDER_LSB #define BITS_LSB_TABLE -#define READ_BYTES do { \ - READ_IF_NEEDED; \ - INJECT_BITS(*i_ptr++, 8); \ +#define READ_BYTES do { \ + READ_IF_NEEDED; \ + INJECT_BITS(*i_ptr++, 8); \ } while (0) #include "readbits.h" @@ -34,13 +34,13 @@ #define HUFF_ERROR return INF_ERR_HUFFSYM #include "readhuff.h" -#define FLUSH_IF_NEEDED do { \ - if (zip->window_posn == MSZIP_FRAME_SIZE) { \ - if (zip->flush_window(zip, MSZIP_FRAME_SIZE)) { \ - return INF_ERR_FLUSH; \ - } \ - zip->window_posn = 0; \ - } \ +#define FLUSH_IF_NEEDED do { \ + if (zip->window_posn == MSZIP_FRAME_SIZE) { \ + if (zip->flush_window(zip, MSZIP_FRAME_SIZE)) { \ + return INF_ERR_FLUSH; \ + } \ + zip->window_posn = 0; \ + } \ } while (0) /* match lengths for literal codes 257.. 285 */ @@ -181,14 +181,14 @@ static int inflate(struct mszipd_stream *zip) { /* read 4 bytes of data, emptying the bit-buffer if necessary */ for (i = 0; (bits_left >= 8); i++) { - if (i == 4) return INF_ERR_BITBUF; - lens_buf[i] = PEEK_BITS(8); - REMOVE_BITS(8); + if (i == 4) return INF_ERR_BITBUF; + lens_buf[i] = PEEK_BITS(8); + REMOVE_BITS(8); } if (bits_left != 0) return INF_ERR_BITBUF; while (i < 4) { - READ_IF_NEEDED; - lens_buf[i++] = *i_ptr++; + READ_IF_NEEDED; + lens_buf[i++] = *i_ptr++; } /* get the length and its complement */ @@ -198,18 +198,18 @@ static int inflate(struct mszipd_stream *zip) { /* read and copy the uncompressed data into the window */ while (length > 0) { - READ_IF_NEEDED; - - this_run = length; - if (this_run > (unsigned int)(i_end - i_ptr)) this_run = i_end - i_ptr; - if (this_run > (MSZIP_FRAME_SIZE - zip->window_posn)) - this_run = MSZIP_FRAME_SIZE - zip->window_posn; - - zip->sys->copy(i_ptr, &zip->window[zip->window_posn], this_run); - zip->window_posn += this_run; - i_ptr += this_run; - length -= this_run; - FLUSH_IF_NEEDED; + READ_IF_NEEDED; + + this_run = length; + if (this_run > (unsigned int)(i_end - i_ptr)) this_run = i_end - i_ptr; + if (this_run > (MSZIP_FRAME_SIZE - zip->window_posn)) + this_run = MSZIP_FRAME_SIZE - zip->window_posn; + + zip->sys->copy(i_ptr, &zip->window[zip->window_posn], this_run); + zip->window_posn += this_run; + i_ptr += this_run; + length -= this_run; + FLUSH_IF_NEEDED; } } else if ((block_type == 1) || (block_type == 2)) { @@ -217,92 +217,92 @@ static int inflate(struct mszipd_stream *zip) { unsigned int match_posn, code; if (block_type == 1) { - /* block with fixed Huffman codes */ - i = 0; - while (i < 144) zip->LITERAL_len[i++] = 8; - while (i < 256) zip->LITERAL_len[i++] = 9; - while (i < 280) zip->LITERAL_len[i++] = 7; - while (i < 288) zip->LITERAL_len[i++] = 8; - for (i = 0; i < 32; i++) zip->DISTANCE_len[i] = 5; + /* block with fixed Huffman codes */ + i = 0; + while (i < 144) zip->LITERAL_len[i++] = 8; + while (i < 256) zip->LITERAL_len[i++] = 9; + while (i < 280) zip->LITERAL_len[i++] = 7; + while (i < 288) zip->LITERAL_len[i++] = 8; + for (i = 0; i < 32; i++) zip->DISTANCE_len[i] = 5; } else { - /* block with dynamic Huffman codes */ - STORE_BITS; - if ((i = zip_read_lens(zip))) return i; - RESTORE_BITS; + /* block with dynamic Huffman codes */ + STORE_BITS; + if ((i = zip_read_lens(zip))) return i; + RESTORE_BITS; } /* now huffman lengths are read for either kind of block, * create huffman decoding tables */ if (make_decode_table(MSZIP_LITERAL_MAXSYMBOLS, MSZIP_LITERAL_TABLEBITS, - &zip->LITERAL_len[0], &zip->LITERAL_table[0])) + &zip->LITERAL_len[0], &zip->LITERAL_table[0])) { - return INF_ERR_LITERALTBL; + return INF_ERR_LITERALTBL; } if (make_decode_table(MSZIP_DISTANCE_MAXSYMBOLS,MSZIP_DISTANCE_TABLEBITS, - &zip->DISTANCE_len[0], &zip->DISTANCE_table[0])) + &zip->DISTANCE_len[0], &zip->DISTANCE_table[0])) { - return INF_ERR_DISTANCETBL; + return INF_ERR_DISTANCETBL; } /* decode forever until end of block code */ for (;;) { - READ_HUFFSYM(LITERAL, code); - if (code < 256) { - zip->window[zip->window_posn++] = (unsigned char) code; - FLUSH_IF_NEEDED; - } - else if (code == 256) { - /* END OF BLOCK CODE: loop break point */ - break; - } - else { - code -= 257; /* codes 257-285 are matches */ - if (code >= 29) return INF_ERR_LITCODE; /* codes 286-287 are illegal */ - READ_BITS_T(length, lit_extrabits[code]); - length += lit_lengths[code]; - - READ_HUFFSYM(DISTANCE, code); - if (code > 30) return INF_ERR_DISTCODE; - READ_BITS_T(distance, dist_extrabits[code]); - distance += dist_offsets[code]; - - /* match position is window position minus distance. If distance - * is more than window position numerically, it must 'wrap - * around' the frame size. */ - match_posn = ((distance > zip->window_posn) ? MSZIP_FRAME_SIZE : 0) - + zip->window_posn - distance; - - /* copy match */ - if (length < 12) { - /* short match, use slower loop but no loop setup code */ - while (length--) { - zip->window[zip->window_posn++] = zip->window[match_posn++]; - match_posn &= MSZIP_FRAME_SIZE - 1; - FLUSH_IF_NEEDED; - } - } - else { - /* longer match, use faster loop but with setup expense */ - unsigned char *runsrc, *rundest; - do { - this_run = length; - if ((match_posn + this_run) > MSZIP_FRAME_SIZE) - this_run = MSZIP_FRAME_SIZE - match_posn; - if ((zip->window_posn + this_run) > MSZIP_FRAME_SIZE) - this_run = MSZIP_FRAME_SIZE - zip->window_posn; - - rundest = &zip->window[zip->window_posn]; zip->window_posn += this_run; - runsrc = &zip->window[match_posn]; match_posn += this_run; - length -= this_run; - while (this_run--) *rundest++ = *runsrc++; - if (match_posn == MSZIP_FRAME_SIZE) match_posn = 0; - FLUSH_IF_NEEDED; - } while (length > 0); - } - - } /* else (code >= 257) */ + READ_HUFFSYM(LITERAL, code); + if (code < 256) { + zip->window[zip->window_posn++] = (unsigned char) code; + FLUSH_IF_NEEDED; + } + else if (code == 256) { + /* END OF BLOCK CODE: loop break point */ + break; + } + else { + code -= 257; /* codes 257-285 are matches */ + if (code >= 29) return INF_ERR_LITCODE; /* codes 286-287 are illegal */ + READ_BITS_T(length, lit_extrabits[code]); + length += lit_lengths[code]; + + READ_HUFFSYM(DISTANCE, code); + if (code >= 30) return INF_ERR_DISTCODE; + READ_BITS_T(distance, dist_extrabits[code]); + distance += dist_offsets[code]; + + /* match position is window position minus distance. If distance + * is more than window position numerically, it must 'wrap + * around' the frame size. */ + match_posn = ((distance > zip->window_posn) ? MSZIP_FRAME_SIZE : 0) + + zip->window_posn - distance; + + /* copy match */ + if (length < 12) { + /* short match, use slower loop but no loop setup code */ + while (length--) { + zip->window[zip->window_posn++] = zip->window[match_posn++]; + match_posn &= MSZIP_FRAME_SIZE - 1; + FLUSH_IF_NEEDED; + } + } + else { + /* longer match, use faster loop but with setup expense */ + unsigned char *runsrc, *rundest; + do { + this_run = length; + if ((match_posn + this_run) > MSZIP_FRAME_SIZE) + this_run = MSZIP_FRAME_SIZE - match_posn; + if ((zip->window_posn + this_run) > MSZIP_FRAME_SIZE) + this_run = MSZIP_FRAME_SIZE - zip->window_posn; + + rundest = &zip->window[zip->window_posn]; zip->window_posn += this_run; + runsrc = &zip->window[match_posn]; match_posn += this_run; + length -= this_run; + while (this_run--) *rundest++ = *runsrc++; + if (match_posn == MSZIP_FRAME_SIZE) match_posn = 0; + FLUSH_IF_NEEDED; + } while (length > 0); + } + + } /* else (code >= 257) */ } /* for(;;) -- break point at 'code == 256' */ } @@ -328,7 +328,7 @@ static int inflate(struct mszipd_stream *zip) { * is flushed, an error is raised. */ static int mszipd_flush_window(struct mszipd_stream *zip, - unsigned int data_flushed) + unsigned int data_flushed) { zip->bytes_output += data_flushed; if (zip->bytes_output > MSZIP_FRAME_SIZE) { @@ -340,17 +340,18 @@ static int mszipd_flush_window(struct mszipd_stream *zip, } struct mszipd_stream *mszipd_init(struct mspack_system *system, - struct mspack_file *input, - struct mspack_file *output, - int input_buffer_size, - int repair_mode) + struct mspack_file *input, + struct mspack_file *output, + int input_buffer_size, + int repair_mode) { struct mszipd_stream *zip; if (!system) return NULL; + /* round up input buffer size to multiple of two */ input_buffer_size = (input_buffer_size + 1) & -2; - if (!input_buffer_size) return NULL; + if (input_buffer_size < 2) return NULL; /* allocate decompression state */ if (!(zip = (struct mszipd_stream *) system->alloc(system, sizeof(struct mszipd_stream)))) { @@ -426,19 +427,19 @@ int mszipd_decompress(struct mszipd_stream *zip, off_t out_bytes) { if ((error = inflate(zip))) { D(("inflate error %d", error)) if (zip->repair_mode) { - /* recover partially-inflated buffers */ - if (zip->bytes_output == 0 && zip->window_posn > 0) { - zip->flush_window(zip, zip->window_posn); - } - zip->sys->message(NULL, "MSZIP error, %u bytes of data lost.", - MSZIP_FRAME_SIZE - zip->bytes_output); - for (i = zip->bytes_output; i < MSZIP_FRAME_SIZE; i++) { - zip->window[i] = '\0'; - } - zip->bytes_output = MSZIP_FRAME_SIZE; + /* recover partially-inflated buffers */ + if (zip->bytes_output == 0 && zip->window_posn > 0) { + zip->flush_window(zip, zip->window_posn); + } + zip->sys->message(NULL, "MSZIP error, %u bytes of data lost.", + MSZIP_FRAME_SIZE - zip->bytes_output); + for (i = zip->bytes_output; i < MSZIP_FRAME_SIZE; i++) { + zip->window[i] = '\0'; + } + zip->bytes_output = MSZIP_FRAME_SIZE; } else { - return zip->error = (error > 0) ? error : MSPACK_ERR_DECRUNCH; + return zip->error = (error > 0) ? error : MSPACK_ERR_DECRUNCH; } } zip->o_ptr = &zip->window[0]; @@ -465,6 +466,45 @@ int mszipd_decompress(struct mszipd_stream *zip, off_t out_bytes) { return MSPACK_ERR_OK; } +int mszipd_decompress_kwaj(struct mszipd_stream *zip) { + /* for the bit buffer */ + register unsigned int bit_buffer; + register int bits_left; + unsigned char *i_ptr, *i_end; + + int i, error, block_len; + + /* unpack blocks until block_len == 0 */ + for (;;) { + RESTORE_BITS; + + /* align to bytestream, read block_len */ + i = bits_left & 7; REMOVE_BITS(i); + READ_BITS(block_len, 8); + READ_BITS(i, 8); block_len |= i << 8; + + if (block_len == 0) break; + + /* read "CK" header */ + READ_BITS(i, 8); if (i != 'C') return MSPACK_ERR_DATAFORMAT; + READ_BITS(i, 8); if (i != 'K') return MSPACK_ERR_DATAFORMAT; + + /* inflate block */ + zip->window_posn = 0; + zip->bytes_output = 0; + STORE_BITS; + if ((error = inflate(zip))) { + D(("inflate error %d", error)) + return zip->error = (error > 0) ? error : MSPACK_ERR_DECRUNCH; + } + + /* write inflated block */ + if (zip->sys->write(zip->output, &zip->window[0], zip->bytes_output) + != zip->bytes_output) return zip->error = MSPACK_ERR_WRITE; + } + return MSPACK_ERR_OK; +} + void mszipd_free(struct mszipd_stream *zip) { struct mspack_system *sys; if (zip) { diff --git a/rbutil/rbutilqt/mspack/qtm.h b/rbutil/rbutilqt/mspack/qtm.h index ab0bb4c32c..20a38538a2 100644 --- a/rbutil/rbutilqt/mspack/qtm.h +++ b/rbutil/rbutilqt/mspack/qtm.h @@ -90,10 +90,10 @@ struct qtmd_stream { * - input_buffer_size is the number of bytes to use to store bitstream data. */ extern struct qtmd_stream *qtmd_init(struct mspack_system *system, - struct mspack_file *input, - struct mspack_file *output, - int window_bits, - int input_buffer_size); + struct mspack_file *input, + struct mspack_file *output, + int window_bits, + int input_buffer_size); /* decompresses, or decompresses more of, a Quantum stream. * diff --git a/rbutil/rbutilqt/mspack/qtmd.c b/rbutil/rbutilqt/mspack/qtmd.c index 0fb20da167..58e4787b7f 100644 --- a/rbutil/rbutilqt/mspack/qtmd.c +++ b/rbutil/rbutilqt/mspack/qtmd.c @@ -27,11 +27,11 @@ #define BITS_TYPE struct qtmd_stream #define BITS_VAR qtm #define BITS_ORDER_MSB -#define READ_BYTES do { \ - unsigned char b0, b1; \ - READ_IF_NEEDED; b0 = *i_ptr++; \ - READ_IF_NEEDED; b1 = *i_ptr++; \ - INJECT_BITS((b0 << 8) | b1, 16); \ +#define READ_BYTES do { \ + unsigned char b0, b1; \ + READ_IF_NEEDED; b0 = *i_ptr++; \ + READ_IF_NEEDED; b1 = *i_ptr++; \ + INJECT_BITS((b0 << 8) | b1, 16); \ } while (0) #include "readbits.h" @@ -115,7 +115,7 @@ static const unsigned char length_extra[27] = { else break; \ } \ L <<= 1; H = (H << 1) | 1; \ - ENSURE_BITS(1); \ + ENSURE_BITS(1); \ C = (C << 1) | PEEK_BITS(1); \ REMOVE_BITS(1); \ } \ @@ -130,7 +130,7 @@ static void qtmd_update_model(struct qtmd_model *model) { /* -1, not -2; the 0 entry saves this */ model->syms[i].cumfreq >>= 1; if (model->syms[i].cumfreq <= model->syms[i+1].cumfreq) { - model->syms[i].cumfreq = model->syms[i+1].cumfreq + 1; + model->syms[i].cumfreq = model->syms[i+1].cumfreq + 1; } } } @@ -149,11 +149,11 @@ static void qtmd_update_model(struct qtmd_model *model) { * characteristics */ for (i = 0; i < model->entries - 1; i++) { for (j = i + 1; j < model->entries; j++) { - if (model->syms[i].cumfreq < model->syms[j].cumfreq) { - tmp = model->syms[i]; - model->syms[i] = model->syms[j]; - model->syms[j] = tmp; - } + if (model->syms[i].cumfreq < model->syms[j].cumfreq) { + tmp = model->syms[i]; + model->syms[i] = model->syms[j]; + model->syms[j] = tmp; + } } } @@ -166,7 +166,7 @@ static void qtmd_update_model(struct qtmd_model *model) { /* Initialises a model to decode symbols from [start] to [start]+[len]-1 */ static void qtmd_init_model(struct qtmd_model *model, - struct qtmd_modelsym *syms, int start, int len) + struct qtmd_modelsym *syms, int start, int len) { int i; @@ -184,9 +184,9 @@ static void qtmd_init_model(struct qtmd_model *model, /*-------- main Quantum code --------*/ struct qtmd_stream *qtmd_init(struct mspack_system *system, - struct mspack_file *input, - struct mspack_file *output, - int window_bits, int input_buffer_size) + struct mspack_file *input, + struct mspack_file *output, + int window_bits, int input_buffer_size) { unsigned int window_size = 1 << window_bits; struct qtmd_stream *qtm; @@ -197,6 +197,7 @@ struct qtmd_stream *qtmd_init(struct mspack_system *system, /* Quantum supports window sizes of 2^10 (1Kb) through 2^21 (2Mb) */ if (window_bits < 10 || window_bits > 21) return NULL; + /* round up input buffer size to multiple of two */ input_buffer_size = (input_buffer_size + 1) & -2; if (input_buffer_size < 2) return NULL; @@ -307,113 +308,113 @@ int qtmd_decompress(struct qtmd_stream *qtm, off_t out_bytes) { while (window_posn < frame_end) { GET_SYMBOL(qtm->model7, selector); if (selector < 4) { - /* literal byte */ - struct qtmd_model *mdl = (selector == 0) ? &qtm->model0 : - ((selector == 1) ? &qtm->model1 : - ((selector == 2) ? &qtm->model2 : + /* literal byte */ + struct qtmd_model *mdl = (selector == 0) ? &qtm->model0 : + ((selector == 1) ? &qtm->model1 : + ((selector == 2) ? &qtm->model2 : &qtm->model3)); - GET_SYMBOL((*mdl), sym); - window[window_posn++] = sym; - frame_todo--; + GET_SYMBOL((*mdl), sym); + window[window_posn++] = sym; + frame_todo--; } else { - /* match repeated string */ - switch (selector) { - case 4: /* selector 4 = fixed length match (3 bytes) */ - GET_SYMBOL(qtm->model4, sym); - READ_MANY_BITS(extra, extra_bits[sym]); - match_offset = position_base[sym] + extra + 1; - match_length = 3; - break; - - case 5: /* selector 5 = fixed length match (4 bytes) */ - GET_SYMBOL(qtm->model5, sym); - READ_MANY_BITS(extra, extra_bits[sym]); - match_offset = position_base[sym] + extra + 1; - match_length = 4; - break; - - case 6: /* selector 6 = variable length match */ - GET_SYMBOL(qtm->model6len, sym); - READ_MANY_BITS(extra, length_extra[sym]); - match_length = length_base[sym] + extra + 5; - - GET_SYMBOL(qtm->model6, sym); - READ_MANY_BITS(extra, extra_bits[sym]); - match_offset = position_base[sym] + extra + 1; - break; - - default: - /* should be impossible, model7 can only return 0-6 */ - D(("got %d from selector", selector)) - return qtm->error = MSPACK_ERR_DECRUNCH; - } - - rundest = &window[window_posn]; - frame_todo -= match_length; - - /* does match destination wrap the window? This situation is possible - * where the window size is less than the 32k frame size, but matches - * must not go beyond a frame boundary */ - if ((window_posn + match_length) > qtm->window_size) { + /* match repeated string */ + switch (selector) { + case 4: /* selector 4 = fixed length match (3 bytes) */ + GET_SYMBOL(qtm->model4, sym); + READ_MANY_BITS(extra, extra_bits[sym]); + match_offset = position_base[sym] + extra + 1; + match_length = 3; + break; + + case 5: /* selector 5 = fixed length match (4 bytes) */ + GET_SYMBOL(qtm->model5, sym); + READ_MANY_BITS(extra, extra_bits[sym]); + match_offset = position_base[sym] + extra + 1; + match_length = 4; + break; + + case 6: /* selector 6 = variable length match */ + GET_SYMBOL(qtm->model6len, sym); + READ_MANY_BITS(extra, length_extra[sym]); + match_length = length_base[sym] + extra + 5; + + GET_SYMBOL(qtm->model6, sym); + READ_MANY_BITS(extra, extra_bits[sym]); + match_offset = position_base[sym] + extra + 1; + break; + + default: + /* should be impossible, model7 can only return 0-6 */ + D(("got %d from selector", selector)) + return qtm->error = MSPACK_ERR_DECRUNCH; + } + + rundest = &window[window_posn]; + frame_todo -= match_length; + + /* does match destination wrap the window? This situation is possible + * where the window size is less than the 32k frame size, but matches + * must not go beyond a frame boundary */ + if ((window_posn + match_length) > qtm->window_size) { /* copy first part of match, before window end */ - i = qtm->window_size - window_posn; - j = window_posn - match_offset; - while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; - - /* flush currently stored data */ - i = (&window[qtm->window_size] - qtm->o_ptr); - - /* this should not happen, but if it does then this code - * can't handle the situation (can't flush up to the end of - * the window, but can't break out either because we haven't - * finished writing the match). bail out in this case */ - if (i > out_bytes) { - D(("during window-wrap match; %d bytes to flush but only need %d", - i, (int) out_bytes)) - return qtm->error = MSPACK_ERR_DECRUNCH; - } - if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { - return qtm->error = MSPACK_ERR_WRITE; - } - out_bytes -= i; - qtm->o_ptr = &window[0]; - qtm->o_end = &window[0]; - - /* copy second part of match, after window wrap */ - rundest = &window[0]; - i = match_length - (qtm->window_size - window_posn); - while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; - window_posn = window_posn + match_length - qtm->window_size; + i = qtm->window_size - window_posn; + j = window_posn - match_offset; + while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; + + /* flush currently stored data */ + i = (&window[qtm->window_size] - qtm->o_ptr); + + /* this should not happen, but if it does then this code + * can't handle the situation (can't flush up to the end of + * the window, but can't break out either because we haven't + * finished writing the match). bail out in this case */ + if (i > out_bytes) { + D(("during window-wrap match; %d bytes to flush but only need %d", + i, (int) out_bytes)) + return qtm->error = MSPACK_ERR_DECRUNCH; + } + if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { + return qtm->error = MSPACK_ERR_WRITE; + } + out_bytes -= i; + qtm->o_ptr = &window[0]; + qtm->o_end = &window[0]; + + /* copy second part of match, after window wrap */ + rundest = &window[0]; + i = match_length - (qtm->window_size - window_posn); + while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; + window_posn = window_posn + match_length - qtm->window_size; break; /* because "window_posn < frame_end" has now failed */ - } - else { + } + else { /* normal match - output won't wrap window or frame end */ - i = match_length; - - /* does match _offset_ wrap the window? */ - if (match_offset > window_posn) { - /* j = length from match offset to end of window */ - j = match_offset - window_posn; - if (j > (int) qtm->window_size) { - D(("match offset beyond window boundaries")) - return qtm->error = MSPACK_ERR_DECRUNCH; - } - runsrc = &window[qtm->window_size - j]; - if (j < i) { - /* if match goes over the window edge, do two copy runs */ - i -= j; while (j-- > 0) *rundest++ = *runsrc++; - runsrc = window; - } - while (i-- > 0) *rundest++ = *runsrc++; - } - else { - runsrc = rundest - match_offset; - while (i-- > 0) *rundest++ = *runsrc++; - } - window_posn += match_length; - } + i = match_length; + + /* does match _offset_ wrap the window? */ + if (match_offset > window_posn) { + /* j = length from match offset to end of window */ + j = match_offset - window_posn; + if (j > (int) qtm->window_size) { + D(("match offset beyond window boundaries")) + return qtm->error = MSPACK_ERR_DECRUNCH; + } + runsrc = &window[qtm->window_size - j]; + if (j < i) { + /* if match goes over the window edge, do two copy runs */ + i -= j; while (j-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + while (i-- > 0) *rundest++ = *runsrc++; + } + else { + runsrc = rundest - match_offset; + while (i-- > 0) *rundest++ = *runsrc++; + } + window_posn += match_length; + } } /* if (window_posn+match_length > frame_end) */ } /* while (window_posn < frame_end) */ @@ -448,7 +449,7 @@ int qtmd_decompress(struct qtmd_stream *qtm, off_t out_bytes) { /* break out if we have more than enough to finish this request */ if (i >= out_bytes) break; if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { - return qtm->error = MSPACK_ERR_WRITE; + return qtm->error = MSPACK_ERR_WRITE; } out_bytes -= i; qtm->o_ptr = &window[0]; diff --git a/rbutil/rbutilqt/mspack/readbits.h b/rbutil/rbutilqt/mspack/readbits.h index 457cbdd7d4..9b237a3693 100644 --- a/rbutil/rbutilqt/mspack/readbits.h +++ b/rbutil/rbutilqt/mspack/readbits.h @@ -100,48 +100,48 @@ #endif #define BITBUF_WIDTH (sizeof(bit_buffer) * CHAR_BIT) -#define INIT_BITS do { \ - BITS_VAR->i_ptr = &BITS_VAR->inbuf[0]; \ - BITS_VAR->i_end = &BITS_VAR->inbuf[0]; \ - BITS_VAR->bit_buffer = 0; \ - BITS_VAR->bits_left = 0; \ - BITS_VAR->input_end = 0; \ +#define INIT_BITS do { \ + BITS_VAR->i_ptr = &BITS_VAR->inbuf[0]; \ + BITS_VAR->i_end = &BITS_VAR->inbuf[0]; \ + BITS_VAR->bit_buffer = 0; \ + BITS_VAR->bits_left = 0; \ + BITS_VAR->input_end = 0; \ } while (0) -#define STORE_BITS do { \ - BITS_VAR->i_ptr = i_ptr; \ - BITS_VAR->i_end = i_end; \ - BITS_VAR->bit_buffer = bit_buffer; \ - BITS_VAR->bits_left = bits_left; \ +#define STORE_BITS do { \ + BITS_VAR->i_ptr = i_ptr; \ + BITS_VAR->i_end = i_end; \ + BITS_VAR->bit_buffer = bit_buffer; \ + BITS_VAR->bits_left = bits_left; \ } while (0) -#define RESTORE_BITS do { \ - i_ptr = BITS_VAR->i_ptr; \ - i_end = BITS_VAR->i_end; \ - bit_buffer = BITS_VAR->bit_buffer; \ - bits_left = BITS_VAR->bits_left; \ +#define RESTORE_BITS do { \ + i_ptr = BITS_VAR->i_ptr; \ + i_end = BITS_VAR->i_end; \ + bit_buffer = BITS_VAR->bit_buffer; \ + bits_left = BITS_VAR->bits_left; \ } while (0) -#define ENSURE_BITS(nbits) do { \ - while (bits_left < (nbits)) READ_BYTES; \ +#define ENSURE_BITS(nbits) do { \ + while (bits_left < (nbits)) READ_BYTES; \ } while (0) -#define READ_BITS(val, nbits) do { \ - ENSURE_BITS(nbits); \ - (val) = PEEK_BITS(nbits); \ - REMOVE_BITS(nbits); \ +#define READ_BITS(val, nbits) do { \ + ENSURE_BITS(nbits); \ + (val) = PEEK_BITS(nbits); \ + REMOVE_BITS(nbits); \ } while (0) -#define READ_MANY_BITS(val, bits) do { \ - unsigned char needed = (bits), bitrun; \ - (val) = 0; \ - while (needed > 0) { \ - if (bits_left <= (BITBUF_WIDTH - 16)) READ_BYTES; \ - bitrun = (bits_left < needed) ? bits_left : needed; \ - (val) = ((val) << bitrun) | PEEK_BITS(bitrun); \ - REMOVE_BITS(bitrun); \ - needed -= bitrun; \ - } \ +#define READ_MANY_BITS(val, bits) do { \ + unsigned char needed = (bits), bitrun; \ + (val) = 0; \ + while (needed > 0) { \ + if (bits_left <= (BITBUF_WIDTH - 16)) READ_BYTES; \ + bitrun = (bits_left < needed) ? bits_left : needed; \ + (val) = ((val) << bitrun) | PEEK_BITS(bitrun); \ + REMOVE_BITS(bitrun); \ + needed -= bitrun; \ + } \ } while (0) #ifdef BITS_ORDER_MSB @@ -163,21 +163,21 @@ static const unsigned short lsb_bit_mask[17] = { 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; # define PEEK_BITS_T(nbits) (bit_buffer & lsb_bit_mask[(nbits)]) -# define READ_BITS_T(val, nbits) do { \ - ENSURE_BITS(nbits); \ - (val) = PEEK_BITS_T(nbits); \ - REMOVE_BITS(nbits); \ +# define READ_BITS_T(val, nbits) do { \ + ENSURE_BITS(nbits); \ + (val) = PEEK_BITS_T(nbits); \ + REMOVE_BITS(nbits); \ } while (0) #endif #ifndef BITS_NO_READ_INPUT -# define READ_IF_NEEDED do { \ - if (i_ptr >= i_end) { \ - if (read_input(BITS_VAR)) \ - return BITS_VAR->error; \ - i_ptr = BITS_VAR->i_ptr; \ - i_end = BITS_VAR->i_end; \ - } \ +# define READ_IF_NEEDED do { \ + if (i_ptr >= i_end) { \ + if (read_input(BITS_VAR)) \ + return BITS_VAR->error; \ + i_ptr = BITS_VAR->i_ptr; \ + i_end = BITS_VAR->i_end; \ + } \ } while (0) static int read_input(BITS_TYPE *p) { @@ -187,15 +187,15 @@ static int read_input(BITS_TYPE *p) { /* we might overrun the input stream by asking for bits we don't use, * so fake 2 more bytes at the end of input */ if (read == 0) { - if (p->input_end) { - D(("out of input bytes")) - return p->error = MSPACK_ERR_READ; - } - else { - read = 2; - p->inbuf[0] = p->inbuf[1] = 0; - p->input_end = 1; - } + if (p->input_end) { + D(("out of input bytes")) + return p->error = MSPACK_ERR_READ; + } + else { + read = 2; + p->inbuf[0] = p->inbuf[1] = 0; + p->input_end = 1; + } } /* update i_ptr and i_end */ diff --git a/rbutil/rbutilqt/mspack/readhuff.h b/rbutil/rbutilqt/mspack/readhuff.h index bb15c0a123..4d94225789 100644 --- a/rbutil/rbutilqt/mspack/readhuff.h +++ b/rbutil/rbutilqt/mspack/readhuff.h @@ -1,5 +1,5 @@ /* This file is part of libmspack. - * (C) 2003-2010 Stuart Caie. + * (C) 2003-2014 Stuart Caie. * * libmspack is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License (LGPL) version 2.1 @@ -10,8 +10,7 @@ #ifndef MSPACK_READHUFF_H #define MSPACK_READHUFF_H 1 -/* This implements a fast Huffman tree decoding system. - */ +/* This implements a fast Huffman tree decoding system. */ #if !(defined(BITS_ORDER_MSB) || defined(BITS_ORDER_LSB)) # error "readhuff.h is used in conjunction with readbits.h, include that first" @@ -32,32 +31,32 @@ /* Decodes the next huffman symbol from the input bitstream into var. * Do not use this macro on a table unless build_decode_table() succeeded. */ -#define READ_HUFFSYM(tbl, var) do { \ - ENSURE_BITS(HUFF_MAXBITS); \ - sym = HUFF_TABLE(tbl, PEEK_BITS(TABLEBITS(tbl))); \ - if (sym >= MAXSYMBOLS(tbl)) HUFF_TRAVERSE(tbl); \ - (var) = sym; \ - i = HUFF_LEN(tbl, sym); \ - REMOVE_BITS(i); \ +#define READ_HUFFSYM(tbl, var) do { \ + ENSURE_BITS(HUFF_MAXBITS); \ + sym = HUFF_TABLE(tbl, PEEK_BITS(TABLEBITS(tbl))); \ + if (sym >= MAXSYMBOLS(tbl)) HUFF_TRAVERSE(tbl); \ + (var) = sym; \ + i = HUFF_LEN(tbl, sym); \ + REMOVE_BITS(i); \ } while (0) #ifdef BITS_ORDER_LSB -# define HUFF_TRAVERSE(tbl) do { \ - i = TABLEBITS(tbl) - 1; \ - do { \ - if (i++ > HUFF_MAXBITS) HUFF_ERROR; \ - sym = HUFF_TABLE(tbl, \ - (sym << 1) | ((bit_buffer >> i) & 1)); \ - } while (sym >= MAXSYMBOLS(tbl)); \ +# define HUFF_TRAVERSE(tbl) do { \ + i = TABLEBITS(tbl) - 1; \ + do { \ + if (i++ > HUFF_MAXBITS) HUFF_ERROR; \ + sym = HUFF_TABLE(tbl, \ + (sym << 1) | ((bit_buffer >> i) & 1)); \ + } while (sym >= MAXSYMBOLS(tbl)); \ } while (0) #else -#define HUFF_TRAVERSE(tbl) do { \ - i = 1 << (BITBUF_WIDTH - TABLEBITS(tbl)); \ - do { \ - if ((i >>= 1) == 0) HUFF_ERROR; \ - sym = HUFF_TABLE(tbl, \ - (sym << 1) | ((bit_buffer & i) ? 1 : 0)); \ - } while (sym >= MAXSYMBOLS(tbl)); \ +#define HUFF_TRAVERSE(tbl) do { \ + i = 1 << (BITBUF_WIDTH - TABLEBITS(tbl)); \ + do { \ + if ((i >>= 1) == 0) HUFF_ERROR; \ + sym = HUFF_TABLE(tbl, \ + (sym << 1) | ((bit_buffer & i) ? 1 : 0)); \ + } while (sym >= MAXSYMBOLS(tbl)); \ } while (0) #endif @@ -77,7 +76,7 @@ * Returns 0 for OK or 1 for error */ static int make_decode_table(unsigned int nsyms, unsigned int nbits, - unsigned char *length, unsigned short *table) + unsigned char *length, unsigned short *table) { register unsigned short sym, next_symbol; register unsigned int leaf, fill; @@ -91,27 +90,27 @@ static int make_decode_table(unsigned int nsyms, unsigned int nbits, /* fill entries for codes short enough for a direct mapping */ for (bit_num = 1; bit_num <= nbits; bit_num++) { - for (sym = 0; sym < nsyms; sym++) { - if (length[sym] != bit_num) continue; + for (sym = 0; sym < nsyms; sym++) { + if (length[sym] != bit_num) continue; #ifdef BITS_ORDER_MSB - leaf = pos; + leaf = pos; #else - /* reverse the significant bits */ - fill = length[sym]; reverse = pos >> (nbits - fill); leaf = 0; - do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); + /* reverse the significant bits */ + fill = length[sym]; reverse = pos >> (nbits - fill); leaf = 0; + do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); #endif - if((pos += bit_mask) > table_mask) return 1; /* table overrun */ + if((pos += bit_mask) > table_mask) return 1; /* table overrun */ - /* fill all possible lookups of this symbol with the symbol itself */ + /* fill all possible lookups of this symbol with the symbol itself */ #ifdef BITS_ORDER_MSB - for (fill = bit_mask; fill-- > 0;) table[leaf++] = sym; + for (fill = bit_mask; fill-- > 0;) table[leaf++] = sym; #else - fill = bit_mask; next_symbol = 1 << bit_num; - do { table[leaf] = sym; leaf += next_symbol; } while (--fill); + fill = bit_mask; next_symbol = 1 << bit_num; + do { table[leaf] = sym; leaf += next_symbol; } while (--fill); #endif - } - bit_mask >>= 1; + } + bit_mask >>= 1; } /* exit with success if table is now complete */ @@ -120,11 +119,11 @@ static int make_decode_table(unsigned int nsyms, unsigned int nbits, /* mark all remaining table entries as unused */ for (sym = pos; sym < table_mask; sym++) { #ifdef BITS_ORDER_MSB - table[sym] = 0xFFFF; + table[sym] = 0xFFFF; #else - reverse = sym; leaf = 0; fill = nbits; - do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill); - table[leaf] = 0xFFFF; + reverse = sym; leaf = 0; fill = nbits; + do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill); + table[leaf] = 0xFFFF; #endif } @@ -138,33 +137,33 @@ static int make_decode_table(unsigned int nsyms, unsigned int nbits, bit_mask = 1 << 15; for (bit_num = nbits+1; bit_num <= HUFF_MAXBITS; bit_num++) { - for (sym = 0; sym < nsyms; sym++) { - if (length[sym] != bit_num) continue; + for (sym = 0; sym < nsyms; sym++) { + if (length[sym] != bit_num) continue; + if (pos >= table_mask) return 1; /* table overflow */ #ifdef BITS_ORDER_MSB - leaf = pos >> 16; + leaf = pos >> 16; #else - /* leaf = the first nbits of the code, reversed */ - reverse = pos >> 16; leaf = 0; fill = nbits; - do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); + /* leaf = the first nbits of the code, reversed */ + reverse = pos >> 16; leaf = 0; fill = nbits; + do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); #endif - for (fill = 0; fill < (bit_num - nbits); fill++) { - /* if this path hasn't been taken yet, 'allocate' two entries */ - if (table[leaf] == 0xFFFF) { - table[(next_symbol << 1) ] = 0xFFFF; - table[(next_symbol << 1) + 1 ] = 0xFFFF; - table[leaf] = next_symbol++; - } - - /* follow the path and select either left or right for next bit */ - leaf = table[leaf] << 1; - if ((pos >> (15-fill)) & 1) leaf++; - } - table[leaf] = sym; - - if ((pos += bit_mask) > table_mask) return 1; /* table overflow */ - } - bit_mask >>= 1; + for (fill = 0; fill < (bit_num - nbits); fill++) { + /* if this path hasn't been taken yet, 'allocate' two entries */ + if (table[leaf] == 0xFFFF) { + table[(next_symbol << 1) ] = 0xFFFF; + table[(next_symbol << 1) + 1 ] = 0xFFFF; + table[leaf] = next_symbol++; + } + + /* follow the path and select either left or right for next bit */ + leaf = table[leaf] << 1; + if ((pos >> (15-fill)) & 1) leaf++; + } + table[leaf] = sym; + pos += bit_mask; + } + bit_mask >>= 1; } /* full table? */ diff --git a/rbutil/rbutilqt/mspack/system-mspack.c b/rbutil/rbutilqt/mspack/system-mspack.c index 13946576fc..9d4886a8db 100644 --- a/rbutil/rbutilqt/mspack/system-mspack.c +++ b/rbutil/rbutilqt/mspack/system-mspack.c @@ -8,7 +8,7 @@ */ #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif #include "system-mspack.h" diff --git a/rbutil/rbutilqt/mspack/system-mspack.h b/rbutil/rbutilqt/mspack/system-mspack.h index 7a033cb04a..a0e6cf3ca8 100644 --- a/rbutil/rbutilqt/mspack/system-mspack.h +++ b/rbutil/rbutilqt/mspack/system-mspack.h @@ -16,7 +16,7 @@ extern "C" { /* ensure config.h is read before mspack.h */ #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif #include "mspack.h" @@ -61,7 +61,7 @@ extern "C" { (defined(FILESIZEBITS) && FILESIZEBITS >= 64) || \ (defined(SIZEOF_OFF_T) && SIZEOF_OFF_T >= 8) || \ defined(_LARGEFILE_SOURCE) || defined(_LARGEFILE64_SOURCE)) -# define LARGEFILE_SUPPORT +# define LARGEFILE_SUPPORT 1 # define LD "lld" # define LU "llu" #else diff --git a/rbutil/rbutilqt/mspack/szddd.c b/rbutil/rbutilqt/mspack/szddd.c index af77f15565..1d6d05f844 100644 --- a/rbutil/rbutilqt/mspack/szddd.c +++ b/rbutil/rbutilqt/mspack/szddd.c @@ -66,8 +66,8 @@ void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *base) { struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base; if (self) { - struct mspack_system *sys = self->system; - sys->free(self); + struct mspack_system *sys = self->system; + sys->free(self); } } @@ -77,7 +77,7 @@ void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *base) * opens an SZDD file without decompressing, reads header */ static struct msszddd_header *szddd_open(struct msszdd_decompressor *base, - const char *filename) + const char *filename) { struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base; struct msszddd_header *hdr; @@ -90,18 +90,18 @@ static struct msszddd_header *szddd_open(struct msszdd_decompressor *base, fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ); hdr = (struct msszddd_header *) sys->alloc(sys, sizeof(struct msszddd_header_p)); if (fh && hdr) { - ((struct msszddd_header_p *) hdr)->fh = fh; - self->error = szddd_read_headers(sys, fh, hdr); + ((struct msszddd_header_p *) hdr)->fh = fh; + self->error = szddd_read_headers(sys, fh, hdr); } else { - if (!fh) self->error = MSPACK_ERR_OPEN; - if (!hdr) self->error = MSPACK_ERR_NOMEMORY; + if (!fh) self->error = MSPACK_ERR_OPEN; + if (!hdr) self->error = MSPACK_ERR_NOMEMORY; } if (self->error) { - if (fh) sys->close(fh); - if (hdr) sys->free(hdr); - hdr = NULL; + if (fh) sys->close(fh); + sys->free(hdr); + hdr = NULL; } return hdr; @@ -113,7 +113,7 @@ static struct msszddd_header *szddd_open(struct msszdd_decompressor *base, * closes an SZDD file */ static void szddd_close(struct msszdd_decompressor *base, - struct msszddd_header *hdr) + struct msszddd_header *hdr) { struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base; struct msszddd_header_p *hdr_p = (struct msszddd_header_p *) hdr; @@ -142,33 +142,33 @@ static unsigned char szdd_signature_qbasic[8] = { }; static int szddd_read_headers(struct mspack_system *sys, - struct mspack_file *fh, - struct msszddd_header *hdr) + struct mspack_file *fh, + struct msszddd_header *hdr) { unsigned char buf[8]; /* read and check signature */ if (sys->read(fh, buf, 8) != 8) return MSPACK_ERR_READ; - if ((mspack_memcmp(buf, szdd_signature_expand, 8) == 0)) { - /* common SZDD */ - hdr->format = MSSZDD_FMT_NORMAL; + if ((memcmp(buf, szdd_signature_expand, 8) == 0)) { + /* common SZDD */ + hdr->format = MSSZDD_FMT_NORMAL; - /* read the rest of the header */ - if (sys->read(fh, buf, 6) != 6) return MSPACK_ERR_READ; - if (buf[0] != 0x41) return MSPACK_ERR_DATAFORMAT; - hdr->missing_char = buf[1]; - hdr->length = EndGetI32(&buf[2]); + /* read the rest of the header */ + if (sys->read(fh, buf, 6) != 6) return MSPACK_ERR_READ; + if (buf[0] != 0x41) return MSPACK_ERR_DATAFORMAT; + hdr->missing_char = buf[1]; + hdr->length = EndGetI32(&buf[2]); } - else if ((mspack_memcmp(buf, szdd_signature_qbasic, 8) == 0)) { - /* special QBasic SZDD */ - hdr->format = MSSZDD_FMT_QBASIC; - if (sys->read(fh, buf, 4) != 4) return MSPACK_ERR_READ; - hdr->missing_char = '\0'; - hdr->length = EndGetI32(buf); + else if ((memcmp(buf, szdd_signature_qbasic, 8) == 0)) { + /* special QBasic SZDD */ + hdr->format = MSSZDD_FMT_QBASIC; + if (sys->read(fh, buf, 4) != 4) return MSPACK_ERR_READ; + hdr->missing_char = '\0'; + hdr->length = EndGetI32(buf); } else { - return MSPACK_ERR_SIGNATURE; + return MSPACK_ERR_SIGNATURE; } return MSPACK_ERR_OK; } @@ -179,7 +179,7 @@ static int szddd_read_headers(struct mspack_system *sys, * decompresses an SZDD file */ static int szddd_extract(struct msszdd_decompressor *base, - struct msszddd_header *hdr, const char *filename) + struct msszddd_header *hdr, const char *filename) { struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base; struct mspack_file *fh, *outfh; @@ -195,19 +195,19 @@ static int szddd_extract(struct msszdd_decompressor *base, /* seek to the compressed data */ data_offset = (hdr->format == MSSZDD_FMT_NORMAL) ? 14 : 12; if (sys->seek(fh, data_offset, MSPACK_SYS_SEEK_START)) { - return self->error = MSPACK_ERR_SEEK; + return self->error = MSPACK_ERR_SEEK; } /* open file for output */ if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { - return self->error = MSPACK_ERR_OPEN; + return self->error = MSPACK_ERR_OPEN; } /* decompress the data */ self->error = lzss_decompress(sys, fh, outfh, SZDD_INPUT_SIZE, - hdr->format == MSSZDD_FMT_NORMAL - ? LZSS_MODE_EXPAND - : LZSS_MODE_QBASIC); + hdr->format == MSSZDD_FMT_NORMAL + ? LZSS_MODE_EXPAND + : LZSS_MODE_QBASIC); /* close output file */ sys->close(outfh); @@ -221,7 +221,7 @@ static int szddd_extract(struct msszdd_decompressor *base, * unpacks directly from input to output */ static int szddd_decompress(struct msszdd_decompressor *base, - const char *input, const char *output) + const char *input, const char *output) { struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base; struct msszddd_header *hdr; -- cgit