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:
2026-04-19 21:02:42 +01:00
parent 692322f86f
commit d9c3129e6a
16 changed files with 683 additions and 2 deletions

View File

@@ -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
View 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
View 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_ */

View File

@@ -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);

View File

@@ -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
View 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
View 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
tests/tests_run Executable file

Binary file not shown.