Descent3/czip/HuffmanBasic.cpp
2024-04-16 12:56:40 -06:00

256 lines
6.2 KiB
C++

/*
* $Logfile: /DescentIII/Main/czip/HuffmanBasic.cpp $
* $Revision: 2 $
* $Date: 8/27/98 3:26p $
* $Author: Jeff $
*
* Basic Huffman compression functions
*
* $Log: /DescentIII/Main/czip/HuffmanBasic.cpp $
*
* 2 8/27/98 3:26p Jeff
* intial creation
*
* 1 8/27/98 3:26p Jeff
*
* $NoKeywords: $
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "CZip.h"
#define END_OF_STREAM 256
int CZip::hb_CompressFile(tVirtualFile *input, BITFILE *output) {
ulong *counts;
tH0Node *nodes;
tH0Code *codes;
int root_node;
int original_pos = VFtell(output->file);
counts = (ulong *)malloc(256 * sizeof(ulong));
if (!counts)
return -1;
if ((nodes = (tH0Node *)malloc(514 * sizeof(tH0Node))) == NULL) {
free(counts);
return -1;
}
if ((codes = (tH0Code *)malloc(257 * sizeof(tH0Code))) == NULL) {
free(counts);
free(nodes);
return -1;
}
memset(counts, 0, 256 * sizeof(ulong));
memset(nodes, 0, 514 * sizeof(tH0Node));
memset(codes, 0, 257 * sizeof(tH0Code));
hb_count_bytes(input, counts);
hb_scale_counts(counts, nodes);
hb_output_counts(output, nodes);
root_node = hb_build_tree(nodes);
hb_convert_tree_to_code(nodes, codes, 0, 0, root_node);
hb_compress_data(input, output, codes);
free(counts);
free(nodes);
free(codes);
FlushOutputBitFile(output);
int compsize = VFtell(output->file) - original_pos;
return compsize;
}
bool CZip::hb_ExpandFile(BITFILE *input, tVirtualFile *output) {
tH0Node *nodes;
int root_node;
if ((nodes = (tH0Node *)malloc(514 * sizeof(tH0Node))) == NULL)
return false;
memset(nodes, 0, sizeof(tH0Node) * 514);
hb_input_counts(input, nodes);
root_node = hb_build_tree(nodes);
hb_expand_data(input, output, nodes, root_node);
free(nodes);
return true;
}
void CZip::hb_output_counts(BITFILE *output, tH0Node *nodes) {
int first, last, next, i;
first = 0;
while (first < 255 && nodes[first].count == 0)
first++;
for (; first < 256; first = next) {
last = first + 1;
for (;;) {
for (; last < 256; last++)
if (nodes[last].count == 0)
break;
last--;
for (next = last + 1; next < 256; next++)
if (nodes[next].count != 0)
break;
if (next > 255)
break;
if ((next - last) > 3)
break;
last = next;
}
if (VFputc(first, output->file) != first) {
// fatal error
}
if (VFputc(last, output->file) != last) {
// fatal error
}
for (i = first; i <= last; i++) {
if (VFputc(nodes[i].count, output->file) != (int)nodes[i].count) {
// fatal error
}
}
}
if (VFputc(0, output->file) != 0) {
// fatal error
}
}
void CZip::hb_input_counts(BITFILE *input, tH0Node *nodes) {
int first;
int last;
int i;
int c;
for (i = 0; i < 256; i++)
nodes[i].count = 0;
if ((first = VFgetc(input->file)) == EOF) {
// fatal_error( "Error reading byte counts\n" );
}
if ((last = VFgetc(input->file)) == EOF) {
// fatal_error( "Error reading byte counts\n" );
}
for (;;) {
for (i = first; i <= last; i++)
if ((c = VFgetc(input->file)) == EOF) {
// fatal_error( "Error reading byte counts\n" );
} else
nodes[i].count = (unsigned int)c;
if ((first = VFgetc(input->file)) == EOF) {
// fatal_error( "Error reading byte counts\n" );
}
if (first == 0)
break;
if ((last = VFgetc(input->file)) == EOF) {
// fatal_error( "Error reading byte counts\n" );
}
}
nodes[END_OF_STREAM].count = 1;
}
void CZip::hb_count_bytes(tVirtualFile *input, ulong *counts) {
long input_marker;
int c;
input_marker = VFtell(input);
while ((c = VFgetc(input)) != EOF)
counts[c]++;
VFseek(input, input_marker, SEEK_SET);
}
void CZip::hb_scale_counts(ulong *counts, tH0Node *nodes) {
ulong max_count;
int i;
max_count = 0;
for (i = 0; i < 256; i++)
if (counts[i] > max_count)
max_count = counts[i];
if (max_count == 0) {
counts[0] = 1;
max_count = 1;
}
max_count = max_count / 255;
max_count = max_count + 1;
for (i = 0; i < 256; i++) {
nodes[i].count = (uint)(counts[i] / max_count);
if (nodes[i].count == 0 && counts[i] != 0)
nodes[i].count = 1;
}
nodes[END_OF_STREAM].count = 1;
}
int CZip::hb_build_tree(tH0Node *nodes) {
int next_free;
int i;
int min_1;
int min_2;
nodes[513].count = 0xffff;
for (next_free = END_OF_STREAM + 1;; next_free++) {
min_1 = 513;
min_2 = 513;
for (i = 0; i < next_free; i++)
if (nodes[i].count != 0) {
if (nodes[i].count < nodes[min_1].count) {
min_2 = min_1;
min_1 = i;
} else if (nodes[i].count < nodes[min_2].count)
min_2 = i;
}
if (min_2 == 513)
break;
nodes[next_free].count = nodes[min_1].count + nodes[min_2].count;
nodes[min_1].saved_count = nodes[min_1].count;
nodes[min_1].count = 0;
nodes[min_2].saved_count = nodes[min_2].count;
nodes[min_2].count = 0;
nodes[next_free].child0 = min_1;
nodes[next_free].child1 = min_2;
}
next_free--;
nodes[next_free].saved_count = nodes[next_free].count;
return (next_free);
}
void CZip::hb_convert_tree_to_code(tH0Node *nodes, tH0Code *codes, uint code_so_far, int bits, int node) {
if (node <= END_OF_STREAM) {
codes[node].code = code_so_far;
codes[node].code_bits = bits;
return;
}
code_so_far <<= 1;
bits++;
hb_convert_tree_to_code(nodes, codes, code_so_far, bits, nodes[node].child0);
hb_convert_tree_to_code(nodes, codes, code_so_far | 1, bits, nodes[node].child1);
}
void CZip::hb_compress_data(tVirtualFile *input, BITFILE *output, tH0Code *codes) {
int c;
while ((c = VFgetc(input)) != EOF)
OutputBits(output, (ulong)codes[c].code, codes[c].code_bits);
OutputBits(output, (ulong)codes[END_OF_STREAM].code, codes[END_OF_STREAM].code_bits);
}
void CZip::hb_expand_data(BITFILE *input, tVirtualFile *output, tH0Node *nodes, int root_node) {
int node;
for (;;) {
node = root_node;
do {
if (InputBit(input))
node = nodes[node].child1;
else
node = nodes[node].child0;
} while (node > END_OF_STREAM);
if (node == END_OF_STREAM)
break;
if ((VFputc(node, output)) != node) {
// fatal error
}
}
}