From 057d0c0242c6e2e8e2ee98170388632dde9d162e Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Tue, 26 Aug 2025 01:02:06 +0100 Subject: [PATCH] Add support for decompressing Lempel-Ziv-Huffman, LH5 variant. --- CMakeLists.txt | 11 ++- library.h | 2 + tests/CMakeLists.txt | 6 +- tests/data/alice29.lh5 | Bin 0 -> 59104 bytes tests/lh5.cpp | 84 ++++++++++++++++++ zoo/ar.h | 40 +++++++++ zoo/decode.c | 71 ++++++++++++++++ zoo/huf.c | 187 +++++++++++++++++++++++++++++++++++++++++ zoo/io.c | 105 +++++++++++++++++++++++ zoo/lh5.c | 94 +++++++++++++++++++++ zoo/lh5.h | 54 ++++++++++++ zoo/lzd.c | 4 +- zoo/lzh.c | 31 +++++++ zoo/lzh.h | 31 +++++++ zoo/maketbl.c | 77 +++++++++++++++++ 15 files changed, 793 insertions(+), 4 deletions(-) create mode 100644 tests/data/alice29.lh5 create mode 100644 tests/lh5.cpp create mode 100644 zoo/ar.h create mode 100644 zoo/decode.c create mode 100644 zoo/huf.c create mode 100644 zoo/io.c create mode 100644 zoo/lh5.c create mode 100644 zoo/lh5.h create mode 100644 zoo/lzh.c create mode 100644 zoo/lzh.h create mode 100644 zoo/maketbl.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 881b378..148bdf3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,7 +130,16 @@ add_subdirectory(3rdparty) add_library("Aaru.Compression.Native" SHARED library.c apple_rle.c apple_rle.h adc.c adc.h lzip.c flac.c flac.h zoo/lzd.c - zoo/lzd.h) + zoo/lzd.h + zoo/lzh.c + zoo/decode.c + zoo/huf.c + zoo/io.c + zoo/lh5.h + zoo/lh5.c + zoo/lzh.h + zoo/ar.h + zoo/maketbl.c) include(3rdparty/bzip2.cmake) include(3rdparty/flac.cmake) diff --git a/library.h b/library.h index 25da777..0d4f017 100644 --- a/library.h +++ b/library.h @@ -112,6 +112,8 @@ AARU_EXPORT int AARU_CALL LZD_FeedNative(void *ctx, const unsigned char *data, s AARU_EXPORT int AARU_CALL LZD_DrainNative(void *ctx, unsigned char *outBuf, size_t outBufLen, size_t *produced); +AARU_EXPORT int AARU_CALL lh5_decompress(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len); + #define AARU_CHECKUMS_NATIVE_VERSION 0x06000089 AARU_EXPORT uint64_t AARU_CALL AARU_get_acn_version(); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 19963aa..c606db6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -42,8 +42,12 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/data.bin file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/alice29.lzd DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/) +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/alice29.lh5 + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/) + # 'Google_Tests_run' is the target name # 'test1.cpp tests2.cpp' are source files with tests add_executable(tests_run apple_rle.cpp crc32.c crc32.h adc.cpp bzip2.cpp lzip.cpp lzfse.cpp zstd.cpp lzma.cpp flac.cpp - zoo/lzd.cpp) + zoo/lzd.cpp + lh5.cpp) target_link_libraries(tests_run gtest gtest_main "Aaru.Compression.Native") diff --git a/tests/data/alice29.lh5 b/tests/data/alice29.lh5 new file mode 100644 index 0000000000000000000000000000000000000000..a715179ef88e9f88f6f6a60b498df6ba867ba442 GIT binary patch literal 59104 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%0DJJ_loty6m!>k5-UC~ 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 literal 0 HcmV?d00001 diff --git a/tests/lh5.cpp b/tests/lh5.cpp new file mode 100644 index 0000000..5f9e8d8 --- /dev/null +++ b/tests/lh5.cpp @@ -0,0 +1,84 @@ +/* + * This file is part of the Aaru Data Preservation Suite. + * Copyright (c) 2019-2025 Natalia Portillo. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "../zoo/lh5.h" + +#include +#include +#include + +#include "../library.h" +#include "crc32.h" +#include "gtest/gtest.h" + +#define EXPECTED_CRC32 0x66007dba + +static const uint8_t *buffer; + +class lh5Fixture : public ::testing::Test +{ +public: + lh5Fixture() + { + // initialization; + // can also be done in SetUp() + } + +protected: + void SetUp() + { + char path[PATH_MAX]; + char filename[PATH_MAX]; + + getcwd(path, PATH_MAX); + snprintf(filename, PATH_MAX, "%s/data/alice29.lh5", path); + + FILE *file = fopen(filename, "rb"); + buffer = (const uint8_t *)malloc(59104); + fread((void *)buffer, 1, 59104, file); + fclose(file); + } + + void TearDown() { free((void *)buffer); } + + ~lh5Fixture() + { + // resources cleanup, no exceptions allowed + } + + // shared user data +}; + +TEST_F(lh5Fixture, lh5) +{ + uint8_t params[] = {0x5D, 0x00, 0x00, 0x00, 0x02}; + size_t destLen = 152089; + size_t srcLen = 59104; + auto *outBuf = (uint8_t *)malloc(152089); + + auto err = lh5_decompress(buffer, srcLen, outBuf, &destLen); + + EXPECT_EQ(err, 0); + EXPECT_EQ(destLen, 152089); + + auto crc = crc32_data(outBuf, 152089); + + free(outBuf); + + EXPECT_EQ(crc, EXPECTED_CRC32); +} \ No newline at end of file diff --git a/zoo/ar.h b/zoo/ar.h new file mode 100644 index 0000000..728acb4 --- /dev/null +++ b/zoo/ar.h @@ -0,0 +1,40 @@ +/*$Source: /usr/home/dhesi/zoo/RCS/ar.h,v $*/ +/*$Id: ar.h,v 1.17 91/07/09 01:39:50 dhesi Exp $*/ +/*********************************************************** + ar.h + +Adapted from "ar" archiver written by Haruhiko Okumura. +***********************************************************/ +// Modified for in-memory decompression by Natalia Portillo, 2025 + +#include +#include + +/* all the prototypes follow here for all files */ + +/* DECODE.C */ +void decode_start(); +int decode(uint32_t count, uint8_t *buffer); + +/* HUF.C */ +void output(uint32_t c, uint32_t p); +uint32_t decode_c(void); +uint32_t decode_p(void); +void huf_decode_start(void); + +/* IO.C */ +void fillbuf(int n); +uint32_t getbits(int n); +void putbits(int n, uint32_t x); +void init_getbits(void); +void init_putbits(void); + +/* MAKETBL.C */ +void make_table(int nchar, uint8_t bitlen[], int tablebits, uint16_t table[]); + +/* MAKETREE.C */ +int make_tree(int nparm, uint16_t freqparm[], uint8_t lenparm[], uint16_t codeparm[]); + +/* for lzh modules and also for ar.c to use in defining buffer size */ +#define DICBIT 13 /* 12(-lh4-) or 13(-lh5-) */ +#define DICSIZ ((unsigned)1 << DICBIT) diff --git a/zoo/decode.c b/zoo/decode.c new file mode 100644 index 0000000..c697e7d --- /dev/null +++ b/zoo/decode.c @@ -0,0 +1,71 @@ +/*$Source: /usr/home/dhesi/zoo/RCS/decode.c,v $*/ +/*$Id: decode.c,v 1.6 91/07/09 01:39:49 dhesi Exp $*/ +/*********************************************************** + decode.c + +Adapted from "ar" archiver written by Haruhiko Okumura. +***********************************************************/ +// Modified for in-memory decompression by Natalia Portillo, 2025 + +#include +#include + +#include "ar.h" +#include "lzh.h" + +extern int decoded; /* from huf.c */ + +static int j; /* remaining bytes to copy */ + +void decode_start() +{ + huf_decode_start(); + j = 0; + decoded = 0; +} + +/* +decodes; returns no. of chars decoded +*/ + +int decode(uint32_t count, uint8_t *buffer) +/* The calling function must keep the number of + bytes to be processed. This function decodes + either 'count' bytes or 'DICSIZ' bytes, whichever + is smaller, into the array 'buffer[]' of size + 'DICSIZ' or more. + Call decode_start() once for each new file + before calling this function. */ +{ + static uint32_t i; + uint32_t r, c; + + r = 0; + while(--j >= 0) + { + buffer[r] = buffer[i]; + i = (i + 1) & (DICSIZ - 1); + if(++r == count) return r; + } + for(;;) + { + c = decode_c(); + if(decoded) return r; + if(c <= UCHAR_MAX) + { + buffer[r] = c; + if(++r == count) return r; + } + else + { + j = c - (UCHAR_MAX + 1 - THRESHOLD); + i = (r - decode_p() - 1) & (DICSIZ - 1); + while(--j >= 0) + { + buffer[r] = buffer[i]; + i = (i + 1) & (DICSIZ - 1); + if(++r == count) return r; + } + } + } +} diff --git a/zoo/huf.c b/zoo/huf.c new file mode 100644 index 0000000..5da36cc --- /dev/null +++ b/zoo/huf.c @@ -0,0 +1,187 @@ +/*$Source: /usr/home/dhesi/zoo/RCS/huf.c,v $*/ +/*$Id: huf.c,v 1.9 91/07/09 01:39:55 dhesi Exp $*/ +/*********************************************************** + huf.c -- static Huffman + +Adapted from "ar" archiver written by Haruhiko Okumura. +***********************************************************/ +// Modified for in-memory decompression by Natalia Portillo, 2025 + +#include + +#include "ar.h" +#include "lzh.h" + +#define NP (DICBIT + 1) +#define NT (CODE_BIT + 3) +#define PBIT 4 /* smallest integer such that (1U << PBIT) > NP */ +#define TBIT 5 /* smallest integer such that (1U << TBIT) > NT */ +#if NT > NP +#define NPT NT +#else +#define NPT NP +#endif + +static void read_pt_len(int, int, int); +static void read_c_len(); + +int decoded; /* for use in decode.c */ + +uint16_t left[2 * NC - 1], right[2 * NC - 1]; +static uint8_t *buf, c_len[NC], pt_len[NPT]; +static uint32_t bufsiz = 0, blocksize; +static uint16_t c_freq[2 * NC - 1], c_table[4096], c_code[NC], p_freq[2 * NP - 1], pt_table[256], pt_code[NPT], + t_freq[2 * NT - 1]; + +/***** decoding *****/ + +static void read_pt_len(int nn, int nbit, int i_special) +{ + int i, c, n; + uint32_t mask; + + n = getbits(nbit); + if(n == 0) + { + c = getbits(nbit); + for(i = 0; i < nn; i++) pt_len[i] = 0; + for(i = 0; i < 256; i++) pt_table[i] = c; + } + else + { + i = 0; + while(i < n) + { + c = bitbuf >> (BITBUFSIZ - 3); + if(c == 7) + { + mask = (unsigned)1 << (BITBUFSIZ - 1 - 3); + while(mask & bitbuf) + { + mask >>= 1; + c++; + } + } + fillbuf((c < 7) ? 3 : c - 3); + pt_len[i++] = c; + if(i == i_special) + { + c = getbits(2); + while(--c >= 0) pt_len[i++] = 0; + } + } + while(i < nn) pt_len[i++] = 0; + make_table(nn, pt_len, 8, pt_table); + } +} + +static void read_c_len() +{ + int i, c, n; + uint32_t mask; + + n = getbits(CBIT); + if(n == 0) + { + c = getbits(CBIT); + for(i = 0; i < NC; i++) c_len[i] = 0; + for(i = 0; i < 4096; i++) c_table[i] = c; + } + else + { + i = 0; + while(i < n) + { + c = pt_table[bitbuf >> (BITBUFSIZ - 8)]; + if(c >= NT) + { + mask = (unsigned)1 << (BITBUFSIZ - 1 - 8); + do { + if(bitbuf & mask) + c = right[c]; + else + c = left[c]; + mask >>= 1; + } while(c >= NT); + } + fillbuf(pt_len[c]); + if(c <= 2) + { + if(c == 0) + c = 1; + else if(c == 1) + c = getbits(4) + 3; + else + c = getbits(CBIT) + 20; + while(--c >= 0) c_len[i++] = 0; + } + else + c_len[i++] = c - 2; + } + while(i < NC) c_len[i++] = 0; + make_table(NC, c_len, 12, c_table); + } +} + +uint32_t decode_c() +{ + uint32_t j, mask; + + if(blocksize == 0) + { + blocksize = getbits(16); + if(blocksize == 0) + { +#if 0 + (void) fprintf(stderr, "block size = 0, decoded\n"); /* debug */ +#endif + decoded = 1; + return 0; + } + read_pt_len(NT, TBIT, 3); + read_c_len(); + read_pt_len(NP, PBIT, -1); + } + blocksize--; + j = c_table[bitbuf >> (BITBUFSIZ - 12)]; + if(j >= NC) + { + mask = (unsigned)1 << (BITBUFSIZ - 1 - 12); + do { + if(bitbuf & mask) + j = right[j]; + else + j = left[j]; + mask >>= 1; + } while(j >= NC); + } + fillbuf(c_len[j]); + return j; +} + +uint32_t decode_p() +{ + uint32_t j, mask; + + j = pt_table[bitbuf >> (BITBUFSIZ - 8)]; + if(j >= NP) + { + mask = (unsigned)1 << (BITBUFSIZ - 1 - 8); + do { + if(bitbuf & mask) + j = right[j]; + else + j = left[j]; + mask >>= 1; + } while(j >= NP); + } + fillbuf(pt_len[j]); + if(j != 0) j = ((unsigned)1 << (j - 1)) + getbits((int)(j - 1)); + return j; +} + +void huf_decode_start() +{ + init_getbits(); + blocksize = 0; +} diff --git a/zoo/io.c b/zoo/io.c new file mode 100644 index 0000000..acae40f --- /dev/null +++ b/zoo/io.c @@ -0,0 +1,105 @@ +/*$Source: /usr/home/dhesi/zoo/RCS/io.c,v $*/ +/*$Id: io.c,v 1.14 91/07/09 01:39:54 dhesi Exp $*/ +/*********************************************************** + io.c -- input/output (modified for in-memory I/O) + +Adapted from "ar" archiver written by Haruhiko Okumura. +This version reads compressed bytes from an input buffer +via mem_getc() and writes output bytes to a buffer via +mem_putc(), removing all FILE* dependencies for decompression. +***********************************************************/ +// Modified for in-memory decompression by Natalia Portillo, 2025 + +#include + +#include "ar.h" +#include "lzh.h" + +#include "lh5.h" /* mem_getc(), mem_putc(), in_ptr/in_left, out_ptr/out_left */ + +uint16_t bitbuf; +int unpackable; +size_t compsize, origsize; +uint32_t subbitbuf; +int bitcount; + +/* + * fillbuf(n) -- shift bitbuf left by n bits and read in n new bits + * now reads bytes directly from in-memory input buffer + */ +void fillbuf(int n) /* Shift bitbuf n bits left, read n bits */ +{ + bitbuf <<= n; + while(n > bitcount) + { + bitbuf |= subbitbuf << (n -= bitcount); + + /* fetch next compressed byte from in_buf */ + { + int c = mem_getc(); + subbitbuf = (c == EOF ? 0 : (uint8_t)c); + } + + bitcount = CHAR_BIT; + } + bitbuf |= subbitbuf >> (bitcount -= n); +} + +/* + * getbits(n) -- return next n bits from the bit buffer + */ +uint32_t getbits(int n) +{ + uint32_t x = bitbuf >> (BITBUFSIZ - n); + fillbuf(n); + return x; +} + +/* + * putbits(n,x) -- write the lowest n bits of x to the bit buffer + * now writes bytes directly to in-memory output buffer + */ +void putbits(int n, uint32_t x) /* Write rightmost n bits of x */ +{ + if(n < bitcount) { subbitbuf |= x << (bitcount -= n); } + else + { + /* output first byte */ + { + int w = (int)(subbitbuf | (x >> (n -= bitcount))); + mem_putc(w); + compsize++; + } + if(n < CHAR_BIT) { subbitbuf = x << (bitcount = CHAR_BIT - n); } + else + { + /* output second byte */ + { + int w2 = (int)(x >> (n - CHAR_BIT)); + mem_putc(w2); + compsize++; + } + subbitbuf = x << (bitcount = 2 * CHAR_BIT - n); + } + } +} + +/* + * init_getbits -- initialize bit reader state + */ +void init_getbits() +{ + bitbuf = 0; + subbitbuf = 0; + bitcount = 0; + fillbuf(BITBUFSIZ); +} + +/* + * init_putbits -- initialize bit writer state + */ +void init_putbits() +{ + bitcount = CHAR_BIT; + subbitbuf = 0; +} diff --git a/zoo/lh5.c b/zoo/lh5.c new file mode 100644 index 0000000..f048b24 --- /dev/null +++ b/zoo/lh5.c @@ -0,0 +1,94 @@ +/* + * This file is part of the Aaru Data Preservation Suite. + * Copyright (c) 2019-2025 Natalia Portillo. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/* lh5_mem.c + * + * In-memory I/O glue for LH5 decompression. + * Implements mem_getc(), mem_putc(), and the top-level + * lh5_decompress() entry point. + */ + +#include "lh5.h" +#include +#include +#include +#include "../library.h" + +/* Forward-declaration of the decompression driver in lzh.c */ +extern int lzh_decode(void); + +/* Buffer I/O state (see lh5_mem.h for externs) */ +const uint8_t *in_ptr; +size_t in_left; +uint8_t *out_ptr; +size_t out_left; +int mem_error; + +/* + * mem_getc(): return next compressed byte, or 0 when in_left==0. + * Never sets mem_error on input underflow. + */ +int mem_getc(void) +{ + if(in_left == 0) { return 0; /* mimic feof → subbitbuf = 0 */ } + int c = *in_ptr++; + in_left--; + return c; +} + +/* + * mem_putc(): write one output byte, set mem_error on overflow. + */ +int mem_putc(int c) +{ + if(out_left == 0) + { + mem_error = 1; + return EOF; + } + *out_ptr++ = (uint8_t)c; + out_left--; + return c; +} + +/* + * Top-level in-memory decompressor. + * + * in_buf points to 'in_len' bytes of compressed data. + * out_buf must have at least *out_len bytes available. + * On return *out_len is set to the number of bytes written. + * + * Returns 0 on success + * -1 on error (bad stream or output too small) + */ +AARU_EXPORT int AARU_CALL lh5_decompress(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len) +{ + /* Initialize buffer pointers and error flag */ + in_ptr = in_buf; + in_left = in_len; + out_ptr = out_buf; + out_left = *out_len; + mem_error = 0; + + /* Invoke the core LH5 decode routine (now buffer-based) */ + if(lzh_decode() != 0 || mem_error) { return -1; } + + /* Compute actual output size */ + *out_len = (size_t)(out_ptr - out_buf); + return 0; +} diff --git a/zoo/lh5.h b/zoo/lh5.h new file mode 100644 index 0000000..2f45874 --- /dev/null +++ b/zoo/lh5.h @@ -0,0 +1,54 @@ +/* + * This file is part of the Aaru Data Preservation Suite. + * Copyright (c) 2019-2025 Natalia Portillo. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/* lh5_mem.h + * + * In-memory I/O glue for LH5 decompression. + * Defines the mem_getc()/mem_putc() buffer readers/writers + * and declares the lh5_decompress() entry point. + */ +#ifndef AARU_COMPRESSION_NATIVE_LH5_H +#define AARU_COMPRESSION_NATIVE_LH5_H + +#include +#include + +/* -------------------------------------------------------------------------- + * State for in-memory I/O + * -------------------------------------------------------------------------- + * in_ptr/in_left: where to read next compressed byte + * out_ptr/out_left: where to write next decompressed byte + * mem_error: set to 1 on underflow/overflow + */ +extern const uint8_t *in_ptr; +extern size_t in_left; +extern uint8_t *out_ptr; +extern size_t out_left; +extern int mem_error; + +/* -------------------------------------------------------------------------- + * Fetch one byte from in_buf; returns EOF on underflow + * -------------------------------------------------------------------------- */ +int mem_getc(void); + +/* -------------------------------------------------------------------------- + * Write one byte into out_buf; returns c or EOF on overflow + * -------------------------------------------------------------------------- */ +int mem_putc(int c); + +#endif // AARU_COMPRESSION_NATIVE_LH5_H diff --git a/zoo/lzd.c b/zoo/lzd.c index 0127346..b153ea5 100644 --- a/zoo/lzd.c +++ b/zoo/lzd.c @@ -220,11 +220,11 @@ AARU_EXPORT void AARU_CALL DestroyLZDContext(void *ctx) // Public API wrapper to feed new compressed data AARU_EXPORT int AARU_CALL LZD_FeedNative(void *ctx, const unsigned char *data, size_t length) { - return (int)LZD_Feed(ctx, data, length); + return LZD_Feed(ctx, data, length); } // Public API wrapper to drain decompressed data AARU_EXPORT int AARU_CALL LZD_DrainNative(void *ctx, unsigned char *outBuf, size_t outBufLen, size_t *produced) { - return (int)LZD_Drain(ctx, outBuf, outBufLen, produced); + return LZD_Drain(ctx, outBuf, outBufLen, produced); } \ No newline at end of file diff --git a/zoo/lzh.c b/zoo/lzh.c new file mode 100644 index 0000000..2931a52 --- /dev/null +++ b/zoo/lzh.c @@ -0,0 +1,31 @@ +/* $Id: lzh.c,v 1.15 91/07/06 19:18:51 dhesi Exp $ */ +// Modified for in-memory decompression by Natalia Portillo, 2025 + +#include "lzh.h" /* prototypes for encode(), lzh_decode() */ +#include +#include "ar.h" +#include "lh5.h" /* mem_getc(), mem_putc(), in_ptr/in_left, out_ptr/out_left */ + +extern int decoded; /* from huf.c */ + +/* + * lzh_decode now reads from in_buf/in_len and writes into out_buf/out_len + * entirely in memory, via mem_getc()/mem_putc(). + */ +int lzh_decode(void) +{ + int n, i; + uint8_t buffer[DICSIZ]; + + /* Initialize the Huffman bit reader and sliding-window state */ + decode_start(); + + /* Decode blocks of up to DICSIZ bytes until end‐of‐stream */ + while(!decoded) + { + n = decode(DICSIZ, buffer); + for(i = 0; i < n; i++) { mem_putc(buffer[i]); } + } + + return mem_error ? -1 : 0; +} diff --git a/zoo/lzh.h b/zoo/lzh.h new file mode 100644 index 0000000..fc4918c --- /dev/null +++ b/zoo/lzh.h @@ -0,0 +1,31 @@ +/*$Source: /usr/home/dhesi/zoo/RCS/lzh.h,v $*/ +/*$Id: lzh.h,v 1.3 91/07/09 01:39:23 dhesi Exp $*/ + +/* +Adapted from "ar" archiver written by Haruhiko Okumura. +*/ + +// Modified for in-memory decompression by Natalia Portillo, 2025 + +/* io.c */ + +#include + +extern uint16_t bitbuf; +#define BITBUFSIZ (CHAR_BIT * sizeof bitbuf) + +/* encode.c and decode.c */ + +#define MATCHBIT 8 /* bits for MAXMATCH - THRESHOLD */ +#define MAXMATCH 256 /* formerly F (not more than UCHAR_MAX + 1) */ +#define THRESHOLD 3 /* choose optimal value */ +#define PERC_FLAG ((unsigned)0x8000) + +/* huf.c */ + +#define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD) +/* alphabet = {0, 1, 2, ..., NC - 1} */ +#define CBIT 9 /* $\lfloor \log_2 NC \rfloor + 1$ */ +#define CODE_BIT 16 /* codeword length */ + +extern uint16_t left[], right[]; \ No newline at end of file diff --git a/zoo/maketbl.c b/zoo/maketbl.c new file mode 100644 index 0000000..c34155e --- /dev/null +++ b/zoo/maketbl.c @@ -0,0 +1,77 @@ +/*$Source: /usr/home/dhesi/zoo/RCS/maketbl.c,v $*/ +/*$Id: maketbl.c,v 1.8 91/07/09 01:39:52 dhesi Exp $*/ +/*********************************************************** + maketbl.c -- make table for decoding + +Adapted from "ar" archiver written by Haruhiko Okumura. +***********************************************************/ +// Modified for in-memory decompression by Natalia Portillo, 2025 + +#include "ar.h" +#include "lzh.h" + +void make_table(int nchar, uint8_t *bitlen, int tablebits, uint16_t *table) +{ + uint16_t count[17], weight[17], start[18], *p; + uint32_t i, k, len, ch, jutbits, avail, nextcode, mask; + + for(i = 1; i <= 16; i++) count[i] = 0; + for(i = 0; i < nchar; i++) count[bitlen[i]]++; + + start[1] = 0; + for(i = 1; i <= 16; i++) start[i + 1] = start[i] + (count[i] << (16 - i)); + if(start[17] != (uint16_t)((unsigned)1 << 16)) fprintf(stderr, "Bad decode table\n"); + + jutbits = 16 - tablebits; + for(i = 1; i <= tablebits; i++) + { + start[i] >>= jutbits; + weight[i] = (unsigned)1 << (tablebits - i); + } + while(i <= 16) + { + weight[i] = (unsigned)1 << (16 - i); + i++; + } + + i = start[tablebits + 1] >> jutbits; + if(i != (uint16_t)((unsigned)1 << 16)) + { + k = 1 << tablebits; + while(i != k) table[i++] = 0; + } + + avail = nchar; + mask = (unsigned)1 << (15 - tablebits); + for(ch = 0; ch < nchar; ch++) + { + if((len = bitlen[ch]) == 0) continue; + nextcode = start[len] + weight[len]; + if(len <= tablebits) + { + for(i = start[len]; i < nextcode; i++) table[i] = ch; + } + else + { + k = start[len]; + p = &table[k >> jutbits]; + i = len - tablebits; + while(i != 0) + { + if(*p == 0) + { + right[avail] = left[avail] = 0; + *p = avail++; + } + if(k & mask) + p = &right[*p]; + else + p = &left[*p]; + k <<= 1; + i--; + } + *p = ch; + } + start[len] = nextcode; + } +}