mirror of
https://github.com/aaru-dps/Aaru.Compression.Native.git
synced 2026-09-22 22:57:00 +00:00
Add Apple LZH decompression support and corresponding tests
- Implemented Apple LZH decompression in `apple_lzh.c` and declared it in `apple_lzh.h`. - Updated `CMakeLists.txt` to include new source files for the Apple LZH decompression. - Added test cases for Apple LZH decompression in `tests/dart/dart.cpp` to verify functionality against known compressed files. - Copied necessary test data files for both fast and best compression modes into the test data directory. - Updated `library.h` to declare the new decompression function.
This commit is contained in:
@@ -131,7 +131,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
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
|
||||
add_library("Aaru.Compression.Native" SHARED library.c apple_rle.c apple_rle.h apple_lzh.c apple_lzh.h adc.c adc.h lzip.c flac.c flac.h
|
||||
zoo/lzd.c zoo/lzd.h zoo/lzh.c zoo/decode.c zoo/huf.c zoo/io.c zoo/lh5.c zoo/lh5.h zoo/lzh.h zoo/ar.h zoo/maketbl.c
|
||||
arc/pack.c arc/squeeze.c arc/crunch.c arc/lzw.c
|
||||
pak/crush.c pak/distill.c pak/bitstream.c pak/bitstream.h pak/lzw.c pak/lzw.h pak/prefixcode.c
|
||||
|
||||
280
apple_lzh.c
Normal file
280
apple_lzh.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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Apple LZH decompression — used by DART disk image format "best" compression.
|
||||
*
|
||||
* This is identical to LHA -lh1- (adaptive Huffman + LZSS, 4KB window) except
|
||||
* the LZSS window is pre-filled with zeros in the last 110 bytes rather than
|
||||
* spaces (0x20). The standard LH1 format uses spaces as specified by Okumura's
|
||||
* original LZHUF.C; Apple's DART tool uses zeros instead.
|
||||
*
|
||||
* Reference: XADLZHDynamicHandle from The Unarchiver with prefill:NO
|
||||
*/
|
||||
|
||||
#include "apple_lzh.h"
|
||||
#include "lha/bitio.h"
|
||||
#include "lha/huffman.h"
|
||||
#include "lha/lzss.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define APPLE_LZH_NUM_LEAVES 314
|
||||
#define APPLE_LZH_NUM_NODES (APPLE_LZH_NUM_LEAVES * 2 - 1)
|
||||
|
||||
typedef struct apple_lzh_node
|
||||
{
|
||||
struct apple_lzh_node *parent;
|
||||
struct apple_lzh_node *leftchild;
|
||||
struct apple_lzh_node *rightchild;
|
||||
int index;
|
||||
int freq;
|
||||
int value;
|
||||
} apple_lzh_node;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
apple_lzh_node storage[APPLE_LZH_NUM_NODES];
|
||||
apple_lzh_node *nodes[APPLE_LZH_NUM_NODES];
|
||||
} apple_lzh_tree;
|
||||
|
||||
static void apple_lzh_init_tree(apple_lzh_tree *tree)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(tree->storage, 0, sizeof(tree->storage));
|
||||
|
||||
for(i = 0; i < APPLE_LZH_NUM_NODES; i++) tree->nodes[i] = &tree->storage[i];
|
||||
|
||||
for(i = 0; i < APPLE_LZH_NUM_LEAVES; i++)
|
||||
{
|
||||
int index = APPLE_LZH_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;
|
||||
}
|
||||
|
||||
for(i = APPLE_LZH_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 apple_lzh_rearrange(apple_lzh_tree *tree, apple_lzh_node *node)
|
||||
{
|
||||
int p_index = node->index;
|
||||
int q_index = p_index;
|
||||
apple_lzh_node *q;
|
||||
|
||||
while(q_index > 0 && tree->nodes[q_index - 1]->freq < node->freq) q_index--;
|
||||
|
||||
if(q_index >= p_index) return;
|
||||
|
||||
q = tree->nodes[q_index];
|
||||
|
||||
{
|
||||
apple_lzh_node *new_q_parent = node->parent;
|
||||
apple_lzh_node *new_p_parent = q->parent;
|
||||
int p_is_right = (node->parent && node->parent->rightchild == node);
|
||||
int q_is_right = (q->parent && q->parent->rightchild == q);
|
||||
|
||||
if(node->parent)
|
||||
{
|
||||
if(p_is_right)
|
||||
node->parent->rightchild = q;
|
||||
else
|
||||
node->parent->leftchild = q;
|
||||
}
|
||||
|
||||
if(q->parent)
|
||||
{
|
||||
if(q_is_right)
|
||||
q->parent->rightchild = node;
|
||||
else
|
||||
q->parent->leftchild = node;
|
||||
}
|
||||
|
||||
node->parent = new_p_parent;
|
||||
q->parent = new_q_parent;
|
||||
}
|
||||
|
||||
tree->nodes[p_index] = q;
|
||||
tree->nodes[p_index]->index = p_index;
|
||||
tree->nodes[q_index] = node;
|
||||
tree->nodes[q_index]->index = q_index;
|
||||
}
|
||||
|
||||
static void apple_lzh_reconstruct(apple_lzh_tree *tree)
|
||||
{
|
||||
apple_lzh_node *leaves[APPLE_LZH_NUM_LEAVES];
|
||||
int n = 0;
|
||||
int i;
|
||||
int leaf_index, branch_index, node_index, pair_index;
|
||||
|
||||
for(i = 0; i < APPLE_LZH_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 = APPLE_LZH_NUM_LEAVES - 1;
|
||||
branch_index = APPLE_LZH_NUM_LEAVES - 2;
|
||||
node_index = APPLE_LZH_NUM_NODES - 1;
|
||||
pair_index = APPLE_LZH_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--;
|
||||
}
|
||||
|
||||
{
|
||||
apple_lzh_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 apple_lzh_update(apple_lzh_tree *tree, apple_lzh_node *node)
|
||||
{
|
||||
if(tree->nodes[0]->freq == 0x8000) apple_lzh_reconstruct(tree);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
node->freq++;
|
||||
|
||||
if(!node->parent) break;
|
||||
|
||||
apple_lzh_rearrange(tree, node);
|
||||
node = node->parent;
|
||||
}
|
||||
}
|
||||
|
||||
AARU_EXPORT int AARU_CALL AARU_apple_lzh_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer,
|
||||
size_t src_size)
|
||||
{
|
||||
lha_bitio bitio;
|
||||
lha_lzss lzss;
|
||||
apple_lzh_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(!dst_buffer || !dst_size || !src_buffer || src_size == 0) return -1;
|
||||
|
||||
expected = *dst_size;
|
||||
|
||||
if(!lha_lzss_init(&lzss, 4096, dst_buffer, expected)) return -1;
|
||||
|
||||
/* Pre-fill window with pattern identical to 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);
|
||||
|
||||
/* Apple variant: zeros instead of spaces for last 110 bytes */
|
||||
memset(&lzss.window[256 * 13 + 512 + 128 + 18], 0, 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, src_buffer, src_size);
|
||||
apple_lzh_init_tree(&tree);
|
||||
|
||||
while(lzss.out_pos < expected && !lha_bitio_at_eof(&bitio))
|
||||
{
|
||||
apple_lzh_node *node = tree.nodes[0];
|
||||
|
||||
while(node->leftchild || node->rightchild)
|
||||
{
|
||||
if(lha_bitio_next_bit(&bitio))
|
||||
node = node->leftchild;
|
||||
else
|
||||
node = node->rightchild;
|
||||
|
||||
if(!node) break;
|
||||
}
|
||||
|
||||
if(!node) break;
|
||||
|
||||
apple_lzh_update(&tree, node);
|
||||
|
||||
if(node->value < 0x100) { lha_lzss_emit_literal(&lzss, (uint8_t)node->value); }
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
*dst_size = lzss.out_pos;
|
||||
lha_prefix_code_free(distancecode);
|
||||
lha_lzss_cleanup(&lzss);
|
||||
return 0;
|
||||
}
|
||||
29
apple_lzh.h
Normal file
29
apple_lzh.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__APPLE_LZH_H_
|
||||
#define AARU_COMPRESSION_NATIVE__APPLE_LZH_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "library.h"
|
||||
|
||||
AARU_EXPORT int AARU_CALL AARU_apple_lzh_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer,
|
||||
size_t src_size);
|
||||
|
||||
#endif /* AARU_COMPRESSION_NATIVE__APPLE_LZH_H_ */
|
||||
@@ -60,6 +60,10 @@ AARU_EXPORT int32_t AARU_CALL AARU_adc_decode_buffer(uint8_t *dst_buffer, int32_
|
||||
AARU_EXPORT int32_t AARU_CALL AARU_apple_rle_decode_buffer(uint8_t *dst_buffer, int32_t dst_size,
|
||||
const uint8_t *src_buffer, int32_t src_size);
|
||||
|
||||
// Apple LZH decompression (DART "best" mode: LH1 variant with zero-filled window tail)
|
||||
AARU_EXPORT int AARU_CALL AARU_apple_lzh_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer,
|
||||
size_t src_size);
|
||||
|
||||
AARU_EXPORT size_t AARU_CALL AARU_flac_decode_redbook_buffer(uint8_t *dst_buffer, size_t dst_size,
|
||||
const uint8_t *src_buffer, size_t src_size);
|
||||
|
||||
|
||||
@@ -195,6 +195,30 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/stuffitx_deflate.bin
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/stuffitx_blend.bin
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/mf2dd_hfs_fast.dart.lz
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/mf2dd_hfs_best.dart.lz
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/mf1dd_hfs_fast.dart.lz
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/mf1dd_hfs_best.dart.lz
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/mf2dd_mfs_fast.dart.lz
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/mf2dd_mfs_best.dart.lz
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/mf1dd_mfs_fast.dart.lz
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/mf1dd_mfs_best.dart.lz
|
||||
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
|
||||
@@ -208,5 +232,6 @@ add_executable(tests_run apple_rle.cpp crc32.c crc32.h adc.cpp bzip2.cpp lzip.cp
|
||||
rar/rar.cpp
|
||||
cpt/cpt.cpp
|
||||
dd/dd.cpp
|
||||
stuffit/stuffit.cpp)
|
||||
stuffit/stuffit.cpp
|
||||
dart/dart.cpp)
|
||||
target_link_libraries(tests_run gtest gtest_main "Aaru.Compression.Native")
|
||||
|
||||
244
tests/dart/dart.cpp
Normal file
244
tests/dart/dart.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 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 <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "../../library.h"
|
||||
#include "../crc32.h"
|
||||
#include "dart_helpers.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t *data;
|
||||
size_t total_size;
|
||||
dart_header_t header;
|
||||
int16_t block_lengths[DART_BLOCK_ARRAY_LEN_HIGH];
|
||||
int num_blocks;
|
||||
uint8_t *raw;
|
||||
size_t raw_size;
|
||||
size_t data_offset;
|
||||
} dart_image_t;
|
||||
|
||||
static size_t read_file_to_buffer(const char *path, uint8_t **out)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if(!f) return 0;
|
||||
fseek(f, 0, SEEK_END);
|
||||
long sz = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if(sz <= 0)
|
||||
{
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
*out = (uint8_t *)malloc(sz);
|
||||
if(!*out)
|
||||
{
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
size_t rd = fread(*out, 1, sz, f);
|
||||
fclose(f);
|
||||
if(rd != (size_t)sz)
|
||||
{
|
||||
free(*out);
|
||||
*out = NULL;
|
||||
return 0;
|
||||
}
|
||||
return (size_t)sz;
|
||||
}
|
||||
|
||||
static size_t lzip_decompress_file(const char *path, uint8_t **out)
|
||||
{
|
||||
uint8_t *lz_buf = NULL;
|
||||
size_t lz_size = read_file_to_buffer(path, &lz_buf);
|
||||
if(lz_size == 0) return 0;
|
||||
|
||||
size_t try_sizes[] = {512 * 1024, 1024 * 1024, 2 * 1024 * 1024};
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
*out = (uint8_t *)malloc(try_sizes[i]);
|
||||
int32_t result = AARU_lzip_decode_buffer(*out, (int32_t)try_sizes[i], lz_buf, (int32_t)lz_size);
|
||||
if(result > 0)
|
||||
{
|
||||
free(lz_buf);
|
||||
return (size_t)result;
|
||||
}
|
||||
free(*out);
|
||||
*out = NULL;
|
||||
}
|
||||
free(lz_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool load_dart_image(const char *path, dart_image_t *img)
|
||||
{
|
||||
memset(img, 0, sizeof(*img));
|
||||
|
||||
img->raw_size = lzip_decompress_file(path, &img->raw);
|
||||
if(img->raw_size == 0) return false;
|
||||
|
||||
img->data_offset = dart_parse_header(img->raw, img->raw_size, &img->header, img->block_lengths, &img->num_blocks);
|
||||
if(img->data_offset == 0)
|
||||
{
|
||||
free(img->raw);
|
||||
img->raw = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
int active_blocks = 0;
|
||||
for(int i = 0; i < img->num_blocks; i++)
|
||||
{
|
||||
if(img->block_lengths[i] != 0) active_blocks++;
|
||||
}
|
||||
img->total_size = active_blocks * DART_BUFFER_SIZE;
|
||||
|
||||
if(img->header.srcCmp == DART_COMPRESS_RLE)
|
||||
{
|
||||
img->data = (uint8_t *)malloc(img->total_size);
|
||||
size_t out_pos = 0;
|
||||
size_t in_pos = img->data_offset;
|
||||
|
||||
for(int i = 0; i < img->num_blocks; i++)
|
||||
{
|
||||
if(img->block_lengths[i] == 0) continue;
|
||||
|
||||
if(img->block_lengths[i] == -1)
|
||||
{
|
||||
if(in_pos + DART_BUFFER_SIZE <= img->raw_size)
|
||||
memcpy(img->data + out_pos, img->raw + in_pos, DART_BUFFER_SIZE);
|
||||
in_pos += DART_BUFFER_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t comp_size = (size_t)img->block_lengths[i] * 2;
|
||||
if(in_pos + comp_size <= img->raw_size)
|
||||
{
|
||||
AARU_apple_rle_decode_buffer(img->data + out_pos, DART_BUFFER_SIZE, img->raw + in_pos,
|
||||
(int32_t)comp_size);
|
||||
}
|
||||
in_pos += comp_size;
|
||||
}
|
||||
out_pos += DART_BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void free_dart_image(dart_image_t *img)
|
||||
{
|
||||
if(img->data) free(img->data);
|
||||
if(img->raw) free(img->raw);
|
||||
memset(img, 0, sizeof(*img));
|
||||
}
|
||||
|
||||
static bool verify_dart_pair(const char *fast_name, const char *best_name)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char fast_path[PATH_MAX];
|
||||
char best_path[PATH_MAX];
|
||||
|
||||
getcwd(path, PATH_MAX);
|
||||
snprintf(fast_path, PATH_MAX, "%s/data/%s", path, fast_name);
|
||||
snprintf(best_path, PATH_MAX, "%s/data/%s", path, best_name);
|
||||
|
||||
dart_image_t fast_img, best_img;
|
||||
|
||||
if(!load_dart_image(fast_path, &fast_img))
|
||||
{
|
||||
printf(" SKIP: cannot load %s\n", fast_name);
|
||||
return false;
|
||||
}
|
||||
if(!load_dart_image(best_path, &best_img))
|
||||
{
|
||||
printf(" SKIP: cannot load %s\n", best_name);
|
||||
free_dart_image(&fast_img);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(fast_img.header.srcCmp != DART_COMPRESS_RLE || best_img.header.srcCmp != DART_COMPRESS_LZH)
|
||||
{
|
||||
printf(" SKIP: unexpected compression types\n");
|
||||
free_dart_image(&fast_img);
|
||||
free_dart_image(&best_img);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t in_pos = best_img.data_offset;
|
||||
size_t ref_block_idx = 0;
|
||||
int blocks_ok = 0;
|
||||
int blocks_fail = 0;
|
||||
|
||||
for(int i = 0; i < best_img.num_blocks; i++)
|
||||
{
|
||||
if(best_img.block_lengths[i] == 0)
|
||||
{
|
||||
if(fast_img.block_lengths[i] != 0) ref_block_idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint8_t *reference = fast_img.data + ref_block_idx * DART_BUFFER_SIZE;
|
||||
|
||||
if(best_img.block_lengths[i] == -1)
|
||||
{
|
||||
if(memcmp(best_img.raw + in_pos, reference, DART_BUFFER_SIZE) == 0)
|
||||
blocks_ok++;
|
||||
else
|
||||
blocks_fail++;
|
||||
in_pos += DART_BUFFER_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t comp_size = (size_t)best_img.block_lengths[i];
|
||||
uint8_t out[DART_BUFFER_SIZE];
|
||||
size_t out_len = DART_BUFFER_SIZE;
|
||||
|
||||
memset(out, 0, DART_BUFFER_SIZE);
|
||||
|
||||
int ret = AARU_apple_lzh_decode_buffer(out, &out_len, best_img.raw + in_pos, comp_size);
|
||||
if(ret == 0 && out_len == DART_BUFFER_SIZE && memcmp(out, reference, DART_BUFFER_SIZE) == 0)
|
||||
blocks_ok++;
|
||||
else
|
||||
blocks_fail++;
|
||||
in_pos += comp_size;
|
||||
}
|
||||
ref_block_idx++;
|
||||
}
|
||||
|
||||
printf(" %s: %d OK, %d FAIL\n", best_name, blocks_ok, blocks_fail);
|
||||
|
||||
free_dart_image(&fast_img);
|
||||
free_dart_image(&best_img);
|
||||
|
||||
return blocks_fail == 0 && blocks_ok > 0;
|
||||
}
|
||||
|
||||
TEST(DartAppleLzh, mf2dd_hfs) { EXPECT_TRUE(verify_dart_pair("mf2dd_hfs_fast.dart.lz", "mf2dd_hfs_best.dart.lz")); }
|
||||
|
||||
TEST(DartAppleLzh, mf2dd_mfs) { EXPECT_TRUE(verify_dart_pair("mf2dd_mfs_fast.dart.lz", "mf2dd_mfs_best.dart.lz")); }
|
||||
|
||||
TEST(DartAppleLzh, mf1dd_hfs) { EXPECT_TRUE(verify_dart_pair("mf1dd_hfs_fast.dart.lz", "mf1dd_hfs_best.dart.lz")); }
|
||||
|
||||
TEST(DartAppleLzh, mf1dd_mfs) { EXPECT_TRUE(verify_dart_pair("mf1dd_mfs_fast.dart.lz", "mf1dd_mfs_best.dart.lz")); }
|
||||
99
tests/dart/dart_helpers.h
Normal file
99
tests/dart/dart_helpers.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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_TESTS_DART_HELPERS_H
|
||||
#define AARU_TESTS_DART_HELPERS_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
// DART header constants
|
||||
#define DART_COMPRESS_RLE 0
|
||||
#define DART_COMPRESS_LZH 1
|
||||
#define DART_COMPRESS_NONE 2
|
||||
|
||||
#define DART_SECTORS_PER_BLOCK 40
|
||||
#define DART_SECTOR_SIZE 512
|
||||
#define DART_TAG_SECTOR_SIZE 12
|
||||
#define DART_DATA_SIZE (DART_SECTORS_PER_BLOCK * DART_SECTOR_SIZE) // 20480
|
||||
#define DART_TAG_SIZE (DART_SECTORS_PER_BLOCK * DART_TAG_SECTOR_SIZE) // 480
|
||||
#define DART_BUFFER_SIZE (DART_DATA_SIZE + DART_TAG_SIZE) // 20960
|
||||
|
||||
#define DART_BLOCK_ARRAY_LEN_LOW 40
|
||||
#define DART_BLOCK_ARRAY_LEN_HIGH 72
|
||||
|
||||
#define DART_HEADER_SIZE 4
|
||||
|
||||
// DART disk types
|
||||
#define DART_TYPE_MAC 1
|
||||
#define DART_TYPE_LISA 2
|
||||
#define DART_TYPE_APPLE2 3
|
||||
#define DART_TYPE_MAC_HD 16
|
||||
#define DART_TYPE_DOS 17
|
||||
#define DART_TYPE_DOS_HD 18
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t srcCmp; // Compression type (0=RLE, 1=LZH, 2=None)
|
||||
uint8_t srcType; // Disk type
|
||||
uint16_t srcSize; // Size in blocks (big-endian)
|
||||
} dart_header_t;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
static inline uint16_t be16(const uint8_t *p) { return (uint16_t)((p[0] << 8) | p[1]); }
|
||||
|
||||
static inline int16_t be16s(const uint8_t *p) { return (int16_t)((p[0] << 8) | p[1]); }
|
||||
|
||||
// Returns the number of block length entries based on disk type
|
||||
static inline int dart_block_array_len(uint8_t srcType)
|
||||
{
|
||||
switch(srcType)
|
||||
{
|
||||
case DART_TYPE_MAC_HD:
|
||||
case DART_TYPE_DOS_HD:
|
||||
return DART_BLOCK_ARRAY_LEN_HIGH;
|
||||
default:
|
||||
return DART_BLOCK_ARRAY_LEN_LOW;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse DART header from raw data. Returns the offset past the block lengths array.
|
||||
static inline size_t dart_parse_header(const uint8_t *data, size_t data_len, dart_header_t *hdr, int16_t *block_lengths,
|
||||
int *num_blocks)
|
||||
{
|
||||
if(data_len < DART_HEADER_SIZE) return 0;
|
||||
|
||||
hdr->srcCmp = data[0];
|
||||
hdr->srcType = data[1];
|
||||
hdr->srcSize = be16(data + 2);
|
||||
|
||||
*num_blocks = dart_block_array_len(hdr->srcType);
|
||||
size_t needed = DART_HEADER_SIZE + (*num_blocks) * 2;
|
||||
|
||||
if(data_len < needed) return 0;
|
||||
|
||||
const uint8_t *blk_ptr = data + DART_HEADER_SIZE;
|
||||
for(int i = 0; i < *num_blocks; i++) { block_lengths[i] = be16s(blk_ptr + i * 2); }
|
||||
|
||||
return needed;
|
||||
}
|
||||
|
||||
#endif // AARU_TESTS_DART_HELPERS_H
|
||||
BIN
tests/data/mf1dd_hfs_best.dart.lz
Normal file
BIN
tests/data/mf1dd_hfs_best.dart.lz
Normal file
Binary file not shown.
BIN
tests/data/mf1dd_hfs_fast.dart.lz
Normal file
BIN
tests/data/mf1dd_hfs_fast.dart.lz
Normal file
Binary file not shown.
BIN
tests/data/mf1dd_mfs_best.dart.lz
Normal file
BIN
tests/data/mf1dd_mfs_best.dart.lz
Normal file
Binary file not shown.
BIN
tests/data/mf1dd_mfs_fast.dart.lz
Normal file
BIN
tests/data/mf1dd_mfs_fast.dart.lz
Normal file
Binary file not shown.
BIN
tests/data/mf2dd_hfs_best.dart.lz
Normal file
BIN
tests/data/mf2dd_hfs_best.dart.lz
Normal file
Binary file not shown.
BIN
tests/data/mf2dd_hfs_fast.dart.lz
Normal file
BIN
tests/data/mf2dd_hfs_fast.dart.lz
Normal file
Binary file not shown.
BIN
tests/data/mf2dd_mfs_best.dart.lz
Normal file
BIN
tests/data/mf2dd_mfs_best.dart.lz
Normal file
Binary file not shown.
BIN
tests/data/mf2dd_mfs_fast.dart.lz
Normal file
BIN
tests/data/mf2dd_mfs_fast.dart.lz
Normal file
Binary file not shown.
BIN
tests/tests_run
Executable file
BIN
tests/tests_run
Executable file
Binary file not shown.
Reference in New Issue
Block a user