mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
688 lines
20 KiB
C++
688 lines
20 KiB
C++
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
|
*
|
|
* Residual is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the AUTHORS
|
|
* file distributed with this source distribution.
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
/* This file is part of libmspack.
|
|
* (C) 2003-2004 Stuart Caie.
|
|
*
|
|
* This source code is adopted and striped for Residual project.
|
|
*
|
|
* libmspack is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
|
|
*
|
|
* For further details, see the file COPYING.LIB distributed with libmspack
|
|
*/
|
|
|
|
#include "tools/patch_ex/cab.h"
|
|
|
|
static struct mscabd_cabinet * cabd_open(
|
|
struct mscab_decompressor *base, char *filename);
|
|
static void cabd_close(
|
|
struct mscab_decompressor *base, struct mscabd_cabinet *origcab);
|
|
static int cabd_read_headers(
|
|
struct mspack_system *sys, struct mspack_file *fh,
|
|
struct mscabd_cabinet_p *cab, off_t offset, int quiet);
|
|
static char *cabd_read_string(
|
|
struct mspack_system *sys, struct mspack_file *fh,
|
|
struct mscabd_cabinet_p *cab, int *error);
|
|
|
|
static int cabd_extract(
|
|
struct mscab_decompressor *base, struct mscabd_file *file, char *filename);
|
|
static int cabd_init_decomp(
|
|
struct mscab_decompressor_p *handle, unsigned int ct);
|
|
static void cabd_free_decomp(
|
|
struct mscab_decompressor_p *handle);
|
|
static int cabd_sys_read(
|
|
struct mspack_file *file, void *buffer, int bytes);
|
|
static int cabd_sys_write(
|
|
struct mspack_file *file, void *buffer, int bytes);
|
|
static int cabd_sys_read_block(
|
|
struct mspack_system *sys, struct mscabd_decompress_state *d, int *out,
|
|
int ignore_cksum);
|
|
static unsigned int cabd_checksum(
|
|
unsigned char *data, unsigned int bytes, unsigned int cksum);
|
|
|
|
static int cabd_error(
|
|
struct mscab_decompressor *base);
|
|
|
|
#define __egi32(a,n) ( ((((unsigned char *) a)[n+3]) << 24) | \
|
|
((((unsigned char *) a)[n+2]) << 16) | \
|
|
((((unsigned char *) a)[n+1]) << 8) | \
|
|
((((unsigned char *) a)[n+0])))
|
|
|
|
#define EndGetI32(a) __egi32(a,0)
|
|
#define EndGetI16(a) ((((a)[1])<<8)|((a)[0]))
|
|
|
|
struct mscab_decompressor *
|
|
mspack_create_cab_decompressor(struct mspack_system *sys)
|
|
{
|
|
struct mscab_decompressor_p *handle = NULL;
|
|
|
|
if ((handle = (mscab_decompressor_p *)sys->alloc(sys, sizeof(struct mscab_decompressor_p)))) {
|
|
handle->base.open = &cabd_open;
|
|
handle->base.close = &cabd_close;
|
|
handle->base.extract = &cabd_extract;
|
|
handle->base.last_error = &cabd_error;
|
|
handle->system = sys;
|
|
handle->d = NULL;
|
|
handle->error = MSPACK_ERR_OK;
|
|
|
|
handle->param[MSCABD_PARAM_SEARCHBUF] = 32768;
|
|
handle->param[MSCABD_PARAM_FIXMSZIP] = 0;
|
|
handle->param[MSCABD_PARAM_DECOMPBUF] = 4096;
|
|
}
|
|
return (struct mscab_decompressor *) handle;
|
|
}
|
|
|
|
void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) {
|
|
struct mscab_decompressor_p *handle = (struct mscab_decompressor_p *) base;
|
|
if (handle) {
|
|
struct mspack_system *sys = handle->system;
|
|
cabd_free_decomp(handle);
|
|
if (handle->d) {
|
|
if (handle->d->infh) sys->close(handle->d->infh);
|
|
sys->free(handle->d);
|
|
}
|
|
sys->free(handle);
|
|
}
|
|
}
|
|
|
|
static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base,
|
|
char *filename)
|
|
{
|
|
struct mscab_decompressor_p *handle = (struct mscab_decompressor_p *) base;
|
|
struct mscabd_cabinet_p *cab = NULL;
|
|
struct mspack_system *sys;
|
|
struct mspack_file *fh;
|
|
int error;
|
|
|
|
if (!base) return NULL;
|
|
sys = handle->system;
|
|
|
|
if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) {
|
|
if ((cab = (mscabd_cabinet_p *)sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) {
|
|
cab->base.filename = filename;
|
|
error = cabd_read_headers(sys, fh, cab, (off_t) 0, 0);
|
|
if (error) {
|
|
cabd_close(base, (struct mscabd_cabinet *) cab);
|
|
cab = NULL;
|
|
}
|
|
handle->error = error;
|
|
}
|
|
else {
|
|
handle->error = MSPACK_ERR_NOMEMORY;
|
|
}
|
|
sys->close(fh);
|
|
}
|
|
else {
|
|
handle->error = MSPACK_ERR_OPEN;
|
|
}
|
|
return (struct mscabd_cabinet *) cab;
|
|
}
|
|
|
|
static void cabd_close(struct mscab_decompressor *base,
|
|
struct mscabd_cabinet *origcab)
|
|
{
|
|
struct mscab_decompressor_p *handle = (struct mscab_decompressor_p *) base;
|
|
struct mscabd_folder_data *dat, *ndat;
|
|
struct mscabd_cabinet *cab, *ncab;
|
|
struct mscabd_folder *fol, *nfol;
|
|
struct mscabd_file *fi, *nfi;
|
|
struct mspack_system *sys;
|
|
|
|
if (!base) return;
|
|
sys = handle->system;
|
|
|
|
handle->error = MSPACK_ERR_OK;
|
|
|
|
while (origcab) {
|
|
/* free files */
|
|
for (fi = origcab->files; fi; fi = nfi) {
|
|
nfi = fi->next;
|
|
sys->free(fi->filename);
|
|
sys->free(fi);
|
|
}
|
|
|
|
for (fol = origcab->folders; fol; fol = nfol) {
|
|
nfol = fol->next;
|
|
|
|
if (handle->d && (handle->d->folder == (struct mscabd_folder_p *) fol)) {
|
|
if (handle->d->infh) sys->close(handle->d->infh);
|
|
cabd_free_decomp(handle);
|
|
sys->free(handle->d);
|
|
handle->d = NULL;
|
|
}
|
|
|
|
for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) {
|
|
ndat = dat->next;
|
|
sys->free(dat);
|
|
}
|
|
sys->free(fol);
|
|
}
|
|
|
|
for (cab = origcab; cab; cab = ncab) {
|
|
ncab = cab->prevcab;
|
|
sys->free(cab->prevname);
|
|
sys->free(cab->nextname);
|
|
sys->free(cab->previnfo);
|
|
sys->free(cab->nextinfo);
|
|
if (cab != origcab) sys->free(cab);
|
|
}
|
|
|
|
for (cab = origcab->nextcab; cab; cab = ncab) {
|
|
ncab = cab->nextcab;
|
|
sys->free(cab->prevname);
|
|
sys->free(cab->nextname);
|
|
sys->free(cab->previnfo);
|
|
sys->free(cab->nextinfo);
|
|
sys->free(cab);
|
|
}
|
|
|
|
cab = origcab->next;
|
|
sys->free(origcab);
|
|
|
|
origcab = cab;
|
|
}
|
|
}
|
|
|
|
static int cabd_read_headers(struct mspack_system *sys,
|
|
struct mspack_file *fh,
|
|
struct mscabd_cabinet_p *cab,
|
|
off_t offset, int quiet)
|
|
{
|
|
int num_folders, num_files, folder_resv, i, x;
|
|
struct mscabd_folder_p *fol, *linkfol = NULL;
|
|
struct mscabd_file *file, *linkfile = NULL;
|
|
unsigned char buf[64];
|
|
|
|
cab->base.next = NULL;
|
|
cab->base.files = NULL;
|
|
cab->base.folders = NULL;
|
|
cab->base.prevcab = cab->base.nextcab = NULL;
|
|
cab->base.prevname = cab->base.nextname = NULL;
|
|
cab->base.previnfo = cab->base.nextinfo = NULL;
|
|
|
|
cab->base.base_offset = offset;
|
|
|
|
if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) {
|
|
return MSPACK_ERR_SEEK;
|
|
}
|
|
|
|
if (sys->read(fh, &buf[0], cfhead_SIZEOF) != cfhead_SIZEOF) {
|
|
return MSPACK_ERR_READ;
|
|
}
|
|
|
|
if (EndGetI32(&buf[cfhead_Signature]) != 0x4643534D) {
|
|
return MSPACK_ERR_SIGNATURE;
|
|
}
|
|
|
|
cab->base.length = EndGetI32(&buf[cfhead_CabinetSize]);
|
|
cab->base.set_id = EndGetI16(&buf[cfhead_SetID]);
|
|
cab->base.set_index = EndGetI16(&buf[cfhead_CabinetIndex]);
|
|
|
|
num_folders = EndGetI16(&buf[cfhead_NumFolders]);
|
|
if (num_folders == 0) {
|
|
if (!quiet) sys->message(fh, "no folders in cabinet.");
|
|
return MSPACK_ERR_DATAFORMAT;
|
|
}
|
|
|
|
num_files = EndGetI16(&buf[cfhead_NumFiles]);
|
|
if (num_files == 0) {
|
|
if (!quiet) sys->message(fh, "no files in cabinet.");
|
|
return MSPACK_ERR_DATAFORMAT;
|
|
}
|
|
|
|
if ((buf[cfhead_MajorVersion] != 1) && (buf[cfhead_MinorVersion] != 3)) {
|
|
if (!quiet) sys->message(fh, "WARNING; cabinet version is not 1.3");
|
|
}
|
|
|
|
cab->base.flags = EndGetI16(&buf[cfhead_Flags]);
|
|
if (cab->base.flags & cfheadRESERVE_PRESENT) {
|
|
if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) {
|
|
return MSPACK_ERR_READ;
|
|
}
|
|
cab->base.header_resv = EndGetI16(&buf[cfheadext_HeaderReserved]);
|
|
folder_resv = buf[cfheadext_FolderReserved];
|
|
cab->block_resv = buf[cfheadext_DataReserved];
|
|
|
|
if (cab->base.header_resv > 60000) {
|
|
if (!quiet) sys->message(fh, "WARNING; reserved header > 60000.");
|
|
}
|
|
|
|
if (cab->base.header_resv) {
|
|
if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) {
|
|
return MSPACK_ERR_SEEK;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
cab->base.header_resv = 0;
|
|
folder_resv = 0;
|
|
cab->block_resv = 0;
|
|
}
|
|
|
|
if (cab->base.flags & cfheadPREV_CABINET) {
|
|
cab->base.prevname = cabd_read_string(sys, fh, cab, &x); if (x) return x;
|
|
cab->base.previnfo = cabd_read_string(sys, fh, cab, &x); if (x) return x;
|
|
}
|
|
|
|
if (cab->base.flags & cfheadNEXT_CABINET) {
|
|
cab->base.nextname = cabd_read_string(sys, fh, cab, &x); if (x) return x;
|
|
cab->base.nextinfo = cabd_read_string(sys, fh, cab, &x); if (x) return x;
|
|
}
|
|
|
|
for (i = 0; i < num_folders; i++) {
|
|
if (sys->read(fh, &buf[0], cffold_SIZEOF) != cffold_SIZEOF) {
|
|
return MSPACK_ERR_READ;
|
|
}
|
|
if (folder_resv) {
|
|
if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) {
|
|
return MSPACK_ERR_SEEK;
|
|
}
|
|
}
|
|
|
|
if (!(fol = (struct mscabd_folder_p *)sys->alloc(sys, sizeof(struct mscabd_folder_p)))) {
|
|
return MSPACK_ERR_NOMEMORY;
|
|
}
|
|
fol->base.next = NULL;
|
|
fol->base.comp_type = EndGetI16(&buf[cffold_CompType]);
|
|
fol->base.num_blocks = EndGetI16(&buf[cffold_NumBlocks]);
|
|
fol->data.next = NULL;
|
|
fol->data.cab = (struct mscabd_cabinet_p *) cab;
|
|
fol->data.offset = offset + (off_t)
|
|
( (unsigned int) EndGetI32(&buf[cffold_DataOffset]) );
|
|
fol->merge_prev = NULL;
|
|
fol->merge_next = NULL;
|
|
|
|
if (!linkfol) cab->base.folders = (struct mscabd_folder *) fol;
|
|
else linkfol->base.next = (struct mscabd_folder *) fol;
|
|
linkfol = fol;
|
|
}
|
|
|
|
for (i = 0; i < num_files; i++) {
|
|
if (sys->read(fh, &buf[0], cffile_SIZEOF) != cffile_SIZEOF) {
|
|
return MSPACK_ERR_READ;
|
|
}
|
|
|
|
if (!(file = (struct mscabd_file *)sys->alloc(sys, sizeof(struct mscabd_file)))) {
|
|
return MSPACK_ERR_NOMEMORY;
|
|
}
|
|
|
|
file->next = NULL;
|
|
file->length = EndGetI32(&buf[cffile_UncompressedSize]);
|
|
file->attribs = EndGetI16(&buf[cffile_Attribs]);
|
|
file->offset = EndGetI32(&buf[cffile_FolderOffset]);
|
|
|
|
x = EndGetI16(&buf[cffile_FolderIndex]);
|
|
if (x < cffileCONTINUED_FROM_PREV) {
|
|
struct mscabd_folder *ifol = cab->base.folders;
|
|
while (x--) if (ifol) ifol = ifol->next;
|
|
file->folder = ifol;
|
|
|
|
if (!ifol) {
|
|
sys->free(file);
|
|
return MSPACK_ERR_DATAFORMAT;
|
|
}
|
|
}
|
|
else {
|
|
if ((x == cffileCONTINUED_TO_NEXT) ||
|
|
(x == cffileCONTINUED_PREV_AND_NEXT))
|
|
{
|
|
struct mscabd_folder *ifol = cab->base.folders;
|
|
while (ifol->next) ifol = ifol->next;
|
|
file->folder = ifol;
|
|
|
|
fol = (struct mscabd_folder_p *) ifol;
|
|
if (!fol->merge_next) fol->merge_next = file;
|
|
}
|
|
|
|
if ((x == cffileCONTINUED_FROM_PREV) ||
|
|
(x == cffileCONTINUED_PREV_AND_NEXT))
|
|
{
|
|
file->folder = cab->base.folders;
|
|
|
|
fol = (struct mscabd_folder_p *) file->folder;
|
|
if (!fol->merge_prev) fol->merge_prev = file;
|
|
}
|
|
}
|
|
|
|
x = EndGetI16(&buf[cffile_Time]);
|
|
file->time_h = x >> 11;
|
|
file->time_m = (x >> 5) & 0x3F;
|
|
file->time_s = (x << 1) & 0x3E;
|
|
|
|
x = EndGetI16(&buf[cffile_Date]);
|
|
file->date_d = x & 0x1F;
|
|
file->date_m = (x >> 5) & 0xF;
|
|
file->date_y = (x >> 9) + 1980;
|
|
|
|
file->filename = cabd_read_string(sys, fh, cab, &x);
|
|
if (x) {
|
|
sys->free(file);
|
|
return x;
|
|
}
|
|
|
|
if (!linkfile) cab->base.files = file;
|
|
else linkfile->next = file;
|
|
linkfile = file;
|
|
}
|
|
|
|
return MSPACK_ERR_OK;
|
|
}
|
|
|
|
static char *cabd_read_string(struct mspack_system *sys,
|
|
struct mspack_file *fh,
|
|
struct mscabd_cabinet_p *, int *error)
|
|
{
|
|
off_t base = sys->tell(fh);
|
|
char buf[256], *str;
|
|
unsigned int len, i, ok;
|
|
|
|
len = sys->read(fh, &buf[0], 256);
|
|
|
|
for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; }
|
|
if (!ok) {
|
|
*error = MSPACK_ERR_DATAFORMAT;
|
|
return NULL;
|
|
}
|
|
|
|
len = i + 1;
|
|
|
|
if (sys->seek(fh, base + (off_t)len, MSPACK_SYS_SEEK_START)) {
|
|
*error = MSPACK_ERR_SEEK;
|
|
return NULL;
|
|
}
|
|
|
|
if (!(str = (char *)sys->alloc(sys, len))) {
|
|
*error = MSPACK_ERR_NOMEMORY;
|
|
return NULL;
|
|
}
|
|
|
|
sys->copy(&buf[0], str, len);
|
|
*error = MSPACK_ERR_OK;
|
|
return str;
|
|
}
|
|
|
|
static int cabd_extract(struct mscab_decompressor *base,
|
|
struct mscabd_file *file, char *filename)
|
|
{
|
|
struct mscab_decompressor_p *handle = (struct mscab_decompressor_p *) base;
|
|
struct mscabd_folder_p *fol;
|
|
struct mspack_system *sys;
|
|
struct mspack_file *fh;
|
|
|
|
if (!handle) return MSPACK_ERR_ARGS;
|
|
if (!file) return handle->error = MSPACK_ERR_ARGS;
|
|
|
|
sys = handle->system;
|
|
fol = (struct mscabd_folder_p *) file->folder;
|
|
|
|
/* check if file can be extracted */
|
|
if ((!fol) || (fol->merge_prev) ||
|
|
(((file->offset + file->length) / CAB_BLOCKMAX) > fol->base.num_blocks))
|
|
{
|
|
sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, "
|
|
"cabinet set is incomplete.", file->filename);
|
|
return handle->error = MSPACK_ERR_DATAFORMAT;
|
|
}
|
|
|
|
if (!handle->d) {
|
|
handle->d = (mscabd_decompress_state *)sys->alloc(sys, sizeof(struct mscabd_decompress_state));
|
|
if (!handle->d) return handle->error = MSPACK_ERR_NOMEMORY;
|
|
handle->d->folder = NULL;
|
|
handle->d->data = NULL;
|
|
handle->d->sys = *sys;
|
|
handle->d->sys.read = &cabd_sys_read;
|
|
handle->d->sys.write = &cabd_sys_write;
|
|
handle->d->state = NULL;
|
|
handle->d->infh = NULL;
|
|
handle->d->incab = NULL;
|
|
}
|
|
|
|
if ((handle->d->folder != fol) || (handle->d->offset > file->offset)) {
|
|
if (!handle->d->infh || (fol->data.cab != handle->d->incab)) {
|
|
if (handle->d->infh) sys->close(handle->d->infh);
|
|
handle->d->incab = fol->data.cab;
|
|
handle->d->infh = sys->open(sys, fol->data.cab->base.filename,
|
|
MSPACK_SYS_OPEN_READ);
|
|
if (!handle->d->infh) return handle->error = MSPACK_ERR_OPEN;
|
|
}
|
|
if (sys->seek(handle->d->infh, fol->data.offset, MSPACK_SYS_SEEK_START)) {
|
|
return handle->error = MSPACK_ERR_SEEK;
|
|
}
|
|
|
|
if (cabd_init_decomp(handle, (unsigned int) fol->base.comp_type)) {
|
|
return handle->error;
|
|
}
|
|
|
|
handle->d->folder = fol;
|
|
handle->d->data = &fol->data;
|
|
handle->d->offset = 0;
|
|
handle->d->block = 0;
|
|
handle->d->i_ptr = handle->d->i_end = &handle->d->input[0];
|
|
}
|
|
|
|
if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) {
|
|
return handle->error = MSPACK_ERR_OPEN;
|
|
}
|
|
|
|
handle->error = MSPACK_ERR_OK;
|
|
|
|
if (file->length) {
|
|
off_t bytes;
|
|
int error;
|
|
handle->d->outfh = NULL;
|
|
if ((bytes = file->offset - handle->d->offset)) {
|
|
error = handle->d->decompress(handle->d->state, bytes);
|
|
if (error != MSPACK_ERR_READ) handle->error = error;
|
|
}
|
|
|
|
if (!handle->error) {
|
|
handle->d->outfh = fh;
|
|
error = handle->d->decompress(handle->d->state, (off_t) file->length);
|
|
if (error != MSPACK_ERR_READ) handle->error = error;
|
|
}
|
|
}
|
|
|
|
sys->close(fh);
|
|
handle->d->outfh = NULL;
|
|
|
|
return handle->error;
|
|
}
|
|
|
|
static int cabd_init_decomp(struct mscab_decompressor_p *handle, unsigned int ct)
|
|
{
|
|
struct mspack_file *fh = (struct mspack_file *) handle;
|
|
|
|
if (!handle || !handle->d) {
|
|
return handle->error = MSPACK_ERR_ARGS;
|
|
}
|
|
|
|
cabd_free_decomp(handle);
|
|
|
|
handle->d->comp_type = ct;
|
|
|
|
switch (ct & cffoldCOMPTYPE_MASK) {
|
|
case cffoldCOMPTYPE_MSZIP:
|
|
handle->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress;
|
|
handle->d->state = mszipd_init(&handle->d->sys, fh, fh,
|
|
handle->param[MSCABD_PARAM_DECOMPBUF],
|
|
handle->param[MSCABD_PARAM_FIXMSZIP]);
|
|
break;
|
|
default:
|
|
return handle->error = MSPACK_ERR_DATAFORMAT;
|
|
}
|
|
return handle->error = (handle->d->state) ? MSPACK_ERR_OK : MSPACK_ERR_NOMEMORY;
|
|
}
|
|
|
|
static void cabd_free_decomp(struct mscab_decompressor_p *handle) {
|
|
if (!handle || !handle->d || !handle->d->folder || !handle->d->state) return;
|
|
|
|
switch (handle->d->comp_type & cffoldCOMPTYPE_MASK) {
|
|
case cffoldCOMPTYPE_MSZIP: mszipd_free((mszipd_stream *)handle->d->state); break;
|
|
}
|
|
handle->d->decompress = NULL;
|
|
handle->d->state = NULL;
|
|
}
|
|
|
|
static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) {
|
|
struct mscab_decompressor_p *handle = (struct mscab_decompressor_p *) file;
|
|
unsigned char *buf = (unsigned char *) buffer;
|
|
struct mspack_system *sys = handle->system;
|
|
int avail, todo, outlen = 0, ignore_cksum;
|
|
|
|
ignore_cksum = handle->param[MSCABD_PARAM_FIXMSZIP] &&
|
|
((handle->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP);
|
|
|
|
todo = bytes;
|
|
while (todo > 0) {
|
|
avail = handle->d->i_end - handle->d->i_ptr;
|
|
|
|
if (avail) {
|
|
if (avail > todo) avail = todo;
|
|
sys->copy(handle->d->i_ptr, buf, (size_t) avail);
|
|
handle->d->i_ptr += avail;
|
|
buf += avail;
|
|
todo -= avail;
|
|
}
|
|
else {
|
|
if (handle->d->block++ >= handle->d->folder->base.num_blocks) {
|
|
handle->error = MSPACK_ERR_DATAFORMAT;
|
|
break;
|
|
}
|
|
|
|
handle->error = cabd_sys_read_block(sys, handle->d, &outlen, ignore_cksum);
|
|
if (handle->error) return -1;
|
|
|
|
if (handle->d->block >= handle->d->folder->base.num_blocks) {
|
|
} else {
|
|
if (outlen != CAB_BLOCKMAX) {
|
|
handle->system->message(handle->d->infh,
|
|
"WARNING; non-maximal data block");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bytes - todo;
|
|
}
|
|
|
|
static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) {
|
|
struct mscab_decompressor_p *handle = (struct mscab_decompressor_p *) file;
|
|
handle->d->offset += bytes;
|
|
if (handle->d->outfh) {
|
|
return handle->system->write(handle->d->outfh, buffer, bytes);
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
static int cabd_sys_read_block(struct mspack_system *sys,
|
|
struct mscabd_decompress_state *d,
|
|
int *out, int ignore_cksum)
|
|
{
|
|
unsigned char hdr[cfdata_SIZEOF];
|
|
unsigned int cksum;
|
|
int len;
|
|
|
|
d->i_ptr = d->i_end = &d->input[0];
|
|
|
|
do {
|
|
if (sys->read(d->infh, &hdr[0], cfdata_SIZEOF) != cfdata_SIZEOF) {
|
|
return MSPACK_ERR_READ;
|
|
}
|
|
|
|
if (d->data->cab->block_resv &&
|
|
sys->seek(d->infh, (off_t) d->data->cab->block_resv,
|
|
MSPACK_SYS_SEEK_CUR))
|
|
{
|
|
return MSPACK_ERR_SEEK;
|
|
}
|
|
|
|
len = EndGetI16(&hdr[cfdata_CompressedSize]);
|
|
if (((d->i_end - d->i_ptr) + len) > CAB_INPUTMAX) {
|
|
return MSPACK_ERR_DATAFORMAT;
|
|
}
|
|
|
|
if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) {
|
|
return MSPACK_ERR_DATAFORMAT;
|
|
}
|
|
|
|
if (sys->read(d->infh, d->i_end, len) != len) {
|
|
return MSPACK_ERR_READ;
|
|
}
|
|
|
|
if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) {
|
|
unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0);
|
|
if (cabd_checksum(&hdr[4], 4, sum2) != cksum) {
|
|
if (!ignore_cksum) return MSPACK_ERR_CHECKSUM;
|
|
sys->message(d->infh, "WARNING; bad block checksum found");
|
|
}
|
|
}
|
|
|
|
d->i_end += len;
|
|
|
|
if ((*out = EndGetI16(&hdr[cfdata_UncompressedSize]))) {
|
|
return MSPACK_ERR_OK;
|
|
}
|
|
|
|
sys->close(d->infh);
|
|
d->infh = NULL;
|
|
|
|
if (!(d->data = d->data->next)) {
|
|
return MSPACK_ERR_DATAFORMAT;
|
|
}
|
|
|
|
d->incab = d->data->cab;
|
|
if (!(d->infh = sys->open(sys, d->incab->base.filename,
|
|
MSPACK_SYS_OPEN_READ)))
|
|
{
|
|
return MSPACK_ERR_OPEN;
|
|
}
|
|
|
|
if (sys->seek(d->infh, d->data->offset, MSPACK_SYS_SEEK_START)) {
|
|
return MSPACK_ERR_SEEK;
|
|
}
|
|
} while (1);
|
|
|
|
return MSPACK_ERR_OK;
|
|
}
|
|
|
|
static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes,
|
|
unsigned int cksum)
|
|
{
|
|
unsigned int len, ul = 0;
|
|
|
|
for (len = bytes >> 2; len--; data += 4) {
|
|
cksum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24));
|
|
}
|
|
|
|
switch (bytes & 3) {
|
|
case 3: ul |= *data++ << 16;
|
|
case 2: ul |= *data++ << 8;
|
|
case 1: ul |= *data;
|
|
}
|
|
cksum ^= ul;
|
|
|
|
return cksum;
|
|
}
|
|
|
|
static int cabd_error(struct mscab_decompressor *base) {
|
|
struct mscab_decompressor_p *handle = (struct mscab_decompressor_p *) base;
|
|
return (handle) ? handle->error : MSPACK_ERR_ARGS;
|
|
}
|