Created
March 27, 2012 08:02
-
-
Save Zab-xx/2213872 to your computer and use it in GitHub Desktop.
Modified LZW decompression code (MSVC) to work with Stellar7 RES data.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Some hacked up LZW decompression code (MSVC++ WARNING) | |
* | |
* Designed to work with Stellar7 .RES format: | |
* http://www.shikadi.net/moddingwiki/RES_Format_(Stellar_7) | |
* | |
* Sample usage: | |
* --------------------------------------------------- | |
// You must provide these two | |
char *input_data; | |
int uncompressed_length; | |
// Here is the rest: | |
char *output_data = new char[uncompressed_length]; | |
LZW lzw; | |
lzw.expand((LZW::UINT8*)input_data, (LZW::UINT8*)output_data, uncompressed_length); | |
* --------------------------------------------------- | |
* | |
* Original source found at: | |
* http://src.gnu-darwin.org/ports/games/sarien/work/sarien-0.7.0/src/core/lzw.c | |
* | |
* Modified 2012 from the original source by Brian Zablocky. | |
* The copyright was included with the original source is: | |
* | |
* 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 | |
************************************************************************** | |
* | |
*/ | |
class LZW | |
{ | |
public: | |
typedef unsigned __int32 UINT32; | |
typedef unsigned char UINT8; | |
private: | |
typedef __int32 SINT32; | |
static const int MAXBITS = 12; | |
static const int TABLE_SIZE = 18041; | |
static const int START_BITS = 9; | |
SINT32 BITS; | |
SINT32 MAX_VALUE; | |
SINT32 MAX_CODE; | |
UINT8 *decode_stack; | |
UINT32 *prefix_code; | |
UINT8 *append_character; | |
SINT32 input_bit_count; | |
UINT32 input_bit_buffer; | |
int setBITS (SINT32 value); | |
UINT8* decode_string(UINT8 *buffer, UINT32 code); | |
UINT32 input_code(UINT8 **input); | |
public: | |
LZW(); | |
~LZW(); | |
void expand(UINT8 *in, UINT8 *out, SINT32 len); | |
}; // class LZW | |
#include <cstdlib> | |
#include <stdio.h> | |
#include <string.h> | |
#include <iostream> | |
LZW::LZW () | |
{ | |
decode_stack = (UINT8 *)calloc (1, 8192); | |
prefix_code = (UINT32 *)malloc(TABLE_SIZE * sizeof(UINT32)); | |
append_character = (UINT8 *)malloc (TABLE_SIZE * sizeof(UINT8)); | |
input_bit_count = 0; /* Number of bits in input bit buffer */ | |
input_bit_buffer = 0L; | |
} | |
LZW::~LZW () | |
{ | |
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 LZW::setBITS (SINT32 value) | |
{ | |
if (value > MAXBITS) | |
return 1; | |
BITS = value; | |
MAX_VALUE = (1 << BITS) - 1; | |
MAX_CODE = MAX_VALUE - 1; | |
return 0; | |
} | |
/*************************************************************************** | |
** 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. | |
***************************************************************************/ | |
LZW::UINT8 *LZW::decode_string(UINT8 *buffer, UINT32 code) | |
{ | |
UINT32 i; | |
for (i = 0; code > 255; ) { | |
*buffer++ = append_character[code]; | |
code = prefix_code[code]; | |
if (i++ >= 4000) | |
{ | |
throw -1; | |
return buffer; | |
} | |
} | |
*buffer = code; | |
return buffer; | |
} | |
/*************************************************************************** | |
** input_code | |
** | |
** Purpose: To return the next code from the input buffer. | |
***************************************************************************/ | |
LZW::UINT32 LZW::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 bits = setBITS(START_BITS); /* Starts at 9-bits */ | |
SINT32 lzwnext = 257; /* Next available code to define */ | |
UINT8 *end = (unsigned char *)(out + (long)len); | |
SINT32 lzwold = input_code(&in); /* Read in the first code */ | |
SINT32 c = lzwold; | |
SINT32 lzwnew = input_code(&in); | |
UINT8 *s; | |
while (out < end) | |
{ | |
if (lzwnew == 0x100) | |
{ | |
// TODO: FIX THIS BLOCK | |
// Everything works up until the reset code 0x100 is encountered. | |
bits = setBITS(START_BITS); | |
lzwnext = 257; | |
lzwold = input_code(&in); | |
c = lzwold; | |
//*out++ = (char)c; | |
lzwnew = input_code(&in); | |
} | |
if (lzwnew >= lzwnext) | |
{ | |
*decode_stack = c; | |
s = decode_string(decode_stack+1, lzwold); | |
} | |
else | |
s = decode_string(decode_stack, lzwnew); | |
c = *s; | |
while (s >= decode_stack && out < end) | |
*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); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Line 204 is where we have the problem. Not every compressed block in Stellar7's game data has a code 0x100 reset, but the ones they do no longer decompress properly from this point on. New data appears to be mangled and inconsistent with what has been previously decoded.