You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
425 lines
9.0 KiB
425 lines
9.0 KiB
/** |
|
libsmacker - A C library for decoding .smk Smacker Video files |
|
Copyright (C) 2012-2017 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" |
|
|
|
/** |
|
8-bit Tree node structure. |
|
If b0 is non-null, this is a branch, and b1 from the union should be used. |
|
If b0 is null, this is a leaf, and val / escape code from union should be used. |
|
*/ |
|
struct smk_huff8_t |
|
{ |
|
struct smk_huff8_t* b0; |
|
union |
|
{ |
|
struct smk_huff8_t* b1; |
|
struct |
|
{ |
|
unsigned short value; |
|
unsigned char escapecode; |
|
} leaf; |
|
} u; |
|
}; |
|
|
|
/** |
|
16-bit Tree root struct: holds a huff8_t structure, |
|
as well as a cache of three 16-bit values. |
|
*/ |
|
struct smk_huff16_t |
|
{ |
|
struct smk_huff8_t* t; |
|
unsigned short cache[3]; |
|
}; |
|
|
|
/*********************** 8-BIT HUFF-TREE FUNCTIONS ***********************/ |
|
/** safe build with built-in error jump */ |
|
#define smk_huff8_build_rec(bs,p) \ |
|
{ \ |
|
if (!(p = _smk_huff8_build_rec(bs))) \ |
|
{ \ |
|
fprintf(stderr, "libsmacker::smk_huff8_build_rec(" #bs ", " #p ") - ERROR (file: %s, line: %lu)\n", __FILE__, (unsigned long)__LINE__); \ |
|
goto error; \ |
|
} \ |
|
} |
|
/** Recursive tree-building function. */ |
|
static struct smk_huff8_t* _smk_huff8_build_rec(struct smk_bit_t* bs) |
|
{ |
|
struct smk_huff8_t* ret = NULL; |
|
char bit; |
|
|
|
/* sanity check - removed: bs cannot be null, because it was checked at smk_huff8_build below */ |
|
/* smk_assert(bs); */ |
|
|
|
/* Read the bit */ |
|
smk_bs_read_1(bs, bit); |
|
|
|
/* Malloc a structure. */ |
|
smk_malloc(ret, sizeof(struct smk_huff8_t)); |
|
|
|
if (bit) |
|
{ |
|
/* Bit set: this forms a Branch node. */ |
|
/* Recursively attempt to build the Left branch. */ |
|
smk_huff8_build_rec(bs, ret->b0); |
|
|
|
/* Everything is still OK: attempt to build the Right branch. */ |
|
smk_huff8_build_rec(bs, ret->u.b1); |
|
|
|
/* return branch pointer here */ |
|
return ret; |
|
} |
|
|
|
/* Bit unset signifies a Leaf node. */ |
|
/* Attempt to read value */ |
|
smk_bs_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: |
|
/* In case of error, undo the subtree we were building, and return NULL. */ |
|
smk_huff8_free(ret); |
|
return NULL; |
|
} |
|
|
|
/* Look up an 8-bit value from a basic huff tree. |
|
Return -1 on error. */ |
|
short _smk_huff8_lookup(struct smk_bit_t* bs, const struct smk_huff8_t* t) |
|
{ |
|
char bit; |
|
|
|
/* sanity check */ |
|
smk_assert(bs); |
|
smk_assert(t); |
|
|
|
if (!t->b0) |
|
{ |
|
/* Reached a Leaf node. Return its value. */ |
|
return t->u.leaf.value; |
|
} |
|
|
|
/* Read the next bit from bitstream to determine path */ |
|
smk_bs_read_1(bs, bit); |
|
|
|
if (bit) |
|
{ |
|
/* get_bit returned Set, follow Right branch. */ |
|
return _smk_huff8_lookup(bs, t->u.b1); |
|
} |
|
|
|
/* follow Left branch */ |
|
return _smk_huff8_lookup(bs, t->b0); |
|
|
|
error: |
|
return -1; |
|
} |
|
|
|
/** |
|
Entry point for huff8 build. Basically just checks the start/end tags |
|
and calls smk_huff8_build_rec recursive function. |
|
*/ |
|
struct smk_huff8_t* _smk_huff8_build(struct smk_bit_t* bs) |
|
{ |
|
struct smk_huff8_t* ret = NULL; |
|
char bit; |
|
|
|
/* sanity check */ |
|
smk_assert(bs); |
|
|
|
/* Smacker huff trees begin with a set-bit. */ |
|
smk_bs_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_huff8_build(bs) - Warning: initial get_bit returned 0\n", stderr); |
|
goto error; |
|
} |
|
|
|
/* Begin parsing the tree data. */ |
|
smk_huff8_build_rec(bs, ret); |
|
|
|
/* huff trees end with an unset-bit */ |
|
smk_bs_read_1(bs, bit); |
|
|
|
if (bit) |
|
{ |
|
fputs("libsmacker::_smk_huff8_build(bs) - ERROR: final get_bit returned 1\n", stderr); |
|
goto error; |
|
} |
|
|
|
return ret; |
|
|
|
error: |
|
smk_huff8_free(ret); |
|
return NULL; |
|
} |
|
|
|
/* function to recursively delete a huffman tree */ |
|
void smk_huff8_free(struct smk_huff8_t* t) |
|
{ |
|
/* Sanity check: do not double-free */ |
|
smk_assert(t); |
|
|
|
/* If this is not a leaf node, free child trees first */ |
|
if (t->b0) |
|
{ |
|
smk_huff8_free(t->b0); |
|
smk_huff8_free(t->u.b1); |
|
} |
|
|
|
/* Safe-delete tree node. */ |
|
smk_free(t); |
|
|
|
error: ; |
|
} |
|
|
|
/*********************** 16-BIT HUFF-TREE FUNCTIONS ***********************/ |
|
/* safe bigtree build with built-in error jump */ |
|
#define smk_huff16_build_rec(bs,cache,low8,hi8,p) \ |
|
{ \ |
|
if (!(p = _smk_huff16_build_rec(bs, cache, low8, hi8))) \ |
|
{ \ |
|
fprintf(stderr, "libsmacker::smk_huff16_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_huff8_t* _smk_huff16_build_rec(struct smk_bit_t* bs, const unsigned short cache[3], const struct smk_huff8_t* low8, const struct smk_huff8_t* hi8) |
|
{ |
|
struct smk_huff8_t* ret = NULL; |
|
|
|
char bit; |
|
short lowval; |
|
|
|
/* sanity check - removed: these cannot be null, because they were checked at smk_huff16_build below */ |
|
/* smk_assert(bs); |
|
smk_assert(cache); |
|
smk_assert(low8); |
|
smk_assert(hi8); */ |
|
|
|
/* Get the first bit */ |
|
smk_bs_read_1(bs, bit); |
|
|
|
/* Malloc a structure. */ |
|
smk_malloc(ret, sizeof(struct smk_huff8_t)); |
|
|
|
if (bit) |
|
{ |
|
/* Recursively attempt to build the Left branch. */ |
|
smk_huff16_build_rec(bs, cache, low8, hi8, ret->b0); |
|
|
|
/* Recursively attempt to build the Left branch. */ |
|
smk_huff16_build_rec(bs, cache, low8, hi8, ret->u.b1); |
|
|
|
/* return branch pointer here */ |
|
return ret; |
|
} |
|
|
|
/* Bit unset signifies a Leaf node. */ |
|
smk_huff8_lookup(bs, low8, lowval); |
|
smk_huff8_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_huff8_free(ret); |
|
return NULL; |
|
} |
|
|
|
/* Entry point for building a big 16-bit tree. */ |
|
struct smk_huff16_t* _smk_huff16_build(struct smk_bit_t* bs) |
|
{ |
|
struct smk_huff16_t* big = NULL; |
|
|
|
struct smk_huff8_t* low8 = NULL; |
|
struct smk_huff8_t* hi8 = NULL; |
|
|
|
short lowval; |
|
|
|
char bit; |
|
unsigned char i; |
|
|
|
/* sanity check */ |
|
smk_assert(bs); |
|
|
|
/* Smacker huff trees begin with a set-bit. */ |
|
smk_bs_read_1(bs, bit); |
|
|
|
if (!bit) |
|
{ |
|
fputs("libsmacker::smk_huff16_build(bs) - ERROR: initial get_bit returned 0\n", stderr); |
|
goto error; |
|
} |
|
|
|
/* build low-8-bits tree */ |
|
smk_huff8_build(bs, low8); |
|
/* build hi-8-bits tree */ |
|
smk_huff8_build(bs, hi8); |
|
|
|
/* Everything looks OK so far. Time to malloc structure. */ |
|
smk_malloc(big, sizeof(struct smk_huff16_t)); |
|
|
|
/* Init the escape code cache. */ |
|
for (i = 0; i < 3; i ++) |
|
{ |
|
smk_bs_read_8(bs, lowval); |
|
smk_bs_read_8(bs, big->cache[i]); |
|
big->cache[i] = lowval | (big->cache[i] << 8); |
|
} |
|
|
|
/* Finally, call recursive function to retrieve the Bigtree. */ |
|
smk_huff16_build_rec(bs, big->cache, low8, hi8, big->t); |
|
|
|
/* Done with 8-bit hufftrees, free them. */ |
|
smk_huff8_free(hi8); |
|
smk_huff8_free(low8); |
|
|
|
/* Check final end tag. */ |
|
smk_bs_read_1(bs, bit); |
|
|
|
if (bit) |
|
{ |
|
fputs("libsmacker::smk_huff16_build(bs) - ERROR: final get_bit returned 1\n", stderr); |
|
goto error; |
|
} |
|
|
|
return big; |
|
|
|
error: |
|
smk_huff16_free(big); |
|
smk_huff8_free(hi8); |
|
smk_huff8_free(low8); |
|
return NULL; |
|
} |
|
|
|
static int _smk_huff16_lookup_rec(struct smk_bit_t* bs, unsigned short cache[3], const struct smk_huff8_t* t) |
|
{ |
|
unsigned short val; |
|
char bit; |
|
|
|
/* sanity check */ |
|
/* smk_assert(bs); |
|
smk_assert(cache); |
|
smk_assert(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; |
|
} |
|
|
|
/* Read the next bit from bitstream to determine path */ |
|
smk_bs_read_1(bs, bit); |
|
|
|
if (bit) |
|
{ |
|
/* get_bit returned Set, follow Right branch. */ |
|
return _smk_huff16_lookup_rec(bs, cache, t->u.b1); |
|
} |
|
|
|
return _smk_huff16_lookup_rec(bs, cache, t->b0); |
|
|
|
error: |
|
return -1; |
|
} |
|
|
|
/* Convenience call-out for recursive bigtree lookup function */ |
|
long _smk_huff16_lookup(struct smk_bit_t* bs, struct smk_huff16_t* big) |
|
{ |
|
/* sanity check */ |
|
smk_assert(bs); |
|
smk_assert(big); |
|
|
|
return _smk_huff16_lookup_rec(bs, big->cache, big->t); |
|
|
|
error: |
|
return -1; |
|
} |
|
|
|
/* Resets a Big hufftree cache */ |
|
void smk_huff16_reset(struct smk_huff16_t* big) |
|
{ |
|
/* sanity check */ |
|
smk_assert(big); |
|
|
|
big->cache[0] = 0; |
|
big->cache[1] = 0; |
|
big->cache[2] = 0; |
|
|
|
error: ; |
|
} |
|
|
|
/* delete a (big) huffman tree */ |
|
void smk_huff16_free(struct smk_huff16_t* big) |
|
{ |
|
/* Sanity check: do not double-free */ |
|
smk_assert(big); |
|
|
|
/* free the subtree */ |
|
if (big->t) |
|
smk_huff8_free(big->t); |
|
|
|
/* free the bigtree */ |
|
smk_free(big); |
|
|
|
error: ; |
|
};
|
|
|