2013-03-11 17:46:03 +00:00
|
|
|
/* This file is part of libmspack.
|
|
|
|
* (C) 2003-2010 Stuart Caie.
|
|
|
|
*
|
|
|
|
* LZSS is a derivative of LZ77 and was created by James Storer and
|
|
|
|
* Thomas Szymanski in 1982. Haruhiko Okumura wrote a very popular C
|
|
|
|
* implementation.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2013-03-11 17:51:11 +00:00
|
|
|
#include "system-mspack.h"
|
|
|
|
#include "lzss.h"
|
2013-03-11 17:46:03 +00:00
|
|
|
|
|
|
|
#define ENSURE_BYTES do { \
|
|
|
|
if (i_ptr >= i_end) { \
|
|
|
|
read = system->read(input, &inbuf[0], \
|
|
|
|
input_buffer_size); \
|
|
|
|
if (read <= 0) { \
|
|
|
|
system->free(window); \
|
|
|
|
return (read < 0) ? MSPACK_ERR_READ \
|
|
|
|
: MSPACK_ERR_OK; \
|
|
|
|
} \
|
|
|
|
i_ptr = &inbuf[0]; i_end = &inbuf[read]; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define WRITE_BYTE do { \
|
|
|
|
if (system->write(output, &window[pos], 1) != 1) { \
|
|
|
|
system->free(window); \
|
|
|
|
return MSPACK_ERR_WRITE; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
int lzss_decompress(struct mspack_system *system,
|
|
|
|
struct mspack_file *input,
|
|
|
|
struct mspack_file *output,
|
|
|
|
int input_buffer_size,
|
|
|
|
int mode)
|
|
|
|
{
|
|
|
|
unsigned char *window, *inbuf, *i_ptr, *i_end;
|
|
|
|
unsigned int pos, i, c, invert, mpos, len;
|
|
|
|
int read;
|
|
|
|
|
|
|
|
/* check parameters */
|
|
|
|
if (!system || input_buffer_size < 1 || (mode != LZSS_MODE_EXPAND &&
|
|
|
|
mode != LZSS_MODE_MSHELP && mode != LZSS_MODE_QBASIC))
|
|
|
|
{
|
|
|
|
return MSPACK_ERR_ARGS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate memory */
|
|
|
|
window = (unsigned char *) system->alloc(system, LZSS_WINDOW_SIZE + input_buffer_size);
|
|
|
|
if (!window) return MSPACK_ERR_NOMEMORY;
|
|
|
|
|
|
|
|
/* initialise decompression */
|
|
|
|
inbuf = &window[LZSS_WINDOW_SIZE];
|
|
|
|
memset(window, LZSS_WINDOW_FILL, (size_t) LZSS_WINDOW_SIZE);
|
|
|
|
pos = LZSS_WINDOW_SIZE - ((mode == LZSS_MODE_QBASIC) ? 18 : 16);
|
|
|
|
invert = (mode == LZSS_MODE_MSHELP) ? ~0 : 0;
|
|
|
|
i_ptr = i_end = &inbuf[0];
|
|
|
|
|
|
|
|
/* loop forever; exit condition is in ENSURE_BYTES macro */
|
|
|
|
for (;;) {
|
|
|
|
ENSURE_BYTES; c = *i_ptr++ ^ invert;
|
|
|
|
for (i = 0x01; i & 0xFF; i <<= 1) {
|
|
|
|
if (c & i) {
|
|
|
|
/* literal */
|
|
|
|
ENSURE_BYTES; window[pos] = *i_ptr++;
|
|
|
|
WRITE_BYTE;
|
|
|
|
pos++; pos &= LZSS_WINDOW_SIZE - 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* match */
|
|
|
|
ENSURE_BYTES; mpos = *i_ptr++;
|
|
|
|
ENSURE_BYTES; mpos |= (*i_ptr & 0xF0) << 4;
|
|
|
|
len = (*i_ptr++ & 0x0F) + 3;
|
|
|
|
while (len--) {
|
|
|
|
window[pos] = window[mpos];
|
|
|
|
WRITE_BYTE;
|
|
|
|
pos++; pos &= LZSS_WINDOW_SIZE - 1;
|
|
|
|
mpos++; mpos &= LZSS_WINDOW_SIZE - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* not reached */
|
|
|
|
system->free(window);
|
|
|
|
return MSPACK_ERR_OK;
|
|
|
|
}
|