- /** 
-         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: ; 
- }; 
-