/* * Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . --- HISTORICAL COMMENTS FOLLOW --- * $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 #include #include #include #include "CZip.h" #define END_OF_STREAM 256 int CZip::hb_CompressFile(tVirtualFile *input, BITFILE *output) { uint32_t *counts; tH0Node *nodes; tH0Code *codes; int root_node; int original_pos = VFtell(output->file); counts = (uint32_t *)malloc(256 * sizeof(uint32_t)); 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(uint32_t)); 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 = (uint32_t)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, uint32_t *counts) { int32_t 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(uint32_t *counts, tH0Node *nodes) { uint32_t 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 = (uint32_t)(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, uint32_t 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, (uint32_t)codes[c].code, codes[c].code_bits); OutputBits(output, (uint32_t)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 } } }