/* Sarien - A Sierra AGI resource interpreter engine * Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka * * $Id: lzw.c,v 1.4 2001/04/26 16:16:33 cmatsuoka Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; see docs/COPYING for further details. */ /*************************************************************************** ** decomp.c ** ** Routines that deal with AGI version 3 specific features. ** The original LZW code is from DJJ, October 1989, p.86. ** It has been modified to handle AGI compression. ** ** (c) 1997 Lance Ewing ***************************************************************************/ #include #include #include "sarien.h" #include "lzw.h" #define MAXBITS 12 #define TABLE_SIZE 18041 /* strange number */ #define START_BITS 9 SINT32 BITS, MAX_VALUE, MAX_CODE; UINT32 *prefix_code; UINT8 *append_character; UINT8 *decode_stack; static SINT32 input_bit_count=0; /* Number of bits in input bit buffer */ static UINT32 input_bit_buffer=0L; void initLZW () { decode_stack = calloc (1, 8192); prefix_code= malloc (TABLE_SIZE * sizeof(UINT32)); append_character= malloc (TABLE_SIZE * sizeof(UINT8)); input_bit_count = 0; /* Number of bits in input bit buffer */ input_bit_buffer = 0L; } void closeLZW () { free (decode_stack); free (prefix_code); free (append_character); } /*************************************************************************** ** setBITS ** ** Purpose: To adjust the number of bits used to store codes to the value ** passed in. ***************************************************************************/ int setBITS (SINT32 value) { if (value == MAXBITS) return TRUE; BITS = value; MAX_VALUE = (1 << BITS) - 1; MAX_CODE = MAX_VALUE - 1; return FALSE; } /*************************************************************************** ** decode_string ** ** Purpose: To return the string that the code taken from the input buffer ** represents. The string is returned as a stack, i.e. the characters are ** in reverse order. ***************************************************************************/ UINT8 *decode_string(UINT8 *buffer, UINT32 code) { UINT32 i; for (i = 0; code > 255; ) { *buffer++ = append_character[code]; code=prefix_code[code]; if (i++ >= 4000) { fprintf (stderr, "lzw: error in code expansion.\n"); abort (); } } *buffer = code; return buffer; } /*************************************************************************** ** input_code ** ** Purpose: To return the next code from the input buffer. ***************************************************************************/ UINT32 input_code (UINT8 **input) { UINT32 r; while (input_bit_count <= 24) { input_bit_buffer |= (UINT32) *(*input)++ << input_bit_count; input_bit_count += 8; } r = (input_bit_buffer & 0x7FFF) % (1 << BITS); input_bit_buffer >>= BITS; input_bit_count -= BITS; return r; } /*************************************************************************** ** expand ** ** Purpose: To uncompress the data contained in the input buffer and store ** the result in the output buffer. The fileLength parameter says how ** many bytes to uncompress. The compression itself is a form of LZW that ** adjusts the number of bits that it represents its codes in as it fills ** up the available codes. Two codes have special meaning: ** ** code 256 = start over ** code 257 = end of data ***************************************************************************/ void LZW_expand(UINT8 *in, UINT8 *out, SINT32 len) { SINT32 lzwnext, lzwnew, lzwold; SINT32 c, bits; UINT8 *s, *end; initLZW(); bits = setBITS(START_BITS); /* Starts at 9-bits */ lzwnext = 257; /* Next available code to define */ end = (unsigned char *)((long)out + (long)len); lzwold = input_code(&in); /* Read in the first code */ c = lzwold; lzwnew = input_code(&in); while ((out < end) && (lzwnew != 0x101)) { if (lzwnew == 0x100) { /* Code to "start over" */ lzwnext = 258; bits = setBITS(START_BITS); lzwold = input_code(&in); c = lzwold; *out++ = (char)c; lzwnew = input_code(&in); } else { if (lzwnew >= lzwnext) { /* Handles special LZW scenario */ *decode_stack = c; s = decode_string(decode_stack+1, lzwold); } else s = decode_string(decode_stack, lzwnew); /* Reverse order of decoded string and * store in out buffer */ c = *s; while (s >= decode_stack) *out++ = *s--; if (lzwnext > MAX_CODE) bits = setBITS(BITS + 1); prefix_code[lzwnext] = lzwold; append_character[lzwnext] = c; lzwnext++; lzwold = lzwnew; lzwnew = input_code(&in); } } closeLZW (); }