mirror of
https://github.com/SimoneN64/Kaizen.git
synced 2025-04-02 10:41:53 -04:00
149 lines
4.4 KiB
C
149 lines
4.4 KiB
C
/* Copyright 2018 the unarr project authors (see AUTHORS file).
|
|
License: LGPLv3 */
|
|
|
|
/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADPrefixCode.m */
|
|
|
|
#include "rar.h"
|
|
|
|
bool rar_new_node(struct huffman_code *code)
|
|
{
|
|
if (!code->tree) {
|
|
code->minlength = INT_MAX;
|
|
code->maxlength = INT_MIN;
|
|
}
|
|
if (code->numentries + 1 >= code->capacity) {
|
|
/* in my small file sample, 1024 is the value needed most often */
|
|
int new_capacity = code->capacity ? code->capacity * 2 : 1024;
|
|
void *new_tree = calloc(new_capacity, sizeof(*code->tree));
|
|
if (!new_tree) {
|
|
warn("OOM during decompression");
|
|
return false;
|
|
}
|
|
if (code->tree) {
|
|
memcpy(new_tree, code->tree, code->capacity * sizeof(*code->tree));
|
|
free(code->tree);
|
|
}
|
|
code->tree = new_tree;
|
|
code->capacity = new_capacity;
|
|
}
|
|
/* if we have no code->tree at this point something went wrong */
|
|
if (!code->tree) {
|
|
warn("Invalid huffman code tree, aborting");
|
|
return false;
|
|
}
|
|
code->tree[code->numentries].branches[0] = -1;
|
|
code->tree[code->numentries].branches[1] = -2;
|
|
code->numentries++;
|
|
return true;
|
|
}
|
|
|
|
bool rar_add_value(struct huffman_code *code, int value, int codebits, int length)
|
|
{
|
|
int lastnode, bitpos, bit;
|
|
|
|
free(code->table);
|
|
code->table = NULL;
|
|
|
|
if (length > code->maxlength)
|
|
code->maxlength = length;
|
|
if (length < code->minlength)
|
|
code->minlength = length;
|
|
|
|
lastnode = 0;
|
|
for (bitpos = length - 1; bitpos >= 0; bitpos--) {
|
|
bit = (codebits >> bitpos) & 1;
|
|
if (rar_is_leaf_node(code, lastnode)) {
|
|
warn("Invalid data in bitstream"); /* prefix found */
|
|
return false;
|
|
}
|
|
if (code->tree[lastnode].branches[bit] < 0) {
|
|
if (!rar_new_node(code))
|
|
return false;
|
|
code->tree[lastnode].branches[bit] = code->numentries - 1;
|
|
}
|
|
lastnode = code->tree[lastnode].branches[bit];
|
|
}
|
|
|
|
if (code->tree[lastnode].branches[0] != -1 || code->tree[lastnode].branches[1] != -2) {
|
|
warn("Invalid data in bitstream"); /* prefix found */
|
|
return false;
|
|
}
|
|
code->tree[lastnode].branches[0] = code->tree[lastnode].branches[1] = value;
|
|
return true;
|
|
}
|
|
|
|
bool rar_create_code(struct huffman_code *code, uint8_t *lengths, int numsymbols)
|
|
{
|
|
int symbolsleft = numsymbols;
|
|
int codebits = 0;
|
|
int i, j;
|
|
|
|
if (!rar_new_node(code))
|
|
return false;
|
|
|
|
for (i = 1; i <= 0x0F; i++) {
|
|
for (j = 0; j < numsymbols; j++) {
|
|
if (lengths[j] != i)
|
|
continue;
|
|
if (!rar_add_value(code, j, codebits, i))
|
|
return false;
|
|
if (--symbolsleft <= 0)
|
|
return true;
|
|
codebits++;
|
|
}
|
|
codebits <<= 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool rar_make_table_rec(struct huffman_code *code, int node, int offset, int depth, int maxdepth)
|
|
{
|
|
int currtablesize = 1 << (maxdepth - depth);
|
|
|
|
if (node < 0 || code->numentries <= node) {
|
|
warn("Invalid data in bitstream"); /* invalid location to Huffman tree specified */
|
|
return false;
|
|
}
|
|
|
|
if (rar_is_leaf_node(code, node)) {
|
|
int i;
|
|
for (i = 0; i < currtablesize; i++) {
|
|
code->table[offset + i].length = depth;
|
|
code->table[offset + i].value = code->tree[node].branches[0];
|
|
}
|
|
}
|
|
else if (depth == maxdepth) {
|
|
code->table[offset].length = maxdepth + 1;
|
|
code->table[offset].value = node;
|
|
}
|
|
else {
|
|
if (!rar_make_table_rec(code, code->tree[node].branches[0], offset, depth + 1, maxdepth))
|
|
return false;
|
|
if (!rar_make_table_rec(code, code->tree[node].branches[1], offset + currtablesize / 2, depth + 1, maxdepth))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool rar_make_table(struct huffman_code *code)
|
|
{
|
|
if (code->minlength <= code->maxlength && code->maxlength <= 10)
|
|
code->tablesize = code->maxlength;
|
|
else
|
|
code->tablesize = 10;
|
|
|
|
code->table = calloc(1ULL << code->tablesize, sizeof(*code->table));
|
|
if (!code->table) {
|
|
warn("OOM during decompression");
|
|
return false;
|
|
}
|
|
|
|
return rar_make_table_rec(code, 0, 0, 0, code->tablesize);
|
|
}
|
|
|
|
void rar_free_code(struct huffman_code *code)
|
|
{
|
|
free(code->tree);
|
|
free(code->table);
|
|
memset(code, 0, sizeof(*code));
|
|
}
|