From c876d3bbefe0dc00c27ca0c12d29da5874946962 Mon Sep 17 00:00:00 2001 From: Dominik Riebeling Date: Wed, 15 Dec 2021 21:04:28 +0100 Subject: rbutil: Merge rbutil with utils folder. rbutil uses several components from the utils folder, and can be considered part of utils too. Having it in a separate folder is an arbitrary split that doesn't help anymore these days, so merge them. This also allows other utils to easily use libtools.make without the need to navigate to a different folder. Change-Id: I3fc2f4de19e3e776553efb5dea5f779dfec0dc21 --- utils/rbutilqt/mspack/cabd.c | 1508 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1508 insertions(+) create mode 100644 utils/rbutilqt/mspack/cabd.c (limited to 'utils/rbutilqt/mspack/cabd.c') diff --git a/utils/rbutilqt/mspack/cabd.c b/utils/rbutilqt/mspack/cabd.c new file mode 100644 index 0000000000..ae66769b24 --- /dev/null +++ b/utils/rbutilqt/mspack/cabd.c @@ -0,0 +1,1508 @@ +/* This file is part of libmspack. + * (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 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* Cabinet (.CAB) files are a form of file archive. Each cabinet contains + * "folders", which are compressed spans of data. Each cabinet has + * "files", whose metadata is in the cabinet header, but whose actual data + * is stored compressed in one of the "folders". Cabinets can span more + * than one physical file on disk, in which case they are a "cabinet set", + * and usually the last folder of each cabinet extends into the next + * cabinet. + * + * For a complete description of the format, see the MSDN site: + * http://msdn.microsoft.com/en-us/library/bb267310.aspx + */ + +/* CAB decompression implementation */ + +#include "system-mspack.h" +#include "cab.h" +#include "mszip.h" +#include "lzx.h" +#include "qtm.h" + +/* Notes on compliance with cabinet specification: + * + * One of the main changes between cabextract 0.6 and libmspack's cab + * decompressor is the move from block-oriented decompression to + * stream-oriented decompression. + * + * cabextract would read one data block from disk, decompress it with the + * appropriate method, then write the decompressed data. The CAB + * specification is specifically designed to work like this, as it ensures + * compression matches do not span the maximum decompressed block size + * limit of 32kb. + * + * However, the compression algorithms used are stream oriented, with + * specific hacks added to them to enforce the "individual 32kb blocks" + * rule in CABs. In other file formats, they do not have this limitation. + * + * In order to make more generalised decompressors, libmspack's CAB + * decompressor has moved from being block-oriented to more stream + * oriented. This also makes decompression slightly faster. + * + * However, this leads to incompliance with the CAB specification. The + * CAB controller can no longer ensure each block of input given to the + * decompressors is matched with their output. The "decompressed size" of + * each individual block is thrown away. + * + * Each CAB block is supposed to be seen as individually compressed. This + * means each consecutive data block can have completely different + * "uncompressed" sizes, ranging from 1 to 32768 bytes. However, in + * reality, all data blocks in a folder decompress to exactly 32768 bytes, + * excepting the final block. + * + * Given this situation, the decompression algorithms are designed to + * realign their input bitstreams on 32768 output-byte boundaries, and + * various other special cases have been made. libmspack will not + * correctly decompress LZX or Quantum compressed folders where the blocks + * do not follow this "32768 bytes until last block" pattern. It could be + * implemented if needed, but hopefully this is not necessary -- it has + * not been seen in over 3Gb of CAB archives. + */ + +/* prototypes */ +static struct mscabd_cabinet * cabd_open( + struct mscab_decompressor *base, const char *filename); +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 salvage, int quiet); +static char *cabd_read_string( + struct mspack_system *sys, struct mspack_file *fh, int *error); + +static struct mscabd_cabinet *cabd_search( + struct mscab_decompressor *base, const char *filename); +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); + +static int cabd_prepend( + struct mscab_decompressor *base, struct mscabd_cabinet *cab, + struct mscabd_cabinet *prevcab); +static int cabd_append( + struct mscab_decompressor *base, struct mscabd_cabinet *cab, + struct mscabd_cabinet *nextcab); +static int cabd_merge( + struct mscab_decompressor *base, struct mscabd_cabinet *lcab, + struct mscabd_cabinet *rcab); +static int cabd_can_merge_folders( + struct mspack_system *sys, struct mscabd_folder_p *lfol, + struct mscabd_folder_p *rfol); + +static int cabd_extract( + struct mscab_decompressor *base, struct mscabd_file *file, + const char *filename); +static int cabd_init_decomp( + struct mscab_decompressor_p *self, unsigned int ct); +static void cabd_free_decomp( + struct mscab_decompressor_p *self); +static int cabd_sys_read( + struct mspack_file *file, void *buffer, int bytes); +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_blocksize); +static unsigned int cabd_checksum( + unsigned char *data, unsigned int bytes, unsigned int cksum); +static struct noned_state *noned_init( + struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out, + int bufsize); + +static int noned_decompress( + struct noned_state *s, off_t bytes); +static void noned_free( + struct noned_state *state); + +static int cabd_param( + struct mscab_decompressor *base, int param, int value); + +static int cabd_error( + struct mscab_decompressor *base); + + +/*************************************** + * MSPACK_CREATE_CAB_DECOMPRESSOR + *************************************** + * constructor + */ +struct mscab_decompressor * + mspack_create_cab_decompressor(struct mspack_system *sys) +{ + struct mscab_decompressor_p *self = NULL; + + if (!sys) sys = mspack_default_system; + if (!mspack_valid_system(sys)) return NULL; + + if ((self = (struct mscab_decompressor_p *) sys->alloc(sys, sizeof(struct mscab_decompressor_p)))) { + self->base.open = &cabd_open; + self->base.close = &cabd_close; + self->base.search = &cabd_search; + self->base.extract = &cabd_extract; + self->base.prepend = &cabd_prepend; + self->base.append = &cabd_append; + self->base.set_param = &cabd_param; + self->base.last_error = &cabd_error; + self->system = sys; + self->d = NULL; + self->error = MSPACK_ERR_OK; + + self->searchbuf_size = 32768; + self->fix_mszip = 0; + self->buf_size = 4096; + self->salvage = 0; + } + return (struct mscab_decompressor *) self; +} + +/*************************************** + * MSPACK_DESTROY_CAB_DECOMPRESSOR + *************************************** + * destructor + */ +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; + if (self->d) { + if (self->d->infh) sys->close(self->d->infh); + cabd_free_decomp(self); + sys->free(self->d); + } + sys->free(self); + } +} + + +/*************************************** + * CABD_OPEN + *************************************** + * 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) +{ + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + struct mscabd_cabinet_p *cab = NULL; + struct mspack_system *sys; + struct mspack_file *fh; + int error; + + if (!base) return NULL; + sys = self->system; + + 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, self->salvage, 0); + if (error) { + cabd_close(base, (struct mscabd_cabinet *) cab); + cab = NULL; + } + self->error = error; + } + else { + self->error = MSPACK_ERR_NOMEMORY; + } + sys->close(fh); + } + else { + self->error = MSPACK_ERR_OPEN; + } + return (struct mscabd_cabinet *) cab; +} + +/*************************************** + * CABD_CLOSE + *************************************** + * frees all memory associated with a given mscabd_cabinet. + */ +static void cabd_close(struct mscab_decompressor *base, + struct mscabd_cabinet *origcab) +{ + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + struct mscabd_folder_data *dat, *ndat; + struct mscabd_cabinet *cab, *ncab; + struct mscabd_folder *fol, *nfol; + struct mscabd_file *fi, *nfi; + struct mspack_system *sys; + + if (!base) return; + sys = self->system; + + self->error = MSPACK_ERR_OK; + + while (origcab) { + /* free files */ + for (fi = origcab->files; fi; fi = nfi) { + nfi = fi->next; + sys->free(fi->filename); + sys->free(fi); + } + + /* free folders */ + for (fol = origcab->folders; fol; fol = nfol) { + nfol = fol->next; + + /* 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; + } + + /* free folder data segments */ + for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) { + ndat = dat->next; + sys->free(dat); + } + sys->free(fol); + } + + /* free predecessor cabinets (and the original cabinet's strings) */ + for (cab = origcab; cab; cab = ncab) { + ncab = cab->prevcab; + sys->free(cab->prevname); + sys->free(cab->nextname); + sys->free(cab->previnfo); + sys->free(cab->nextinfo); + if (cab != origcab) sys->free(cab); + } + + /* free successor cabinets */ + for (cab = origcab->nextcab; cab; cab = ncab) { + ncab = cab->nextcab; + sys->free(cab->prevname); + sys->free(cab->nextname); + sys->free(cab->previnfo); + sys->free(cab->nextinfo); + sys->free(cab); + } + + /* free actual cabinet structure */ + cab = origcab->next; + sys->free(origcab); + + /* repeat full procedure again with the cab->next pointer (if set) */ + origcab = cab; + } +} + +/*************************************** + * CABD_READ_HEADERS + *************************************** + * reads the cabinet file header, folder list and file list. + * fills out a pre-existing mscabd_cabinet structure, allocates memory + * 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 salvage, int quiet) +{ + 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]; + + /* initialise pointers */ + cab->base.next = NULL; + cab->base.files = NULL; + cab->base.folders = NULL; + cab->base.prevcab = cab->base.nextcab = NULL; + cab->base.prevname = cab->base.nextname = NULL; + cab->base.previnfo = cab->base.nextinfo = NULL; + + cab->base.base_offset = offset; + + /* seek to CFHEADER */ + if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { + return MSPACK_ERR_SEEK; + } + + /* read in the CFHEADER */ + if (sys->read(fh, &buf[0], cfhead_SIZEOF) != cfhead_SIZEOF) { + return MSPACK_ERR_READ; + } + + /* check for "MSCF" signature */ + if (EndGetI32(&buf[cfhead_Signature]) != 0x4643534D) { + return MSPACK_ERR_SIGNATURE; + } + + /* some basic header fields */ + cab->base.length = EndGetI32(&buf[cfhead_CabinetSize]); + cab->base.set_id = EndGetI16(&buf[cfhead_SetID]); + cab->base.set_index = EndGetI16(&buf[cfhead_CabinetIndex]); + + /* get the number of folders */ + num_folders = EndGetI16(&buf[cfhead_NumFolders]); + if (num_folders == 0) { + if (!quiet) sys->message(fh, "no folders in cabinet."); + return MSPACK_ERR_DATAFORMAT; + } + + /* get the number of files */ + num_files = EndGetI16(&buf[cfhead_NumFiles]); + if (num_files == 0) { + if (!quiet) sys->message(fh, "no files in cabinet."); + return MSPACK_ERR_DATAFORMAT; + } + + /* check cabinet version */ + if ((buf[cfhead_MajorVersion] != 1) && (buf[cfhead_MinorVersion] != 3)) { + if (!quiet) sys->message(fh, "WARNING; cabinet version is not 1.3"); + } + + /* 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; + } + cab->base.header_resv = EndGetI16(&buf[cfheadext_HeaderReserved]); + folder_resv = buf[cfheadext_FolderReserved]; + cab->block_resv = buf[cfheadext_DataReserved]; + + if (cab->base.header_resv > 60000) { + if (!quiet) sys->message(fh, "WARNING; reserved header > 60000."); + } + + /* 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; + } + } + } + else { + cab->base.header_resv = 0; + folder_resv = 0; + cab->block_resv = 0; + } + + /* 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, &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, &err); + if (err) return err; + cab->base.nextinfo = cabd_read_string(sys, fh, &err); + if (err) return err; + } + + /* read folders */ + for (i = 0; i < num_folders; i++) { + if (sys->read(fh, &buf[0], cffold_SIZEOF) != cffold_SIZEOF) { + return MSPACK_ERR_READ; + } + if (folder_resv) { + if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) { + return MSPACK_ERR_SEEK; + } + } + + if (!(fol = (struct mscabd_folder_p *) sys->alloc(sys, sizeof(struct mscabd_folder_p)))) { + return MSPACK_ERR_NOMEMORY; + } + fol->base.next = NULL; + fol->base.comp_type = EndGetI16(&buf[cffold_CompType]); + fol->base.num_blocks = EndGetI16(&buf[cffold_NumBlocks]); + fol->data.next = NULL; + fol->data.cab = (struct mscabd_cabinet_p *) cab; + fol->data.offset = offset + (off_t) + ( (unsigned int) EndGetI32(&buf[cffold_DataOffset]) ); + fol->merge_prev = NULL; + fol->merge_next = NULL; + + /* link folder into list of folders */ + if (!linkfol) cab->base.folders = (struct mscabd_folder *) fol; + else linkfol->base.next = (struct mscabd_folder *) fol; + linkfol = fol; + } + + /* read files */ + for (i = 0; i < num_files; i++) { + if (sys->read(fh, &buf[0], cffile_SIZEOF) != cffile_SIZEOF) { + return MSPACK_ERR_READ; + } + + if (!(file = (struct mscabd_file *) sys->alloc(sys, sizeof(struct mscabd_file)))) { + return MSPACK_ERR_NOMEMORY; + } + + file->next = NULL; + file->length = EndGetI32(&buf[cffile_UncompressedSize]); + file->attribs = EndGetI16(&buf[cffile_Attribs]); + file->offset = EndGetI32(&buf[cffile_FolderOffset]); + + /* set folder pointer */ + 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 ((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; + } + + if ((fidx == cffileCONTINUED_FROM_PREV) || + (fidx == cffileCONTINUED_PREV_AND_NEXT)) + { + /* 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; + } + } + + /* get time */ + x = EndGetI16(&buf[cffile_Time]); + file->time_h = x >> 11; + file->time_m = (x >> 5) & 0x3F; + file->time_s = (x << 1) & 0x3E; + + /* get date */ + x = EndGetI16(&buf[cffile_Date]); + file->date_d = x & 0x1F; + file->date_m = (x >> 5) & 0xF; + file->date_y = (x >> 9) + 1980; + + /* get filename */ + 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); + if (salvage) continue; + return err ? err : MSPACK_ERR_DATAFORMAT; + } + + /* link file entry into file list */ + if (!linkfile) cab->base.files = file; + else linkfile->next = file; + 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, int *error) +{ + off_t base = sys->tell(fh); + char buf[256], *str; + int len, i, ok; + + /* read up to 256 bytes */ + 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; + } + + len = i + 1; + + /* set the data stream to just after the string and return */ + if (sys->seek(fh, base + (off_t)len, MSPACK_SYS_SEEK_START)) { + *error = MSPACK_ERR_SEEK; + return NULL; + } + + if (!(str = (char *) sys->alloc(sys, len))) { + *error = MSPACK_ERR_NOMEMORY; + return NULL; + } + + sys->copy(&buf[0], str, len); + *error = MSPACK_ERR_OK; + return str; +} + +/*************************************** + * CABD_SEARCH, CABD_FIND + *************************************** + * cabd_search opens a file, finds its extent, allocates a search buffer, + * then reads through the whole file looking for possible cabinet headers. + * if it finds any, it tries to read them as real cabinets. returns a linked + * list of results + * + * cabd_find is the inner loop of cabd_search, to make it easier to + * 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) +{ + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + struct mscabd_cabinet_p *cab = NULL; + struct mspack_system *sys; + unsigned char *search_buf; + struct mspack_file *fh; + off_t filelen, firstlen = 0; + + if (!base) return NULL; + sys = self->system; + + /* allocate a search buffer */ + search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->searchbuf_size); + if (!search_buf) { + self->error = MSPACK_ERR_NOMEMORY; + return NULL; + } + + /* open file and get its full file length */ + 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); + } + + /* truncated / extraneous data warning: */ + if (firstlen && (firstlen != filelen) && + (!cab || (cab->base.base_offset == 0))) + { + if (firstlen < filelen) { + 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->close(fh); + } + else { + self->error = MSPACK_ERR_OPEN; + } + + /* free the search buffer */ + sys->free(search_buf); + + return (struct mscabd_cabinet *) cab; +} + +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 mscabd_cabinet_p *cab, *link = NULL; + off_t caboff, offset, length; + struct mspack_system *sys = self->system; + unsigned char *p, *pend, state = 0; + unsigned int cablen_u32 = 0, foffset_u32 = 0; + int false_cabs = 0; + +#if !LARGEFILE_SUPPORT + /* detect 32-bit off_t overflow */ + if (flen < 0) { + sys->message(fh, largefile_msg); + return MSPACK_ERR_OK; + } +#endif + + /* search through the full file length */ + for (offset = 0; offset < flen; offset += length) { + /* 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->searchbuf_size) { + length = self->searchbuf_size; + } + + /* fill the search buffer with data from disk */ + if (sys->read(fh, &buf[0], (int) length) != (int) length) { + return MSPACK_ERR_READ; + } + + /* FAQ avoidance strategy */ + if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) { + 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 */ + 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; + + /* verify that the next 3 bytes are 'S', 'C' and 'F' */ + case 1: state = (*p++ == 0x53) ? 2 : 0; break; + case 2: state = (*p++ == 0x43) ? 3 : 0; break; + case 3: state = (*p++ == 0x46) ? 4 : 0; break; + + /* we don't care about bytes 4-7 (see default: for action) */ + + /* bytes 8-11 are the overall length of the cabinet */ + case 8: cablen_u32 = *p++; state++; break; + case 9: cablen_u32 |= *p++ << 8; state++; break; + case 10: cablen_u32 |= *p++ << 16; state++; break; + case 11: cablen_u32 |= *p++ << 24; state++; break; + + /* we don't care about bytes 12-15 (see default: for action) */ + + /* bytes 16-19 are the offset within the cabinet of the filedata */ + case 16: foffset_u32 = *p++; state++; break; + 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. 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++; + } /* switch(state) */ + } /* for (... p < pend ...) */ + } /* for (... offset < length ...) */ + + if (false_cabs) { + D(("%d false cabinets found", false_cabs)) + } + + return MSPACK_ERR_OK; +} + +/*************************************** + * CABD_MERGE, CABD_PREPEND, CABD_APPEND + *************************************** + * joins cabinets together, also merges split folders between these two + * cabinets only. This includes freeing the duplicate folder and file(s) + * and allocating a further mscabd_folder_data structure to append to the + * merged folder's data parts list. + */ +static int cabd_prepend(struct mscab_decompressor *base, + 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) +{ + return cabd_merge(base, cab, nextcab); +} + +static int cabd_merge(struct mscab_decompressor *base, + struct mscabd_cabinet *lcab, + struct mscabd_cabinet *rcab) +{ + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + struct mscabd_folder_data *data, *ndata; + struct mscabd_folder_p *lfol, *rfol; + struct mscabd_file *fi, *rfi, *lfi; + struct mscabd_cabinet *cab; + struct mspack_system *sys; + + if (!self) return MSPACK_ERR_ARGS; + sys = self->system; + + /* basic args check */ + if (!lcab || !rcab || (lcab == rcab)) { + D(("lcab NULL, rcab NULL or lcab = rcab")) + return self->error = MSPACK_ERR_ARGS; + } + + /* check there's not already a cabinet attached */ + if (lcab->nextcab || rcab->prevcab) { + D(("cabs already joined")) + return self->error = MSPACK_ERR_ARGS; + } + + /* do not create circular cabinet chains */ + for (cab = lcab->prevcab; cab; cab = cab->prevcab) { + if (cab == rcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;} + } + for (cab = rcab->nextcab; cab; cab = cab->nextcab) { + if (cab == lcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;} + } + + /* warn about odd set IDs or indices */ + if (lcab->set_id != rcab->set_id) { + sys->message(NULL, "WARNING; merged cabinets with differing Set IDs."); + } + + if (lcab->set_index > rcab->set_index) { + sys->message(NULL, "WARNING; merged cabinets with odd order."); + } + + /* merging the last folder in lcab with the first folder in rcab */ + lfol = (struct mscabd_folder_p *) lcab->folders; + rfol = (struct mscabd_folder_p *) rcab->folders; + while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; + + /* do we need to merge folders? */ + if (!lfol->merge_next && !rfol->merge_prev) { + /* no, at least one of the folders is not for merging */ + + /* attach cabs */ + lcab->nextcab = rcab; + rcab->prevcab = lcab; + + /* attach folders */ + lfol->base.next = (struct mscabd_folder *) rfol; + + /* attach files */ + fi = lcab->files; + while (fi->next) fi = fi->next; + fi->next = rcab->files; + } + else { + /* folder merge required - do the files match? */ + if (! cabd_can_merge_folders(sys, lfol, rfol)) { + return self->error = MSPACK_ERR_DATAFORMAT; + } + + /* allocate a new folder data structure */ + if (!(data = (struct mscabd_folder_data *) sys->alloc(sys, sizeof(struct mscabd_folder_data)))) { + return self->error = MSPACK_ERR_NOMEMORY; + } + + /* attach cabs */ + lcab->nextcab = rcab; + rcab->prevcab = lcab; + + /* append rfol's data to lfol */ + ndata = &lfol->data; + while (ndata->next) ndata = ndata->next; + ndata->next = data; + *data = rfol->data; + rfol->data.next = NULL; + + /* lfol becomes rfol. + * NOTE: special case, don't merge if rfol is merge prev and next, + * rfol->merge_next is going to be deleted, so keep lfol's version + * instead */ + lfol->base.num_blocks += rfol->base.num_blocks - 1; + if ((rfol->merge_next == NULL) || + (rfol->merge_next->folder != (struct mscabd_folder *) rfol)) + { + lfol->merge_next = rfol->merge_next; + } + + /* attach the rfol's folder (except the merge folder) */ + while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; + lfol->base.next = rfol->base.next; + + /* free disused merge folder */ + sys->free(rfol); + + /* attach rfol's files */ + fi = lcab->files; + while (fi->next) fi = fi->next; + fi->next = rcab->files; + + /* delete all files from rfol's merge folder */ + lfi = NULL; + for (fi = lcab->files; fi ; fi = rfi) { + 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); + } + else lfi = fi; + } + } + + /* all done! fix files and folders pointers in all cabs so they all + * point to the same list */ + for (cab = lcab->prevcab; cab; cab = cab->prevcab) { + cab->files = lcab->files; + cab->folders = lcab->folders; + } + + for (cab = lcab->nextcab; cab; cab = cab->nextcab) { + cab->files = lcab->files; + cab->folders = lcab->folders; + } + + return self->error = MSPACK_ERR_OK; +} + +/* decides if two folders are OK to merge */ +static int cabd_can_merge_folders(struct mspack_system *sys, + struct mscabd_folder_p *lfol, + struct mscabd_folder_p *rfol) +{ + struct mscabd_file *lfi, *rfi, *l, *r; + int matching = 1; + + /* check that both folders use the same compression method/settings */ + if (lfol->base.comp_type != rfol->base.comp_type) { + D(("folder merge: compression type mismatch")) + 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; + } + + /* for all files in lfol (which is the last folder in whichever cab and + * only has files to merge), compare them to the files from rfol. They + * 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 (matching) return 1; + + /* if rfol does not begin with an identical copy of the files in lfol, make + * make a judgement call; if at least ONE file from lfol is in rfol, allow + * 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; + } + if (r) matching = 1; else sys->message(NULL, + "WARNING; merged file %s not listed in both cabinets", l->filename); + } + return matching; +} + + +/*************************************** + * CABD_EXTRACT + *************************************** + * extracts a file from a cabinet + */ +static int cabd_extract(struct mscab_decompressor *base, + struct mscabd_file *file, const char *filename) +{ + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) 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; + + sys = self->system; + fol = (struct mscabd_folder_p *) file->folder; + + /* 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)); + if (!self->d) return self->error = MSPACK_ERR_NOMEMORY; + self->d->folder = NULL; + self->d->data = NULL; + self->d->sys = *sys; + self->d->sys.read = &cabd_sys_read; + self->d->sys.write = &cabd_sys_write; + self->d->state = NULL; + self->d->infh = NULL; + self->d->incab = NULL; + } + + /* do we need to change folder or reset the current folder? */ + 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); + if (!self->d->infh) return self->error = MSPACK_ERR_OPEN; + } + /* seek to start of data blocks */ + if (sys->seek(self->d->infh, fol->data.offset, MSPACK_SYS_SEEK_START)) { + return self->error = MSPACK_ERR_SEEK; + } + + /* set up decompressor */ + if (cabd_init_decomp(self, (unsigned int) fol->base.comp_type)) { + return self->error; + } + + /* initialise new folder state */ + self->d->folder = fol; + 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 */ + self->read_error = MSPACK_ERR_OK; + } + + /* open file for output */ + if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { + return self->error = MSPACK_ERR_OPEN; + } + + self->error = MSPACK_ERR_OK; + + /* if file has more than 0 bytes */ + if (filelen) { + off_t bytes; + int error; + /* get to correct offset. + * - use NULL fh to say 'no writing' to cabd_sys_write() + * - if cabd_sys_read() has an error, it will set self->read_error + * and pass back MSPACK_ERR_READ + */ + 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; + } + + /* 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, filelen); + self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; + } + } + + /* close output file */ + sys->close(fh); + self->d->outfh = NULL; + + return self->error; +} + +/*************************************** + * CABD_INIT_DECOMP, CABD_FREE_DECOMP + *************************************** + * cabd_init_decomp initialises decompression state, according to which + * decompression method was used. relies on self->d->folder being the same + * as when initialised. + * + * cabd_free_decomp frees decompression state, according to which method + * was used. + */ +static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct) +{ + struct mspack_file *fh = (struct mspack_file *) 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->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->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->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->buf_size, (off_t)0,0); + break; + default: + return self->error = MSPACK_ERR_DATAFORMAT; + } + return self->error = (self->d->state) ? MSPACK_ERR_OK : MSPACK_ERR_NOMEMORY; +} + +static void cabd_free_decomp(struct mscab_decompressor_p *self) { + 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; + case cffoldCOMPTYPE_MSZIP: mszipd_free((struct mszipd_stream *) self->d->state); break; + case cffoldCOMPTYPE_QUANTUM: qtmd_free((struct qtmd_stream *) self->d->state); break; + case cffoldCOMPTYPE_LZX: lzxd_free((struct lzxd_stream *) self->d->state); break; + } + self->d->decompress = NULL; + self->d->state = NULL; +} + +/*************************************** + * CABD_SYS_READ, CABD_SYS_WRITE + *************************************** + * cabd_sys_read is the internal reader function which the decompressors + * use. will read data blocks (and merge split blocks) from the cabinet + * and serve the read bytes to the decompressors + * + * cabd_sys_write is the internal writer function which the decompressors + * use. it either writes data to disk (self->d->outfh) with the real + * sys->write() function, or does nothing with the data when + * self->d->outfh == NULL. advances self->d->offset + */ +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, ignore_blocksize; + + ignore_cksum = self->salvage || + (self->fix_mszip && + ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP)); + ignore_blocksize = self->salvage; + + todo = bytes; + while (todo > 0) { + avail = self->d->i_end - self->d->i_ptr; + + /* if out of input data, read a new block */ + if (avail) { + /* copy as many input bytes available as possible */ + if (avail > todo) avail = todo; + sys->copy(self->d->i_ptr, buf, (size_t) avail); + self->d->i_ptr += avail; + buf += avail; + todo -= avail; + } + else { + /* out of data, read a new block */ + + /* check if we're out of input blocks, advance block counter */ + if (self->d->block++ >= self->d->folder->base.num_blocks) { + 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, 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; + } + + /* is this the last block? */ + if (self->d->block >= self->d->folder->base.num_blocks) { + 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) */ + return bytes - todo; +} + +static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) { + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; + self->d->offset += bytes; + if (self->d->outfh) { + return self->system->write(self->d->outfh, buffer, bytes); + } + return bytes; +} + +/*************************************** + * CABD_SYS_READ_BLOCK + *************************************** + * reads a whole data block from a cab file. the block may span more than + * 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, + int ignore_blocksize) +{ + unsigned char hdr[cfdata_SIZEOF]; + unsigned int cksum; + int len, full_len; + + /* reset the input block pointer and end of block pointer */ + d->i_ptr = d->i_end = &d->input[0]; + + do { + /* read the block header */ + if (sys->read(d->infh, &hdr[0], cfdata_SIZEOF) != cfdata_SIZEOF) { + return MSPACK_ERR_READ; + } + + /* 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)) + { + return MSPACK_ERR_SEEK; + } + + /* blocks must not be over CAB_INPUTMAX in size */ + len = EndGetI16(&hdr[cfdata_CompressedSize]); + 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")) + if (!ignore_blocksize) return MSPACK_ERR_DATAFORMAT; + } + + /* read the block data */ + if (sys->read(d->infh, d->i_end, len) != len) { + return MSPACK_ERR_READ; + } + + /* perform checksum test on the block (if one is stored) */ + 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"); + } + } + + /* advance end of block pointer to include newly read data */ + d->i_end += len; + + /* uncompressed size == 0 means this block was part of a split block + * and it continues as the first block of the next cabinet in the set. + * otherwise, this is the last part of the block, and no more block + * reading needs to be done. + */ + /* EXIT POINT OF LOOP -- uncompressed size != 0 */ + if ((*out = EndGetI16(&hdr[cfdata_UncompressedSize]))) { + return MSPACK_ERR_OK; + } + + /* otherwise, advance to next cabinet */ + + /* close current file handle */ + sys->close(d->infh); + d->infh = NULL; + + /* advance to next member in the cabinet set */ + if (!(d->data = d->data->next)) { + 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))) + { + return MSPACK_ERR_OPEN; + } + + /* seek to start of data blocks */ + if (sys->seek(d->infh, d->data->offset, MSPACK_SYS_SEEK_START)) { + return MSPACK_ERR_SEEK; + } + } while (1); + + /* not reached */ + return MSPACK_ERR_OK; +} + +static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes, + unsigned int cksum) +{ + unsigned int len, ul = 0; + + for (len = bytes >> 2; len--; data += 4) { + cksum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24)); + } + + switch (bytes & 3) { + case 3: ul |= *data++ << 16; /*@fallthrough@*/ + case 2: ul |= *data++ << 8; /*@fallthrough@*/ + case 1: ul |= *data; + } + cksum ^= ul; + + return cksum; +} + +/*************************************** + * NONED_INIT, NONED_DECOMPRESS, NONED_FREE + *************************************** + * the "not compressed" method decompressor + */ +struct noned_state { + struct mspack_system *sys; + struct mspack_file *i; + struct mspack_file *o; + unsigned char *buf; + int bufsize; +}; + +static struct noned_state *noned_init(struct mspack_system *sys, + 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); + if (state && buf) { + state->sys = sys; + state->i = in; + state->o = out; + state->buf = buf; + state->bufsize = bufsize; + } + else { + sys->free(buf); + sys->free(state); + state = NULL; + } + return state; +} + +static int noned_decompress(struct noned_state *s, off_t bytes) { + int run; + while (bytes > 0) { + run = (bytes > s->bufsize) ? s->bufsize : (int) bytes; + if (s->sys->read(s->i, &s->buf[0], run) != run) return MSPACK_ERR_READ; + if (s->sys->write(s->o, &s->buf[0], run) != run) return MSPACK_ERR_WRITE; + bytes -= run; + } + return MSPACK_ERR_OK; +} + +static void noned_free(struct noned_state *state) { + struct mspack_system *sys; + if (state) { + sys = state->sys; + sys->free(state->buf); + sys->free(state); + } +} + + +/*************************************** + * CABD_PARAM + *************************************** + * allows a parameter to be set + */ +static int cabd_param(struct mscab_decompressor *base, int param, int value) { + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + if (!self) return MSPACK_ERR_ARGS; + + switch (param) { + case MSCABD_PARAM_SEARCHBUF: + if (value < 4) return MSPACK_ERR_ARGS; + self->searchbuf_size = value; + break; + case MSCABD_PARAM_FIXMSZIP: + self->fix_mszip = value; + break; + case MSCABD_PARAM_DECOMPBUF: + if (value < 4) return MSPACK_ERR_ARGS; + self->buf_size = value; + break; + case MSCABD_PARAM_SALVAGE: + self->salvage = value; + break; + default: + return MSPACK_ERR_ARGS; + } + return MSPACK_ERR_OK; +} + +/*************************************** + * CABD_ERROR + *************************************** + * returns the last error that occurred + */ +static int cabd_error(struct mscab_decompressor *base) { + struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; + return (self) ? self->error : MSPACK_ERR_ARGS; +} -- cgit v1.2.3