Star Control 3 - PIC File Format (using old library Crusher)

Read or post about compression. And decompression. Or ask questions how to decompress your files.
Post Reply
0xdeadbeef
ultra-n00b
Posts: 6
Joined: Mon Feb 01, 2016 12:34 am
Been thanked: 4 times

Star Control 3 - PIC File Format (using old library Crusher)

Post by 0xdeadbeef » Sun Nov 05, 2017 4:01 am

Hi all

I am going to share the file format for all the PIC files in the old game Star Control 3. These files are archives compressed using the old library Crusher! by David Cecil.

Here is a download link to crushers.dll:

http://s000.tinyupload.com/?file_id=082 ... 7420998422

SHA-256: F0E44B9F9FE5E4AF6A8B11A5F0AF65A11D696F0291721C4B2923ED84495A26D5

Here is some quick Python code showing how to use the DLL. This is a 32-bit DLL, so you will need a 32-bit Python installation. Unfortunately there isn't an easy way around this.

Code: Select all

"""

Interface to the long-defunct Crusher! compression library by David Cecil.

Initialize with the path to crushers.dll:

    crusher.init(dllpath)
    
Expand from a bytes object to another bytes object of specified size:

    result = crusher.expand(compressed_data, output_size)
    
Errors from crushers.dll will generally raise crusher.error.  However,
incorrect parameters to crusher.expand may cause access violations.

"""

import atexit
import ctypes

ERROR_FORMAT = '{}{} failed with error code {}'

class error(Exception):
    def __init__(self, msg):
        self.msg = msg
        
def _cxTry(func, *args):
    rc = func(*args)
    if rc != 0:
        raise error(ERROR_FORMAT.format(func.__name__, args, rc))
    
def expand(data, outputsize):
    _cxTry(_crusherdll.cxBuf2BufInit)
    outbuf = ctypes.create_string_buffer(outputsize)
    try:
        _cxTry(_expander, data, outbuf, outputsize, len(data))
    except:
        raise
    finally:
        _cxTry(_crusherdll.cxBuf2BufClose)
    return outbuf.raw
        
_crusherdll = None
_expander = None

def init(dllpath):
    global _crusherdll
    global _expander
    _crusherdll = ctypes.WinDLL(dllpath)
    _cxTry(_crusherdll.cxInit)
    _expander = _crusherdll.cxBuf2BufExpand
    _expander.argtypes = (
        ctypes.c_char_p, ctypes.c_char_p,
        ctypes.c_ulong, ctypes.c_ulong
    )
    atexit.register(_close)

def _close():
    _cxTry(_crusherdll.cxCleanup)
Here is pseudocode showing the format of the PIC files:

Code: Select all

struct fileinfo_t {
    int32_t offset;
    int32_t compressed_size;
    // Flags: 0 - normal file, 4 - cursor file, 0xffff - has palette patch, see below
    int16_t flags;
    // Drawing offsets:
    int16_t x, y;
    // Uncompressed size is w * h
    int16_t w, h;
    int16_t alwayszero;
};

// Header:
int16_t filecount;
fileinfo_t directory[filecount];

// The rest of the archive is the packed file data.

// Files with a palette patch have this format:
int16_t first_color;
int16_t palette_patch_size;
char palette_patch[palette_patch_size];
char compressed_data[fileinfo.compressed_size - 4 - palette_patch_size];
Hopefully this post is useful to anyone who wants to play around with this old game, or has something else compressed with Crusher.

Post Reply