diff --git a/CMakeLists.txt b/CMakeLists.txt
index 87ceb68..8abe34a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -161,7 +161,17 @@ add_library("Aaru.Compression.Native" SHARED library.c apple_rle.c apple_rle.h a
lha/lh_old.c
lha/lh_old.h
lha/pmarc1.c
- lha/pmarc1.h)
+ lha/pmarc1.h
+ ace/ace.c
+ ace/ace.h
+ ace/ace_internal.h
+ ace/bitstream.c
+ ace/huffman.c
+ ace/lz77.c
+ ace/sound.c
+ ace/pic.c
+ ace/v1.c
+ ace/v2.c)
include(3rdparty/bzip2.cmake)
include(3rdparty/flac.cmake)
diff --git a/ace/ace.c b/ace/ace.c
new file mode 100644
index 0000000..375e793
--- /dev/null
+++ b/ace/ace.c
@@ -0,0 +1,93 @@
+/*
+ * 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 .
+ */
+
+#include
+#include
+
+#include "ace.h"
+#include "ace_internal.h"
+
+void ace_init_bitwidth(ace_decompress_ctx_t *ctx)
+{
+ int i, k, j;
+
+ i = k = 0;
+ j = 1;
+
+ do
+ {
+ if(i == j)
+ {
+ k++;
+ j <<= 1;
+ }
+ ctx->bit_width_array[i++] = (uint8_t)k;
+ } while(i < ACE_MAX_DIST2);
+
+ for(i = -128; i <= 127; i++)
+ {
+ if(i < 0)
+ ctx->dif_bit_width_array[(uint8_t)i] = ctx->bit_width_array[-2 * i - 1];
+ else
+ ctx->dif_bit_width_array[(uint8_t)i] = ctx->bit_width_array[2 * i];
+ }
+}
+
+int ace_get_bit_width(ace_decompress_ctx_t *ctx, int value)
+{
+ return value < ACE_MAX_DIST2 ? ctx->bit_width_array[value]
+ : ctx->bit_width_array[value >> ACE_MAX_DIC_BITS2] + ACE_MAX_DIC_BITS2;
+}
+
+int ace_decompress_init(ace_decompress_ctx_t *ctx, int dic_bits)
+{
+ memset(ctx, 0, sizeof(*ctx));
+
+ if(dic_bits < 10) dic_bits = 10;
+ if(dic_bits > ACE_MAX_DIC_BITS) dic_bits = ACE_MAX_DIC_BITS;
+
+ ctx->dic_bits = dic_bits;
+ ctx->dic_size = 1u << dic_bits;
+ ctx->dic_and = ctx->dic_size - 1;
+
+ ctx->dictionary = (uint8_t *)malloc(ctx->dic_size);
+ if(!ctx->dictionary) return -1;
+ memset(ctx->dictionary, 0, ctx->dic_size);
+
+ ace_init_bitwidth(ctx);
+
+ {
+ int i;
+ for(i = 0; i <= 128; i++)
+ {
+ ctx->sound_quantizer[256 - i] = ctx->sound_quantizer[i] = ace_get_bit_width(ctx, i);
+ }
+ }
+
+ return 0;
+}
+
+void ace_decompress_free(ace_decompress_ctx_t *ctx)
+{
+ if(ctx->dictionary)
+ {
+ free(ctx->dictionary);
+ ctx->dictionary = NULL;
+ }
+ ace_pic_done(ctx);
+}
diff --git a/ace/ace.h b/ace/ace.h
new file mode 100644
index 0000000..34052c1
--- /dev/null
+++ b/ace/ace.h
@@ -0,0 +1,245 @@
+/*
+ * 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 .
+ */
+
+#ifndef AARU_ACE_H
+#define AARU_ACE_H
+
+#include
+#include
+
+/* ACE compression technique types */
+#define ACE_TECHNIQUE_STORE 0
+#define ACE_TECHNIQUE_LZ77 1 /* ACE v1 LZ77 */
+#define ACE_TECHNIQUE_BLOCKED 2 /* ACE v2 blocked */
+
+/* ACE v2 block subtypes */
+#define ACE_BLOCK_LZ77_NORM 0
+#define ACE_BLOCK_LZ77_DELTA 1
+#define ACE_BLOCK_LZ77_EXE 2
+#define ACE_BLOCK_SOUND_8 3
+#define ACE_BLOCK_SOUND_16 4
+#define ACE_BLOCK_SOUND_32_1 5
+#define ACE_BLOCK_SOUND_32_2 6
+#define ACE_BLOCK_PIC 7
+
+/* Constants */
+#define ACE_MAX_DIC_BITS 22
+#define ACE_MAX_CODE_WIDTH 11
+#define ACE_MAX_LEN 259
+#define ACE_MAX_DIST_AT_LEN2 255
+#define ACE_MAX_DIST_AT_LEN3 8191
+#define ACE_MAX_DIC_BITS2 (ACE_MAX_DIC_BITS / 2)
+#define ACE_MAX_DIC_SIZE (1 << ACE_MAX_DIC_BITS)
+#define ACE_MAX_DIST2 (1 << ACE_MAX_DIC_BITS2)
+
+#define ACE_MAX_MAIN_CODE (260 + ACE_MAX_DIC_BITS + 2)
+#define ACE_MAX_LEN_CODE (256 - 1)
+#define ACE_TYPE_CODE (260 + ACE_MAX_DIC_BITS + 1)
+
+#define ACE_MAX_SAVE_WIDTH 7
+#define ACE_MAX_WIDTH_TO_SAVE 15
+
+/* Sound constants */
+#define ACE_SOUND_RUNLEN_CODES 32
+#define ACE_SOUND_MAX_CODE (256 + ACE_SOUND_RUNLEN_CODES + 1)
+#define ACE_SOUND_MAX_CODE_WIDTH 10
+#define ACE_SOUND_TYPE_CODE (256 + ACE_SOUND_RUNLEN_CODES)
+#define ACE_SOUND_MAX_CHANNELS 3
+#define ACE_SOUND_MAX_MODELS (ACE_SOUND_MAX_CHANNELS * 3)
+#define ACE_SOUND_CHANNEL_BLOCK 2000
+#define ACE_SOUND_HISTORY_SIZE 256
+#define ACE_SOUND_MAX_BLOCK (ACE_SOUND_CHANNEL_BLOCK * ACE_SOUND_MAX_MODELS + 8)
+
+/* PIC constants */
+#define ACE_PIC_N0 32
+#define ACE_PIC_S1 3
+#define ACE_PIC_S2 12
+#define ACE_PIC_S3 40
+#define ACE_PIC_CONTEXT_NUMBER (9 * 9 * 9)
+
+/* Read buffer size in 32-bit words */
+#define ACE_READ_BUF_SIZE 8192
+
+/* Max partition size for symbol batching */
+#define ACE_MAX_PART_SIZE 1024
+
+/* Max delta block */
+#define ACE_MAX_DELTA_BLOCK 65536
+
+/* Max HUFF code (for sort arrays) */
+#define ACE_MAX_HUFF_CODE ACE_SOUND_MAX_CODE
+
+/* PIC quantizer array access macros */
+#define ACE_PIC_QUANT(ctx, v) ((ctx)->pic_quantizer[(v) + 255])
+#define ACE_PIC_QUANT_X9(ctx, v) ((ctx)->pic_quantizer_x9[(v) + 255])
+#define ACE_PIC_QUANT_X9X9(ctx, v) ((ctx)->pic_quantizer_x9x9[(v) + 255])
+
+/* Sound channel number tables */
+extern const int ace_sound_channel_num[4][4];
+extern const int ace_sound_models[4];
+
+/* PIC context entry */
+typedef struct
+{
+ uint8_t error_counters[4];
+ uint8_t predictor_number;
+ uint16_t used_counter;
+ uint16_t average_counter;
+} ace_pic_context_t;
+
+/* Main decompressor context */
+typedef struct
+{
+ /* Input buffer and position */
+ const uint8_t *in_buf;
+ size_t in_len;
+ size_t in_pos;
+
+ /* Bit reader state */
+ uint32_t read_buf[ACE_READ_BUF_SIZE];
+ int read_buf_pos;
+ int read_code_bit_pos;
+ uint32_t read_code;
+
+ /* Dictionary */
+ uint8_t *dictionary;
+ uint32_t dic_pos;
+ uint32_t dic_size;
+ uint32_t dic_and;
+ int dic_bits;
+
+ /* File decompression state */
+ int64_t file_size;
+ int64_t file_pos;
+
+ /* Huffman tables for LZ77 */
+ uint16_t main_huff_symbols[(1 << ACE_MAX_CODE_WIDTH) + 1];
+ uint16_t main_huff_widths[ACE_MAX_MAIN_CODE + 2];
+ uint16_t len_huff_symbols[(1 << ACE_MAX_CODE_WIDTH) + 1];
+ uint16_t len_huff_widths[ACE_MAX_LEN_CODE + 3];
+
+ /* Symbol batching buffers */
+ uint16_t main_buf[ACE_MAX_PART_SIZE];
+ uint16_t len_buf[ACE_MAX_PART_SIZE];
+ uint32_t dist_buf[ACE_MAX_PART_SIZE];
+ uint32_t main_buf_pos;
+ uint32_t len_dist_buf_pos;
+
+ /* LZ77 state */
+ int old_dists_pos;
+ uint32_t old_dists[4];
+ uint32_t block_size;
+ uint32_t part_size;
+ uint32_t block_byte_count;
+ uint32_t block_buf_size;
+
+ /* V2 blocked state */
+ int type;
+ int next_type;
+ int over_next_type;
+
+ /* Delta preprocessing */
+ int prep_num_kept_bytes;
+ int prep_kept_bytes_pos;
+ uint32_t prep_last_delta;
+ int delta_dist;
+ int next_delta_dist;
+ int delta_len;
+ int next_delta_len;
+ int delta_block_size;
+ int delta_plane_size;
+ int delta_plane;
+ int delta_plane_pos;
+ uint8_t prep_kept_bytes_buf[ACE_MAX_DELTA_BLOCK + ACE_MAX_LEN];
+
+ /* EXE preprocessing */
+ int exe_mode;
+ int next_exe_mode;
+
+ /* Quicksort working arrays */
+ uint16_t sort_elements[ACE_MAX_HUFF_CODE + 2];
+ uint16_t sort_frequencies[(ACE_MAX_HUFF_CODE + 2) * 2];
+ uint16_t save_widths[ACE_MAX_WIDTH_TO_SAVE];
+
+ /* Bitwidth tables */
+ uint8_t bit_width_array[ACE_MAX_DIST2];
+ uint8_t dif_bit_width_array[256];
+
+ /* Sound decompression */
+ uint16_t sound_huff_symbols[ACE_SOUND_MAX_MODELS][(1 << ACE_SOUND_MAX_CODE_WIDTH) + 1];
+ uint16_t sound_huff_widths[ACE_SOUND_MAX_MODELS][ACE_SOUND_MAX_CODE + 2];
+ int sound_quantizer[256];
+
+ struct
+ {
+ int predictor_dif_cnt[ACE_SOUND_MAX_CHANNELS][2];
+ int last_predictor_dif_cnt[ACE_SOUND_MAX_CHANNELS][2];
+ int rar_dif_cnt[ACE_SOUND_MAX_CHANNELS][4];
+ int rar_coefficient[ACE_SOUND_MAX_CHANNELS][4];
+ int rar_dif[ACE_SOUND_MAX_CHANNELS][9];
+ int byte_count[ACE_SOUND_MAX_CHANNELS];
+ int last_byte[ACE_SOUND_MAX_CHANNELS];
+ int last_delta[ACE_SOUND_MAX_CHANNELS];
+ int state[ACE_SOUND_MAX_CHANNELS];
+ int code[ACE_SOUND_MAX_CHANNELS];
+ int adaptive_model_cnt[ACE_SOUND_MAX_CHANNELS];
+ int adaptive_model_use[ACE_SOUND_MAX_CHANNELS];
+ int models;
+ int mode;
+ int sound_block_size;
+ } sound_var;
+
+ /* PIC decompression */
+ int pic_quantizer[511]; /* indexed as [v+255] for v in -255..255 */
+ int pic_quantizer_x9[511];
+ int pic_quantizer_x9x9[511];
+ ace_pic_context_t pic_context[2][ACE_PIC_CONTEXT_NUMBER];
+ ace_pic_context_t *cur_context;
+ int8_t *pic_data[2];
+ int pic_width;
+ int pic_planes;
+ int pic_data_pos;
+ int pic_cur_col;
+ int pic_cur_plane;
+ int pic_cur_pred;
+ int pic_cur_state;
+ int pic_pixel_a;
+ int pic_pixel_b;
+ int pic_pixel_c;
+ int pic_pixel_d;
+ int pic_pixel_x;
+
+ /* Error flag */
+ int error;
+} ace_decompress_ctx_t;
+
+/* Initialize context for decompression */
+int ace_decompress_init(ace_decompress_ctx_t *ctx, int dic_bits);
+
+/* Decompress an ACE v1 (LZ77) compressed block */
+int ace_decompress_v1(ace_decompress_ctx_t *ctx, const uint8_t *in_buf, size_t in_len, uint8_t *out_buf,
+ size_t *out_len);
+
+/* Decompress an ACE v2 (blocked) compressed block */
+int ace_decompress_v2(ace_decompress_ctx_t *ctx, const uint8_t *in_buf, size_t in_len, uint8_t *out_buf,
+ size_t *out_len);
+
+/* Free context resources */
+void ace_decompress_free(ace_decompress_ctx_t *ctx);
+
+#endif /* AARU_ACE_H */
diff --git a/ace/ace_internal.h b/ace/ace_internal.h
new file mode 100644
index 0000000..1f12d4d
--- /dev/null
+++ b/ace/ace_internal.h
@@ -0,0 +1,60 @@
+/*
+ * 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 .
+ */
+
+#ifndef AARU_ACE_INTERNAL_H
+#define AARU_ACE_INTERNAL_H
+
+#include "ace.h"
+
+/* bitstream.c */
+void ace_fill_read_buf(ace_decompress_ctx_t *ctx);
+void ace_add_bits(ace_decompress_ctx_t *ctx, int bits);
+void ace_init_read_buf(ace_decompress_ctx_t *ctx);
+
+/* huffman.c */
+int ace_make_codes(ace_decompress_ctx_t *ctx, unsigned max_width, unsigned tab_size, uint16_t *widths, uint16_t *codes);
+int ace_read_widths(ace_decompress_ctx_t *ctx, unsigned max_width, uint16_t *codes, uint16_t *widths,
+ unsigned max_size);
+
+/* lz77.c */
+void ace_lz77_write_char(ace_decompress_ctx_t *ctx, uint8_t ch);
+void ace_lz77_copy_string(ace_decompress_ctx_t *ctx, uint32_t dist, int len);
+int ace_lz77_calc_huff_tabs(ace_decompress_ctx_t *ctx);
+int ace_lz77_read_symbols(ace_decompress_ctx_t *ctx);
+void ace_lz77_block_core(ace_decompress_ctx_t *ctx);
+int ace_lz77_block(ace_decompress_ctx_t *ctx, uint8_t *buf, int len);
+void ace_lz77_copy_to_dict(ace_decompress_ctx_t *ctx, const uint8_t *buf, int len);
+
+/* sound.c */
+int ace_sound_block(ace_decompress_ctx_t *ctx, uint8_t *buf, int len);
+void ace_sound_init(ace_decompress_ctx_t *ctx, int type);
+
+/* pic.c */
+int ace_pic_block(ace_decompress_ctx_t *ctx, uint8_t *buf, int len);
+void ace_pic_init(ace_decompress_ctx_t *ctx);
+void ace_pic_done(ace_decompress_ctx_t *ctx);
+
+/* v2.c */
+int ace_decompress_v20_block(ace_decompress_ctx_t *ctx, uint8_t *buf, int len);
+int ace_lz77_preprocess_block(ace_decompress_ctx_t *ctx, uint8_t *buf, int len);
+
+/* ace.c (init/bitwidth) */
+void ace_init_bitwidth(ace_decompress_ctx_t *ctx);
+int ace_get_bit_width(ace_decompress_ctx_t *ctx, int value);
+
+#endif /* AARU_ACE_INTERNAL_H */
diff --git a/ace/bitstream.c b/ace/bitstream.c
new file mode 100644
index 0000000..8fca575
--- /dev/null
+++ b/ace/bitstream.c
@@ -0,0 +1,108 @@
+/*
+ * 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 .
+ */
+
+#include
+
+#include "ace.h"
+#include "ace_internal.h"
+
+void ace_fill_read_buf(ace_decompress_ctx_t *ctx)
+{
+ uint32_t i;
+ size_t bytes_to_read;
+ size_t words_to_read;
+
+ ctx->read_buf[0] = ctx->read_buf[ACE_READ_BUF_SIZE - 2];
+ ctx->read_buf[1] = ctx->read_buf[ACE_READ_BUF_SIZE - 1];
+ ctx->read_buf_pos -= ACE_READ_BUF_SIZE - 2;
+
+ bytes_to_read = (ACE_READ_BUF_SIZE - 2) * 4;
+ if(bytes_to_read > ctx->in_len - ctx->in_pos) bytes_to_read = ctx->in_len - ctx->in_pos;
+
+ words_to_read = bytes_to_read / 4;
+
+ for(i = 0; i < words_to_read; i++)
+ {
+ size_t off = ctx->in_pos + i * 4;
+ ctx->read_buf[2 + i] = (uint32_t)ctx->in_buf[off] | ((uint32_t)ctx->in_buf[off + 1] << 8) |
+ ((uint32_t)ctx->in_buf[off + 2] << 16) | ((uint32_t)ctx->in_buf[off + 3] << 24);
+ }
+
+ if(bytes_to_read > words_to_read * 4)
+ {
+ uint32_t word = 0;
+ size_t off = ctx->in_pos + words_to_read * 4;
+ size_t rem = bytes_to_read - words_to_read * 4;
+ size_t b;
+ for(b = 0; b < rem; b++) word |= (uint32_t)ctx->in_buf[off + b] << (b * 8);
+ ctx->read_buf[2 + words_to_read] = word;
+ }
+
+ ctx->in_pos += bytes_to_read;
+}
+
+void ace_add_bits(ace_decompress_ctx_t *ctx, int bits)
+{
+ uint32_t b0, b1;
+
+ ctx->read_buf_pos += (ctx->read_code_bit_pos += bits) >> 5;
+ ctx->read_code_bit_pos &= 31;
+
+ if(ctx->read_buf_pos == ACE_READ_BUF_SIZE - 2) ace_fill_read_buf(ctx);
+
+ b0 = ctx->read_buf[ctx->read_buf_pos];
+ b1 = ctx->read_buf[ctx->read_buf_pos + 1];
+
+ ctx->read_code = (b0 << ctx->read_code_bit_pos) +
+ ((b1 >> (32 - ctx->read_code_bit_pos)) & ((uint32_t)(!ctx->read_code_bit_pos) - 1));
+}
+
+void ace_init_read_buf(ace_decompress_ctx_t *ctx)
+{
+ size_t bytes_to_read;
+ size_t words_to_read;
+ size_t i;
+
+ bytes_to_read = ACE_READ_BUF_SIZE * 4;
+ if(bytes_to_read > ctx->in_len - ctx->in_pos) bytes_to_read = ctx->in_len - ctx->in_pos;
+
+ words_to_read = bytes_to_read / 4;
+
+ for(i = 0; i < words_to_read; i++)
+ {
+ size_t off = ctx->in_pos + i * 4;
+ ctx->read_buf[i] = (uint32_t)ctx->in_buf[off] | ((uint32_t)ctx->in_buf[off + 1] << 8) |
+ ((uint32_t)ctx->in_buf[off + 2] << 16) | ((uint32_t)ctx->in_buf[off + 3] << 24);
+ }
+
+ if(bytes_to_read > words_to_read * 4)
+ {
+ uint32_t word = 0;
+ size_t off = ctx->in_pos + words_to_read * 4;
+ size_t rem = bytes_to_read - words_to_read * 4;
+ size_t b;
+ for(b = 0; b < rem; b++) word |= (uint32_t)ctx->in_buf[off + b] << (b * 8);
+ ctx->read_buf[words_to_read] = word;
+ }
+
+ ctx->in_pos += bytes_to_read;
+
+ ctx->read_code = ctx->read_buf[0];
+ ctx->read_code_bit_pos = 0;
+ ctx->read_buf_pos = 0;
+}
diff --git a/ace/huffman.c b/ace/huffman.c
new file mode 100644
index 0000000..d753ff7
--- /dev/null
+++ b/ace/huffman.c
@@ -0,0 +1,176 @@
+/*
+ * 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 .
+ */
+
+#include
+
+#include "ace.h"
+#include "ace_internal.h"
+
+static void ace_qsort_xchg(uint16_t *a, uint16_t *b)
+{
+ uint16_t tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+static void ace_qsort_range(ace_decompress_ctx_t *ctx, int left, int right)
+{
+ int new_left, new_right;
+ uint16_t hyphen;
+
+ new_left = left;
+ new_right = right;
+ hyphen = ctx->sort_frequencies[right];
+
+ do
+ {
+ while(ctx->sort_frequencies[new_left] > hyphen) new_left++;
+ while(ctx->sort_frequencies[new_right] < hyphen) new_right--;
+
+ if(new_left <= new_right)
+ {
+ ace_qsort_xchg(&ctx->sort_frequencies[new_left], &ctx->sort_frequencies[new_right]);
+ ace_qsort_xchg(&ctx->sort_elements[new_left], &ctx->sort_elements[new_right]);
+ new_left++;
+ new_right--;
+ }
+ } while(new_left < new_right);
+
+ if(left < new_right)
+ {
+ if(left < new_right - 1)
+ ace_qsort_range(ctx, left, new_right);
+ else if(ctx->sort_frequencies[left] < ctx->sort_frequencies[new_right])
+ {
+ ace_qsort_xchg(&ctx->sort_frequencies[left], &ctx->sort_frequencies[new_right]);
+ ace_qsort_xchg(&ctx->sort_elements[left], &ctx->sort_elements[new_right]);
+ }
+ }
+
+ if(right > new_left)
+ {
+ if(new_left < right - 1)
+ ace_qsort_range(ctx, new_left, right);
+ else if(ctx->sort_frequencies[new_left] < ctx->sort_frequencies[right])
+ {
+ ace_qsort_xchg(&ctx->sort_frequencies[new_left], &ctx->sort_frequencies[right]);
+ ace_qsort_xchg(&ctx->sort_elements[new_left], &ctx->sort_elements[right]);
+ }
+ }
+}
+
+static void ace_quicksort(ace_decompress_ctx_t *ctx, int n)
+{
+ int i;
+ for(i = n + 1; i--;) ctx->sort_elements[i] = (uint16_t)i;
+ ace_qsort_range(ctx, 0, n);
+}
+
+static void ace_memset16(uint16_t *buf, uint16_t val, int count)
+{
+ while(count--) *buf++ = val;
+}
+
+int ace_make_codes(ace_decompress_ctx_t *ctx, unsigned max_width, unsigned tab_size, uint16_t *widths, uint16_t *codes)
+{
+ unsigned num_codes, code, code_pos, i, max_code_pos, actual_size;
+
+ memcpy(ctx->sort_frequencies, widths, (tab_size + 1) * sizeof(uint16_t));
+
+ if(tab_size)
+ ace_quicksort(ctx, (int)tab_size);
+ else
+ ctx->sort_elements[0] = 0;
+
+ ctx->sort_frequencies[tab_size + 1] = actual_size = code_pos = 0;
+
+ while(ctx->sort_frequencies[actual_size]) actual_size++;
+
+ if(actual_size < 2)
+ {
+ i = ctx->sort_elements[0];
+ widths[i] = 1;
+ actual_size += (actual_size == 0);
+ }
+ actual_size--;
+
+ max_code_pos = 1u << max_width;
+ for(i = actual_size + 1; i-- && code_pos < max_code_pos;)
+ {
+ num_codes = 1u << (max_width - ctx->sort_frequencies[i]);
+ code = ctx->sort_elements[i];
+ if(code_pos + num_codes > max_code_pos) return 0;
+ ace_memset16(&codes[code_pos], (uint16_t)code, (int)num_codes);
+ code_pos += num_codes;
+ }
+
+ return 1;
+}
+
+int ace_read_widths(ace_decompress_ctx_t *ctx, unsigned max_width, uint16_t *codes, uint16_t *widths, unsigned max_size)
+{
+ unsigned code, i, width_pos, num_widths, len, upper_width, lower_width;
+
+ memset(widths, 0, max_size * sizeof(uint16_t));
+ memset(codes, 0, (1u << max_width) * sizeof(uint16_t));
+
+ num_widths = ctx->read_code >> (32 - 9);
+ ace_add_bits(ctx, 9);
+ if(num_widths > max_size) num_widths = max_size;
+
+ lower_width = ctx->read_code >> (32 - 4);
+ ace_add_bits(ctx, 4);
+ upper_width = ctx->read_code >> (32 - 4);
+ ace_add_bits(ctx, 4);
+
+ for(i = 0; i <= upper_width; i++)
+ {
+ ctx->save_widths[i] = ctx->read_code >> (32 - 3);
+ ace_add_bits(ctx, 3);
+ }
+
+ if(!ace_make_codes(ctx, ACE_MAX_SAVE_WIDTH, upper_width, ctx->save_widths, codes)) return 0;
+
+ width_pos = 0;
+ while(width_pos <= num_widths)
+ {
+ code = codes[ctx->read_code >> (32 - ACE_MAX_SAVE_WIDTH)];
+ ace_add_bits(ctx, ctx->save_widths[code]);
+
+ if(code < upper_width)
+ widths[width_pos++] = (uint16_t)code;
+ else
+ {
+ len = (ctx->read_code >> 28) + 4;
+ ace_add_bits(ctx, 4);
+ while(len-- && width_pos <= num_widths) widths[width_pos++] = 0;
+ }
+ }
+
+ if(upper_width)
+ {
+ for(i = 1; i <= num_widths; i++) widths[i] = (widths[i] + widths[i - 1]) % upper_width;
+ }
+
+ for(i = 0; i <= num_widths; i++)
+ {
+ if(widths[i]) widths[i] += (uint16_t)lower_width;
+ }
+
+ return ace_make_codes(ctx, max_width, num_widths, widths, codes);
+}
diff --git a/ace/lz77.c b/ace/lz77.c
new file mode 100644
index 0000000..d12ed61
--- /dev/null
+++ b/ace/lz77.c
@@ -0,0 +1,260 @@
+/*
+ * 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 .
+ */
+
+#include
+
+#include "ace.h"
+#include "ace_internal.h"
+
+void ace_lz77_write_char(ace_decompress_ctx_t *ctx, uint8_t ch)
+{
+ ctx->dictionary[ctx->dic_pos] = ch;
+ ctx->dic_pos = (ctx->dic_pos + 1) & ctx->dic_and;
+ ctx->block_byte_count++;
+}
+
+void ace_lz77_copy_string(ace_decompress_ctx_t *ctx, uint32_t dist, int len)
+{
+ uint32_t src_pos;
+ int i;
+
+ src_pos = (ctx->dic_pos - dist) & ctx->dic_and;
+
+ if(src_pos >= ctx->dic_size - ACE_MAX_LEN || ctx->dic_pos >= ctx->dic_size - ACE_MAX_LEN)
+ {
+ for(i = len; i--;)
+ {
+ ctx->dictionary[ctx->dic_pos] = ctx->dictionary[src_pos];
+ ctx->dic_pos = (ctx->dic_pos + 1) & ctx->dic_and;
+ src_pos = (src_pos + 1) & ctx->dic_and;
+ }
+ }
+ else
+ {
+ for(i = 0; i < len; i++) ctx->dictionary[ctx->dic_pos + i] = ctx->dictionary[src_pos + i];
+ ctx->dic_pos = (ctx->dic_pos + len) & ctx->dic_and;
+ }
+
+ ctx->block_byte_count += len;
+}
+
+int ace_lz77_calc_huff_tabs(ace_decompress_ctx_t *ctx)
+{
+ if(!ace_read_widths(ctx, ACE_MAX_CODE_WIDTH, ctx->main_huff_symbols, ctx->main_huff_widths, ACE_MAX_MAIN_CODE) ||
+ !ace_read_widths(ctx, ACE_MAX_CODE_WIDTH, ctx->len_huff_symbols, ctx->len_huff_widths, ACE_MAX_LEN_CODE))
+ return 0;
+
+ ctx->block_size = ctx->read_code >> (32 - 15);
+ ace_add_bits(ctx, 15);
+
+ return 1;
+}
+
+int ace_lz77_read_symbols(ace_decompress_ctx_t *ctx)
+{
+ int symbol_count, match_count, symbol, type;
+
+ if(!ctx->block_size && !ace_lz77_calc_huff_tabs(ctx)) return 0;
+
+ ctx->part_size = ACE_MAX_PART_SIZE > ctx->block_size ? ctx->block_size : ACE_MAX_PART_SIZE;
+ ctx->block_size -= ctx->part_size;
+
+ match_count = 0;
+
+ for(symbol_count = 0; (uint32_t)symbol_count < ctx->part_size; symbol_count++)
+ {
+ symbol = ctx->main_huff_symbols[ctx->read_code >> (32 - ACE_MAX_CODE_WIDTH)];
+ ace_add_bits(ctx, ctx->main_huff_widths[symbol]);
+ ctx->main_buf[symbol_count] = (uint16_t)symbol;
+
+ if(symbol > 255)
+ {
+ if(symbol == ACE_TYPE_CODE)
+ {
+ type = ctx->read_code >> 24;
+ ctx->len_buf[match_count] = (uint16_t)type;
+ ace_add_bits(ctx, 8);
+
+ switch(type)
+ {
+ case ACE_BLOCK_LZ77_DELTA:
+ ctx->dist_buf[match_count] = ctx->read_code >> (32 - 25);
+ ace_add_bits(ctx, 25);
+ break;
+ case ACE_BLOCK_LZ77_EXE:
+ ctx->dist_buf[match_count] = ctx->read_code >> (32 - 8);
+ ace_add_bits(ctx, 8);
+ break;
+ default:
+ break;
+ }
+
+ match_count++;
+ }
+ else
+ {
+ if(symbol > 259)
+ {
+ if((symbol -= 260) > 1)
+ {
+ ctx->dist_buf[match_count] = (ctx->read_code >> (33 - symbol)) + (1u << (symbol - 1));
+ ace_add_bits(ctx, symbol - 1);
+ }
+ else
+ {
+ ctx->dist_buf[match_count] = (uint32_t)symbol;
+ }
+ }
+
+ ctx->len_buf[match_count] = ctx->len_huff_symbols[ctx->read_code >> (32 - ACE_MAX_CODE_WIDTH)];
+ ace_add_bits(ctx, ctx->len_huff_widths[ctx->len_buf[match_count]]);
+ match_count++;
+ }
+ }
+ }
+
+ ctx->len_dist_buf_pos = ctx->main_buf_pos = 0;
+ return 1;
+}
+
+void ace_lz77_block_core(ace_decompress_ctx_t *ctx)
+{
+ int symbol, len_symbol, i;
+ uint32_t match_dist;
+
+ while(ctx->block_byte_count < ctx->block_buf_size)
+ {
+ if(ctx->main_buf_pos == ctx->part_size)
+ {
+ if(!ace_lz77_read_symbols(ctx)) return;
+ }
+
+ symbol = ctx->main_buf[ctx->main_buf_pos++];
+
+ if(symbol > 255)
+ {
+ if(symbol == ACE_TYPE_CODE)
+ {
+ ctx->next_type = ctx->len_buf[ctx->len_dist_buf_pos] & 255;
+
+ switch(ctx->next_type)
+ {
+ case ACE_BLOCK_LZ77_DELTA:
+ ctx->next_delta_dist = ctx->dist_buf[ctx->len_dist_buf_pos] >> 17;
+ ctx->next_delta_len = ctx->dist_buf[ctx->len_dist_buf_pos] & 0x1FFFF;
+ break;
+ case ACE_BLOCK_LZ77_EXE:
+ ctx->next_exe_mode = ctx->dist_buf[ctx->len_dist_buf_pos];
+ break;
+ default:
+ break;
+ }
+
+ ctx->len_dist_buf_pos++;
+ break;
+ }
+
+ if(symbol > 259)
+ {
+ symbol -= 260;
+ match_dist = ctx->dist_buf[ctx->len_dist_buf_pos];
+
+ ctx->old_dists_pos = (ctx->old_dists_pos + 1) & 3;
+ ctx->old_dists[ctx->old_dists_pos] = match_dist;
+
+ i = 2;
+ if(match_dist > ACE_MAX_DIST_AT_LEN2)
+ {
+ i++;
+ if(match_dist > ACE_MAX_DIST_AT_LEN3) i++;
+ }
+ }
+ else
+ {
+ match_dist = ctx->old_dists[(ctx->old_dists_pos - (symbol &= 255)) & 3];
+
+ for(i = symbol; i >= 0; i--)
+ ctx->old_dists[(ctx->old_dists_pos - i) & 3] = ctx->old_dists[(ctx->old_dists_pos - i + 1) & 3];
+
+ ctx->old_dists[ctx->old_dists_pos] = match_dist;
+ i = symbol > 1 ? 3 : 2;
+ }
+
+ len_symbol = ctx->len_buf[ctx->len_dist_buf_pos++] + i;
+ match_dist++;
+ ace_lz77_copy_string(ctx, match_dist, len_symbol);
+ }
+ else
+ {
+ ace_lz77_write_char(ctx, (uint8_t)symbol);
+ }
+ }
+}
+
+int ace_lz77_block(ace_decompress_ctx_t *ctx, uint8_t *buf, int len)
+{
+ int old_pos, i;
+
+ old_pos = ctx->dic_pos;
+ ctx->block_byte_count = 0;
+
+ if(len < ACE_MAX_LEN)
+ {
+ if(ctx->file_size <= 0 || (int64_t)len < ctx->file_size) return 0;
+ ctx->block_buf_size = (uint32_t)ctx->file_size;
+ }
+ else
+ {
+ ctx->block_buf_size = len - ACE_MAX_LEN;
+ }
+
+ if((int64_t)ctx->block_buf_size > ctx->file_size) ctx->block_buf_size = (uint32_t)ctx->file_size;
+ if(ctx->block_buf_size > ctx->dic_size - ACE_MAX_LEN) ctx->block_buf_size = ctx->dic_size - ACE_MAX_LEN;
+
+ if(ctx->file_size > 0 && ctx->block_buf_size)
+ {
+ ace_lz77_block_core(ctx);
+
+ if(ctx->block_byte_count <= (uint32_t)len)
+ {
+ if((uint32_t)old_pos + ctx->block_byte_count > ctx->dic_size)
+ {
+ i = ctx->dic_size - old_pos;
+ memcpy(buf, &ctx->dictionary[old_pos], i);
+ memcpy(&buf[i], ctx->dictionary, ctx->block_byte_count - i);
+ }
+ else
+ {
+ memcpy(buf, &ctx->dictionary[old_pos], ctx->block_byte_count);
+ }
+ }
+ }
+
+ ctx->file_size -= ctx->block_byte_count;
+ return (int)ctx->block_byte_count;
+}
+
+void ace_lz77_copy_to_dict(ace_decompress_ctx_t *ctx, const uint8_t *buf, int len)
+{
+ int i;
+ for(i = 0; i < len; i++)
+ {
+ ctx->dictionary[ctx->dic_pos] = buf[i];
+ ctx->dic_pos = (ctx->dic_pos + 1) & ctx->dic_and;
+ }
+}
diff --git a/ace/pic.c b/ace/pic.c
new file mode 100644
index 0000000..0ce3c11
--- /dev/null
+++ b/ace/pic.c
@@ -0,0 +1,378 @@
+/*
+ * 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 .
+ */
+
+#include
+#include
+
+#include "ace.h"
+#include "ace_internal.h"
+
+static int ace_pic_golomb_rice(ace_decompress_ctx_t *ctx, int k)
+{
+ int i, err;
+
+ err = k ? (int)(ctx->read_code >> (32 - k)) : 0;
+ ace_add_bits(ctx, k);
+
+ do
+ {
+ i = ctx->read_code >> 31;
+ if(i) err += 1 << k;
+ ace_add_bits(ctx, 1);
+ } while(i);
+
+ return err;
+}
+
+static int ace_pic_get_context(ace_decompress_ctx_t *ctx)
+{
+ return ACE_PIC_QUANT_X9X9(ctx, ctx->pic_pixel_d - ctx->pic_pixel_a) +
+ ACE_PIC_QUANT_X9(ctx, ctx->pic_pixel_a - ctx->pic_pixel_c) +
+ ACE_PIC_QUANT(ctx, ctx->pic_pixel_c - ctx->pic_pixel_b);
+}
+
+static void ace_pic_init_model(ace_decompress_ctx_t *ctx)
+{
+ int pred, i;
+ for(pred = 0; pred <= 1; pred++)
+ {
+ memset(&ctx->pic_context[pred], 0, sizeof(ctx->pic_context[0]));
+ for(i = 0; i < ACE_PIC_CONTEXT_NUMBER; i++) ctx->pic_context[pred][i].average_counter = 4;
+ }
+}
+
+static void ace_pic_init_quantizers(ace_decompress_ctx_t *ctx)
+{
+ int i;
+ for(i = -255; i <= 255; i++)
+ {
+ if(i <= -ACE_PIC_S3)
+ ACE_PIC_QUANT(ctx, i) = -4;
+ else if(i <= -ACE_PIC_S2)
+ ACE_PIC_QUANT(ctx, i) = -3;
+ else if(i <= -ACE_PIC_S1)
+ ACE_PIC_QUANT(ctx, i) = -2;
+ else if(i <= -1)
+ ACE_PIC_QUANT(ctx, i) = -1;
+ else if(!i)
+ ACE_PIC_QUANT(ctx, i) = 0;
+ else if(i < ACE_PIC_S1)
+ ACE_PIC_QUANT(ctx, i) = 1;
+ else if(i < ACE_PIC_S2)
+ ACE_PIC_QUANT(ctx, i) = 2;
+ else if(i < ACE_PIC_S3)
+ ACE_PIC_QUANT(ctx, i) = 3;
+ else
+ ACE_PIC_QUANT(ctx, i) = 4;
+ }
+
+ for(i = -255; i <= 255; i++) ACE_PIC_QUANT_X9(ctx, i) = 9 * ACE_PIC_QUANT(ctx, i);
+ for(i = -255; i <= 255; i++) ACE_PIC_QUANT_X9X9(ctx, i) = 9 * ACE_PIC_QUANT_X9(ctx, i);
+}
+
+static void ace_pic_set_pixels1(ace_decompress_ctx_t *ctx)
+{
+ ctx->pic_pixel_d = ctx->pic_data[1][ctx->pic_cur_plane];
+ ctx->pic_pixel_a = ctx->pic_pixel_b = ctx->pic_pixel_c = ctx->pic_pixel_x = 0;
+
+ if(ctx->pic_cur_pred == 1)
+ {
+ ctx->pic_pixel_a = ctx->pic_pixel_b = ctx->pic_pixel_c = ctx->pic_pixel_x = 128;
+ if(ctx->pic_cur_plane > 0) ctx->pic_pixel_d -= ctx->pic_data[1][ctx->pic_cur_plane - 1] - 128;
+ }
+ else if(ctx->pic_cur_pred == 2)
+ {
+ ctx->pic_pixel_a = ctx->pic_pixel_b = ctx->pic_pixel_c = ctx->pic_pixel_x = 128;
+ if(ctx->pic_cur_plane > 0) ctx->pic_pixel_d -= (ctx->pic_data[1][ctx->pic_cur_plane - 1] * 11 >> 4) - 128;
+ }
+}
+
+static void ace_pic_set_pixels2(ace_decompress_ctx_t *ctx)
+{
+ ctx->pic_pixel_c = ctx->pic_pixel_a;
+ ctx->pic_pixel_a = ctx->pic_pixel_d;
+ ctx->pic_pixel_b = ctx->pic_pixel_x;
+}
+
+static void ace_pic_set_pixels3(ace_decompress_ctx_t *ctx)
+{
+ ctx->pic_pixel_a = ctx->pic_data[1][ctx->pic_cur_col];
+ ctx->pic_pixel_b = ctx->pic_data[0][ctx->pic_cur_col - ctx->pic_planes];
+ ctx->pic_pixel_c = ctx->pic_data[1][ctx->pic_cur_col - ctx->pic_planes];
+
+ if(ctx->pic_cur_pred == 1)
+ {
+ ctx->pic_pixel_a -= ctx->pic_data[1][ctx->pic_cur_col - 1] - 128;
+ ctx->pic_pixel_b -= ctx->pic_data[0][ctx->pic_cur_col - ctx->pic_planes - 1] - 128;
+ ctx->pic_pixel_c -= ctx->pic_data[1][ctx->pic_cur_col - ctx->pic_planes - 1] - 128;
+ }
+ else if(ctx->pic_cur_pred == 2)
+ {
+ ctx->pic_pixel_a -= (ctx->pic_data[1][ctx->pic_cur_col - 1] * 11 >> 4) - 128;
+ ctx->pic_pixel_b -= (ctx->pic_data[0][ctx->pic_cur_col - ctx->pic_planes - 1] * 11 >> 4) - 128;
+ ctx->pic_pixel_c -= (ctx->pic_data[1][ctx->pic_cur_col - ctx->pic_planes - 1] * 11 >> 4) - 128;
+ }
+}
+
+static void ace_pic_set_pixel_state(ace_decompress_ctx_t *ctx)
+{
+ ctx->pic_pixel_d = ctx->pic_data[1][ctx->pic_cur_col + ctx->pic_planes];
+
+ if(ctx->pic_cur_pred == 1)
+ ctx->pic_pixel_d -= ctx->pic_data[1][ctx->pic_cur_col + ctx->pic_planes - 1] - 128;
+ else if(ctx->pic_cur_pred == 2)
+ ctx->pic_pixel_d -= (ctx->pic_data[1][ctx->pic_cur_col + ctx->pic_planes - 1] * 11 >> 4) - 128;
+
+ ctx->pic_cur_state = abs(ace_pic_get_context(ctx));
+}
+
+static void ace_pic_pixel(ace_decompress_ctx_t *ctx)
+{
+ int k, best_predictor, best_error_count;
+ uint8_t m_epsilon, predicted;
+ int8_t epsilon;
+
+ ctx->cur_context[ctx->pic_cur_state].used_counter++;
+
+ k = ctx->bit_width_array[ctx->cur_context[ctx->pic_cur_state].average_counter /
+ ctx->cur_context[ctx->pic_cur_state].used_counter];
+
+ m_epsilon = (uint8_t)ace_pic_golomb_rice(ctx, k);
+
+ if(m_epsilon & 1)
+ epsilon = -(int8_t)(m_epsilon / 2) - 1;
+ else
+ epsilon = (int8_t)(m_epsilon / 2);
+
+ switch(ctx->cur_context[ctx->pic_cur_state].predictor_number)
+ {
+ case 0:
+ predicted = (uint8_t)ctx->pic_pixel_a;
+ break;
+ case 1:
+ predicted = (uint8_t)ctx->pic_pixel_b;
+ break;
+ case 2:
+ predicted = (uint8_t)((ctx->pic_pixel_a + ctx->pic_pixel_b) >> 1);
+ break;
+ case 3:
+ predicted = (uint8_t)(ctx->pic_pixel_a + ctx->pic_pixel_b - ctx->pic_pixel_c);
+ break;
+ default:
+ predicted = 0;
+ }
+
+ ctx->pic_pixel_x = (uint8_t)(epsilon + predicted);
+
+ ctx->cur_context[ctx->pic_cur_state].error_counters[0] +=
+ ctx->dif_bit_width_array[(uint8_t)(ctx->pic_pixel_x - ctx->pic_pixel_a)];
+
+ best_predictor = 0;
+ best_error_count = ctx->cur_context[ctx->pic_cur_state].error_counters[0];
+
+ ctx->cur_context[ctx->pic_cur_state].error_counters[1] +=
+ ctx->dif_bit_width_array[(uint8_t)(ctx->pic_pixel_x - ctx->pic_pixel_b)];
+
+ if(ctx->cur_context[ctx->pic_cur_state].error_counters[1] < best_error_count)
+ {
+ best_predictor = 1;
+ best_error_count = ctx->cur_context[ctx->pic_cur_state].error_counters[1];
+ }
+
+ ctx->cur_context[ctx->pic_cur_state].error_counters[2] +=
+ ctx->dif_bit_width_array[(uint8_t)(ctx->pic_pixel_x - ((ctx->pic_pixel_a + ctx->pic_pixel_b) >> 1))];
+
+ if(ctx->cur_context[ctx->pic_cur_state].error_counters[2] < best_error_count)
+ {
+ best_predictor = 2;
+ best_error_count = ctx->cur_context[ctx->pic_cur_state].error_counters[2];
+ }
+
+ ctx->cur_context[ctx->pic_cur_state].error_counters[3] += ctx->dif_bit_width_array[(
+ uint8_t)(ctx->pic_pixel_x - (ctx->pic_pixel_a + ctx->pic_pixel_b - ctx->pic_pixel_c))];
+
+ if(ctx->cur_context[ctx->pic_cur_state].error_counters[3] < best_error_count) best_predictor = 3;
+
+ {
+ uint32_t *ec = (uint32_t *)&ctx->cur_context[ctx->pic_cur_state].error_counters;
+ if(*ec & 0x80808080u) *ec = (*ec >> 1) & 0x7f7f7f7fu;
+ }
+
+ ctx->cur_context[ctx->pic_cur_state].predictor_number = (uint8_t)best_predictor;
+ ctx->cur_context[ctx->pic_cur_state].average_counter += (uint16_t)abs(epsilon);
+
+ if(ctx->cur_context[ctx->pic_cur_state].used_counter == ACE_PIC_N0)
+ {
+ ctx->cur_context[ctx->pic_cur_state].used_counter >>= 1;
+ ctx->cur_context[ctx->pic_cur_state].average_counter >>= 1;
+ }
+
+ switch(ctx->pic_cur_pred)
+ {
+ case 0:
+ ctx->pic_data[0][ctx->pic_cur_col] = (int8_t)ctx->pic_pixel_x;
+ break;
+ case 1:
+ ctx->pic_data[0][ctx->pic_cur_col] =
+ (int8_t)(ctx->pic_pixel_x + ctx->pic_data[0][ctx->pic_cur_col - 1] - 128);
+ break;
+ case 2:
+ ctx->pic_data[0][ctx->pic_cur_col] =
+ (int8_t)(ctx->pic_pixel_x + (ctx->pic_data[0][ctx->pic_cur_col - 1] * 11 >> 4) - 128);
+ break;
+ }
+}
+
+static void ace_pic_symbol(ace_decompress_ctx_t *ctx)
+{
+ ace_pic_set_pixels2(ctx);
+ ace_pic_set_pixels3(ctx);
+ ace_pic_set_pixel_state(ctx);
+ ace_pic_pixel(ctx);
+}
+
+static void ace_pic_line(ace_decompress_ctx_t *ctx)
+{
+ int8_t *temp;
+
+ for(ctx->pic_cur_plane = 0; ctx->pic_cur_plane < ctx->pic_planes; ctx->pic_cur_plane++)
+ {
+ if(ctx->pic_cur_plane)
+ {
+ ctx->cur_context = ctx->pic_context[1];
+ ctx->pic_cur_pred = ctx->read_code >> (32 - 2);
+ ace_add_bits(ctx, 2);
+ }
+ else
+ {
+ ctx->cur_context = ctx->pic_context[0];
+ ctx->pic_cur_pred = 0;
+ }
+
+ ace_pic_set_pixels1(ctx);
+
+ for(ctx->pic_cur_col = ctx->pic_cur_plane; ctx->pic_cur_col < ctx->pic_width;
+ ctx->pic_cur_col += ctx->pic_planes)
+ {
+ ace_pic_symbol(ctx);
+ }
+ }
+
+ ctx->pic_data_pos = ctx->pic_width;
+ temp = ctx->pic_data[0];
+ ctx->pic_data[0] = ctx->pic_data[1];
+ ctx->pic_data[1] = temp;
+}
+
+void ace_pic_done(ace_decompress_ctx_t *ctx)
+{
+ if(ctx->pic_data[0])
+ {
+ free(ctx->pic_data[0] - ctx->pic_planes);
+ free(ctx->pic_data[1] - ctx->pic_planes);
+ ctx->pic_data[0] = NULL;
+ ctx->pic_data[1] = NULL;
+ }
+}
+
+void ace_pic_init(ace_decompress_ctx_t *ctx)
+{
+ int i, j;
+ int8_t *buf;
+
+ ace_pic_done(ctx);
+
+ ctx->pic_width = ace_pic_golomb_rice(ctx, 12);
+ ctx->pic_planes = ace_pic_golomb_rice(ctx, 2);
+
+ i = ctx->pic_width + 2 * ctx->pic_planes;
+
+ for(j = 0; j <= 1; j++)
+ {
+ buf = (int8_t *)malloc(i);
+ if(!buf)
+ {
+ ctx->error = 1;
+ return;
+ }
+ memset(buf, 0, i);
+ ctx->pic_data[j] = buf + ctx->pic_planes;
+ }
+
+ ace_pic_init_model(ctx);
+ ace_pic_init_quantizers(ctx);
+ ctx->pic_data_pos = 0;
+}
+
+int ace_pic_block(ace_decompress_ctx_t *ctx, uint8_t *buf, int len)
+{
+ int i, rest_len;
+
+ rest_len = len;
+
+ while(rest_len)
+ {
+ if(!ctx->pic_data_pos)
+ {
+ if(ctx->file_size)
+ {
+ i = ctx->read_code >> (32 - 1);
+ ace_add_bits(ctx, 1);
+
+ if(!i)
+ {
+ ctx->next_type = ctx->read_code >> (32 - 8);
+ ace_add_bits(ctx, 8);
+
+ switch(ctx->next_type)
+ {
+ case ACE_BLOCK_LZ77_DELTA:
+ ctx->next_delta_dist = ctx->read_code >> (32 - 8);
+ ace_add_bits(ctx, 8);
+ ctx->next_delta_len = ctx->read_code >> (32 - 17);
+ ace_add_bits(ctx, 17);
+ break;
+ case ACE_BLOCK_LZ77_EXE:
+ ctx->next_exe_mode = ctx->read_code >> (32 - 8);
+ ace_add_bits(ctx, 8);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+
+ ace_pic_line(ctx);
+ }
+
+ i = rest_len > ctx->pic_data_pos ? ctx->pic_data_pos : rest_len;
+ memcpy(buf, &ctx->pic_data[1][ctx->pic_width - ctx->pic_data_pos], i);
+
+ buf += i;
+ rest_len -= i;
+ ctx->pic_data_pos -= i;
+ ctx->file_size -= i;
+ }
+
+ return len - rest_len;
+}
diff --git a/ace/sound.c b/ace/sound.c
new file mode 100644
index 0000000..5039f54
--- /dev/null
+++ b/ace/sound.c
@@ -0,0 +1,250 @@
+/*
+ * 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 .
+ */
+
+#include
+#include
+
+#include "ace.h"
+#include "ace_internal.h"
+
+const int ace_sound_channel_num[4][4] = {
+ {0, 0, 0, 0},
+ {0, 1, 0, 1},
+ {0, 1, 2, 0},
+ {0, 1, 2, 0}
+};
+
+const int ace_sound_models[4] = {3, 6, 9, 6};
+
+static uint8_t ace_sound_get_predicted(ace_decompress_ctx_t *ctx, int ch)
+{
+ return (uint8_t)((8 * ctx->sound_var.last_byte[ch] +
+ ctx->sound_var.rar_coefficient[ch][0] * ctx->sound_var.rar_dif_cnt[ch][0] +
+ ctx->sound_var.rar_coefficient[ch][1] * ctx->sound_var.rar_dif_cnt[ch][1] +
+ ctx->sound_var.rar_coefficient[ch][2] * ctx->sound_var.rar_dif_cnt[ch][2] +
+ ctx->sound_var.rar_coefficient[ch][3] * ctx->sound_var.rar_dif_cnt[ch][3]) >>
+ 3);
+}
+
+static int ace_sound_calc_tabs(ace_decompress_ctx_t *ctx)
+{
+ int i;
+ for(i = 0; i < ctx->sound_var.models; i++)
+ {
+ ace_read_widths(ctx, ACE_SOUND_MAX_CODE_WIDTH, ctx->sound_huff_symbols[i], ctx->sound_huff_widths[i],
+ ACE_SOUND_MAX_CODE);
+ }
+ ctx->sound_var.sound_block_size = ctx->read_code >> (32 - 15);
+ ace_add_bits(ctx, 15);
+ return 1;
+}
+
+static int ace_sound_get_symbol(ace_decompress_ctx_t *ctx, int model, int channel)
+{
+ int symbol;
+
+ if(!ctx->sound_var.sound_block_size) ace_sound_calc_tabs(ctx);
+
+ model <<= 1;
+ if(!model) model += ctx->sound_var.adaptive_model_use[channel];
+ model += 3 * channel;
+
+ symbol = ctx->sound_huff_symbols[model][ctx->read_code >> (32 - ACE_SOUND_MAX_CODE_WIDTH)];
+ ace_add_bits(ctx, ctx->sound_huff_widths[model][symbol]);
+ ctx->sound_var.sound_block_size--;
+
+ return symbol;
+}
+
+static int ace_sound_get(ace_decompress_ctx_t *ctx, int channel)
+{
+ int err;
+
+ if(ctx->sound_var.state[channel] != 2)
+ {
+ ctx->sound_var.code[channel] = ace_sound_get_symbol(ctx, ctx->sound_var.state[channel], channel);
+
+ if(ctx->sound_var.code[channel] == ACE_SOUND_TYPE_CODE)
+ {
+ ctx->next_type = ctx->read_code >> (32 - 8);
+ ace_add_bits(ctx, 8);
+
+ switch(ctx->next_type)
+ {
+ case ACE_BLOCK_LZ77_DELTA:
+ ctx->next_delta_dist = ctx->read_code >> (32 - 8);
+ ace_add_bits(ctx, 8);
+ ctx->next_delta_len = ctx->read_code >> (32 - 17);
+ ace_add_bits(ctx, 17);
+ break;
+ case ACE_BLOCK_LZ77_EXE:
+ ctx->next_exe_mode = ctx->read_code >> (32 - 8);
+ ace_add_bits(ctx, 8);
+ break;
+ default:
+ break;
+ }
+ return -1;
+ }
+ }
+
+ if(ctx->sound_var.state[channel] != 2)
+ {
+ if(!ctx->sound_var.state[channel] && ctx->sound_var.code[channel] < ACE_SOUND_RUNLEN_CODES)
+ {
+ ctx->sound_var.state[channel] = 2;
+ }
+ else
+ {
+ if(ctx->sound_var.state[channel] == 1)
+ {
+ err = ctx->sound_var.code[channel];
+ ctx->sound_var.state[channel] = 0;
+ }
+ else
+ {
+ err = ctx->sound_var.code[channel] - ACE_SOUND_RUNLEN_CODES;
+
+ ctx->sound_var.adaptive_model_cnt[channel] =
+ (ctx->sound_var.adaptive_model_cnt[channel] * 7 >> 3) + err;
+
+ ctx->sound_var.adaptive_model_use[channel] = ctx->sound_var.adaptive_model_cnt[channel] > 40;
+ }
+ }
+ }
+
+ if(ctx->sound_var.state[channel] == 2)
+ {
+ if(!ctx->sound_var.code[channel])
+ ctx->sound_var.state[channel] = 1;
+ else
+ ctx->sound_var.code[channel]--;
+
+ err = 0;
+ }
+
+ return err & 1 ? (255 - (err >> 1)) : err >> 1;
+}
+
+static int ace_sound_rar_predict(ace_decompress_ctx_t *ctx, int channel)
+{
+ int pch = ace_sound_get_predicted(ctx, channel);
+
+ if(ctx->sound_var.predictor_dif_cnt[channel][0] > ctx->sound_var.predictor_dif_cnt[channel][1])
+ pch = ctx->sound_var.last_byte[channel];
+
+ return pch - 128;
+}
+
+static void ace_sound_rar_adjust(ace_decompress_ctx_t *ctx, int channel, int ch)
+{
+ int pred_dif, i, min_dif, min_dif_pos = 0, pch;
+
+ ctx->sound_var.byte_count[channel]++;
+
+ pch = ace_sound_get_predicted(ctx, channel);
+ pred_dif = ((int8_t)(pch - ch)) << 3;
+
+ ctx->sound_var.rar_dif[channel][0] += abs(pred_dif - ctx->sound_var.rar_dif_cnt[channel][0]);
+ ctx->sound_var.rar_dif[channel][1] += abs(pred_dif + ctx->sound_var.rar_dif_cnt[channel][0]);
+ ctx->sound_var.rar_dif[channel][2] += abs(pred_dif - ctx->sound_var.rar_dif_cnt[channel][1]);
+ ctx->sound_var.rar_dif[channel][3] += abs(pred_dif + ctx->sound_var.rar_dif_cnt[channel][1]);
+ ctx->sound_var.rar_dif[channel][4] += abs(pred_dif - ctx->sound_var.rar_dif_cnt[channel][2]);
+ ctx->sound_var.rar_dif[channel][5] += abs(pred_dif + ctx->sound_var.rar_dif_cnt[channel][2]);
+ ctx->sound_var.rar_dif[channel][6] += abs(pred_dif - ctx->sound_var.rar_dif_cnt[channel][3]);
+ ctx->sound_var.rar_dif[channel][7] += abs(pred_dif + ctx->sound_var.rar_dif_cnt[channel][3]);
+ ctx->sound_var.rar_dif[channel][8] += abs(pred_dif);
+
+ ctx->sound_var.predictor_dif_cnt[channel][0] += ctx->sound_quantizer[(uint8_t)(pred_dif >> 3)];
+ ctx->sound_var.predictor_dif_cnt[channel][1] +=
+ ctx->sound_quantizer[(uint8_t)(ctx->sound_var.last_byte[channel] - ch)];
+
+ ctx->sound_var.last_delta[channel] = (int8_t)(ch - ctx->sound_var.last_byte[channel]);
+ ctx->sound_var.last_byte[channel] = ch;
+
+ if(!(ctx->sound_var.byte_count[channel] & 0x1f))
+ {
+ min_dif = 0xffff;
+ for(i = 8; i >= 0; i--)
+ {
+ if(ctx->sound_var.rar_dif[channel][i] <= min_dif)
+ {
+ min_dif = ctx->sound_var.rar_dif[channel][i];
+ min_dif_pos = i;
+ }
+ ctx->sound_var.rar_dif[channel][i] = 0;
+ }
+
+ if(min_dif_pos != 8)
+ {
+ i = min_dif_pos >> 1;
+ if(!(min_dif_pos & 1))
+ {
+ if(ctx->sound_var.rar_coefficient[channel][i] >= -16) ctx->sound_var.rar_coefficient[channel][i]--;
+ }
+ else
+ {
+ if(ctx->sound_var.rar_coefficient[channel][i] <= 16) ctx->sound_var.rar_coefficient[channel][i]++;
+ }
+ }
+
+ if(!(ctx->sound_var.byte_count[channel] & 0xff))
+ {
+ ctx->sound_var.predictor_dif_cnt[channel][0] -= ctx->sound_var.last_predictor_dif_cnt[channel][0];
+ ctx->sound_var.last_predictor_dif_cnt[channel][0] = ctx->sound_var.predictor_dif_cnt[channel][0];
+ ctx->sound_var.predictor_dif_cnt[channel][1] -= ctx->sound_var.last_predictor_dif_cnt[channel][1];
+ ctx->sound_var.last_predictor_dif_cnt[channel][1] = ctx->sound_var.predictor_dif_cnt[channel][1];
+ }
+ }
+
+ ctx->sound_var.rar_dif_cnt[channel][3] = ctx->sound_var.rar_dif_cnt[channel][2];
+ ctx->sound_var.rar_dif_cnt[channel][2] = ctx->sound_var.rar_dif_cnt[channel][1];
+ ctx->sound_var.rar_dif_cnt[channel][1] =
+ ctx->sound_var.last_delta[channel] - ctx->sound_var.rar_dif_cnt[channel][0];
+ ctx->sound_var.rar_dif_cnt[channel][0] = ctx->sound_var.last_delta[channel];
+}
+
+int ace_sound_block(ace_decompress_ctx_t *ctx, uint8_t *buf, int len)
+{
+ int i, err, channel, sample;
+
+ len &= (int)0xfffffffc;
+ if(len > (int)ctx->file_size) len = (int)ctx->file_size;
+
+ for(i = 0; i < len; i++)
+ {
+ channel = ace_sound_channel_num[ctx->sound_var.mode][i & 3];
+ err = ace_sound_get(ctx, channel);
+
+ if(err == -1) break;
+
+ sample = (uint8_t)(err + ace_sound_rar_predict(ctx, channel));
+ buf[i] = (uint8_t)(sample + 128);
+ ace_sound_rar_adjust(ctx, channel, buf[i]);
+ }
+
+ ctx->file_size -= i;
+ return i;
+}
+
+void ace_sound_init(ace_decompress_ctx_t *ctx, int type)
+{
+ memset(&ctx->sound_var, 0, sizeof(ctx->sound_var));
+ ctx->sound_var.mode = type - ACE_BLOCK_SOUND_8;
+ ctx->sound_var.models = ace_sound_models[ctx->sound_var.mode];
+}
diff --git a/ace/v1.c b/ace/v1.c
new file mode 100644
index 0000000..0322a85
--- /dev/null
+++ b/ace/v1.c
@@ -0,0 +1,148 @@
+/*
+ * 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 .
+ */
+
+#include
+
+#include "ace.h"
+#include "ace_internal.h"
+
+static void ace_v1_decompress_core(ace_decompress_ctx_t *ctx)
+{
+ int c, lg, i, k;
+ uint32_t dist;
+
+ while(ctx->block_byte_count < ctx->block_buf_size && !ctx->error)
+ {
+ if(!ctx->block_size)
+ {
+ if(!ace_read_widths(ctx, ACE_MAX_CODE_WIDTH, ctx->main_huff_symbols, ctx->main_huff_widths,
+ ACE_MAX_MAIN_CODE) ||
+ !ace_read_widths(ctx, ACE_MAX_CODE_WIDTH, ctx->len_huff_symbols, ctx->len_huff_widths, ACE_MAX_LEN_CODE))
+ {
+ ctx->error = 1;
+ return;
+ }
+ ctx->block_size = ctx->read_code >> (32 - 15);
+ ace_add_bits(ctx, 15);
+ }
+
+ c = ctx->main_huff_symbols[ctx->read_code >> (32 - ACE_MAX_CODE_WIDTH)];
+ ace_add_bits(ctx, ctx->main_huff_widths[c]);
+ ctx->block_size--;
+
+ if(c > 255)
+ {
+ if(c > 259)
+ {
+ if((c -= 260) > 1)
+ {
+ dist = (ctx->read_code >> (33 - c)) + (1u << (c - 1));
+ ace_add_bits(ctx, c - 1);
+ }
+ else
+ {
+ dist = (uint32_t)c;
+ }
+ ctx->old_dists[(ctx->old_dists_pos = (ctx->old_dists_pos + 1) & 3)] = dist;
+ i = 2;
+ if(dist > ACE_MAX_DIST_AT_LEN2)
+ {
+ i++;
+ if(dist > ACE_MAX_DIST_AT_LEN3) i++;
+ }
+ }
+ else
+ {
+ dist = ctx->old_dists[(ctx->old_dists_pos - (c &= 255)) & 3];
+ for(k = c + 1; k--;)
+ ctx->old_dists[(ctx->old_dists_pos - k) & 3] = ctx->old_dists[(ctx->old_dists_pos - k + 1) & 3];
+ ctx->old_dists[ctx->old_dists_pos] = dist;
+ i = 2;
+ if(c > 1) i++;
+ }
+
+ lg = ctx->len_huff_symbols[ctx->read_code >> (32 - ACE_MAX_CODE_WIDTH)];
+ ace_add_bits(ctx, ctx->len_huff_widths[lg]);
+ lg += i;
+ dist++;
+ ace_lz77_copy_string(ctx, dist, lg);
+ }
+ else
+ {
+ ace_lz77_write_char(ctx, (uint8_t)c);
+ }
+ }
+}
+
+int ace_decompress_v1(ace_decompress_ctx_t *ctx, const uint8_t *in_buf, size_t in_len, uint8_t *out_buf,
+ size_t *out_len)
+{
+ size_t orig_size = *out_len;
+ uint32_t old_pos;
+ int i;
+
+ ctx->in_buf = in_buf;
+ ctx->in_len = in_len;
+ ctx->in_pos = 0;
+ ctx->error = 0;
+ ctx->dic_pos = 0;
+
+ ctx->file_size = (int64_t)orig_size;
+ ctx->block_size = 0;
+ ctx->block_byte_count = 0;
+ ctx->old_dists_pos = 0;
+ memset(ctx->old_dists, 0, sizeof(ctx->old_dists));
+
+ ace_init_read_buf(ctx);
+
+ {
+ size_t total_out = 0;
+
+ while(ctx->file_size > 0 && !ctx->error)
+ {
+ old_pos = ctx->dic_pos;
+ ctx->block_byte_count = 0;
+ ctx->block_buf_size = (uint32_t)ctx->file_size;
+
+ if(ctx->block_buf_size > ctx->dic_size - ACE_MAX_LEN) ctx->block_buf_size = ctx->dic_size - ACE_MAX_LEN;
+
+ ace_v1_decompress_core(ctx);
+
+ if(ctx->block_byte_count > 0 && total_out + ctx->block_byte_count <= orig_size)
+ {
+ if(old_pos + ctx->block_byte_count > ctx->dic_size)
+ {
+ i = ctx->dic_size - old_pos;
+ memcpy(out_buf + total_out, &ctx->dictionary[old_pos], i);
+ memcpy(out_buf + total_out + i, ctx->dictionary, ctx->block_byte_count - i);
+ }
+ else
+ {
+ memcpy(out_buf + total_out, &ctx->dictionary[old_pos], ctx->block_byte_count);
+ }
+ }
+
+ total_out += ctx->block_byte_count;
+ ctx->file_size -= ctx->block_byte_count;
+ }
+
+ *out_len = total_out;
+ }
+
+ return ctx->error ? -1 : 0;
+}
diff --git a/ace/v2.c b/ace/v2.c
new file mode 100644
index 0000000..cb1ec9d
--- /dev/null
+++ b/ace/v2.c
@@ -0,0 +1,248 @@
+/*
+ * 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 .
+ */
+
+#include
+
+#include "ace.h"
+#include "ace_internal.h"
+
+int ace_decompress_v20_block(ace_decompress_ctx_t *ctx, uint8_t *buf, int len)
+{
+ int r, rest_len;
+
+ rest_len = len;
+
+ do
+ {
+ if(ctx->type != ctx->next_type)
+ {
+ switch(ctx->next_type)
+ {
+ case ACE_BLOCK_SOUND_8:
+ case ACE_BLOCK_SOUND_16:
+ case ACE_BLOCK_SOUND_32_1:
+ case ACE_BLOCK_SOUND_32_2:
+ ace_sound_init(ctx, ctx->next_type);
+ break;
+ case ACE_BLOCK_PIC:
+ ace_pic_init(ctx);
+ break;
+ default:
+ break;
+ }
+ }
+
+ ctx->type = ctx->next_type;
+
+ switch(ctx->type)
+ {
+ case ACE_BLOCK_LZ77_NORM:
+ case ACE_BLOCK_LZ77_DELTA:
+ case ACE_BLOCK_LZ77_EXE:
+ r = ace_lz77_preprocess_block(ctx, buf, rest_len);
+ break;
+ case ACE_BLOCK_PIC:
+ r = ace_pic_block(ctx, buf, rest_len);
+ ace_lz77_copy_to_dict(ctx, buf, r);
+ break;
+ case ACE_BLOCK_SOUND_8:
+ case ACE_BLOCK_SOUND_16:
+ case ACE_BLOCK_SOUND_32_1:
+ case ACE_BLOCK_SOUND_32_2:
+ r = ace_sound_block(ctx, buf, rest_len);
+ ace_lz77_copy_to_dict(ctx, buf, r);
+ break;
+ default:
+ r = 0;
+ break;
+ }
+
+ ctx->file_pos += r;
+ buf += r;
+ rest_len -= r;
+ } while((r || ctx->next_type != ctx->type || (ctx->next_type == ACE_BLOCK_LZ77_DELTA && ctx->next_delta_len)) &&
+ (rest_len > ACE_MAX_LEN || (ctx->file_size > 0 && ctx->file_size <= ACE_MAX_LEN && rest_len > 0)));
+
+ return len - rest_len;
+}
+
+int ace_lz77_preprocess_block(ace_decompress_ctx_t *ctx, uint8_t *buf, int len)
+{
+ int i, r, pos, read_count;
+
+ if(ctx->type == ACE_BLOCK_LZ77_DELTA)
+ {
+ if(!ctx->prep_num_kept_bytes)
+ {
+ ctx->delta_dist = ctx->next_delta_dist;
+ ctx->delta_len = ctx->next_delta_len;
+ ctx->next_delta_len = 0;
+
+ do
+ {
+ ctx->prep_num_kept_bytes +=
+ (read_count = ace_lz77_block(ctx, ctx->prep_kept_bytes_buf + ctx->prep_num_kept_bytes,
+ ACE_MAX_DELTA_BLOCK + ACE_MAX_LEN - ctx->prep_num_kept_bytes));
+ } while(ctx->prep_num_kept_bytes < ctx->delta_len);
+
+ ctx->delta_block_size = ctx->prep_num_kept_bytes;
+
+ if(ctx->delta_dist)
+ ctx->delta_plane_size = ctx->delta_block_size / ctx->delta_dist;
+ else
+ ctx->delta_plane_size = 0;
+
+ ctx->over_next_type = ctx->next_type;
+ ctx->next_type = ACE_BLOCK_LZ77_DELTA;
+
+ for(i = 0; i < ctx->delta_block_size; i++)
+ ctx->prep_last_delta = (ctx->prep_kept_bytes_buf[i] += (uint8_t)ctx->prep_last_delta);
+
+ ctx->prep_kept_bytes_pos = ctx->delta_plane = ctx->delta_plane_pos = 0;
+ }
+
+ r = ctx->prep_num_kept_bytes > len ? len : ctx->prep_num_kept_bytes;
+
+ if(ctx->delta_plane_size)
+ {
+ for(i = 0;; ctx->delta_plane_pos++)
+ {
+ for(; ctx->delta_plane < ctx->delta_block_size; ctx->delta_plane += ctx->delta_plane_size, i++)
+ {
+ if(i == r) goto DELTA_BREAK;
+ buf[i] = ctx->prep_kept_bytes_buf[ctx->delta_plane_pos + ctx->delta_plane];
+ }
+ ctx->delta_plane = 0;
+ }
+ }
+
+ DELTA_BREAK:
+ ctx->prep_kept_bytes_pos += r;
+ ctx->prep_num_kept_bytes -= r;
+
+ if(!ctx->prep_num_kept_bytes) ctx->next_type = ctx->over_next_type;
+
+ return r;
+ }
+ else
+ {
+ memcpy(buf, ctx->prep_kept_bytes_buf, ctx->prep_num_kept_bytes);
+
+ r = ace_lz77_block(ctx, &buf[ctx->prep_num_kept_bytes], len - ctx->prep_num_kept_bytes) +
+ ctx->prep_num_kept_bytes;
+
+ ctx->prep_num_kept_bytes = 0;
+
+ if(ctx->type == ACE_BLOCK_LZ77_EXE)
+ {
+ ctx->exe_mode = ctx->next_exe_mode;
+
+ for(i = 0; i < r - 4; i++)
+ {
+ pos = (int)(ctx->file_pos + i);
+
+ if(buf[i] == (uint8_t)0xe8)
+ {
+ if(!ctx->exe_mode)
+ {
+ uint16_t val;
+ memcpy(&val, &buf[i + 1], 2);
+ val -= (uint16_t)pos;
+ memcpy(&buf[i + 1], &val, 2);
+ i += 2;
+ }
+ else
+ {
+ uint32_t val;
+ memcpy(&val, &buf[i + 1], 4);
+ val -= (uint32_t)pos;
+ memcpy(&buf[i + 1], &val, 4);
+ i += 4;
+ }
+ }
+ else if(buf[i] == (uint8_t)0xe9)
+ {
+ uint16_t val;
+ memcpy(&val, &buf[i + 1], 2);
+ val -= (uint16_t)pos;
+ memcpy(&buf[i + 1], &val, 2);
+ i += 2;
+ }
+ }
+
+ for(; i < r; i++)
+ {
+ if(buf[i] == (uint8_t)0xe8 || buf[i] == (uint8_t)0xe9)
+ {
+ ctx->prep_num_kept_bytes = r - i;
+ memcpy(ctx->prep_kept_bytes_buf, &buf[i], ctx->prep_num_kept_bytes);
+ break;
+ }
+ }
+ }
+
+ return r - ctx->prep_num_kept_bytes;
+ }
+}
+
+int ace_decompress_v2(ace_decompress_ctx_t *ctx, const uint8_t *in_buf, size_t in_len, uint8_t *out_buf,
+ size_t *out_len)
+{
+ size_t orig_size = *out_len;
+ size_t total_out = 0;
+ int r;
+
+ ctx->in_buf = in_buf;
+ ctx->in_len = in_len;
+ ctx->in_pos = 0;
+ ctx->error = 0;
+ ctx->dic_pos = 0;
+
+ ctx->file_size = (int64_t)orig_size;
+ ctx->file_pos = 0;
+
+ ctx->block_size = 0;
+ ctx->part_size = 0;
+ ctx->main_buf_pos = 0;
+ ctx->old_dists_pos = 0;
+ ctx->prep_num_kept_bytes = 0;
+ ctx->prep_last_delta = 0;
+ ctx->delta_block_size = 0;
+ ctx->next_delta_len = 0;
+ memset(ctx->old_dists, 0, sizeof(ctx->old_dists));
+
+ ctx->type = ACE_BLOCK_LZ77_NORM;
+ ctx->next_type = ACE_BLOCK_LZ77_NORM;
+
+ ace_init_read_buf(ctx);
+
+ while(total_out < orig_size && !ctx->error)
+ {
+ int remaining = (int)(orig_size - total_out);
+
+ r = ace_decompress_v20_block(ctx, out_buf + total_out, remaining);
+ if(r <= 0) break;
+ total_out += r;
+ }
+
+ *out_len = total_out;
+
+ ace_pic_done(ctx);
+
+ return ctx->error ? -1 : 0;
+}
diff --git a/library.c b/library.c
index 86e87f4..41a5ef6 100644
--- a/library.c
+++ b/library.c
@@ -25,15 +25,14 @@
#include "3rdparty/bzip2/bzlib.h"
#include "3rdparty/lz4/lib/lz4.h"
#include "3rdparty/lzfse/src/lzfse.h"
-#include "3rdparty/lzfse/src/lzvn_encode_base.h"
#include "3rdparty/lzfse/src/lzvn_decode_base.h"
+#include "3rdparty/lzfse/src/lzvn_encode_base.h"
+#include "3rdparty/lzma/C/7zCrc.h"
+#include "3rdparty/lzma/C/Alloc.h"
#include "3rdparty/lzma/C/LzmaLib.h"
#include "3rdparty/lzma/C/Xz.h"
-#include "3rdparty/lzma/C/XzEnc.h"
-#include "3rdparty/lzma/C/Alloc.h"
-#include "3rdparty/lzma/C/7zCrc.h"
#include "3rdparty/lzma/C/XzCrc64.h"
-#include "3rdparty/lzo-2.10/include/lzo/lzoconf.h"
+#include "3rdparty/lzma/C/XzEnc.h"
#include "3rdparty/lzo-2.10/include/lzo/lzo1.h"
#include "3rdparty/lzo-2.10/include/lzo/lzo1a.h"
#include "3rdparty/lzo-2.10/include/lzo/lzo1b.h"
@@ -43,64 +42,38 @@
#include "3rdparty/lzo-2.10/include/lzo/lzo1y.h"
#include "3rdparty/lzo-2.10/include/lzo/lzo1z.h"
#include "3rdparty/lzo-2.10/include/lzo/lzo2a.h"
+#include "3rdparty/lzo-2.10/include/lzo/lzoconf.h"
#include "3rdparty/lzo-2.10/include/lzo/lzodefs.h"
#include "3rdparty/zstd/lib/zstd.h"
+#include "ace/ace.h"
-AARU_EXPORT int32_t AARU_CALL AARU_bzip2_decode_buffer(uint8_t * dst_buffer,
- uint32_t * dst_size,
- const uint8_t *src_buffer,
- uint32_t src_size)
-{
- return BZ2_bzBuffToBuffDecompress((char *)dst_buffer, dst_size, (char *)src_buffer, src_size, 0, 0);
-}
+AARU_EXPORT int32_t AARU_CALL AARU_bzip2_decode_buffer(uint8_t *dst_buffer, uint32_t *dst_size,
+ const uint8_t *src_buffer, uint32_t src_size)
+{ return BZ2_bzBuffToBuffDecompress((char *)dst_buffer, dst_size, (char *)src_buffer, src_size, 0, 0); }
-AARU_EXPORT int32_t AARU_CALL AARU_bzip2_encode_buffer(uint8_t * dst_buffer,
- uint32_t * dst_size,
- const uint8_t *src_buffer,
- uint32_t src_size,
- int32_t blockSize100k)
-{
- return BZ2_bzBuffToBuffCompress((char *)dst_buffer, dst_size, (char *)src_buffer, src_size, blockSize100k, 0, 0);
-}
+AARU_EXPORT int32_t AARU_CALL AARU_bzip2_encode_buffer(uint8_t *dst_buffer, uint32_t *dst_size,
+ const uint8_t *src_buffer, uint32_t src_size,
+ int32_t blockSize100k)
+{ return BZ2_bzBuffToBuffCompress((char *)dst_buffer, dst_size, (char *)src_buffer, src_size, blockSize100k, 0, 0); }
-AARU_EXPORT int32_t AARU_CALL AARU_lz4_decode_buffer(uint8_t * dst_buffer,
- int32_t dst_size,
- const uint8_t *src_buffer,
- int32_t src_size)
-{
- return LZ4_decompress_safe((const char *)src_buffer, (char *)dst_buffer, src_size, dst_size);
-}
+AARU_EXPORT int32_t AARU_CALL AARU_lz4_decode_buffer(uint8_t *dst_buffer, int32_t dst_size, const uint8_t *src_buffer,
+ int32_t src_size)
+{ return LZ4_decompress_safe((const char *)src_buffer, (char *)dst_buffer, src_size, dst_size); }
-AARU_EXPORT int32_t AARU_CALL AARU_lz4_encode_buffer(uint8_t * dst_buffer,
- int32_t dst_size,
- const uint8_t *src_buffer,
- int32_t src_size)
-{
- return LZ4_compress_default((const char *)src_buffer, (char *)dst_buffer, src_size, dst_size);
-}
+AARU_EXPORT int32_t AARU_CALL AARU_lz4_encode_buffer(uint8_t *dst_buffer, int32_t dst_size, const uint8_t *src_buffer,
+ int32_t src_size)
+{ return LZ4_compress_default((const char *)src_buffer, (char *)dst_buffer, src_size, dst_size); }
-AARU_EXPORT size_t AARU_CALL AARU_lzfse_decode_buffer(uint8_t * dst_buffer,
- size_t dst_size,
- const uint8_t *src_buffer,
- size_t src_size,
- void * scratch_buffer)
-{
- return lzfse_decode_buffer(dst_buffer, dst_size, src_buffer, src_size, scratch_buffer);
-}
+AARU_EXPORT size_t AARU_CALL AARU_lzfse_decode_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer,
+ size_t src_size, void *scratch_buffer)
+{ return lzfse_decode_buffer(dst_buffer, dst_size, src_buffer, src_size, scratch_buffer); }
-AARU_EXPORT size_t AARU_CALL AARU_lzfse_encode_buffer(uint8_t * dst_buffer,
- size_t dst_size,
- const uint8_t *src_buffer,
- size_t src_size,
- void * scratch_buffer)
-{
- return lzfse_encode_buffer(dst_buffer, dst_size, src_buffer, src_size, scratch_buffer);
-}
+AARU_EXPORT size_t AARU_CALL AARU_lzfse_encode_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer,
+ size_t src_size, void *scratch_buffer)
+{ return lzfse_encode_buffer(dst_buffer, dst_size, src_buffer, src_size, scratch_buffer); }
-AARU_EXPORT size_t AARU_CALL AARU_lzvn_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_lzvn_decode_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer,
+ size_t src_size)
{
lzvn_decoder_state state;
memset(&state, 0, sizeof(state));
@@ -117,11 +90,8 @@ AARU_EXPORT size_t AARU_CALL AARU_lzvn_decode_buffer(uint8_t * dst_buffer,
return (size_t)(state.dst - dst_buffer);
}
-AARU_EXPORT size_t AARU_CALL AARU_lzvn_encode_buffer(uint8_t * dst_buffer,
- size_t dst_size,
- const uint8_t *src_buffer,
- size_t src_size,
- void * scratch_buffer)
+AARU_EXPORT size_t AARU_CALL AARU_lzvn_encode_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer,
+ size_t src_size, void *scratch_buffer)
{
lzvn_encoder_state state;
memset(&state, 0, sizeof(state));
@@ -144,29 +114,14 @@ AARU_EXPORT size_t AARU_CALL AARU_lzvn_encode_buffer(uint8_t * dst_buffer,
return (size_t)(state.dst - dst_buffer);
}
-AARU_EXPORT int32_t AARU_CALL AARU_lzma_decode_buffer(uint8_t * dst_buffer,
- size_t * dst_size,
- const uint8_t *src_buffer,
- size_t * srcLen,
- const uint8_t *props,
- size_t propsSize)
-{
- return LzmaUncompress(dst_buffer, dst_size, src_buffer, srcLen, props, propsSize);
-}
+AARU_EXPORT int32_t AARU_CALL AARU_lzma_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer,
+ size_t *srcLen, const uint8_t *props, size_t propsSize)
+{ return LzmaUncompress(dst_buffer, dst_size, src_buffer, srcLen, props, propsSize); }
-AARU_EXPORT int32_t AARU_CALL AARU_lzma_encode_buffer(uint8_t * dst_buffer,
- size_t * dst_size,
- const uint8_t *src_buffer,
- size_t srcLen,
- uint8_t * outProps,
- size_t * outPropsSize,
- int32_t level,
- uint32_t dictSize,
- int32_t lc,
- int32_t lp,
- int32_t pb,
- int32_t fb,
- int32_t numThreads)
+AARU_EXPORT int32_t AARU_CALL AARU_lzma_encode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer,
+ size_t srcLen, uint8_t *outProps, size_t *outPropsSize,
+ int32_t level, uint32_t dictSize, int32_t lc, int32_t lp,
+ int32_t pb, int32_t fb, int32_t numThreads)
{
return LzmaCompress(dst_buffer, dst_size, src_buffer, srcLen, outProps, outPropsSize, level, dictSize, lc, lp, pb,
fb, numThreads);
@@ -176,7 +131,7 @@ AARU_EXPORT int32_t AARU_CALL AARU_lzma_encode_buffer(uint8_t * dst_buffer,
typedef struct
{
ISeqInStream vt;
- const Byte * data;
+ const Byte *data;
size_t size;
size_t pos;
} CBufferInStream;
@@ -194,7 +149,7 @@ static SRes BufferInStream_Read(ISeqInStreamPtr pp, void *buf, size_t *size)
typedef struct
{
ISeqOutStream vt;
- Byte * data;
+ Byte *data;
size_t size;
size_t pos;
} CBufferOutStream;
@@ -220,10 +175,8 @@ static void xz_init_crc_tables(void)
}
}
-AARU_EXPORT int32_t AARU_CALL AARU_xz_decode_buffer(uint8_t * dst_buffer,
- size_t * dst_size,
- const uint8_t *src_buffer,
- size_t src_size)
+AARU_EXPORT int32_t AARU_CALL AARU_xz_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer,
+ size_t src_size)
{
CXzUnpacker state;
SizeT destLen = (SizeT)*dst_size;
@@ -245,12 +198,8 @@ AARU_EXPORT int32_t AARU_CALL AARU_xz_decode_buffer(uint8_t * dst_buffer,
return res;
}
-AARU_EXPORT int32_t AARU_CALL AARU_xz_encode_buffer(uint8_t * dst_buffer,
- size_t * dst_size,
- const uint8_t *src_buffer,
- size_t src_size,
- uint32_t preset,
- uint32_t checkType)
+AARU_EXPORT int32_t AARU_CALL AARU_xz_encode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer,
+ size_t src_size, uint32_t preset, uint32_t checkType)
{
CXzProps props;
CBufferInStream inStream;
@@ -280,33 +229,21 @@ AARU_EXPORT int32_t AARU_CALL AARU_xz_encode_buffer(uint8_t * dst_buffer,
return res;
}
-AARU_EXPORT size_t AARU_CALL AARU_zstd_decode_buffer(void * dst_buffer,
- size_t dst_size,
- const void *src_buffer,
- size_t src_size)
-{
- return ZSTD_decompress(dst_buffer, dst_size, src_buffer, src_size);
-}
+AARU_EXPORT size_t AARU_CALL AARU_zstd_decode_buffer(void *dst_buffer, size_t dst_size, const void *src_buffer,
+ size_t src_size)
+{ return ZSTD_decompress(dst_buffer, dst_size, src_buffer, src_size); }
-AARU_EXPORT size_t AARU_CALL AARU_zstd_encode_buffer(void * dst_buffer,
- size_t dst_size,
- const void *src_buffer,
- size_t src_size,
- int32_t compressionLevel)
-{
- return ZSTD_compress(dst_buffer, dst_size, src_buffer, src_size, compressionLevel);
-}
+AARU_EXPORT size_t AARU_CALL AARU_zstd_encode_buffer(void *dst_buffer, size_t dst_size, const void *src_buffer,
+ size_t src_size, int32_t compressionLevel)
+{ return ZSTD_compress(dst_buffer, dst_size, src_buffer, src_size, compressionLevel); }
-AARU_EXPORT int32_t AARU_CALL AARU_lzo_decode_buffer(uint8_t * dst_buffer,
- size_t * dst_size,
- const uint8_t *src_buffer,
- size_t src_size,
- int32_t algorithm)
+AARU_EXPORT int32_t AARU_CALL AARU_lzo_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer,
+ size_t src_size, int32_t algorithm)
{
lzo_uint out_len = *dst_size;
int result = lzo_init();
- if(result != LZO_E_OK) return result; // Initialization failed
+ if(result != LZO_E_OK) return result; // Initialization failed
switch(algorithm)
{
@@ -338,59 +275,74 @@ AARU_EXPORT int32_t AARU_CALL AARU_lzo_decode_buffer(uint8_t * dst_buffer,
result = lzo2a_decompress_safe(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, NULL);
break;
default:
- return -1; // Invalid algorithm
+ return -1; // Invalid algorithm
}
*dst_size = out_len;
return result;
}
-AARU_EXPORT int32_t AARU_CALL AARU_lzo_encode_buffer(uint8_t * dst_buffer,
- size_t * dst_size,
- const uint8_t *src_buffer,
- size_t src_size,
- int32_t algorithm,
- int32_t compression_level)
+AARU_EXPORT int32_t AARU_CALL AARU_lzo_encode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer,
+ size_t src_size, int32_t algorithm, int32_t compression_level)
{
lzo_uint out_len = *dst_size;
- void * wrkmem = NULL;
+ void *wrkmem = NULL;
size_t wrkmem_size = 0;
// Determine work memory size based on algorithm and compression level
switch(algorithm)
{
case AARU_LZO_ALGORITHM_LZO1:
- if(compression_level == 99) wrkmem_size = LZO1_99_MEM_COMPRESS;
- else wrkmem_size = LZO1_MEM_COMPRESS;
+ if(compression_level == 99)
+ wrkmem_size = LZO1_99_MEM_COMPRESS;
+ else
+ wrkmem_size = LZO1_MEM_COMPRESS;
break;
case AARU_LZO_ALGORITHM_LZO1A:
- if(compression_level == 99) wrkmem_size = LZO1A_99_MEM_COMPRESS;
- else wrkmem_size = LZO1A_MEM_COMPRESS;
+ if(compression_level == 99)
+ wrkmem_size = LZO1A_99_MEM_COMPRESS;
+ else
+ wrkmem_size = LZO1A_MEM_COMPRESS;
break;
case AARU_LZO_ALGORITHM_LZO1B:
- if(compression_level == 99) wrkmem_size = LZO1B_99_MEM_COMPRESS;
- else if(compression_level == 999) wrkmem_size = LZO1B_999_MEM_COMPRESS;
- else wrkmem_size = LZO1B_MEM_COMPRESS;
+ if(compression_level == 99)
+ wrkmem_size = LZO1B_99_MEM_COMPRESS;
+ else if(compression_level == 999)
+ wrkmem_size = LZO1B_999_MEM_COMPRESS;
+ else
+ wrkmem_size = LZO1B_MEM_COMPRESS;
break;
case AARU_LZO_ALGORITHM_LZO1C:
- if(compression_level == 99) wrkmem_size = LZO1C_99_MEM_COMPRESS;
- else if(compression_level == 999) wrkmem_size = LZO1C_999_MEM_COMPRESS;
- else wrkmem_size = LZO1C_MEM_COMPRESS;
+ if(compression_level == 99)
+ wrkmem_size = LZO1C_99_MEM_COMPRESS;
+ else if(compression_level == 999)
+ wrkmem_size = LZO1C_999_MEM_COMPRESS;
+ else
+ wrkmem_size = LZO1C_MEM_COMPRESS;
break;
case AARU_LZO_ALGORITHM_LZO1F:
- if(compression_level == 999) wrkmem_size = LZO1F_999_MEM_COMPRESS;
- else wrkmem_size = LZO1F_MEM_COMPRESS;
+ if(compression_level == 999)
+ wrkmem_size = LZO1F_999_MEM_COMPRESS;
+ else
+ wrkmem_size = LZO1F_MEM_COMPRESS;
break;
case AARU_LZO_ALGORITHM_LZO1X:
- if(compression_level == 11) wrkmem_size = LZO1X_1_11_MEM_COMPRESS;
- else if(compression_level == 12) wrkmem_size = LZO1X_1_12_MEM_COMPRESS;
- else if(compression_level == 15) wrkmem_size = LZO1X_1_15_MEM_COMPRESS;
- else if(compression_level == 999) wrkmem_size = LZO1X_999_MEM_COMPRESS;
- else wrkmem_size = LZO1X_1_MEM_COMPRESS;
+ if(compression_level == 11)
+ wrkmem_size = LZO1X_1_11_MEM_COMPRESS;
+ else if(compression_level == 12)
+ wrkmem_size = LZO1X_1_12_MEM_COMPRESS;
+ else if(compression_level == 15)
+ wrkmem_size = LZO1X_1_15_MEM_COMPRESS;
+ else if(compression_level == 999)
+ wrkmem_size = LZO1X_999_MEM_COMPRESS;
+ else
+ wrkmem_size = LZO1X_1_MEM_COMPRESS;
break;
case AARU_LZO_ALGORITHM_LZO1Y:
- if(compression_level == 999) wrkmem_size = LZO1Y_999_MEM_COMPRESS;
- else wrkmem_size = LZO1Y_MEM_COMPRESS;
+ if(compression_level == 999)
+ wrkmem_size = LZO1Y_999_MEM_COMPRESS;
+ else
+ wrkmem_size = LZO1Y_MEM_COMPRESS;
break;
case AARU_LZO_ALGORITHM_LZO1Z:
wrkmem_size = LZO1Z_999_MEM_COMPRESS;
@@ -399,11 +351,11 @@ AARU_EXPORT int32_t AARU_CALL AARU_lzo_encode_buffer(uint8_t * dst_buffer,
wrkmem_size = LZO2A_999_MEM_COMPRESS;
break;
default:
- return -1; // Invalid algorithm
+ return -1; // Invalid algorithm
}
int result = lzo_init();
- if(result != LZO_E_OK) return result; // Initialization failed
+ if(result != LZO_E_OK) return result; // Initialization failed
wrkmem = malloc(wrkmem_size);
if(wrkmem == NULL) return -1;
@@ -413,13 +365,15 @@ AARU_EXPORT int32_t AARU_CALL AARU_lzo_encode_buffer(uint8_t * dst_buffer,
{
case AARU_LZO_ALGORITHM_LZO1:
if(compression_level == 99)
- result = lzo1_99_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
- else result = lzo1_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
+ result = lzo1_99_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
+ else
+ result = lzo1_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
break;
case AARU_LZO_ALGORITHM_LZO1A:
if(compression_level == 99)
- result = lzo1a_99_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
- else result = lzo1a_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
+ result = lzo1a_99_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
+ else
+ result = lzo1a_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
break;
case AARU_LZO_ALGORITHM_LZO1B:
if(compression_level == 99)
@@ -427,8 +381,8 @@ AARU_EXPORT int32_t AARU_CALL AARU_lzo_encode_buffer(uint8_t * dst_buffer,
else if(compression_level == 999)
result = lzo1b_999_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
else if(compression_level >= 1 && compression_level <= 9)
- result = lzo1b_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem,
- compression_level);
+ result =
+ lzo1b_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem, compression_level);
else
result = lzo1b_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem,
LZO1B_DEFAULT_COMPRESSION);
@@ -439,16 +393,17 @@ AARU_EXPORT int32_t AARU_CALL AARU_lzo_encode_buffer(uint8_t * dst_buffer,
else if(compression_level == 999)
result = lzo1c_999_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
else if(compression_level >= 1 && compression_level <= 9)
- result = lzo1c_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem,
- compression_level);
+ result =
+ lzo1c_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem, compression_level);
else
result = lzo1c_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem,
LZO1C_DEFAULT_COMPRESSION);
break;
case AARU_LZO_ALGORITHM_LZO1F:
if(compression_level == 999)
- result = lzo1f_999_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
- else result = lzo1f_1_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
+ result = lzo1f_999_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
+ else
+ result = lzo1f_1_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
break;
case AARU_LZO_ALGORITHM_LZO1X:
if(compression_level == 11)
@@ -458,13 +413,15 @@ AARU_EXPORT int32_t AARU_CALL AARU_lzo_encode_buffer(uint8_t * dst_buffer,
else if(compression_level == 15)
result = lzo1x_1_15_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
else if(compression_level == 999)
- result = lzo1x_999_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
- else result = lzo1x_1_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
+ result = lzo1x_999_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
+ else
+ result = lzo1x_1_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
break;
case AARU_LZO_ALGORITHM_LZO1Y:
if(compression_level == 999)
- result = lzo1y_999_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
- else result = lzo1y_1_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
+ result = lzo1y_999_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
+ else
+ result = lzo1y_1_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
break;
case AARU_LZO_ALGORITHM_LZO1Z:
result = lzo1z_999_compress(src_buffer, (lzo_uint)src_size, dst_buffer, &out_len, wrkmem);
@@ -474,7 +431,7 @@ AARU_EXPORT int32_t AARU_CALL AARU_lzo_encode_buffer(uint8_t * dst_buffer,
break;
default:
free(wrkmem);
- return -1; // Invalid algorithm
+ return -1; // Invalid algorithm
}
free(wrkmem);
@@ -485,4 +442,32 @@ AARU_EXPORT int32_t AARU_CALL AARU_lzo_encode_buffer(uint8_t * dst_buffer,
// This is required if BZ_NO_STDIO
void bz_internal_error(int errcode) {}
+AARU_EXPORT int AARU_CALL ace_decompress_lz77(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len,
+ int dic_bits)
+{
+ ace_decompress_ctx_t ctx;
+ int ret;
+
+ if(ace_decompress_init(&ctx, dic_bits) != 0) return -1;
+
+ ret = ace_decompress_v1(&ctx, in_buf, in_len, out_buf, out_len);
+ ace_decompress_free(&ctx);
+
+ return ret;
+}
+
+AARU_EXPORT int AARU_CALL ace_decompress_blocked(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf,
+ size_t *out_len, int dic_bits)
+{
+ ace_decompress_ctx_t ctx;
+ int ret;
+
+ if(ace_decompress_init(&ctx, dic_bits) != 0) return -1;
+
+ ret = ace_decompress_v2(&ctx, in_buf, in_len, out_buf, out_len);
+ ace_decompress_free(&ctx);
+
+ return ret;
+}
+
AARU_EXPORT uint64_t AARU_CALL AARU_get_acn_version() { return AARU_CHECKUMS_NATIVE_VERSION; }
\ No newline at end of file
diff --git a/library.h b/library.h
index caeead9..2130d4e 100644
--- a/library.h
+++ b/library.h
@@ -234,4 +234,10 @@ AARU_EXPORT int AARU_CALL pmarc_decompress_pm1(const uint8_t *in_buf, size_t in_
// 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);
+// ACE v1 (LZ77) decompression
+AARU_EXPORT int AARU_CALL ace_decompress_lz77(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len, int dic_bits);
+
+// ACE v2 (Blocked) decompression
+AARU_EXPORT int AARU_CALL ace_decompress_blocked(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len, int dic_bits);
+
#endif // AARU_COMPRESSION_NATIVE_LIBRARY_H
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 6919daf..abb0848 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -90,10 +90,17 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/lharc_lh1.lzh
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/pmarc_pm2.pma
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/ace_v1_lz77.bin
+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
+
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/ace_v2_blocked.bin
+ 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
- lha/lh_static.cpp lha/lh1.cpp lha/larc.cpp lha/lh_old.cpp)
+ lha/lh_static.cpp lha/lh1.cpp lha/larc.cpp lha/lh_old.cpp
+ ace/ace.cpp)
target_link_libraries(tests_run gtest gtest_main "Aaru.Compression.Native")
diff --git a/tests/ace/ace.cpp b/tests/ace/ace.cpp
new file mode 100644
index 0000000..3d5a90d
--- /dev/null
+++ b/tests/ace/ace.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 .
+ */
+
+#include
+#include
+#include
+
+#include "../../library.h"
+#include "../crc32.h"
+#include "gtest/gtest.h"
+
+#define EXPECTED_CRC32 0x66007dba
+
+static const uint8_t *buffer_v1;
+static const uint8_t *buffer_v2;
+
+class ace_v1Fixture : public ::testing::Test
+{
+public:
+ ace_v1Fixture() {}
+
+protected:
+ void SetUp()
+ {
+ char path[PATH_MAX];
+ char filename[PATH_MAX];
+
+ getcwd(path, PATH_MAX);
+ snprintf(filename, PATH_MAX, "%s/data/ace_v1_lz77.bin", path);
+
+ FILE *file = fopen(filename, "rb");
+ buffer_v1 = (const uint8_t *)malloc(54904);
+ fread((void *)buffer_v1, 1, 54904, file);
+ fclose(file);
+ }
+
+ void TearDown() { free((void *)buffer_v1); }
+
+ ~ace_v1Fixture() {}
+};
+
+TEST_F(ace_v1Fixture, ace_v1_lz77)
+{
+ size_t destLen = 152089;
+ size_t srcLen = 54904;
+ auto *outBuf = (uint8_t *)malloc(152089);
+
+ auto err = ace_decompress_lz77(buffer_v1, srcLen, outBuf, &destLen, 20);
+
+ EXPECT_EQ(err, 0);
+ EXPECT_EQ(destLen, 152089);
+
+ auto crc = crc32_data(outBuf, 152089);
+
+ free(outBuf);
+
+ EXPECT_EQ(crc, EXPECTED_CRC32);
+}
+
+class ace_v2Fixture : public ::testing::Test
+{
+public:
+ ace_v2Fixture() {}
+
+protected:
+ void SetUp()
+ {
+ char path[PATH_MAX];
+ char filename[PATH_MAX];
+
+ getcwd(path, PATH_MAX);
+ snprintf(filename, PATH_MAX, "%s/data/ace_v2_blocked.bin", path);
+
+ FILE *file = fopen(filename, "rb");
+ buffer_v2 = (const uint8_t *)malloc(50984);
+ fread((void *)buffer_v2, 1, 50984, file);
+ fclose(file);
+ }
+
+ void TearDown() { free((void *)buffer_v2); }
+
+ ~ace_v2Fixture() {}
+};
+
+TEST_F(ace_v2Fixture, ace_v2_blocked)
+{
+ size_t destLen = 152089;
+ size_t srcLen = 50984;
+ auto *outBuf = (uint8_t *)malloc(152089);
+
+ auto err = ace_decompress_blocked(buffer_v2, srcLen, outBuf, &destLen, 20);
+
+ EXPECT_EQ(err, 0);
+ EXPECT_EQ(destLen, 152089);
+
+ auto crc = crc32_data(outBuf, 152089);
+
+ free(outBuf);
+
+ EXPECT_EQ(crc, EXPECTED_CRC32);
+}
diff --git a/tests/data/ace_v1_lz77.bin b/tests/data/ace_v1_lz77.bin
new file mode 100644
index 0000000..afbdaeb
Binary files /dev/null and b/tests/data/ace_v1_lz77.bin differ
diff --git a/tests/data/ace_v2_blocked.bin b/tests/data/ace_v2_blocked.bin
new file mode 100644
index 0000000..997301d
Binary files /dev/null and b/tests/data/ace_v2_blocked.bin differ