2013-03-11 17:46:03 +00:00
|
|
|
/* This file is part of libmspack.
|
|
|
|
* (C) 2003-2010 Stuart Caie.
|
|
|
|
*
|
|
|
|
* SZDD is a format used in the MS-DOS commands COMPRESS.EXE and
|
|
|
|
* EXPAND.EXE. The compression method is attributed to Steven Zeck,
|
|
|
|
* however it's pretty much identical to LZSS.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* SZDD decompression implementation */
|
|
|
|
|
2013-03-11 17:51:11 +00:00
|
|
|
#include "system-mspack.h"
|
|
|
|
#include "szdd.h"
|
2013-03-11 17:46:03 +00:00
|
|
|
|
|
|
|
/* prototypes */
|
|
|
|
static struct msszddd_header *szddd_open(
|
|
|
|
struct msszdd_decompressor *base, const char *filename);
|
|
|
|
static void szddd_close(
|
|
|
|
struct msszdd_decompressor *base, struct msszddd_header *hdr);
|
|
|
|
static int szddd_read_headers(
|
|
|
|
struct mspack_system *sys, struct mspack_file *fh,
|
|
|
|
struct msszddd_header *hdr);
|
|
|
|
static int szddd_extract(
|
|
|
|
struct msszdd_decompressor *base, struct msszddd_header *hdr,
|
|
|
|
const char *filename);
|
|
|
|
static int szddd_decompress(
|
|
|
|
struct msszdd_decompressor *base, const char *input, const char *output);
|
|
|
|
static int szddd_error(
|
|
|
|
struct msszdd_decompressor *base);
|
|
|
|
|
|
|
|
/***************************************
|
|
|
|
* MSPACK_CREATE_SZDD_DECOMPRESSOR
|
|
|
|
***************************************
|
|
|
|
* constructor
|
|
|
|
*/
|
|
|
|
struct msszdd_decompressor *
|
|
|
|
mspack_create_szdd_decompressor(struct mspack_system *sys)
|
|
|
|
{
|
|
|
|
struct msszdd_decompressor_p *self = NULL;
|
|
|
|
|
|
|
|
if (!sys) sys = mspack_default_system;
|
|
|
|
if (!mspack_valid_system(sys)) return NULL;
|
|
|
|
|
|
|
|
if ((self = (struct msszdd_decompressor_p *) sys->alloc(sys, sizeof(struct msszdd_decompressor_p)))) {
|
|
|
|
self->base.open = &szddd_open;
|
|
|
|
self->base.close = &szddd_close;
|
|
|
|
self->base.extract = &szddd_extract;
|
|
|
|
self->base.decompress = &szddd_decompress;
|
|
|
|
self->base.last_error = &szddd_error;
|
|
|
|
self->system = sys;
|
|
|
|
self->error = MSPACK_ERR_OK;
|
|
|
|
}
|
|
|
|
return (struct msszdd_decompressor *) self;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************
|
|
|
|
* MSPACK_DESTROY_SZDD_DECOMPRESSOR
|
|
|
|
***************************************
|
|
|
|
* destructor
|
|
|
|
*/
|
|
|
|
void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *base)
|
|
|
|
{
|
|
|
|
struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
|
|
|
|
if (self) {
|
|
|
|
struct mspack_system *sys = self->system;
|
|
|
|
sys->free(self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************
|
|
|
|
* SZDDD_OPEN
|
|
|
|
***************************************
|
|
|
|
* opens an SZDD file without decompressing, reads header
|
|
|
|
*/
|
|
|
|
static struct msszddd_header *szddd_open(struct msszdd_decompressor *base,
|
|
|
|
const char *filename)
|
|
|
|
{
|
|
|
|
struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
|
|
|
|
struct msszddd_header *hdr;
|
|
|
|
struct mspack_system *sys;
|
|
|
|
struct mspack_file *fh;
|
|
|
|
|
|
|
|
if (!self) return NULL;
|
|
|
|
sys = self->system;
|
|
|
|
|
|
|
|
fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ);
|
|
|
|
hdr = (struct msszddd_header *) sys->alloc(sys, sizeof(struct msszddd_header_p));
|
|
|
|
if (fh && hdr) {
|
|
|
|
((struct msszddd_header_p *) hdr)->fh = fh;
|
|
|
|
self->error = szddd_read_headers(sys, fh, hdr);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!fh) self->error = MSPACK_ERR_OPEN;
|
|
|
|
if (!hdr) self->error = MSPACK_ERR_NOMEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->error) {
|
|
|
|
if (fh) sys->close(fh);
|
|
|
|
if (hdr) sys->free(hdr);
|
|
|
|
hdr = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************
|
|
|
|
* SZDDD_CLOSE
|
|
|
|
***************************************
|
|
|
|
* closes an SZDD file
|
|
|
|
*/
|
|
|
|
static void szddd_close(struct msszdd_decompressor *base,
|
|
|
|
struct msszddd_header *hdr)
|
|
|
|
{
|
|
|
|
struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
|
|
|
|
struct msszddd_header_p *hdr_p = (struct msszddd_header_p *) hdr;
|
|
|
|
|
|
|
|
if (!self || !self->system) return;
|
|
|
|
|
|
|
|
/* close the file handle associated */
|
|
|
|
self->system->close(hdr_p->fh);
|
|
|
|
|
|
|
|
/* free the memory associated */
|
|
|
|
self->system->free(hdr);
|
|
|
|
|
|
|
|
self->error = MSPACK_ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************
|
|
|
|
* SZDDD_READ_HEADERS
|
|
|
|
***************************************
|
|
|
|
* reads the headers of an SZDD format file
|
|
|
|
*/
|
|
|
|
static unsigned char szdd_signature_expand[8] = {
|
|
|
|
0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33
|
|
|
|
};
|
|
|
|
static unsigned char szdd_signature_qbasic[8] = {
|
|
|
|
0x53, 0x5A, 0x20, 0x88, 0xF0, 0x27, 0x33, 0xD1
|
|
|
|
};
|
|
|
|
|
|
|
|
static int szddd_read_headers(struct mspack_system *sys,
|
|
|
|
struct mspack_file *fh,
|
|
|
|
struct msszddd_header *hdr)
|
|
|
|
{
|
|
|
|
unsigned char buf[8];
|
|
|
|
|
|
|
|
/* read and check signature */
|
|
|
|
if (sys->read(fh, buf, 8) != 8) return MSPACK_ERR_READ;
|
|
|
|
|
|
|
|
if ((mspack_memcmp(buf, szdd_signature_expand, 8) == 0)) {
|
|
|
|
/* common SZDD */
|
|
|
|
hdr->format = MSSZDD_FMT_NORMAL;
|
|
|
|
|
|
|
|
/* read the rest of the header */
|
|
|
|
if (sys->read(fh, buf, 6) != 6) return MSPACK_ERR_READ;
|
|
|
|
if (buf[0] != 0x41) return MSPACK_ERR_DATAFORMAT;
|
|
|
|
hdr->missing_char = buf[1];
|
|
|
|
hdr->length = EndGetI32(&buf[2]);
|
|
|
|
}
|
|
|
|
else if ((mspack_memcmp(buf, szdd_signature_qbasic, 8) == 0)) {
|
|
|
|
/* special QBasic SZDD */
|
|
|
|
hdr->format = MSSZDD_FMT_QBASIC;
|
|
|
|
if (sys->read(fh, buf, 4) != 4) return MSPACK_ERR_READ;
|
|
|
|
hdr->missing_char = '\0';
|
|
|
|
hdr->length = EndGetI32(buf);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return MSPACK_ERR_SIGNATURE;
|
|
|
|
}
|
|
|
|
return MSPACK_ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************
|
|
|
|
* SZDDD_EXTRACT
|
|
|
|
***************************************
|
|
|
|
* decompresses an SZDD file
|
|
|
|
*/
|
|
|
|
static int szddd_extract(struct msszdd_decompressor *base,
|
|
|
|
struct msszddd_header *hdr, const char *filename)
|
|
|
|
{
|
|
|
|
struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
|
|
|
|
struct mspack_file *fh, *outfh;
|
|
|
|
struct mspack_system *sys;
|
|
|
|
off_t data_offset;
|
|
|
|
|
|
|
|
if (!self) return MSPACK_ERR_ARGS;
|
|
|
|
if (!hdr) return self->error = MSPACK_ERR_ARGS;
|
|
|
|
sys = self->system;
|
|
|
|
|
|
|
|
fh = ((struct msszddd_header_p *) hdr)->fh;
|
|
|
|
|
|
|
|
/* seek to the compressed data */
|
|
|
|
data_offset = (hdr->format == MSSZDD_FMT_NORMAL) ? 14 : 12;
|
|
|
|
if (sys->seek(fh, data_offset, MSPACK_SYS_SEEK_START)) {
|
|
|
|
return self->error = MSPACK_ERR_SEEK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* open file for output */
|
|
|
|
if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) {
|
|
|
|
return self->error = MSPACK_ERR_OPEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* decompress the data */
|
|
|
|
self->error = lzss_decompress(sys, fh, outfh, SZDD_INPUT_SIZE,
|
|
|
|
hdr->format == MSSZDD_FMT_NORMAL
|
|
|
|
? LZSS_MODE_EXPAND
|
|
|
|
: LZSS_MODE_QBASIC);
|
|
|
|
|
|
|
|
/* close output file */
|
|
|
|
sys->close(outfh);
|
|
|
|
|
|
|
|
return self->error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************
|
|
|
|
* SZDDD_DECOMPRESS
|
|
|
|
***************************************
|
|
|
|
* unpacks directly from input to output
|
|
|
|
*/
|
|
|
|
static int szddd_decompress(struct msszdd_decompressor *base,
|
|
|
|
const char *input, const char *output)
|
|
|
|
{
|
|
|
|
struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
|
|
|
|
struct msszddd_header *hdr;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (!self) return MSPACK_ERR_ARGS;
|
|
|
|
|
|
|
|
if (!(hdr = szddd_open(base, input))) return self->error;
|
|
|
|
error = szddd_extract(base, hdr, output);
|
|
|
|
szddd_close(base, hdr);
|
|
|
|
return self->error = error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************
|
|
|
|
* SZDDD_ERROR
|
|
|
|
***************************************
|
|
|
|
* returns the last error that occurred
|
|
|
|
*/
|
|
|
|
static int szddd_error(struct msszdd_decompressor *base)
|
|
|
|
{
|
|
|
|
struct msszdd_decompressor_p *self = (struct msszdd_decompressor_p *) base;
|
|
|
|
return (self) ? self->error : MSPACK_ERR_ARGS;
|
|
|
|
}
|