/*
* COMPRESS.C:
*
* Routines to do LZW compression on the player data.
*
* Copyright (C) 1992 Brett J. Vickers
*
*/
#ifdef COMPRESS
#include <stdio.h>
#include "mstruct.h"
#include "mextern.h"
#define FALSE 0
#define TRUE !FALSE
#define TABSIZE 4096
#define STACKSIZE TABSIZE
#define NO_PRED 0xFFFF
#define EMPTY 0xFFFF
#define NOT_FND 0xFFFF
#define UEOF ((unsigned)EOF)
#define UPDATE TRUE
#define NO_UPDATE FALSE
static struct entry {
char used;
unsigned int next;
unsigned int predecessor;
unsigned char follower;
} string_tab[TABSIZE];
static char *start_inbuf, *start_outbuf;
static char *inbuf, *outbuf;
static int sizeof_inbuf, sizeof_outbuf;
static unsigned int temp_inbuf;
static unsigned int temp_outbuf;
static char stack[STACKSIZE];
static int sp;
unsigned int h(), eolist(), hash(), unhash(), getcode(), readc(), writec();
void init_tab(), upd_tab(), flushout(), putcode(), push();
int pop(), compress(), uncompress();
/* h uses the 'mid-square' algorithm. I.E. for a hash val of n bits */
/* hash = middle binary digits of (key * key). Upon collision, hash */
/* searches down linked list of keys that hashed to that key already. */
/* It will NOT notice if the table is full. This must be handled */
/* elsewhere */
unsigned int h(pred,foll)
unsigned int pred;
unsigned char foll;
{
long temp, local;
local = (pred + foll) | 0x0800;
temp = local * local;
local = (temp >> 6) & 0x0FFF;
return local;
}
/* return last element in a collision list */
unsigned int eolist(index)
unsigned int index;
{
register int temp;
while((temp = string_tab[index].next) != 0)
index = temp;
return index;
}
unsigned int hash(pred, foll, update)
unsigned int pred;
unsigned char foll;
int update;
{
unsigned int local, tempnext;
struct entry *ep;
local = h(pred,foll);
if(!string_tab[local].used)
return local;
else {
local = eolist(local);
tempnext = (local + 101) & 0x0FFF;
ep = &string_tab[tempnext];
while (ep->used) {
++tempnext;
if(tempnext == TABSIZE) {
tempnext = 0;
ep = string_tab;
}
else
ep++;
}
if (update)
string_tab[local].next = tempnext;
return tempnext;
}
}
/* unhash uses the 'next' field to go down the collision tree to find */
/* the entry corresponding to the passed key */
/* passed key and returns either the matching entry # or NOT_FND */
unsigned int unhash(pred, foll)
unsigned int pred;
unsigned char foll;
{
unsigned int local, offset;
struct entry *ep;
local = h(pred,foll);
loop:
ep = &string_tab[local];
if((ep->predecessor == pred) && (ep->follower == foll))
return local;
if(!ep->next)
return NOT_FND;
local = ep->next;
goto loop;
}
void init_tab()
{
int i;
temp_outbuf = temp_inbuf = EMPTY;
sp = 0;
memset((char *)string_tab, 0, sizeof(string_tab));
for(i=0; i<256; i++)
upd_tab(NO_PRED,i);
}
void upd_tab(pred, foll)
unsigned int pred, foll;
{
struct entry *ep;
ep = &string_tab[hash(pred,foll,UPDATE)];
ep->used = TRUE;
ep->next = 0;
ep->predecessor = pred;
ep->follower = foll;
}
/* getcode and putcode 'gallop' through input and output - they either */
/* output two bytes or one depending on the contents of the buffer. */
unsigned int getcode()
{
unsigned int localbuf, returnval;
if(temp_inbuf == EMPTY) {
if((localbuf = readc()) == UEOF)
return UEOF;
localbuf &= 0xFF;
if((temp_inbuf = readc()) == UEOF)
return UEOF;
temp_inbuf &= 0xFF;
returnval = ((localbuf << 4) & 0xFF0) |
((temp_inbuf >> 4) & 0x00F);
temp_inbuf &= 0x000F;
}
else {
if((localbuf = readc()) == UEOF)
return UEOF;
localbuf &= 0xFF;
returnval = localbuf | ((temp_inbuf << 8) & 0xF00);
temp_inbuf = EMPTY;
}
return returnval;
}
void putcode(code)
unsigned int code;
{
unsigned int localbuf;
if(temp_outbuf == EMPTY) {
writec((code >> 4) & 0xFF);
temp_outbuf = code & 0x000F;
}
else {
writec(((temp_outbuf << 4) & 0xFF0) | ((code >> 8) & 0x00F));
writec(code & 0x00FF);
temp_outbuf = EMPTY;
}
}
unsigned int readc()
{
if(inbuf - start_inbuf >= sizeof_inbuf)
return(UEOF);
else
return((*(inbuf++) & 0xFF));
}
unsigned int writec(ch)
int ch;
{
*outbuf = (char)ch;
outbuf++;
sizeof_outbuf++;
}
/* flushout makes sure fractional output buffer gets written */
void flushout()
{
if(temp_outbuf != EMPTY) {
*outbuf = (temp_outbuf << 4) & 0xFF0;
outbuf++;
sizeof_outbuf++;
}
}
/* Compress routines */
int compress(in, out, size)
char *in;
char *out;
int size;
{
unsigned int c, code, localcode;
int code_count = TABSIZE - 256;
start_inbuf = inbuf = in;
start_outbuf = outbuf = out;
sizeof_inbuf = size;
sizeof_outbuf = 0;
init_tab();
c = readc();
code = unhash(NO_PRED, c);
while((c = readc()) != UEOF) {
if((localcode = unhash(code, c)) != NOT_FND) {
code = localcode;
continue;
}
putcode(code);
if(code_count) {
upd_tab(code, c);
code_count--;
}
code = unhash(NO_PRED, c);
}
putcode(code);
flushout();
return sizeof_outbuf;
}
/* Uncompression routines */
int uncompress(in, out, size)
char *in;
char *out;
int size;
{
unsigned int c, tempc, code, oldcode, incode, finchar, lastchar;
char unknown = FALSE;
int code_count = TABSIZE - 256;
struct entry *ep;
start_inbuf = inbuf = in;
start_outbuf = outbuf = out;
sizeof_inbuf = size;
sizeof_outbuf = 0;
init_tab();
code = oldcode = getcode();
c = string_tab[code].follower;
writec(c);
finchar = c;
while ((code = incode = getcode()) != UEOF) {
ep = &string_tab[code];
if(!ep->used) {
lastchar = finchar;
code = oldcode;
unknown = TRUE;
ep = &string_tab[code];
}
while(ep->predecessor != NO_PRED) {
push(ep->follower);
code = ep->predecessor;
ep = &string_tab[code];
}
finchar = ep->follower;
writec(finchar);
while((tempc = pop()) != EMPTY)
writec(tempc);
if(unknown) {
writec(finchar = lastchar);
unknown = FALSE;
}
if(code_count) {
upd_tab(oldcode,finchar);
code_count--;
}
oldcode = incode;
}
flushout();
return sizeof_outbuf;
}
void push(c)
int c;
{
stack[sp] = (char) c;
sp++;
if(sp >= STACKSIZE)
merror(FATAL, "Stack overflow");
}
int pop()
{
if(sp > 0) {
sp--;
return((int)stack[sp]);
}
else
return EMPTY;
}
#endif