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