mirror of
https://github.com/jmarshall23/CnC_Remastered_Collection.git
synced 2026-08-12 08:00:55 +02:00
6685b5d51a
This reverts commitae0648fccc, reversing changes made tod7243e8265.
365 lines
7.7 KiB
C
365 lines
7.7 KiB
C
/*
|
|
libsmacker - A C library for decoding .smk Smacker Video files
|
|
Copyright (C) 2012-2013 Greg Kennedy
|
|
|
|
See smacker.h for more information.
|
|
|
|
smk_hufftree.c
|
|
Implementation of Smacker Huffman coding trees.
|
|
*/
|
|
|
|
#include "smk_hufftree.h"
|
|
|
|
/* malloc and friends */
|
|
#include "smk_malloc.h"
|
|
|
|
/* function to recursively delete a huffman tree */
|
|
void smk_huff_free(struct smk_huff_t *t)
|
|
{
|
|
/* Sanity check: do not double-free */
|
|
smk_null_check(t);
|
|
|
|
/* If this is not a leaf node, free child trees first */
|
|
if (t->b0)
|
|
{
|
|
smk_huff_free(t->b0);
|
|
smk_huff_free(t->u.b1);
|
|
}
|
|
|
|
/* Safe-delete tree node. */
|
|
smk_free(t);
|
|
|
|
error: ;
|
|
}
|
|
|
|
/* safe build with built-in error jump */
|
|
#define smk_huff_safe_build_rec(bs,p) \
|
|
{ \
|
|
if (!(p = smk_huff_build_rec(bs))) \
|
|
{ \
|
|
fprintf(stderr,"libsmacker::smk_huff_safe_build_rec(" #bs "," #p ") - ERROR (file: %s, line: %lu)\n", __FILE__, (unsigned long)__LINE__); \
|
|
goto error; \
|
|
} \
|
|
}
|
|
|
|
/* Recursive tree-building function. */
|
|
static struct smk_huff_t *smk_huff_build_rec(struct smk_bit_t *bs)
|
|
{
|
|
struct smk_huff_t *ret = NULL;
|
|
char bit;
|
|
|
|
/* sanity check */
|
|
smk_null_check(bs);
|
|
|
|
/* Read the bit */
|
|
smk_bs_safe_read_1(bs,bit);
|
|
|
|
/* Malloc a structure. */
|
|
smk_malloc(ret,sizeof(struct smk_huff_t));
|
|
|
|
if (bit)
|
|
{
|
|
/* Bit set: this forms a Branch node. */
|
|
/* Recursively attempt to build the Left branch. */
|
|
smk_huff_safe_build_rec(bs,ret->b0);
|
|
|
|
/* Everything is still OK: attempt to build the Right branch. */
|
|
smk_huff_safe_build_rec(bs,ret->u.b1);
|
|
|
|
/* return branch pointer here */
|
|
return ret;
|
|
}
|
|
|
|
/* Bit unset signifies a Leaf node. */
|
|
/* Attempt to read value */
|
|
smk_bs_safe_read_8(bs,ret->u.leaf.value);
|
|
|
|
/* smk_malloc sets entries to 0 by default */
|
|
/* ret->b0 = NULL; */
|
|
ret->u.leaf.escapecode = 0xFF;
|
|
|
|
return ret;
|
|
|
|
error:
|
|
smk_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
Entry point for huff_build. Basically just checks the start/end tags
|
|
and calls smk_huff_build_rec recursive function.
|
|
*/
|
|
struct smk_huff_t *smk_huff_build(struct smk_bit_t* bs)
|
|
{
|
|
struct smk_huff_t *ret = NULL;
|
|
char bit;
|
|
|
|
/* sanity check */
|
|
smk_null_check(bs);
|
|
|
|
/* Smacker huff trees begin with a set-bit. */
|
|
smk_bs_safe_read_1(bs,bit);
|
|
|
|
if (!bit)
|
|
{
|
|
/* Got a bit, but it was not 1. In theory, there could be a smk file
|
|
without this particular tree. */
|
|
fputs("libsmacker::smk_huff_build(bs) - Warning: initial get_bit returned 0\n",stderr);
|
|
goto error;
|
|
}
|
|
|
|
/* Begin parsing the tree data. */
|
|
smk_huff_safe_build_rec(bs,ret);
|
|
|
|
/* huff trees end with an unset-bit */
|
|
smk_bs_safe_read_1(bs,bit);
|
|
|
|
if (bit)
|
|
{
|
|
fputs("libsmacker::smk_huff_build(bs) - ERROR: final get_bit returned 1\n",stderr);
|
|
goto error;
|
|
}
|
|
|
|
return ret;
|
|
|
|
error:
|
|
smk_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
/* Look up an 8-bit value from a basic huff tree.
|
|
Return -1 on error. */
|
|
short smk_huff_lookup (struct smk_bit_t *bs, const struct smk_huff_t *t)
|
|
{
|
|
char bit;
|
|
|
|
/* sanity check */
|
|
smk_null_check(bs);
|
|
smk_null_check(t);
|
|
|
|
if (!t->b0)
|
|
{
|
|
/* Reached a Leaf node. Return its value. */
|
|
return t->u.leaf.value;
|
|
}
|
|
|
|
smk_bs_safe_read_1(bs,bit);
|
|
|
|
if (bit)
|
|
{
|
|
/* get_bit returned Set, follow Right branch. */
|
|
return smk_huff_lookup(bs,t->u.b1);
|
|
}
|
|
|
|
/* follow Right branch */
|
|
return smk_huff_lookup(bs,t->b0);
|
|
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
#define smk_huff_big_safe_build_rec(bs,cache,low8,hi8,p) \
|
|
{ \
|
|
if (!(p = smk_huff_big_build_rec(bs,cache,low8,hi8))) \
|
|
{ \
|
|
fprintf(stderr,"libsmacker::smk_huff_big_safe_build_rec(" #bs "," #cache "," #low8 "," #hi8 "," #p ") - ERROR (file: %s, line: %lu)\n", __FILE__, (unsigned long)__LINE__); \
|
|
goto error; \
|
|
} \
|
|
}
|
|
|
|
/* Recursively builds a Big tree. */
|
|
static struct smk_huff_t *smk_huff_big_build_rec(struct smk_bit_t *bs, const unsigned short cache[3], const struct smk_huff_t *low8, const struct smk_huff_t *hi8)
|
|
{
|
|
struct smk_huff_t *ret = NULL;
|
|
|
|
char bit;
|
|
short lowval;
|
|
|
|
/* sanity check */
|
|
smk_null_check(bs);
|
|
smk_null_check(cache);
|
|
smk_null_check(low8);
|
|
smk_null_check(hi8);
|
|
|
|
/* Get the first bit */
|
|
smk_bs_safe_read_1(bs,bit);
|
|
|
|
/* Malloc a structure. */
|
|
smk_malloc(ret,sizeof(struct smk_huff_t));
|
|
|
|
if (bit)
|
|
{
|
|
/* Recursively attempt to build the Left branch. */
|
|
smk_huff_big_safe_build_rec(bs,cache,low8,hi8,ret->b0);
|
|
|
|
/* Recursively attempt to build the Left branch. */
|
|
smk_huff_big_safe_build_rec(bs,cache,low8,hi8,ret->u.b1);
|
|
|
|
/* return branch pointer here */
|
|
return ret;
|
|
}
|
|
|
|
/* Bit unset signifies a Leaf node. */
|
|
smk_huff_safe_lookup(bs,low8,lowval);
|
|
smk_huff_safe_lookup(bs,hi8,ret->u.leaf.value);
|
|
|
|
/* Looks OK: we got low and hi values. Return a new LEAF */
|
|
/* ret->b0 = NULL; */
|
|
ret->u.leaf.value = lowval | (ret->u.leaf.value << 8);
|
|
|
|
/* Last: when building the tree, some Values may correspond to cache positions.
|
|
Identify these values and set the Escape code byte accordingly. */
|
|
if (ret->u.leaf.value == cache[0])
|
|
{
|
|
ret->u.leaf.escapecode = 0;
|
|
}
|
|
else if (ret->u.leaf.value == cache[1])
|
|
{
|
|
ret->u.leaf.escapecode = 1;
|
|
}
|
|
else if (ret->u.leaf.value == cache[2])
|
|
{
|
|
ret->u.leaf.escapecode = 2;
|
|
}
|
|
else
|
|
{
|
|
ret->u.leaf.escapecode = 0xFF;
|
|
}
|
|
|
|
return ret;
|
|
|
|
error:
|
|
smk_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
/* Entry point for building a big 16-bit tree. */
|
|
struct smk_huff_big_t *smk_huff_big_build(struct smk_bit_t* bs)
|
|
{
|
|
struct smk_huff_big_t *big = NULL;
|
|
|
|
struct smk_huff_t *low8 = NULL, *hi8 = NULL;
|
|
|
|
short lowval;
|
|
|
|
char bit;
|
|
unsigned char i;
|
|
|
|
/* sanity check */
|
|
smk_null_check(bs);
|
|
|
|
/* Smacker huff trees begin with a set-bit. */
|
|
smk_bs_safe_read_1(bs,bit);
|
|
|
|
if (!bit)
|
|
{
|
|
fputs("libsmacker::smk_huff_big_build(bs) - ERROR: initial get_bit returned 0\n",stderr);
|
|
goto error;
|
|
}
|
|
|
|
/* build low-8-bits tree */
|
|
smk_huff_safe_build(bs,low8);
|
|
/* build hi-8-bits tree */
|
|
smk_huff_safe_build(bs,hi8);
|
|
|
|
/* Everything looks OK so far. Time to malloc structure. */
|
|
smk_malloc(big,sizeof(struct smk_huff_big_t));
|
|
|
|
/* Init the escape code cache. */
|
|
for (i = 0; i < 3; i ++)
|
|
{
|
|
smk_bs_safe_read_8(bs,lowval);
|
|
smk_bs_safe_read_8(bs,big->cache[i]);
|
|
big->cache[i] = lowval | (big->cache[i] << 8);
|
|
/* fprintf(stderr,"Escape code cache: cache[%d] = %d\n",i,big->cache[i]); */
|
|
}
|
|
|
|
/* Finally, call recursive function to retrieve the Bigtree. */
|
|
smk_huff_big_safe_build_rec(bs,big->cache,low8,hi8,big->t);
|
|
|
|
/* Done with 8-bit hufftrees, free them. */
|
|
smk_huff_free(hi8);
|
|
smk_huff_free(low8);
|
|
|
|
/* Check final end tag. */
|
|
smk_bs_safe_read_1(bs,bit);
|
|
|
|
if (bit)
|
|
{
|
|
fputs("libsmacker::smk_huff_big_build(bs) - ERROR: final get_bit returned 1\n",stderr);
|
|
goto error;
|
|
}
|
|
|
|
return big;
|
|
|
|
error:
|
|
smk_free(big);
|
|
smk_free(hi8);
|
|
smk_free(low8);
|
|
return NULL;
|
|
}
|
|
|
|
static int smk_huff_big_lookup_rec (struct smk_bit_t *bs, unsigned short cache[3], struct smk_huff_t *t)
|
|
{
|
|
unsigned short val;
|
|
char bit;
|
|
|
|
/* sanity check */
|
|
smk_null_check(bs);
|
|
smk_null_check(cache);
|
|
smk_null_check(t);
|
|
|
|
/* Reached a Leaf node */
|
|
if (!t->b0)
|
|
{
|
|
if (t->u.leaf.escapecode != 0xFF)
|
|
{
|
|
/* Found escape code. Retrieve value from Cache. */
|
|
val = cache[t->u.leaf.escapecode];
|
|
}
|
|
else
|
|
{
|
|
/* Use value directly. */
|
|
val = t->u.leaf.value;
|
|
}
|
|
|
|
if ( cache[0] != val)
|
|
{
|
|
/* Update the cache, by moving val to the front of the queue,
|
|
if it isn't already there. */
|
|
cache[2] = cache[1];
|
|
cache[1] = cache[0];
|
|
cache[0] = val;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
smk_bs_safe_read_1(bs,bit);
|
|
|
|
if (bit)
|
|
{
|
|
/* get_bit returned Set, follow Right branch. */
|
|
return smk_huff_big_lookup_rec(bs,cache,t->u.b1);
|
|
}
|
|
|
|
return smk_huff_big_lookup_rec(bs,cache,t->b0);
|
|
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
/* Convenience call-out for recursive bigtree lookup function */
|
|
long smk_huff_big_lookup (struct smk_bit_t *bs, struct smk_huff_big_t *big)
|
|
{
|
|
return smk_huff_big_lookup_rec(bs,big->cache,big->t);
|
|
}
|
|
|
|
/* Resets a Big hufftree cache */
|
|
void smk_huff_big_reset (struct smk_huff_big_t *big)
|
|
{
|
|
big->cache[0] = 0;
|
|
big->cache[1] = 0;
|
|
big->cache[2] = 0;
|
|
}
|