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
This commit is contained in:
Dominik Riebeling 2020-06-08 21:44:02 +02:00
parent b0f22620a2
commit 729b6e4f33
19 changed files with 2066 additions and 1527 deletions

View file

@ -1,6 +1,6 @@
This folder contains the mspack project for MS files compression/decompression. This folder contains the mspack project for MS files compression/decompression.
These files are distributed under the LGPL. 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

View file

@ -1,5 +1,5 @@
/* This file is part of libmspack. /* 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 * 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 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
@ -10,10 +10,6 @@
#ifndef MSPACK_CAB_H #ifndef MSPACK_CAB_H
#define MSPACK_CAB_H 1 #define MSPACK_CAB_H 1
#include "mszip.h"
#include "qtm.h"
#include "lzx.h"
/* generic CAB definitions */ /* generic CAB definitions */
/* structure offsets */ /* structure offsets */
@ -70,6 +66,22 @@
#define CAB_BLOCKMAX (32768) #define CAB_BLOCKMAX (32768)
#define CAB_INPUTMAX (CAB_BLOCKMAX+6144) #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 */ /* CAB compression definitions */
struct mscab_compressor_p { struct mscab_compressor_p {
@ -85,6 +97,7 @@ struct mscabd_decompress_state {
struct mscabd_folder_data *data; /* current folder split we're in */ struct mscabd_folder_data *data; /* current folder split we're in */
unsigned int offset; /* uncompressed offset within folder */ unsigned int offset; /* uncompressed offset within folder */
unsigned int block; /* which block are we decompressing? */ 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 */ struct mspack_system sys; /* special I/O code for decompressor */
int comp_type; /* type of compression used by folder */ int comp_type; /* type of compression used by folder */
int (*decompress)(void *, off_t); /* decompressor code */ 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 *infh; /* input file handle */
struct mspack_file *outfh; /* output file handle */ struct mspack_file *outfh; /* output file handle */
unsigned char *i_ptr, *i_end; /* input data consumed, end */ 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_p {
struct mscab_decompressor base; struct mscab_decompressor base;
struct mscabd_decompress_state *d; struct mscabd_decompress_state *d;
struct mspack_system *system; 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; int error, read_error;
}; };

View file

@ -1,5 +1,5 @@
/* This file is part of libmspack. /* 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 * 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 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
@ -23,7 +23,9 @@
#include "system-mspack.h" #include "system-mspack.h"
#include "cab.h" #include "cab.h"
#include <assert.h> #include "mszip.h"
#include "lzx.h"
#include "qtm.h"
/* Notes on compliance with cabinet specification: /* Notes on compliance with cabinet specification:
* *
@ -72,10 +74,9 @@ static void cabd_close(
struct mscab_decompressor *base, struct mscabd_cabinet *origcab); struct mscab_decompressor *base, struct mscabd_cabinet *origcab);
static int cabd_read_headers( static int cabd_read_headers(
struct mspack_system *sys, struct mspack_file *fh, 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( static char *cabd_read_string(
struct mspack_system *sys, struct mspack_file *fh, struct mspack_system *sys, struct mspack_file *fh, int *error);
struct mscabd_cabinet_p *cab, int *error);
static struct mscabd_cabinet *cabd_search( static struct mscabd_cabinet *cabd_search(
struct mscab_decompressor *base, const char *filename); struct mscab_decompressor *base, const char *filename);
@ -110,7 +111,7 @@ static int cabd_sys_write(
struct mspack_file *file, void *buffer, int bytes); struct mspack_file *file, void *buffer, int bytes);
static int cabd_sys_read_block( static int cabd_sys_read_block(
struct mspack_system *sys, struct mscabd_decompress_state *d, int *out, 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( static unsigned int cabd_checksum(
unsigned char *data, unsigned int bytes, unsigned int cksum); unsigned char *data, unsigned int bytes, unsigned int cksum);
static struct noned_state *noned_init( static struct noned_state *noned_init(
@ -155,9 +156,10 @@ struct mscab_decompressor *
self->d = NULL; self->d = NULL;
self->error = MSPACK_ERR_OK; self->error = MSPACK_ERR_OK;
self->param[MSCABD_PARAM_SEARCHBUF] = 32768; self->searchbuf_size = 32768;
self->param[MSCABD_PARAM_FIXMSZIP] = 0; self->fix_mszip = 0;
self->param[MSCABD_PARAM_DECOMPBUF] = 4096; self->buf_size = 4096;
self->salvage = 0;
} }
return (struct mscab_decompressor *) self; 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; struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
if (self) { if (self) {
struct mspack_system *sys = self->system; struct mspack_system *sys = self->system;
cabd_free_decomp(self);
if (self->d) { if (self->d) {
if (self->d->infh) sys->close(self->d->infh); if (self->d->infh) sys->close(self->d->infh);
cabd_free_decomp(self);
sys->free(self->d); sys->free(self->d);
} }
sys->free(self); 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 * opens a file and tries to read it as a cabinet file
*/ */
static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, 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 mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
struct mscabd_cabinet_p *cab = NULL; 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 ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) {
if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) {
cab->base.filename = filename; 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) { if (error) {
cabd_close(base, (struct mscabd_cabinet *) cab); cabd_close(base, (struct mscabd_cabinet *) cab);
cab = NULL; cab = NULL;
} }
self->error = error; 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. * frees all memory associated with a given mscabd_cabinet.
*/ */
static void cabd_close(struct mscab_decompressor *base, 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 mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
struct mscabd_folder_data *dat, *ndat; 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 */ /* free folder decompression state if it has been decompressed */
if (self->d && (self->d->folder == (struct mscabd_folder_p *) fol)) { if (self->d && (self->d->folder == (struct mscabd_folder_p *) fol)) {
if (self->d->infh) sys->close(self->d->infh); if (self->d->infh) sys->close(self->d->infh);
cabd_free_decomp(self); cabd_free_decomp(self);
sys->free(self->d); sys->free(self->d);
self->d = NULL; self->d = NULL;
} }
/* free folder data segments */ /* free folder data segments */
for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) { for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) {
ndat = dat->next; ndat = dat->next;
sys->free(dat); sys->free(dat);
} }
sys->free(fol); sys->free(fol);
} }
@ -304,11 +306,11 @@ static void cabd_close(struct mscab_decompressor *base,
* for folders and files as necessary * for folders and files as necessary
*/ */
static int cabd_read_headers(struct mspack_system *sys, static int cabd_read_headers(struct mspack_system *sys,
struct mspack_file *fh, struct mspack_file *fh,
struct mscabd_cabinet_p *cab, struct mscabd_cabinet_p *cab,
off_t offset, int quiet) 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_folder_p *fol, *linkfol = NULL;
struct mscabd_file *file, *linkfile = NULL; struct mscabd_file *file, *linkfile = NULL;
unsigned char buf[64]; 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 */ /* read the reserved-sizes part of header, if present */
cab->base.flags = EndGetI16(&buf[cfhead_Flags]); cab->base.flags = EndGetI16(&buf[cfhead_Flags]);
if (cab->base.flags & cfheadRESERVE_PRESENT) { if (cab->base.flags & cfheadRESERVE_PRESENT) {
if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) { if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) {
return MSPACK_ERR_READ; return MSPACK_ERR_READ;
@ -379,7 +382,7 @@ static int cabd_read_headers(struct mspack_system *sys,
/* skip the reserved header */ /* skip the reserved header */
if (cab->base.header_resv) { if (cab->base.header_resv) {
if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) { 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 */ /* read name and info of preceeding cabinet in set, if present */
if (cab->base.flags & cfheadPREV_CABINET) { if (cab->base.flags & cfheadPREV_CABINET) {
cab->base.prevname = cabd_read_string(sys, fh, cab, &x); if (x) return x; cab->base.prevname = cabd_read_string(sys, fh, &err);
cab->base.previnfo = cabd_read_string(sys, fh, cab, &x); if (x) return x; 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 */ /* read name and info of next cabinet in set, if present */
if (cab->base.flags & cfheadNEXT_CABINET) { if (cab->base.flags & cfheadNEXT_CABINET) {
cab->base.nextname = cabd_read_string(sys, fh, cab, &x); if (x) return x; cab->base.nextname = cabd_read_string(sys, fh, &err);
cab->base.nextinfo = cabd_read_string(sys, fh, cab, &x); if (x) return x; if (err) return err;
cab->base.nextinfo = cabd_read_string(sys, fh, &err);
if (err) return err;
} }
/* read folders */ /* read folders */
@ -408,7 +415,7 @@ static int cabd_read_headers(struct mspack_system *sys,
} }
if (folder_resv) { if (folder_resv) {
if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) { 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]); file->offset = EndGetI32(&buf[cffile_FolderOffset]);
/* set folder pointer */ /* set folder pointer */
x = EndGetI16(&buf[cffile_FolderIndex]); fidx = EndGetI16(&buf[cffile_FolderIndex]);
if (x < cffileCONTINUED_FROM_PREV) { if (fidx < cffileCONTINUED_FROM_PREV) {
/* normal folder index; count up to the correct folder. the folder /* normal folder index; count up to the correct folder */
* pointer will be NULL if folder index is invalid */ if (fidx < num_folders) {
struct mscabd_folder *ifol = cab->base.folders; struct mscabd_folder *ifol = cab->base.folders;
while (x--) if (ifol) ifol = ifol->next; while (fidx--) if (ifol) ifol = ifol->next;
file->folder = ifol; file->folder = ifol;
}
if (!ifol) { else {
sys->free(file); D(("invalid folder index"))
D(("invalid folder index")) file->folder = NULL;
return MSPACK_ERR_DATAFORMAT;
} }
} }
else { else {
/* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or /* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or
* CONTINUED_PREV_AND_NEXT */ * CONTINUED_PREV_AND_NEXT */
if ((x == cffileCONTINUED_TO_NEXT) || if ((fidx == cffileCONTINUED_TO_NEXT) ||
(x == cffileCONTINUED_PREV_AND_NEXT)) (fidx == cffileCONTINUED_PREV_AND_NEXT))
{ {
/* get last folder */ /* get last folder */
struct mscabd_folder *ifol = cab->base.folders; struct mscabd_folder *ifol = cab->base.folders;
while (ifol->next) ifol = ifol->next; while (ifol->next) ifol = ifol->next;
file->folder = ifol; file->folder = ifol;
/* set "merge next" pointer */ /* set "merge next" pointer */
fol = (struct mscabd_folder_p *) ifol; fol = (struct mscabd_folder_p *) ifol;
if (!fol->merge_next) fol->merge_next = file; if (!fol->merge_next) fol->merge_next = file;
} }
if ((x == cffileCONTINUED_FROM_PREV) || if ((fidx == cffileCONTINUED_FROM_PREV) ||
(x == cffileCONTINUED_PREV_AND_NEXT)) (fidx == cffileCONTINUED_PREV_AND_NEXT))
{ {
/* get first folder */ /* get first folder */
file->folder = cab->base.folders; file->folder = cab->base.folders;
/* set "merge prev" pointer */ /* set "merge prev" pointer */
fol = (struct mscabd_folder_p *) file->folder; fol = (struct mscabd_folder_p *) file->folder;
if (!fol->merge_prev) fol->merge_prev = file; 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; file->date_y = (x >> 9) + 1980;
/* get filename */ /* get filename */
file->filename = cabd_read_string(sys, fh, cab, &x); file->filename = cabd_read_string(sys, fh, &err);
if (x) {
/* if folder index or filename are bad, either skip it or fail */
if (err || !file->folder) {
sys->free(file->filename);
sys->free(file); sys->free(file);
return x; if (salvage) continue;
return err ? err : MSPACK_ERR_DATAFORMAT;
} }
/* link file entry into file list */ /* link file entry into file list */
@ -514,23 +524,34 @@ static int cabd_read_headers(struct mspack_system *sys,
linkfile = 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; return MSPACK_ERR_OK;
} }
static char *cabd_read_string(struct mspack_system *sys, static char *cabd_read_string(struct mspack_system *sys,
struct mspack_file *fh, struct mspack_file *fh, int *error)
struct mscabd_cabinet_p *cab, int *error)
{ {
off_t base = sys->tell(fh); off_t base = sys->tell(fh);
char buf[256], *str; char buf[256], *str;
unsigned int len, i, ok; int len, i, ok;
(void)cab;
/* read up to 256 bytes */ /* 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 */ /* search for a null terminator in the buffer */
for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; } for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; }
/* reject empty strings */
if (i == 0) ok = 0;
if (!ok) { if (!ok) {
*error = MSPACK_ERR_DATAFORMAT; *error = MSPACK_ERR_DATAFORMAT;
return NULL; 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 * break out of the loop and be sure that all resources are freed
*/ */
static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, 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 mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
struct mscabd_cabinet_p *cab = NULL; struct mscabd_cabinet_p *cab = NULL;
@ -579,7 +600,7 @@ static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base,
sys = self->system; sys = self->system;
/* allocate a search buffer */ /* 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) { if (!search_buf) {
self->error = MSPACK_ERR_NOMEMORY; self->error = MSPACK_ERR_NOMEMORY;
return NULL; 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 ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) {
if (!(self->error = mspack_sys_filelen(sys, fh, &filelen))) { if (!(self->error = mspack_sys_filelen(sys, fh, &filelen))) {
self->error = cabd_find(self, search_buf, fh, filename, self->error = cabd_find(self, search_buf, fh, filename,
filelen, &firstlen, &cab); filelen, &firstlen, &cab);
} }
/* truncated / extraneous data warning: */ /* truncated / extraneous data warning: */
if (firstlen && (firstlen != filelen) && if (firstlen && (firstlen != filelen) &&
(!cab || (cab->base.base_offset == 0))) (!cab || (cab->base.base_offset == 0)))
{ {
if (firstlen < filelen) { if (firstlen < filelen) {
sys->message(fh, "WARNING; possible %" LD sys->message(fh, "WARNING; possible %" LD
" extra bytes at end of file.", " extra bytes at end of file.",
filelen - firstlen); filelen - firstlen);
} }
else { else {
sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.", sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.",
firstlen - filelen); 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, static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
struct mspack_file *fh, const char *filename, off_t flen, struct mspack_file *fh, const char *filename, off_t flen,
off_t *firstlen, struct mscabd_cabinet_p **firstcab) off_t *firstlen, struct mscabd_cabinet_p **firstcab)
{ {
struct mscabd_cabinet_p *cab, *link = NULL; struct mscabd_cabinet_p *cab, *link = NULL;
off_t caboff, offset, length; 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; unsigned int cablen_u32 = 0, foffset_u32 = 0;
int false_cabs = 0; int false_cabs = 0;
#ifndef LARGEFILE_SUPPORT #if !LARGEFILE_SUPPORT
/* detect 32-bit off_t overflow */ /* detect 32-bit off_t overflow */
if (flen < 0) { if (flen < 0) {
sys->message(fh, largefile_msg); 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 /* 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. */ * amount of data remaining to the end of the file, whichever is less. */
length = flen - offset; length = flen - offset;
if (length > self->param[MSCABD_PARAM_SEARCHBUF]) { if (length > self->searchbuf_size) {
length = self->param[MSCABD_PARAM_SEARCHBUF]; length = self->searchbuf_size;
} }
/* fill the search buffer with data from disk */ /* 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 */ /* FAQ avoidance strategy */
if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) { if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) {
sys->message(fh, "WARNING; found InstallShield header. " sys->message(fh, "WARNING; found InstallShield header. Use unshield "
"This is probably an InstallShield file. " "(https://github.com/twogood/unshield) to unpack this file");
"Use UNSHIELD from www.synce.org to unpack it.");
} }
/* read through the entire buffer. */ /* read through the entire buffer. */
for (p = &buf[0], pend = &buf[length]; p < pend; ) { for (p = &buf[0], pend = &buf[length]; p < pend; ) {
switch (state) { switch (state) {
/* starting state */ /* starting state */
case 0: case 0:
/* we spend most of our time in this while loop, looking for /* we spend most of our time in this while loop, looking for
* a leading 'M' of the 'MSCF' signature */ * a leading 'M' of the 'MSCF' signature */
while (p < pend && *p != 0x4D) p++; while (p < pend && *p != 0x4D) p++;
/* if we found tht 'M', advance state */ /* if we found tht 'M', advance state */
if (p++ < pend) state = 1; if (p++ < pend) state = 1;
break; break;
/* verify that the next 3 bytes are 'S', 'C' and 'F' */ /* verify that the next 3 bytes are 'S', 'C' and 'F' */
case 1: state = (*p++ == 0x53) ? 2 : 0; break; 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 17: foffset_u32 |= *p++ << 8; state++; break;
case 18: foffset_u32 |= *p++ << 16; state++; break; case 18: foffset_u32 |= *p++ << 16; state++; break;
case 19: foffset_u32 |= *p++ << 24; case 19: foffset_u32 |= *p++ << 24;
/* now we have recieved 20 bytes of potential cab header. work out /* now we have recieved 20 bytes of potential cab header. work out
* the offset in the file of this potential cabinet */ * the offset in the file of this potential cabinet */
caboff = offset + (p - &buf[0]) - 20; caboff = offset + (p - &buf[0]) - 20;
/* should reading cabinet fail, restart search just after 'MSCF' */ /* should reading cabinet fail, restart search just after 'MSCF' */
offset = caboff + 4; offset = caboff + 4;
/* capture the "length of cabinet" field if there is a cabinet at /* capture the "length of cabinet" field if there is a cabinet at
* offset 0 in the file, regardless of whether the cabinet can be * offset 0 in the file, regardless of whether the cabinet can be
* read correctly or not */ * read correctly or not */
if (caboff == 0) *firstlen = (off_t) cablen_u32; if (caboff == 0) *firstlen = (off_t) cablen_u32;
/* check that the files offset is less than the alleged length of /* check that the files offset is less than the alleged length of
* the cabinet, and that the offset + the alleged length are * the cabinet, and that the offset + the alleged length are
* 'roughly' within the end of overall file length */ * 'roughly' within the end of overall file length. In salvage
if ((foffset_u32 < cablen_u32) && * mode, don't check the alleged length, allow it to be garbage */
((caboff + (off_t) foffset_u32) < (flen + 32)) && if ((foffset_u32 < cablen_u32) &&
((caboff + (off_t) cablen_u32) < (flen + 32)) ) ((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)))) { /* likely cabinet found -- try reading it */
return MSPACK_ERR_NOMEMORY; 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)) { cab->base.filename = filename;
/* destroy the failed cabinet */ if (cabd_read_headers(sys, fh, cab, caboff, self->salvage, 1)) {
cabd_close((struct mscab_decompressor *) self, /* destroy the failed cabinet */
(struct mscabd_cabinet *) cab); cabd_close((struct mscab_decompressor *) self,
false_cabs++; (struct mscabd_cabinet *) cab);
} false_cabs++;
else { }
/* cabinet read correctly! */ else {
/* cabinet read correctly! */
/* link the cab into the list */ /* link the cab into the list */
if (!link) *firstcab = cab; if (!link) *firstcab = cab;
else link->base.next = (struct mscabd_cabinet *) cab; else link->base.next = (struct mscabd_cabinet *) cab;
link = cab; link = cab;
/* cause the search to restart after this cab's data. */ /* cause the search to restart after this cab's data. */
offset = caboff + (off_t) cablen_u32; offset = caboff + (off_t) cablen_u32;
#ifndef LARGEFILE_SUPPORT #if !LARGEFILE_SUPPORT
/* detect 32-bit off_t overflow */ /* detect 32-bit off_t overflow */
if (offset < caboff) { if (offset < caboff) {
sys->message(fh, largefile_msg); sys->message(fh, largefile_msg);
return MSPACK_ERR_OK; return MSPACK_ERR_OK;
} }
#endif #endif
} }
} }
/* restart search */ /* restart search */
if (offset >= flen) return MSPACK_ERR_OK; if (offset >= flen) return MSPACK_ERR_OK;
if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) {
return MSPACK_ERR_SEEK; return MSPACK_ERR_SEEK;
} }
length = 0; length = 0;
p = pend; p = pend;
state = 0; state = 0;
break; break;
/* for bytes 4-7 and 12-15, just advance state/pointer */ /* for bytes 4-7 and 12-15, just advance state/pointer */
default: default:
p++, state++; p++, state++;
} /* switch(state) */ } /* switch(state) */
} /* for (... p < pend ...) */ } /* for (... p < pend ...) */
} /* for (... offset < length ...) */ } /* for (... offset < length ...) */
@ -765,7 +786,7 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
return MSPACK_ERR_OK; return MSPACK_ERR_OK;
} }
/*************************************** /***************************************
* CABD_MERGE, CABD_PREPEND, CABD_APPEND * 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. * merged folder's data parts list.
*/ */
static int cabd_prepend(struct mscab_decompressor *base, static int cabd_prepend(struct mscab_decompressor *base,
struct mscabd_cabinet *cab, struct mscabd_cabinet *cab,
struct mscabd_cabinet *prevcab) struct mscabd_cabinet *prevcab)
{ {
return cabd_merge(base, prevcab, cab); return cabd_merge(base, prevcab, cab);
} }
static int cabd_append(struct mscab_decompressor *base, static int cabd_append(struct mscab_decompressor *base,
struct mscabd_cabinet *cab, struct mscabd_cabinet *cab,
struct mscabd_cabinet *nextcab) struct mscabd_cabinet *nextcab)
{ {
return cabd_merge(base, cab, nextcab); return cabd_merge(base, cab, nextcab);
} }
static int cabd_merge(struct mscab_decompressor *base, static int cabd_merge(struct mscab_decompressor *base,
struct mscabd_cabinet *lcab, struct mscabd_cabinet *lcab,
struct mscabd_cabinet *rcab) struct mscabd_cabinet *rcab)
{ {
struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
struct mscabd_folder_data *data, *ndata; struct mscabd_folder_data *data, *ndata;
@ -880,7 +901,7 @@ static int cabd_merge(struct mscab_decompressor *base,
* instead */ * instead */
lfol->base.num_blocks += rfol->base.num_blocks - 1; lfol->base.num_blocks += rfol->base.num_blocks - 1;
if ((rfol->merge_next == NULL) || 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; lfol->merge_next = rfol->merge_next;
} }
@ -903,9 +924,9 @@ static int cabd_merge(struct mscab_decompressor *base,
rfi = fi->next; rfi = fi->next;
/* if file's folder matches the merge folder, unlink and free it */ /* if file's folder matches the merge folder, unlink and free it */
if (fi->folder == (struct mscabd_folder *) rfol) { if (fi->folder == (struct mscabd_folder *) rfol) {
if (lfi) lfi->next = rfi; else lcab->files = rfi; if (lfi) lfi->next = rfi; else lcab->files = rfi;
sys->free(fi->filename); sys->free(fi->filename);
sys->free(fi); sys->free(fi);
} }
else lfi = fi; else lfi = fi;
} }
@ -940,6 +961,12 @@ static int cabd_can_merge_folders(struct mspack_system *sys,
return 0; 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)) { if (!(lfi = lfol->merge_next) || !(rfi = rfol->merge_prev)) {
D(("folder merge: one cabinet has no files to merge")) D(("folder merge: one cabinet has no files to merge"))
return 0; 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 * should be identical in number and order. to verify this, check the
* offset and length of each file. */ * offset and length of each file. */
for (l=lfi, r=rfi; l; l=l->next, r=r->next) { for (l=lfi, r=rfi; l; l=l->next, r=r->next) {
if (!r || (l->offset != r->offset) || (l->length != r->length)) { if (!r || (l->offset != r->offset) || (l->length != r->length)) {
matching = 0; matching = 0;
break; break;
} }
} }
if (matching) return 1; 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. */ * the merge with a warning about missing files. */
matching = 0; matching = 0;
for (l = lfi; l; l = l->next) { for (l = lfi; l; l = l->next) {
for (r = rfi; r; r = r->next) { for (r = rfi; r; r = r->next) {
if (l->offset == r->offset && l->length == r->length) break; if (l->offset == r->offset && l->length == r->length) break;
} }
if (r) matching = 1; else sys->message(NULL, if (r) matching = 1; else sys->message(NULL,
"WARNING; merged file %s not listed in both cabinets", l->filename); "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 mscabd_folder_p *fol;
struct mspack_system *sys; struct mspack_system *sys;
struct mspack_file *fh; struct mspack_file *fh;
off_t filelen;
if (!self) return MSPACK_ERR_ARGS; if (!self) return MSPACK_ERR_ARGS;
if (!file) return self->error = 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; sys = self->system;
fol = (struct mscabd_folder_p *) file->folder; fol = (struct mscabd_folder_p *) file->folder;
/* check if file can be extracted */ /* if offset is beyond 2GB, nothing can be extracted */
if ((!fol) || (fol->merge_prev) || if (file->offset > CAB_LENGTHMAX) {
(((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);
return self->error = MSPACK_ERR_DATAFORMAT; 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 */ /* allocate generic decompression state */
if (!self->d) { if (!self->d) {
self->d = (struct mscabd_decompress_state *) sys->alloc(sys, sizeof(struct mscabd_decompress_state)); 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? */ /* 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? */ /* do we need to open a new cab file? */
if (!self->d->infh || (fol->data.cab != self->d->incab)) { if (!self->d->infh || (fol->data.cab != self->d->incab)) {
/* close previous file handle if from a different cab */ /* close previous file handle if from a different cab */
if (self->d->infh) sys->close(self->d->infh); if (self->d->infh) sys->close(self->d->infh);
self->d->incab = fol->data.cab; self->d->incab = fol->data.cab;
self->d->infh = sys->open(sys, fol->data.cab->base.filename, 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; if (!self->d->infh) return self->error = MSPACK_ERR_OPEN;
} }
/* seek to start of data blocks */ /* 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->data = &fol->data;
self->d->offset = 0; self->d->offset = 0;
self->d->block = 0; self->d->block = 0;
self->d->outlen = 0;
self->d->i_ptr = self->d->i_end = &self->d->input[0]; self->d->i_ptr = self->d->i_end = &self->d->input[0];
/* read_error lasts for the lifetime of a decompressor */ /* 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; self->error = MSPACK_ERR_OK;
/* if file has more than 0 bytes */ /* if file has more than 0 bytes */
if (file->length) { if (filelen) {
off_t bytes; off_t bytes;
int error; int error;
/* get to correct offset. /* get to correct offset.
@ -1065,14 +1127,14 @@ static int cabd_extract(struct mscab_decompressor *base,
*/ */
self->d->outfh = NULL; self->d->outfh = NULL;
if ((bytes = file->offset - self->d->offset)) { if ((bytes = file->offset - self->d->offset)) {
error = self->d->decompress(self->d->state, bytes); error = self->d->decompress(self->d->state, bytes);
self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; self->error = (error == MSPACK_ERR_READ) ? self->read_error : error;
} }
/* if getting to the correct offset was error free, unpack file */ /* if getting to the correct offset was error free, unpack file */
if (!self->error) { if (!self->error) {
self->d->outfh = fh; 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; 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; 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; self->d->comp_type = ct;
switch (ct & cffoldCOMPTYPE_MASK) { switch (ct & cffoldCOMPTYPE_MASK) {
case cffoldCOMPTYPE_NONE: case cffoldCOMPTYPE_NONE:
self->d->decompress = (int (*)(void *, off_t)) &noned_decompress; self->d->decompress = (int (*)(void *, off_t)) &noned_decompress;
self->d->state = noned_init(&self->d->sys, fh, fh, self->d->state = noned_init(&self->d->sys, fh, fh, self->buf_size);
self->param[MSCABD_PARAM_DECOMPBUF]);
break; break;
case cffoldCOMPTYPE_MSZIP: case cffoldCOMPTYPE_MSZIP:
self->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress; self->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress;
self->d->state = mszipd_init(&self->d->sys, fh, fh, self->d->state = mszipd_init(&self->d->sys, fh, fh, self->buf_size,
self->param[MSCABD_PARAM_DECOMPBUF], self->fix_mszip);
self->param[MSCABD_PARAM_FIXMSZIP]);
break; break;
case cffoldCOMPTYPE_QUANTUM: case cffoldCOMPTYPE_QUANTUM:
self->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress; self->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress;
self->d->state = qtmd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, self->d->state = qtmd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f,
self->param[MSCABD_PARAM_DECOMPBUF]); self->buf_size);
break; break;
case cffoldCOMPTYPE_LZX: case cffoldCOMPTYPE_LZX:
self->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress; 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->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; break;
default: default:
return self->error = MSPACK_ERR_DATAFORMAT; 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) { 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) { switch (self->d->comp_type & cffoldCOMPTYPE_MASK) {
case cffoldCOMPTYPE_NONE: noned_free((struct noned_state *) self->d->state); break; 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; struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file;
unsigned char *buf = (unsigned char *) buffer; unsigned char *buf = (unsigned char *) buffer;
struct mspack_system *sys = self->system; 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] && ignore_cksum = self->salvage ||
((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP); (self->fix_mszip &&
((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP));
ignore_blocksize = self->salvage;
todo = bytes; todo = bytes;
while (todo > 0) { 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 */ /* check if we're out of input blocks, advance block counter */
if (self->d->block++ >= self->d->folder->base.num_blocks) { if (self->d->block++ >= self->d->folder->base.num_blocks) {
self->read_error = MSPACK_ERR_DATAFORMAT; if (!self->salvage) {
break; self->read_error = MSPACK_ERR_DATAFORMAT;
}
else {
D(("Ran out of CAB input blocks prematurely"))
}
break;
} }
/* read a block */ /* 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; if (self->read_error) return -1;
self->d->outlen += outlen;
/* special Quantum hack -- trailer byte to allow the decompressor /* special Quantum hack -- trailer byte to allow the decompressor
* to realign itself. CAB Quantum blocks, unlike LZX blocks, can have * to realign itself. CAB Quantum blocks, unlike LZX blocks, can have
* anything from 0 to 4 trailing null bytes. */ * anything from 0 to 4 trailing null bytes. */
if ((self->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) { if ((self->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) {
*self->d->i_end++ = 0xFF; *self->d->i_end++ = 0xFF;
} }
/* is this the last block? */ /* is this the last block? */
if (self->d->block >= self->d->folder->base.num_blocks) { if (self->d->block >= self->d->folder->base.num_blocks) {
/* last block */ if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) {
if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) { /* special LZX hack -- on the last block, inform LZX of the
/* special LZX hack -- on the last block, inform LZX of the * size of the output data stream. */
* size of the output data stream. */ lzxd_set_output_length((struct lzxd_stream *) self->d->state, self->d->outlen);
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 (avail) */ } /* if (avail) */
} /* while (todo > 0) */ } /* 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 * one cab file, if it does then the fragments will be reassembled
*/ */
static int cabd_sys_read_block(struct mspack_system *sys, static int cabd_sys_read_block(struct mspack_system *sys,
struct mscabd_decompress_state *d, struct mscabd_decompress_state *d,
int *out, int ignore_cksum) int *out, int ignore_cksum,
int ignore_blocksize)
{ {
unsigned char hdr[cfdata_SIZEOF]; unsigned char hdr[cfdata_SIZEOF];
unsigned int cksum; unsigned int cksum;
int len; int len, full_len;
/* reset the input block pointer and end of block pointer */ /* reset the input block pointer and end of block pointer */
d->i_ptr = d->i_end = &d->input[0]; 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 */ /* skip any reserved block headers */
if (d->data->cab->block_resv && if (d->data->cab->block_resv &&
sys->seek(d->infh, (off_t) d->data->cab->block_resv, sys->seek(d->infh, (off_t) d->data->cab->block_resv,
MSPACK_SYS_SEEK_CUR)) MSPACK_SYS_SEEK_CUR))
{ {
return MSPACK_ERR_SEEK; return MSPACK_ERR_SEEK;
} }
/* blocks must not be over CAB_INPUTMAX in size */ /* blocks must not be over CAB_INPUTMAX in size */
len = EndGetI16(&hdr[cfdata_CompressedSize]); len = EndGetI16(&hdr[cfdata_CompressedSize]);
if (((d->i_end - d->i_ptr) + len) > CAB_INPUTMAX) { full_len = (d->i_end - d->i_ptr) + len; /* include cab-spanning blocks */
D(("block size > CAB_INPUTMAX (%ld + %d)", d->i_end - d->i_ptr, len)) if (full_len > CAB_INPUTMAX) {
return MSPACK_ERR_DATAFORMAT; 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 */ /* blocks must not expand to more than CAB_BLOCKMAX */
if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) { if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) {
D(("block size > CAB_BLOCKMAX")) D(("block size > CAB_BLOCKMAX"))
return MSPACK_ERR_DATAFORMAT; if (!ignore_blocksize) return MSPACK_ERR_DATAFORMAT;
} }
/* read the block data */ /* read the block data */
@ -1284,8 +1344,8 @@ static int cabd_sys_read_block(struct mspack_system *sys,
if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) { if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) {
unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0); unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0);
if (cabd_checksum(&hdr[4], 4, sum2) != cksum) { if (cabd_checksum(&hdr[4], 4, sum2) != cksum) {
if (!ignore_cksum) return MSPACK_ERR_CHECKSUM; if (!ignore_cksum) return MSPACK_ERR_CHECKSUM;
sys->message(d->infh, "WARNING; bad block checksum found"); 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 */ /* advance to next member in the cabinet set */
if (!(d->data = d->data->next)) { 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; return MSPACK_ERR_DATAFORMAT;
} }
/* open next cab file */ /* open next cab file */
d->incab = d->data->cab; d->incab = d->data->cab;
if (!(d->infh = sys->open(sys, d->incab->base.filename, if (!(d->infh = sys->open(sys, d->incab->base.filename,
MSPACK_SYS_OPEN_READ))) MSPACK_SYS_OPEN_READ)))
{ {
return MSPACK_ERR_OPEN; 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, static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes,
unsigned int cksum) unsigned int cksum)
{ {
unsigned int len, ul = 0; unsigned int len, ul = 0;
@ -1342,8 +1402,8 @@ static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes,
} }
switch (bytes & 3) { switch (bytes & 3) {
case 3: ul |= *data++ << 16; case 3: ul |= *data++ << 16; /*@fallthrough@*/
case 2: ul |= *data++ << 8; case 2: ul |= *data++ << 8; /*@fallthrough@*/
case 1: ul |= *data; case 1: ul |= *data;
} }
cksum ^= ul; cksum ^= ul;
@ -1365,9 +1425,9 @@ struct noned_state {
}; };
static struct noned_state *noned_init(struct mspack_system *sys, static struct noned_state *noned_init(struct mspack_system *sys,
struct mspack_file *in, struct mspack_file *in,
struct mspack_file *out, struct mspack_file *out,
int bufsize) int bufsize)
{ {
struct noned_state *state = (struct noned_state *) sys->alloc(sys, sizeof(struct noned_state)); 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); 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) { switch (param) {
case MSCABD_PARAM_SEARCHBUF: case MSCABD_PARAM_SEARCHBUF:
if (value < 4) return MSPACK_ERR_ARGS; if (value < 4) return MSPACK_ERR_ARGS;
self->param[MSCABD_PARAM_SEARCHBUF] = value; self->searchbuf_size = value;
break; break;
case MSCABD_PARAM_FIXMSZIP: case MSCABD_PARAM_FIXMSZIP:
self->param[MSCABD_PARAM_FIXMSZIP] = value; self->fix_mszip = value;
break; break;
case MSCABD_PARAM_DECOMPBUF: case MSCABD_PARAM_DECOMPBUF:
if (value < 4) return MSPACK_ERR_ARGS; 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; break;
default: default:
return MSPACK_ERR_ARGS; return MSPACK_ERR_ARGS;

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/* This file is part of libmspack. /* 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 * KWAJ is a format very similar to SZDD. KWAJ method 3 (LZH) was
* written by Jeff Johnson. * written by Jeff Johnson.
@ -14,6 +14,7 @@
#include "system-mspack.h" #include "system-mspack.h"
#include "kwaj.h" #include "kwaj.h"
#include "mszip.h"
/* prototypes */ /* prototypes */
static struct mskwajd_header *kwajd_open( static struct mskwajd_header *kwajd_open(
@ -40,7 +41,7 @@ static void lzh_free(
static int lzh_read_lens( static int lzh_read_lens(
struct kwajd_stream *kwaj, struct kwajd_stream *kwaj,
unsigned int type, unsigned int numsyms, unsigned int type, unsigned int numsyms,
unsigned char *lens, unsigned short *table); unsigned char *lens);
static int lzh_read_input( static int lzh_read_input(
struct kwajd_stream *kwaj); 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; struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
if (self) { if (self) {
struct mspack_system *sys = self->system; struct mspack_system *sys = self->system;
sys->free(self); sys->free(self);
} }
} }
@ -90,7 +91,7 @@ void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *base)
* opens a KWAJ file without decompressing, reads header * opens a KWAJ file without decompressing, reads header
*/ */
static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base, 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 mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
struct mskwajd_header *hdr; 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); fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ);
hdr = (struct mskwajd_header *) sys->alloc(sys, sizeof(struct mskwajd_header_p)); hdr = (struct mskwajd_header *) sys->alloc(sys, sizeof(struct mskwajd_header_p));
if (fh && hdr) { if (fh && hdr) {
((struct mskwajd_header_p *) hdr)->fh = fh; ((struct mskwajd_header_p *) hdr)->fh = fh;
self->error = kwajd_read_headers(sys, fh, hdr); self->error = kwajd_read_headers(sys, fh, hdr);
} }
else { else {
if (!fh) self->error = MSPACK_ERR_OPEN; if (!fh) self->error = MSPACK_ERR_OPEN;
if (!hdr) self->error = MSPACK_ERR_NOMEMORY; if (!hdr) self->error = MSPACK_ERR_NOMEMORY;
} }
if (self->error) { if (self->error) {
if (fh) sys->close(fh); if (fh) sys->close(fh);
if (hdr) sys->free(hdr); sys->free(hdr);
hdr = NULL; hdr = NULL;
} }
return hdr; return hdr;
@ -126,7 +127,7 @@ static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base,
* closes a KWAJ file * closes a KWAJ file
*/ */
static void kwajd_close(struct mskwaj_decompressor *base, 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 mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
struct mskwajd_header_p *hdr_p = (struct mskwajd_header_p *) hdr; 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 * reads the headers of a KWAJ format file
*/ */
static int kwajd_read_headers(struct mspack_system *sys, static int kwajd_read_headers(struct mspack_system *sys,
struct mspack_file *fh, struct mspack_file *fh,
struct mskwajd_header *hdr) struct mskwajd_header *hdr)
{ {
unsigned char buf[16]; unsigned char buf[16];
int i; int i;
/* read in the header */ /* read in the header */
if (sys->read(fh, &buf[0], kwajh_SIZEOF) != kwajh_SIZEOF) { if (sys->read(fh, &buf[0], kwajh_SIZEOF) != kwajh_SIZEOF) {
return MSPACK_ERR_READ; return MSPACK_ERR_READ;
} }
/* check for "KWAJ" signature */ /* check for "KWAJ" signature */
if (((unsigned int) EndGetI32(&buf[kwajh_Signature1]) != 0x4A41574B) || 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 */ /* basic header fields */
@ -179,61 +180,67 @@ static int kwajd_read_headers(struct mspack_system *sys,
/* 4 bytes: length of unpacked file */ /* 4 bytes: length of unpacked file */
if (hdr->headers & MSKWAJ_HDR_HASLENGTH) { if (hdr->headers & MSKWAJ_HDR_HASLENGTH) {
if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ; if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ;
hdr->length = EndGetI32(&buf[0]); hdr->length = EndGetI32(&buf[0]);
} }
/* 2 bytes: unknown purpose */ /* 2 bytes: unknown purpose */
if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN1) { 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 */ /* 2 bytes: length of section, then [length] bytes: unknown purpose */
if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN2) { if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN2) {
if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ;
i = EndGetI16(&buf[0]); i = EndGetI16(&buf[0]);
if (sys->seek(fh, (off_t)i, MSPACK_SYS_SEEK_CUR)) return MSPACK_ERR_SEEK; if (sys->seek(fh, (off_t)i, MSPACK_SYS_SEEK_CUR)) return MSPACK_ERR_SEEK;
} }
/* filename and extension */ /* filename and extension */
if (hdr->headers & (MSKWAJ_HDR_HASFILENAME | MSKWAJ_HDR_HASFILEEXT)) { if (hdr->headers & (MSKWAJ_HDR_HASFILENAME | MSKWAJ_HDR_HASFILEEXT)) {
off_t pos = sys->tell(fh); int len;
char *fn = (char *) sys->alloc(sys, (size_t) 13); /* allocate memory for maximum length filename */
char *fn = (char *) sys->alloc(sys, (size_t) 13);
if (!(hdr->filename = fn)) return MSPACK_ERR_NOMEMORY;
/* allocate memory for maximum length filename */ /* copy filename if present */
if (! fn) return MSPACK_ERR_NOMEMORY; if (hdr->headers & MSKWAJ_HDR_HASFILENAME) {
hdr->filename = fn; /* 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 filename if present */ /* copy extension if present */
if (hdr->headers & MSKWAJ_HDR_HASFILENAME) { if (hdr->headers & MSKWAJ_HDR_HASFILEEXT) {
if (sys->read(fh, &buf[0], 9) != 9) return MSPACK_ERR_READ; *fn++ = '.';
for (i = 0; i < 9; i++, fn++) if (!(*fn = buf[i])) break; /* read and copy up to 4 bytes of a null terminated string */
pos += (i < 9) ? i+1 : 9; if ((len = sys->read(fh, &buf[0], 4)) < 2) return MSPACK_ERR_READ;
if (sys->seek(fh, pos, MSPACK_SYS_SEEK_START)) for (i = 0; i < len; i++) if (!(*fn++ = buf[i])) break;
return MSPACK_ERR_SEEK; /* 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 */
/* copy extension if present */ if (sys->seek(fh, (off_t)(i + 1 - len), MSPACK_SYS_SEEK_CUR))
if (hdr->headers & MSKWAJ_HDR_HASFILEEXT) { return MSPACK_ERR_SEEK;
*fn++ = '.'; fn--; /* remove the null terminator */
if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ; }
for (i = 0; i < 4; i++, fn++) if (!(*fn = buf[i])) break; *fn = '\0';
pos += (i < 4) ? i+1 : 4;
if (sys->seek(fh, pos, MSPACK_SYS_SEEK_START))
return MSPACK_ERR_SEEK;
}
*fn = '\0';
} }
/* 2 bytes: extra text length then [length] bytes of extra text data */ /* 2 bytes: extra text length then [length] bytes of extra text data */
if (hdr->headers & MSKWAJ_HDR_HASEXTRATEXT) { if (hdr->headers & MSKWAJ_HDR_HASEXTRATEXT) {
if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ;
i = EndGetI16(&buf[0]); i = EndGetI16(&buf[0]);
hdr->extra = (char *) sys->alloc(sys, (size_t)i+1); hdr->extra = (char *) sys->alloc(sys, (size_t)i+1);
if (! hdr->extra) return MSPACK_ERR_NOMEMORY; if (! hdr->extra) return MSPACK_ERR_NOMEMORY;
if (sys->read(fh, hdr->extra, i) != i) return MSPACK_ERR_READ; if (sys->read(fh, hdr->extra, i) != i) return MSPACK_ERR_READ;
hdr->extra[i] = '\0'; hdr->extra[i] = '\0';
hdr->extra_length = i; hdr->extra_length = i;
} }
return MSPACK_ERR_OK; return MSPACK_ERR_OK;
} }
@ -244,7 +251,7 @@ static int kwajd_read_headers(struct mspack_system *sys,
* decompresses a KWAJ file * decompresses a KWAJ file
*/ */
static int kwajd_extract(struct mskwaj_decompressor *base, 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 mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
struct mspack_system *sys; struct mspack_system *sys;
@ -258,51 +265,56 @@ static int kwajd_extract(struct mskwaj_decompressor *base,
/* seek to the compressed data */ /* seek to the compressed data */
if (sys->seek(fh, hdr->data_offset, MSPACK_SYS_SEEK_START)) { 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 */ /* open file for output */
if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { 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; self->error = MSPACK_ERR_OK;
/* decompress based on format */ /* decompress based on format */
if (hdr->comp_type == MSKWAJ_COMP_NONE || 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 */ /* 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); unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) KWAJ_INPUT_SIZE);
if (buf) { if (buf) {
int read, i; int read, i;
while ((read = sys->read(fh, buf, KWAJ_INPUT_SIZE)) > 0) { while ((read = sys->read(fh, buf, KWAJ_INPUT_SIZE)) > 0) {
if (hdr->comp_type == MSKWAJ_COMP_XOR) { if (hdr->comp_type == MSKWAJ_COMP_XOR) {
for (i = 0; i < read; i++) buf[i] ^= 0xFF; for (i = 0; i < read; i++) buf[i] ^= 0xFF;
} }
if (sys->write(outfh, buf, read) != read) { if (sys->write(outfh, buf, read) != read) {
self->error = MSPACK_ERR_WRITE; self->error = MSPACK_ERR_WRITE;
break; break;
} }
} }
if (read < 0) self->error = MSPACK_ERR_READ; if (read < 0) self->error = MSPACK_ERR_READ;
sys->free(buf); sys->free(buf);
} }
else { else {
self->error = MSPACK_ERR_NOMEMORY; self->error = MSPACK_ERR_NOMEMORY;
} }
} }
else if (hdr->comp_type == MSKWAJ_COMP_SZDD) { else if (hdr->comp_type == MSKWAJ_COMP_SZDD) {
self->error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE, self->error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE,
LZSS_MODE_EXPAND); LZSS_MODE_EXPAND);
} }
else if (hdr->comp_type == MSKWAJ_COMP_LZH) { else if (hdr->comp_type == MSKWAJ_COMP_LZH) {
struct kwajd_stream *lzh = lzh_init(sys, fh, outfh); struct kwajd_stream *lzh = lzh_init(sys, fh, outfh);
self->error = (lzh) ? lzh_decompress(lzh) : MSPACK_ERR_NOMEMORY; self->error = (lzh) ? lzh_decompress(lzh) : MSPACK_ERR_NOMEMORY;
lzh_free(lzh); 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 { else {
self->error = MSPACK_ERR_DATAFORMAT; self->error = MSPACK_ERR_DATAFORMAT;
} }
/* close output file */ /* close output file */
@ -317,7 +329,7 @@ static int kwajd_extract(struct mskwaj_decompressor *base,
* unpacks directly from input to output * unpacks directly from input to output
*/ */
static int kwajd_decompress(struct mskwaj_decompressor *base, 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 mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
struct mskwajd_header *hdr; struct mskwajd_header *hdr;
@ -353,15 +365,15 @@ static int kwajd_error(struct mskwaj_decompressor *base)
#define BITS_VAR lzh #define BITS_VAR lzh
#define BITS_ORDER_MSB #define BITS_ORDER_MSB
#define BITS_NO_READ_INPUT #define BITS_NO_READ_INPUT
#define READ_BYTES do { \ #define READ_BYTES do { \
if (i_ptr >= i_end) { \ if (i_ptr >= i_end) { \
if ((err = lzh_read_input(lzh))) return err; \ if ((err = lzh_read_input(lzh))) return err; \
i_ptr = lzh->i_ptr; \ i_ptr = lzh->i_ptr; \
i_end = lzh->i_end; \ i_end = lzh->i_end; \
} \ } \
INJECT_BITS(*i_ptr++, 8); \ INJECT_BITS(*i_ptr++, 8); \
} while (0) } while (0)
#include "readbits.h" #include <readbits.h>
/* import huffman-reading macros and code */ /* import huffman-reading macros and code */
#define TABLEBITS(tbl) KWAJ_TABLEBITS #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_TABLE(tbl,idx) lzh->tbl##_table[idx]
#define HUFF_LEN(tbl,idx) lzh->tbl##_len[idx] #define HUFF_LEN(tbl,idx) lzh->tbl##_len[idx]
#define HUFF_ERROR return MSPACK_ERR_DATAFORMAT #define HUFF_ERROR return MSPACK_ERR_DATAFORMAT
#include "readhuff.h" #include <readhuff.h>
/* In the KWAJ LZH format, there is no special 'eof' marker, it just /* 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 * 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 * isn't how the default readbits.h read_input() works (it simply lets
* 2 fake bytes in then stops), so we implement our own. * 2 fake bytes in then stops), so we implement our own.
*/ */
#define READ_BITS_SAFE(val, n) do { \ #define READ_BITS_SAFE(val, n) do { \
READ_BITS(val, n); \ READ_BITS(val, n); \
if (lzh->input_end && bits_left < lzh->input_end) \ if (lzh->input_end && bits_left < lzh->input_end) \
return MSPACK_ERR_OK; \ return MSPACK_ERR_OK; \
} while (0) } while (0)
#define READ_HUFFSYM_SAFE(tbl, val) do { \ #define READ_HUFFSYM_SAFE(tbl, val) do { \
READ_HUFFSYM(tbl, val); \ READ_HUFFSYM(tbl, val); \
if (lzh->input_end && bits_left < lzh->input_end) \ if (lzh->input_end && bits_left < lzh->input_end) \
return MSPACK_ERR_OK; \ return MSPACK_ERR_OK; \
} while (0) } while (0)
#define BUILD_TREE(tbl, type) \ #define BUILD_TREE(tbl, type) \
STORE_BITS; \ STORE_BITS; \
err = lzh_read_lens(lzh, type, MAXSYMBOLS(tbl), \ err = lzh_read_lens(lzh, type, MAXSYMBOLS(tbl), &HUFF_LEN(tbl,0)); \
&HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0)); \ if (err) return err; \
if (err) return err; \ RESTORE_BITS; \
RESTORE_BITS; \ if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \
if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \
&HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ return MSPACK_ERR_DATAFORMAT;
return MSPACK_ERR_DATAFORMAT;
#define WRITE_BYTE do { \ #define WRITE_BYTE do { \
if (lzh->sys->write(lzh->output, &lzh->window[pos], 1) != 1) \ if (lzh->sys->write(lzh->output, &lzh->window[pos], 1) != 1) \
return MSPACK_ERR_WRITE; \ return MSPACK_ERR_WRITE; \
} while (0) } while (0)
static struct kwajd_stream *lzh_init(struct mspack_system *sys, 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]); BUILD_TREE(LITERAL, types[4]);
while (!lzh->input_end) { while (!lzh->input_end) {
if (lit_run) READ_HUFFSYM_SAFE(MATCHLEN2, len); if (lit_run) READ_HUFFSYM_SAFE(MATCHLEN2, len);
else READ_HUFFSYM_SAFE(MATCHLEN1, len); else READ_HUFFSYM_SAFE(MATCHLEN1, len);
if (len > 0) { if (len > 0) {
len += 2; len += 2;
lit_run = 0; /* not the end of a literal run */ lit_run = 0; /* not the end of a literal run */
READ_HUFFSYM_SAFE(OFFSET, j); offset = j << 6; READ_HUFFSYM_SAFE(OFFSET, j); offset = j << 6;
READ_BITS_SAFE(j, 6); offset |= j; READ_BITS_SAFE(j, 6); offset |= j;
/* copy match as output and into the ring buffer */ /* copy match as output and into the ring buffer */
while (len-- > 0) { while (len-- > 0) {
lzh->window[pos] = lzh->window[(pos+4096-offset) & 4095]; lzh->window[pos] = lzh->window[(pos+4096-offset) & 4095];
WRITE_BYTE; WRITE_BYTE;
pos++; pos &= 4095; pos++; pos &= 4095;
} }
} }
else { else {
READ_HUFFSYM_SAFE(LITLEN, len); len++; READ_HUFFSYM_SAFE(LITLEN, len); len++;
lit_run = (len == 32) ? 0 : 1; /* end of a literal run? */ lit_run = (len == 32) ? 0 : 1; /* end of a literal run? */
while (len-- > 0) { while (len-- > 0) {
READ_HUFFSYM_SAFE(LITERAL, j); READ_HUFFSYM_SAFE(LITERAL, j);
/* copy as output and into the ring buffer */ /* copy as output and into the ring buffer */
lzh->window[pos] = j; lzh->window[pos] = j;
WRITE_BYTE; WRITE_BYTE;
pos++; pos &= 4095; pos++; pos &= 4095;
} }
} }
} }
return MSPACK_ERR_OK; 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, static int lzh_read_lens(struct kwajd_stream *lzh,
unsigned int type, unsigned int numsyms, unsigned int type, unsigned int numsyms,
unsigned char *lens, unsigned short *table) unsigned char *lens)
{ {
register unsigned int bit_buffer; register unsigned int bit_buffer;
register int bits_left; register int bits_left;
@ -499,33 +510,33 @@ static int lzh_read_lens(struct kwajd_stream *lzh,
RESTORE_BITS; RESTORE_BITS;
switch (type) { switch (type) {
case 0: case 0:
i = numsyms; c = (i==16)?4: (i==32)?5: (i==64)?6: (i==256)?8 :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; for (i = 0; i < numsyms; i++) lens[i] = c;
break; break;
case 1: case 1:
READ_BITS_SAFE(c, 4); lens[0] = c; READ_BITS_SAFE(c, 4); lens[0] = c;
for (i = 1; i < numsyms; i++) { for (i = 1; i < numsyms; i++) {
READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = c; 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(sel, 1); if (sel == 0) lens[i] = ++c;
else { READ_BITS_SAFE(c, 4); lens[i] = c; }} else { READ_BITS_SAFE(c, 4); lens[i] = c; }}
} }
break; break;
case 2: case 2:
READ_BITS_SAFE(c, 4); lens[0] = c; READ_BITS_SAFE(c, 4); lens[0] = c;
for (i = 1; i < numsyms; i++) { for (i = 1; i < numsyms; i++) {
READ_BITS_SAFE(sel, 2); READ_BITS_SAFE(sel, 2);
if (sel == 3) READ_BITS_SAFE(c, 4); else c += (char) sel-1; if (sel == 3) READ_BITS_SAFE(c, 4); else c += (char) sel-1;
lens[i] = c; lens[i] = c;
} }
break; break;
case 3: case 3:
for (i = 0; i < numsyms; i++) { for (i = 0; i < numsyms; i++) {
READ_BITS_SAFE(c, 4); lens[i] = c; READ_BITS_SAFE(c, 4); lens[i] = c;
} }
break; break;
} }
STORE_BITS; STORE_BITS;
return MSPACK_ERR_OK; 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) { static int lzh_read_input(struct kwajd_stream *lzh) {
int read; int read;
if (lzh->input_end) { if (lzh->input_end) {
lzh->input_end += 8; lzh->input_end += 8;
lzh->inbuf[0] = 0; lzh->inbuf[0] = 0;
read = 1; read = 1;
} }
else { else {
read = lzh->sys->read(lzh->input, &lzh->inbuf[0], KWAJ_INPUT_SIZE); read = lzh->sys->read(lzh->input, &lzh->inbuf[0], KWAJ_INPUT_SIZE);
if (read < 0) return MSPACK_ERR_READ; if (read < 0) return MSPACK_ERR_READ;
if (read == 0) { if (read == 0) {
lzh->input_end = 8; lzh->input_end = 8;
lzh->inbuf[0] = 0; lzh->inbuf[0] = 0;
read = 1; read = 1;
} }
} }
/* update i_ptr and i_end */ /* update i_ptr and i_end */

View file

@ -54,10 +54,10 @@ extern "C" {
* @return an error code, or MSPACK_ERR_OK if successful * @return an error code, or MSPACK_ERR_OK if successful
*/ */
extern int lzss_decompress(struct mspack_system *system, extern int lzss_decompress(struct mspack_system *system,
struct mspack_file *input, struct mspack_file *input,
struct mspack_file *output, struct mspack_file *output,
int input_buffer_size, int input_buffer_size,
int mode); int mode);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -14,31 +14,31 @@
#include "system-mspack.h" #include "system-mspack.h"
#include "lzss.h" #include "lzss.h"
#define ENSURE_BYTES do { \ #define ENSURE_BYTES do { \
if (i_ptr >= i_end) { \ if (i_ptr >= i_end) { \
read = system->read(input, &inbuf[0], \ read = system->read(input, &inbuf[0], \
input_buffer_size); \ input_buffer_size); \
if (read <= 0) { \ if (read <= 0) { \
system->free(window); \ system->free(window); \
return (read < 0) ? MSPACK_ERR_READ \ return (read < 0) ? MSPACK_ERR_READ \
: MSPACK_ERR_OK; \ : MSPACK_ERR_OK; \
} \ } \
i_ptr = &inbuf[0]; i_end = &inbuf[read]; \ i_ptr = &inbuf[0]; i_end = &inbuf[read]; \
} \ } \
} while (0) } while (0)
#define WRITE_BYTE do { \ #define WRITE_BYTE do { \
if (system->write(output, &window[pos], 1) != 1) { \ if (system->write(output, &window[pos], 1) != 1) { \
system->free(window); \ system->free(window); \
return MSPACK_ERR_WRITE; \ return MSPACK_ERR_WRITE; \
} \ } \
} while (0) } while (0)
int lzss_decompress(struct mspack_system *system, int lzss_decompress(struct mspack_system *system,
struct mspack_file *input, struct mspack_file *input,
struct mspack_file *output, struct mspack_file *output,
int input_buffer_size, int input_buffer_size,
int mode) int mode)
{ {
unsigned char *window, *inbuf, *i_ptr, *i_end; unsigned char *window, *inbuf, *i_ptr, *i_end;
unsigned int pos, i, c, invert, mpos, len; 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 && if (!system || input_buffer_size < 1 || (mode != LZSS_MODE_EXPAND &&
mode != LZSS_MODE_MSHELP && mode != LZSS_MODE_QBASIC)) mode != LZSS_MODE_MSHELP && mode != LZSS_MODE_QBASIC))
{ {
return MSPACK_ERR_ARGS; return MSPACK_ERR_ARGS;
} }
/* allocate memory */ /* allocate memory */
@ -64,27 +64,27 @@ int lzss_decompress(struct mspack_system *system,
/* loop forever; exit condition is in ENSURE_BYTES macro */ /* loop forever; exit condition is in ENSURE_BYTES macro */
for (;;) { for (;;) {
ENSURE_BYTES; c = *i_ptr++ ^ invert; ENSURE_BYTES; c = *i_ptr++ ^ invert;
for (i = 0x01; i & 0xFF; i <<= 1) { for (i = 0x01; i & 0xFF; i <<= 1) {
if (c & i) { if (c & i) {
/* literal */ /* literal */
ENSURE_BYTES; window[pos] = *i_ptr++; ENSURE_BYTES; window[pos] = *i_ptr++;
WRITE_BYTE; WRITE_BYTE;
pos++; pos &= LZSS_WINDOW_SIZE - 1; pos++; pos &= LZSS_WINDOW_SIZE - 1;
} }
else { else {
/* match */ /* match */
ENSURE_BYTES; mpos = *i_ptr++; ENSURE_BYTES; mpos = *i_ptr++;
ENSURE_BYTES; mpos |= (*i_ptr & 0xF0) << 4; ENSURE_BYTES; mpos |= (*i_ptr & 0xF0) << 4;
len = (*i_ptr++ & 0x0F) + 3; len = (*i_ptr++ & 0x0F) + 3;
while (len--) { while (len--) {
window[pos] = window[mpos]; window[pos] = window[mpos];
WRITE_BYTE; WRITE_BYTE;
pos++; pos &= LZSS_WINDOW_SIZE - 1; pos++; pos &= LZSS_WINDOW_SIZE - 1;
mpos++; mpos &= LZSS_WINDOW_SIZE - 1; mpos++; mpos &= LZSS_WINDOW_SIZE - 1;
} }
} }
} }
} }
/* not reached */ /* not reached */

View file

@ -1,5 +1,5 @@
/* This file is part of libmspack. /* 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 * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted
* by Microsoft Corporation. * by Microsoft Corporation.
@ -35,7 +35,7 @@ extern "C" {
/* LZX huffman defines: tweak tablebits as desired */ /* LZX huffman defines: tweak tablebits as desired */
#define LZX_PRETREE_MAXSYMBOLS (LZX_PRETREE_NUM_ELEMENTS) #define LZX_PRETREE_MAXSYMBOLS (LZX_PRETREE_NUM_ELEMENTS)
#define LZX_PRETREE_TABLEBITS (6) #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_MAINTREE_TABLEBITS (12)
#define LZX_LENGTH_MAXSYMBOLS (LZX_NUM_SECONDARY_LENGTHS+1) #define LZX_LENGTH_MAXSYMBOLS (LZX_NUM_SECONDARY_LENGTHS+1)
#define LZX_LENGTH_TABLEBITS (12) #define LZX_LENGTH_TABLEBITS (12)
@ -55,6 +55,8 @@ struct lzxd_stream {
unsigned char *window; /* decoding window */ unsigned char *window; /* decoding window */
unsigned int window_size; /* window size */ 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 window_posn; /* decompression offset within window */
unsigned int frame_posn; /* current frame offset within in window */ unsigned int frame_posn; /* current frame offset within in window */
unsigned int frame; /* the number of 32kb frames processed */ 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 intel_started; /* has intel E8 decoding started? */
unsigned char block_type; /* type of the current block */ unsigned char block_type; /* type of the current block */
unsigned char header_read; /* have we started decoding at all yet? */ 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 input_end; /* have we reached the end of input? */
unsigned char is_delta; /* does stream follow LZX DELTA spec? */
int error; int error;
@ -87,13 +89,13 @@ struct lzxd_stream {
/* huffman decoding tables */ /* huffman decoding tables */
unsigned short PRETREE_table [(1 << LZX_PRETREE_TABLEBITS) + unsigned short PRETREE_table [(1 << LZX_PRETREE_TABLEBITS) +
(LZX_PRETREE_MAXSYMBOLS * 2)]; (LZX_PRETREE_MAXSYMBOLS * 2)];
unsigned short MAINTREE_table[(1 << LZX_MAINTREE_TABLEBITS) + unsigned short MAINTREE_table[(1 << LZX_MAINTREE_TABLEBITS) +
(LZX_MAINTREE_MAXSYMBOLS * 2)]; (LZX_MAINTREE_MAXSYMBOLS * 2)];
unsigned short LENGTH_table [(1 << LZX_LENGTH_TABLEBITS) + unsigned short LENGTH_table [(1 << LZX_LENGTH_TABLEBITS) +
(LZX_LENGTH_MAXSYMBOLS * 2)]; (LZX_LENGTH_MAXSYMBOLS * 2)];
unsigned short ALIGNED_table [(1 << LZX_ALIGNED_TABLEBITS) + unsigned short ALIGNED_table [(1 << LZX_ALIGNED_TABLEBITS) +
(LZX_ALIGNED_MAXSYMBOLS * 2)]; (LZX_ALIGNED_MAXSYMBOLS * 2)];
unsigned char LENGTH_empty; unsigned char LENGTH_empty;
/* this is used purely for doing the intel E8 transform */ /* 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 input an input stream with the LZX data.
* @param output an output stream to write the decoded data to. * @param output an output stream to write the decoded data to.
* @param window_bits the size of the decoding window, which must be * @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 * @param reset_interval the interval at which the LZX bitstream is
* reset, in multiples of LZX frames (32678 * reset, in multiples of LZX frames (32678
* bytes), e.g. a value of 2 indicates the input * bytes), e.g. a value of 2 indicates the input
* stream resets after every 65536 output bytes. * 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. * resets, such as in CAB LZX streams.
* @param input_buffer_size the number of bytes to use as an input * @param input_buffer_size the number of bytes to use as an input
* bitstream buffer. * bitstream buffer.
@ -135,26 +139,49 @@ struct lzxd_stream {
* lzxd_set_output_length() once it is * lzxd_set_output_length() once it is
* known. If never set, 4 of the final 6 bytes * known. If never set, 4 of the final 6 bytes
* of the output stream may be incorrect. * 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 * @return a pointer to an initialised lzxd_stream structure, or NULL if
* there was not enough memory or parameters to the function were wrong. * there was not enough memory or parameters to the function were wrong.
*/ */
extern struct lzxd_stream *lzxd_init(struct mspack_system *system, extern struct lzxd_stream *lzxd_init(struct mspack_system *system,
struct mspack_file *input, struct mspack_file *input,
struct mspack_file *output, struct mspack_file *output,
int window_bits, int window_bits,
int reset_interval, int reset_interval,
int input_buffer_size, int input_buffer_size,
off_t output_length); off_t output_length,
char is_delta);
/* see description of output_length in lzxd_init() */ /* see description of output_length in lzxd_init() */
extern void lzxd_set_output_length(struct lzxd_stream *lzx, 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. * Decompresses entire or partial LZX streams.
* *
* The number of bytes of data that should be decompressed is given as the * 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. * will be kept over for a later invocation.
* *
* The output bytes will be passed to the system->write() function given in * The output bytes will be passed to the system->write() function given in

View file

@ -1,5 +1,5 @@
/* This file is part of libmspack. /* 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 * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted
* by Microsoft Corporation. * by Microsoft Corporation.
@ -70,6 +70,10 @@
* The maximum window size has increased from 2MB to 32MB. This also * The maximum window size has increased from 2MB to 32MB. This also
* increases the maximum number of position slots, etc. * 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. * The format now allows for "reference data", supplied by the caller.
* If match offsets go further back than the number of bytes * If match offsets go further back than the number of bytes
* decompressed so far, that is them accessing the reference data. * decompressed so far, that is them accessing the reference data.
@ -79,11 +83,11 @@
#define BITS_TYPE struct lzxd_stream #define BITS_TYPE struct lzxd_stream
#define BITS_VAR lzx #define BITS_VAR lzx
#define BITS_ORDER_MSB #define BITS_ORDER_MSB
#define READ_BYTES do { \ #define READ_BYTES do { \
unsigned char b0, b1; \ unsigned char b0, b1; \
READ_IF_NEEDED; b0 = *i_ptr++; \ READ_IF_NEEDED; b0 = *i_ptr++; \
READ_IF_NEEDED; b1 = *i_ptr++; \ READ_IF_NEEDED; b1 = *i_ptr++; \
INJECT_BITS((b1 << 8) | b0, 16); \ INJECT_BITS((b1 << 8) | b0, 16); \
} while (0) } while (0)
#include "readbits.h" #include "readbits.h"
@ -96,43 +100,43 @@
#include "readhuff.h" #include "readhuff.h"
/* BUILD_TABLE(tbl) builds a huffman lookup table from code lengths */ /* BUILD_TABLE(tbl) builds a huffman lookup table from code lengths */
#define BUILD_TABLE(tbl) \ #define BUILD_TABLE(tbl) \
if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \
&HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \
{ \ { \
D(("failed to build %s table", #tbl)) \ D(("failed to build %s table", #tbl)) \
return lzx->error = MSPACK_ERR_DECRUNCH; \ return lzx->error = MSPACK_ERR_DECRUNCH; \
} }
#define BUILD_TABLE_MAYBE_EMPTY(tbl) do { \ #define BUILD_TABLE_MAYBE_EMPTY(tbl) do { \
lzx->tbl##_empty = 0; \ lzx->tbl##_empty = 0; \
if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \
&HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \
{ \ { \
for (i = 0; i < MAXSYMBOLS(tbl); i++) { \ for (i = 0; i < MAXSYMBOLS(tbl); i++) { \
if (HUFF_LEN(tbl, i) > 0) { \ if (HUFF_LEN(tbl, i) > 0) { \
D(("failed to build %s table", #tbl)) \ D(("failed to build %s table", #tbl)) \
return lzx->error = MSPACK_ERR_DECRUNCH; \ return lzx->error = MSPACK_ERR_DECRUNCH; \
} \ } \
} \ } \
/* empty tree - allow it, but don't decode symbols with it */ \ /* empty tree - allow it, but don't decode symbols with it */ \
lzx->tbl##_empty = 1; \ lzx->tbl##_empty = 1; \
} \ } \
} while (0) } while (0)
/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols /* 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 * first to last in the given table. The code lengths are stored in their
* own special LZX way. * own special LZX way.
*/ */
#define READ_LENGTHS(tbl, first, last) do { \ #define READ_LENGTHS(tbl, first, last) do { \
STORE_BITS; \ STORE_BITS; \
if (lzxd_read_lens(lzx, &HUFF_LEN(tbl, 0), (first), \ if (lzxd_read_lens(lzx, &HUFF_LEN(tbl, 0), (first), \
(unsigned int)(last))) return lzx->error; \ (unsigned int)(last))) return lzx->error; \
RESTORE_BITS; \ RESTORE_BITS; \
} while (0) } while (0)
static int lzxd_read_lens(struct lzxd_stream *lzx, unsigned char *lens, 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 */ /* bit buffer and huffman symbol decode variables */
register unsigned int bit_buffer; 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 * a small 'position slot' number and a small offset from that slot are
* encoded instead of one large offset. * 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 * position_base[] is an index to the position slot bases
* *
* extra_bits[] states how many bits of offset-from-base data is needed. * extra_bits[] states how many bits of offset-from-base data is needed.
* *
* They are generated like so: * They are calculated as follows:
* for (i = 0; i < 4; i++) extra_bits[i] = 0; * extra_bits[i] = 0 where i < 4
* for (i = 4, j = 0; i < 36; i+=2) extra_bits[i] = extra_bits[i+1] = j++; * extra_bits[i] = floor(i/2)-1 where i >= 4 && i < 36
* for (i = 36; i < 51; i++) extra_bits[i] = 17; * extra_bits[i] = 17 where i >= 36
* for (i = 0, j = 0; i < 51; j += 1 << extra_bits[i++]) position_base[i] = j; * position_base[0] = 0
* position_base[i] = position_base[i-1] + (1 << extra_bits[i-1])
*/ */
static const unsigned int position_base[51] = { static const unsigned int position_slots[11] = {
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290
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 char extra_bits[51] = { 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, 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, 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) { 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 --------*/ /*-------- main LZX code --------*/
struct lzxd_stream *lzxd_init(struct mspack_system *system, struct lzxd_stream *lzxd_init(struct mspack_system *system,
struct mspack_file *input, struct mspack_file *input,
struct mspack_file *output, struct mspack_file *output,
int window_bits, int window_bits,
int reset_interval, int reset_interval,
int input_buffer_size, int input_buffer_size,
off_t output_length) off_t output_length,
char is_delta)
{ {
unsigned int window_size = 1 << window_bits; unsigned int window_size = 1 << window_bits;
struct lzxd_stream *lzx; struct lzxd_stream *lzx;
if (!system) return NULL; if (!system) return NULL;
/* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */ /* LZX DELTA window sizes are between 2^17 (128KiB) and 2^25 (32MiB),
if (window_bits < 15 || window_bits > 21) return NULL; * 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; input_buffer_size = (input_buffer_size + 1) & -2;
if (!input_buffer_size) return NULL; if (input_buffer_size < 2) return NULL;
/* allocate decompression state */ /* allocate decompression state */
if (!(lzx = (struct lzxd_stream *) system->alloc(system, sizeof(struct lzxd_stream)))) { 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->inbuf_size = input_buffer_size;
lzx->window_size = 1 << window_bits; lzx->window_size = 1 << window_bits;
lzx->ref_data_size = 0;
lzx->window_posn = 0; lzx->window_posn = 0;
lzx->frame_posn = 0; lzx->frame_posn = 0;
lzx->frame = 0; lzx->frame = 0;
@ -280,11 +342,8 @@ struct lzxd_stream *lzxd_init(struct mspack_system *system,
lzx->intel_curpos = 0; lzx->intel_curpos = 0;
lzx->intel_started = 0; lzx->intel_started = 0;
lzx->error = MSPACK_ERR_OK; lzx->error = MSPACK_ERR_OK;
lzx->num_offsets = position_slots[window_bits - 15] << 3;
/* window bits: 15 16 17 18 19 20 21 lzx->is_delta = is_delta;
* position slots: 30 32 34 36 38 42 50 */
lzx->posn_slots = ((window_bits == 21) ? 50 :
((window_bits == 20) ? 42 : (window_bits << 1)));
lzx->o_ptr = lzx->o_end = &lzx->e8_buf[0]; lzx->o_ptr = lzx->o_end = &lzx->e8_buf[0];
lzxd_reset_state(lzx); lzxd_reset_state(lzx);
@ -292,8 +351,44 @@ struct lzxd_stream *lzxd_init(struct mspack_system *system,
return lzx; 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) { 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) { 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; register unsigned short sym;
int match_length, length_footer, extra, verbatim_bits, bytes_todo; 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 char *window, *runsrc, *rundest, buf[12];
unsigned int frame_size=0, end_frame, match_offset, window_posn; unsigned int frame_size=0, end_frame, match_offset, window_posn;
unsigned int R0, R1, R2; 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?) */ /* have we reached the reset interval? (if there is one?) */
if (lzx->reset_interval && ((lzx->frame % lzx->reset_interval) == 0)) { if (lzx->reset_interval && ((lzx->frame % lzx->reset_interval) == 0)) {
if (lzx->block_remaining) { if (lzx->block_remaining) {
D(("%d bytes remaining at reset interval", lzx->block_remaining)) /* this is a file format error, we can make a best effort to extract what we can */
return lzx->error = MSPACK_ERR_DECRUNCH; 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 */ /* 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; 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 */ /* read header if necessary */
if (!lzx->header_read) { if (!lzx->header_read) {
/* read 1 bit. if bit=0, intel filesize = 0. /* 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) { while (bytes_todo > 0) {
/* initialise new block, if one is needed */ /* initialise new block, if one is needed */
if (lzx->block_remaining == 0) { if (lzx->block_remaining == 0) {
/* realign if previous block was an odd-sized UNCOMPRESSED block */ /* realign if previous block was an odd-sized UNCOMPRESSED block */
if ((lzx->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) && if ((lzx->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) &&
(lzx->block_length & 1)) (lzx->block_length & 1))
{ {
READ_IF_NEEDED; READ_IF_NEEDED;
i_ptr++; i_ptr++;
} }
/* read block type (3 bits) and block length (24 bits) */ /* read block type (3 bits) and block length (24 bits) */
READ_BITS(lzx->block_type, 3); READ_BITS(lzx->block_type, 3);
READ_BITS(i, 16); READ_BITS(j, 8); READ_BITS(i, 16); READ_BITS(j, 8);
lzx->block_remaining = lzx->block_length = (i << 8) | j; lzx->block_remaining = lzx->block_length = (i << 8) | j;
/*D(("new block t%d len %u", lzx->block_type, lzx->block_length))*/ /*D(("new block t%d len %u", lzx->block_type, lzx->block_length))*/
/* read individual block headers */ /* read individual block headers */
switch (lzx->block_type) { switch (lzx->block_type) {
case LZX_BLOCKTYPE_ALIGNED: case LZX_BLOCKTYPE_ALIGNED:
/* read lengths of and build aligned huffman decoding tree */ /* read lengths of and build aligned huffman decoding tree */
for (i = 0; i < 8; i++) { READ_BITS(j, 3); lzx->ALIGNED_len[i] = j; } for (i = 0; i < 8; i++) { READ_BITS(j, 3); lzx->ALIGNED_len[i] = j; }
BUILD_TABLE(ALIGNED); BUILD_TABLE(ALIGNED);
/* no break -- rest of aligned header is same as verbatim */ /* rest of aligned header is same as verbatim */ /*@fallthrough@*/
case LZX_BLOCKTYPE_VERBATIM: case LZX_BLOCKTYPE_VERBATIM:
/* read lengths of and build main huffman decoding tree */ /* read lengths of and build main huffman decoding tree */
READ_LENGTHS(MAINTREE, 0, 256); READ_LENGTHS(MAINTREE, 0, 256);
READ_LENGTHS(MAINTREE, 256, LZX_NUM_CHARS + (lzx->posn_slots << 3)); READ_LENGTHS(MAINTREE, 256, LZX_NUM_CHARS + lzx->num_offsets);
BUILD_TABLE(MAINTREE); BUILD_TABLE(MAINTREE);
/* if the literal 0xE8 is anywhere in the block... */ /* if the literal 0xE8 is anywhere in the block... */
if (lzx->MAINTREE_len[0xE8] != 0) lzx->intel_started = 1; if (lzx->MAINTREE_len[0xE8] != 0) lzx->intel_started = 1;
/* read lengths of and build lengths huffman decoding tree */ /* read lengths of and build lengths huffman decoding tree */
READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS); READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS);
BUILD_TABLE_MAYBE_EMPTY(LENGTH); BUILD_TABLE_MAYBE_EMPTY(LENGTH);
break; break;
case LZX_BLOCKTYPE_UNCOMPRESSED: case LZX_BLOCKTYPE_UNCOMPRESSED:
/* because we can't assume otherwise */ /* because we can't assume otherwise */
lzx->intel_started = 1; lzx->intel_started = 1;
/* read 1-16 (not 0-15) bits to align to bytes */ /* read 1-16 (not 0-15) bits to align to bytes */
ENSURE_BITS(16); if (bits_left == 0) ENSURE_BITS(16);
if (bits_left > 16) i_ptr -= 2; bits_left = 0; bit_buffer = 0;
bits_left = 0; bit_buffer = 0;
/* read 12 bytes of stored R0 / R1 / R2 values */ /* read 12 bytes of stored R0 / R1 / R2 values */
for (rundest = &buf[0], i = 0; i < 12; i++) { for (rundest = &buf[0], i = 0; i < 12; i++) {
READ_IF_NEEDED; READ_IF_NEEDED;
*rundest++ = *i_ptr++; *rundest++ = *i_ptr++;
} }
R0 = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); R0 = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
R1 = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 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); R2 = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24);
break; break;
default: default:
D(("bad block type")) D(("bad block type"))
return lzx->error = MSPACK_ERR_DECRUNCH; return lzx->error = MSPACK_ERR_DECRUNCH;
} }
} }
/* decode more of the block: /* 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 */ /* decode at least this_run bytes */
switch (lzx->block_type) { switch (lzx->block_type) {
case LZX_BLOCKTYPE_VERBATIM: case LZX_BLOCKTYPE_VERBATIM:
while (this_run > 0) { while (this_run > 0) {
READ_HUFFSYM(MAINTREE, main_element); READ_HUFFSYM(MAINTREE, main_element);
if (main_element < LZX_NUM_CHARS) { if (main_element < LZX_NUM_CHARS) {
/* literal: 0 to LZX_NUM_CHARS-1 */ /* literal: 0 to LZX_NUM_CHARS-1 */
window[window_posn++] = main_element; window[window_posn++] = main_element;
this_run--; this_run--;
} }
else { else {
/* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
main_element -= LZX_NUM_CHARS; main_element -= LZX_NUM_CHARS;
/* get match length */ /* get match length */
match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
if (match_length == LZX_NUM_PRIMARY_LENGTHS) { if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
if (lzx->LENGTH_empty) { if (lzx->LENGTH_empty) {
D(("LENGTH symbol needed but tree is empty")) D(("LENGTH symbol needed but tree is empty"))
return lzx->error = MSPACK_ERR_DECRUNCH; return lzx->error = MSPACK_ERR_DECRUNCH;
} }
READ_HUFFSYM(LENGTH, length_footer); READ_HUFFSYM(LENGTH, length_footer);
match_length += length_footer; match_length += length_footer;
} }
match_length += LZX_MIN_MATCH; 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) { /* get match offset */
D(("match ran over window wrap")) switch ((match_offset = (main_element >> 3))) {
return lzx->error = MSPACK_ERR_DECRUNCH; 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;
/* copy match */ case 3: match_offset = 1; R2=R1; R1=R0; R0 = match_offset; break;
rundest = &window[window_posn]; default:
i = match_length; extra = (match_offset >= 36) ? 17 : extra_bits[match_offset];
/* does match offset wrap the window? */ READ_BITS(verbatim_bits, extra);
if (match_offset > window_posn) { match_offset = position_base[match_offset] - 2 + verbatim_bits;
/* j = length from match offset to end of window */ R2 = R1; R1 = R0; R0 = match_offset;
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; /* LZX DELTA uses max match length to signal even longer match */
window_posn += match_length; if (match_length == LZX_MAX_MATCH && lzx->is_delta) {
} int extra_len = 0;
} /* while (this_run > 0) */ ENSURE_BITS(3); /* 4 entry huffman tree */
break; 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: case LZX_BLOCKTYPE_ALIGNED:
while (this_run > 0) { while (this_run > 0) {
READ_HUFFSYM(MAINTREE, main_element); READ_HUFFSYM(MAINTREE, main_element);
if (main_element < LZX_NUM_CHARS) { if (main_element < LZX_NUM_CHARS) {
/* literal: 0 to LZX_NUM_CHARS-1 */ /* literal: 0 to LZX_NUM_CHARS-1 */
window[window_posn++] = main_element; window[window_posn++] = main_element;
this_run--; this_run--;
} }
else { else {
/* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
main_element -= LZX_NUM_CHARS; main_element -= LZX_NUM_CHARS;
/* get match length */ /* get match length */
match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
if (match_length == LZX_NUM_PRIMARY_LENGTHS) { if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
if (lzx->LENGTH_empty) { if (lzx->LENGTH_empty) {
D(("LENGTH symbol needed but tree is empty")) D(("LENGTH symbol needed but tree is empty"))
return lzx->error = MSPACK_ERR_DECRUNCH; return lzx->error = MSPACK_ERR_DECRUNCH;
} }
READ_HUFFSYM(LENGTH, length_footer); READ_HUFFSYM(LENGTH, length_footer);
match_length += length_footer; match_length += length_footer;
} }
match_length += LZX_MIN_MATCH; match_length += LZX_MIN_MATCH;
/* get match offset */ /* get match offset */
switch ((match_offset = (main_element >> 3))) { switch ((match_offset = (main_element >> 3))) {
case 0: match_offset = R0; break; case 0: match_offset = R0; break;
case 1: match_offset = R1; R1 = R0; R0 = match_offset; break; case 1: match_offset = R1; R1 = R0; R0 = match_offset; break;
case 2: match_offset = R2; R2 = R0; R0 = match_offset; break; case 2: match_offset = R2; R2 = R0; R0 = match_offset; break;
default: default:
extra = extra_bits[match_offset]; extra = (match_offset >= 36) ? 17 : extra_bits[match_offset];
match_offset = position_base[match_offset] - 2; match_offset = position_base[match_offset] - 2;
if (extra > 3) { if (extra > 3) {
/* verbatim and aligned bits */ /* verbatim and aligned bits */
extra -= 3; extra -= 3;
READ_BITS(verbatim_bits, extra); READ_BITS(verbatim_bits, extra);
match_offset += (verbatim_bits << 3); match_offset += (verbatim_bits << 3);
READ_HUFFSYM(ALIGNED, aligned_bits); READ_HUFFSYM(ALIGNED, aligned_bits);
match_offset += aligned_bits; match_offset += aligned_bits;
} }
else if (extra == 3) { else if (extra == 3) {
/* aligned bits only */ /* aligned bits only */
READ_HUFFSYM(ALIGNED, aligned_bits); READ_HUFFSYM(ALIGNED, aligned_bits);
match_offset += aligned_bits; match_offset += aligned_bits;
} }
else if (extra > 0) { /* extra==1, extra==2 */ else if (extra > 0) { /* extra==1, extra==2 */
/* verbatim bits only */ /* verbatim bits only */
READ_BITS(verbatim_bits, extra); READ_BITS(verbatim_bits, extra);
match_offset += verbatim_bits; match_offset += verbatim_bits;
} }
else /* extra == 0 */ { else /* extra == 0 */ {
/* ??? not defined in LZX specification! */ /* ??? not defined in LZX specification! */
match_offset = 1; match_offset = 1;
} }
/* update repeated offset LRU queue */ /* update repeated offset LRU queue */
R2 = R1; R1 = R0; R0 = match_offset; R2 = R1; R1 = R0; R0 = match_offset;
} }
if ((window_posn + match_length) > lzx->window_size) { /* LZX DELTA uses max match length to signal even longer match */
D(("match ran over window wrap")) if (match_length == LZX_MAX_MATCH && lzx->is_delta) {
return lzx->error = MSPACK_ERR_DECRUNCH; 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;
}
/* copy match */ if ((window_posn + match_length) > lzx->window_size) {
rundest = &window[window_posn]; D(("match ran over window wrap"))
i = match_length; return lzx->error = MSPACK_ERR_DECRUNCH;
/* 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; /* copy match */
window_posn += match_length; rundest = &window[window_posn];
} i = match_length;
} /* while (this_run > 0) */ /* does match offset wrap the window? */
break; 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: case LZX_BLOCKTYPE_UNCOMPRESSED:
/* as this_run is limited not to wrap a frame, this also means it /* 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) */ * won't wrap the window (as the window is a multiple of 32k) */
rundest = &window[window_posn]; rundest = &window[window_posn];
window_posn += this_run; window_posn += this_run;
while (this_run > 0) { while (this_run > 0) {
if ((i = i_end - i_ptr) == 0) { if ((i = i_end - i_ptr) == 0) {
READ_IF_NEEDED; READ_IF_NEEDED;
} }
else { else {
if (i > this_run) i = this_run; if (i > this_run) i = this_run;
lzx->sys->copy(i_ptr, rundest, (size_t) i); lzx->sys->copy(i_ptr, rundest, (size_t) i);
rundest += i; rundest += i;
i_ptr += i; i_ptr += i;
this_run -= i; this_run -= i;
} }
} }
break; break;
default: 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? */ /* did the final match overrun our desired this_run length? */
if (this_run < 0) { if (this_run < 0) {
if ((unsigned int)(-this_run) > lzx->block_remaining) { if ((unsigned int)(-this_run) > lzx->block_remaining) {
D(("overrun went past end of block by %d (%d remaining)", D(("overrun went past end of block by %d (%d remaining)",
-this_run, lzx->block_remaining )) -this_run, lzx->block_remaining ))
return lzx->error = MSPACK_ERR_DECRUNCH; return lzx->error = MSPACK_ERR_DECRUNCH;
} }
lzx->block_remaining -= -this_run; lzx->block_remaining -= -this_run;
} }
} /* while (bytes_todo > 0) */ } /* while (bytes_todo > 0) */
/* streams don't extend over frame boundaries */ /* streams don't extend over frame boundaries */
if ((window_posn - lzx->frame_posn) != frame_size) { if ((window_posn - lzx->frame_posn) != frame_size) {
D(("decode beyond output frame limits! %d != %d", 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; 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 */ /* check that we've used all of the previous frame first */
if (lzx->o_ptr != lzx->o_end) { 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; return lzx->error = MSPACK_ERR_DECRUNCH;
} }
/* does this intel block _really_ need decoding? */ /* does this intel block _really_ need decoding? */
if (lzx->intel_started && lzx->intel_filesize && 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 *data = &lzx->e8_buf[0];
unsigned char *dataend = &lzx->e8_buf[frame_size - 10]; 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); lzx->sys->copy(&lzx->window[lzx->frame_posn], data, frame_size);
while (data < dataend) { while (data < dataend) {
if (*data++ != 0xE8) { curpos++; continue; } if (*data++ != 0xE8) { curpos++; continue; }
abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
if ((abs_off >= -curpos) && (abs_off < filesize)) { if ((abs_off >= -curpos) && (abs_off < filesize)) {
rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
data[0] = (unsigned char) rel_off; data[0] = (unsigned char) rel_off;
data[1] = (unsigned char) (rel_off >> 8); data[1] = (unsigned char) (rel_off >> 8);
data[2] = (unsigned char) (rel_off >> 16); data[2] = (unsigned char) (rel_off >> 16);
data[3] = (unsigned char) (rel_off >> 24); data[3] = (unsigned char) (rel_off >> 24);
} }
data += 4; data += 4;
curpos += 5; curpos += 5;
} }
lzx->intel_curpos += frame_size; lzx->intel_curpos += frame_size;
} }

View file

@ -1,5 +1,5 @@
/* libmspack -- a library for working with Microsoft compression formats. /* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2011 Stuart Caie <kyzer@4u.net> * (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
* *
* libmspack is free software; you can redistribute it and/or modify it under * 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 * 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 * - .CAB (MS Cabinet) files, which use deflate, LZX or Quantum compression
* - .CHM (HTML Help) files, which use LZX compression * - .CHM (HTML Help) files, which use LZX compression
* - .LIT (MS EBook) files, which use LZX compression and DES encryption * - .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 * To determine the capabilities of the library, and the binary
* compatibility version of any particular compressor or decompressor, use * compatibility version of any particular compressor or decompressor, use
@ -60,6 +61,7 @@
* - mspack_create_hlp_compressor() creates a mshlp_compressor * - mspack_create_hlp_compressor() creates a mshlp_compressor
* - mspack_create_szdd_compressor() creates a msszdd_compressor * - mspack_create_szdd_compressor() creates a msszdd_compressor
* - mspack_create_kwaj_compressor() creates a mskwaj_compressor * - mspack_create_kwaj_compressor() creates a mskwaj_compressor
* - mspack_create_oab_compressor() creates a msoab_compressor
* *
* For decompression: * For decompression:
* - mspack_create_cab_decompressor() creates a mscab_decompressor * - mspack_create_cab_decompressor() creates a mscab_decompressor
@ -68,6 +70,7 @@
* - mspack_create_hlp_decompressor() creates a mshlp_decompressor * - mspack_create_hlp_decompressor() creates a mshlp_decompressor
* - mspack_create_szdd_decompressor() creates a msszdd_decompressor * - mspack_create_szdd_decompressor() creates a msszdd_decompressor
* - mspack_create_kwaj_decompressor() creates a mskwaj_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 * Once finished working with a format, each kind of
* compressor/decompressor has its own specific destructor: * compressor/decompressor has its own specific destructor:
@ -83,6 +86,8 @@
* - mspack_destroy_szdd_decompressor() * - mspack_destroy_szdd_decompressor()
* - mspack_destroy_kwaj_compressor() * - mspack_destroy_kwaj_compressor()
* - mspack_destroy_kwaj_decompressor() * - mspack_destroy_kwaj_decompressor()
* - mspack_destroy_oab_compressor()
* - mspack_destroy_oab_decompressor()
* *
* Destroying a compressor or decompressor does not destroy any objects, * Destroying a compressor or decompressor does not destroy any objects,
* structures or handles that have been created using that compressor or * 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_MSSZDDC: the msszdd_compressor interface
* - #MSPACK_VER_MSKWAJD: the mskwaj_decompressor interface * - #MSPACK_VER_MSKWAJD: the mskwaj_decompressor interface
* - #MSPACK_VER_MSKWAJC: the mskwaj_compressor 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: * The result of the function should be interpreted as follows:
* - -1: this interface is completely unknown to the library * - -1: this interface is completely unknown to the library
@ -249,6 +256,10 @@ extern int mspack_version(int entity);
#define MSPACK_VER_MSKWAJD (12) #define MSPACK_VER_MSKWAJD (12)
/** Pass to mspack_version() to get the mskwaj_compressor version */ /** Pass to mspack_version() to get the mskwaj_compressor version */
#define MSPACK_VER_MSKWAJC (13) #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 ------------------------------------------------ */ /* --- file I/O abstraction ------------------------------------------------ */
@ -297,8 +308,8 @@ struct mspack_system {
* @see close(), read(), write(), seek(), tell(), message() * @see close(), read(), write(), seek(), tell(), message()
*/ */
struct mspack_file * (*open)(struct mspack_system *self, struct mspack_file * (*open)(struct mspack_system *self,
const char *filename, const char *filename,
int mode); int mode);
/** /**
* Closes a previously opened file. If any memory was allocated for this * 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. * @param bytes the number of bytes to read from the file.
* @return the number of bytes successfully read (this can be less than * @return the number of bytes successfully read (this can be less than
* the number requested), zero to mark the end of file, or less * 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() * @see open(), write()
*/ */
int (*read)(struct mspack_file *file, int (*read)(struct mspack_file *file,
void *buffer, void *buffer,
int bytes); int bytes);
/** /**
* Writes a given number of bytes to an open file. * Writes a given number of bytes to an open file.
@ -338,8 +351,8 @@ struct mspack_system {
* @see open(), read() * @see open(), read()
*/ */
int (*write)(struct mspack_file *file, int (*write)(struct mspack_file *file,
void *buffer, void *buffer,
int bytes); int bytes);
/** /**
* Seeks to a specific file offset within an open file. * Seeks to a specific file offset within an open file.
@ -365,8 +378,8 @@ struct mspack_system {
* @see open(), tell() * @see open(), tell()
*/ */
int (*seek)(struct mspack_file *file, int (*seek)(struct mspack_file *file,
off_t offset, off_t offset,
int mode); int mode);
/** /**
* Returns the current file position (in bytes) of the given file. * Returns the current file position (in bytes) of the given file.
@ -392,8 +405,8 @@ struct mspack_system {
* @see open() * @see open()
*/ */
void (*message)(struct mspack_file *file, void (*message)(struct mspack_file *file,
const char *format, const char *format,
...); ...);
/** /**
* Allocates memory. * Allocates memory.
@ -406,12 +419,12 @@ struct mspack_system {
* @see free() * @see free()
*/ */
void * (*alloc)(struct mspack_system *self, void * (*alloc)(struct mspack_system *self,
size_t bytes); size_t bytes);
/** /**
* Frees memory. * Frees memory.
* *
* @param ptr the memory to be freed. * @param ptr the memory to be freed. NULL is accepted and ignored.
* @see alloc() * @see alloc()
*/ */
void (*free)(void *ptr); void (*free)(void *ptr);
@ -429,8 +442,8 @@ struct mspack_system {
* @param bytes the size of the memory region, in bytes * @param bytes the size of the memory region, in bytes
*/ */
void (*copy)(void *src, void (*copy)(void *src,
void *dest, void *dest,
size_t bytes); size_t bytes);
/** /**
* A null pointer to mark the end of mspack_system. It must equal NULL. * 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); 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 --------------------------- */ /* --- support for .CAB (MS Cabinet) file format --------------------------- */
/** /**
@ -896,6 +934,13 @@ struct mscabd_file {
#define MSCABD_PARAM_FIXMSZIP (1) #define MSCABD_PARAM_FIXMSZIP (1)
/** mscab_decompressor::set_param() parameter: size of decompression buffer */ /** mscab_decompressor::set_param() parameter: size of decompression buffer */
#define MSCABD_PARAM_DECOMPBUF (2) #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 */ /** TODO */
struct mscab_compressor { struct mscab_compressor {
@ -931,7 +976,7 @@ struct mscab_decompressor {
* @see close(), search(), last_error() * @see close(), search(), last_error()
*/ */
struct mscabd_cabinet * (*open) (struct mscab_decompressor *self, struct mscabd_cabinet * (*open) (struct mscab_decompressor *self,
const char *filename); const char *filename);
/** /**
* Closes a previously opened cabinet or cabinet set. * Closes a previously opened cabinet or cabinet set.
@ -963,7 +1008,7 @@ struct mscab_decompressor {
* @see open(), search(), append(), prepend() * @see open(), search(), append(), prepend()
*/ */
void (*close)(struct mscab_decompressor *self, void (*close)(struct mscab_decompressor *self,
struct mscabd_cabinet *cab); struct mscabd_cabinet *cab);
/** /**
* Searches a regular file for embedded cabinets. * Searches a regular file for embedded cabinets.
@ -1000,7 +1045,7 @@ struct mscab_decompressor {
* @see close(), open(), last_error() * @see close(), open(), last_error()
*/ */
struct mscabd_cabinet * (*search) (struct mscab_decompressor *self, 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 * Appends one mscabd_cabinet to another, forming or extending a cabinet
@ -1043,8 +1088,8 @@ struct mscab_decompressor {
* @see prepend(), open(), close() * @see prepend(), open(), close()
*/ */
int (*append) (struct mscab_decompressor *self, int (*append) (struct mscab_decompressor *self,
struct mscabd_cabinet *cab, struct mscabd_cabinet *cab,
struct mscabd_cabinet *nextcab); struct mscabd_cabinet *nextcab);
/** /**
* Prepends one mscabd_cabinet to another, forming or extending a * Prepends one mscabd_cabinet to another, forming or extending a
@ -1065,8 +1110,8 @@ struct mscab_decompressor {
* @see append(), open(), close() * @see append(), open(), close()
*/ */
int (*prepend) (struct mscab_decompressor *self, int (*prepend) (struct mscab_decompressor *self,
struct mscabd_cabinet *cab, struct mscabd_cabinet *cab,
struct mscabd_cabinet *prevcab); struct mscabd_cabinet *prevcab);
/** /**
* Extracts a file from a cabinet or cabinet set. * 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 * @return an error code, or MSPACK_ERR_OK if successful
*/ */
int (*extract)(struct mscab_decompressor *self, int (*extract)(struct mscab_decompressor *self,
struct mscabd_file *file, struct mscabd_file *file,
const char *filename); const char *filename);
/** /**
* Sets a CAB decompression engine parameter. * Sets a CAB decompression engine parameter.
@ -1117,8 +1162,8 @@ struct mscab_decompressor {
* @see search(), extract() * @see search(), extract()
*/ */
int (*set_param)(struct mscab_decompressor *self, int (*set_param)(struct mscab_decompressor *self,
int param, int param,
int value); int value);
/** /**
* Returns the error code set by the most recently called method. * Returns the error code set by the most recently called method.
@ -1403,8 +1448,8 @@ struct mschm_compressor {
* @see use_temporary_file() set_param() * @see use_temporary_file() set_param()
*/ */
int (*generate)(struct mschm_compressor *self, int (*generate)(struct mschm_compressor *self,
struct mschmc_file file_list[], struct mschmc_file file_list[],
const char *output_file); const char *output_file);
/** /**
* Specifies whether a temporary file is used during CHM generation. * Specifies whether a temporary file is used during CHM generation.
@ -1460,8 +1505,8 @@ struct mschm_compressor {
* @see generate() * @see generate()
*/ */
int (*use_temporary_file)(struct mschm_compressor *self, int (*use_temporary_file)(struct mschm_compressor *self,
int use_temp_file, int use_temp_file,
const char *temp_file); const char *temp_file);
/** /**
* Sets a CHM compression engine parameter. * Sets a CHM compression engine parameter.
* *
@ -1508,8 +1553,8 @@ struct mschm_compressor {
* @see generate() * @see generate()
*/ */
int (*set_param)(struct mschm_compressor *self, int (*set_param)(struct mschm_compressor *self,
int param, int param,
unsigned int value); int value);
/** /**
* Returns the error code set by the most recently called method. * Returns the error code set by the most recently called method.
@ -1551,7 +1596,7 @@ struct mschm_decompressor {
* @see close() * @see close()
*/ */
struct mschmd_header *(*open)(struct mschm_decompressor *self, struct mschmd_header *(*open)(struct mschm_decompressor *self,
const char *filename); const char *filename);
/** /**
* Closes a previously opened CHM helpfile. * Closes a previously opened CHM helpfile.
@ -1571,7 +1616,7 @@ struct mschm_decompressor {
* @see open(), fast_open() * @see open(), fast_open()
*/ */
void (*close)(struct mschm_decompressor *self, void (*close)(struct mschm_decompressor *self,
struct mschmd_header *chm); struct mschmd_header *chm);
/** /**
* Extracts a file from a CHM helpfile. * Extracts a file from a CHM helpfile.
@ -1592,8 +1637,8 @@ struct mschm_decompressor {
* @return an error code, or MSPACK_ERR_OK if successful * @return an error code, or MSPACK_ERR_OK if successful
*/ */
int (*extract)(struct mschm_decompressor *self, int (*extract)(struct mschm_decompressor *self,
struct mschmd_file *file, struct mschmd_file *file,
const char *filename); const char *filename);
/** /**
* Returns the error code set by the most recently called method. * Returns the error code set by the most recently called method.
@ -1631,7 +1676,7 @@ struct mschm_decompressor {
* @see open(), close(), fast_find(), extract() * @see open(), close(), fast_find(), extract()
*/ */
struct mschmd_header *(*fast_open)(struct mschm_decompressor *self, struct mschmd_header *(*fast_open)(struct mschm_decompressor *self,
const char *filename); const char *filename);
/** /**
* Finds file details quickly. * Finds file details quickly.
@ -1672,10 +1717,10 @@ struct mschm_decompressor {
* @see open(), close(), fast_find(), extract() * @see open(), close(), fast_find(), extract()
*/ */
int (*fast_find)(struct mschm_decompressor *self, int (*fast_find)(struct mschm_decompressor *self,
struct mschmd_header *chm, struct mschmd_header *chm,
const char *filename, const char *filename,
struct mschmd_file *f_ptr, struct mschmd_file *f_ptr,
int f_size); int f_size);
}; };
/* --- support for .LIT (EBook) file format -------------------------------- */ /* --- support for .LIT (EBook) file format -------------------------------- */
@ -1781,9 +1826,9 @@ struct msszdd_compressor {
* @see set_param() * @see set_param()
*/ */
int (*compress)(struct msszdd_compressor *self, int (*compress)(struct msszdd_compressor *self,
const char *input, const char *input,
const char *output, const char *output,
off_t length); off_t length);
/** /**
* Sets an SZDD compression engine parameter. * Sets an SZDD compression engine parameter.
@ -1807,8 +1852,8 @@ struct msszdd_compressor {
* @see compress() * @see compress()
*/ */
int (*set_param)(struct msszdd_compressor *self, int (*set_param)(struct msszdd_compressor *self,
int param, int param,
unsigned int value); int value);
/** /**
* Returns the error code set by the most recently called method. * Returns the error code set by the most recently called method.
@ -1849,7 +1894,7 @@ struct msszdd_decompressor {
* @see close() * @see close()
*/ */
struct msszddd_header *(*open)(struct msszdd_decompressor *self, struct msszddd_header *(*open)(struct msszdd_decompressor *self,
const char *filename); const char *filename);
/** /**
* Closes a previously opened SZDD file. * Closes a previously opened SZDD file.
@ -1865,7 +1910,7 @@ struct msszdd_decompressor {
* @see open() * @see open()
*/ */
void (*close)(struct msszdd_decompressor *self, void (*close)(struct msszdd_decompressor *self,
struct msszddd_header *szdd); struct msszddd_header *szdd);
/** /**
* Extracts the compressed data from a SZDD file. * 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 * @return an error code, or MSPACK_ERR_OK if successful
*/ */
int (*extract)(struct msszdd_decompressor *self, int (*extract)(struct msszdd_decompressor *self,
struct msszddd_header *szdd, struct msszddd_header *szdd,
const char *filename); const char *filename);
/** /**
* Decompresses an SZDD file to an output file in one step. * 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 * @return an error code, or MSPACK_ERR_OK if successful
*/ */
int (*decompress)(struct msszdd_decompressor *self, int (*decompress)(struct msszdd_decompressor *self,
const char *input, const char *input,
const char *output); const char *output);
/** /**
* Returns the error code set by the most recently called method. * Returns the error code set by the most recently called method.
@ -1937,6 +1982,8 @@ struct msszdd_decompressor {
#define MSKWAJ_COMP_SZDD (2) #define MSKWAJ_COMP_SZDD (2)
/** KWAJ compression type: LZ+Huffman compression */ /** KWAJ compression type: LZ+Huffman compression */
#define MSKWAJ_COMP_LZH (3) #define MSKWAJ_COMP_LZH (3)
/** KWAJ compression type: MSZIP */
#define MSKWAJ_COMP_MSZIP (4)
/** KWAJ optional header flag: decompressed file length is included */ /** KWAJ optional header flag: decompressed file length is included */
#define MSKWAJ_HDR_HASLENGTH (0x01) #define MSKWAJ_HDR_HASLENGTH (0x01)
@ -2015,9 +2062,9 @@ struct mskwaj_compressor {
* @see set_param() * @see set_param()
*/ */
int (*compress)(struct mskwaj_compressor *self, int (*compress)(struct mskwaj_compressor *self,
const char *input, const char *input,
const char *output, const char *output,
off_t length); off_t length);
/** /**
* Sets an KWAJ compression engine parameter. * Sets an KWAJ compression engine parameter.
@ -2043,8 +2090,8 @@ struct mskwaj_compressor {
* @see generate() * @see generate()
*/ */
int (*set_param)(struct mskwaj_compressor *self, int (*set_param)(struct mskwaj_compressor *self,
int param, int param,
unsigned int value); int value);
/** /**
@ -2065,7 +2112,7 @@ struct mskwaj_compressor {
* filename is too long * filename is too long
*/ */
int (*set_filename)(struct mskwaj_compressor *self, 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 * Sets arbitrary data that will be stored in the header of the
@ -2085,8 +2132,8 @@ struct mskwaj_compressor {
* is too long * is too long
*/ */
int (*set_extra_data)(struct mskwaj_compressor *self, int (*set_extra_data)(struct mskwaj_compressor *self,
void *data, void *data,
size_t bytes); size_t bytes);
/** /**
* Returns the error code set by the most recently called method. * Returns the error code set by the most recently called method.
@ -2127,7 +2174,7 @@ struct mskwaj_decompressor {
* @see close() * @see close()
*/ */
struct mskwajd_header *(*open)(struct mskwaj_decompressor *self, struct mskwajd_header *(*open)(struct mskwaj_decompressor *self,
const char *filename); const char *filename);
/** /**
* Closes a previously opened KWAJ file. * Closes a previously opened KWAJ file.
@ -2142,7 +2189,7 @@ struct mskwaj_decompressor {
* @see open() * @see open()
*/ */
void (*close)(struct mskwaj_decompressor *self, void (*close)(struct mskwaj_decompressor *self,
struct mskwajd_header *kwaj); struct mskwajd_header *kwaj);
/** /**
* Extracts the compressed data from a KWAJ file. * 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 * @return an error code, or MSPACK_ERR_OK if successful
*/ */
int (*extract)(struct mskwaj_decompressor *self, int (*extract)(struct mskwaj_decompressor *self,
struct mskwajd_header *kwaj, struct mskwajd_header *kwaj,
const char *filename); const char *filename);
/** /**
* Decompresses an KWAJ file to an output file in one step. * 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 * @return an error code, or MSPACK_ERR_OK if successful
*/ */
int (*decompress)(struct mskwaj_decompressor *self, int (*decompress)(struct mskwaj_decompressor *self,
const char *input, const char *input,
const char *output); const char *output);
/** /**
* Returns the error code set by the most recently called method. * 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); 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 #ifdef __cplusplus
} }
#endif #endif

View file

@ -32,14 +32,14 @@ extern "C" {
# define MSZIP_LITERAL_TABLESIZE (MSZIP_LITERAL_MAXSYMBOLS * 4) # define MSZIP_LITERAL_TABLESIZE (MSZIP_LITERAL_MAXSYMBOLS * 4)
#else #else
# define MSZIP_LITERAL_TABLESIZE ((1 << MSZIP_LITERAL_TABLEBITS) + \ # define MSZIP_LITERAL_TABLESIZE ((1 << MSZIP_LITERAL_TABLEBITS) + \
(MSZIP_LITERAL_MAXSYMBOLS * 2)) (MSZIP_LITERAL_MAXSYMBOLS * 2))
#endif #endif
#if (1 << MSZIP_DISTANCE_TABLEBITS) < (MSZIP_DISTANCE_MAXSYMBOLS * 2) #if (1 << MSZIP_DISTANCE_TABLEBITS) < (MSZIP_DISTANCE_MAXSYMBOLS * 2)
# define MSZIP_DISTANCE_TABLESIZE (MSZIP_DISTANCE_MAXSYMBOLS * 4) # define MSZIP_DISTANCE_TABLESIZE (MSZIP_DISTANCE_MAXSYMBOLS * 4)
#else #else
# define MSZIP_DISTANCE_TABLESIZE ((1 << MSZIP_DISTANCE_TABLEBITS) + \ # define MSZIP_DISTANCE_TABLESIZE ((1 << MSZIP_DISTANCE_TABLEBITS) + \
(MSZIP_DISTANCE_MAXSYMBOLS * 2)) (MSZIP_DISTANCE_MAXSYMBOLS * 2))
#endif #endif
struct mszipd_stream { struct mszipd_stream {
@ -83,10 +83,10 @@ struct mszipd_stream {
* a partial recovery of erroneous data. * a partial recovery of erroneous data.
*/ */
extern struct mszipd_stream *mszipd_init(struct mspack_system *system, extern struct mszipd_stream *mszipd_init(struct mspack_system *system,
struct mspack_file *input, struct mspack_file *input,
struct mspack_file *output, struct mspack_file *output,
int input_buffer_size, int input_buffer_size,
int repair_mode); int repair_mode);
/* decompresses, or decompresses more of, an MS-ZIP stream. /* 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); 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 /* frees all stream associated with an MS-ZIP data stream
* *
* - calls system->free() using the system pointer given in mszipd_init() * - calls system->free() using the system pointer given in mszipd_init()

View file

@ -20,9 +20,9 @@
#define BITS_VAR zip #define BITS_VAR zip
#define BITS_ORDER_LSB #define BITS_ORDER_LSB
#define BITS_LSB_TABLE #define BITS_LSB_TABLE
#define READ_BYTES do { \ #define READ_BYTES do { \
READ_IF_NEEDED; \ READ_IF_NEEDED; \
INJECT_BITS(*i_ptr++, 8); \ INJECT_BITS(*i_ptr++, 8); \
} while (0) } while (0)
#include "readbits.h" #include "readbits.h"
@ -34,13 +34,13 @@
#define HUFF_ERROR return INF_ERR_HUFFSYM #define HUFF_ERROR return INF_ERR_HUFFSYM
#include "readhuff.h" #include "readhuff.h"
#define FLUSH_IF_NEEDED do { \ #define FLUSH_IF_NEEDED do { \
if (zip->window_posn == MSZIP_FRAME_SIZE) { \ if (zip->window_posn == MSZIP_FRAME_SIZE) { \
if (zip->flush_window(zip, MSZIP_FRAME_SIZE)) { \ if (zip->flush_window(zip, MSZIP_FRAME_SIZE)) { \
return INF_ERR_FLUSH; \ return INF_ERR_FLUSH; \
} \ } \
zip->window_posn = 0; \ zip->window_posn = 0; \
} \ } \
} while (0) } while (0)
/* match lengths for literal codes 257.. 285 */ /* 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 */ /* read 4 bytes of data, emptying the bit-buffer if necessary */
for (i = 0; (bits_left >= 8); i++) { for (i = 0; (bits_left >= 8); i++) {
if (i == 4) return INF_ERR_BITBUF; if (i == 4) return INF_ERR_BITBUF;
lens_buf[i] = PEEK_BITS(8); lens_buf[i] = PEEK_BITS(8);
REMOVE_BITS(8); REMOVE_BITS(8);
} }
if (bits_left != 0) return INF_ERR_BITBUF; if (bits_left != 0) return INF_ERR_BITBUF;
while (i < 4) { while (i < 4) {
READ_IF_NEEDED; READ_IF_NEEDED;
lens_buf[i++] = *i_ptr++; lens_buf[i++] = *i_ptr++;
} }
/* get the length and its complement */ /* 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 */ /* read and copy the uncompressed data into the window */
while (length > 0) { while (length > 0) {
READ_IF_NEEDED; READ_IF_NEEDED;
this_run = length; this_run = length;
if (this_run > (unsigned int)(i_end - i_ptr)) this_run = i_end - i_ptr; if (this_run > (unsigned int)(i_end - i_ptr)) this_run = i_end - i_ptr;
if (this_run > (MSZIP_FRAME_SIZE - zip->window_posn)) if (this_run > (MSZIP_FRAME_SIZE - zip->window_posn))
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->sys->copy(i_ptr, &zip->window[zip->window_posn], this_run);
zip->window_posn += this_run; zip->window_posn += this_run;
i_ptr += this_run; i_ptr += this_run;
length -= this_run; length -= this_run;
FLUSH_IF_NEEDED; FLUSH_IF_NEEDED;
} }
} }
else if ((block_type == 1) || (block_type == 2)) { else if ((block_type == 1) || (block_type == 2)) {
@ -217,92 +217,92 @@ static int inflate(struct mszipd_stream *zip) {
unsigned int match_posn, code; unsigned int match_posn, code;
if (block_type == 1) { if (block_type == 1) {
/* block with fixed Huffman codes */ /* block with fixed Huffman codes */
i = 0; i = 0;
while (i < 144) zip->LITERAL_len[i++] = 8; while (i < 144) zip->LITERAL_len[i++] = 8;
while (i < 256) zip->LITERAL_len[i++] = 9; while (i < 256) zip->LITERAL_len[i++] = 9;
while (i < 280) zip->LITERAL_len[i++] = 7; while (i < 280) zip->LITERAL_len[i++] = 7;
while (i < 288) zip->LITERAL_len[i++] = 8; while (i < 288) zip->LITERAL_len[i++] = 8;
for (i = 0; i < 32; i++) zip->DISTANCE_len[i] = 5; for (i = 0; i < 32; i++) zip->DISTANCE_len[i] = 5;
} }
else { else {
/* block with dynamic Huffman codes */ /* block with dynamic Huffman codes */
STORE_BITS; STORE_BITS;
if ((i = zip_read_lens(zip))) return i; if ((i = zip_read_lens(zip))) return i;
RESTORE_BITS; RESTORE_BITS;
} }
/* now huffman lengths are read for either kind of block, /* now huffman lengths are read for either kind of block,
* create huffman decoding tables */ * create huffman decoding tables */
if (make_decode_table(MSZIP_LITERAL_MAXSYMBOLS, MSZIP_LITERAL_TABLEBITS, 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, 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 */ /* decode forever until end of block code */
for (;;) { for (;;) {
READ_HUFFSYM(LITERAL, code); READ_HUFFSYM(LITERAL, code);
if (code < 256) { if (code < 256) {
zip->window[zip->window_posn++] = (unsigned char) code; zip->window[zip->window_posn++] = (unsigned char) code;
FLUSH_IF_NEEDED; FLUSH_IF_NEEDED;
} }
else if (code == 256) { else if (code == 256) {
/* END OF BLOCK CODE: loop break point */ /* END OF BLOCK CODE: loop break point */
break; break;
} }
else { else {
code -= 257; /* codes 257-285 are matches */ code -= 257; /* codes 257-285 are matches */
if (code >= 29) return INF_ERR_LITCODE; /* codes 286-287 are illegal */ if (code >= 29) return INF_ERR_LITCODE; /* codes 286-287 are illegal */
READ_BITS_T(length, lit_extrabits[code]); READ_BITS_T(length, lit_extrabits[code]);
length += lit_lengths[code]; length += lit_lengths[code];
READ_HUFFSYM(DISTANCE, code); READ_HUFFSYM(DISTANCE, code);
if (code > 30) return INF_ERR_DISTCODE; if (code >= 30) return INF_ERR_DISTCODE;
READ_BITS_T(distance, dist_extrabits[code]); READ_BITS_T(distance, dist_extrabits[code]);
distance += dist_offsets[code]; distance += dist_offsets[code];
/* match position is window position minus distance. If distance /* match position is window position minus distance. If distance
* is more than window position numerically, it must 'wrap * is more than window position numerically, it must 'wrap
* around' the frame size. */ * around' the frame size. */
match_posn = ((distance > zip->window_posn) ? MSZIP_FRAME_SIZE : 0) match_posn = ((distance > zip->window_posn) ? MSZIP_FRAME_SIZE : 0)
+ zip->window_posn - distance; + zip->window_posn - distance;
/* copy match */ /* copy match */
if (length < 12) { if (length < 12) {
/* short match, use slower loop but no loop setup code */ /* short match, use slower loop but no loop setup code */
while (length--) { while (length--) {
zip->window[zip->window_posn++] = zip->window[match_posn++]; zip->window[zip->window_posn++] = zip->window[match_posn++];
match_posn &= MSZIP_FRAME_SIZE - 1; match_posn &= MSZIP_FRAME_SIZE - 1;
FLUSH_IF_NEEDED; FLUSH_IF_NEEDED;
} }
} }
else { else {
/* longer match, use faster loop but with setup expense */ /* longer match, use faster loop but with setup expense */
unsigned char *runsrc, *rundest; unsigned char *runsrc, *rundest;
do { do {
this_run = length; this_run = length;
if ((match_posn + this_run) > MSZIP_FRAME_SIZE) if ((match_posn + this_run) > MSZIP_FRAME_SIZE)
this_run = MSZIP_FRAME_SIZE - match_posn; this_run = MSZIP_FRAME_SIZE - match_posn;
if ((zip->window_posn + this_run) > MSZIP_FRAME_SIZE) if ((zip->window_posn + this_run) > MSZIP_FRAME_SIZE)
this_run = MSZIP_FRAME_SIZE - zip->window_posn; this_run = MSZIP_FRAME_SIZE - zip->window_posn;
rundest = &zip->window[zip->window_posn]; zip->window_posn += this_run; rundest = &zip->window[zip->window_posn]; zip->window_posn += this_run;
runsrc = &zip->window[match_posn]; match_posn += this_run; runsrc = &zip->window[match_posn]; match_posn += this_run;
length -= this_run; length -= this_run;
while (this_run--) *rundest++ = *runsrc++; while (this_run--) *rundest++ = *runsrc++;
if (match_posn == MSZIP_FRAME_SIZE) match_posn = 0; if (match_posn == MSZIP_FRAME_SIZE) match_posn = 0;
FLUSH_IF_NEEDED; FLUSH_IF_NEEDED;
} while (length > 0); } while (length > 0);
} }
} /* else (code >= 257) */ } /* else (code >= 257) */
} /* for(;;) -- break point at 'code == 256' */ } /* for(;;) -- break point at 'code == 256' */
} }
@ -328,7 +328,7 @@ static int inflate(struct mszipd_stream *zip) {
* is flushed, an error is raised. * is flushed, an error is raised.
*/ */
static int mszipd_flush_window(struct mszipd_stream *zip, static int mszipd_flush_window(struct mszipd_stream *zip,
unsigned int data_flushed) unsigned int data_flushed)
{ {
zip->bytes_output += data_flushed; zip->bytes_output += data_flushed;
if (zip->bytes_output > MSZIP_FRAME_SIZE) { 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 mszipd_stream *mszipd_init(struct mspack_system *system,
struct mspack_file *input, struct mspack_file *input,
struct mspack_file *output, struct mspack_file *output,
int input_buffer_size, int input_buffer_size,
int repair_mode) int repair_mode)
{ {
struct mszipd_stream *zip; struct mszipd_stream *zip;
if (!system) return NULL; if (!system) return NULL;
/* round up input buffer size to multiple of two */
input_buffer_size = (input_buffer_size + 1) & -2; input_buffer_size = (input_buffer_size + 1) & -2;
if (!input_buffer_size) return NULL; if (input_buffer_size < 2) return NULL;
/* allocate decompression state */ /* allocate decompression state */
if (!(zip = (struct mszipd_stream *) system->alloc(system, sizeof(struct mszipd_stream)))) { 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))) { if ((error = inflate(zip))) {
D(("inflate error %d", error)) D(("inflate error %d", error))
if (zip->repair_mode) { if (zip->repair_mode) {
/* recover partially-inflated buffers */ /* recover partially-inflated buffers */
if (zip->bytes_output == 0 && zip->window_posn > 0) { if (zip->bytes_output == 0 && zip->window_posn > 0) {
zip->flush_window(zip, zip->window_posn); zip->flush_window(zip, zip->window_posn);
} }
zip->sys->message(NULL, "MSZIP error, %u bytes of data lost.", zip->sys->message(NULL, "MSZIP error, %u bytes of data lost.",
MSZIP_FRAME_SIZE - zip->bytes_output); MSZIP_FRAME_SIZE - zip->bytes_output);
for (i = zip->bytes_output; i < MSZIP_FRAME_SIZE; i++) { for (i = zip->bytes_output; i < MSZIP_FRAME_SIZE; i++) {
zip->window[i] = '\0'; zip->window[i] = '\0';
} }
zip->bytes_output = MSZIP_FRAME_SIZE; zip->bytes_output = MSZIP_FRAME_SIZE;
} }
else { 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]; 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; 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) { void mszipd_free(struct mszipd_stream *zip) {
struct mspack_system *sys; struct mspack_system *sys;
if (zip) { if (zip) {

View file

@ -90,10 +90,10 @@ struct qtmd_stream {
* - input_buffer_size is the number of bytes to use to store bitstream data. * - input_buffer_size is the number of bytes to use to store bitstream data.
*/ */
extern struct qtmd_stream *qtmd_init(struct mspack_system *system, extern struct qtmd_stream *qtmd_init(struct mspack_system *system,
struct mspack_file *input, struct mspack_file *input,
struct mspack_file *output, struct mspack_file *output,
int window_bits, int window_bits,
int input_buffer_size); int input_buffer_size);
/* decompresses, or decompresses more of, a Quantum stream. /* decompresses, or decompresses more of, a Quantum stream.
* *

View file

@ -27,11 +27,11 @@
#define BITS_TYPE struct qtmd_stream #define BITS_TYPE struct qtmd_stream
#define BITS_VAR qtm #define BITS_VAR qtm
#define BITS_ORDER_MSB #define BITS_ORDER_MSB
#define READ_BYTES do { \ #define READ_BYTES do { \
unsigned char b0, b1; \ unsigned char b0, b1; \
READ_IF_NEEDED; b0 = *i_ptr++; \ READ_IF_NEEDED; b0 = *i_ptr++; \
READ_IF_NEEDED; b1 = *i_ptr++; \ READ_IF_NEEDED; b1 = *i_ptr++; \
INJECT_BITS((b0 << 8) | b1, 16); \ INJECT_BITS((b0 << 8) | b1, 16); \
} while (0) } while (0)
#include "readbits.h" #include "readbits.h"
@ -115,7 +115,7 @@ static const unsigned char length_extra[27] = {
else break; \ else break; \
} \ } \
L <<= 1; H = (H << 1) | 1; \ L <<= 1; H = (H << 1) | 1; \
ENSURE_BITS(1); \ ENSURE_BITS(1); \
C = (C << 1) | PEEK_BITS(1); \ C = (C << 1) | PEEK_BITS(1); \
REMOVE_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 */ /* -1, not -2; the 0 entry saves this */
model->syms[i].cumfreq >>= 1; model->syms[i].cumfreq >>= 1;
if (model->syms[i].cumfreq <= model->syms[i+1].cumfreq) { 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 */ * characteristics */
for (i = 0; i < model->entries - 1; i++) { for (i = 0; i < model->entries - 1; i++) {
for (j = i + 1; j < model->entries; j++) { for (j = i + 1; j < model->entries; j++) {
if (model->syms[i].cumfreq < model->syms[j].cumfreq) { if (model->syms[i].cumfreq < model->syms[j].cumfreq) {
tmp = model->syms[i]; tmp = model->syms[i];
model->syms[i] = model->syms[j]; model->syms[i] = model->syms[j];
model->syms[j] = tmp; 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 */ /* Initialises a model to decode symbols from [start] to [start]+[len]-1 */
static void qtmd_init_model(struct qtmd_model *model, 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; int i;
@ -184,9 +184,9 @@ static void qtmd_init_model(struct qtmd_model *model,
/*-------- main Quantum code --------*/ /*-------- main Quantum code --------*/
struct qtmd_stream *qtmd_init(struct mspack_system *system, struct qtmd_stream *qtmd_init(struct mspack_system *system,
struct mspack_file *input, struct mspack_file *input,
struct mspack_file *output, struct mspack_file *output,
int window_bits, int input_buffer_size) int window_bits, int input_buffer_size)
{ {
unsigned int window_size = 1 << window_bits; unsigned int window_size = 1 << window_bits;
struct qtmd_stream *qtm; 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) */ /* Quantum supports window sizes of 2^10 (1Kb) through 2^21 (2Mb) */
if (window_bits < 10 || window_bits > 21) return NULL; 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; input_buffer_size = (input_buffer_size + 1) & -2;
if (input_buffer_size < 2) return NULL; 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) { while (window_posn < frame_end) {
GET_SYMBOL(qtm->model7, selector); GET_SYMBOL(qtm->model7, selector);
if (selector < 4) { if (selector < 4) {
/* literal byte */ /* literal byte */
struct qtmd_model *mdl = (selector == 0) ? &qtm->model0 : struct qtmd_model *mdl = (selector == 0) ? &qtm->model0 :
((selector == 1) ? &qtm->model1 : ((selector == 1) ? &qtm->model1 :
((selector == 2) ? &qtm->model2 : ((selector == 2) ? &qtm->model2 :
&qtm->model3)); &qtm->model3));
GET_SYMBOL((*mdl), sym); GET_SYMBOL((*mdl), sym);
window[window_posn++] = sym; window[window_posn++] = sym;
frame_todo--; frame_todo--;
} }
else { else {
/* match repeated string */ /* match repeated string */
switch (selector) { switch (selector) {
case 4: /* selector 4 = fixed length match (3 bytes) */ case 4: /* selector 4 = fixed length match (3 bytes) */
GET_SYMBOL(qtm->model4, sym); GET_SYMBOL(qtm->model4, sym);
READ_MANY_BITS(extra, extra_bits[sym]); READ_MANY_BITS(extra, extra_bits[sym]);
match_offset = position_base[sym] + extra + 1; match_offset = position_base[sym] + extra + 1;
match_length = 3; match_length = 3;
break; break;
case 5: /* selector 5 = fixed length match (4 bytes) */ case 5: /* selector 5 = fixed length match (4 bytes) */
GET_SYMBOL(qtm->model5, sym); GET_SYMBOL(qtm->model5, sym);
READ_MANY_BITS(extra, extra_bits[sym]); READ_MANY_BITS(extra, extra_bits[sym]);
match_offset = position_base[sym] + extra + 1; match_offset = position_base[sym] + extra + 1;
match_length = 4; match_length = 4;
break; break;
case 6: /* selector 6 = variable length match */ case 6: /* selector 6 = variable length match */
GET_SYMBOL(qtm->model6len, sym); GET_SYMBOL(qtm->model6len, sym);
READ_MANY_BITS(extra, length_extra[sym]); READ_MANY_BITS(extra, length_extra[sym]);
match_length = length_base[sym] + extra + 5; match_length = length_base[sym] + extra + 5;
GET_SYMBOL(qtm->model6, sym); GET_SYMBOL(qtm->model6, sym);
READ_MANY_BITS(extra, extra_bits[sym]); READ_MANY_BITS(extra, extra_bits[sym]);
match_offset = position_base[sym] + extra + 1; match_offset = position_base[sym] + extra + 1;
break; break;
default: default:
/* should be impossible, model7 can only return 0-6 */ /* should be impossible, model7 can only return 0-6 */
D(("got %d from selector", selector)) D(("got %d from selector", selector))
return qtm->error = MSPACK_ERR_DECRUNCH; return qtm->error = MSPACK_ERR_DECRUNCH;
} }
rundest = &window[window_posn]; rundest = &window[window_posn];
frame_todo -= match_length; frame_todo -= match_length;
/* does match destination wrap the window? This situation is possible /* does match destination wrap the window? This situation is possible
* where the window size is less than the 32k frame size, but matches * where the window size is less than the 32k frame size, but matches
* must not go beyond a frame boundary */ * must not go beyond a frame boundary */
if ((window_posn + match_length) > qtm->window_size) { if ((window_posn + match_length) > qtm->window_size) {
/* copy first part of match, before window end */ /* copy first part of match, before window end */
i = qtm->window_size - window_posn; i = qtm->window_size - window_posn;
j = window_posn - match_offset; j = window_posn - match_offset;
while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)];
/* flush currently stored data */ /* flush currently stored data */
i = (&window[qtm->window_size] - qtm->o_ptr); i = (&window[qtm->window_size] - qtm->o_ptr);
/* this should not happen, but if it does then this code /* this should not happen, but if it does then this code
* can't handle the situation (can't flush up to the end of * 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 * the window, but can't break out either because we haven't
* finished writing the match). bail out in this case */ * finished writing the match). bail out in this case */
if (i > out_bytes) { if (i > out_bytes) {
D(("during window-wrap match; %d bytes to flush but only need %d", D(("during window-wrap match; %d bytes to flush but only need %d",
i, (int) out_bytes)) i, (int) out_bytes))
return qtm->error = MSPACK_ERR_DECRUNCH; return qtm->error = MSPACK_ERR_DECRUNCH;
} }
if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { 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; out_bytes -= i;
qtm->o_ptr = &window[0]; qtm->o_ptr = &window[0];
qtm->o_end = &window[0]; qtm->o_end = &window[0];
/* copy second part of match, after window wrap */ /* copy second part of match, after window wrap */
rundest = &window[0]; rundest = &window[0];
i = match_length - (qtm->window_size - window_posn); i = match_length - (qtm->window_size - window_posn);
while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)];
window_posn = window_posn + match_length - qtm->window_size; window_posn = window_posn + match_length - qtm->window_size;
break; /* because "window_posn < frame_end" has now failed */ break; /* because "window_posn < frame_end" has now failed */
} }
else { else {
/* normal match - output won't wrap window or frame end */ /* normal match - output won't wrap window or frame end */
i = match_length; i = match_length;
/* does match _offset_ wrap the window? */ /* does match _offset_ wrap the window? */
if (match_offset > window_posn) { if (match_offset > window_posn) {
/* j = length from match offset to end of window */ /* j = length from match offset to end of window */
j = match_offset - window_posn; j = match_offset - window_posn;
if (j > (int) qtm->window_size) { if (j > (int) qtm->window_size) {
D(("match offset beyond window boundaries")) D(("match offset beyond window boundaries"))
return qtm->error = MSPACK_ERR_DECRUNCH; return qtm->error = MSPACK_ERR_DECRUNCH;
} }
runsrc = &window[qtm->window_size - j]; runsrc = &window[qtm->window_size - j];
if (j < i) { if (j < i) {
/* if match goes over the window edge, do two copy runs */ /* if match goes over the window edge, do two copy runs */
i -= j; while (j-- > 0) *rundest++ = *runsrc++; i -= j; while (j-- > 0) *rundest++ = *runsrc++;
runsrc = window; runsrc = window;
} }
while (i-- > 0) *rundest++ = *runsrc++; while (i-- > 0) *rundest++ = *runsrc++;
} }
else { else {
runsrc = rundest - match_offset; runsrc = rundest - match_offset;
while (i-- > 0) *rundest++ = *runsrc++; while (i-- > 0) *rundest++ = *runsrc++;
} }
window_posn += match_length; window_posn += match_length;
} }
} /* if (window_posn+match_length > frame_end) */ } /* if (window_posn+match_length > frame_end) */
} /* while (window_posn < 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 */ /* break out if we have more than enough to finish this request */
if (i >= out_bytes) break; if (i >= out_bytes) break;
if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { 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; out_bytes -= i;
qtm->o_ptr = &window[0]; qtm->o_ptr = &window[0];

View file

@ -100,48 +100,48 @@
#endif #endif
#define BITBUF_WIDTH (sizeof(bit_buffer) * CHAR_BIT) #define BITBUF_WIDTH (sizeof(bit_buffer) * CHAR_BIT)
#define INIT_BITS do { \ #define INIT_BITS do { \
BITS_VAR->i_ptr = &BITS_VAR->inbuf[0]; \ BITS_VAR->i_ptr = &BITS_VAR->inbuf[0]; \
BITS_VAR->i_end = &BITS_VAR->inbuf[0]; \ BITS_VAR->i_end = &BITS_VAR->inbuf[0]; \
BITS_VAR->bit_buffer = 0; \ BITS_VAR->bit_buffer = 0; \
BITS_VAR->bits_left = 0; \ BITS_VAR->bits_left = 0; \
BITS_VAR->input_end = 0; \ BITS_VAR->input_end = 0; \
} while (0) } while (0)
#define STORE_BITS do { \ #define STORE_BITS do { \
BITS_VAR->i_ptr = i_ptr; \ BITS_VAR->i_ptr = i_ptr; \
BITS_VAR->i_end = i_end; \ BITS_VAR->i_end = i_end; \
BITS_VAR->bit_buffer = bit_buffer; \ BITS_VAR->bit_buffer = bit_buffer; \
BITS_VAR->bits_left = bits_left; \ BITS_VAR->bits_left = bits_left; \
} while (0) } while (0)
#define RESTORE_BITS do { \ #define RESTORE_BITS do { \
i_ptr = BITS_VAR->i_ptr; \ i_ptr = BITS_VAR->i_ptr; \
i_end = BITS_VAR->i_end; \ i_end = BITS_VAR->i_end; \
bit_buffer = BITS_VAR->bit_buffer; \ bit_buffer = BITS_VAR->bit_buffer; \
bits_left = BITS_VAR->bits_left; \ bits_left = BITS_VAR->bits_left; \
} while (0) } while (0)
#define ENSURE_BITS(nbits) do { \ #define ENSURE_BITS(nbits) do { \
while (bits_left < (nbits)) READ_BYTES; \ while (bits_left < (nbits)) READ_BYTES; \
} while (0) } while (0)
#define READ_BITS(val, nbits) do { \ #define READ_BITS(val, nbits) do { \
ENSURE_BITS(nbits); \ ENSURE_BITS(nbits); \
(val) = PEEK_BITS(nbits); \ (val) = PEEK_BITS(nbits); \
REMOVE_BITS(nbits); \ REMOVE_BITS(nbits); \
} while (0) } while (0)
#define READ_MANY_BITS(val, bits) do { \ #define READ_MANY_BITS(val, bits) do { \
unsigned char needed = (bits), bitrun; \ unsigned char needed = (bits), bitrun; \
(val) = 0; \ (val) = 0; \
while (needed > 0) { \ while (needed > 0) { \
if (bits_left <= (BITBUF_WIDTH - 16)) READ_BYTES; \ if (bits_left <= (BITBUF_WIDTH - 16)) READ_BYTES; \
bitrun = (bits_left < needed) ? bits_left : needed; \ bitrun = (bits_left < needed) ? bits_left : needed; \
(val) = ((val) << bitrun) | PEEK_BITS(bitrun); \ (val) = ((val) << bitrun) | PEEK_BITS(bitrun); \
REMOVE_BITS(bitrun); \ REMOVE_BITS(bitrun); \
needed -= bitrun; \ needed -= bitrun; \
} \ } \
} while (0) } while (0)
#ifdef BITS_ORDER_MSB #ifdef BITS_ORDER_MSB
@ -163,21 +163,21 @@ static const unsigned short lsb_bit_mask[17] = {
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
}; };
# define PEEK_BITS_T(nbits) (bit_buffer & lsb_bit_mask[(nbits)]) # define PEEK_BITS_T(nbits) (bit_buffer & lsb_bit_mask[(nbits)])
# define READ_BITS_T(val, nbits) do { \ # define READ_BITS_T(val, nbits) do { \
ENSURE_BITS(nbits); \ ENSURE_BITS(nbits); \
(val) = PEEK_BITS_T(nbits); \ (val) = PEEK_BITS_T(nbits); \
REMOVE_BITS(nbits); \ REMOVE_BITS(nbits); \
} while (0) } while (0)
#endif #endif
#ifndef BITS_NO_READ_INPUT #ifndef BITS_NO_READ_INPUT
# define READ_IF_NEEDED do { \ # define READ_IF_NEEDED do { \
if (i_ptr >= i_end) { \ if (i_ptr >= i_end) { \
if (read_input(BITS_VAR)) \ if (read_input(BITS_VAR)) \
return BITS_VAR->error; \ return BITS_VAR->error; \
i_ptr = BITS_VAR->i_ptr; \ i_ptr = BITS_VAR->i_ptr; \
i_end = BITS_VAR->i_end; \ i_end = BITS_VAR->i_end; \
} \ } \
} while (0) } while (0)
static int read_input(BITS_TYPE *p) { 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, /* we might overrun the input stream by asking for bits we don't use,
* so fake 2 more bytes at the end of input */ * so fake 2 more bytes at the end of input */
if (read == 0) { if (read == 0) {
if (p->input_end) { if (p->input_end) {
D(("out of input bytes")) D(("out of input bytes"))
return p->error = MSPACK_ERR_READ; return p->error = MSPACK_ERR_READ;
} }
else { else {
read = 2; read = 2;
p->inbuf[0] = p->inbuf[1] = 0; p->inbuf[0] = p->inbuf[1] = 0;
p->input_end = 1; p->input_end = 1;
} }
} }
/* update i_ptr and i_end */ /* update i_ptr and i_end */

View file

@ -1,5 +1,5 @@
/* This file is part of libmspack. /* 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 * 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 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
@ -10,8 +10,7 @@
#ifndef MSPACK_READHUFF_H #ifndef MSPACK_READHUFF_H
#define MSPACK_READHUFF_H 1 #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)) #if !(defined(BITS_ORDER_MSB) || defined(BITS_ORDER_LSB))
# error "readhuff.h is used in conjunction with readbits.h, include that first" # 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. /* Decodes the next huffman symbol from the input bitstream into var.
* Do not use this macro on a table unless build_decode_table() succeeded. * Do not use this macro on a table unless build_decode_table() succeeded.
*/ */
#define READ_HUFFSYM(tbl, var) do { \ #define READ_HUFFSYM(tbl, var) do { \
ENSURE_BITS(HUFF_MAXBITS); \ ENSURE_BITS(HUFF_MAXBITS); \
sym = HUFF_TABLE(tbl, PEEK_BITS(TABLEBITS(tbl))); \ sym = HUFF_TABLE(tbl, PEEK_BITS(TABLEBITS(tbl))); \
if (sym >= MAXSYMBOLS(tbl)) HUFF_TRAVERSE(tbl); \ if (sym >= MAXSYMBOLS(tbl)) HUFF_TRAVERSE(tbl); \
(var) = sym; \ (var) = sym; \
i = HUFF_LEN(tbl, sym); \ i = HUFF_LEN(tbl, sym); \
REMOVE_BITS(i); \ REMOVE_BITS(i); \
} while (0) } while (0)
#ifdef BITS_ORDER_LSB #ifdef BITS_ORDER_LSB
# define HUFF_TRAVERSE(tbl) do { \ # define HUFF_TRAVERSE(tbl) do { \
i = TABLEBITS(tbl) - 1; \ i = TABLEBITS(tbl) - 1; \
do { \ do { \
if (i++ > HUFF_MAXBITS) HUFF_ERROR; \ if (i++ > HUFF_MAXBITS) HUFF_ERROR; \
sym = HUFF_TABLE(tbl, \ sym = HUFF_TABLE(tbl, \
(sym << 1) | ((bit_buffer >> i) & 1)); \ (sym << 1) | ((bit_buffer >> i) & 1)); \
} while (sym >= MAXSYMBOLS(tbl)); \ } while (sym >= MAXSYMBOLS(tbl)); \
} while (0) } while (0)
#else #else
#define HUFF_TRAVERSE(tbl) do { \ #define HUFF_TRAVERSE(tbl) do { \
i = 1 << (BITBUF_WIDTH - TABLEBITS(tbl)); \ i = 1 << (BITBUF_WIDTH - TABLEBITS(tbl)); \
do { \ do { \
if ((i >>= 1) == 0) HUFF_ERROR; \ if ((i >>= 1) == 0) HUFF_ERROR; \
sym = HUFF_TABLE(tbl, \ sym = HUFF_TABLE(tbl, \
(sym << 1) | ((bit_buffer & i) ? 1 : 0)); \ (sym << 1) | ((bit_buffer & i) ? 1 : 0)); \
} while (sym >= MAXSYMBOLS(tbl)); \ } while (sym >= MAXSYMBOLS(tbl)); \
} while (0) } while (0)
#endif #endif
@ -77,7 +76,7 @@
* Returns 0 for OK or 1 for error * Returns 0 for OK or 1 for error
*/ */
static int make_decode_table(unsigned int nsyms, unsigned int nbits, 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 short sym, next_symbol;
register unsigned int leaf, fill; 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 */ /* fill entries for codes short enough for a direct mapping */
for (bit_num = 1; bit_num <= nbits; bit_num++) { for (bit_num = 1; bit_num <= nbits; bit_num++) {
for (sym = 0; sym < nsyms; sym++) { for (sym = 0; sym < nsyms; sym++) {
if (length[sym] != bit_num) continue; if (length[sym] != bit_num) continue;
#ifdef BITS_ORDER_MSB #ifdef BITS_ORDER_MSB
leaf = pos; leaf = pos;
#else #else
/* reverse the significant bits */ /* reverse the significant bits */
fill = length[sym]; reverse = pos >> (nbits - fill); leaf = 0; fill = length[sym]; reverse = pos >> (nbits - fill); leaf = 0;
do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill);
#endif #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 #ifdef BITS_ORDER_MSB
for (fill = bit_mask; fill-- > 0;) table[leaf++] = sym; for (fill = bit_mask; fill-- > 0;) table[leaf++] = sym;
#else #else
fill = bit_mask; next_symbol = 1 << bit_num; fill = bit_mask; next_symbol = 1 << bit_num;
do { table[leaf] = sym; leaf += next_symbol; } while (--fill); do { table[leaf] = sym; leaf += next_symbol; } while (--fill);
#endif #endif
} }
bit_mask >>= 1; bit_mask >>= 1;
} }
/* exit with success if table is now complete */ /* 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 */ /* mark all remaining table entries as unused */
for (sym = pos; sym < table_mask; sym++) { for (sym = pos; sym < table_mask; sym++) {
#ifdef BITS_ORDER_MSB #ifdef BITS_ORDER_MSB
table[sym] = 0xFFFF; table[sym] = 0xFFFF;
#else #else
reverse = sym; leaf = 0; fill = nbits; reverse = sym; leaf = 0; fill = nbits;
do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill); do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill);
table[leaf] = 0xFFFF; table[leaf] = 0xFFFF;
#endif #endif
} }
@ -138,33 +137,33 @@ static int make_decode_table(unsigned int nsyms, unsigned int nbits,
bit_mask = 1 << 15; bit_mask = 1 << 15;
for (bit_num = nbits+1; bit_num <= HUFF_MAXBITS; bit_num++) { for (bit_num = nbits+1; bit_num <= HUFF_MAXBITS; bit_num++) {
for (sym = 0; sym < nsyms; sym++) { for (sym = 0; sym < nsyms; sym++) {
if (length[sym] != bit_num) continue; if (length[sym] != bit_num) continue;
if (pos >= table_mask) return 1; /* table overflow */
#ifdef BITS_ORDER_MSB #ifdef BITS_ORDER_MSB
leaf = pos >> 16; leaf = pos >> 16;
#else #else
/* leaf = the first nbits of the code, reversed */ /* leaf = the first nbits of the code, reversed */
reverse = pos >> 16; leaf = 0; fill = nbits; reverse = pos >> 16; leaf = 0; fill = nbits;
do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill);
#endif #endif
for (fill = 0; fill < (bit_num - nbits); fill++) { for (fill = 0; fill < (bit_num - nbits); fill++) {
/* if this path hasn't been taken yet, 'allocate' two entries */ /* if this path hasn't been taken yet, 'allocate' two entries */
if (table[leaf] == 0xFFFF) { if (table[leaf] == 0xFFFF) {
table[(next_symbol << 1) ] = 0xFFFF; table[(next_symbol << 1) ] = 0xFFFF;
table[(next_symbol << 1) + 1 ] = 0xFFFF; table[(next_symbol << 1) + 1 ] = 0xFFFF;
table[leaf] = next_symbol++; table[leaf] = next_symbol++;
} }
/* follow the path and select either left or right for next bit */ /* follow the path and select either left or right for next bit */
leaf = table[leaf] << 1; leaf = table[leaf] << 1;
if ((pos >> (15-fill)) & 1) leaf++; if ((pos >> (15-fill)) & 1) leaf++;
} }
table[leaf] = sym; table[leaf] = sym;
pos += bit_mask;
if ((pos += bit_mask) > table_mask) return 1; /* table overflow */ }
} bit_mask >>= 1;
bit_mask >>= 1;
} }
/* full table? */ /* full table? */

View file

@ -8,7 +8,7 @@
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include "config.h"
#endif #endif
#include "system-mspack.h" #include "system-mspack.h"

View file

@ -16,7 +16,7 @@ extern "C" {
/* ensure config.h is read before mspack.h */ /* ensure config.h is read before mspack.h */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include "config.h"
#endif #endif
#include "mspack.h" #include "mspack.h"
@ -61,7 +61,7 @@ extern "C" {
(defined(FILESIZEBITS) && FILESIZEBITS >= 64) || \ (defined(FILESIZEBITS) && FILESIZEBITS >= 64) || \
(defined(SIZEOF_OFF_T) && SIZEOF_OFF_T >= 8) || \ (defined(SIZEOF_OFF_T) && SIZEOF_OFF_T >= 8) || \
defined(_LARGEFILE_SOURCE) || defined(_LARGEFILE64_SOURCE)) defined(_LARGEFILE_SOURCE) || defined(_LARGEFILE64_SOURCE))
# define LARGEFILE_SUPPORT # define LARGEFILE_SUPPORT 1
# define LD "lld" # define LD "lld"
# define LU "llu" # define LU "llu"
#else #else

View file

@ -66,8 +66,8 @@ void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *base)
{ {
struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base; struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
if (self) { if (self) {
struct mspack_system *sys = self->system; struct mspack_system *sys = self->system;
sys->free(self); sys->free(self);
} }
} }
@ -77,7 +77,7 @@ void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *base)
* opens an SZDD file without decompressing, reads header * opens an SZDD file without decompressing, reads header
*/ */
static struct msszddd_header *szddd_open(struct msszdd_decompressor *base, 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 msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
struct msszddd_header *hdr; 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); fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ);
hdr = (struct msszddd_header *) sys->alloc(sys, sizeof(struct msszddd_header_p)); hdr = (struct msszddd_header *) sys->alloc(sys, sizeof(struct msszddd_header_p));
if (fh && hdr) { if (fh && hdr) {
((struct msszddd_header_p *) hdr)->fh = fh; ((struct msszddd_header_p *) hdr)->fh = fh;
self->error = szddd_read_headers(sys, fh, hdr); self->error = szddd_read_headers(sys, fh, hdr);
} }
else { else {
if (!fh) self->error = MSPACK_ERR_OPEN; if (!fh) self->error = MSPACK_ERR_OPEN;
if (!hdr) self->error = MSPACK_ERR_NOMEMORY; if (!hdr) self->error = MSPACK_ERR_NOMEMORY;
} }
if (self->error) { if (self->error) {
if (fh) sys->close(fh); if (fh) sys->close(fh);
if (hdr) sys->free(hdr); sys->free(hdr);
hdr = NULL; hdr = NULL;
} }
return hdr; return hdr;
@ -113,7 +113,7 @@ static struct msszddd_header *szddd_open(struct msszdd_decompressor *base,
* closes an SZDD file * closes an SZDD file
*/ */
static void szddd_close(struct msszdd_decompressor *base, 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 msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
struct msszddd_header_p *hdr_p = (struct msszddd_header_p *) hdr; 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, static int szddd_read_headers(struct mspack_system *sys,
struct mspack_file *fh, struct mspack_file *fh,
struct msszddd_header *hdr) struct msszddd_header *hdr)
{ {
unsigned char buf[8]; unsigned char buf[8];
/* read and check signature */ /* read and check signature */
if (sys->read(fh, buf, 8) != 8) return MSPACK_ERR_READ; if (sys->read(fh, buf, 8) != 8) return MSPACK_ERR_READ;
if ((mspack_memcmp(buf, szdd_signature_expand, 8) == 0)) { if ((memcmp(buf, szdd_signature_expand, 8) == 0)) {
/* common SZDD */ /* common SZDD */
hdr->format = MSSZDD_FMT_NORMAL; hdr->format = MSSZDD_FMT_NORMAL;
/* read the rest of the header */ /* read the rest of the header */
if (sys->read(fh, buf, 6) != 6) return MSPACK_ERR_READ; if (sys->read(fh, buf, 6) != 6) return MSPACK_ERR_READ;
if (buf[0] != 0x41) return MSPACK_ERR_DATAFORMAT; if (buf[0] != 0x41) return MSPACK_ERR_DATAFORMAT;
hdr->missing_char = buf[1]; hdr->missing_char = buf[1];
hdr->length = EndGetI32(&buf[2]); hdr->length = EndGetI32(&buf[2]);
} }
else if ((mspack_memcmp(buf, szdd_signature_qbasic, 8) == 0)) { else if ((memcmp(buf, szdd_signature_qbasic, 8) == 0)) {
/* special QBasic SZDD */ /* special QBasic SZDD */
hdr->format = MSSZDD_FMT_QBASIC; hdr->format = MSSZDD_FMT_QBASIC;
if (sys->read(fh, buf, 4) != 4) return MSPACK_ERR_READ; if (sys->read(fh, buf, 4) != 4) return MSPACK_ERR_READ;
hdr->missing_char = '\0'; hdr->missing_char = '\0';
hdr->length = EndGetI32(buf); hdr->length = EndGetI32(buf);
} }
else { else {
return MSPACK_ERR_SIGNATURE; return MSPACK_ERR_SIGNATURE;
} }
return MSPACK_ERR_OK; return MSPACK_ERR_OK;
} }
@ -179,7 +179,7 @@ static int szddd_read_headers(struct mspack_system *sys,
* decompresses an SZDD file * decompresses an SZDD file
*/ */
static int szddd_extract(struct msszdd_decompressor *base, 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 msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
struct mspack_file *fh, *outfh; struct mspack_file *fh, *outfh;
@ -195,19 +195,19 @@ static int szddd_extract(struct msszdd_decompressor *base,
/* seek to the compressed data */ /* seek to the compressed data */
data_offset = (hdr->format == MSSZDD_FMT_NORMAL) ? 14 : 12; data_offset = (hdr->format == MSSZDD_FMT_NORMAL) ? 14 : 12;
if (sys->seek(fh, data_offset, MSPACK_SYS_SEEK_START)) { 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 */ /* open file for output */
if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { 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 */ /* decompress the data */
self->error = lzss_decompress(sys, fh, outfh, SZDD_INPUT_SIZE, self->error = lzss_decompress(sys, fh, outfh, SZDD_INPUT_SIZE,
hdr->format == MSSZDD_FMT_NORMAL hdr->format == MSSZDD_FMT_NORMAL
? LZSS_MODE_EXPAND ? LZSS_MODE_EXPAND
: LZSS_MODE_QBASIC); : LZSS_MODE_QBASIC);
/* close output file */ /* close output file */
sys->close(outfh); sys->close(outfh);
@ -221,7 +221,7 @@ static int szddd_extract(struct msszdd_decompressor *base,
* unpacks directly from input to output * unpacks directly from input to output
*/ */
static int szddd_decompress(struct msszdd_decompressor *base, 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 msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
struct msszddd_header *hdr; struct msszddd_header *hdr;