mirror of
https://github.com/aaru-dps/Aaru.Compression.Native.git
synced 2026-07-08 18:06:12 +00:00
Add LHA, LHARC, LARC and PMARC decompression support for various algorithms
- Implemented LZSS compression in lha/lzss.c and lha/lzss.h. - Added PMarc decompression functionality in lha/pmarc1.c and lha/pmarc1.h. - Updated library.h to include declarations for new decompression functions. - Enhanced tests to cover LHA decompression for LH1, LH6, and PMarc formats. - Added test helpers for loading LHA payloads and validating decompression results. - Included sample data files for testing LHA and PMarc decompression.
This commit is contained in:
@@ -145,7 +145,23 @@ add_library("Aaru.Compression.Native" SHARED library.c apple_rle.c apple_rle.h a
|
||||
ha/hsc.h
|
||||
ha/internal.h
|
||||
ha/swdict.c
|
||||
ha/swdict.h)
|
||||
ha/swdict.h
|
||||
lha/bitio.c
|
||||
lha/bitio.h
|
||||
lha/lzss.c
|
||||
lha/lzss.h
|
||||
lha/huffman.c
|
||||
lha/huffman.h
|
||||
lha/larc.c
|
||||
lha/larc.h
|
||||
lha/lh1.c
|
||||
lha/lh1.h
|
||||
lha/lh_static.c
|
||||
lha/lh_static.h
|
||||
lha/lh_old.c
|
||||
lha/lh_old.h
|
||||
lha/pmarc1.c
|
||||
lha/pmarc1.h)
|
||||
|
||||
include(3rdparty/bzip2.cmake)
|
||||
include(3rdparty/flac.cmake)
|
||||
|
||||
98
lha/bitio.c
Normal file
98
lha/bitio.c
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "bitio.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void lha_bitio_init(lha_bitio *self, const uint8_t *buffer, size_t length)
|
||||
{
|
||||
self->buffer = buffer;
|
||||
self->length = length;
|
||||
self->pos = 0;
|
||||
self->bits = 0;
|
||||
self->numbits = 0;
|
||||
self->eof = false;
|
||||
}
|
||||
|
||||
static void lha_bitio_fill(lha_bitio *self)
|
||||
{
|
||||
while(self->numbits <= 24 && self->pos < self->length)
|
||||
{
|
||||
self->bits |= (uint32_t)self->buffer[self->pos++] << (24 - self->numbits);
|
||||
self->numbits += 8;
|
||||
}
|
||||
|
||||
if(self->pos >= self->length && self->numbits == 0) self->eof = true;
|
||||
}
|
||||
|
||||
bool lha_bitio_at_eof(lha_bitio *self) { return self->eof && self->numbits == 0; }
|
||||
|
||||
unsigned int lha_bitio_next_bit(lha_bitio *self)
|
||||
{
|
||||
if(self->numbits == 0) lha_bitio_fill(self);
|
||||
|
||||
if(self->numbits == 0) return 0;
|
||||
|
||||
unsigned int bit = (self->bits >> 31) & 1;
|
||||
self->bits <<= 1;
|
||||
self->numbits--;
|
||||
return bit;
|
||||
}
|
||||
|
||||
unsigned int lha_bitio_next_bits(lha_bitio *self, int numbits)
|
||||
{
|
||||
if(numbits == 0) return 0;
|
||||
|
||||
if((int)self->numbits < numbits) lha_bitio_fill(self);
|
||||
|
||||
unsigned int result = self->bits >> (32 - numbits);
|
||||
self->bits <<= numbits;
|
||||
self->numbits -= numbits;
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned int lha_bitio_peek_bits(lha_bitio *self, int numbits)
|
||||
{
|
||||
if(numbits == 0) return 0;
|
||||
|
||||
if((int)self->numbits < numbits) lha_bitio_fill(self);
|
||||
|
||||
return self->bits >> (32 - numbits);
|
||||
}
|
||||
|
||||
void lha_bitio_skip_bits(lha_bitio *self, int numbits)
|
||||
{
|
||||
self->bits <<= numbits;
|
||||
self->numbits -= numbits;
|
||||
}
|
||||
|
||||
int lha_bitio_next_byte(lha_bitio *self)
|
||||
{
|
||||
if(self->pos >= self->length && self->numbits < 8) return -1;
|
||||
|
||||
/* If on byte boundary, read directly */
|
||||
if(self->numbits == 0)
|
||||
{
|
||||
if(self->pos < self->length) return self->buffer[self->pos++];
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)lha_bitio_next_bits(self, 8);
|
||||
}
|
||||
44
lha/bitio.h
Normal file
44
lha/bitio.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AARU_COMPRESSION_NATIVE__LHA_BITIO_H_
|
||||
#define AARU_COMPRESSION_NATIVE__LHA_BITIO_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct lha_bitio
|
||||
{
|
||||
const uint8_t *buffer;
|
||||
size_t length;
|
||||
size_t pos;
|
||||
uint32_t bits;
|
||||
unsigned int numbits;
|
||||
bool eof;
|
||||
} lha_bitio;
|
||||
|
||||
void lha_bitio_init(lha_bitio *self, const uint8_t *buffer, size_t length);
|
||||
bool lha_bitio_at_eof(lha_bitio *self);
|
||||
unsigned int lha_bitio_next_bit(lha_bitio *self);
|
||||
unsigned int lha_bitio_next_bits(lha_bitio *self, int numbits);
|
||||
unsigned int lha_bitio_peek_bits(lha_bitio *self, int numbits);
|
||||
void lha_bitio_skip_bits(lha_bitio *self, int numbits);
|
||||
int lha_bitio_next_byte(lha_bitio *self);
|
||||
|
||||
#endif /* AARU_COMPRESSION_NATIVE__LHA_BITIO_H_ */
|
||||
259
lha/huffman.c
Normal file
259
lha/huffman.c
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "huffman.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* --- Internal helpers --- */
|
||||
|
||||
static inline int left_branch(lha_prefix_code *c, int node) { return c->tree[node].branches[0]; }
|
||||
|
||||
static inline int right_branch(lha_prefix_code *c, int node) { return c->tree[node].branches[1]; }
|
||||
|
||||
static inline void set_branch(lha_prefix_code *c, int node, int bit, int next)
|
||||
{
|
||||
c->tree[node].branches[bit] = next;
|
||||
}
|
||||
|
||||
static inline bool is_leaf(lha_prefix_code *c, int node)
|
||||
{
|
||||
return c->tree[node].branches[0] == c->tree[node].branches[1];
|
||||
}
|
||||
|
||||
static inline int leaf_value(lha_prefix_code *c, int node) { return left_branch(c, node); }
|
||||
|
||||
static inline void set_leaf(lha_prefix_code *c, int node, int value)
|
||||
{
|
||||
c->tree[node].branches[0] = value;
|
||||
c->tree[node].branches[1] = value;
|
||||
}
|
||||
|
||||
static inline void set_empty(lha_prefix_code *c, int node)
|
||||
{
|
||||
c->tree[node].branches[0] = -1;
|
||||
c->tree[node].branches[1] = -2;
|
||||
}
|
||||
|
||||
static inline bool is_empty(lha_prefix_code *c, int node)
|
||||
{
|
||||
return c->tree[node].branches[0] == -1 && c->tree[node].branches[1] == -2;
|
||||
}
|
||||
|
||||
static inline bool is_open(lha_prefix_code *c, int node, int bit) { return c->tree[node].branches[bit] < 0; }
|
||||
|
||||
static int new_node(lha_prefix_code *c)
|
||||
{
|
||||
lha_tree_node *tmp = (lha_tree_node *)realloc(c->tree, (size_t)(c->numentries + 1) * sizeof(lha_tree_node));
|
||||
|
||||
if(!tmp) return -1;
|
||||
|
||||
c->tree = tmp;
|
||||
set_empty(c, c->numentries);
|
||||
return c->numentries++;
|
||||
}
|
||||
|
||||
/* --- Build lookup table --- */
|
||||
|
||||
static void make_table(lha_prefix_code *code, int node, lha_table_entry *table, int depth, int maxdepth)
|
||||
{
|
||||
int currtablesize = 1 << (maxdepth - depth);
|
||||
|
||||
if(node < 0)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < currtablesize; i++) table[i].length = (uint32_t)-1;
|
||||
}
|
||||
else if(is_leaf(code, node))
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < currtablesize; i++)
|
||||
{
|
||||
table[i].length = (uint32_t)depth;
|
||||
table[i].value = leaf_value(code, node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(depth == maxdepth)
|
||||
{
|
||||
table[0].length = (uint32_t)(maxdepth + 1);
|
||||
table[0].value = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
make_table(code, left_branch(code, node), table, depth + 1, maxdepth);
|
||||
make_table(code, right_branch(code, node), table + currtablesize / 2, depth + 1, maxdepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void build_table(lha_prefix_code *code)
|
||||
{
|
||||
if(code->table) return;
|
||||
|
||||
if(code->maxlength < code->minlength)
|
||||
code->tablesize = LHA_HUFFMAN_TABLE_SIZE;
|
||||
else if(code->maxlength >= LHA_HUFFMAN_TABLE_SIZE)
|
||||
code->tablesize = LHA_HUFFMAN_TABLE_SIZE;
|
||||
else
|
||||
code->tablesize = code->maxlength;
|
||||
|
||||
code->table = (lha_table_entry *)malloc(sizeof(lha_table_entry) * (size_t)(1 << code->tablesize));
|
||||
|
||||
if(code->table) make_table(code, 0, code->table, 0, code->tablesize);
|
||||
}
|
||||
|
||||
/* --- Public API --- */
|
||||
|
||||
lha_prefix_code *lha_prefix_code_new(void)
|
||||
{
|
||||
lha_prefix_code *code = (lha_prefix_code *)calloc(1, sizeof(lha_prefix_code));
|
||||
|
||||
if(!code) return NULL;
|
||||
|
||||
code->tree = (lha_tree_node *)malloc(sizeof(lha_tree_node));
|
||||
|
||||
if(!code->tree)
|
||||
{
|
||||
free(code);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
set_empty(code, 0);
|
||||
code->numentries = 1;
|
||||
code->minlength = INT_MAX;
|
||||
code->maxlength = INT_MIN;
|
||||
code->table = NULL;
|
||||
code->tablesize = 0;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
lha_prefix_code *lha_prefix_code_from_lengths(const int *lengths, int num_symbols, int max_length,
|
||||
bool shortest_is_zeros)
|
||||
{
|
||||
lha_prefix_code *code = lha_prefix_code_new();
|
||||
|
||||
if(!code) return NULL;
|
||||
|
||||
int codeval = 0;
|
||||
int symbolsleft = num_symbols;
|
||||
int length, i;
|
||||
|
||||
for(length = 1; length <= max_length; length++)
|
||||
{
|
||||
for(i = 0; i < num_symbols; i++)
|
||||
{
|
||||
if(lengths[i] != length) continue;
|
||||
|
||||
if(shortest_is_zeros)
|
||||
lha_prefix_code_add(code, i, (uint32_t)codeval, length);
|
||||
else
|
||||
lha_prefix_code_add(code, i, (uint32_t)~codeval, length);
|
||||
|
||||
codeval++;
|
||||
|
||||
if(--symbolsleft == 0) return code;
|
||||
}
|
||||
|
||||
codeval <<= 1;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
void lha_prefix_code_free(lha_prefix_code *code)
|
||||
{
|
||||
if(!code) return;
|
||||
|
||||
free(code->tree);
|
||||
free(code->table);
|
||||
free(code);
|
||||
}
|
||||
|
||||
void lha_prefix_code_add(lha_prefix_code *code, int value, uint32_t codebits, int length)
|
||||
{
|
||||
int bitpos, lastnode;
|
||||
|
||||
/* Invalidate lookup table */
|
||||
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--)
|
||||
{
|
||||
int bit = (codebits >> bitpos) & 1;
|
||||
|
||||
if(is_leaf(code, lastnode)) return; /* prefix collision */
|
||||
|
||||
if(is_open(code, lastnode, bit)) set_branch(code, lastnode, bit, new_node(code));
|
||||
|
||||
lastnode = code->tree[lastnode].branches[bit];
|
||||
|
||||
if(lastnode < 0) return; /* allocation failure */
|
||||
}
|
||||
|
||||
if(!is_empty(code, lastnode)) return; /* prefix collision */
|
||||
|
||||
set_leaf(code, lastnode, value);
|
||||
}
|
||||
|
||||
int lha_prefix_code_decode(lha_bitio *bitio, lha_prefix_code *code)
|
||||
{
|
||||
int bits, length, value, node;
|
||||
|
||||
if(!code) return -1;
|
||||
|
||||
build_table(code);
|
||||
|
||||
if(!code->table) return -1;
|
||||
|
||||
bits = (int)lha_bitio_peek_bits(bitio, code->tablesize);
|
||||
length = (int)code->table[bits].length;
|
||||
value = code->table[bits].value;
|
||||
|
||||
if(length == (int)(uint32_t)-1) return -1; /* invalid code */
|
||||
|
||||
if(length <= code->tablesize)
|
||||
{
|
||||
lha_bitio_skip_bits(bitio, length);
|
||||
return value;
|
||||
}
|
||||
|
||||
lha_bitio_skip_bits(bitio, code->tablesize);
|
||||
|
||||
node = value;
|
||||
|
||||
while(!is_leaf(code, node))
|
||||
{
|
||||
int bit = (int)lha_bitio_next_bit(bitio);
|
||||
|
||||
if(is_open(code, node, bit)) return -1;
|
||||
|
||||
node = code->tree[node].branches[bit];
|
||||
}
|
||||
|
||||
return leaf_value(code, node);
|
||||
}
|
||||
57
lha/huffman.h
Normal file
57
lha/huffman.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AARU_COMPRESSION_NATIVE__LHA_HUFFMAN_H_
|
||||
#define AARU_COMPRESSION_NATIVE__LHA_HUFFMAN_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bitio.h"
|
||||
|
||||
#define LHA_HUFFMAN_TABLE_SIZE 10
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int branches[2];
|
||||
} lha_tree_node;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t length;
|
||||
int32_t value;
|
||||
} lha_table_entry;
|
||||
|
||||
typedef struct lha_prefix_code
|
||||
{
|
||||
lha_tree_node *tree;
|
||||
int numentries;
|
||||
int minlength;
|
||||
int maxlength;
|
||||
|
||||
int tablesize;
|
||||
lha_table_entry *table;
|
||||
} lha_prefix_code;
|
||||
|
||||
lha_prefix_code *lha_prefix_code_new(void);
|
||||
lha_prefix_code *lha_prefix_code_from_lengths(const int *lengths, int num_symbols, int max_length,
|
||||
bool shortest_is_zeros);
|
||||
void lha_prefix_code_free(lha_prefix_code *code);
|
||||
void lha_prefix_code_add(lha_prefix_code *code, int value, uint32_t codebits, int length);
|
||||
int lha_prefix_code_decode(lha_bitio *bitio, lha_prefix_code *code);
|
||||
|
||||
#endif /* AARU_COMPRESSION_NATIVE__LHA_HUFFMAN_H_ */
|
||||
134
lha/larc.c
Normal file
134
lha/larc.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "larc.h"
|
||||
#include "bitio.h"
|
||||
#include "lzss.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* LArc -lzs- decompression.
|
||||
* Window: 2048 bytes.
|
||||
* Bit stream, MSB first.
|
||||
* Flag bit 1 = literal (8 bits), flag bit 0 = match (11-bit offset + 4-bit length).
|
||||
*/
|
||||
AARU_EXPORT int AARU_CALL larc_decompress_lzs(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len)
|
||||
{
|
||||
lha_bitio bitio;
|
||||
lha_lzss lzss;
|
||||
size_t expected;
|
||||
|
||||
if(!in_buf || !out_buf || !out_len || in_len == 0) return -1;
|
||||
|
||||
expected = *out_len;
|
||||
|
||||
if(!lha_lzss_init(&lzss, 2048, out_buf, expected)) return -1;
|
||||
|
||||
lha_bitio_init(&bitio, in_buf, in_len);
|
||||
|
||||
while(lzss.out_pos < expected && !lha_bitio_at_eof(&bitio))
|
||||
{
|
||||
if(lha_bitio_next_bit(&bitio))
|
||||
{
|
||||
/* Literal byte */
|
||||
uint8_t literal = (uint8_t)lha_bitio_next_bits(&bitio, 8);
|
||||
lha_lzss_emit_literal(&lzss, literal);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Match */
|
||||
int raw_offset = (int)lha_bitio_next_bits(&bitio, 11);
|
||||
int length = (int)lha_bitio_next_bits(&bitio, 4) + 2;
|
||||
int offset = (int)lzss.position - raw_offset - 17;
|
||||
|
||||
lha_lzss_emit_match(&lzss, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = lzss.out_pos;
|
||||
lha_lzss_cleanup(&lzss);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* LArc -lz5- decompression.
|
||||
* Window: 4096 bytes, pre-filled with specific byte pattern.
|
||||
* Flag-byte stream: each flag byte controls 8 items.
|
||||
* Flag bit set = literal (1 byte), flag bit clear = match (2 bytes).
|
||||
*/
|
||||
AARU_EXPORT int AARU_CALL larc_decompress_lz5(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len)
|
||||
{
|
||||
lha_lzss lzss;
|
||||
size_t expected;
|
||||
size_t in_pos;
|
||||
int i;
|
||||
|
||||
if(!in_buf || !out_buf || !out_len || in_len == 0) return -1;
|
||||
|
||||
expected = *out_len;
|
||||
|
||||
if(!lha_lzss_init(&lzss, 4096, out_buf, expected)) return -1;
|
||||
|
||||
/* Pre-fill window with specific pattern (same as lharc -lh1-) */
|
||||
for(i = 0; i < 256; i++) memset(&lzss.window[i * 13 + 18], i, 13);
|
||||
|
||||
for(i = 0; i < 256; i++) lzss.window[256 * 13 + 18 + i] = (uint8_t)i;
|
||||
|
||||
for(i = 0; i < 256; i++) lzss.window[256 * 13 + 256 + 18 + i] = (uint8_t)(255 - i);
|
||||
|
||||
memset(&lzss.window[256 * 13 + 512 + 18], 0, 128);
|
||||
memset(&lzss.window[256 * 13 + 512 + 128 + 18], ' ', 128 - 18);
|
||||
|
||||
in_pos = 0;
|
||||
|
||||
while(lzss.out_pos < expected && in_pos < in_len)
|
||||
{
|
||||
uint8_t flags = in_buf[in_pos++];
|
||||
int bit;
|
||||
|
||||
for(bit = 0; bit < 8 && lzss.out_pos < expected && in_pos < in_len; bit++)
|
||||
{
|
||||
if(flags & (1 << bit))
|
||||
{
|
||||
/* Literal */
|
||||
lha_lzss_emit_literal(&lzss, in_buf[in_pos++]);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Match: 2 bytes */
|
||||
uint8_t byte1, byte2;
|
||||
int offset, length;
|
||||
|
||||
if(in_pos + 1 >= in_len) break;
|
||||
|
||||
byte1 = in_buf[in_pos++];
|
||||
byte2 = in_buf[in_pos++];
|
||||
|
||||
offset = (int)lzss.position - (int)byte1 - ((byte2 & 0xf0) << 4) - 18;
|
||||
length = (byte2 & 0x0f) + 3;
|
||||
|
||||
lha_lzss_emit_match(&lzss, offset, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = lzss.out_pos;
|
||||
lha_lzss_cleanup(&lzss);
|
||||
return 0;
|
||||
}
|
||||
30
lha/larc.h
Normal file
30
lha/larc.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AARU_COMPRESSION_NATIVE__LHA_LARC_H_
|
||||
#define AARU_COMPRESSION_NATIVE__LHA_LARC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "../library.h"
|
||||
|
||||
AARU_EXPORT int AARU_CALL larc_decompress_lzs(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
AARU_EXPORT int AARU_CALL larc_decompress_lz5(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
#endif /* AARU_COMPRESSION_NATIVE__LHA_LARC_H_ */
|
||||
280
lha/lh1.c
Normal file
280
lha/lh1.c
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "lh1.h"
|
||||
#include "bitio.h"
|
||||
#include "huffman.h"
|
||||
#include "lzss.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define LH1_NUM_LEAVES 314
|
||||
#define LH1_NUM_NODES (LH1_NUM_LEAVES * 2 - 1)
|
||||
|
||||
typedef struct lh1_node
|
||||
{
|
||||
struct lh1_node *parent;
|
||||
struct lh1_node *leftchild;
|
||||
struct lh1_node *rightchild;
|
||||
int index; /* Position in sorted nodes[] array */
|
||||
int freq;
|
||||
int value; /* Symbol value for leaf nodes */
|
||||
} lh1_node;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
lh1_node storage[LH1_NUM_NODES];
|
||||
lh1_node *nodes[LH1_NUM_NODES]; /* Sorted by frequency order */
|
||||
} lh1_tree;
|
||||
|
||||
static void lh1_init_tree(lh1_tree *tree)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(tree->storage, 0, sizeof(tree->storage));
|
||||
|
||||
for(i = 0; i < LH1_NUM_NODES; i++) tree->nodes[i] = &tree->storage[i];
|
||||
|
||||
/* Initialize leaf nodes (at the high end of the sorted array) */
|
||||
for(i = 0; i < LH1_NUM_LEAVES; i++)
|
||||
{
|
||||
int index = LH1_NUM_NODES - 1 - i;
|
||||
tree->nodes[index]->index = index;
|
||||
tree->nodes[index]->freq = 1;
|
||||
tree->nodes[index]->value = i;
|
||||
tree->nodes[index]->leftchild = NULL;
|
||||
tree->nodes[index]->rightchild = NULL;
|
||||
}
|
||||
|
||||
/* Build branch nodes bottom-up */
|
||||
for(i = LH1_NUM_LEAVES - 2; i >= 0; i--)
|
||||
{
|
||||
tree->nodes[i]->index = i;
|
||||
tree->nodes[i]->leftchild = tree->nodes[2 * i + 1];
|
||||
tree->nodes[i]->rightchild = tree->nodes[2 * i + 2];
|
||||
tree->nodes[i]->leftchild->parent = tree->nodes[i];
|
||||
tree->nodes[i]->rightchild->parent = tree->nodes[i];
|
||||
tree->nodes[i]->freq = tree->nodes[i]->leftchild->freq + tree->nodes[i]->rightchild->freq;
|
||||
}
|
||||
|
||||
tree->nodes[0]->parent = NULL;
|
||||
}
|
||||
|
||||
static void lh1_rearrange(lh1_tree *tree, lh1_node *node)
|
||||
{
|
||||
int p_index = node->index;
|
||||
int q_index = p_index;
|
||||
lh1_node *p = node;
|
||||
lh1_node *q;
|
||||
|
||||
/* Find highest position where this node should be */
|
||||
while(q_index > 0 && tree->nodes[q_index - 1]->freq < p->freq) q_index--;
|
||||
|
||||
if(q_index >= p_index) return;
|
||||
|
||||
q = tree->nodes[q_index];
|
||||
|
||||
/* Swap parent-child links using pointers */
|
||||
{
|
||||
lh1_node *new_q_parent = p->parent;
|
||||
lh1_node *new_p_parent = q->parent;
|
||||
int p_is_right = (p->parent && p->parent->rightchild == p);
|
||||
int q_is_right = (q->parent && q->parent->rightchild == q);
|
||||
|
||||
if(p->parent)
|
||||
{
|
||||
if(p_is_right)
|
||||
p->parent->rightchild = q;
|
||||
else
|
||||
p->parent->leftchild = q;
|
||||
}
|
||||
|
||||
if(q->parent)
|
||||
{
|
||||
if(q_is_right)
|
||||
q->parent->rightchild = p;
|
||||
else
|
||||
q->parent->leftchild = p;
|
||||
}
|
||||
|
||||
p->parent = new_p_parent;
|
||||
q->parent = new_q_parent;
|
||||
}
|
||||
|
||||
/* Swap positions in sorted array */
|
||||
tree->nodes[p_index] = q;
|
||||
tree->nodes[p_index]->index = p_index;
|
||||
tree->nodes[q_index] = p;
|
||||
tree->nodes[q_index]->index = q_index;
|
||||
}
|
||||
|
||||
static void lh1_reconstruct(lh1_tree *tree)
|
||||
{
|
||||
lh1_node *leaves[LH1_NUM_LEAVES];
|
||||
int n = 0;
|
||||
int i;
|
||||
int leaf_index, branch_index, node_index, pair_index;
|
||||
|
||||
/* Collect all leaf nodes and halve their frequencies */
|
||||
for(i = 0; i < LH1_NUM_NODES; i++)
|
||||
{
|
||||
if(!tree->nodes[i]->leftchild && !tree->nodes[i]->rightchild)
|
||||
{
|
||||
tree->nodes[i]->freq = (tree->nodes[i]->freq + 1) / 2;
|
||||
leaves[n++] = tree->nodes[i];
|
||||
}
|
||||
}
|
||||
|
||||
leaf_index = LH1_NUM_LEAVES - 1;
|
||||
branch_index = LH1_NUM_LEAVES - 2;
|
||||
node_index = LH1_NUM_NODES - 1;
|
||||
pair_index = LH1_NUM_NODES - 2;
|
||||
|
||||
while(node_index >= 0)
|
||||
{
|
||||
while(node_index >= pair_index)
|
||||
{
|
||||
tree->nodes[node_index] = leaves[leaf_index];
|
||||
tree->nodes[node_index]->index = node_index;
|
||||
node_index--;
|
||||
leaf_index--;
|
||||
}
|
||||
|
||||
{
|
||||
lh1_node *branch = &tree->storage[branch_index--];
|
||||
branch->leftchild = tree->nodes[pair_index];
|
||||
branch->rightchild = tree->nodes[pair_index + 1];
|
||||
branch->leftchild->parent = branch;
|
||||
branch->rightchild->parent = branch;
|
||||
branch->freq = tree->nodes[pair_index]->freq + tree->nodes[pair_index + 1]->freq;
|
||||
|
||||
while(leaf_index >= 0 && leaves[leaf_index]->freq <= branch->freq)
|
||||
{
|
||||
tree->nodes[node_index] = leaves[leaf_index];
|
||||
tree->nodes[node_index]->index = node_index;
|
||||
node_index--;
|
||||
leaf_index--;
|
||||
}
|
||||
|
||||
tree->nodes[node_index] = branch;
|
||||
tree->nodes[node_index]->index = node_index;
|
||||
node_index--;
|
||||
pair_index -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
tree->nodes[0]->parent = NULL;
|
||||
}
|
||||
|
||||
static void lh1_update(lh1_tree *tree, lh1_node *node)
|
||||
{
|
||||
if(tree->nodes[0]->freq == 0x8000) lh1_reconstruct(tree);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
node->freq++;
|
||||
|
||||
if(!node->parent) break;
|
||||
|
||||
lh1_rearrange(tree, node);
|
||||
node = node->parent;
|
||||
}
|
||||
}
|
||||
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh1(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len)
|
||||
{
|
||||
lha_bitio bitio;
|
||||
lha_lzss lzss;
|
||||
lh1_tree tree;
|
||||
lha_prefix_code *distancecode;
|
||||
size_t expected;
|
||||
int i;
|
||||
|
||||
static const int dist_lengths[64] = {3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8};
|
||||
|
||||
if(!in_buf || !out_buf || !out_len || in_len == 0) return -1;
|
||||
|
||||
expected = *out_len;
|
||||
|
||||
if(!lha_lzss_init(&lzss, 4096, out_buf, expected)) return -1;
|
||||
|
||||
/* Pre-fill window with specific pattern */
|
||||
for(i = 0; i < 256; i++) memset(&lzss.window[i * 13 + 18], i, 13);
|
||||
|
||||
for(i = 0; i < 256; i++) lzss.window[256 * 13 + 18 + i] = (uint8_t)i;
|
||||
|
||||
for(i = 0; i < 256; i++) lzss.window[256 * 13 + 256 + 18 + i] = (uint8_t)(255 - i);
|
||||
|
||||
memset(&lzss.window[256 * 13 + 512 + 18], 0, 128);
|
||||
memset(&lzss.window[256 * 13 + 512 + 128 + 18], ' ', 128 - 18);
|
||||
|
||||
distancecode = lha_prefix_code_from_lengths(dist_lengths, 64, 8, true);
|
||||
|
||||
if(!distancecode)
|
||||
{
|
||||
lha_lzss_cleanup(&lzss);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lha_bitio_init(&bitio, in_buf, in_len);
|
||||
lh1_init_tree(&tree);
|
||||
|
||||
while(lzss.out_pos < expected && !lha_bitio_at_eof(&bitio))
|
||||
{
|
||||
/* Traverse adaptive Huffman tree */
|
||||
lh1_node *node = tree.nodes[0]; /* root */
|
||||
|
||||
while(node->leftchild || node->rightchild)
|
||||
{
|
||||
if(lha_bitio_next_bit(&bitio))
|
||||
node = node->leftchild;
|
||||
else
|
||||
node = node->rightchild;
|
||||
|
||||
if(!node) break;
|
||||
}
|
||||
|
||||
if(!node) break;
|
||||
|
||||
lh1_update(&tree, node);
|
||||
|
||||
if(node->value < 0x100)
|
||||
{
|
||||
/* Literal */
|
||||
lha_lzss_emit_literal(&lzss, (uint8_t)node->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Match */
|
||||
int length = node->value - 0x100 + 3;
|
||||
int highbits = lha_prefix_code_decode(&bitio, distancecode);
|
||||
int lowbits = (int)lha_bitio_next_bits(&bitio, 6);
|
||||
int offset = (highbits << 6) + lowbits + 1;
|
||||
|
||||
lha_lzss_emit_match(&lzss, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = lzss.out_pos;
|
||||
lha_prefix_code_free(distancecode);
|
||||
lha_lzss_cleanup(&lzss);
|
||||
return 0;
|
||||
}
|
||||
28
lha/lh1.h
Normal file
28
lha/lh1.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AARU_COMPRESSION_NATIVE__LHA_LH1_H_
|
||||
#define AARU_COMPRESSION_NATIVE__LHA_LH1_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "../library.h"
|
||||
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh1(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
#endif /* AARU_COMPRESSION_NATIVE__LHA_LH1_H_ */
|
||||
1217
lha/lh_old.c
Normal file
1217
lha/lh_old.c
Normal file
File diff suppressed because it is too large
Load Diff
32
lha/lh_old.h
Normal file
32
lha/lh_old.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AARU_COMPRESSION_NATIVE__LHA_LH_OLD_H_
|
||||
#define AARU_COMPRESSION_NATIVE__LHA_LH_OLD_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "../library.h"
|
||||
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh2(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh3(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
AARU_EXPORT int AARU_CALL pmarc_decompress_pm2(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
#endif /* AARU_COMPRESSION_NATIVE__LHA_LH_OLD_H_ */
|
||||
252
lha/lh_static.c
Normal file
252
lha/lh_static.c
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "lh_static.h"
|
||||
#include "bitio.h"
|
||||
#include "huffman.h"
|
||||
#include "lzss.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Parse a generic Huffman code from the bitstream.
|
||||
* Width = number of bits for the symbol count.
|
||||
* specialindex = index at which to insert extra zero-run bits (-1 for none).
|
||||
*/
|
||||
static lha_prefix_code *parse_code(lha_bitio *bitio, int width, int specialindex)
|
||||
{
|
||||
int num = (int)lha_bitio_next_bits(bitio, width);
|
||||
|
||||
if(num == 0)
|
||||
{
|
||||
/* Single symbol */
|
||||
int val = (int)lha_bitio_next_bits(bitio, width);
|
||||
lha_prefix_code *code = lha_prefix_code_new();
|
||||
|
||||
if(!code) return NULL;
|
||||
|
||||
lha_prefix_code_add(code, val, 0, 0);
|
||||
return code;
|
||||
}
|
||||
else
|
||||
{
|
||||
int *codelengths = (int *)calloc((size_t)num, sizeof(int));
|
||||
int n = 0;
|
||||
lha_prefix_code *code;
|
||||
|
||||
if(!codelengths) return NULL;
|
||||
|
||||
while(n < num)
|
||||
{
|
||||
int len = (int)lha_bitio_next_bits(bitio, 3);
|
||||
|
||||
if(len == 7)
|
||||
while(lha_bitio_next_bit(bitio)) len++;
|
||||
|
||||
codelengths[n++] = len;
|
||||
|
||||
if(n == specialindex)
|
||||
{
|
||||
int zeroes = (int)lha_bitio_next_bits(bitio, 2);
|
||||
int i;
|
||||
|
||||
for(i = 0; i < zeroes && n < num; i++) codelengths[n++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
code = lha_prefix_code_from_lengths(codelengths, num, 16, true);
|
||||
free(codelengths);
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the literal Huffman code from the bitstream (uses metacode).
|
||||
*/
|
||||
static lha_prefix_code *parse_literal_code(lha_bitio *bitio)
|
||||
{
|
||||
lha_prefix_code *metacode = parse_code(bitio, 5, 3);
|
||||
lha_prefix_code *code;
|
||||
int num, n;
|
||||
int *codelengths;
|
||||
|
||||
if(!metacode) return NULL;
|
||||
|
||||
num = (int)lha_bitio_next_bits(bitio, 9);
|
||||
|
||||
if(num == 0)
|
||||
{
|
||||
int val = (int)lha_bitio_next_bits(bitio, 9);
|
||||
|
||||
lha_prefix_code_free(metacode);
|
||||
code = lha_prefix_code_new();
|
||||
|
||||
if(!code) return NULL;
|
||||
|
||||
lha_prefix_code_add(code, val, 0, 0);
|
||||
return code;
|
||||
}
|
||||
|
||||
codelengths = (int *)calloc((size_t)num, sizeof(int));
|
||||
|
||||
if(!codelengths)
|
||||
{
|
||||
lha_prefix_code_free(metacode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
n = 0;
|
||||
|
||||
while(n < num)
|
||||
{
|
||||
int c = lha_prefix_code_decode(bitio, metacode);
|
||||
|
||||
if(c < 0) break;
|
||||
|
||||
if(c <= 2)
|
||||
{
|
||||
int zeros = 0, i;
|
||||
|
||||
switch(c)
|
||||
{
|
||||
case 0: zeros = 1; break;
|
||||
case 1: zeros = (int)lha_bitio_next_bits(bitio, 4) + 3; break;
|
||||
case 2: zeros = (int)lha_bitio_next_bits(bitio, 9) + 20; break;
|
||||
}
|
||||
|
||||
if(n + zeros > num) zeros = num - n;
|
||||
|
||||
for(i = 0; i < zeros; i++) codelengths[n++] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
codelengths[n++] = c - 2;
|
||||
}
|
||||
}
|
||||
|
||||
lha_prefix_code_free(metacode);
|
||||
code = lha_prefix_code_from_lengths(codelengths, num, 16, true);
|
||||
free(codelengths);
|
||||
return code;
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal decompression for LH4/5/6/7 with parameterized window bits.
|
||||
*/
|
||||
static int lh_static_decompress(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len,
|
||||
int window_bits)
|
||||
{
|
||||
lha_bitio bitio;
|
||||
lha_lzss lzss;
|
||||
lha_prefix_code *literalcode = NULL;
|
||||
lha_prefix_code *distancecode = NULL;
|
||||
int blocksize = 0;
|
||||
int blockpos = 0;
|
||||
size_t expected;
|
||||
int dist_width;
|
||||
|
||||
if(!in_buf || !out_buf || !out_len || in_len == 0) return -1;
|
||||
|
||||
expected = *out_len;
|
||||
dist_width = window_bits < 15 ? 4 : 5;
|
||||
|
||||
if(!lha_lzss_init(&lzss, (size_t)1 << window_bits, out_buf, expected)) return -1;
|
||||
|
||||
lha_bitio_init(&bitio, in_buf, in_len);
|
||||
|
||||
while(lzss.out_pos < expected && !lha_bitio_at_eof(&bitio))
|
||||
{
|
||||
if(blockpos >= blocksize)
|
||||
{
|
||||
/* New block */
|
||||
blocksize = (int)lha_bitio_next_bits(&bitio, 16);
|
||||
blockpos = 0;
|
||||
|
||||
lha_prefix_code_free(literalcode);
|
||||
lha_prefix_code_free(distancecode);
|
||||
|
||||
literalcode = parse_literal_code(&bitio);
|
||||
distancecode = parse_code(&bitio, dist_width, -1);
|
||||
|
||||
if(!literalcode || !distancecode)
|
||||
{
|
||||
lha_prefix_code_free(literalcode);
|
||||
lha_prefix_code_free(distancecode);
|
||||
lha_lzss_cleanup(&lzss);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
blockpos++;
|
||||
|
||||
{
|
||||
int lit = lha_prefix_code_decode(&bitio, literalcode);
|
||||
|
||||
if(lit < 0) break;
|
||||
|
||||
if(lit < 0x100)
|
||||
{
|
||||
lha_lzss_emit_literal(&lzss, (uint8_t)lit);
|
||||
}
|
||||
else
|
||||
{
|
||||
int length = lit - 0x100 + 3;
|
||||
int bit = lha_prefix_code_decode(&bitio, distancecode);
|
||||
int offset;
|
||||
|
||||
if(bit < 0) break;
|
||||
|
||||
if(bit == 0)
|
||||
offset = 1;
|
||||
else if(bit == 1)
|
||||
offset = 2;
|
||||
else
|
||||
offset = (1 << (bit - 1)) + (int)lha_bitio_next_bits(&bitio, bit - 1) + 1;
|
||||
|
||||
lha_lzss_emit_match(&lzss, offset, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = lzss.out_pos;
|
||||
lha_prefix_code_free(literalcode);
|
||||
lha_prefix_code_free(distancecode);
|
||||
lha_lzss_cleanup(&lzss);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh4(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len)
|
||||
{
|
||||
return lh_static_decompress(in_buf, in_len, out_buf, out_len, 12);
|
||||
}
|
||||
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh5(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len)
|
||||
{
|
||||
return lh_static_decompress(in_buf, in_len, out_buf, out_len, 13);
|
||||
}
|
||||
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh6(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len)
|
||||
{
|
||||
return lh_static_decompress(in_buf, in_len, out_buf, out_len, 15);
|
||||
}
|
||||
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh7(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len)
|
||||
{
|
||||
return lh_static_decompress(in_buf, in_len, out_buf, out_len, 16);
|
||||
}
|
||||
34
lha/lh_static.h
Normal file
34
lha/lh_static.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AARU_COMPRESSION_NATIVE__LHA_LH_STATIC_H_
|
||||
#define AARU_COMPRESSION_NATIVE__LHA_LH_STATIC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "../library.h"
|
||||
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh4(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh5(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh6(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh7(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
#endif /* AARU_COMPRESSION_NATIVE__LHA_LH_STATIC_H_ */
|
||||
48
lha/lzss.c
Normal file
48
lha/lzss.c
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "lzss.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
bool lha_lzss_init(lha_lzss *self, size_t window_size, uint8_t *out_buf, size_t out_size)
|
||||
{
|
||||
self->window = (uint8_t *)malloc(window_size);
|
||||
|
||||
if(!self->window) return false;
|
||||
|
||||
self->mask = window_size - 1; /* Assume power-of-two */
|
||||
self->position = 0;
|
||||
self->out_buf = out_buf;
|
||||
self->out_pos = 0;
|
||||
self->out_size = out_size;
|
||||
|
||||
memset(self->window, 0, window_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void lha_lzss_cleanup(lha_lzss *self)
|
||||
{
|
||||
if(self->window)
|
||||
{
|
||||
free(self->window);
|
||||
self->window = NULL;
|
||||
}
|
||||
}
|
||||
68
lha/lzss.h
Normal file
68
lha/lzss.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AARU_COMPRESSION_NATIVE__LHA_LZSS_H_
|
||||
#define AARU_COMPRESSION_NATIVE__LHA_LZSS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct lha_lzss
|
||||
{
|
||||
uint8_t *window;
|
||||
size_t mask;
|
||||
int64_t position;
|
||||
uint8_t *out_buf;
|
||||
size_t out_pos;
|
||||
size_t out_size;
|
||||
} lha_lzss;
|
||||
|
||||
bool lha_lzss_init(lha_lzss *self, size_t window_size, uint8_t *out_buf, size_t out_size);
|
||||
void lha_lzss_cleanup(lha_lzss *self);
|
||||
|
||||
static inline void lha_lzss_emit_literal(lha_lzss *self, uint8_t literal)
|
||||
{
|
||||
self->window[self->position & self->mask] = literal;
|
||||
self->position++;
|
||||
|
||||
if(self->out_pos < self->out_size) self->out_buf[self->out_pos++] = literal;
|
||||
}
|
||||
|
||||
static inline void lha_lzss_emit_match(lha_lzss *self, int offset, int length)
|
||||
{
|
||||
size_t windowoffs = (size_t)(self->position & self->mask);
|
||||
int i;
|
||||
|
||||
for(i = 0; i < length; i++)
|
||||
{
|
||||
uint8_t byte = self->window[(windowoffs + i - offset) & self->mask];
|
||||
self->window[(windowoffs + i) & self->mask] = byte;
|
||||
|
||||
if(self->out_pos < self->out_size) self->out_buf[self->out_pos++] = byte;
|
||||
}
|
||||
|
||||
self->position += length;
|
||||
}
|
||||
|
||||
static inline uint8_t lha_lzss_get_byte(lha_lzss *self, int64_t pos)
|
||||
{
|
||||
return self->window[pos & self->mask];
|
||||
}
|
||||
|
||||
#endif /* AARU_COMPRESSION_NATIVE__LHA_LZSS_H_ */
|
||||
387
lha/pmarc1.c
Normal file
387
lha/pmarc1.c
Normal file
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 Natalia Portillo.
|
||||
*
|
||||
* Based on code by Simon Howard, ISC license:
|
||||
* Copyright (c) 2011, 2012, Simon Howard
|
||||
* Permission to use, copy, modify, and/or distribute this software
|
||||
* for any purpose with or without fee is hereby granted, provided
|
||||
* that the above copyright notice and this permission notice appear
|
||||
* in all copies.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pmarc1.h"
|
||||
#include "bitio.h"
|
||||
#include "lzss.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_BYTE_BLOCK_LEN 216
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t prev;
|
||||
uint8_t next;
|
||||
} pma1_history_node;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
pma1_history_node history[256];
|
||||
uint8_t history_head;
|
||||
} pma1_history_list;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int offset;
|
||||
unsigned int bits;
|
||||
} variable_length_table;
|
||||
|
||||
/* --- History list --- */
|
||||
|
||||
static void init_history_list(pma1_history_list *list)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 256; i++)
|
||||
{
|
||||
list->history[i].prev = (uint8_t)(i + 1);
|
||||
list->history[i].next = (uint8_t)(i - 1);
|
||||
}
|
||||
|
||||
list->history_head = 0x20;
|
||||
|
||||
list->history[0x7f].prev = 0x00;
|
||||
list->history[0x00].next = 0x7f;
|
||||
list->history[0x1f].prev = 0xa0;
|
||||
list->history[0xa0].next = 0x1f;
|
||||
list->history[0xdf].prev = 0x80;
|
||||
list->history[0x80].next = 0xdf;
|
||||
list->history[0x9f].prev = 0xe0;
|
||||
list->history[0xe0].next = 0x9f;
|
||||
list->history[0xff].prev = 0x20;
|
||||
list->history[0x20].next = 0xff;
|
||||
}
|
||||
|
||||
static uint8_t find_in_history_list(pma1_history_list *list, uint8_t count)
|
||||
{
|
||||
uint8_t code = list->history_head;
|
||||
int i;
|
||||
|
||||
if(count < 128)
|
||||
{
|
||||
for(i = 0; i < count; i++) code = list->history[code].prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0; i < 256 - count; i++) code = list->history[code].next;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static void update_history_list(pma1_history_list *list, uint8_t b)
|
||||
{
|
||||
pma1_history_node *node, *old_head;
|
||||
|
||||
if(list->history_head == b) return;
|
||||
|
||||
node = &list->history[b];
|
||||
old_head = &list->history[list->history_head];
|
||||
|
||||
/* Unhook from current position */
|
||||
list->history[node->next].prev = node->prev;
|
||||
list->history[node->prev].next = node->next;
|
||||
|
||||
/* Hook between old head and its next */
|
||||
node->prev = list->history_head;
|
||||
node->next = old_head->next;
|
||||
list->history[old_head->next].prev = b;
|
||||
old_head->next = b;
|
||||
|
||||
list->history_head = b;
|
||||
}
|
||||
|
||||
/* --- Variable length tables --- */
|
||||
|
||||
static const variable_length_table copy_ranges[] = {
|
||||
{0, 6}, /* 0: 0 + (1<<6) = 64 */
|
||||
{64, 8}, /* 1: 64 + (1<<8) = 320 */
|
||||
{0, 6}, /* 2: 0 + (1<<6) = 64 */
|
||||
{64, 9}, /* 3: 64 + (1<<9) = 576 */
|
||||
{576, 11}, /* 4: 576 + (1<<11) = 2624 */
|
||||
{2624, 13}, /* 5: 2624 + (1<<13) = 10816 */
|
||||
/* Early-stream overrides: */
|
||||
{64, 8}, /* 6: replaces 3 when pos < 320 */
|
||||
{576, 8}, /* 7: replaces 4 when pos < 832 */
|
||||
{576, 9}, /* 8: replaces 4 when pos < 1088 */
|
||||
{576, 10}, /* 9: replaces 4 when pos < 1600 */
|
||||
{2624, 8}, /* 10: replaces 5 when pos < 2880 */
|
||||
{2624, 9}, /* 11: replaces 5 when pos < 3136 */
|
||||
{2624, 10}, /* 12: replaces 5 when pos < 3648 */
|
||||
{2624, 11}, /* 13: replaces 5 when pos < 4672 */
|
||||
{2624, 12}, /* 14: replaces 5 when pos < 6720 */
|
||||
};
|
||||
|
||||
static const variable_length_table byte_ranges[] = {
|
||||
{0, 4}, /* 0 + (1<<4) = 16 */
|
||||
{16, 4}, /* 16 + (1<<4) = 32 */
|
||||
{32, 5}, /* 32 + (1<<5) = 64 */
|
||||
{64, 6}, /* 64 + (1<<6) = 128 */
|
||||
{128, 6}, /* 128 + (1<<6) = 192 */
|
||||
{192, 6}, /* 192 + (1<<6) = 256 */
|
||||
};
|
||||
|
||||
static const uint8_t byte_decode_trees[][5] = {
|
||||
{0x12, 0x2d, 0xef, 0x1c, 0xab}, {0x12, 0x23, 0xde, 0xab, 0xcf}, {0x12, 0x2c, 0xd2, 0xab, 0xef},
|
||||
{0x12, 0xa2, 0xd2, 0xbc, 0xef}, {0x12, 0xa2, 0xc2, 0xbd, 0xef}, {0x12, 0xa2, 0xcd, 0xb1, 0xef},
|
||||
{0x12, 0xab, 0x12, 0xcd, 0xef}, {0x12, 0xab, 0x1d, 0xc1, 0xef}, {0x12, 0xab, 0xc1, 0xd1, 0xef},
|
||||
{0xa1, 0x12, 0x2c, 0xde, 0xbf}, {0xa1, 0x1d, 0x1c, 0xb1, 0xef}, {0xa1, 0x12, 0x2d, 0xef, 0xbc},
|
||||
{0xa1, 0x12, 0xb2, 0xde, 0xcf}, {0xa1, 0x12, 0xbc, 0xd1, 0xef}, {0xa1, 0x1c, 0xb1, 0xd1, 0xef},
|
||||
{0xa1, 0xb1, 0x12, 0xcd, 0xef}, {0xa1, 0xb1, 0xc1, 0xd1, 0xef}, {0x12, 0x1c, 0xde, 0xab, 0x00},
|
||||
{0x12, 0xa2, 0xcd, 0xbe, 0x00}, {0x12, 0xab, 0xc1, 0xde, 0x00}, {0xa1, 0x1d, 0x1c, 0xbe, 0x00},
|
||||
{0xa1, 0x12, 0xbc, 0xde, 0x00}, {0xa1, 0x1c, 0xb1, 0xde, 0x00}, {0xa1, 0xb1, 0xc1, 0xde, 0x00},
|
||||
{0x1d, 0x1c, 0xab, 0x00, 0x00}, {0x1c, 0xa1, 0xbd, 0x00, 0x00}, {0x12, 0xab, 0xcd, 0x00, 0x00},
|
||||
{0xa1, 0x1c, 0xbd, 0x00, 0x00}, {0xa1, 0xb1, 0xcd, 0x00, 0x00}, {0xa1, 0xbc, 0x00, 0x00, 0x00},
|
||||
{0xab, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
};
|
||||
|
||||
/* --- Decode helper functions --- */
|
||||
|
||||
static int decode_variable_length(lha_bitio *input, const variable_length_table *table, unsigned int header)
|
||||
{
|
||||
int value = (int)lha_bitio_next_bits(input, (int)table[header].bits);
|
||||
|
||||
return (int)table[header].offset + value;
|
||||
}
|
||||
|
||||
static int next_bit_after_threshold(lha_bitio *input, unsigned int pos, unsigned int threshold, int def)
|
||||
{
|
||||
if(pos >= threshold) return (int)lha_bitio_next_bit(input);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
static int read_copy_byte_count(lha_bitio *input)
|
||||
{
|
||||
int x = (int)lha_bitio_next_bits(input, 2);
|
||||
|
||||
if(x < 3) return x + 3;
|
||||
|
||||
x = (int)lha_bitio_next_bits(input, 3);
|
||||
|
||||
if(x < 5) return x + 6;
|
||||
else if(x == 5) return (int)lha_bitio_next_bits(input, 2) + 11;
|
||||
else if(x == 6) return (int)lha_bitio_next_bits(input, 3) + 15;
|
||||
|
||||
/* x == 7 */
|
||||
x = (int)lha_bitio_next_bits(input, 6);
|
||||
|
||||
if(x < 62) return x + 23;
|
||||
else if(x == 62) return (int)lha_bitio_next_bits(input, 5) + 85;
|
||||
|
||||
/* x == 63 */
|
||||
return (int)lha_bitio_next_bits(input, 7) + 117;
|
||||
}
|
||||
|
||||
static int read_copy_type_range(lha_bitio *input, unsigned int pos)
|
||||
{
|
||||
int x = (int)lha_bitio_next_bit(input);
|
||||
|
||||
if(x == 0)
|
||||
{
|
||||
x = next_bit_after_threshold(input, pos, 576, 0);
|
||||
|
||||
if(x != 0) return 4;
|
||||
|
||||
return next_bit_after_threshold(input, pos, 64, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = next_bit_after_threshold(input, pos, 64, 1);
|
||||
|
||||
if(x == 0) return 3;
|
||||
|
||||
x = next_bit_after_threshold(input, pos, 2624, 1);
|
||||
|
||||
if(x != 0) return 2;
|
||||
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
static int read_byte_decode_index(lha_bitio *input, const uint8_t *tree)
|
||||
{
|
||||
const uint8_t *ptr = tree;
|
||||
|
||||
if(ptr[0] == 0) return 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int bit = (int)lha_bitio_next_bit(input);
|
||||
unsigned int child;
|
||||
|
||||
if(bit == 0)
|
||||
child = (*ptr >> 4) & 0x0f;
|
||||
else
|
||||
child = *ptr & 0x0f;
|
||||
|
||||
if(child >= 10) return (int)(child - 10);
|
||||
|
||||
ptr += child;
|
||||
}
|
||||
}
|
||||
|
||||
static int read_byte(lha_bitio *input, const uint8_t *tree, pma1_history_list *history)
|
||||
{
|
||||
int index = read_byte_decode_index(input, tree);
|
||||
int count = decode_variable_length(input, byte_ranges, (unsigned int)index);
|
||||
|
||||
return find_in_history_list(history, (uint8_t)count);
|
||||
}
|
||||
|
||||
static int read_byte_block_count(lha_bitio *input)
|
||||
{
|
||||
int x = (int)lha_bitio_next_bits(input, 2);
|
||||
|
||||
if(x < 3) return x + 1;
|
||||
|
||||
x = (int)lha_bitio_next_bits(input, 3);
|
||||
|
||||
if(x < 7) return x + 4;
|
||||
|
||||
x = (int)lha_bitio_next_bits(input, 4);
|
||||
|
||||
if(x < 14) return x + 11;
|
||||
else if(x == 14) return (int)lha_bitio_next_bits(input, 6) + 25;
|
||||
|
||||
/* x == 15 */
|
||||
return (int)lha_bitio_next_bits(input, 7) + 89;
|
||||
}
|
||||
|
||||
/* --- Main decompression --- */
|
||||
|
||||
AARU_EXPORT int AARU_CALL pmarc_decompress_pm1(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf,
|
||||
size_t *out_len)
|
||||
{
|
||||
lha_bitio bitio;
|
||||
lha_lzss lzss;
|
||||
pma1_history_list history;
|
||||
const uint8_t *byte_tree;
|
||||
size_t expected;
|
||||
int bytes_left = 0;
|
||||
bool next_is_match = false;
|
||||
|
||||
if(!in_buf || !out_buf || !out_len || in_len == 0) return -1;
|
||||
|
||||
expected = *out_len;
|
||||
|
||||
if(!lha_lzss_init(&lzss, 16384, out_buf, expected)) return -1;
|
||||
|
||||
lha_bitio_init(&bitio, in_buf, in_len);
|
||||
init_history_list(&history);
|
||||
|
||||
/* Read 5-bit header selecting the byte decode tree */
|
||||
{
|
||||
int index = (int)lha_bitio_next_bits(&bitio, 5);
|
||||
byte_tree = byte_decode_trees[index];
|
||||
}
|
||||
|
||||
while(lzss.out_pos < expected && !lha_bitio_at_eof(&bitio))
|
||||
{
|
||||
if(bytes_left > 0)
|
||||
{
|
||||
/* Emit a byte from the history-encoded stream */
|
||||
uint8_t b = (uint8_t)read_byte(&bitio, byte_tree, &history);
|
||||
lha_lzss_emit_literal(&lzss, b);
|
||||
update_history_list(&history, b);
|
||||
bytes_left--;
|
||||
}
|
||||
else if(next_is_match || lha_bitio_next_bit(&bitio) == 0)
|
||||
{
|
||||
/* Match/copy */
|
||||
int range_index, count, history_distance, offset;
|
||||
|
||||
next_is_match = false;
|
||||
range_index = read_copy_type_range(&bitio, (unsigned int)lzss.position);
|
||||
|
||||
if(range_index < 2)
|
||||
count = 2;
|
||||
else
|
||||
count = read_copy_byte_count(&bitio);
|
||||
|
||||
/* Apply early-stream overrides */
|
||||
if(range_index == 3)
|
||||
{
|
||||
if(lzss.position < 320) range_index = 6;
|
||||
}
|
||||
else if(range_index == 4)
|
||||
{
|
||||
if(lzss.position < 832)
|
||||
range_index = 7;
|
||||
else if(lzss.position < 1088)
|
||||
range_index = 8;
|
||||
else if(lzss.position < 1600)
|
||||
range_index = 9;
|
||||
}
|
||||
else if(range_index == 5)
|
||||
{
|
||||
if(lzss.position < 2880)
|
||||
range_index = 10;
|
||||
else if(lzss.position < 3136)
|
||||
range_index = 11;
|
||||
else if(lzss.position < 3648)
|
||||
range_index = 12;
|
||||
else if(lzss.position < 4672)
|
||||
range_index = 13;
|
||||
else if(lzss.position < 6720)
|
||||
range_index = 14;
|
||||
}
|
||||
|
||||
history_distance = decode_variable_length(&bitio, copy_ranges, (unsigned int)range_index);
|
||||
offset = history_distance + 1;
|
||||
|
||||
lha_lzss_emit_match(&lzss, offset, count);
|
||||
|
||||
/* Update history for all emitted match bytes */
|
||||
{
|
||||
size_t match_start = lzss.out_pos - (size_t)count;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < count; i++) update_history_list(&history, out_buf[match_start + i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Byte block: read count, then emit that many literal bytes */
|
||||
bytes_left = read_byte_block_count(&bitio);
|
||||
|
||||
if(bytes_left < MAX_BYTE_BLOCK_LEN) next_is_match = true;
|
||||
|
||||
/* Process first byte immediately */
|
||||
{
|
||||
uint8_t b = (uint8_t)read_byte(&bitio, byte_tree, &history);
|
||||
lha_lzss_emit_literal(&lzss, b);
|
||||
update_history_list(&history, b);
|
||||
bytes_left--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = lzss.out_pos;
|
||||
lha_lzss_cleanup(&lzss);
|
||||
return 0;
|
||||
}
|
||||
29
lha/pmarc1.h
Normal file
29
lha/pmarc1.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AARU_COMPRESSION_NATIVE__LHA_PMARC1_H_
|
||||
#define AARU_COMPRESSION_NATIVE__LHA_PMARC1_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "../library.h"
|
||||
|
||||
AARU_EXPORT int AARU_CALL pmarc_decompress_pm1(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf,
|
||||
size_t *out_len);
|
||||
|
||||
#endif /* AARU_COMPRESSION_NATIVE__LHA_PMARC1_H_ */
|
||||
33
library.h
33
library.h
@@ -201,4 +201,37 @@ AARU_EXPORT int AARU_CALL ha_asc_decompress(const unsigned char *in_buf, size_t
|
||||
|
||||
AARU_EXPORT int AARU_CALL ha_hsc_decompress(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf, size_t *out_len);
|
||||
|
||||
// LHA -lh1- (Dynamic Huffman, 4KB window)
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh1(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
// LHA -lh2- (Dynamic Huffman, legacy)
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh2(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
// LHA -lh3- (Static Huffman, legacy)
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh3(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
// LHA -lh4- (Block Huffman, 4KB window)
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh4(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
// LHA -lh5- (Block Huffman, 8KB window)
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh5(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
// LHA -lh6- (Block Huffman, 32KB window)
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh6(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
// LHA -lh7- (Block Huffman, 64KB window)
|
||||
AARU_EXPORT int AARU_CALL lha_decompress_lh7(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
// LArc -lzs- (LZSS, 2KB window)
|
||||
AARU_EXPORT int AARU_CALL larc_decompress_lzs(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
// LArc -lz5- (Flag-byte LZSS, 4KB window)
|
||||
AARU_EXPORT int AARU_CALL larc_decompress_lz5(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
// PMarc -pm1- (Adaptive history + Huffman)
|
||||
AARU_EXPORT int AARU_CALL pmarc_decompress_pm1(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
// PMarc -pm2- (Dynamic trees + history, legacy)
|
||||
AARU_EXPORT int AARU_CALL pmarc_decompress_pm2(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len);
|
||||
|
||||
#endif // AARU_COMPRESSION_NATIVE_LIBRARY_H
|
||||
|
||||
@@ -78,9 +78,22 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/ha_hsc.bin
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/xz.xz
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/larc_lz5.lzs
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/lha_lh6.lzh
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/lharc_lh1.lzh
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/pmarc_pm2.pma
|
||||
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 lz4.cpp
|
||||
zoo/lzd.cpp arc/pack.cpp lh5.cpp arc/squeeze.cpp arc/crunch.cpp arc/squash.cpp pak/crush.cpp
|
||||
pak/distill.cpp ha.cpp xz.cpp)
|
||||
pak/distill.cpp ha.cpp xz.cpp
|
||||
lha/lh_static.cpp lha/lh1.cpp lha/larc.cpp lha/lh_old.cpp)
|
||||
target_link_libraries(tests_run gtest gtest_main "Aaru.Compression.Native")
|
||||
|
||||
BIN
tests/data/larc_lz5.lzs
Normal file
BIN
tests/data/larc_lz5.lzs
Normal file
Binary file not shown.
BIN
tests/data/lha_lh6.lzh
Normal file
BIN
tests/data/lha_lh6.lzh
Normal file
Binary file not shown.
BIN
tests/data/lharc_lh1.lzh
Executable file
BIN
tests/data/lharc_lh1.lzh
Executable file
Binary file not shown.
BIN
tests/data/pmarc_pm2.pma
Executable file
BIN
tests/data/pmarc_pm2.pma
Executable file
Binary file not shown.
74
tests/lha/larc.cpp
Normal file
74
tests/lha/larc.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../../library.h"
|
||||
#include "../../lha/larc.h"
|
||||
#include "../crc32.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "lha_helpers.h"
|
||||
|
||||
/* alice29.txt decompressed CRC32 */
|
||||
#define EXPECTED_CRC32 0x66007dba
|
||||
#define EXPECTED_SIZE 152089
|
||||
|
||||
static uint8_t *lz5_payload;
|
||||
static lha_test_info lz5_info;
|
||||
|
||||
class lz5Fixture : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp()
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char filename[PATH_MAX];
|
||||
|
||||
getcwd(path, PATH_MAX);
|
||||
snprintf(filename, PATH_MAX, "%s/data/larc_lz5.lzs", path);
|
||||
|
||||
lz5_payload = load_lha_payload(filename, &lz5_info);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
if(lz5_payload) free(lz5_payload);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(lz5Fixture, lz5_decompress)
|
||||
{
|
||||
ASSERT_NE(lz5_payload, nullptr);
|
||||
EXPECT_EQ(lz5_info.uncompressed_size, (uint32_t)EXPECTED_SIZE);
|
||||
|
||||
size_t out_len = EXPECTED_SIZE;
|
||||
uint8_t *out_buf = (uint8_t *)malloc(out_len);
|
||||
|
||||
int err = larc_decompress_lz5(lz5_payload, lz5_info.payload_size, out_buf, &out_len);
|
||||
|
||||
EXPECT_EQ(err, 0);
|
||||
EXPECT_EQ(out_len, (size_t)EXPECTED_SIZE);
|
||||
|
||||
uint32_t crc = crc32_data(out_buf, (uint32_t)out_len);
|
||||
|
||||
free(out_buf);
|
||||
|
||||
EXPECT_EQ(crc, (uint32_t)EXPECTED_CRC32);
|
||||
}
|
||||
74
tests/lha/lh1.cpp
Normal file
74
tests/lha/lh1.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../../library.h"
|
||||
#include "../../lha/lh1.h"
|
||||
#include "../crc32.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "lha_helpers.h"
|
||||
|
||||
/* alice29.txt decompressed CRC32 */
|
||||
#define EXPECTED_CRC32 0x66007dba
|
||||
#define EXPECTED_SIZE 152089
|
||||
|
||||
static uint8_t *lh1_payload;
|
||||
static lha_test_info lh1_info;
|
||||
|
||||
class lh1Fixture : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp()
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char filename[PATH_MAX];
|
||||
|
||||
getcwd(path, PATH_MAX);
|
||||
snprintf(filename, PATH_MAX, "%s/data/lharc_lh1.lzh", path);
|
||||
|
||||
lh1_payload = load_lha_payload(filename, &lh1_info);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
if(lh1_payload) free(lh1_payload);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(lh1Fixture, lh1_decompress)
|
||||
{
|
||||
ASSERT_NE(lh1_payload, nullptr);
|
||||
EXPECT_EQ(lh1_info.uncompressed_size, (uint32_t)EXPECTED_SIZE);
|
||||
|
||||
size_t out_len = EXPECTED_SIZE;
|
||||
uint8_t *out_buf = (uint8_t *)malloc(out_len);
|
||||
|
||||
int err = lha_decompress_lh1(lh1_payload, lh1_info.payload_size, out_buf, &out_len);
|
||||
|
||||
EXPECT_EQ(err, 0);
|
||||
EXPECT_EQ(out_len, (size_t)EXPECTED_SIZE);
|
||||
|
||||
uint32_t crc = crc32_data(out_buf, (uint32_t)out_len);
|
||||
|
||||
free(out_buf);
|
||||
|
||||
EXPECT_EQ(crc, (uint32_t)EXPECTED_CRC32);
|
||||
}
|
||||
75
tests/lha/lh_old.cpp
Normal file
75
tests/lha/lh_old.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../../library.h"
|
||||
#include "../../lha/lh_old.h"
|
||||
#include "../crc32.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "lha_helpers.h"
|
||||
|
||||
/* PMarc -pm2- decompresses to 152192 bytes */
|
||||
#define PM2_EXPECTED_SIZE 152192
|
||||
#define PM2_EXPECTED_CRC32 0x1bbf031e
|
||||
|
||||
static uint8_t *pm2_payload;
|
||||
static lha_test_info pm2_info;
|
||||
|
||||
class pm2Fixture : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp()
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char filename[PATH_MAX];
|
||||
|
||||
getcwd(path, PATH_MAX);
|
||||
snprintf(filename, PATH_MAX, "%s/data/pmarc_pm2.pma", path);
|
||||
|
||||
pm2_payload = load_lha_payload(filename, &pm2_info);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
if(pm2_payload) free(pm2_payload);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(pm2Fixture, pm2_decompress)
|
||||
{
|
||||
ASSERT_NE(pm2_payload, nullptr);
|
||||
EXPECT_EQ(pm2_info.uncompressed_size, (uint32_t)PM2_EXPECTED_SIZE);
|
||||
|
||||
size_t out_len = PM2_EXPECTED_SIZE;
|
||||
uint8_t *out_buf = (uint8_t *)malloc(out_len);
|
||||
|
||||
int err = pmarc_decompress_pm2(pm2_payload, pm2_info.payload_size, out_buf, &out_len);
|
||||
|
||||
EXPECT_EQ(err, 0);
|
||||
EXPECT_EQ(out_len, (size_t)PM2_EXPECTED_SIZE);
|
||||
|
||||
uint32_t crc = crc32_data(out_buf, (uint32_t)out_len);
|
||||
|
||||
free(out_buf);
|
||||
|
||||
/* Validate CRC32 of decompressed data */
|
||||
EXPECT_EQ(crc, (uint32_t)PM2_EXPECTED_CRC32);
|
||||
}
|
||||
74
tests/lha/lh_static.cpp
Normal file
74
tests/lha/lh_static.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../../library.h"
|
||||
#include "../../lha/lh_static.h"
|
||||
#include "../crc32.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "lha_helpers.h"
|
||||
|
||||
/* alice29.txt decompressed CRC32 (same as zoo/lh5 test) */
|
||||
#define EXPECTED_CRC32 0x66007dba
|
||||
#define EXPECTED_SIZE 152089
|
||||
|
||||
static uint8_t *lh6_payload;
|
||||
static lha_test_info lh6_info;
|
||||
|
||||
class lh6Fixture : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp()
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char filename[PATH_MAX];
|
||||
|
||||
getcwd(path, PATH_MAX);
|
||||
snprintf(filename, PATH_MAX, "%s/data/lha_lh6.lzh", path);
|
||||
|
||||
lh6_payload = load_lha_payload(filename, &lh6_info);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
if(lh6_payload) free(lh6_payload);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(lh6Fixture, lh6_decompress)
|
||||
{
|
||||
ASSERT_NE(lh6_payload, nullptr);
|
||||
EXPECT_EQ(lh6_info.uncompressed_size, (uint32_t)EXPECTED_SIZE);
|
||||
|
||||
size_t out_len = EXPECTED_SIZE;
|
||||
uint8_t *out_buf = (uint8_t *)malloc(out_len);
|
||||
|
||||
int err = lha_decompress_lh6(lh6_payload, lh6_info.payload_size, out_buf, &out_len);
|
||||
|
||||
EXPECT_EQ(err, 0);
|
||||
EXPECT_EQ(out_len, (size_t)EXPECTED_SIZE);
|
||||
|
||||
uint32_t crc = crc32_data(out_buf, (uint32_t)out_len);
|
||||
|
||||
free(out_buf);
|
||||
|
||||
EXPECT_EQ(crc, (uint32_t)EXPECTED_CRC32);
|
||||
}
|
||||
122
tests/lha/lha_helpers.h
Normal file
122
tests/lha/lha_helpers.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AARU_COMPRESSION_NATIVE__TESTS_LHA_HELPERS_H_
|
||||
#define AARU_COMPRESSION_NATIVE__TESTS_LHA_HELPERS_H_
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
/*
|
||||
* Minimal LHA archive header parser for tests.
|
||||
* Extracts compressed payload from an LHA/LArc/PMarc archive file.
|
||||
* Supports header levels 0, 1, and 2.
|
||||
*/
|
||||
struct lha_test_info
|
||||
{
|
||||
char method[6]; /* e.g., "-lh6-" */
|
||||
uint32_t compressed_size;
|
||||
uint32_t uncompressed_size;
|
||||
uint8_t level;
|
||||
size_t payload_offset; /* offset of compressed data in the file */
|
||||
size_t payload_size; /* actual compressed data size */
|
||||
};
|
||||
|
||||
static int parse_lha_header(const uint8_t *data, size_t data_len, struct lha_test_info *info)
|
||||
{
|
||||
if(data_len < 22) return -1;
|
||||
|
||||
uint8_t header_size_byte = data[0];
|
||||
uint32_t compressed_size = (uint32_t)data[7] | ((uint32_t)data[8] << 8) | ((uint32_t)data[9] << 16) |
|
||||
((uint32_t)data[10] << 24);
|
||||
uint32_t uncompressed_size = (uint32_t)data[11] | ((uint32_t)data[12] << 8) | ((uint32_t)data[13] << 16) |
|
||||
((uint32_t)data[14] << 24);
|
||||
uint8_t level = data[20];
|
||||
|
||||
memcpy(info->method, &data[2], 5);
|
||||
info->method[5] = '\0';
|
||||
info->compressed_size = compressed_size;
|
||||
info->uncompressed_size = uncompressed_size;
|
||||
info->level = level;
|
||||
|
||||
if(level == 0 || level == 1)
|
||||
{
|
||||
size_t base_header = (size_t)header_size_byte + 2;
|
||||
info->payload_offset = base_header;
|
||||
info->payload_size = compressed_size;
|
||||
}
|
||||
else if(level == 2)
|
||||
{
|
||||
uint16_t header_size = (uint16_t)data[0] | ((uint16_t)data[1] << 8);
|
||||
info->payload_offset = header_size;
|
||||
info->payload_size = compressed_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t *load_lha_payload(const char *filename, struct lha_test_info *info)
|
||||
{
|
||||
FILE *file;
|
||||
size_t file_size;
|
||||
uint8_t *file_data;
|
||||
uint8_t *payload;
|
||||
|
||||
file = fopen(filename, "rb");
|
||||
if(!file) return NULL;
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
file_size = (size_t)ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
file_data = (uint8_t *)malloc(file_size);
|
||||
if(!file_data) { fclose(file); return NULL; }
|
||||
|
||||
fread(file_data, 1, file_size, file);
|
||||
fclose(file);
|
||||
|
||||
if(parse_lha_header(file_data, file_size, info) != 0)
|
||||
{
|
||||
free(file_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(info->payload_offset + info->payload_size > file_size)
|
||||
{
|
||||
free(file_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
payload = (uint8_t *)malloc(info->payload_size);
|
||||
if(!payload) { free(file_data); return NULL; }
|
||||
|
||||
memcpy(payload, file_data + info->payload_offset, info->payload_size);
|
||||
free(file_data);
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
#endif /* AARU_COMPRESSION_NATIVE__TESTS_LHA_HELPERS_H_ */
|
||||
Reference in New Issue
Block a user