libclammspack/mspack/lzssd.c
d3699d57
 /* 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
  */
 
 #include <system.h>
 #include <lzss.h>
 
a7ac0e8d
 #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];        \
     }                                                   \
d3699d57
 } while (0)
 
a7ac0e8d
 #define WRITE_BYTE do {                                 \
     if (system->write(output, &window[pos], 1) != 1) {  \
         system->free(window);                           \
         return MSPACK_ERR_WRITE;                        \
     }                                                   \
d3699d57
 } while (0)
 
 int lzss_decompress(struct mspack_system *system,
a7ac0e8d
                     struct mspack_file *input,
                     struct mspack_file *output,
                     int input_buffer_size,
                     int mode)
d3699d57
 {
     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))
     {
a7ac0e8d
         return MSPACK_ERR_ARGS;
d3699d57
     }
 
     /* 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 (;;) {
a7ac0e8d
         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;
                 }
             }
         }
d3699d57
     }
 
     /* not reached */
     system->free(window);
     return MSPACK_ERR_OK;
 }