From 12a4d684f51743cd8ff76bfe735bf7d3d1068a24 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 15 Apr 2026 00:52:22 +0100 Subject: [PATCH] Add support for various ZIP compression methods - Implement Deflate64 decompression in zip/deflate64.h and zip/deflate64.c. - Add ZIP Implode decompression functionality in zip/implode.h and zip/implode.c. - Introduce ZIP Reduce decompression in zip/reduce.h and zip/reduce.c. - Implement ZIP Shrink decompression in zip/shrink.h and zip/shrink.c. - Create a unified ZIP interface in zip/zip.h and zip/zip.c to handle multiple compression methods including PPMd, WavPack, and WinZip JPEG. - Ensure all new functions adhere to the Aaru Data Preservation Suite licensing and documentation standards. --- CMakeLists.txt | 43 +- library.c | 85 ++ library.h | 110 ++- ppmd/Context.c | 341 ++++++++ ppmd/Context.h | 110 +++ ppmd/RangeCoder.c | 109 +++ ppmd/RangeCoder.h | 50 ++ ppmd/SubAllocator.h | 69 ++ ppmd/SubAllocatorVariantI.c | 365 +++++++++ ppmd/SubAllocatorVariantI.h | 52 ++ ppmd/VariantI.c | 886 +++++++++++++++++++++ ppmd/VariantI.h | 53 ++ tests/CMakeLists.txt | 15 +- tests/data/zip_deflate64.bin | Bin 0 -> 50564 bytes tests/data/zip_implode.bin | Bin 0 -> 60488 bytes tests/data/zip_ppmd.bin | Bin 0 -> 38627 bytes tests/data/zip_shrink.bin | Bin 0 -> 65014 bytes tests/zip/zip.cpp | 193 +++++ wavpack/common_utils.c | 713 +++++++++++++++++ wavpack/decorr_utils.c | 210 +++++ wavpack/entropy_utils.c | 369 +++++++++ wavpack/open_legacy.c | 116 +++ wavpack/open_utils.c | 1327 ++++++++++++++++++++++++++++++++ wavpack/read_words.c | 637 +++++++++++++++ wavpack/tags.c | 189 +++++ wavpack/unpack.c | 872 +++++++++++++++++++++ wavpack/unpack_floats.c | 154 ++++ wavpack/unpack_seek.c | 404 ++++++++++ wavpack/unpack_utils.c | 417 ++++++++++ wavpack/wavpack.h | 436 +++++++++++ wavpack/wavpack_local.h | 945 +++++++++++++++++++++++ wavpack/wavpack_version.h | 19 + winzipjpeg/ArithmeticDecoder.c | 696 +++++++++++++++++ winzipjpeg/ArithmeticDecoder.h | 71 ++ winzipjpeg/Decompressor.c | 1079 ++++++++++++++++++++++++++ winzipjpeg/Decompressor.h | 102 +++ winzipjpeg/InputStream.h | 29 + winzipjpeg/JPEG.c | 329 ++++++++ winzipjpeg/JPEG.h | 89 +++ winzipjpeg/LZMA.h | 32 + zip/deflate64.c | 350 +++++++++ zip/deflate64.h | 39 + zip/implode.c | 199 +++++ zip/implode.h | 41 + zip/reduce.c | 161 ++++ zip/reduce.h | 40 + zip/shrink.c | 227 ++++++ zip/shrink.h | 39 + zip/zip.c | 335 ++++++++ zip/zip.h | 71 ++ 50 files changed, 13188 insertions(+), 30 deletions(-) create mode 100644 ppmd/Context.c create mode 100644 ppmd/Context.h create mode 100644 ppmd/RangeCoder.c create mode 100644 ppmd/RangeCoder.h create mode 100644 ppmd/SubAllocator.h create mode 100644 ppmd/SubAllocatorVariantI.c create mode 100644 ppmd/SubAllocatorVariantI.h create mode 100644 ppmd/VariantI.c create mode 100644 ppmd/VariantI.h create mode 100644 tests/data/zip_deflate64.bin create mode 100644 tests/data/zip_implode.bin create mode 100644 tests/data/zip_ppmd.bin create mode 100644 tests/data/zip_shrink.bin create mode 100644 tests/zip/zip.cpp create mode 100644 wavpack/common_utils.c create mode 100644 wavpack/decorr_utils.c create mode 100644 wavpack/entropy_utils.c create mode 100644 wavpack/open_legacy.c create mode 100644 wavpack/open_utils.c create mode 100644 wavpack/read_words.c create mode 100644 wavpack/tags.c create mode 100644 wavpack/unpack.c create mode 100644 wavpack/unpack_floats.c create mode 100644 wavpack/unpack_seek.c create mode 100644 wavpack/unpack_utils.c create mode 100644 wavpack/wavpack.h create mode 100644 wavpack/wavpack_local.h create mode 100644 wavpack/wavpack_version.h create mode 100644 winzipjpeg/ArithmeticDecoder.c create mode 100644 winzipjpeg/ArithmeticDecoder.h create mode 100644 winzipjpeg/Decompressor.c create mode 100644 winzipjpeg/Decompressor.h create mode 100644 winzipjpeg/InputStream.h create mode 100644 winzipjpeg/JPEG.c create mode 100644 winzipjpeg/JPEG.h create mode 100644 winzipjpeg/LZMA.h create mode 100644 zip/deflate64.c create mode 100644 zip/deflate64.h create mode 100644 zip/implode.c create mode 100644 zip/implode.h create mode 100644 zip/reduce.c create mode 100644 zip/reduce.h create mode 100644 zip/shrink.c create mode 100644 zip/shrink.h create mode 100644 zip/zip.c create mode 100644 zip/zip.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 03c3461..4519c2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,7 +176,48 @@ add_library("Aaru.Compression.Native" SHARED library.c apple_rle.c apple_rle.h a arj/arj.h arj/arj_fastest.c arjz/arjz.c - arjz/arjz.h) + arjz/arjz.h + zip/shrink.c + zip/shrink.h + zip/reduce.c + zip/reduce.h + zip/implode.c + zip/implode.h + zip/deflate64.c + zip/deflate64.h + zip/zip.c + zip/zip.h + ppmd/RangeCoder.c + ppmd/RangeCoder.h + ppmd/Context.c + ppmd/Context.h + ppmd/SubAllocator.h + ppmd/SubAllocatorVariantI.c + ppmd/SubAllocatorVariantI.h + ppmd/VariantI.c + ppmd/VariantI.h + wavpack/common_utils.c + wavpack/decorr_utils.c + wavpack/entropy_utils.c + wavpack/open_legacy.c + wavpack/open_utils.c + wavpack/read_words.c + wavpack/tags.c + wavpack/unpack.c + wavpack/unpack_floats.c + wavpack/unpack_seek.c + wavpack/unpack_utils.c + wavpack/wavpack.h + wavpack/wavpack_local.h + wavpack/wavpack_version.h + winzipjpeg/ArithmeticDecoder.c + winzipjpeg/ArithmeticDecoder.h + winzipjpeg/Decompressor.c + winzipjpeg/Decompressor.h + winzipjpeg/JPEG.c + winzipjpeg/JPEG.h + winzipjpeg/InputStream.h + winzipjpeg/LZMA.h) include(3rdparty/bzip2.cmake) include(3rdparty/flac.cmake) diff --git a/library.c b/library.c index 41a5ef6..04eb107 100644 --- a/library.c +++ b/library.c @@ -29,6 +29,8 @@ #include "3rdparty/lzfse/src/lzvn_encode_base.h" #include "3rdparty/lzma/C/7zCrc.h" #include "3rdparty/lzma/C/Alloc.h" +#include "3rdparty/lzma/C/Lzma2Dec.h" +#include "3rdparty/lzma/C/Lzma2Enc.h" #include "3rdparty/lzma/C/LzmaLib.h" #include "3rdparty/lzma/C/Xz.h" #include "3rdparty/lzma/C/XzCrc64.h" @@ -46,6 +48,7 @@ #include "3rdparty/lzo-2.10/include/lzo/lzodefs.h" #include "3rdparty/zstd/lib/zstd.h" #include "ace/ace.h" +#include "zip/zip.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) @@ -470,4 +473,86 @@ AARU_EXPORT int AARU_CALL ace_decompress_blocked(const uint8_t *in_buf, size_t i return ret; } +/* ============== LZMA2 ============== */ + +AARU_EXPORT int32_t AARU_CALL AARU_lzma2_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, + size_t *src_size, uint8_t prop) +{ + ELzmaStatus status; + return Lzma2Decode(dst_buffer, (SizeT *)dst_size, src_buffer, (SizeT *)src_size, prop, LZMA_FINISH_END, &status, + &g_Alloc); +} + +AARU_EXPORT int32_t AARU_CALL AARU_lzma2_encode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, + size_t src_size, uint8_t *outProp, int32_t level, + uint32_t dictSize, int32_t lc, int32_t lp, int32_t pb, + int32_t fb, int32_t numThreads) +{ + CLzma2EncHandle enc; + CLzma2EncProps props; + SRes res; + + enc = Lzma2Enc_Create(&g_Alloc, &g_Alloc); + if(!enc) return SZ_ERROR_MEM; + + Lzma2EncProps_Init(&props); + props.lzmaProps.level = level; + props.lzmaProps.dictSize = dictSize; + props.lzmaProps.lc = lc; + props.lzmaProps.lp = lp; + props.lzmaProps.pb = pb; + props.lzmaProps.fb = fb; + props.lzmaProps.numThreads = numThreads; + + res = Lzma2Enc_SetProps(enc, &props); + if(res != SZ_OK) + { + Lzma2Enc_Destroy(enc); + return res; + } + + *outProp = Lzma2Enc_WriteProperties(enc); + + res = Lzma2Enc_Encode2(enc, NULL, dst_buffer, dst_size, NULL, src_buffer, src_size, NULL); + + Lzma2Enc_Destroy(enc); + return res; +} + +/* ============== ZIP Wrappers ============== */ + +AARU_EXPORT int AARU_CALL AARU_zip_shrink_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, + const uint8_t *src_buffer, size_t src_size) +{ return zip_shrink_decompress(src_buffer, src_size, dst_buffer, dst_size); } + +AARU_EXPORT int AARU_CALL AARU_zip_reduce_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, + const uint8_t *src_buffer, size_t src_size, int comp_factor) +{ return zip_reduce_decompress(src_buffer, src_size, dst_buffer, dst_size, comp_factor); } + +AARU_EXPORT int AARU_CALL AARU_zip_implode_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, + const uint8_t *src_buffer, size_t src_size, + int large_dictionary, int has_literals) +{ return zip_implode_decompress(src_buffer, src_size, dst_buffer, dst_size, large_dictionary, has_literals); } + +AARU_EXPORT int AARU_CALL AARU_zip_deflate64_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, + const uint8_t *src_buffer, size_t src_size) +{ return zip_deflate64_decompress(src_buffer, src_size, dst_buffer, dst_size); } + +AARU_EXPORT int AARU_CALL AARU_zip_ppmd_decode_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer, + size_t src_size, int max_order, int sub_alloc_size, + int restoration) +{ return zip_ppmd_decompress(dst_buffer, dst_size, src_buffer, src_size, max_order, sub_alloc_size, restoration); } + +AARU_EXPORT int AARU_CALL AARU_zip_wavpack_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, + const uint8_t *src_buffer, size_t src_size, + uint32_t num_samples, int bits_per_sample, int num_channels) +{ + return zip_wavpack_decompress(dst_buffer, dst_size, src_buffer, src_size, num_samples, bits_per_sample, + num_channels); +} + +AARU_EXPORT int AARU_CALL AARU_zip_winzipjpeg_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, + const uint8_t *src_buffer, size_t src_size) +{ return zip_winzipjpeg_decompress(dst_buffer, dst_size, src_buffer, src_size); } + 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 3cd7d57..dc9b549 100644 --- a/library.h +++ b/library.h @@ -71,10 +71,10 @@ AARU_EXPORT size_t AARU_CALL AARU_flac_encode_redbook_buffer( uint32_t application_id_len); 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); + int32_t src_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); + int32_t src_size); AARU_EXPORT int32_t AARU_CALL AARU_lzip_decode_buffer(uint8_t *dst_buffer, int32_t dst_size, const uint8_t *src_buffer, int32_t src_size); @@ -125,16 +125,17 @@ AARU_EXPORT size_t AARU_CALL AARU_zstd_encode_buffer(void *dst_buffer, size_t ds /** * LZO Algorithm Types */ -typedef enum { - AARU_LZO_ALGORITHM_LZO1 = 0, /* LZO1 algorithm */ - AARU_LZO_ALGORITHM_LZO1A = 1, /* LZO1A algorithm */ - AARU_LZO_ALGORITHM_LZO1B = 2, /* LZO1B algorithm (supports compression levels 1-9, 99, 999) */ - AARU_LZO_ALGORITHM_LZO1C = 3, /* LZO1C algorithm (supports compression levels 1-9, 99, 999) */ - AARU_LZO_ALGORITHM_LZO1F = 4, /* LZO1F algorithm (supports compression level 999) */ - AARU_LZO_ALGORITHM_LZO1X = 5, /* LZO1X algorithm (supports compression levels 11, 12, 15, 999) - most common */ - AARU_LZO_ALGORITHM_LZO1Y = 6, /* LZO1Y algorithm (supports compression level 999) */ - AARU_LZO_ALGORITHM_LZO1Z = 7, /* LZO1Z algorithm (only 999 compression level) */ - AARU_LZO_ALGORITHM_LZO2A = 8 /* LZO2A algorithm (only 999 compression level) */ +typedef enum +{ + AARU_LZO_ALGORITHM_LZO1 = 0, /* LZO1 algorithm */ + AARU_LZO_ALGORITHM_LZO1A = 1, /* LZO1A algorithm */ + AARU_LZO_ALGORITHM_LZO1B = 2, /* LZO1B algorithm (supports compression levels 1-9, 99, 999) */ + AARU_LZO_ALGORITHM_LZO1C = 3, /* LZO1C algorithm (supports compression levels 1-9, 99, 999) */ + AARU_LZO_ALGORITHM_LZO1F = 4, /* LZO1F algorithm (supports compression level 999) */ + AARU_LZO_ALGORITHM_LZO1X = 5, /* LZO1X algorithm (supports compression levels 11, 12, 15, 999) - most common */ + AARU_LZO_ALGORITHM_LZO1Y = 6, /* LZO1Y algorithm (supports compression level 999) */ + AARU_LZO_ALGORITHM_LZO1Z = 7, /* LZO1Z algorithm (only 999 compression level) */ + AARU_LZO_ALGORITHM_LZO2A = 8 /* LZO2A algorithm (only 999 compression level) */ } aaru_lzo_algorithm_t; AARU_EXPORT int32_t AARU_CALL AARU_lzo_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, @@ -192,14 +193,17 @@ AARU_EXPORT int AARU_CALL pak_decompress_distill(const unsigned char *in_buf, si /** * HA Algorithm Types */ -typedef enum { - HA_ALGORITHM_ASC = 0, /* ASC algorithm */ - HA_ALGORITHM_HSC = 1 /* HSC algorithm */ +typedef enum +{ + HA_ALGORITHM_ASC = 0, /* ASC algorithm */ + HA_ALGORITHM_HSC = 1 /* HSC algorithm */ } ha_algorithm_t; -AARU_EXPORT int AARU_CALL ha_asc_decompress(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf, size_t *out_len); +AARU_EXPORT int AARU_CALL ha_asc_decompress(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf, + size_t *out_len); -AARU_EXPORT int AARU_CALL ha_hsc_decompress(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf, size_t *out_len); +AARU_EXPORT int AARU_CALL ha_hsc_decompress(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf, + size_t *out_len); // LHA -lh1- (Dynamic Huffman, 4KB window) AARU_EXPORT int AARU_CALL lha_decompress_lh1(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len); @@ -235,34 +239,84 @@ AARU_EXPORT int AARU_CALL pmarc_decompress_pm1(const uint8_t *in_buf, size_t in_ 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); +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); +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); // ARJ Method 1 (LZH, most compression) -AARU_EXPORT int AARU_CALL arj_decompress_method1(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len); +AARU_EXPORT int AARU_CALL arj_decompress_method1(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, + size_t *out_len); // ARJ Method 2 (LZH, medium compression) -AARU_EXPORT int AARU_CALL arj_decompress_method2(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len); +AARU_EXPORT int AARU_CALL arj_decompress_method2(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, + size_t *out_len); // ARJ Method 3 (LZH, fast compression) -AARU_EXPORT int AARU_CALL arj_decompress_method3(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len); +AARU_EXPORT int AARU_CALL arj_decompress_method3(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, + size_t *out_len); // ARJ Method 4 (Fastest, variable-width LZSS) -AARU_EXPORT int AARU_CALL arj_decompress_fastest(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len); +AARU_EXPORT int AARU_CALL arj_decompress_fastest(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, + size_t *out_len); // ARJZ Method 1 (LZH, 64KB window) -AARU_EXPORT int AARU_CALL arjz_decompress_method1(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len); +AARU_EXPORT int AARU_CALL arjz_decompress_method1(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, + size_t *out_len); // ARJZ Method 2 (LZH, 64KB window) -AARU_EXPORT int AARU_CALL arjz_decompress_method2(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len); +AARU_EXPORT int AARU_CALL arjz_decompress_method2(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, + size_t *out_len); // ARJZ Method 3 (LZH, 64KB window) -AARU_EXPORT int AARU_CALL arjz_decompress_method3(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len); +AARU_EXPORT int AARU_CALL arjz_decompress_method3(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, + size_t *out_len); // ARJZ custom extended DEFLATE decompression -AARU_EXPORT int AARU_CALL arjz_decompress_buffer(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len, - size_t orig_size); +AARU_EXPORT int AARU_CALL arjz_decompress_buffer(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, + size_t *out_len, size_t orig_size); + +// LZMA2 decode (single prop byte instead of 5-byte props blob) +AARU_EXPORT int32_t AARU_CALL AARU_lzma2_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, + size_t *src_size, uint8_t prop); + +// LZMA2 encode +AARU_EXPORT int32_t AARU_CALL AARU_lzma2_encode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, + size_t src_size, uint8_t *outProp, int32_t level, + uint32_t dictSize, int32_t lc, int32_t lp, int32_t pb, + int32_t fb, int32_t numThreads); + +// ZIP method 1: Shrink (LZW, 9-13 bit codes) +AARU_EXPORT int AARU_CALL AARU_zip_shrink_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, + const uint8_t *src_buffer, size_t src_size); + +// ZIP methods 2-5: Reduce (follower sets + LZ77, compression factor 1-4) +AARU_EXPORT int AARU_CALL AARU_zip_reduce_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, + const uint8_t *src_buffer, size_t src_size, int comp_factor); + +// ZIP method 6: Implode (Shannon-Fano + LZSS) +AARU_EXPORT int AARU_CALL AARU_zip_implode_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, + const uint8_t *src_buffer, size_t src_size, + int large_dictionary, int has_literals); + +// ZIP method 9: Deflate64 +AARU_EXPORT int AARU_CALL AARU_zip_deflate64_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, + const uint8_t *src_buffer, size_t src_size); + +// ZIP method 98: PPMd variant I +AARU_EXPORT int AARU_CALL AARU_zip_ppmd_decode_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer, + size_t src_size, int max_order, int sub_alloc_size, + int restoration); + +// ZIP method 97: WinZip WavPack +AARU_EXPORT int AARU_CALL AARU_zip_wavpack_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, + const uint8_t *src_buffer, size_t src_size, + uint32_t num_samples, int bits_per_sample, int num_channels); + +// ZIP method 96: WinZip JPEG +AARU_EXPORT int AARU_CALL AARU_zip_winzipjpeg_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, + const uint8_t *src_buffer, size_t src_size); #endif // AARU_COMPRESSION_NATIVE_LIBRARY_H diff --git a/ppmd/Context.c b/ppmd/Context.c new file mode 100644 index 0000000..0fe4564 --- /dev/null +++ b/ppmd/Context.c @@ -0,0 +1,341 @@ +/* + * Context.c + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#include "Context.h" + +#include + +SEE2Context MakeSEE2(int initval, int count) +{ + SEE2Context self; + self.Shift = PERIOD_BITS - 4; + self.Summ = initval << self.Shift; + self.Count = count; + return self; +} + +unsigned int GetSEE2MeanMasked(SEE2Context *self) +{ + unsigned int retval = self->Summ >> self->Shift; + self->Summ -= retval; + retval &= 0x03ff; + if(retval == 0) return 1; + return retval; +} + +unsigned int GetSEE2Mean(SEE2Context *self) +{ + unsigned int retval = self->Summ >> self->Shift; + self->Summ -= retval; + if(retval == 0) return 1; + return retval; +} + +void UpdateSEE2(SEE2Context *self) +{ + if(self->Shift >= PERIOD_BITS) return; + + self->Count--; + if(self->Count == 0) + { + self->Summ *= 2; + self->Count = 3 << self->Shift; + self->Shift++; + } +} + +PPMdContext *PPMdStateSuccessor(PPMdState *self, PPMdCoreModel *model) +{ return OffsetToPointer(model->alloc, self->Successor); } + +void SetPPMdStateSuccessorPointer(PPMdState *self, PPMdContext *newsuccessor, PPMdCoreModel *model) +{ self->Successor = PointerToOffset(model->alloc, newsuccessor); } + +PPMdState *PPMdContextStates(PPMdContext *self, PPMdCoreModel *model) +{ return OffsetToPointer(model->alloc, self->States); } + +void SetPPMdContextStatesPointer(PPMdContext *self, PPMdState *newstates, PPMdCoreModel *model) +{ self->States = PointerToOffset(model->alloc, newstates); } + +PPMdContext *PPMdContextSuffix(PPMdContext *self, PPMdCoreModel *model) +{ return OffsetToPointer(model->alloc, self->Suffix); } + +void SetPPMdContextSuffixPointer(PPMdContext *self, PPMdContext *newsuffix, PPMdCoreModel *model) +{ self->Suffix = PointerToOffset(model->alloc, newsuffix); } + +PPMdState *PPMdContextOneState(PPMdContext *self) { return (PPMdState *)&self->SummFreq; } + +PPMdContext *NewPPMdContext(PPMdCoreModel *model) +{ + PPMdContext *context = OffsetToPointer(model->alloc, AllocContext(model->alloc)); + if(context) + { + context->LastStateIndex = 0; + context->Flags = 0; + context->Suffix = 0; + } + return context; +} + +PPMdContext *NewPPMdContextAsChildOf(PPMdCoreModel *model, PPMdContext *suffixcontext, PPMdState *suffixstate, + PPMdState *firststate) +{ + PPMdContext *context = OffsetToPointer(model->alloc, AllocContext(model->alloc)); + if(context) + { + context->LastStateIndex = 0; + context->Flags = 0; + SetPPMdContextSuffixPointer(context, suffixcontext, model); + SetPPMdStateSuccessorPointer(suffixstate, context, model); + if(firststate) *(PPMdContextOneState(context)) = *firststate; + } + return context; +} + +// Tabulated escapes for exponential symbol distribution +static const uint8_t ExpEscape[16] = {25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2}; + +#define GET_MEAN(SUMM, SHIFT, ROUND) ((SUMM + (1 << (SHIFT - ROUND))) >> (SHIFT)) + +void PPMdDecodeBinSymbol(PPMdContext *self, PPMdCoreModel *model, uint16_t *bs, int freqlimit, bool altnextbit) +{ + PPMdState *rs = PPMdContextOneState(self); + + int bit; + if(altnextbit) + bit = NextWeightedBitFromPPMdRangeCoder2(&model->coder, *bs, TOT_BITS); + else + bit = NextWeightedBitFromPPMdRangeCoder(&model->coder, *bs, 1 << TOT_BITS); + + if(bit == 0) + { + model->PrevSuccess = 1; + model->RunLength++; + model->FoundState = rs; + + if(rs->Freq < freqlimit) rs->Freq++; + *bs += INTERVAL - GET_MEAN(*bs, PERIOD_BITS, 2); + } + else + { + model->PrevSuccess = 0; + model->FoundState = NULL; + model->LastMaskIndex = 0; + model->CharMask[rs->Symbol] = model->EscCount; + + *bs -= GET_MEAN(*bs, PERIOD_BITS, 2); + model->InitEsc = ExpEscape[*bs >> 10]; + } +} + +int PPMdDecodeSymbol1(PPMdContext *self, PPMdCoreModel *model, bool greaterorequal) +{ + model->scale = self->SummFreq; + + PPMdState *states = PPMdContextStates(self, model); + int firstcount = states[0].Freq; + int count = PPMdRangeCoderCurrentCount(&model->coder, model->scale); + int adder = greaterorequal ? 1 : 0; + + if(count < firstcount) + { + RemovePPMdRangeCoderSubRange(&model->coder, 0, firstcount); + if(2 * firstcount + adder > model->scale) + { + model->PrevSuccess = 1; + model->RunLength++; + } + else + model->PrevSuccess = 0; + + model->FoundState = &states[0]; + states[0].Freq = firstcount + 4; + self->SummFreq += 4; + + if(firstcount + 4 > MAX_FREQ) model->RescalePPMdContext(self, model); + + return -1; + } + + int highcount = firstcount; + model->PrevSuccess = 0; + + for(int i = 1; i <= self->LastStateIndex; i++) + { + highcount += states[i].Freq; + if(highcount > count) + { + RemovePPMdRangeCoderSubRange(&model->coder, highcount - states[i].Freq, highcount); + UpdatePPMdContext1(self, model, &states[i]); + return -1; + } + } + + if(!model->FoundState) return -1; + int lastsym = model->FoundState->Symbol; + + // if ( Suffix ) PrefetchData(Suffix); + RemovePPMdRangeCoderSubRange(&model->coder, highcount, model->scale); + model->LastMaskIndex = self->LastStateIndex; + model->FoundState = NULL; + + for(int i = 0; i <= self->LastStateIndex; i++) model->CharMask[states[i].Symbol] = model->EscCount; + + return lastsym; +} + +void UpdatePPMdContext1(PPMdContext *self, PPMdCoreModel *model, PPMdState *state) +{ + state->Freq += 4; + self->SummFreq += 4; + + if(state[0].Freq > state[-1].Freq) + { + SWAP(state[0], state[-1]); + model->FoundState = &state[-1]; + if(state[-1].Freq > MAX_FREQ) model->RescalePPMdContext(self, model); + } + else + { + model->FoundState = state; + } +} + +void PPMdDecodeSymbol2(PPMdContext *self, PPMdCoreModel *model, SEE2Context *see) +{ + int n = self->LastStateIndex - model->LastMaskIndex; + PPMdState *ps[256]; + + int total = 0; + PPMdState *state = PPMdContextStates(self, model); + for(int i = 0; i < n; i++) + { + while(model->CharMask[state->Symbol] == model->EscCount) state++; + + total += state->Freq; + ps[i] = state++; + } + + model->scale += total; + int count = PPMdRangeCoderCurrentCount(&model->coder, model->scale); + + if(count < total) + { + int i = 0, highcount = ps[0]->Freq; + while(highcount <= count && i + 1 < n) highcount += ps[++i]->Freq; + + RemovePPMdRangeCoderSubRange(&model->coder, highcount - ps[i]->Freq, highcount); + UpdateSEE2(see); + UpdatePPMdContext2(self, model, ps[i]); + } + else + { + RemovePPMdRangeCoderSubRange(&model->coder, total, model->scale); + model->LastMaskIndex = self->LastStateIndex; + see->Summ += model->scale; + + for(int i = 0; i < n; i++) model->CharMask[ps[i]->Symbol] = model->EscCount; + } +} + +void UpdatePPMdContext2(PPMdContext *self, PPMdCoreModel *model, PPMdState *state) +{ + model->FoundState = state; + state->Freq += 4; + self->SummFreq += 4; + if(state->Freq > MAX_FREQ) model->RescalePPMdContext(self, model); + model->EscCount++; + model->RunLength = model->InitRL; +} + +void RescalePPMdContext(PPMdContext *self, PPMdCoreModel *model) +{ + PPMdState *states = PPMdContextStates(self, model); + int n = self->LastStateIndex + 1; + + // Bump frequency of found state + model->FoundState->Freq += 4; + + // Divide all frequencies and sort list + int escfreq = self->SummFreq + 4; + int adder = (model->OrderFall == 0 ? 0 : 1); + self->SummFreq = 0; + + for(int i = 0; i < n; i++) + { + escfreq -= states[i].Freq; + states[i].Freq = (states[i].Freq + adder) >> 1; + self->SummFreq += states[i].Freq; + + // Keep states sorted by decreasing frequency + if(i > 0 && states[i].Freq > states[i - 1].Freq) + { + // If not sorted, move current state upwards until list is sorted + PPMdState tmp = states[i]; + + int j = i - 1; + while(j > 0 && tmp.Freq > states[j - 1].Freq) j--; + + memmove(&states[j + 1], &states[j], sizeof(PPMdState) * (i - j)); + states[j] = tmp; + } + } + + // TODO: add better sorting stage here. + + // Drop states whose frequency has fallen to 0 + if(states[n - 1].Freq == 0) + { + int numzeros = 1; + while(numzeros < n && states[n - 1 - numzeros].Freq == 0) numzeros++; + + escfreq += numzeros; + + self->LastStateIndex -= numzeros; + if(self->LastStateIndex == 0) + { + PPMdState tmp = states[0]; + do + { + tmp.Freq = (tmp.Freq + 1) >> 1; + escfreq >>= 1; + } while(escfreq > 1); + + FreeUnits(model->alloc, self->States, (n + 1) >> 1); + model->FoundState = PPMdContextOneState(self); + *model->FoundState = tmp; + + return; + } + + int n0 = (n + 1) >> 1, n1 = (self->LastStateIndex + 2) >> 1; + if(n0 != n1) self->States = ShrinkUnits(model->alloc, self->States, n0, n1); + } + + self->SummFreq += (escfreq + 1) >> 1; + + // The found state is the first one to breach the limit, thus it is the largest and also first + model->FoundState = PPMdContextStates(self, model); +} + +void ClearPPMdModelMask(PPMdCoreModel *self) +{ + self->EscCount = 1; + memset(self->CharMask, 0, sizeof(self->CharMask)); +} diff --git a/ppmd/Context.h b/ppmd/Context.h new file mode 100644 index 0000000..54f6468 --- /dev/null +++ b/ppmd/Context.h @@ -0,0 +1,110 @@ +/* + * Context.h + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef __PPMD_CONTEXT_H__ +#define __PPMD_CONTEXT_H__ + +#include "RangeCoder.h" +#include "SubAllocator.h" + +#include +#include + +#define MAX_O 255 +#define INT_BITS 7 +#define PERIOD_BITS 7 +#define TOT_BITS (INT_BITS + PERIOD_BITS) +#define MAX_FREQ 124 +#define INTERVAL (1 << INT_BITS) +#define BIN_SCALE (1 << TOT_BITS) + +#define SWAP(t1, t2) \ + { \ + PPMdState tmp = (t1); \ + (t1) = (t2); \ + (t2) = tmp; \ + } + +typedef struct SEE2Context +{ // SEE-contexts for PPM-contexts with masked symbols + uint16_t Summ; + uint8_t Shift, Count; +} __attribute__((__packed__)) SEE2Context; + +typedef struct PPMdContext PPMdContext; + +typedef struct PPMdState +{ + uint8_t Symbol, Freq; + uint32_t Successor; +} __attribute__((__packed__)) PPMdState; + +struct PPMdContext +{ + uint8_t LastStateIndex, Flags; + uint16_t SummFreq; + uint32_t States; + uint32_t Suffix; +} __attribute__((__packed__)); + +typedef struct PPMdCoreModel PPMdCoreModel; + +struct PPMdCoreModel +{ + PPMdSubAllocator *alloc; + + PPMdRangeCoder coder; + uint32_t scale; + + PPMdState *FoundState; // found next state transition + int OrderFall, InitEsc, RunLength, InitRL; + uint8_t CharMask[256]; + uint8_t LastMaskIndex, EscCount, PrevSuccess; + + void (*RescalePPMdContext)(PPMdContext *self, PPMdCoreModel *model); +}; + +SEE2Context MakeSEE2(int initval, int count); +unsigned int GetSEE2MeanMasked(SEE2Context *self); +unsigned int GetSEE2Mean(SEE2Context *self); +void UpdateSEE2(SEE2Context *self); + +PPMdContext *PPMdStateSuccessor(PPMdState *self, PPMdCoreModel *model); +void SetPPMdStateSuccessorPointer(PPMdState *self, PPMdContext *newsuccessor, PPMdCoreModel *model); +PPMdState *PPMdContextStates(PPMdContext *self, PPMdCoreModel *model); +void SetPPMdContextStatesPointer(PPMdContext *self, PPMdState *newstates, PPMdCoreModel *model); +PPMdContext *PPMdContextSuffix(PPMdContext *self, PPMdCoreModel *model); +void SetPPMdContextSuffixPointer(PPMdContext *self, PPMdContext *newsuffix, PPMdCoreModel *model); +PPMdState *PPMdContextOneState(PPMdContext *self); + +PPMdContext *NewPPMdContext(PPMdCoreModel *model); +PPMdContext *NewPPMdContextAsChildOf(PPMdCoreModel *model, PPMdContext *suffixcontext, PPMdState *suffixstate, + PPMdState *firststate); + +void PPMdDecodeBinSymbol(PPMdContext *self, PPMdCoreModel *model, uint16_t *bs, int freqlimit, bool altnextbit); +int PPMdDecodeSymbol1(PPMdContext *self, PPMdCoreModel *model, bool greaterorequal); +void UpdatePPMdContext1(PPMdContext *self, PPMdCoreModel *model, PPMdState *state); +void PPMdDecodeSymbol2(PPMdContext *self, PPMdCoreModel *model, SEE2Context *see); +void UpdatePPMdContext2(PPMdContext *self, PPMdCoreModel *model, PPMdState *state); +void RescalePPMdContext(PPMdContext *self, PPMdCoreModel *model); + +void ClearPPMdModelMask(PPMdCoreModel *self); + +#endif diff --git a/ppmd/RangeCoder.c b/ppmd/RangeCoder.c new file mode 100644 index 0000000..5e62cdd --- /dev/null +++ b/ppmd/RangeCoder.c @@ -0,0 +1,109 @@ +/* + * RangeCoder.c + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#include "RangeCoder.h" + +void InitializePPMdRangeCoder(PPMdRangeCoder *self, PPMdReadFunction *readfunc, void *inputcontext, bool uselow, + int bottom) +{ + self->readfunc = readfunc; + self->inputcontext = inputcontext; + self->low = 0; + self->code = 0; + self->range = 0xffffffff; + self->uselow = uselow; + self->bottom = bottom; + for(int i = 0; i < 4; i++) self->code = (self->code << 8) | readfunc(inputcontext); +} + +uint32_t PPMdRangeCoderCurrentCount(PPMdRangeCoder *self, uint32_t scale) +{ + self->range /= scale; + return (self->code - self->low) / self->range; +} + +void RemovePPMdRangeCoderSubRange(PPMdRangeCoder *self, uint32_t lowcount, uint32_t highcount) +{ + if(self->uselow) + self->low += self->range * lowcount; + else + self->code -= self->range * lowcount; + + self->range *= highcount - lowcount; + + NormalizePPMdRangeCoder(self); +} + +int NextWeightedBitFromPPMdRangeCoder(PPMdRangeCoder *self, int weight, int size) +{ + uint32_t val = PPMdRangeCoderCurrentCount(self, size); + + if(val < weight) + { + RemovePPMdRangeCoderSubRange(self, 0, weight); + return 0; + } + else + { + RemovePPMdRangeCoderSubRange(self, weight, size); + return 1; + } +} + +int NextWeightedBitFromPPMdRangeCoder2(PPMdRangeCoder *self, int weight, int shift) +{ + uint32_t threshold = (self->range >> shift) * weight; + + int bit; + if(self->code < threshold) // <= ? + { + bit = 0; + self->range = threshold; + } + else + { + bit = 1; + self->range -= threshold; + self->code -= threshold; + } + + NormalizePPMdRangeCoder(self); + + return bit; +} + +void NormalizePPMdRangeCoder(PPMdRangeCoder *self) +{ + for(;;) + { + if((self->low ^ (self->low + self->range)) >= 0x1000000) + { + if(self->range >= self->bottom) + break; + else + self->range = -self->low & (self->bottom - 1); + } + + int byte = self->readfunc(self->inputcontext); + self->code = (self->code << 8) | byte; + self->range <<= 8; + self->low <<= 8; + } +} diff --git a/ppmd/RangeCoder.h b/ppmd/RangeCoder.h new file mode 100644 index 0000000..2652c99 --- /dev/null +++ b/ppmd/RangeCoder.h @@ -0,0 +1,50 @@ +/* + * RangeCoder.h + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef __PPMD_RANGE_CODER_H__ +#define __PPMD_RANGE_CODER_H__ + +#include +#include + +typedef int PPMdReadFunction(void *context); + +typedef struct PPMdRangeCoder +{ + PPMdReadFunction *readfunc; + void *inputcontext; + + uint32_t low, code, range, bottom; + bool uselow; +} PPMdRangeCoder; + +void InitializePPMdRangeCoder(PPMdRangeCoder *self, PPMdReadFunction *readfunc, void *inputcontext, bool uselow, + int bottom); + +uint32_t PPMdRangeCoderCurrentCount(PPMdRangeCoder *self, uint32_t scale); +void RemovePPMdRangeCoderSubRange(PPMdRangeCoder *self, uint32_t lowcount, uint32_t highcount); + +int NextWeightedBitFromPPMdRangeCoder(PPMdRangeCoder *self, int weight, int size); + +int NextWeightedBitFromPPMdRangeCoder2(PPMdRangeCoder *self, int weight, int shift); + +void NormalizePPMdRangeCoder(PPMdRangeCoder *self); + +#endif diff --git a/ppmd/SubAllocator.h b/ppmd/SubAllocator.h new file mode 100644 index 0000000..0000f5d --- /dev/null +++ b/ppmd/SubAllocator.h @@ -0,0 +1,69 @@ +/* + * SubAllocator.h + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef __PPMD_SUB_ALLOCATOR_H__ +#define __PPMD_SUB_ALLOCATOR_H__ + +#include +#include +#include + +typedef struct PPMdSubAllocator PPMdSubAllocator; + +struct PPMdSubAllocator +{ + void (*Init)(PPMdSubAllocator *self); + uint32_t (*AllocContext)(PPMdSubAllocator *self); + uint32_t (*AllocUnits)(PPMdSubAllocator *self, int num); // 1 unit == 12 bytes, NU <= 128 + uint32_t (*ExpandUnits)(PPMdSubAllocator *self, uint32_t oldoffs, int oldnum); + uint32_t (*ShrinkUnits)(PPMdSubAllocator *self, uint32_t oldoffs, int oldnum, int newnum); + void (*FreeUnits)(PPMdSubAllocator *self, uint32_t offs, int num); +}; + +static inline void InitSubAllocator(PPMdSubAllocator *self) { self->Init(self); }; + +static inline uint32_t AllocContext(PPMdSubAllocator *self) { return self->AllocContext(self); } + +static inline uint32_t AllocUnits(PPMdSubAllocator *self, int num) { return self->AllocUnits(self, num); } + +static inline uint32_t ExpandUnits(PPMdSubAllocator *self, uint32_t oldoffs, int oldnum) +{ return self->ExpandUnits(self, oldoffs, oldnum); } + +static inline uint32_t ShrinkUnits(PPMdSubAllocator *self, uint32_t oldoffs, int oldnum, int newnum) +{ return self->ShrinkUnits(self, oldoffs, oldnum, newnum); } + +static inline void FreeUnits(PPMdSubAllocator *self, uint32_t offs, int num) +{ return self->FreeUnits(self, offs, num); } + +// TODO: Keep pointers as pointers on 32 bit, and offsets on 64 bit. + +static inline void *OffsetToPointer(void *base, uint32_t offset) +{ + if(!offset) return NULL; + return ((uint8_t *)base) + offset; +} + +static inline uint32_t PointerToOffset(void *base, void *pointer) +{ + if(!pointer) return 0; + return (uint32_t)(((uintptr_t)pointer) - (uintptr_t)base); +} + +#endif diff --git a/ppmd/SubAllocatorVariantI.c b/ppmd/SubAllocatorVariantI.c new file mode 100644 index 0000000..90b7e1b --- /dev/null +++ b/ppmd/SubAllocatorVariantI.c @@ -0,0 +1,365 @@ +/* + * SubAllocatorVariantI.c + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#include "SubAllocatorVariantI.h" + +#include +#include + +#define N1 4 +#define N2 4 +#define N3 4 +#define N4 ((128 + 3 - 1 * N1 - 2 * N2 - 3 * N3) / 4) +#define UNIT_SIZE 12 +#define N_INDEXES (N1 + N2 + N3 + N4) + +static PPMdMemoryBlockVariantI *NextBlock(PPMdMemoryBlockVariantI *self, PPMdSubAllocatorVariantI *alloc); +static void SetNextBlock(PPMdMemoryBlockVariantI *self, PPMdMemoryBlockVariantI *newnext, + PPMdSubAllocatorVariantI *alloc); +static bool AreBlocksAvailable(PPMdMemoryBlockVariantI *self); +static void LinkBlockAfter(PPMdMemoryBlockVariantI *self, PPMdMemoryBlockVariantI *p, PPMdSubAllocatorVariantI *alloc); +static void UnlinkBlockAfter(PPMdMemoryBlockVariantI *self, PPMdSubAllocatorVariantI *alloc); +static void *RemoveBlockAfter(PPMdMemoryBlockVariantI *self, PPMdSubAllocatorVariantI *alloc); +static void InsertBlockAfter(PPMdMemoryBlockVariantI *self, void *pv, int NU, PPMdSubAllocatorVariantI *alloc); + +static unsigned int I2B(PPMdSubAllocatorVariantI *self, int index); +static void SplitBlock(PPMdSubAllocatorVariantI *self, void *pv, int oldindex, int newindex); +// static uint32_t GetUsedMemory(PPMdSubAllocatorVariantI *self); + +static void InitVariantI(PPMdSubAllocatorVariantI *self); +static uint32_t AllocContextVariantI(PPMdSubAllocatorVariantI *self); +static uint32_t AllocUnitsVariantI(PPMdSubAllocatorVariantI *self, int num); +static uint32_t _AllocUnits(PPMdSubAllocatorVariantI *self, int index); +static uint32_t ExpandUnitsVariantI(PPMdSubAllocatorVariantI *self, uint32_t oldoffs, int oldnum); +static uint32_t ShrinkUnitsVariantI(PPMdSubAllocatorVariantI *self, uint32_t oldoffs, int oldnum, int newnum); +static void FreeUnitsVariantI(PPMdSubAllocatorVariantI *self, uint32_t offs, int num); + +static inline void GlueFreeBlocks(PPMdSubAllocatorVariantI *self); + +PPMdSubAllocatorVariantI *CreateSubAllocatorVariantI(int size) +{ + PPMdSubAllocatorVariantI *self = malloc(sizeof(PPMdSubAllocatorVariantI) + size); + if(!self) return NULL; + + self->core.Init = (void *)InitVariantI; + self->core.AllocContext = (void *)AllocContextVariantI; + self->core.AllocUnits = (void *)AllocUnitsVariantI; + self->core.ExpandUnits = (void *)ExpandUnitsVariantI; + self->core.ShrinkUnits = (void *)ShrinkUnitsVariantI; + self->core.FreeUnits = (void *)FreeUnitsVariantI; + + self->SubAllocatorSize = size; + + return self; +} + +void FreeSubAllocatorVariantI(PPMdSubAllocatorVariantI *self) { free(self); } + +static void InitVariantI(PPMdSubAllocatorVariantI *self) +{ + memset(self->BList, 0, sizeof(self->BList)); + + self->pText = self->HeapStart; + self->HighUnit = self->HeapStart + self->SubAllocatorSize; + unsigned int diff = UNIT_SIZE * (self->SubAllocatorSize / 8 / UNIT_SIZE * 7); + self->LowUnit = self->UnitsStart = self->HighUnit - diff; + self->GlueCount = 0; + + for(int i = 0; i < N1; i++) self->Index2Units[i] = 1 + i; + for(int i = 0; i < N2; i++) self->Index2Units[N1 + i] = 2 + N1 + i * 2; + for(int i = 0; i < N3; i++) self->Index2Units[N1 + N2 + i] = 3 + N1 + 2 * N2 + i * 3; + for(int i = 0; i < N4; i++) self->Index2Units[N1 + N2 + N3 + i] = 4 + N1 + 2 * N2 + 3 * N3 + i * 4; + + int i = 0; + for(int k = 0; k < 128; k++) + { + if(self->Index2Units[i] < k + 1) i++; + self->Units2Index[k] = i; + } +} + +static uint32_t AllocContextVariantI(PPMdSubAllocatorVariantI *self) +{ + if(self->HighUnit != self->LowUnit) + { + self->HighUnit -= UNIT_SIZE; + return PointerToOffset(self, self->HighUnit); + } + else if(AreBlocksAvailable(&self->BList[0])) + return PointerToOffset(self, RemoveBlockAfter(&self->BList[0], self)); + else + return _AllocUnits(self, 0); +} + +static uint32_t AllocUnitsVariantI(PPMdSubAllocatorVariantI *self, int num) +{ + int index = self->Units2Index[num - 1]; + + if(AreBlocksAvailable(&self->BList[index])) + return PointerToOffset(self, RemoveBlockAfter(&self->BList[index], self)); + + void *units = self->LowUnit; + self->LowUnit += I2B(self, index); + if(self->LowUnit <= self->HighUnit) return PointerToOffset(self, units); + + self->LowUnit -= I2B(self, index); + + return _AllocUnits(self, index); +} + +static uint32_t _AllocUnits(PPMdSubAllocatorVariantI *self, int index) +{ + if(self->GlueCount == 0) + { + GlueFreeBlocks(self); + if(AreBlocksAvailable(&self->BList[index])) + return PointerToOffset(self, RemoveBlockAfter(&self->BList[index], self)); + } + + for(int i = index + 1; i < N_INDEXES; i++) + { + if(AreBlocksAvailable(&self->BList[i])) + { + void *units = RemoveBlockAfter(&self->BList[i], self); + SplitBlock(self, units, i, index); + return PointerToOffset(self, units); + } + } + + self->GlueCount--; + + int i = I2B(self, index); + if(self->UnitsStart - self->pText > i) + { + self->UnitsStart -= i; + return PointerToOffset(self, self->UnitsStart); + } + + return 0; +} + +static uint32_t ExpandUnitsVariantI(PPMdSubAllocatorVariantI *self, uint32_t oldoffs, int oldnum) +{ + void *oldptr = OffsetToPointer(self, oldoffs); + int oldindex = self->Units2Index[oldnum - 1]; + int newindex = self->Units2Index[oldnum]; + if(oldindex == newindex) return oldoffs; + + uint32_t offs = AllocUnitsVariantI(self, oldnum + 1); + if(offs) + { + memcpy(OffsetToPointer(self, offs), oldptr, oldnum * UNIT_SIZE); + InsertBlockAfter(&self->BList[oldindex], oldptr, oldnum, self); + } + return offs; +} + +static uint32_t ShrinkUnitsVariantI(PPMdSubAllocatorVariantI *self, uint32_t oldoffs, int oldnum, int newnum) +{ + void *oldptr = OffsetToPointer(self, oldoffs); + int oldindex = self->Units2Index[oldnum - 1]; + int newindex = self->Units2Index[newnum - 1]; + if(oldindex == newindex) return oldoffs; + + if(AreBlocksAvailable(&self->BList[newindex])) + { + void *ptr = RemoveBlockAfter(&self->BList[newindex], self); + memcpy(ptr, oldptr, newnum * UNIT_SIZE); + InsertBlockAfter(&self->BList[oldindex], oldptr, self->Index2Units[oldindex], self); + return PointerToOffset(self, ptr); + } + else + { + SplitBlock(self, oldptr, oldindex, newindex); + return oldoffs; + } +} + +static void FreeUnitsVariantI(PPMdSubAllocatorVariantI *self, uint32_t offs, int num) +{ + int index = self->Units2Index[num - 1]; + InsertBlockAfter(&self->BList[index], OffsetToPointer(self, offs), self->Index2Units[index], self); +} + +uint32_t GetUsedMemoryVariantI(PPMdSubAllocatorVariantI *self) +{ + size_t size = self->SubAllocatorSize - (self->HighUnit - self->LowUnit) - (self->UnitsStart - self->pText); + + for(int i = 0; i < N_INDEXES; i++) size -= UNIT_SIZE * self->Index2Units[i] * self->BList[i].Stamp; + + return (uint32_t)size; +} + +void SpecialFreeUnitVariantI(PPMdSubAllocatorVariantI *self, uint32_t offs) +{ + void *ptr = OffsetToPointer(self, offs); + if((uint8_t *)ptr == self->UnitsStart) + { + *(uint32_t *)ptr = 0xffffffff; + self->UnitsStart += UNIT_SIZE; + } + else + InsertBlockAfter(&self->BList[0], ptr, 1, self); +} + +uint32_t MoveUnitsUpVariantI(PPMdSubAllocatorVariantI *self, uint32_t oldoffs, int num) +{ + void *oldptr = OffsetToPointer(self, oldoffs); + int index = self->Units2Index[num - 1]; + + if((uint8_t *)oldptr > self->UnitsStart + 16 * 1024 || oldoffs > self->BList[index].next) return oldoffs; + + void *ptr = RemoveBlockAfter(&self->BList[index], self); + memcpy(ptr, oldptr, num * UNIT_SIZE); + + int newnum = self->Index2Units[index]; + if((uint8_t *)oldptr != self->UnitsStart) + InsertBlockAfter(&self->BList[index], oldptr, newnum, self); + else + self->UnitsStart += newnum * UNIT_SIZE; + + return PointerToOffset(self, ptr); +} + +void ExpandTextAreaVariantI(PPMdSubAllocatorVariantI *self) +{ + PPMdMemoryBlockVariantI *p; + unsigned int Count[N_INDEXES]; + memset(Count, 0, sizeof(Count)); + + while((p = (PPMdMemoryBlockVariantI *)self->UnitsStart)->Stamp == 0xffffffff) + { + PPMdMemoryBlockVariantI *pm = p; + self->UnitsStart = (uint8_t *)(pm + pm->NU); + Count[self->Units2Index[pm->NU - 1]]++; + pm->Stamp = 0; + } + + for(int i = 0; i < N_INDEXES; i++) + for(p = &self->BList[i]; Count[i] != 0; p = NextBlock(p, self)) + while(!NextBlock(p, self)->Stamp) + { + UnlinkBlockAfter(p, self); + self->BList[i].Stamp--; + if(!--Count[i]) break; + } +} + +static inline void GlueFreeBlocks(PPMdSubAllocatorVariantI *self) +{ + if(self->LowUnit != self->HighUnit) *self->LowUnit = 0; + + PPMdMemoryBlockVariantI s0, *p0 = &s0; + s0.next = 0; + for(int i = 0; i < N_INDEXES; i++) + { + while(AreBlocksAvailable(&self->BList[i])) + { + PPMdMemoryBlockVariantI *p = (PPMdMemoryBlockVariantI *)RemoveBlockAfter(&self->BList[i], self); + if(!p->NU) continue; + PPMdMemoryBlockVariantI *p1; + while((p1 = p + p->NU)->Stamp == 0xffffffff) + { + p->NU += p1->NU; + p1->NU = 0; + } + LinkBlockAfter(p0, p, self); + p0 = p; + } + } + + while(AreBlocksAvailable(&s0)) + { + PPMdMemoryBlockVariantI *p = RemoveBlockAfter(&s0, self); + int sz = p->NU; + if(!sz) continue; + + while(sz > 128) + { + InsertBlockAfter(&self->BList[N_INDEXES - 1], p, 128, self); + sz -= 128; + p += 128; + } + + int i = self->Units2Index[sz - 1]; + if(self->Index2Units[i] != sz) + { + i--; + int k = sz - self->Index2Units[i]; + InsertBlockAfter(&self->BList[k - 1], p + (sz - k), k, self); + } + InsertBlockAfter(&self->BList[i], p, self->Index2Units[i], self); + } + self->GlueCount = 1 << 13; +} + +static PPMdMemoryBlockVariantI *NextBlock(PPMdMemoryBlockVariantI *self, PPMdSubAllocatorVariantI *alloc) +{ return OffsetToPointer(&alloc->core, self->next); } + +static void SetNextBlock(PPMdMemoryBlockVariantI *self, PPMdMemoryBlockVariantI *newnext, + PPMdSubAllocatorVariantI *alloc) +{ self->next = PointerToOffset(&alloc->core, newnext); } + +static bool AreBlocksAvailable(PPMdMemoryBlockVariantI *self) { return self->next != 0; } + +static void LinkBlockAfter(PPMdMemoryBlockVariantI *self, PPMdMemoryBlockVariantI *p, PPMdSubAllocatorVariantI *alloc) +{ + SetNextBlock(p, NextBlock(self, alloc), alloc); + SetNextBlock(self, p, alloc); +} + +static void UnlinkBlockAfter(PPMdMemoryBlockVariantI *self, PPMdSubAllocatorVariantI *alloc) +{ SetNextBlock(self, NextBlock(NextBlock(self, alloc), alloc), alloc); } + +static void *RemoveBlockAfter(PPMdMemoryBlockVariantI *self, PPMdSubAllocatorVariantI *alloc) +{ + PPMdMemoryBlockVariantI *p = NextBlock(self, alloc); + UnlinkBlockAfter(self, alloc); + self->Stamp--; + return p; +} + +static void InsertBlockAfter(PPMdMemoryBlockVariantI *self, void *pv, int NU, PPMdSubAllocatorVariantI *alloc) +{ + PPMdMemoryBlockVariantI *p = (PPMdMemoryBlockVariantI *)pv; + LinkBlockAfter(self, p, alloc); + p->Stamp = 0xffffffff; + p->NU = NU; + self->Stamp++; +} + +static inline unsigned int I2B(PPMdSubAllocatorVariantI *self, int index) +{ return UNIT_SIZE * self->Index2Units[index]; } + +static void SplitBlock(PPMdSubAllocatorVariantI *self, void *pv, int oldindex, int newindex) +{ + uint8_t *p = ((uint8_t *)pv) + I2B(self, newindex); + + int diff = self->Index2Units[oldindex] - self->Index2Units[newindex]; + int i = self->Units2Index[diff - 1]; + if(self->Index2Units[i] != diff) + { + int k = self->Index2Units[--i]; + InsertBlockAfter(&self->BList[i], p, k, self); + p += k * UNIT_SIZE; + diff -= k; + } + InsertBlockAfter(&self->BList[self->Units2Index[diff - 1]], p, diff, self); +} diff --git a/ppmd/SubAllocatorVariantI.h b/ppmd/SubAllocatorVariantI.h new file mode 100644 index 0000000..c27589b --- /dev/null +++ b/ppmd/SubAllocatorVariantI.h @@ -0,0 +1,52 @@ +/* + * SubAllocatorVariantI.h + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef __PPMD_SUB_ALLOCATOR_VARIANT_I_H__ +#define __PPMD_SUB_ALLOCATOR_VARIANT_I_H__ + +#include "SubAllocator.h" + +typedef struct PPMdMemoryBlockVariantI +{ + uint32_t Stamp; + uint32_t next; + uint32_t NU; +} __attribute__((packed)) PPMdMemoryBlockVariantI; + +typedef struct PPMdSubAllocatorVariantI +{ + PPMdSubAllocator core; + + uint32_t GlueCount, SubAllocatorSize; + uint8_t Index2Units[38], Units2Index[128]; // constants + uint8_t *pText, *UnitsStart, *LowUnit, *HighUnit; + PPMdMemoryBlockVariantI BList[38]; + uint8_t HeapStart[0]; +} PPMdSubAllocatorVariantI; + +PPMdSubAllocatorVariantI *CreateSubAllocatorVariantI(int size); +void FreeSubAllocatorVariantI(PPMdSubAllocatorVariantI *self); + +uint32_t GetUsedMemoryVariantI(PPMdSubAllocatorVariantI *self); +void SpecialFreeUnitVariantI(PPMdSubAllocatorVariantI *self, uint32_t offs); +uint32_t MoveUnitsUpVariantI(PPMdSubAllocatorVariantI *self, uint32_t oldoffs, int num); +void ExpandTextAreaVariantI(PPMdSubAllocatorVariantI *self); + +#endif diff --git a/ppmd/VariantI.c b/ppmd/VariantI.c new file mode 100644 index 0000000..db429c4 --- /dev/null +++ b/ppmd/VariantI.c @@ -0,0 +1,886 @@ +/* + * VariantI.c + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#include "VariantI.h" + +#include + +#define UP_FREQ 5 +#define O_BOUND 9 + +static void RestartModel(PPMdModelVariantI *self); + +static void UpdateModel(PPMdModelVariantI *self, PPMdContext *mincontext); +static PPMdContext *CreateSuccessors(PPMdModelVariantI *self, bool skip, PPMdState *p1, PPMdContext *mincontext); +static PPMdContext *ReduceOrder(PPMdModelVariantI *self, PPMdState *state, PPMdContext *startcontext); +static void RestoreModel(PPMdModelVariantI *self, PPMdContext *currcontext, PPMdContext *mincontext, + PPMdContext *FSuccessor); + +static void ShrinkContext(PPMdContext *self, int newlastindex, bool scale, PPMdModelVariantI *model); +static PPMdContext *CutOffContext(PPMdContext *self, int order, PPMdModelVariantI *model); +static PPMdContext *RemoveBinConts(PPMdContext *self, int order, PPMdModelVariantI *model); + +static void DecodeBinSymbolVariantI(PPMdContext *self, PPMdModelVariantI *model); +static void DecodeSymbol1VariantI(PPMdContext *self, PPMdModelVariantI *model); +static void DecodeSymbol2VariantI(PPMdContext *self, PPMdModelVariantI *model); + +static void RescalePPMdContextVariantI(PPMdContext *self, PPMdModelVariantI *model); + +void StartPPMdModelVariantI(PPMdModelVariantI *self, PPMdReadFunction *readfunc, void *inputcontext, + PPMdSubAllocatorVariantI *alloc, int maxorder, int restoration) +{ + InitializePPMdRangeCoder(&self->core.coder, readfunc, inputcontext, true, 0x8000); + + if(maxorder < 2) // TODO: solid mode + { + memset(self->core.CharMask, 0, sizeof(self->core.CharMask)); + self->core.OrderFall = self->MaxOrder; + for(PPMdContext *pc = self->MaxContext; pc->Suffix; pc = PPMdContextSuffix(pc, &self->core)) + self->core.OrderFall--; + return; + } + + self->alloc = alloc; + self->core.alloc = &alloc->core; + + self->core.RescalePPMdContext = (void *)RescalePPMdContextVariantI; + + self->MaxOrder = maxorder; + self->MRMethod = restoration; + self->core.EscCount = 1; + + self->NS2BSIndx[0] = 2 * 0; + self->NS2BSIndx[1] = 2 * 1; + for(int i = 2; i < 11; i++) self->NS2BSIndx[i] = 2 * 2; + for(int i = 11; i < 256; i++) self->NS2BSIndx[i] = 2 * 3; + + for(int i = 0; i < UP_FREQ; i++) self->QTable[i] = i; + int m = UP_FREQ, k = 1, step = 1; + for(int i = UP_FREQ; i < 260; i++) + { + self->QTable[i] = m; + if(!--k) + { + m++; + step++; + k = step; + } + } + + self->DummySEE2Cont.Summ = 0xaf8f; + // self->DummySEE2Cont.Shift=0xac; + self->DummySEE2Cont.Count = 0x84; + self->DummySEE2Cont.Shift = PERIOD_BITS; + + self->endofstream = false; + + RestartModel(self); +} + +static void RestartModel(PPMdModelVariantI *self) +{ + InitSubAllocator(self->core.alloc); + + memset(self->core.CharMask, 0, sizeof(self->core.CharMask)); + + self->core.PrevSuccess = 0; + self->core.OrderFall = self->MaxOrder; + self->core.RunLength = self->core.InitRL = -((self->MaxOrder < 12) ? self->MaxOrder : 12) - 1; + + self->MaxContext = NewPPMdContext(&self->core); + self->MaxContext->LastStateIndex = 255; + self->MaxContext->SummFreq = 257; + self->MaxContext->States = AllocUnits(self->core.alloc, 256 / 2); + + PPMdState *maxstates = PPMdContextStates(self->MaxContext, &self->core); + for(int i = 0; i < 256; i++) + { + maxstates[i].Symbol = i; + maxstates[i].Freq = 1; + maxstates[i].Successor = 0; + } + + static const uint16_t InitBinEsc[8] = {0x3cdd, 0x1f3f, 0x59bf, 0x48f3, 0x64a1, 0x5abc, 0x6632, 0x6051}; + + int i = 0; + for(int m = 0; m < 25; m++) + { + while(self->QTable[i] == m) i++; + for(int k = 0; k < 8; k++) self->BinSumm[m][k] = BIN_SCALE - InitBinEsc[k] / (i + 1); + for(int k = 8; k < 64; k += 8) memcpy(&self->BinSumm[m][k], &self->BinSumm[m][0], 8 * sizeof(uint16_t)); + } + + i = 0; + for(int m = 0; m < 24; m++) + { + while(self->QTable[i + 3] == m + 3) i++; + for(int k = 0; k < 32; k++) self->SEE2Cont[m][k] = MakeSEE2(2 * i + 5, 7); + } +} + +int NextPPMdVariantIByte(PPMdModelVariantI *self) +{ + if(self->endofstream) return -1; + + PPMdContext *mincontext = self->MaxContext; + + if(mincontext->LastStateIndex != 0) + DecodeSymbol1VariantI(mincontext, self); + else + DecodeBinSymbolVariantI(mincontext, self); + + while(!self->core.FoundState) + { + do + { + self->core.OrderFall++; + mincontext = PPMdContextSuffix(mincontext, &self->core); + if(!mincontext) + { + self->endofstream = true; + return -1; + } + } while(mincontext->LastStateIndex == self->core.LastMaskIndex); + + DecodeSymbol2VariantI(mincontext, self); + } + + uint8_t byte = self->core.FoundState->Symbol; + + if(self->core.OrderFall == 0 && + (uint8_t *)PPMdStateSuccessor(self->core.FoundState, &self->core) >= self->alloc->UnitsStart) + { + self->MaxContext = PPMdStateSuccessor(self->core.FoundState, &self->core); + // PrefetchData(MaxContext) + } + else + { + UpdateModel(self, mincontext); + // PrefetchData(MaxContext) + if(self->core.EscCount == 0) ClearPPMdModelMask(&self->core); + } + + return byte; +} + +static void UpdateModel(PPMdModelVariantI *self, PPMdContext *mincontext) +{ + PPMdState fs = *self->core.FoundState; + PPMdState *state = NULL; + PPMdContext *currcontext = self->MaxContext; + + if(fs.Freq < MAX_FREQ / 4 && mincontext->Suffix) + { + PPMdContext *context = PPMdContextSuffix(mincontext, &self->core); + if(context->LastStateIndex != 0) + { + state = PPMdContextStates(context, &self->core); + + if(state->Symbol != fs.Symbol) + { + do state++; + while(state->Symbol != fs.Symbol); + + if(state[0].Freq >= state[-1].Freq) + { + SWAP(state[0], state[-1]); + state--; + } + } + + if(state->Freq < MAX_FREQ - 9) + { + state->Freq += 2; + context->SummFreq += 2; + } + } + else + { + state = PPMdContextOneState(context); + if(state->Freq < 32) state->Freq++; + } + } + + if(self->core.OrderFall == 0 && fs.Successor) + { + PPMdContext *newsuccessor = CreateSuccessors(self, true, state, mincontext); + SetPPMdStateSuccessorPointer(self->core.FoundState, newsuccessor, &self->core); + if(!newsuccessor) goto RESTART_MODEL; + self->MaxContext = newsuccessor; + return; + } + + *self->alloc->pText++ = fs.Symbol; + PPMdContext *Successor = (PPMdContext *)self->alloc->pText; + + if(self->alloc->pText >= self->alloc->UnitsStart) goto RESTART_MODEL; + + if(fs.Successor) + { + if((uint8_t *)PPMdStateSuccessor(&fs, &self->core) < self->alloc->UnitsStart) + { + SetPPMdStateSuccessorPointer(&fs, CreateSuccessors(self, false, state, mincontext), &self->core); + } + } + else + { + SetPPMdStateSuccessorPointer(&fs, ReduceOrder(self, state, mincontext), &self->core); + } + + if(!fs.Successor) goto RESTART_MODEL; + + if(--self->core.OrderFall == 0) + { + Successor = PPMdStateSuccessor(&fs, &self->core); + if(self->MaxContext != mincontext) self->alloc->pText--; + } + else if(self->MRMethod > MRM_FREEZE) + { + Successor = PPMdStateSuccessor(&fs, &self->core); + self->alloc->pText = self->alloc->HeapStart; + self->core.OrderFall = 0; + } + + int minnum = mincontext->LastStateIndex + 1; + int s0 = mincontext->SummFreq - minnum - (fs.Freq - 1); + uint8_t flag = fs.Symbol >= 0x40 ? 8 : 0; + + for(; currcontext != mincontext; currcontext = PPMdContextSuffix(currcontext, &self->core)) + { + int currnum = currcontext->LastStateIndex + 1; + if(currnum != 1) + { + if((currnum & 1) == 0) + { + uint32_t states = ExpandUnits(self->core.alloc, currcontext->States, currnum >> 1); + if(!states) goto RESTART_MODEL; + currcontext->States = states; + } + if(3 * currnum - 1 < minnum) currcontext->SummFreq++; + } + else + { + PPMdState *states = OffsetToPointer(self->core.alloc, AllocUnits(self->core.alloc, 1)); + if(!states) goto RESTART_MODEL; + states[0] = *(PPMdContextOneState(currcontext)); + SetPPMdContextStatesPointer(currcontext, states, &self->core); + + if(states[0].Freq < MAX_FREQ / 4 - 1) + states[0].Freq *= 2; + else + states[0].Freq = MAX_FREQ - 4; + + currcontext->SummFreq = states[0].Freq + self->core.InitEsc + (minnum > 3 ? 1 : 0); + } + + unsigned int cf = 2 * fs.Freq * (currcontext->SummFreq + 6); + unsigned int sf = s0 + currcontext->SummFreq; + unsigned int freq; + + if(cf < 6 * sf) + { + if(cf >= 4 * sf) + freq = 3; + else if(cf > sf) + freq = 2; + else + freq = 1; + currcontext->SummFreq += 4; + } + else + { + if(cf > 15 * sf) + freq = 7; + else if(cf > 12 * sf) + freq = 6; + else if(cf > 9 * sf) + freq = 5; + else + freq = 4; + currcontext->SummFreq += freq; + } + + currcontext->LastStateIndex++; + PPMdState *currstates = PPMdContextStates(currcontext, &self->core); + PPMdState *new = &currstates[currcontext->LastStateIndex]; + SetPPMdStateSuccessorPointer(new, Successor, &self->core); + new->Symbol = fs.Symbol; + new->Freq = freq; + currcontext->Flags |= flag; + } + + self->MaxContext = PPMdStateSuccessor(&fs, &self->core); + + return; + +RESTART_MODEL: + RestoreModel(self, currcontext, mincontext, PPMdStateSuccessor(&fs, &self->core)); +} + +static PPMdContext *CreateSuccessors(PPMdModelVariantI *self, bool skip, PPMdState *state, PPMdContext *context) +{ + PPMdContext *upbranch = PPMdStateSuccessor(self->core.FoundState, &self->core); + PPMdState *statelist[MAX_O]; + uint8_t sym = self->core.FoundState->Symbol; + int n = 0; + + if(!skip) + { + statelist[n++] = self->core.FoundState; + if(!context->Suffix) goto skip; + } + + if(state) + { + context = PPMdContextSuffix(context, &self->core); + if(PPMdStateSuccessor(state, &self->core) != upbranch) + { + context = PPMdStateSuccessor(state, &self->core); + goto skip; + } + statelist[n++] = state; + if(!context->Suffix) goto skip; + } + + do + { + context = PPMdContextSuffix(context, &self->core); + if(context->LastStateIndex != 0) + { + state = PPMdContextStates(context, &self->core); + while(state->Symbol != sym) state++; + + if(state->Freq < MAX_FREQ - 9) + { + state->Freq++; + context->SummFreq++; + } + } + else + { + state = PPMdContextOneState(context); + state->Freq += (!PPMdContextSuffix(context, &self->core)->LastStateIndex & (state->Freq < 24)); + } + + if(PPMdStateSuccessor(state, &self->core) != upbranch) + { + context = PPMdStateSuccessor(state, &self->core); + break; + } + statelist[n++] = state; + } while(context->Suffix); + +skip: + + if(n == 0) return context; + + PPMdContext ct; + uint8_t newsym = *(uint8_t *)upbranch; + + ct.LastStateIndex = 0; + ct.Flags = 0; + if(sym >= 0x40) ct.Flags |= 0x10; + if(newsym >= 0x40) ct.Flags |= 0x08; + + PPMdState *onestate = PPMdContextOneState(&ct); + onestate->Symbol = newsym; + SetPPMdStateSuccessorPointer(onestate, (PPMdContext *)(((uint8_t *)upbranch) + 1), &self->core); + + if(context->LastStateIndex != 0) + { + state = PPMdContextStates(context, &self->core); + while(state->Symbol != newsym) state++; + + int cf = state->Freq - 1; + int s0 = context->SummFreq - context->LastStateIndex - cf; + + if(2 * cf <= s0) + { + if(5 * cf > s0) + onestate->Freq = 2; + else + onestate->Freq = 1; + } + else + onestate->Freq = 1 + ((cf + 2 * s0 - 3) / s0); + } + else + onestate->Freq = PPMdContextOneState(context)->Freq; + + for(int i = n - 1; i >= 0; i--) + { + PPMdContext *newcontext = (PPMdContext *)OffsetToPointer(self->core.alloc, AllocContext(self->core.alloc)); + if(!newcontext) return NULL; + + memcpy(newcontext, &ct, 8); + SetPPMdContextSuffixPointer(newcontext, context, &self->core); + SetPPMdStateSuccessorPointer(statelist[i], newcontext, &self->core); + + context = newcontext; + } + + return context; +} + +static PPMdContext *ReduceOrder(PPMdModelVariantI *self, PPMdState *state, PPMdContext *startcontext) +{ + PPMdState *statelist[MAX_O]; + PPMdContext *context = startcontext, *upbranch = (PPMdContext *)self->alloc->pText; + uint8_t sym = self->core.FoundState->Symbol; + + int n = 0; + + statelist[n++] = self->core.FoundState; + self->core.OrderFall++; + + if(state) + { + context = PPMdContextSuffix(context, &self->core); + if(state->Successor) goto skip; + statelist[n++] = state; + self->core.OrderFall++; + } + + for(;;) + { + if(!context->Suffix) + { + if(self->MRMethod > MRM_FREEZE) + { + for(int i = 0; i < n; i++) SetPPMdStateSuccessorPointer(statelist[i], context, &self->core); + self->alloc->pText = self->alloc->HeapStart + 1; + self->core.OrderFall = 1; + } + else + { + for(int i = 0; i < n; i++) SetPPMdStateSuccessorPointer(statelist[i], upbranch, &self->core); + } + return context; + } + + context = PPMdContextSuffix(context, &self->core); + + if(context->LastStateIndex) + { + state = PPMdContextStates(context, &self->core); + while(state->Symbol != sym) state++; + + if(state->Freq < MAX_FREQ - 9) + { + state->Freq += 2; + context->SummFreq += 2; + } + } + else + { + state = PPMdContextOneState(context); + if(state->Freq < 32) state->Freq++; + } + + if(state->Successor) break; + + statelist[n++] = state; + self->core.OrderFall++; + } +skip: + + if(self->MRMethod > MRM_FREEZE) + { + PPMdContext *successor = PPMdStateSuccessor(state, &self->core); + for(int i = 0; i < n; i++) SetPPMdStateSuccessorPointer(statelist[i], successor, &self->core); + + self->alloc->pText = self->alloc->HeapStart + 1; + self->core.OrderFall = 1; + + return successor; + } + else + { + for(int i = 0; i < n; i++) SetPPMdStateSuccessorPointer(statelist[i], upbranch, &self->core); + } + + if(PPMdStateSuccessor(state, &self->core) <= upbranch) + { + PPMdState *tmp = self->core.FoundState; + self->core.FoundState = state; + SetPPMdStateSuccessorPointer(state, CreateSuccessors(self, false, NULL, context), &self->core); + self->core.FoundState = tmp; + } + + if(self->core.OrderFall == 1 && startcontext == self->MaxContext) + { + self->core.FoundState->Successor = state->Successor; + self->alloc->pText--; + } + + return PPMdStateSuccessor(state, &self->core); +} + +static void RestoreModel(PPMdModelVariantI *self, PPMdContext *currcontext, PPMdContext *mincontext, + PPMdContext *FSuccessor) +{ + self->alloc->pText = self->alloc->HeapStart; + + PPMdContext *context = self->MaxContext; + while(context != currcontext) + { + if(context->LastStateIndex == 1) + { + PPMdState state = *(PPMdContextStates(context, &self->core)); + SpecialFreeUnitVariantI(self->alloc, context->States); + + state.Freq = (state.Freq + 11) >> 3; + *(PPMdContextOneState(context)) = state; + + context->LastStateIndex = 0; + context->Flags &= 0x10; + if(state.Symbol >= 0x40) context->Flags += 0x08; + } + else + { + ShrinkContext(context, context->LastStateIndex - 1, false, self); + } + + context = PPMdContextSuffix(context, &self->core); + } + + while(context != mincontext) + { + if(!context->LastStateIndex) + { + PPMdContextOneState(context)->Freq = (PPMdContextOneState(context)->Freq + 1) >> 1; + } + else + { + context->SummFreq += 4; + if(context->SummFreq > 128 + 4 * context->LastStateIndex) + ShrinkContext(context, context->LastStateIndex, true, self); + } + + context = PPMdContextSuffix(context, &self->core); + } + + if(self->MRMethod > MRM_FREEZE) + { + self->MaxContext = FSuccessor; + if(!(self->alloc->BList[1].Stamp & 1)) self->alloc->GlueCount++; + } + else if(self->MRMethod == MRM_FREEZE) + { + while(self->MaxContext->Suffix) self->MaxContext = PPMdContextSuffix(self->MaxContext, &self->core); + + RemoveBinConts(self->MaxContext, 0, self); + self->MRMethod = self->MRMethod + 1; + self->alloc->GlueCount = 0; + self->core.OrderFall = self->MaxOrder; + } + else if(self->MRMethod == MRM_RESTART || GetUsedMemoryVariantI(self->alloc) < (self->alloc->SubAllocatorSize >> 1)) + { + RestartModel(self); + self->core.EscCount = 0; + } + else + { + while(self->MaxContext->Suffix) self->MaxContext = PPMdContextSuffix(self->MaxContext, &self->core); + do + { + CutOffContext(self->MaxContext, 0, self); + ExpandTextAreaVariantI(self->alloc); + } while(GetUsedMemoryVariantI(self->alloc) > 3 * (self->alloc->SubAllocatorSize >> 2)); + + self->alloc->GlueCount = 0; + self->core.OrderFall = self->MaxOrder; + } +} + +static void ShrinkContext(PPMdContext *self, int newlastindex, bool scale, PPMdModelVariantI *model) +{ + self->States = + ShrinkUnits(model->core.alloc, self->States, (self->LastStateIndex + 2) >> 1, (newlastindex + 2) >> 1); + self->LastStateIndex = newlastindex; + + if(scale) + self->Flags &= 0x14; + else + self->Flags &= 0x10; + + PPMdState *states = PPMdContextStates(self, &model->core); + int escfreq = self->SummFreq; + self->SummFreq = 0; + + for(int i = 0; i <= self->LastStateIndex; i++) + { + escfreq -= states[i].Freq; + if(scale) states[i].Freq = (states[i].Freq + 1) >> 1; + self->SummFreq += states[i].Freq; + if(states[i].Symbol >= 0x40) self->Flags |= 0x08; + } + + if(scale) escfreq = (escfreq + 1) >> 1; + + self->SummFreq += escfreq; +} + +static PPMdContext *CutOffContext(PPMdContext *self, int order, PPMdModelVariantI *model) +{ + if(self->LastStateIndex == 0) + { + PPMdState *onestate = PPMdContextOneState(self); + if((uint8_t *)PPMdStateSuccessor(onestate, &model->core) >= model->alloc->UnitsStart) + { + if(order < model->MaxOrder) + { + // PrefetchData(p->Successor); + SetPPMdStateSuccessorPointer( + onestate, CutOffContext(PPMdStateSuccessor(onestate, &model->core), order + 1, model), + &model->core); + } + else + onestate->Successor = 0; + + if(!onestate->Successor && order > O_BOUND) + { + SpecialFreeUnitVariantI(model->alloc, PointerToOffset(model->core.alloc, self)); + return NULL; + } + + return self; + } + else + { + SpecialFreeUnitVariantI(model->alloc, PointerToOffset(model->core.alloc, self)); + return NULL; + } + } + // PrefetchData(self->States); + + int oldnum = (self->LastStateIndex + 2) >> 1; + self->States = MoveUnitsUpVariantI(model->alloc, self->States, oldnum); + + int n = self->LastStateIndex; + PPMdState *states = PPMdContextStates(self, &model->core); + for(int i = n; i >= 0; i--) + { + if((uint8_t *)PPMdStateSuccessor(&states[i], &model->core) < model->alloc->UnitsStart) + { + states[i].Successor = 0; + SWAP(states[i], states[n]); + n--; + } + else if(order < model->MaxOrder) + { + // PrefetchData(state->Successor); + SetPPMdStateSuccessorPointer(&states[i], + CutOffContext(PPMdStateSuccessor(&states[i], &model->core), order + 1, model), + &model->core); + } + else + states[i].Successor = 0; + } + + if(n != self->LastStateIndex && order) + { + if(n < 0) + { + FreeUnits(model->core.alloc, self->States, oldnum); + SpecialFreeUnitVariantI(model->alloc, PointerToOffset(model->core.alloc, self)); + return NULL; + } + else if(n == 0) + { + PPMdState state = *(PPMdContextStates(self, &model->core)); + FreeUnits(model->core.alloc, self->States, oldnum); + + state.Freq = (state.Freq + 11) >> 3; + *(PPMdContextOneState(self)) = state; + + self->LastStateIndex = 0; + self->Flags &= 0x10; + if(state.Symbol >= 0x40) self->Flags += 0x08; + } + else + ShrinkContext(self, n, self->SummFreq > 16 * n, model); + } + return self; +} + +static PPMdContext *RemoveBinConts(PPMdContext *self, int order, PPMdModelVariantI *model) +{ + if(self->LastStateIndex == 0) + { + PPMdState *state = PPMdContextOneState(self); + if((uint8_t *)PPMdStateSuccessor(state, &model->core) >= model->alloc->UnitsStart && order < model->MaxOrder) + { + // PrefetchData(onestate->Successor); + SetPPMdStateSuccessorPointer( + state, RemoveBinConts(PPMdStateSuccessor(state, &model->core), order + 1, model), &model->core); + } + else + state->Successor = 0; + + if(!state->Successor) + { + PPMdContext *suffix = PPMdContextSuffix(self, &model->core); + if(suffix->LastStateIndex == 0 || suffix->Flags == 0xff) + { + FreeUnits(model->core.alloc, PointerToOffset(model->core.alloc, self), 1); + return NULL; + } + } + + return self; + } + // PrefetchData(self->States); + + PPMdState *states = PPMdContextStates(self, &model->core); + for(int i = self->LastStateIndex; i >= 0; i--) + { + if((uint8_t *)PPMdStateSuccessor(&states[i], &model->core) >= model->alloc->UnitsStart && + order < model->MaxOrder) + { + // PrefetchData(states[i].Successor); + SetPPMdStateSuccessorPointer(&states[i], + RemoveBinConts(PPMdStateSuccessor(&states[i], &model->core), order + 1, model), + &model->core); + } + else + states[i].Successor = 0; + } + + return self; +} + +static void DecodeBinSymbolVariantI(PPMdContext *self, PPMdModelVariantI *model) +{ + PPMdState *rs = PPMdContextOneState(self); + + uint8_t index = + model->NS2BSIndx[PPMdContextSuffix(self, &model->core)->LastStateIndex] + model->core.PrevSuccess + self->Flags; + uint16_t *bs = &model->BinSumm[model->QTable[rs->Freq - 1]][index + ((model->core.RunLength >> 26) & 0x20)]; + + PPMdDecodeBinSymbol(self, &model->core, bs, 196, false); +} + +static void DecodeSymbol1VariantI(PPMdContext *self, PPMdModelVariantI *model) +{ PPMdDecodeSymbol1(self, &model->core, true); } + +static void DecodeSymbol2VariantI(PPMdContext *self, PPMdModelVariantI *model) +{ + SEE2Context *see; + + // uint8_t *pb=(uint8_t *)PPMdContextStates(self); + // unsigned int t=2*self->LastStateIndex; + // PrefetchData(pb); + // PrefetchData(pb+t); + // PrefetchData(pb+2*t); + // PrefetchData(pb+3*t); + + if(self->LastStateIndex != 255) + { + int n = PPMdContextSuffix(self, &model->core)->LastStateIndex; + see = &model->SEE2Cont[model->QTable[self->LastStateIndex + 2] - 3] + [(self->SummFreq > 11 * (self->LastStateIndex + 1) ? 1 : 0) + + (2 * self->LastStateIndex < n + model->core.LastMaskIndex ? 2 : 0) + self->Flags]; + model->core.scale = GetSEE2Mean(see); + } + else + { + model->core.scale = 1; + see = &model->DummySEE2Cont; + } + + PPMdDecodeSymbol2(self, &model->core, see); +} + +static void RescalePPMdContextVariantI(PPMdContext *self, PPMdModelVariantI *model) +{ + PPMdState *states = PPMdContextStates(self, &model->core); + int n = self->LastStateIndex + 1; + + // Bump frequency of found state + model->core.FoundState->Freq += 4; + + // Divide all frequencies and sort list + int escfreq = self->SummFreq + 4; + int adder = (model->core.OrderFall != 0 || model->MRMethod > MRM_FREEZE ? 1 : 0); + self->SummFreq = 0; + + for(int i = 0; i < n; i++) + { + escfreq -= states[i].Freq; + states[i].Freq = (states[i].Freq + adder) >> 1; + self->SummFreq += states[i].Freq; + + // Keep states sorted by decreasing frequency + if(i > 0 && states[i].Freq > states[i - 1].Freq) + { + // If not sorted, move current state upwards until list is sorted + PPMdState tmp = states[i]; + + int j = i - 1; + while(j > 0 && tmp.Freq > states[j - 1].Freq) j--; + + memmove(&states[j + 1], &states[j], sizeof(PPMdState) * (i - j)); + states[j] = tmp; + } + } + + // TODO: add better sorting stage here. + + // Drop states whose frequency has fallen to 0 + if(states[n - 1].Freq == 0) + { + int numzeros = 1; + while(numzeros < n && states[n - 1 - numzeros].Freq == 0) numzeros++; + + escfreq += numzeros; + + self->LastStateIndex -= numzeros; + if(self->LastStateIndex == 0) + { + PPMdState tmp = states[0]; + + tmp.Freq = (2 * tmp.Freq + escfreq - 1) / escfreq; + if(tmp.Freq > MAX_FREQ / 3) tmp.Freq = MAX_FREQ / 3; + + FreeUnits(model->core.alloc, self->States, (n + 1) >> 1); + model->core.FoundState = PPMdContextOneState(self); + *model->core.FoundState = tmp; + + self->Flags = (self->Flags & 0x10) + 0x08 * (tmp.Symbol >= 0x40); + + return; + } + + self->States = ShrinkUnits(model->core.alloc, self->States, (n + 1) >> 1, (self->LastStateIndex + 2) >> 1); + + PPMdState *states = PPMdContextStates(self, &model->core); + self->Flags &= ~0x08; + + for(int i = 0; i <= self->LastStateIndex; i++) + if(states[i].Symbol >= 0x40) self->Flags |= 0x08; + } + + self->SummFreq += (escfreq + 1) >> 1; + self->Flags |= 0x04; + + // The found state is the first one to breach the limit, thus it is the largest and also first + model->core.FoundState = PPMdContextStates(self, &model->core); +} diff --git a/ppmd/VariantI.h b/ppmd/VariantI.h new file mode 100644 index 0000000..78aa780 --- /dev/null +++ b/ppmd/VariantI.h @@ -0,0 +1,53 @@ +/* + * VariantI.h + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef __PPMD_VARIANT_I_H__ +#define __PPMD_VARIANT_I_H__ + +#include "Context.h" +#include "SubAllocatorVariantI.h" + +// PPMd Variant I. Used by WinZip. + +#define MRM_RESTART 0 +#define MRM_CUT_OFF 1 +#define MRM_FREEZE 2 + +typedef struct PPMdModelVariantI +{ + PPMdCoreModel core; + + PPMdSubAllocatorVariantI *alloc; + + uint8_t NS2BSIndx[256], QTable[260]; // constants + + PPMdContext *MaxContext; + int MaxOrder, MRMethod; + SEE2Context SEE2Cont[24][32], DummySEE2Cont; + uint16_t BinSumm[25][64]; // binary SEE-contexts + + bool endofstream; +} PPMdModelVariantI; + +void StartPPMdModelVariantI(PPMdModelVariantI *self, PPMdReadFunction *readfunc, void *inputcontext, + PPMdSubAllocatorVariantI *alloc, int maxorder, int restoration); +int NextPPMdVariantIByte(PPMdModelVariantI *self); + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 53997d8..6356948 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -114,6 +114,18 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/arjz_default.bin file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/arjz_v55_new.bin DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/) +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/zip_shrink.bin + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/) + +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/zip_implode.bin + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/) + +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/zip_deflate64.bin + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/) + +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/zip_ppmd.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 @@ -122,5 +134,6 @@ add_executable(tests_run apple_rle.cpp crc32.c crc32.h adc.cpp bzip2.cpp lzip.cp lha/lh_static.cpp lha/lh1.cpp lha/larc.cpp lha/lh_old.cpp ace/ace.cpp arj/arj.cpp - arjz/arjz.cpp) + arjz/arjz.cpp + zip/zip.cpp) target_link_libraries(tests_run gtest gtest_main "Aaru.Compression.Native") diff --git a/tests/data/zip_deflate64.bin b/tests/data/zip_deflate64.bin new file mode 100644 index 0000000000000000000000000000000000000000..80515116d8244f45b758353b4b2199c3c04789a8 GIT binary patch literal 50564 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

({R& zjZEp|#;YGQecJZzn)2kX9G<`LcmHZ{Z)&!=$W;7iFKbgt<{g1pXJ0$kejkXWZr$J$DZ6Cti1bz)l?H1&s#G-xE|V_(sXvxvL^l; zPj1QR?aO}^t7-b9{OPuBQ#Z-auXy6U``gaf$6s#AysRg*^0v{Q{?{UYS2iCqUEFj3 zaUlOe-ukL;QN{H~xz9*WzV-D_*ozNGckbF=yE*sdy2)EtYTtRZL(5ztXW!l}vdSIT zHb>_0SuUuI&b@QYednZH3I9!7n943ryRj=RCq1G5vEkyI`y^h*PcxjjeV=BflkxeO z`F{Odr{ymXzWOHZtxVk9#}kiq?+-G@bMGs_A(q3xVY|d*#029-QQVel&+ooKis6Utj+}|To?E8Iv&rV+g>v>nwT{OVfBp?}nDxH&EmQF8pN49`Y>Tdnh8B9*_PqJeD+}(Zd zb37sr?3UftHHY=`rw@gF>p#7j-~02}+wC7U)S16B-;6)tcyt}_6uq;1JlLc^_pFaG zJ^O@VPRyR$b2bTZ1+S>yxjEs=occV&oM(+k6(+l1u@SLbK9jdO>r2y(LYq+kfY3kY zA2m0bPM!X#DNKG((v?3atY(OEaIa$zJousN*7fb#UlKGGEhE}5yiN~{oWzmXTj{>H zDa7IMa@GdV`73e~51qTLnt9;L@!;9<%Vuf**pq&^gfn$Q!~3*dA8OJ9|L^gRs9vKn zGu~=T$AlF9ITQBm{Sy)+UDd3*^VG+*1kXc@EAv~+56n?rw=r60clWGCuWwCbK5kea z8C}{v$Er@EYO+pQN71Y))2=3F?AmcZ`2H*DD5cpSHom%Dvm8k$E}nAomd>hfnPoxme6E!3F?Y-NxjN&t*FE{8noFjh z&=kAMzx3BxEn@|tQ$j+UL|?qIIV%}+)Lr_+IQ{IstSB|JbspB{mBEu zi#^^vl$;##EqI$RpG1MmW4=!s-qQQHXBO?>IwRZ6J#}-zr}wkX3=axnpqJoT`NP_Qsz#7K_?wFZDaBuEgwa zCLD3(oRHl+bHOIR?b))v$00exL1Lewg3h8f6CT;Fw0iv{HdIQb*|jLY zWL@2a)}oy|CvF$0Z#ZMCx+4AJz8C5*idrwYzH6A(wr1h9k2&|YO*uUE)1h+9b&Smi zKCiAl{>1S5j}J=M4$Tqy{^UZV{JTqz);Fg9nYE%nFrobI%C}vUrpql?4xYs_&!qOC zn)a;Ezy8_1X{l0C?Xlgq_Q*D0&$FH}8>V|)KKFH2OV_zpnbZCS>P`&;vje@39zDIL zL?!EKr^@fS`fa-V`>*G;cb=S?ZLa$@6A~#7m~7XY4)cByo)+Kw(y-xe%_FzEX=V~ z^-qrRkz>Y5ovWfZ3C=b=R(jgA%=WwJj)g0a-79E5dvv3@f~0qD`wP|ERXfw1y5%rOEuiuvRf#AnT+;> zrv|H*MYt6>&o9qPIR9vBuX|HM%DPw5k6Z-Q{pLK1ee>yn{OWfB#aj{=Em2tO=U-cR zlCfLR?YozeE9?3$<7^N&yJ>N zZ~PNkU&Lbk;AUO-)TWt50UJFR-Bl>L_;dQhGXl{z$GqkpQq%Z!H|)*xJ;{!&HQ!$6 z?u@(q>GRu5yXN@qy!6qy-AL}I{>#hfza5@Ab8_yko#%Lee_Ua{Ec{^L{euqY7DOaw zm34e}{P(rP)K5-RTY2izgjIclOd3t16BQem>^J_ezmIQo?@`-}cMbXH751!KA38au zPdCR@@7&9+KSg&O%6WP7?xQfHz*`sgZMxvI(`@CZ-0sZK+hyKIesx;fcveY?JbI90 zlg&8!z2UiKrarR*ryuD#p{8?iOK45>?9)5HOs+fOnP;iQA;LjLRo$F0H%gH#55UVsvP?m5wvp(G2r_<~#zEy!xh#YQLLym&aZ_ zM|sA>S-&qdiH;j_6+%tWV8znCs2$f%o8?V;Az{lVwWB0Z;bLD}+G?Gw&?jEJ(oQY{`z(z&vuLh0*nBbc zXSvR2&Htv6SLZ2P-Z*ekWTK(Zy#%4tiXWu3TkPcTFt)H(eYunS{M|$rgSoRV78*1k z%bCnQzu}FOf#HqAtDXqbb4%BvP_sp5hdz9~em7ol;vudH)$6WGc`&*x z6uB$9p)-E3a8i=Gi2_f1XQj>l_u&^(VogK7ioMO>CF^se*gSe?*^czt%NUQTTZpv! z?>RYRYW97%l9~++Ul;sYp|&%02~)xGx_}3Uky`)x1@4xsFn8|Rwb1%i=8{V~H{&A@ z-rqWP=HuC_DM9N_&A1)B@4f1zqV2P@CM{W^v1pnGlg`EI-{O`W^;yk4efy>li&gk6 z!WVISU9?ms==?tK+#SF7{$NvMsaPOCv3$a=n0`05 zr&~HNHY%@M$|e0T)oJ2W{~Qt3XNC3cx9@tzsI1@JeDBmnt8yL9XKPgJzN}ooS5rlX z?Z`Q=TOVBY!rSAPp2?RAwzzyha;^4E5w^Ak$AgAt zUd99I#`ll*%xBjLc9787`R`6bX6H|rBeQ=!Nq6v4HCj67$?jXHeRuU-RlT|~KYr7d z1+lB-oJ8u|JFKe(Z!`p${`t!&{CQi~>PdQqe#Ys)ybNEa7Bn!dPI>C;(#7!E$?>%% zPoz%cnzC|(P9>#NtgpNOYn=EwQ{%b!6t4Tq4~u`Xo_I9*mqSoR^s}V9+vSc(`xyI3 z1ix}^*vNbF0s9%HO(BAp{iC(Jccf0pW_@=1)4zHCx>|+l#;!}3o|yVb!8hQT^0$Ze zVQp$nX~L4JbEb>d)UrG|`*5{>XTptjYj#F|yYxRc^@O3qvG?Di7u$A;cDxe&-0{=c zCcuutD)i^uzo*tDtV~#7eazLi?~{BmhdrC=img3;Je+;J_a3qt&RnRsecrd6K)K2D zUP~zp1!uE}IjQ(eITd$hk?)_AS888!-TYSPpPk#ZbHdFf3TxEYcyxYB*vH>q5c}mx z`qbx>I2pwX^uHgJ)r;`xFy=lo*XY;zwR8FwJQY6A8sVi@QhAZ9TAS;B#I7CFJ-$3x zX%dt3JK)Gc3-NcZ;y(p7Q$i;>DoRflJo`;8OS~}J?6Gp;2aOAN|2A)uertG1w<|2_ zp>w>{bJc0Pe!g9{Eg>lN{^O-Qvtl+D>88K@+0OrXV)QOKEq}lMJ6h(RGFQC#0+wo| zPKtFA?{g1Pk2!Gi&XRZ6MT-tSR!n<%T&}-J>-?gBf~Jx#v6EZ!_&&@0n!Eax;o*SO zTV9Co>W!Tu>~m;Oq2@OA56=sCZC#jnl`rphT6*c*eLHWQd>K9Cp|sa*&3`T%=f!4N zT^C$Y9K1e7*kj?Uh7kK20ipBV^ZqXOc*d_=izwj3Z1}bMD`0x_H6< zwkIzuieB9Q>bx@Jhwd-c%iiah`^z%LS5-fHc)>XB;r70gYO5I6Or4u9$!}ID#GjSV zuJyQ>Y2Cb4`&8*Vm9V-s+cxjI5Ky1b7a{~`6~q1~FNmG6HG8DyzUPyJoC z=Q(#q`A|7ZQd=Z^a`w|bWwhqg>u_2tf+SJi)#bQ43jnRZ(>_MMwn9+BmpSbciw zy+iZY8$GDL%&lWNt!Kq{hW(Q)zKeD}n0>EBBRPg=-j029=RDtOB*Hm$g40gZY3T-j zYjj>Sr|qi`d>?mV@7)sd$nct1*+=JFU!7Uev&t~ zSKDuIoq3NfaQfqkR`Y((F=n|Mx!`!f;xnOJjyMEWAKKyJ?C^bE$|e6U37+El-@^_( zbvyiJwPomA@1Dk_wuz6k#M1(G_g)cO*mQJPZfK8*+`%J}!a4j$Et`CL^w!+oxoC;5 zLg;!W|HY=0p18I6Gw?O=O_}hUJ>kRDVotHm6T+2Fwd}JrKfCJCcJw??e0TcC99iCSjx#>8NimUctFr;poTq>CA^J_=g+}o`}VR1dXj|4j_+$(o!iYZ;gBbvl5 z`sh)OLC5Te+wjMct#*>VoLrW{}P@A6C|o#LX;KObM!x8MK! zaz%=K+ikz>r>ToXr-?Gf-VNA%;R$=|Gh+?^18hBSpMU>&_EmeA%ZzVX7ia(Y>Evf5 z9uh2gFJN9Gw0m)MW73aO|LMz2WEOiKXIx;v`sdr{4iC!Dhi`wR z#(L1+be{pI^`C2RYQC0jJJfDp7}v9Qqszvk$w{qyudIC~ANk_P=_Sv1SDv_f<)cTI z(g8#1kWbxx?e3N`Q(anryGV3+tn9qHq9oNzQF^D<^2+vY)k|HM)ChfEbk5f@fR$;{ zx0Barl(pt=v&*rbs=%*|nc{o}F;-Q-+6Dh;_B6rDyxxmdcFRoqHn} z&pBfBEZHxnSb^z%f>>|h6*G?htXn(91j2ZQb5vL4Z|FAQYi_#!bl!>|%&GyWLbe2{ zUc2>tL8d`o=;>{@FC1^<-1JsSvTxJf6GF3Mw@+zQtG3_Ub^X|;=)Tf<%iTp)4j#drr`%YAUgakG@3lN%=DDJ$nlI#= z$;w#Sg3azFQ!W{t4C@S5T4L<>E9Sm0v*d;K^EWBC?yG3A@)lh*<7cGBv-L*O?JqBd zHU2K2bt3X%{qHUF?XDa+k)o_TK)aU{+aLYELhohX5Tix*O|{(GtEdBQn}PVjq`BPu9d6j z-J21nUZh#P$tg3p^`_cy2|tr58IOzOu5VeQc(Iy6<-8Z8ce$U|vZo&H*J}ITXeh9M zIo0?~-6-*$q{*=*6;dzRk`{<6^s(Q+-6wYQ;TgXGgWH#8J?FS|$$!SAk|)0n_u9*~ z#z=79+vbv)BggKS9(&1VrFgisYV|XTX<4pq8@k2MxBhTj`uD=V6&Vv3%?ah`-?(7& zQsecTt#xX{8dP@ zee%&!Zg$K3%`)xmdmSD#?tXlc*4t8$!~g8uWYIOyZpXltr)y={Af!8=!z4JsbB z`m-xtS8WgFRb6pL@pAelL7BGrLwz%E*{S@VDPb5TuygWFotU!kwaW`6gnxFr{&+qq z^KZ-t-a8?3;_{~7TDL#^Q?bir?vZzm+%5elIgRG-yV`KC@=O+|;XxbYRrmHC^of1; zf_?InMF(!)H}i@4-*MH!{@jY*?THRWapCH1bM0&;`-|9kCZG8A=#!~y-B!t+Zmz40 ztmkYf^W1T5ZfRvy=FQFf=FRSxRrD>BdZ?&$>%=Ob2Q#W4?)|8ddS;ui*B9@DDl>mQ zNoWz7=C*P|L<(bEQqoSp+g|6Nxt4RvUGUe8TK@m%H|-g9K`n(puQ&_#lxE+x`*^~t z=#KyXEnDV4Jg$F6yZp}^*%CKyGrPA;KSS4Zg&WM1VE8Ec^xVUJ6-7)voQI|{_c58B z58GGZufTiDSL)EqdWC=2RKtF~Hm?0D`A*pX&9;i)Q*BPV->>@85tj2|s=Cx^-Mhb) znm&ATS34cEM`BOyW9ug?wr`!>tsb7c?RZ&jp|j}s61JshA9`9u>0I*ND_d7R>+QEM z*QcEM$JJ8v>ULT8`e)z$*TgSUbiZF<9q`5Gmqf}tmai*xb&MYvt>RKII{nk$PD1f) z@ZqE0vrau(a&ht2%3rT%zRWzoM_XOE{N3t52VU-&|5MN8*Yo45jF0Bm>VI1`^+)>u zi8ub_aH}kO{cI<@hs}K1|DSgk=e_3Wv^U;8MK|)@o_7y8@1Hp384}B~+@R5HkELaD z-N$EtEd38Rl+SqK8oc-B?vHcCqUu4 zH$FSXOsag}*R)D!n)da55z{U2*(}PtktB9v(rjJ#b*?{N>^U14x4L(aeeWC3b@@-9 zu++R-a%$#OAFIj1&*u8a^ljN3>h)&Vhofs=o=w~SDdr|~SC5xyXVHJTTrKI9Y+*T1 zF271W;db(*#q>3cU2bZA(q);X!#!6v`q$I&FtPcvMviwi);G>wGwpBz!`TTRcr#V9 z=2mW578!C!)xX;LT6grHhwcaeh`!pgtLSMB(~6KEm*=!Fn9kZAeD>i(iQTGUCerz@ zP4AY(6{tAGK2UFz67<;isay?3V0XneAI)$^Igp8IkS9htwV`d8}p zoVFkPPuz^2b8dEN^F71vpapI1Zf8TU%Gw_=o&AS5EL_LvRM(})D?j!LPFCw%l$gQh z+N3W~dtG{Sd3|-i;Rj2*i+A3aD#R@lUUtsxyVO&a>%U$~xwIzRhPTZ>q*(5;aIQ}# zYi=-`>3!$z$@~4Ea7?s~oUtKu^E<_9C+jxMEmd27rla`k&y7vJUN6>4O9sBWSr?kM z|DHlq**eyb4-VW~CAeJBL9euX!q;5R<7u3Iv)=t|Irv6ad81T?bG23Gr@yWYsb%Mx zW&Jt=`P?@z_~E;?dTQT+*Gf&ZwrxmvYC2oyzA;NcNT&7exxF^stzKFSEk3y!Y(N zNyjhgCyIp`-Ia6jYTkC~_@}zyb#w9W2OqpC zZp%rxda+n*#-H+{f>q%mD_E?ea#Ex$Ud#5)s7zMbQ7~cUqB{o1ju~_wFp6ZcxAWWW z+v}lxbNRaB&XnMscGn+kPu7Py$>92UIwlsBKNZ{%8=d4R5WeN^YegFDZ%(0Nk zCsx<)&eXad)ol>^MZfJVdvE-I_lG_8Hnjx-|6aGTPrWhiN5bU)OHE>`-_Fd+pBz)Q zRy;U+!yoZa(S4fVHrSZkyb9T$W3}PL_Brc2m$O~H(=?@K_uJ{&+Q0U1j9ZetWRv5M zy|qmiCl9WyOV_FpSUUfD(Ob{y=Gn^T4FBCCn73`(bpCJp#MDJOdV3nCS3kXct@*J% z@A9=D8ao4irW}5g;k`k5-goXrFP}f>dcmr<=zoY<-Krg)td$wkhAp+zeQYoO`SrrW z{@GH^zq`u+Jt&#AIj6W|{rL{ss^ z_e(mL(^Ob}eT&}7E63eU=l6kARrtajPT2r0I=yd)0j18?PPbIne ztL?U5C1)}B*11cuBm-*k%^|X35eL||K{BARb z$09k|H>YxhO;w%a{_gy)Nl{5J6FD1$&+7F@owJ;4cky4U!nD*?uO?{*CFCYJubMBR zcxP@p)4T*mv&k#EkKPUNy2#k}sbINx#)_c+aDIcChc+l*wT=tken$VaWpPmUg-uc) zD}w$6i%m>F9z4D0OO)3ieY-~+UWe38FH&fK`&(Tk>=V~z*(t}=&-!}3xydb>vTCo= zO82e)I`a)Sn9G;@Tck1`T(d~=%CUt#;v5x=?Srf$E-AA`PCMYtwf1Co$+;7J{@<_Y z>Kn}1v*}p5tlisde!jiMFM9R9^_y;d`Zsmotb+{dMS&dlPlf!?T)x<~=Vrrh-4Ctt zs;YA@OG`#L)x{lCl?prh&~j0_(f8_{%18Sj#rE(U891b`npbex;`8S%t2EpemdjrJ z`}1Mp!L}(cL&R*Cb4&eTwEb&rv8B-8Pr$^h=iT$4eq86SKI9PGZ_V{`<75WohWWNa z1x0z{eiIED?YQ29=T)&YOz(`uoR7jCHETVhdd_by+43uC`6MM9ePx4-{fk#TGboi8 zJYv~2v&19of6eiGiC!yz?P&d!(f^fu!g;OGTeH9X*?MSJZ{3H*btP>}F8#`>I<>22 zh1ABbDaz-cPWyFD{^ZM@ALII8-RVh8)STt_H{oBl{%`#g>Q8g5Qg5v*ym(^Qnd6%8 zh4fvFe^jqnE!DHe{3jphGZBHquLA!D&po17ey!!=JURQE*#{HV9tZy4KF3(Fp(Z1M zL*dk--wTr@mWWq==koEJTYSvhsxQpi?wp8~)xlG)SF$#4;CT?!v^PGawv=7sR#Cb{ zr1Q=>rdv9^!gay$@Ex_m8L>FXPs?(U}{faPDB@lJfN}=g;K2@BDC(z27tH3DZQ|%=*}S z*NqR~QrSv&-2dRev^|j=bx-PN8;7_4|vfKiqO%TGJ>y&27Uh;l1)R zZZ^36ww$xqfv?+%i$VM4yE7g2|F1Z_x^zY7GiUX@Z>DmZug$|H?uB_dt}T^&?iK!U z!iI47A3W!IJrBj*xc_-)ZJrO2>6< z^RLBCmsz(p z-r&u7m{pl0*vspwaaSnS%5)0f>O-DOLpK|^SvG4tY?9m3yo9i9B^c*(1r{<>vvE1s;}_V=>#9RWU@wDW!m z<$0&FY?+N;%y>N^X4;R9?=Sm_7S@SQ>-&1p_3g(M>3ko3&bo1Zx3@f?wA91W6Cqb51N@u}#{w=+@jVCmvsFez|A= zG5r%)cULxbYt0C2nbYrnrP7*Ca;>F+qSBTwe_I*kwVRN;e{#7bN?(@ zY9^lcVe_{OdH!~uUpk{_M_)eWI!8b=`EhrwU9Gb2^9qp~1?391C7CLPLJ4BsyF${Z z-YZ|f?(Q>*HvuAE-d0&Jp1i)R(aIFv7d!R3S7~Z+N|x7m8{5sS@`=3ilYZtOeWCU{ zmCbunU$@D|`~D%OtqRjyws6gQAnY8xDNQQ<*W=5NSMge{s9bhvW^jU{nQMX> z7`Hr^jxBxW#+s;fFmHp;yiApkmWN{0TRfK>&fMC@COCac?xnD1w+Z*w{oWjW{{2j& zgYmOxeyLklm;Zf3w%hjU|K_^tn{Q_R*NeEM##(&*!MQ$m z_D4nxyTjSu@!w|HAGLCRcOs*ScWdY}t+>|_Ypy>1hK`*I!QjYj-=PHu|a*b9(Re+*`uWe}CNWWOdIl zZejVMD@SbqsvLZ{@Y^1#hyOKC7E0Ywwpq6$?US)w>aSwyXN&6;r`=vZzdhtw!$YA& z@f+4IcSH&&pME75$hB5Dy1@MJN70>Z;^|dBWuC3cGnYrgy*=??w|MS8#Yh!i?odw0$%VO$kt)4DVw={6S-QPRUclrCDo8NFg zzZz9on0NScY~~Lmeg}sf!TtV^%?-n39wy8_cFy!OM@^gmj+ZCDxXs%rgsq5rK^ zxxOoPEwKD?Am<6!cFwr$#kZaAG1`16wR+X>E+Xt9AF6+=|K;-Rg^O=?HXBrlJX$O- z_8~4^?|_B2`5pfy2Gx7?xBV%yQqA1HNVM2r%RzkEv+R8%J-?2s}LZ+rgw&Iuk;d{1Y&rALPS0&tA^K zztbvt>G~e6!rZl!!(YruwYfigMNvUbEd%#ot1oPiCrSm0uihxV}bd!P>z8LAB~n z7j4-j$#ZP>;%BaYufJ5aSp}Z%n7o4{>h0-CjvAhaH$J>~D{p=5x}Bdn-0!}>5YNx3 z5cnXZ$X-3+>6)M|oe#W!247smx+1AK_v5=prGq7_W=)dWW;tu)S)cexA`RhBxtd;> z$xY1s#v3k^G-vImaF$%1_)n{APyAU@zHYthw}Z2z?7qA@`8Va+?}%Mf!so1;FSLA3 z`@%<#kN-bmi@upB=TIC!xm=CiYTik<6`RUez0dN0HF5KnJ!@3!IP8T~RSo8zb=&f2Y4Dn=+}Y&<47r6~i#|8^&f^P7>FZ8^U1Fmb8W-j>`~KV$OV}<{PV!tC zI9>Byo9FeDWZkfLDL?KdOHTSyAGyTcaE->R%kF85Z~e7a ztlTuG;`zUGS-DG{xs)fY3TX^Jx%%X+kh_V-rMHr{Bs@M`aPGa|9i_cONWwU zo-kGJk1zLr#gr6%8_Q@*7|t4dVm z6fadgnQ^7Rq_05TF>k4^t0A0ib7ddcdDe#z2x*_;kO4y)*4M(tKNopE1CGG>|5lv zqxbPXjzDosyYu@d)@U4c-4*Q|vUpo{&=bSk9mOpUci2v?*PWDdQl?-MKZ}>3@|OOk z3~xCc`cFGN&8X!#Ul>um?St!?-R7MBWaJs^*4_0 z`&i5^C#A9}T7Uj_^Y^BT^CIkT!!|N!`q~QI-d%A1LH?p`Ws?Q&cr+Z{|1JNbsCqif zwTHU9!vCF|o4oG#_WDh;S)U4jFAb{I6U~-drO~@i+l_hQJj3|9ZRzEME zzi6FBz|rTk51k86Te(58SH(RBu-r{LWu}`aIjgQ{sPOBQIR=Wc;A8o!9j8+o*+q6_jS(Ht?LC zU!g0p*fs6Lw`3g)Y2goiHdExh%I^vDZjC8eB+}mb{~*Ux*YErPzWZ@a%m0eb^MfAt zk1jM!-oTp2SF`v+`c|jEMsJntp_P4=3i|R9_+S z^U?9&7Prn_`@B`w&ULLxeTMnVU_r6^Ye^?ej)g4NWy%YVJ<&Y(wXDtIoL@?LKaDnI zdbBkM3Y}d)XQORKW7$i$(kXQdUL482A=LLS$5pk!_JP4qm#0Nf1gz4&o_Qbl@tNhO zF3m4F-bHL_4n6L*+Dn#wI^t4Rz4Q8(&5I1Ywr*;6XIv8#xc|Ab<*er_s(ZdATl&O* zaL)ZR&7i`3?w!n=5z%fMIdh%AtrHH46mGuF@@_#`zCqY$_NPVFDLa2E#WTkB-`~`j zwnF0ZavP3KOHO%d&2^d@Xnwjtfc2lN;F^b)XO*6JU(4GNb3-*e>$B~S*x4~hi*#-F z9WGk-$KdYOgWoe#cAXFtRJGmnVv6LvJ6%G{qn>6(G$}Sd@a$-f7d5#5_J&`{C*`1@ zyC2JE&OTFZ_4B>b^SMtMq@!=V`29m}^=qSZ?>;aK#WTmeWK;WFQC52P>qVC4g0IPp zKN;G6{!e{=?X~LO3De*15$0(A*?L}_yQV6&X#)431)>vWgWt|ksh-`p)?aSTG707_ zvUOK4GQ|DzF8wF#il&@96b3}xBLCW3fDvm9|9nPl=B;_L4FZF7@<0t^uyS@qdh5wlw*AdJtscHUyxaYW_WOzs7o%%!f2M5rh$-EC?@iQl;a&T76)J9h ztX@h|!m8{1fp7sMEG9bK>aO3?A;jJFde$6npOxU?jHo8wFE4L9pwA84MU^yBz*xjk2w zE_$>qKmT(}^>W!S_RkjdZ#(Okb7tF%iObjKO;UUuI`Q>${@Fe+fAGzCeq%!V0%V_Sg4{Oh3lUmj;FTFapD3vp1Wn8w=>tCVbL2WmW z+ux0;{So@#Y8%tBi>U#BwCcM4KS=uVwdm4T$;%V&dY(V8_CF_+{rTG;Gi=hGwiQ`V z?Z3LCZ7WM(*4Ep16|Nk(xUjjjr!a%5w0Sejr;@A3cl5Uw#-2YuH+Z-C#_M~_2g6 z!QZou-}@qF`5k7Te8|viwe`#=^N*gAo&QtlVBNdk`&p~bJ>}Cmn3J?Fz;0^y*EOa2Qk9G&YsTDKc@f47-lp!~ zVWH%gb>Mx=QHI`<=||#@ZOvZ)^M3mTzO`kHP26ziY~F?e%_Z?(gOi zxs|ypcY4a+4RYKf=dVpL`Fq64ME&2f!s*Rh7*A^${&=$6#fZbTjOXw8u}yKGv*1_dxq_hlz%Fv)sHJqRoGF zD^_gl3SaXxe92Yj+XjEknVsaszHONL)3fFK599yNGF981_j*+Sp8w6_%c8kn(~PRT zxlcbkuA|rMzLO(@R*MMKWs$@NOCqC!oNDz@%D8-GTHUo?Rk=C+_4#qW?`ECs{}+ul{|-+K z_~}|M=XlfUg8npC4ZHdZbIrQa&UO1w3GG?blIW56ekO18{f$3o)tAg$b@XSY(cA3m8*63DWCLH#Qpqujnf+^d znoYu?dlGT;=RBNH<;LzEDYWqAj>Z}n`^xWpLeJ0lrz!pT-6iFi{mDikQnfqv)*^|= z@wOoncZ8yyE(e_3`bwi^V$#{%eG{_JF?0O&Q`oR}$ur#<=ibPl%M5mScJ$}u(-Xw6 zFAuIsX2{UGG1+O>&vl2xZL4qb8*k8w&DEWJX3?FWJpWRo>h{u{|Ci<>&n0P3+@(hXai>d4F%HKi8PP>EDUsomXb2|69Mm!&%O&h-=HgXZPbL zIsD)MswZK;z-MKSO?&f0Y;I?s2nh=Mu+J)LQr*cnrxvZL-s=0#VDY?Gj#{4SZG7u@ z1niJJIO*cs`TD)$y92xg_B|K*xLw=JCsv`3#qm9D?Q9>vFWP_kuF@h2@0($> zs=uWB$@I88CcWG6CwqPO_fr)w6o-f%jE>)6(1QY90nw{We$W3ViITf(g}{nU+?V+8h-f{2cz23=z|WbYmac|55|)c@zPqt9OzrvM z%kP_+gMNzyXZ$S+{3%gge(6OJFSmm0xt(VnS|q-_{1Ybs&w5eG|4FRtyAsR#dOQLf zJ-FU%oAWq8aQ~GsnH4Yh9P(ys@<@2J8O%132D?eub zW)0iIOQD+&&;PwmeDRD=MqMQl`T8BVyX;mh%C(#JZ%w-R$4%Ll2`}4B&o6kHU2bEP zopSwhS4z++tH5b9H}~3RZ?Oz^`|Nj9VWq~}`_KC#r*HYY+;_gz&b&TnjsL}ux5|Eq z`J|`PTwfFXMsnYCiv+uQZZSJb_1D!bQjgKgD!@dBU(pWqN{8;XC(FJ4=rDnMBGyTCwIt zV@F3o;F+2!ds2I6mfu^PbLjN5q-D?lJ}GY0Tl>bTW9r<$AD+#3IdAq=_OR!t(wIf! zySFn)r{53!U~xUIGE|10qu>4KrZa`lZDZwc`h4egX1%nx`h%5n!{xV%KU+n8Enh#M zSb9F@p2j~z4~^8Xbn3-|%J860RrgC;0VLrzwZ#sBfHd zd-ik{k35&8$cPI(pT54dyPJOQ4BrD4{mPB5LO14S`^Wo#|Fi#ew(eKu^u5307Iw_N zBGVckTNgNImqEbgY#lR>t>u|kl^c9-39l>NczM=iN^(pR(dpr+K5_3{_ZCP;h zT}_`*gz~R*Rf>824u(D2=klF@aUP34Ts6~$U3S~ofA93)&(`{1`BCOk(dIK@tL`f- z?zhn3taoKduIBG_VDmj-b@&8lteaX{O_IGrql4ruopY6ItDR0HUp2R^je6R zT-xf?_=e$r4rlAh>iHJ0-=19M9eeU+SYKJ9zrv1dlO3u8PVPUpyPmf^Yq{6Wex`>e zD=fRBHraStHm!1;;U`%Ra9zxFE`h=?V*vkElY`IWX(SIMK=o)8ob3gZtM}UW^q1n;mh;Y zv(um5UOJyWIi{`Yor!<=vE5!j{muQuI1WncG(11hb=+!NFw@ybF@m4tB&9O#3RkJ# zd7$=eD)X{1gEv|IN1IxO)~>XB#A^Ry`?beEwz?ZBxw$0=7c^x!1nJGZbHL>0;n2>@ ztUfspoemm#N?lD`nzD2~tE7VW_MSPly+5vpwF|Hc|F~23@za-WDeA9Snjf)oGRa*? z(~+O?@ZtBKM%Li+^o6RWUOg3gC9$l`&S$4Tzsfpsou$%5s{^|`mmKt26ClPQmHBzz zvxEx%)E7c7IkP*@PG6>XDCPZ~aB1z_}B{Jo6f`>=Pg|64J7hS3nuD9N4u1;*3 zQ?avlWlF{Fn=We3*WXC$*MxH&zF3?5OyBN9{kJ7QTlsXSh$>mv&fNC)B46p_qpVC( zZ~vPf=-7KC)$3UR*SZCYB_$r8Bu~`F9Zu+vjur`hG3lAC{*}nLrmwac`%c$Ca?U|- zinM|697i>ljT3F=ow;)0ez3<3-tB8|ZnBbaY}~>SqE}j*v*T2Xa!%myR|`LP{^8nC z5YD=I(Yt#xi4}Wv!x$HS-F4M*+6vjjJLjvOOl|GCee$2+&CEY7r!!k)cI&LU*P_z1 z@8ho`wqth;glDa1xgfFZv5(Z;vmaGvuKcurwOY>88u|YmUP|E|i>s$3ZCTGgIXQ<* z+16vi?6Qy7iJ&r*(4D%rJ15_M`;zzSt;wI6BgXNKq} zOW6hH$D+QhXfEmU&^jN;%?j{mAr$&XO~xV-}%2Q*Y>>% z-tcg<=Dke$w%J;fS6^8gEpozf`ii;ibEf)xec}lF{vy}%VzJR514ePr!)&)Wq&{tV z9J%Gp?rBnU{EM=AKMBd{T0Bv|rk=d&;*J^jA5G&sJ?Z+60%0z(#<&;ELZR07YFF>< zi2V_%vsJ^BY07GiErEx!&M>4~T83S)(hb=7E@r~5kpHXeJm+R!6VjcUs{E7T;__8R zGm2*Zm)Uv$@{J2|>}m5ldbXpZT2lGs9p<`{*|rHp$nS>D>0eC3_h(ahG@uMV+DZah6h z;*V(*OUP>9+0M^W4sj)ltEufh{!5ba6JxMP(&Up(-)#d-ZH#h|asNi3?Af^69U#E0LQ1Lqc-aiIWRr3JV#ZD8)61 zbgq_5p86_L?~044#X-{-56wQ`aMNGBRdi-{W$0bQ)9Yu=;XTczA$o$1Jz~%4`$kNK z+D-o(ykD!yyziBnujIzp|96w=)W6Ou5+23pgs*> zPOf$3A<9N;N(^VqF3`^@ynpLMT+M9ernhOzQzcZCYxNhrKJ;PNznxQF$b|<)rH3T& zHoh)a_daL$VQRs>X$#h@G|~`Suhn$ZSknHp{>^uL_EH?yuUO3zK+>>|jj_1C!U?2Si838@mC3#=|ZZ|h#R zvq&>8E4!^d0tTF%Ub{<+VRd?3jO$>cUE;cr9YHhz7%Dni^$8URXvYxXw znW@#b>wCb0i;OQdI*f{RJoQz#Ml!BBFFg6=j4Vf8YtgJJPiE#d7cJlabWQhhwTUeM z4Xy87_m{s2Kc2y(W0uIWFmCfS@q$;ZTI|Bl`}gr^#Caat z8kx^gA2R#o-30BLFFvzAR$P7b`bbg9k1`qF4Zaf6kKIesJ{Xr?*#BmE@E$u)j{`F4 zeh&O~o0c|}*vBjE?1*mND;aIQLVKDul$ywH2N)sn1Ts(DS zYu2vUGxa>D`)Q@T-Ft1axXE4})8jQ&AM!V)vk7H z;;Bb-{1)$;SbgqZ{A)2Ak!5YFk$FAKjH>JlZ%OgHth~LU>yh7cy;E$jKMSl}y_T0} z)&}*1HewMcAOEvZnZ|kHUxCch!+JI?0@jBwC&#n@5t_(9RdK8NrPBud*$z7rPyPLL zN%7Y&PxoxDgU^>H-A;K}th8X;=edIZ|JKfTT5D^oBBb*`aT%AMVfXJ#Ne$GsE^;#I*(Q%>Huy32IN} z){6*uT*K7RJV)SJ=A7b#?RPp-jybz(}lHifvVx{4j*=HTdaKfVw-Q56IZwHR@>$*A8+|} z?ADXZf}89zUs_fs=G!QI%siNM^~~`pdtM4ZNet8M&EFffWRgh0v%s|~)8uyczgzVB zulC`~>-?TKOlqCX`1Hl2M!g*|@%(&BuW!%3SDEtKu;cZlrpDY4{u9l)ZEEy%(PU zTfQ)1`+w6nVO_8GnMxkGy(!LBP3ieu$v+`GiUKw5-gkYoYhM?!aGTlrD~8FB4z6r@ zC3tfq--9cAiZd!WUig*>pV+uK;)c$>erp~Vv#s}R5AMHQki@^QrFZAC_GjUT$}g$f z-`p3pf6_kj>lay^92d;*RG!8aZ1ZmEw@0roI||=cI%2g>ztT)3uXx^+&qfYfW@lB4 zj6FmITXIW<;w2=n^YC-4bZmW)`;7JH9qv`Xwe~S4)YUA!P^n~Xm0Xc>WV2o=hty?V zhbQ^Vj#(YMo>EY@?&r$0I@~)KB^a?}HGEP&_})=-wf94B?;CuTOT*u@mRj5`lDcVN za`VG9M~}KCGfGps*7ir*c$?qSuM1YmOYFVpEIql%OGi>v={D=1|633Gtl|FoFa6rw z{dz4-+{gL31&c0a+n;j%aWQDuv{M01@0ZPWe(+9H{uftin#NW>DHofb>{|hU^?%Ij zz9=OyWA7G2hnOd>*Q6H2-;w#5d80~WQQ_q+#tt1SHbVD1>TYdWQq?z)=iam2w9Mk2 zvu^BuuB_FvJon}6YyRKvumAB`{rc?l z8hodgA5XCj6KPXk!n5t?qTIMco)Js`3(KjhT5g`Tu=8uI7$Z-Q=%Y}L1;x@@oA;db zVcuL9tS^$Ye~UWno!AiDB$3@A3nx^&aW1>0c$=fNEkCL3PJzy9Ek5TOK|S>ofzOZf z&r&7S837q;+DX{@*E@LqW1pj7j{ zf5pb~_G-FXY$XeSv~PQtQZ86N)6T%u?)AQaIY-yVKKQ-myR+Yhr)DaNF*y-II)c?= zmI42jIYh%0=Jr)u#F>7V&A4mxqRPANa6qHp~nodaB}Wh)n6 zZJRSWAZT)5^1HtHn2TYngN4p)_~HA0F4M|`H?=zhZrgW!o)I!lPDJf(o{{~$IbJTY z(@*@o!enKBICay_f5}!aoGm5F#%f#TXMScF*vg*^+?R>${2{wHAj~rd||?4HY%}bE}=Pvi*^utV2A< zysZtEI$?*N&DXMSSRizC%bB(F&YpgHC$XaGvgrS5YKdZJ@20hOaa{beEaiqLI-qc}58=F?h+-+=Ia{E_)m2?w0ZmxNj;e^Jl{ah&#rKh?0y>@y@Z*)>O*i#Yb zbaM4cnfi@KPCbyw>OVVSW#5Cqwf4Uop2aIh2YR%0mUZ6zVN!K;cBH7U_MIK~3w(?g zWy;BMbS`P=3%K0g-n0K$+>4W6A{L%{p8SW?t?704S8e&~H1tBL}n{+amGJEtq?x`EQFdh6@BKN##f#`;-zB)HfYD;Of8mF~$ z@!YoQ?Dbx@rg#3CpAXEO%paJvZ#btnak*Ll$rU=smp(bZ!PO`BbD8lnDZ$f8+b_-i zYw-Q}28$)xizXSZE?pC{Zoy5x$Dc19{L5?U`s37s&eFAW-OE3e_@8FDX4O>p&GC9? zchAR#&pmZ=g1FL7lv~VQtoYb|qj$tXA5CZFImOq?zsbv}<)4&uJTk9)4;Q=Ye4!qx zg(dp`FWX*IVEDE@qWO`+*&pV@9;-I+y=CqG<@{?(?L%*aTxY*S#@)Y_zxZ~)=Cqkr z5p`qkvu$4z*1Y-up=t9)`OPBvtLhpTT#%R$7QScw8;0USvFtjDf**J9ZJS}4p%8lP zj>3ha$<@C-E)>7`-rIUctM!`c(iB<#ImL5$3lfg`Zm-$#q~@E%mx~=c^7qzNKJMId z;a9j(+l1+Bm&UE)IVe9r_V1F+G@br65(!67MIK~kY|2bNDj>%DdjH0GcUONpwMW$E zxVK&PyOf!J7krp}b-(qzF6;W7`MPIH5p%?*xwUHY%HEF;zLaY}@odQ*g@jerQKd=&!;pyTfa34vOdG|51J786CHNmRyChcTT`h zgA1=~XEc{7ue=ezmxFBFx2>ob3GVVLc$8O> zxgp<|IcmN|T8R5QQKkiMIqjYoZ|pJ*a{QDaVq^J3p`&zQ`U zT*>*R(4_rfPL8x~i+ zpSXNdZ%8MY)VB*A)S38g{`ao7viaV-BJvqnt8XTiJ4Sh$ZH+&9IIOfX>|le>KmI;vL}9r5aoU<~zprg1#vdaZ@j?MYwluYE6E zcS>~im2Xz|24|oAD|}~~cxIVI|Auo%6m4F1@2Yt*Bk@@O`HyZE>Q_>YbQ=ymw>W(K z=DeZ<2Gg^Dy?LZlQ&N$l_K72Y?#X)1-c`1Cw+e2yRm`2AXurkvneGK0%gHkTul?pe zI4$YEXLiZKhKCKN&x;d}t$1qn%;->laiP$pnX@$VeGf309S_qIO#ZdkF)Cx{(w%QB zj8hgY%#Hl}eQxD)*(ruU9zN8N(&YUzW%}{GV#f0hSI=LwlxIPMfAaMI!Yp#iulKL9 z7kOhTnWnT~bE>aimg9Y|prx}{J>b}2!+2@t#%~{@CUMW$@3m&{C%>4f`#&B27#gz*&)x4qke}{vWOU0BE53X)Hr7RO*_kPL4Z=0UHpSV;e?C*cgYagdK%ih1KwDaTX z#`r&mUJKqEFMYpR#&-sr(v6<1cEN|Sx6Rm#qzZ1ac4P*`Zx6EciQpUB$|zh@{=Sg&6h~{eOKWpy7Jz>7(V|^V&Z9<_dn8z9sX?17$aM zo(iF4i_pUrvU$@seu$KE6F9Wpp`(rWvY5hu)^qn}UN?MvI7DmT6xThan_^n;D!*z! zU%y@bdGemb8JTQeoe$cI&SI_KOX6cRShI2&Jg!e@)wyabP?&?ViI*S^^sx_PI=xsJq`sN);H z$-gmYiSN}gUcps5KVK)q)5NEf@$8|T+Y(eNdCSDJC+~i)ZJHmZ&{?~$>{FyPv(_BTp?KyU4A}K?zqYufh!LL6XZKrP5xlM zV%y>s)pNc${k-1RSi3l<{71%YS9eyIOF!mKOs`hU`O&KS$1ZtM)zS8E4;?@AI?veN z@b={i{;Nt`^S-Lo?D1kHV@*4v+&(z8o2?zYmaID_BM7koX={&DX0gG)>FBVWq;F&W!H5RxjwCe$riGbKZtMubxi(wqfrEy;M2()L;#P*##+O-@~pM zhFi_q(_9);{prsd%TG^(Kh6F=-|o40mDZ1pjvl3wr%#?tRVTT8VF zf9m}?ksU1G&h43`nHx_xVu+-$4Ub@+D$-dj65A7u;JUX2J`DTB8 zwNSyzPFbgJjq?w7vP4abUid>{>dtrW|Go(<{+xC2T9V;`7R$@b`!WY{q^tJEuLb+y@!$?dQLsQDR*1NhWWqWmYUYA=uhiw zwbw|qGBHv6UlD;&3;Ef55io@9qD6_{X7sTT=C@ znj_1N)3e(4B_0W?%}WgC&|tPhsa>xw(AJtHYu-d260MX{-IQf1PJcdCO9sySHo$w&$uW zNr=`Cn9tpR=6*_GW3sCjIxx-S3y1u}}00 za5r9X@b8%e|M}(j+e=C86cMVr=)}zgl|Hj|k`!fu~3J{j>P(cJ9^UJIqbR9ZkC^Z((~U{|Ip4n&u9KG zPARB|KV6wFtzWMjuKZK^P~(GVB7XUEi`gA`wbX(oR);!j?Nsyg*mPu*&dh_qw@&U3 zd3tMktM<+JSu1MZU;gZ|TJiq!FDE#EKD2RrR`+LCGz){qUY6#~%ij1$OSv7m`2X~) zvV!le4$ZrZs%P!Y&udu1e!SG@)X~=U>QR+;d)kc->as~GuBSSU4Qqz)-;EIQ+Js!R<)aG;V%>AcaE#>RqclZu1ycczl2x6y|m=RFXL;=*Sq~| zzL=iImu>%A&XVPPj#9?k$?N|ftUo_b%dGoyysG=Zox4p;o)xd;n|ed&_bRt3nNPSC zpYOPQV_MXK!xMLGzFuD!kllDH9xuK@QT^j zY8x8v8(Qql)m|Zb#xL=6P2ABWo{ZSIQ2F1w`)75MB2OC}lsuIi(muV&^yqc@GruM_Sa^(4Q2ldv9~ItdmKp1KdgfvF<;khW zuVa2xmFCrl@DzXjb*8Z6>eWPrrQ(y{d2y%j-?xt0;JJ;`x${D|zO~u8ehiUn$#v1{ zFF54#<+zbdblG|TyhcN_q>7K>f#?1HYllVfMzO~Ho4@?;GB2Zl4NfN%7cM@xbI+;+ z7kNG>#r|DkGtZQpPnAVl^V{aZ@i}a!VAf#1s`3%#T|=&d|1IK zVDlE%wJN8M7B1wsQ}s^fY||~Buw@UIwDz{#1>$QqAKUG5^rP7Q!-~SE?9{(5_!9Z` zj!fCJ5{3(pw%Ugs{`*S&$g+R4)P8*T^>02<`S6>n>XDXLufxh_mYq5^VeeT1i6YT` z@r_qwpEmAEOZ^(VaMp%nQ!QExw{2IEUfJ2bc)9jDt-TWx(#y@Ok2UPApW(i|_df6S zWTCF}^J4EPA1nHGx0r`}ZOA!W+e5KjhoYzYt8AS1MKxL`zWj%P=ez~)f;*V@9$)j2 zN%PD0mKTDzR{OkKu6pj+&2E7m1-{nLFZDU_FR5twe6_bZ_Vba~Zc{4{GX-SFwJPuh zBn8}M5DvOFb$L)wj=||Wb+aVaY_-1S_uKA~#TVH<9-PM)1)Msym-ULFX#8zfflL{u z6(zhTvz?eLjH?ub+Mn{PT$#SerQ_kxZ5JLnTOVE?)_P}A@1_&ZC;InKF7jVDv4yWt zvCR1Php?H8Z=F)%o9)+J@kOa?_543yO1agWQ!k1|Zp}Pe#VYyytwC?^K6HI_ z#Zy&SZTk61yOvFP>)o??TB_xTcQR}{8wJ+nAO8}%lV`_`g%+o3n3jI(x~#Rj<>Mar z(z?g{RwaDh;yZPDh)|~c)1~j0WvB~>or_5S{Bg43s%+nV2dq4O^)7LC&6;m<;KRyq zUtW37+jcJPsYX$dMJbBv}> z{QSbf)n|U4{Q5VK*wt$*<8B?&U1`3%H9X-+%*1s6<4#(y&E_+`T>evvJ8> z^2#pp!waQHbhgbkIgl^z z7;E%5ChhKpx3gTiJx?2`#lK#{eDlEZ9G9A$naO5#EH7W5-?P4=Wg9C;;OX|~AA4(( zy#4g`%)TE>WfQ2~#(vjncjt@V@I{>58kftYTgyLf)r@_1HDK%Kt)ozZTkRP!6v8qV6cKgQF4;}k2uQ5M+S@iQ% zqfD9H-OpFAK2|d=E4G#UMxT{kuDqcNqM(c}$IaS3wlRtkqm9>3w zeQlb<{12ADf7wTG^fDCs9e3i$J)VZWvoGCcQ%;$9@7m6X(Z>Yu`uum|T)N_josF*M zi${_|Pn1$vg^K1?zPPu^>?51c7u`eI{XA19UNq%i9=A|@Szk$RRcN8YYAvJl^E92m z9++GAm-Q^UqVSySsH3v>fD{?Q7!2JihAp#*@gRz7u}0ldq(Vdcz5uHPDa-6uCbdqRWGXa_^%ApEOMN|lGPEq!*92b z?B*7Mt=kT<#!grn`p2oq`OOlCwU1iZULM}1UX)kyV)Co4Gp*;(i%vRvS*GUS4AnrU(T=Bbb6OINBG zZk%Rlb)ZeAPxbGqi{J0_*siE=c$Fe282;tPjw>e~KaR`z;oG$^+%%<^U-+EiMEBo2 zCJElX#9XuT z+5Q3>o{4$hGd6#ka`91VRH_Aao77{G;8jA!};b4DM#H3&z(1Y`DaRh zdD{B*5guWwTukc)8(MGMKAF~ecFKYe;gbDRmVWJUI^eAMO}2(BzwYj|!yFS0ii-`O zWeDc7~|V! zKdz@8DV%P=D=>e>Y|f;QK`T$X-}sPdKVyy`&*kNcJ*#KFzx?&;+EVLaGm~hO&s^I& z)SJ<%-<`zl=H_0O>K8k0hpVpUhoYk3L5o_FJm9FZmwR_@DN@c;%@B0Z$iHO?OXH>sb}y=GAoL&xGXd1{srbHr3yrVYcEf%el`su!5S({^--aKpyRti*_#eF#o2yZ2-MDt|R4*s3FCU(_e|*XG^PQyii{4o= zoF2*1CP!AEN^>gMavJ6XTy*S;7<3)p*KKY;xlng!+~wChj>;`=ntFV~>D=Q@R|Su~bL6<^lW?TB;YHQH zS&CU_y2Q>lS0nd&&FqTfXES)Z_b!+=J?3DaJmW1dS=s8>UYgGq_LV2N7&-klun`flFiAYF z7V+9c#p=KX=_5NI8?t<^zw<`PgGu>tb;4u$x{S1!$;C=)nH#QNF7a(FpIU99q-!{v zD?IW0uA=|0)o0(ooh$Kl#s>+0!{UCe?9WxZZdc9u{VA`(NqLR)%(kZNJzJ9nWPCCb zd?r-NocyjAYS8k1!n1=NZ89qr|1Fs{tH<=~u8eE%w<`#K(tXU;yYf#hmoc9@NAJeJ zmpS(TeVu+krn;^0aL69EV52dwxUjO|q zA6Eb4n#`Mfe}DYE9Q1Ruj?lt}<7dBnCP;qB{I=i1xOV;KZ~a$ao;|{1+$5H)5$Z9w zy{AL{eYyA4lka{qvT1$oUg@{atf2eDgSEH3H|ssmy>Oi+xv_1>YH7z)e~ugw%du*^ zylYX?I*YitW~;~Z=eDw`<3xMv8{Zf$C~R*OZbA*U7ov6;4o}Cy!h=6rK2yI#EdF9>TSMpw0J(rSeuj||883yVIfm&CrZ zy}!if>Z&)rDW@Ln-oHW9>_%Kf|2KhU%adju`SnTvn^B9_Nr%Qt&28nEGLrT!`DYQ{ zq}`ZN>M~id#Oqye2S!rQ~m~fJ<6Adi)r)rO@95fT>5!<^(+QE!qdGo|B80g4+RdX36W7g=Z{ER`U%X)J z(>qH8UTr*JnQ5)?Y2SQzF82bwB?#N`_s+oic?o&C?fym)I`Jycz%8 zOW~pN&!#|=IK~TgYaZ`7k-16ZcJz{Tsc)vH?%j)LeDHiB`|nZhg7aHFWWIPUWNTNx z;Pm0ruMZJi!A++oMg#XbGV4IfSyX%NI%Z8NK$)(e;d~M%SbaWa+)3My$KMivn zJT+6Vtjv11I{lzM_x#-I!p&z^)?IS{YjHN_-Q%~NqM6L0=db;gl)DofwJY@YUaN2a zG(@&KADHrf%Dtwgvt7ys^>p_J6wgnQ+2Y20OuE2(;*>6r7gPDxZpq<%Xz4gNg3ILQ z%^Scw&_KM<@;+_T&p&D>aoz1Gj6NKa)E#BFY}j`SZ_L5XHa>%MCsrDe}~l0 z{Qt9QnU17}_qU&m_602d_2BbjKF8ZG+1B=pPrd)?`Ly}B-ZA5V<55v_H?4Nrq9d5| zt?W+GtfvVFmumNNEO1(&x_4i=a*4>SkENyW--fS$`&de_E$GDcUJ1{;JIuec@vhH# z%G0Uw$EBhsHY%(s!p>E4*;lP??{72Qn3R31xp?|FZjq+NU2i|_ap^Nkxqs32-22*n zv)QLA`u|4=vsv&W=uq!QjnaS3RhJJ$KexH{{O`gi)rQwiCv)$TY!aAr=v7eO2ambzJKEW+ zUl@Ow!TYkuE7x|qvD=}t#r$QrW_X0@FZ#w-FU-CB&nhjO!^w-^n_W3$TkTe(are#9 zZ`V~0e5iB{jsAZ5uFM+oRgw44|Cn8S`HX=X&tw%v!NOxrijJ3;U8-BQmt8AEY{~8r z@nflc5_WCRA8E`v+HC!Q`_8pSu@iY6_DhO;O$m#bJXtt+cQbQ-R!Fn4knPk1{_p1B z-Nmr&wpe$ zOb=c3?(5f$idMphkNk|{JrTU>n9IU~JicGrm;M^97I~pA=53j&d@;mQdXsxh=9jbS z^OVlqyRd5QrMQZ(Il%`ug*Z68VekH#dRFpu-s+T{>$-X7ru|#@s!XuKk3VgdUfMcI zzCBMXcN@Dd=1(e#)=&Ibpz@?QNXL=EW>2vzZ`4J_Td%o4%~vZnO$cv1+^u``_^n{4 zgQ;tEuls0jFTQnei|$=^U4{P1(l3r(*6!r}%sb0aJ7(jZ@F(mFUwv55)=yVmyV9WF z^sc13)@6kUAHu3;MPBN?!aP-%J!tB?%eQ7Eysvm)^Jlk;+%hqVU3*jN6k~r*FuHTI z{{FoVC5@(B#=7N_2Gd@p_#|vG=$1{NGs!2|u{?irkR5Nto}l1nnO3Iw(wz@v4p!H{ zQg*w4Bj)w@fDfPeE10Zwv-W>D`c(B~XloUN=g&!(O?O-?{doI=XJkj`|2eHc&ia45 zlbYs~^@1V$pl8Ze(Sx18u6Mo;ouga*cD+SJEYDSz$-G*Ot&`UCP2D>;VUw5TX}4bX z)$8kI40zU_dXXo#=};8^o>0BZpRC?KKHp@UC-|ti<>k>+Z!&kZxamk1KKW*)^}=&W z&Gm%(?Hi_9gm{1KDY|xvbM}E3A4~#oGUkV$-k{3KcrRAQZHCyZJsGFBz5jYhalPPl zw<|4IJTx*(-brXxS*_Fk%oKUM;ap+ewubDU$GJ@>kN=BLKa%h^S4B_GRa%1Zk z1x68B#>ppR`#U#@t@<{9PQkUA3Bebyc3%wpb@F7xcgbl?h6jViPIW(g#(raO>MMD> zs$z!FK;j!)1KAv%k2N&06y3+SOUpZm=uQ(cmimK7Zp0 z!{_4SHB3?B&Ii00=X4xUFnb&lu=iN}T8@P~!`9h7@XQnBntt`b8J!tI`HkttZYw`; za(?MK=XUv#$m?!vf6sT=w{O$g`wNawxHNHPZhpMcMMYEZi-$7(vaW|{hAn<@yiu=lw(?UErFNEFu z=X|p9rsqCmmrs>JVjkjA4?|}gRL@n^%D-2Mr|y(m--JUI4I$gb#dlr1&^<9? z-ppOrI$6i~+m4*H{Hz#|^jb&W~4%p>3Y2h=38}oeygqtonKvig=M?#=jY3M zc;e*bPQ(YqJ!cG3Fq!o6NaXvs*xa`ebk!TfIcco80deleVPksxNtYdCA+Ll9q_2Q6W0MVrI$r6rTU_`RT;f+vxLz zc|z_>-w2BnMt*&Bq)e1jzNsGTU*@gQ|606GgNW$Lx9wNe zVsn<>x&20H;(BSv9l@c_?!3#q!~Q>I6?><4_xFi@jXAO^lF!p8a_*4 zQp_qhE8aF=I7j)IFsoGPgHuxKpC*34lrQ$#;v9cxj(~MW-N}$eGbbmhSUmKY*em|o zc5aP%|6vuT7mC*XqU}%PnTn%AnMz_N#7|>gcEkQ6(~5I%w%$urWNz4b^T)wiVjqgG zdAjKC_xkoCZ-@8V)Q*`F=btcrt(E?`%uVXjlV{&jPyeb}F`?U2Px)U*xX$T=mbHwV z!}3E{eehcD`a%2jt~J^IZekas*_EcMtYhC*?U?!aMhUBsN$`|=i`F$7NM7OWtk7+E zR`jzps>;kl`OC~Gw%JNsGz=fSxbS4Tqvn~lQ$H@8n8UT(@5K(g^C~UtlqHV^f7XnB z^iz^If#;I!^Rf?34I=%jtC)^YkU4)o>S(-n`tLT6Oc5__O{qn{_gS<=OYu}RpY`3| zX0cw{eZ|jxD^oU1{^6x&W?6LO_#d`R3f{n~+J(^v0Tv)XRazkW__XO7H_ zA7`C+eRJmFoYYasm0Fve7!sDPx)oIsC4&TvRQeIK@fqUt8u31T8_8(@h z^x3Sw;#9#kuJx~79?eM#zx}Y@c>1@FiXWA_?l!+OB&PiJX%4%xy~g!qmi(qeZ|f%t+UU5l-qiJS?_&FPQd`?fa;r_5(K#{83KP0pigp`xW1+?YO`2yT7; zMvA-T&Z+fGzZQ2(hlekeoD_8E>FpGo=O?G`R%8<2trckH`lq^}u;Q#h_0bcnxVg=` z-tB+$dY9(%mv2`!es#F8mNoX6fQO#P;Z^T-JQ!+Oq89CwR{+KBBZLKCfMR)z_nsepTh2zEy1OTe|LJMDjH^*}L~`-Ww`q zFYDX9yf<+5UO^ur+4Y}dVxC?oKkAzIWBb%kj*GfZ-f};3&q3(sFM*8nEX`Z|l%q!N?5d}m z{_nB(Kf`Ouof%h~FSlMJas4FE50V1sf}FqEsP0Oc>T!C7ceKH@Eo>8yfBStkpzVdr z&#(J$`BiZEnu%@L>v_hrOM8DDw{5$Z@iq}l_MhLb?mmCaqVBuwjW^HL3!=T+{4c(K zbm-2lm`7sQlJDlf-`x4>sEol|{Ui^?oo2fvG|w6S>Qs|R@{pVPZJ$Qn^sAqpZci85 zTYJu|n$P?mSKDpYx6frmQu5x+im~4Or|0>>oB5BNKb~Bj_3Xs zb?8TiV)*$b8fU8e+l9GQML-k|KG{x!P7w$Seqj_J0=cWrwqYhX(zBu+j{C?L3cfDqp8=}9; zRE_;#f7+J#ls~Z8dS9GlzU*1nEM4YLZ3f*;M?Yn>7N+KX?x;}~<(b;OE52}U21B6F z%=i=T(`ViM-FnAuVxNB1=gK+a8Jk-tG97(2{rf5I>Z-r&?CMpU%k>IAF}VHRyfoTn zMjC7A`X|iO-0b$ud>vaCHFKd?#r$io8<#sQX4!uwymIM@p5LZz5)nrWcF)BMsdHx6020K3# z6`x(WlCl5Ft)mxat#CarKI6l@)ExI+e}widt5!X>aq3R_)PpTa%Z-e;OSAS@-kWnHs=U#( z>wtgb;>ah7QI;#y9|^=(+L>=|~o#(t}R=?{<4olwS>xAC-JqS$M|kO#PgL^OYh?!NnikRF!RZ#xX6HDF3&0`5%U(XMeoVo49M|BrpBo ztH^r&vxEB^keJ7UXF5CrS1pbWsBEye}9=!XI8Xo#yOstV=4T`d-orFpnK7- zXui+I+bm%Rmw$AM317bS`K~qB?dkDrG0Hb1UcdY;FASZu@GwtEpa zZYTWmk1Ut`tenxvE^Q+5I_SQlxz~3Q#>_r5t~igrsNFqQ)0K*5F0lK%{qM!@-Sh8R zJ!`-ymr7+ODVT@nqky44=59+=nus59#Dh3{Vh?araxPe3AFH=-hBd zQ-#l=$|kp728F&gdRYH(lUF^{_34Rwy4s!As5ATzocmTlW9Q4*1E0&-`7d36Gs9>> zlhew~qmzS~R~MYMJz_X*Ma-8O$3u?KG}+VUyfJ;XQAq3j2_k$E2^RBbayFe1-dL*r zHB;I{G245d4d=mydp0`#IoqnDedy{NvF|b=GJQG*4L9rFX!^D~Uh!@j2Gkxpf;KK`6O=b<1)?=>Fi=Qc0 zea-!vRmt{idB3FReA&RpUUuZ@u2s<&)str>GBZj=73S_xOyge0TJq@DwQb!87hVm2 zv8O(Ar?lZr9cA7XYrZ^g-nm0W(1h!;(tM9_n>T{~r@16~=bg+w8vj#+C#Z4$DqDk! zxpN+0JRkM?b;Y9{srUc**Ye!{8EI&o!J7Z|h*31Ba^K_)duujxZ9glno&0ppxA>cE zGd6D$*m#dotM8o!U&}9hlk*IZbtbyan|gNnf?m1w8HW?*_e}rlxPHw;!QUEQ&i6$b zrdrKB`*OyGcNbUY>`UjX$_-jym9=onoBAN6?jaRM|3)@tFU`=azj|fvB#4) zmvAXxlKm@Coan#F?_go$b;E+SCuaMvVOkXtQ+2yBqW||oJ%%%LWo-%=>h`~V>wZdT zwyDhzvs1b2ot^m-wu=@Vnqsv7w(PujwJR7m1zyh!JfQXb`P-$_nSQS2FPiQ)xxb;W z*{)*a`eWJ8zujT`b$R~$H~C>j#ozuPnX;zXS&IC*`K>0~@HDpzZN<13SW=+Bwto1T;V1+MWtSSs@CpyCdjS&4^tib)^4X1QAL z?+31jT1MKxPw&<$nInGoIpfopW;w@x^DO3=oBhl1^{pL=kChg=dA_`;^rt3_|IO!W zH)G3!3f3ia*@R6rPJ0N3xi>GlWySq6`}+4Z-lc1D@#|Ctl~|9~F?hRg{{?9;O+* zWA@X1uih1|`&IF*v9aqmuZ))}&qAZ{2GXw02!~I7i-H_cbDM>(+^E z*!TGF_rP&da_e$LjR_$ zHyZf@Q9 z?FJ8KEz=EozNRLH_iW)Sal_I|21OCoqg(>=8CDW<57Y~+H>}BGJ%6H>=ir+CmsM8& zFwAc*OZ&`clJ74O%WQjY=q91$y zFZ`Rj-PnQ2py_T{ce8)F?1tnmBEK{4$CpiRelMHlt(=#fYrHXjftdTJiF=l-2un|u zDf$s+)Otg(c1Ov^Wy{Ze_&a}ZRkN=~exZ+HgleMI)zE}vG|?)J$ntbBCbnZ5;3uuapeQYHb1WD11x91E>&2t;r@y$ z8`Y94zx-NbtDp0Teh^$8FA}Yp6e7O4$9l7_L&q5{ubbZw*_~1RnqBPkOZd>D{7nb` zZatrucE}{cjP0q*p5qGVr?K=+;ujF>@!nsN#WG_SaH|4Bsu0`SQ%d+P#FF)M$Kqm0nrt`6MT*CoAnUUx3h&ehh{CJ@LxS;NtkF!S0vfQ~fx!ZSs4!C>eWTx)YfA+5r zZ{KKQShD@ja{Egw4;*{Fez#$b|B{EBRqu+q+&Dk+)w`7^SLMx!UFZ1JzxMh1iaQY^ ztJEE;zE_yKtvF!5IC)#lasK*!#v3#C$ceaq=(RLe2+DTsUoyGsZq3WDhW=YCHJpzB zj1059?0xCm6x$pH3vmTEdtUtyp$lKO`G2%puOu9td2I9cM+~u@wV`5Pj`-wkE=#kT z*t~t2=b_ppd+{$%ES!#)$nV-wAW*(tGcM-B>HF^AGoP;!Kdtss+b?9k=vC``a+k## zZ9CTqYRMnmVUwTRF1MaTewn#gPpDAE`iMDGf>^Yr8s11b+Wck?_PQ4HWOjRq>)|D* zWWSc)&UMYMsV}H|b)mSdXglKr2IJnVnMd!sTc#XuKNwq@?&9>hQ#<-a@+N_6j>12; z?@7P7{H#IboP(GDHqM%wFJ#RAQ}=7|@wbxcoZeqrds8`9C;f7gDh=bk{&ll^eTBZ= zn?F7Nrm0>rHCtUIYqLw{h)uok_xSLg%e>bcoH)OIefdc>^UnPDiC4Z$o_KJfuHxf_ z?TItKWi}mt_f}UnYKvTPezMr?4;}o>9_lj1)f>h8k45PT#vN;x+_=55BT#iWv%%i1 z4%z)LvT~E3?~lGDVR!N8J>j`g%ie!?R!Dzn{XbqPB722wEN9_28}U^k>eJYsep#XY z@AHhBe({x6YB^JV^cWYp$}Y_g%gg$a`k$$e#rJW@COf|+z7D=04qp55da}$0hJLZH zJN6x4`hBVY;dm4A*||26U$uTn`)z2?54T_FATVcH->U}G*U6h&#ALks^wwu={T>T&8|E~SK;^*7-bKB)~O^$UupR%|z zc1A*H{fCPS&X>l@9eBq0G~>Se#XlDohu`#y2{gVH|BI>a(##)6Tuii=>_1S+qrB?q z{=4^s^PUtQD)>;w*RhtVslMh$_DZGc5wf%QFWQ#5-&1r0)0q!flJ3n7{C2oL_*Q)9 zOH0{H%z6)ZzdXg+In%yPy1n4uhm&hr4sFT}x;|5C!`9h=Eo%(U&-i6fHV;=5k<&G@wL#h)vD z@nI8gb~o`Y|MTq#k9&lCg>6|t_eG&EL0TNfYkn)9+J7f)W8XK!J>DtlK^4rL>y}!x zM6rm3-1%{``sDxVjKV2z7q9ZO7Sqq3q^!O5^{t6Ij{Z?Y?A%Vnk$;$iesu?d?tRxq+1o%D=m2|C=4e%y81fpiRTcX=dZ?FZbRs37T!2 zz57466@R5u>BnGa*P|1zGc>JZ2<$HNE_EzU)O}sAT>Em{$u^rKzjs(|$XgzEtnOS` zkI(=57l$V@nTbVg3+dH7+g*Biga641MLsJ}IW~P|&rY41HjP&>BG>l7jqK^1PpTw1 z%$E1{E3Fq+I;Xz(tM2o@yt04G_KVj2Sex)cyjr%C*@~y(?+wc{i%oKlv%R?`daS%7 zEy!q?7jRS`WJ8X|gl+IFG0(Ho|3j?S{4@O{x+2_c!am7(AC zpJh5p{|#W6*b-Qq(d+cg%|2l@)9I4L7e>V_2XBcNo?_kO^4;o|Rl%&jD zuIXDu#rYdroTYR3OmJLxqQ2rx+Pm&p*$*7CAMZSBW7^Dnj}xAmxV{#;_1Yk1j_kvuyYGuyJ}p{5KVZ*hqe=4bXD$%m?AN;2{HE11 zOADV*83t~0M;QPAKKl6m?*f~q#T{&Hr_v2A3Lc%f-D_mc_v1(T-r}Ol8bjAiiHx(d zJ_@2mw`$i-;&>A7@8Y;|wR6lMJf78GVdSHiCz>YQQ0!2^^zQVmX$$tX z`gZuUKV{goh|i=e`BTk{y$9?f+--{gy}$o=!_G7HyEy)rZ#2BNR+4}Iu0$@wZ~fWr z7s4kc=KSm0V{kd&k% zEME3ey_~IEHItg$>%0H=u77=Q&*d78`N~uMjx;Zs+Mq0^c+WG2xyUeg*{hm-hF|uU z#jf36?uK#=EIAB zj|F_S<{h+G*=s*FAHZkK&6{}d&oqQ0I3eGlGF*v4U)Vs*9Rr#X&t(kDEoo^{S%`}@^1zo5-bt$%eN z%{}u@K4kHX2Oqxvb<~o(tPvB?dUW|i(T{~3RVVwr`;K?JPuQe+x;;O2Mo$Ub`nE65 ztC#S{%*&pz#A&irvin_8clAGUHpgc4&AKD|_Jqy;rd=YB#2)aRuDkj?RfF{%>*-}v z56(ZXz{jU+_j%`Y@4(Fh2k%C7bZ$89{OEZ1gdfwrZgjo-d|}a)@^mqcRXb0 zc{Y2JZNkzeVzahSO_|^OWQ))a&(+f_FHF}9n{Q`r>)v*)f4=Q*i}tO*{}lhe>A5uX zY~<+$eqMihU-f&*C?#1}Cbwms57sp{QY}826I!TMvZL!-xJ{bL?AFdV+fTfeyB-mH zG5Ggk<>!*eue@JyW~E@}F~&m%)rzX$BGKu6Wb2m7x9kL3C>!S ze!c&eW%%V=8lRF&pI^~Bp6a|NMo02w!tB^eXWl7KzMU5gwJhBbEv&kd*)RF+-K!N5 zKQ8WVbhO)EDA=Fy`*XxDcAb7TrkUa|Ba-*mIXpWY&8!)?$7tu)B(|w4%ID{`d|G&S z{^^Du# z+#j}eygglz`CQ~`0z0pTzrxzT7iM|yog-T=@bK*__t1*8TCr$bQ)a7*Q$ou=)qP?J zo%vGi-o_ZoX&iIqww_W9w-?*VclOMdU=2l?&6;s(3I{k>Z8!dSvG=E|>zSFKxqtJw zY07gkEmc@~H1Oxgxu!SoeS7ShU2^dKjrSL~Ui+K;WYeL_ou*Fh%CoN6aQ28FQ~MN9 zkaqTElF_-jHC?`s5|{SpnsNDwb!hGM4^wMz(_vgQA>v=?W7SfiS+mcci^`aIEN7gfmZbW2@r-MYfq zd193NIiBoBb=$)ig@n~QuTHf1?X0s|llP`;gDz{>EeqWuZzsE>UskQuet7F(!4Cfq zb<5g9BIoDtXZ^aGsotoUDN!&q=%iQbBr9(oi$6BMKLuarvH$zx&_(V2_P;-!-rT?M z(@Zmgh`QbTH**Ug?eTKs$f~>4>V4LP@8?1@HKA=mrzVDGsQPt#m&BR;T_K+yVs9@P z@=$SqtlrOlkMD2dkKbBm`FTa8|EBs4O;^(A?c(5N-JSbAK&0FG=I#8GMY39P^Vr{b z^VqN8J@{3~^ie{%wV(m(sg^I-@9zF_Ol0nR|T8GX$0?%u%)8|H3h~ikoR|Ir~9BgB=Uy zeoJ^rtXlCxIhSj4trg?>53En4LZs~?SpN#K`S0EH@sIXL-xt1z&oBP`v9Tw`@B1z1 zGzouBjbk3d7p3`vYd$N#;rQNp_Ed&rv5@4;>X!MNFWmcasr9+a@$)_>TBk?E;ko^UsY%dZnyS|7!9-a=F8vsdc(#|KF_)E1j>|Zk?+a z{LjpF-i3?x8;^-|-747L%Cq_J@(s?pW*V{&#lC&A(_RA$obT z@1@7C&mXPUdmZxX?8VI0C$z7*wx%?=DwLg4SZP0nIj-~tn+l(cm(R~i$|<@+?msOx_3?Yspc)bDS3WuqOoPx4OepN z@(y40eCOPx&+qJ){rXqc%P(;izs}r!zIEG$<17}o@@$@w@~+qUULEfWo1&Pz$Ygct zpTNv$1^%^CA^+OW@b>L`YdE`e!M66sh#nMzQx;TO6s0IS zvG>c&aJSbY5B>cDwiM2=G(XZGawYt}%Yv?DCoO*bIOG>OQ%&KxF32&qqw(KDxx8mTr4@# z_5RV~+jrkFuKa3bf1~uSMcH5T|DTHg--!PIqw)XUxwFq+&fmW!=<2?|X;Js=Hcoz3 zE7rN=$%_@6r>glr3lm(p>emK#MX4p_7dQ>3&g3pF|LiiwK2h{rs$h5ST#Gda&8=>& zGmYGmcynRE(@pw8-=^NYaP4>-F}fxuvbMn$j-coF*9}IMYLQLZ!UJrPYfi3>Z45 z2imdw{WXZY_`j1c%pm9HdgC2`Z$1C%o6xi-@}^qp>*>|UllxVtYOpR^^Sn%I{iN$D zr`wn5T^Ijf{h>hO|MF9{bry$~^#)&-Mx(v+DVO{Uj&$R2t8oa&oF(nUF%;$`D3{gzX>JW^D3h%(DE}$~R|P%S}DSc`QYL zLKWp=L~3egFh2^tcD5qYLT!Ip7F&*T;r*u09CktnmYEfCy2_p95Bgm*IE-kMFm+&OthbneIW7r`xEo$`&Bd?Vup<(V5!G4_6z zl0TN+UB%uZ>B4*4Lct(%<;esyZEtDy)87s&+|b#xPCQaNtcbns?yPKHpK^=c-jVJO zCRU-B7CyLD#&7f6!yxR4p4vK-GrBAEOfnZtTw8M1UL-gBhg)DbZ||4H1C4GcYZs(n z_gtNFuE5hu;O6PWrdJ;PQ{+)xxjfl$+s4?w)Zbb{WqnqqnpR#0dlxA+^acIx-@U@A zSHZq&!HZdwwGN%$&eUvbHSJ|F|Ao$+V7Bgn>YaxX7ZdnCt`zwd@G1AN z(~MJQ{tV96?TOl%y zI{g#t_BEI{wrcO@JinvKrn~Fat zu2uf`>4nLWG>=VP(;L+^--#_TeZEtp#Zsp9J=bHV;|4mF8v`bqUl&>R)Hh?>fhkgV zIg+;XTv4;(G1q^zt6=KKb%}c{xqg($ZA%V~e%r$6qkn~XqoTAI}{&Pj(+Zk@}O&+{{ z@~)$7b-mH0Pv_!l{P#}MTzseKa8Sn6$i-JSTSWwiOu8kMAA8AP=4t!NdC7wFgnBfD znzp9y`TT|{b@x(hx5+aXv1Tp$=RaBG{^jHkPm4HJ z4F7cLy4HUXX~;H8_Ra0?+2!;8`Wt77TC@E}Pp)F`vVXpK69ZFmjO*8#I&`+JQ0~$JlUWtgw*q%H-wHqe_~r{{OW(cap1o&Z*Zp2w|8?JC z*D1HzcW5Tfn0|7p-ZR|;E7rZX44&9*(|_~eyMKJIrOzktn6I(J%;(}tg@-eFalz^3A&Og6XS}>69Vz8}TxZLxC63*TEd!=?{Cwg) z$396iWfRBCqX8}R!bT_^o<+0nRtZeL6P<|Y4k3=h|--7D(;;cZv17uxqN!o8zwCrz9&t0en*fnIROocW@&&Gv0u+ma)@v6Sob;@`aA`dm!k zP7`P0t=swiiw;*o^&UI<6-{ORC7zDm9rvRy+DnMC?oOHZnCH{^Yu~Dwni#Y5vUo1> z`DIoetLzMOKlQD2i};Z#6GT^^7p!JgU%ld1=~t=yV)0*UJ>Rq?Esc#!N=?jmXge)* zLFZPJx=)1K5o6WUGS-^E4|!%6JiC9RIcCQG^=Xnil`b)Ua%I^YgcBU^?P#kI<*}Ax z;}<=~<$wIlOP4j8HhSR)r{Ony_js%cv6 zqFopL`Y$&rt-TkoyZlmPuBlv3JloROOLUH(Sd#1V%;Jk-^VA@w*X%BV{Yz#RJw3La z^~aKG(!fnWf4FnpYRKn7N)_$jz~Y_dHusXQGBjMf1|h zk97Q8bQ+?|CPbc%e3Upby>REkqhG?lDVO|vt!(NZ;$rv8=Iz(L3vcX~$-6XnMxE-x zo2mO#onz#Jlee9b?T$~jOWhGzE_v$NQa8nKTA#I&`)__%+urJYrIh{L!ZWX}e!g+N zpR$uhFoZ8*&RfyGsO~+oCUYu|=zipQCBnUVj<);sU3tsBN>-;Q6_*5iobmd#@bRkq z2X@uihvml1zACW&%(A;*E>Bch^sD*v!Wj+ewvO{|xPE>3%d%R!;GfPj-I(wNht*iS z)?~7;NJ!z_o@)BoeR_6oWywv&qI?l&qqP?$OuSCnUp#jpVAAU^4Bs?Mdvyw{_PmYC z44fUG`SaQ1%GDA=&fm-?NM-)!PAuHK(R$G$g`~%+Ws)`Q*>hVqx$OxxJr)r2z*^vK zh_Oh#<~!A?VZS-=WS7!rv2U(WBy}L_g#%D zE92UGAtj%-GX4DQSkri}IXKpWM{a@2u|0EBJ7%6?yv)7oLG=|og)6tOggsQZ482$9 zJ-Ky#Kor?rgbK`SVcYw{`tSxohs-$l2O;SuT9_ zrCX0%T>!6;iW&Phv-kyE!J8y>-nSD$^Tj_tb$o; zC2V$P&pw&H=XC?$>GTC}6Yn3s_cdb6(%L-B)%+F9j|V4w{-JQEm^_$W_OgciBERskEzbP zD%#d9-RT=x;ZZLVEgUN#-K^{peNrT6`6@=R$RPzPT*?}>!+4;6RcLr zCHtP?T6pq)&SX2OKKsLZe@(=9SUz1ET#{*><~8@!Tg7kOJ!_{;5i)G|x@mOu)MQDe zN2?b;{Sk6~iqHI|v)Mb&7~V42mBr;*lDtSZU{hLWm3!Ye#rg@;g`O1bp5Xa8^p@AF zwQQFdR!v)Z=l7K>YOPaEw$4&qWc2)csw{JA?59tO%Zo}YU+%qdd`Z^Y;9dJ<*p}HZ zIxcccNJsz9mwAjrMNOTGM^1_Tof{ODH`zOx7@ihPA731wI+qPwtTR80%nVxxb*Z&oBjVB#HZkQunlD&Q2 z*1vP|lg|BlWyQQuLclaw_>^*YU6w_~x8IWr3@6=EE0EiC=TWNR!L{~%5|6(3-Ey43 zf5YYF_MpCVrlPyKzMWsL>QcMiKf2TD`^+O!%pEz$W}l1KOI&{1DX?Jrq?a?VS@qvJ zlH`7I?X3PtdCwaPeh>cqnzd(-N_~~bhLs$zPk%UR=yf;LN<1oj&TaYqFW26lxHl~< zO=iu%{i0vFY}QV;JNbBR$^Td7myWNkoUwXo$|^(2i6>?{_d0TDsHS%$%L@UFl1#?Z_ z_WJzwdhE@Rb*E`!=$>cirfqdkRp9%~X=ZFGyLjE)&zfJ&?)3VktaF$blGMIRY2NnL zdX=ZwB}8-b>}nBf%5XH2kJ%VzKQB(_cf-$OH?{izokm564_&eA-K)$u<;x4{Gosd4 zcAk7PGws8S14WV5jeWoR^;XqK$5>77oc}D???$xVvkS&QGZuQ=x@s$I3x4SmWPjyt zSIUa@`AhY<*7?n-`gCUl_lkKAf(H-&jB(t|e&V?dcafd8)2kWX0`H$}zqj4^N|wv* z|6le-r&yj&Jim$KQR1QbViwu?YLRLFXUe8Wyf;#1Iy~|FZI!o-wS_!hSK~gvN^@w- z4{>HX!Tqgw=J$hhB`R81X>#sTYhnvsB4uayS7+Om4U^bB_olbbp*q9PkbjSS}Z(U%hG?@vrjDto;TXIBxq{(E%0^?)4dy3_ufZK@l$crP2r%f zgtd!~x^^~IJUF?f<5Slh{&35QEAPIznOk(_`{BcR9?QP1b>04;mRHUH(3!@mizThL zg>5$UX-hc%S9j)zJTry&&7Unr7l?WvSR0;h_H8kbiv^GHAu)9^J*)7;NtJKfSMhV? zeo&iYD8zN#`NO@YM47o7)1_*(@a*&sE<1Kl&gaS>;OwC_A-pue) z7F)@!T5s@X(@$w3-I~rxt!pOiyudQ8m*;S+-F}7TN5g4x4l1; zHRaXR;43W?^Uv$~sc&No{M1*o$zulpjEOjiu-8 zn86eKm?ddz&GDJ9@5@g-Y97b?>2ZsAdA)!Z!xsGp%}DD{C+2_rGwVcJ-tN2{1Fge3 zGgkBSrJAbAdx^|X^_|Y_@7kXBvV8S>-oCqskH5|0S%2-7!EXZ<9`Osew6CQyRZiLT zJL3JV&raF@+SQ^Y0xTY%DmW$Hk$$`;H%3`RBj~hTP#v@KtJ~|ZuJW2KF>}EiUeOCN zR~egKEe}8NPYO18^-`qSQ0nQc)Lk>)JrJroe82FX&|Q(%ZP#nv^jE*JzSE+3y}i+N z#hI3<_`S=ebIiV5|2;VE`K$h;|Lqcky1PzSuei8ijn9N;-K?4i9;WB)mnO~9)75zt z|CZzar7Jh~1}L9lyRhTxqgZX<67xqqC!VOcD~4E1X*Ut-IDVnv3$wlYn(5t=c^`Fr zW!0@JrewS;RZM=VIInb>_R0(AtP+KFwBv6zP2HD1`FYVVtzZkb+uhMMg7-HDcL<*O z^!e7q#9{|iy=^Pk?X#?{`}gK`@sx-coXhsb)uyBh)Kq4*UEh4(ElKRQBWAawjvLr6Y=J)4KTBqeX|4GYTee2nQn|_(Bvl180F*@@5 z)x+LXa{0H8yE6R#c4C75>m?78ooAVbM6pEMRJd0d9q3$DV3-hA>a{}Zk?*%nOUyj% z^X@%2FlevSTlQgrx0M_350%x7FZoY0%0|VM@tqBwbVB;PO`W#v_P)}|dsq70^{Tt6 z)HgZy$lPn2TH@x+udSL@5cS>q`g7m7%N_@=d|1}6xjt;;@0CI=-ja>$3jcBc|2T1i z`zlj~&zD~N+IC856rD_67w)8M@cK-1mB)%|7SDUU$B*+X?+_7Uf3Z^c?HAV14#pp1 zpRE3%{^*m}jO*W1B(HP4;@!%;T#3(wWz}f*l@ed$F*65DZs-TT)RZ+>;`^RFLwn)DeSPI-8pAv7{8=#r4( zn}?6QwN0yZS(LN%-6s@&zpVdDZ-I?Ncvo+*rVAt=jYd!cYRuQN3N{c;+&dF z{BaX&(T9Bdm$99!(3KEfl5lV9j3PD8*RiR~Dp9oAnlp;JM@if1O`6F({4gL@17IIaHE}qS|Ol;bll{Krq75ir1Jls8D znYmc)(r1aea=`_Adp*u=?&5N-JX*g0yjH~TDX9(xZ}u)KOy@{nto87nUDIaGK;elU zAOGv-7wfR{F)h$Oc6cA3hjW;=!<_iZQm4I>{ym=lUD>9^PeoRid3|X9(+3_w z$!3~czW$uH?(NQ1W}D(Nj)>l}-#o4M>wztLK7O&hhhOp5_pmQMXBt(0S3YO^1&*yR z)(dVHzwT7Ry>i>M>%ZnqEU;K{>vJE^+;vgx|0D0e_{e6k=h*d)OvisUO-}x~XM^X$ z)Ef%MG5azW{x-XPI@bAW*Y@bbvbE`+CNU!HOzX4H^>xn(3<}bSXn(Zv>mDtu`Wsqv z7n&Jfn{jedj#~)FD|g=aj4w5wE@i*5&q(QV`E4=RezuNsb%zE6)gI>viN2d6YSoRK zjw(BOh4M|kTBfw?<6ebzjA!Pks3yt0P^vztpT569IPn}(z03IZGF)Gu9~Lrn^W_AFT_%wZ6 zaCZXFQI*~45(X=|cD!n7;Q4ooS>Afam+6%r1xqGdzo>A3FD$OV)%2;~YaN#eooL?^ zzqdJ^Yi63EDEsYx$j%p$AE)lpQhWTV?BB(k%dc4}Xsz2Or73j?!mN#vo`v?Do_h5E))rQaBcUAJ8AX;PR-mi?zyeH zmH0?qGK^_^4By>2r5%-9H=o!qICJT&>$d5vh1~kCzj*^Szc?+aH2!%=NIOz9KHmO! zU1`}vs~P>ijGzD94t{SnNAbWb9eIlPpL2-kYX4%@$+n+5dw{pPMEhvuXxi!R_ytG=YizQbg47h9#plR*Bj1!^uO zVO7ihW0{|2AAc@iEA>v>|GeIw?H~Rqan;<7p07|Xlk@Y)zuy}!E)94zjo<0;^^U1F zEWQ(YzAzj+nE&wPag7cG*(+}>3j@}y;$C$0$l|>h!`wnWrh9PyK4x#ned^dG=b+;) zw;UHTy*vKfvB@Oad=ZzVS-`AW9aDFF{kC3xUp7OzL2c?(HHpy5DIc9`mrQ)bIsKI3 zLbK%F&*hKaFP@tjvv1QQFV_dcmM6EiZ~paV-G-EwUnR#3{=M7BV|rv#$r7%-ImJgi zButwasz04hI-V9L{A$|LhQqI?YAP!+xTIeS@4MGz@q&GE@O+2=vlCmN>|S!AHTY@T zsb?pJavxi<%)T46GLqr;j3W1g7kVb^^i02aIl0n!9%HnbqObGT6V*nX4;j^0 z-*#WiR5HE<`WKx7oLd*VtUk5QoqdPEiuDJLwKwnDRIr{yI3?}ys(*8z{4o}8mu7>eiQ708!^B&bDE;|pRCqU2F>JEk3M2W%(5m0M zKb{-<&c3_PEA;rTn=j*?kFV5z|4#X8q^yJdq8A;mb?cLwxL0~(r_ey_8!pQKES}tL320xqwm>}0RwY76a#NXqkB{Af0$-cS z6)|bk4o|(3w)fH2NzF2CjqeUssO^?(eb@8g%3Zghv;D{CfAMQr)gaydze%REm+4Q?lnV(&JGS~izNk9bSvFdf+$epcD% zCF5|yGcT0GKr&(T(R&)|+db4Lyh?iJCUGEvOKj>tfx|MJUHHy;p5vap?8EV`#WzBq z?tRW)+s!oDs%c+!$<4d7XB`vkkomkrqP_a^3g*WxZ#Ql$mws`GW0%IQsU@jx?1rBk z!XDq*U!NZPVsgcXGe>2{%&HKN*M)SRI-n-zB z@BA{(e6P6ovyNrSzqij0R({T#qIs`|`_#GW&d{X3PjkMzW&}lF-q$wYzpax`(s|Xs zH*PDq-2^psY^9i2@u@$2H|=>^7vo{mlr1%d+1t2(zhU!yU;E^PSF9Y@J)Z5c0XIK& z9}YCvYv@fsdv&t)UVe@8;|vEaV(eG^p78csh|?PyIb^SA7nf2lik3Swe^J3?NgOG^=BUc zU7fh{>&gwa7kpKzzf;|^;I;Id?eXpG@i#QAg@4ztk}ZCG<)6vP ZCe9@Smn{nV{Hm#>&Rd#%jhcCeF>p#=*tS$|lOfCeF&m!OAAe z#-+-}!7jnY&c(sa&ds9AqRPU>#=+#}6Cai__ltdE5U5-<8X& z4{yIL|Gh1(UBu$;{g2mwxu4(m_PJ;2im)l))%kz@-F{tKMeYBTBmY)B{&4@{0@D}Y z>iptLrgwgF{Ab@~gKX*AN?zG9Nx_cX@f2=fpC;xxV{>t$6 zk9YLFivJeBce?z;dx!o$z4h+b{p|X^(^#M9w*8)3{rJc0qI}n*yWiZc>1+A(Una!= zX{W+hx!*w#_gwp)@&A4Mm;2lIADO&=^{w?bUl%>PR6D)nf2l?7sr3889sW7f-qyXE zV*lU#`O|prJEwoY{dYCu+1t3t`^V}!-hPc-_cy|F|K;=l9p|U?uD8jmzgqk3YQpt@ zhyOe|uBR2J^*d&*j=)++`+rQ8)Ag%2D9YVld-ooz_?)TF*N6YTwi zzx@38y|S+T{a3SZKi51L7ridCi|KCd&#&rt^zFY!zgv0tyXO5k{~303GSg$9T?*Ad zG5yZ=TlcSA`#t^K@5tV5&!g&ue%xpO9G|@-bMYsSHx+NLZ*BW=JLqHGwA}f#e}!(& zkLeD3CiF>pAFuqf@Q-`niT<(tXLbGc_rj<*@;i%ktK^OTZrqjk-{VKQ#j3jMzWWyC z59KuHx?jx;tXa9uG%R|zR!w#V;(C71eQ>i4bx(x!j9 zv!Z6k^w1o5i{_ZRPR~8<5Jb&%GOSAX%9^3P<<(0kkeWvdf%R=v|H$CXP&p+qR z%g;4u_bRz1+no5XbvwUr{f6tm^^E^cID6!;z1+V)mfTH&fri&tZ~D+@V^z3W{@wOn zcMbmkaF^Te7FcJ#@#}8Q`U_dje;jw4zUqsBz9iz#n`T{#6?ZD+avzPTg$&Z`$PKnzIA`N%X=J+C(Mst`~0@~t?z%# zKiBsZ?ObAhsQj(ewJZO6|3)rbx_9D(KkszDC&eQMZ!C-Ru3ouf2j8pj zj;FT2^0k;7CNlSbPW03-wbxf~ueic^ZF^tp%Xd$I8+>E0d8`_D*X-zupyS6p12-;d z5C2v%CBe?VYW8hm*=W;U{!?$pTL#Qbym5Y4&iSQ+r&H?V+Wvk%``z&kZ)9xlw)hq8 z)-FYVwK8w0ewWNMeOo>;_;J^xGYPj7#YL_CdEa*YtexfXde@t!lb$_#{^r4d!=Mdi zxyN58>baPv$Glpar&b0<%g_3SsX-;C3W)FJ|s<}1rAo}8c(cC9+JHtVwKnRRx2 z*a*qjr*Eu{eZKOOZ1c2P)*GWAC0w)r`Zv9$n%9jdF3-qc!27zo zT=DcUNAEK{;b~#oFDhTITc=Vhy`OuhaD}&%$LW3B-lop@p=;W>uWQPbd8K<_AM9AT zb;XaGS>{)SzU-b`sK097ryY}R-u1WV=d9n>yf#!P_tdYbz^nyTg`GQP-Wu*M-?^n_ za{znY)U~gU9%@lodH0*pk*3XWp5!Uoo%7p~k@ThdNk#Gcc4kgZ`{Th^b*FDR)sp4l zZZLbzzsy+@H_m>Y<;MGKqVK*vTbsMK%z5c^^wXY0vupPSuXygA85uk8!j>w74T=n} zWAd8VR%NaiQaA5E^y==Ngfomr8#9l5np@N|>G;&`SNdZL?=icab`cTeXRzrqe@%?v|_M8&pduPdk>^_wM78ms0s2hZ0}q zu7apvDn z&3UyWuO}_!@AURl6;`>s?z9K+bo>7HXqvv0Pp9nn-@hxeY*=1ueT^&o`b+%N%pcc_ z>b~?%iTcdhe;`Qr?aHa^+xTp4?!Q`g_PpLZ@zdvo`%Jxrx{agGrTffV`iyT$n16qa z&O;Z&BbUufd42_j-08aXQq8TmY3`Ma*QJ))#hIJUTQVnhx`4FqQ`z8z#Juv7;)yHH z&veM0{Vaa=>uXSGge{%l4d1u$?O!c+VSoeA9x%UOn zBTvYB@6mWu5#@EIy`s_6t}I!z_fCCi=ni}KsW($6u33G(%xcZXr#UjG^#l6<_15Q~ zZ}(jNn%R5h`-Zh9@0M1tp7C<4-$XIfy32nTCGvlJruY14+Qx$$iWa;tZZ+A%ELr&E z^*(uyKV8T5e(anl`cvcZ>k6%|?4OH-dUnLF5h&jD=5n)2cFwBCrPrc6g?F^OZ>;$m z`RT$g=BnS5)|}k)=H64au3*2ac{>ZMUp*}M+YnS_A3Q6f^KIqy!zVYalzZCccl5gc z+%=Dk<5o-%Z2Wfk{d8yPYS;2BJJW7&Jbk9=Z|n7odv@H?(>`vJP-(c}uSCI&+r3=} z&Gz@Ci5MlV+&m#-&-K8$`LF+)-oKw3zCAf$Vg9eMFJbz1U0o+@QkTyD>n6Tfh5xCZ zCf6>tNz#+;mTvNVpPjm%Q>*p2V||8!OULA66J#WB_of|_*|B?-E!WPAOA||~XaBvU zCLCC(wEq0YTi-wQiHWx7>YP)3B zvY;)xYq&NvUpvp9Y=v;`rWKd+IS$q{K1{f0y8;f*9l;)jNjgdR{@6o46|DtB_y1C>St{1SKk z4%OJ1De|tJZ~2zOj{S)f-bgL`rIfns$1DZ;bIXOD9amZHRJg~sXwvRn<&CT}UhID4 z`&QLCOvm`t_8oFBx+g8N>y_V~(mpZr-O<*dML~jY8|Fk9Iz2f0>TLLrkMmDu?JIh4 z_0XD{ttXq_Kl_|G_x$phsGVOHX*Pd9Fz3dro8P6_hy1U8r6)R_sdF*-_(fv`!Blj zb>-p9Azw7(zy7Lx{`%9=Hl+#kG=3T`Y{;G+*m|_x`c>BA+WVJ3ud~|pxFOf>$)d)- zS@ZuM`4AQscRe9R_s9&(SCYTqf9TzPM8bP&d$5@O+5i4KA~l(=ZakB%v*l8!Px=f- zd6D4KneTJfaYkObs}m$TN!ddBM7`RFtWD0E9utmU?_VFjp-Wpv>}nQ+i&U$OK1j}nQ- z<$Niz1#f<9u4sBIDYAZ905qj-Rpld)dcRFUiy_*Eb=1n$0o6t3gO||u8O8= zF9a?sZCF%3b*+)X%B<+K))JA9(UX@|JbSn#Z}N*(zqvF$>_l6q*{t#s4XWOE!q@8l zv`tYd7Z!4J>(28sJ*ts+rDA(io^(u6;KzpCU14c)>RA#yVUVg>XmmtM&*PvW`rJ$5&!r`=6KWVP`!x?`{vv=)!~uzv-SBsqqy|vf?qL@9z2rb zn7-D!YmGzIY=7m4r(HrzLd@1De&F`tTqL`vXMMcCug9HBAF2uuYqxrr99TQ=Zknc- z?237|C$6Qe^40vlOf&MT=0R0$!>)qcckW2eo6B~l)$#tNj{e_oAGb03?M(``GFf+w zO=+fO>;t*OsTUsSsYE!j@Lk!%KkxOWV58uyw%mQ@&AAy;U)tw7>Ytha=UCgrFEgjF zHv1gnz2g}-Q;A98x36++<5tt6e4pec)t|VOnB1xAboy3gy9B4QxXh;M zmhY|>M#^zcnqvA^YT@RlpIo{1*OwnT@O07C;0US2mtnm6Irn(#BEQV@u>EcXR=&&@{Uy#_BvcQ*RZ(ym92NLfsAd!8j)3nzpuE? zT)FY_wqFa*Z1|}e|8VAhwHJal7g&F;x%KPgO52RjcdJ<@T&i{ZUY(Vs|GMn<#r^;0 zw{Gi+b-ZT2V3p_$37!43{N~urFs~J7zQnipV%5pU5YZE+qKzDXBDnewGb)#dSvyq8 zD>T)m+{`GLa7NpF_i};jK732Bq`L%|&wf6MXVomWNz+x5b)sIRPBiXatTF9%MZ9wT z#_1E+O*nP#6tmyjiBguza`%ofG_|JHSqau&E%Z5><-y3QRv6;G^p)Df@RpCiwK?0@ zuSoReTPJ#11B^{T(NJ-V@a%fUPIeOi)~ zCop@u&dk0QB>2Ng;HbRF?L~dxb(j1%i@jwL$ue(X#T#Oq@bO)v(8*6^7DZ&23}hQ zHZ(Z={a1J6_@{y!S%yNH8h2+b6r9C$?T^y)W`=}yVI4VlrnCo4+sF8^$zPmZ@{hRZ zXSw*wjAPj!CtQjBS-0kwcef6QQ>&w8%4Wl*ONBi2+jjLFiTmq%DPWPL*{jVGyCn=tM3`?UE%}tA_Um8XyegK;rI$Y3 z+{?L*G4O&#>+^tDyEjjYEz7JOg!}3qJoA3D-qQHn{^EYJ zTg9GSPM#3ANxCR<%d*vcDLb7+?@lUzGsmiBmO!#J^R9bR4}_=WCTq&SQ(&nw%Uomf z{X;^3!SoLk&jHwEhAKYLZq5NMd?(RljC z^}Hqb*aKO^wdXD{ZJqEbc}98l`ndhm-nCWsa95beehrY4xb^K~*F7)J1tBU-vWM;@ zvV=4I@tb?|u%6M;HT(JM_4N723s;G*J#miH=gFd~(~s63QC`ncuY9j5 z$NFP|Y-Fd?+8>*_wm&p|;jVT&p=NL5iiMArw*GK47dPtuw{v0e-8~yOE;PG8Pg3rc z(7KBrUGu}NRqiLHguTsM;LV(!T@oY9=e8oMwi z^y50Vcs;F!UEbQRZL0Gak}huEt+;{pDeLCF=VojC{`zcp+^U+uFH_CmcwG{8)^7}Q zNDB;@w>{czfrZ}V;_izNH@waK{;g$Q!maaq0=>Uxl`gw_e?=*`gL~=T!@@0_)<0-_ zuvaF8<9Oe%$?w_MzM0*<>_l;_N}j!b@ykbHH9-aD>Z!|9o*6iL%$&LmSC+BB{~0+*$u#PO}hN*)ZYpTc>`}xMZOo zuB^BM1>UdE9$(b;I1(~rRc|P($J3Uj%PwRsSpIFr=4kB#L+wUwzC0^Y@qHItQ~tMn zdC)ZNj2_oswx)a9jsXc*aumx}?+J_AyrjXANzY&W+WUTs4v(eNZrz=KXMNgKQSr&^ zop}4>B$wE!x63ToTqjaf8MX3h!LGcu7qkPL{(juk%J}%4ra_*5NN7jn`>tP8mprv> zx;Wo&?_aKsGlTzpGVSqL+v0aeCs0I;{mt?%THBOv9&FNE?cV##UEt2uyG7r9|DN8k zV?|Es?QoWU*QwW-n~PuWG*d5G@=;%#bxPlto$I}SM#e@+tPgP5Q+TDnnTw&5(e-4@ z#Q8?ezDai;Ma>lFDtBA+C~9+yxyPa?|Br!QKd(J|d|_+%w%#XQUwWd0OkX=sx7Vi&n0mclUb1`bpa0vO zP52Kr_kHTKUaK+p$cmpHazEys4?pt%-<0jL0}1KY2c zN1qvQtUZvM^Ln;!S|PhZv!0@vx6?Db7M@+N7}uPS_7y5MnAm(ovq)@#(uCIz$9Gsx zY~;-Cm0RPzJg8sSUDHW%LvqtiNvT^6Zz_Y!7mKoV_&Q#>6*;TrklF%?xSc%q+6?Vi zI1T1kG?b*9A9Br}>;673sO6Hp-h&*+4}9zu?3*H&R5S7Hl`V@pLoXiR=McRAsnS}72m4GIxeq4<`Ehy( z`JWV*PE}gPZsbtp+8*kMan{Z0Skw5aV%Mx2aur zpKit1Q@f`IBxXx_`K+&S zJ97Bvm(Q6iudBGfResZQklDPY^kLES-4%D2UA_^q>7f^E;L&umLr;1CY)oj{zVGd# zLoZp|w9-CI*RtOdpcQPqOl`uhUmHF*zu{CU;5S;&Z+n^}@y9mtjwH8(i&jnEJ$vz! z(CgQKo7H{VU#Zm~70@=}+HP4-SuN9Q=ZCx-t2XW^@qNmTP8^W3XXInAc^Q`)!4LRvqqH z^PKroZpY*mI&4BuEIwIXcplVq=0NZ4$O~H;9_+5@m=hE1`JnM;r=*&Os)NRa<9u;} z56qftPAZr4ZkckB^I+LZ`5A{l3wiC+*UU40b>i5i5@DftQ@#fOV{W$nX5hx8KXczk zQ4ag1mP-_mP8K^Zuu)>yHpj2;j~w!v8sW4dv(-^g?d%G_g(X*C2>d8sCS){~r&Gl* zT<%j_u(GpE)aU3^7RwHF_gpYqXFnmqLpO*=Vkgf7?q^l0kB;8s-W1L@VQFDh+NG%Q zUqx}N<6;*WD+ipL+?)39{F&3bVSR5IXQv1ybnj=Db}6w^3Q#?`ihoAcw!pvTD&_0H z$?c!CsKuM7{HE@_s_u-?-)}XRc-@U_I4Lhs*KI%d-JUKD&(_rmj;sDxOBQge_Gl)q z?>Vc{C)>S%Ste&A%frggOIkS!jbBw<-_yF4SM^GNf%F->P1m!{kz=hBReTjy)ud->&t zD0A@cn`J?Z{X$&=S|7CZ$lpJ4VF#1@tLX=|V&=aou;*KU``5l5jt?(q&bjetd-REI z#V@a_eg1XyW0jq1#CD~CLl4VC4@kIbwQPDQ;viK0ev+YM>{5w+YH2c$nOeL)tXfu+ ztn8~iBmeC3tvAH;opO2Xzuhw5>p#Kr)SRIEi+z`8*;HIuE;r@ftsAeW?~l&6D+wm^2g7Y3Vjr4%JXJ0Y^f4h)B9k@@~Rc@8jpoB>|57+{7`(Bz^z-~-P*UB zTb4&U_=%pK-O2sAig(MxFAJv%Zr!BY`?)iIFI%hhv)2_DUAwPbJ>U0g;>6k~VSm3K zNZkMAZ;QZ7_NQDEogE`~hpO(IraHUjKw79%ql981qd?o#pvh_c;mrpmnCyK1toYP& z=|YV{f{e*A^KkLp4!yb3+3})IS6I_3{loOzx4yqLui-(*)UZu=7#+^WMHv^|7k-w? z&wg)VtNz(TC5Nr1eo=DFlsvbpEW|+KuEC#g-)ds+K3!gS`-SSFm~V@&Mm*^5+qf<< zH9zvULEVk1hhK8(99wUszA<;@x7M(m^KR`*VO}A{pZvX%^Xs#&#`T2_3%IyMPOV$E zs_=kD2%CXAueXKGv@6-m#aWMNT;jVkEk9>b6Jz9&@T8R;(F`uLw{vK4AG}#|D`AG@ z%(X95Rko=j{4Z|Q-sa$Y{I=!W)&ssjBhxpmQ{Qjyb91KB>b7Gw zIkSZithQXn9%|koq+FOO`J#|lX?R8ZfksXV6|yam!ZsT!E;w8#pZ7Q(a3!4%z~~{@kg|`2h4hCvQY3x z{=UgFCUJ@f&njPj_2w+EiKsfaTFV;4sZ2hq4^MH;kad66rmoqz&v>_(iK1i6zrq@= zXH_YJwJy%((E(qRJB|1M5>JmPO$=gQx1%`w$d#Ie#=^Z#pS+K+Tv<6cnWbU&^O&-# z+^O@zy9^WCWGnBK9i2YS;Io?B|0#L82|WM2n<7MJORW@^@385%kG;EiVd-M2V_R!p z9e(oqpbLLXr}LDz_Z}XVy#J_&@#CsHr7m4eH+gQ?EL!khCgg1XOEH$r&yLD^@m(1! zg;ze>J8k~+BaULvIow*bjqi#TiiU{`DQyv!nA#fTZlJuJ)p_>z>(>~?Q#^kvZq9#w z)^U+e#v|?nv;HtVt7_s2QBiFR|FA;aQ|hPF7mMPME5FlB@BJwfv_G@LOrFOgQLyf% z#G1#7yawE>v>t8U7VP|WkI80%+}zFWs=6(6v?k=Z#kN#v*eXdEYw=|86m9%jc=oC( z_b>OIjX9TdHpG}{t#MD88@14x;nv|6$Lqff*`_v2_(`8Xbv~RqWXI|W|D^X@rO7mU zy9BZ%hzB`s2>5E(5!A5XWWQ8Ie8r?J=XZG?vB6>Nb1pT`mfPh1c+U0iORFr7|M1(n zs%FcsoPdxsazPTMf3*JD@Yk*>`PUygThwzmzwF^uucQqY&*X2HEWEj;E_AhDh1Xs= zt5wH}e{4RfYg=?DW6|dt`~8Rh-+7(GziOIjuNu)bos6;Y{jzjTqT_$!q3`JZomjHo82myYT*BirBudOLX_n zy7cIDp@o9L*XHp4H6|BRyKeeEGX1AD{qH29a~r}HSBLd|nh_$u@6Xb@7^XP~v#Z&s zYW1p3Shh-N*0QOueFQITo3H6>XK~f*Pp86{r3Z>TZd|(aW39&5TIuhP-S5sTikx?8 zfxX)Pz?Ij3=wH@6`YOU|?y5Pv1vfy32#h0@kdGha6_Rj@kAJxmMH5@lvO zA$s6J#p6{RBA@?f2MPpPm6fI&hR93o(pD;P>+<}>9yss3n_zmCdESm|XLTNS6$q!w zm8)Ob&c6Lb)x7zOl5}t1*83T?tg~QYq|$D!c!r&;rd&L?>pd@TQqw+z)caE^79Co3 zWBZw?&FixCUVPm&`+(><1((8;K8Gj8Uc5RlCU*alA2y5Dbg&*>&-8v$XNc$OzKc)7 z4z3IhJ0xlRKU&-%An5JGW#VhnZpNf=Uw?LM`-D8Hwu{U6==dLP^g9u(yNB!4`(%Z# zYkD_XzOqhX71dhP+IfAG#K&wd?OSKFwmb1h?u!k1!yjn8!u;9}EA~!i)h+wC-PAG* z>oh#Ghw13F&oa|HjT6`QUJ5;Ka`@Zhb;8e{gx*-MRXKOf7U{f0lNe;aRPR2;d8EQI z!?7(i$B{EbSF^MD>N+i!`hLjGSYq*|yV4BjLQkKyC=QYk)@c$7O`Lu4 z!Dr5uj1hl%XG~q;$KQTk$fV@tm#P4}n*BHP+lqUYLT3MGVaQ-z^*EfpEhIp13xn`3 z*W>O5HxiqRc?(`S^>GDklxr|GyBf*A_}vNq8$4m*JV#QGoNru}bA?~padUz1V&8+D z+`Fb5`Wp{#;ln`yH3xfg)vp)lS@3^yzW=(eN?tqZqq)%g-q( zzK6qh+aHPZE2~I8o!S;+p24k@-IY8e?15pcz;%|k%`GpCo8L)hcRdK{-|n~9h`VRc ztZxhU#_|^*$;*6|MiZcD%Fohjz~YeJlURM8Wq zqv=Ig*>8M^l!_A(6)Rk;9DXF2^Gfn|@5x8ch1|TcWS^nqstuMRfu>VUr~Nr-czK4c z+5WF>YV52+tsyPpsMb0&V>ib^qlSKsVXQ1Z{X@Vuvs^Toq;%V$i9<1iBAf8^8{z3{HZ!vFgu z?mT0ju_&)iZta%Pw!D-pYo10N(mbGaF{=ldtM&0bzXkNsP< z{-kg8y7@6<`htXit*;Ev?Amlv>*ZB{uSOMDLzk_entm(FBrR%P@|=COUOKmd@a3&Z zsvNpO3lv1;Bj1X)?3{YUWqr@x;M=o;RX$EF_?NQSXp=*>*5^;9FLNYk6#F>pOZ5F+ z{f9+y-HxPU>BG|XVNEu2w&7Fv|4`Uk89C|IgX0k=y^b&2m+~jdq`Ee5L#Xia_RS^y z-!q<+{(kfRy;8y{wSRUh4Xu+~F5G0ZNLRVXfrptLMG;_g>~)#P`-{vGZ$bv*h{aar?OWgoON`H=mYQUw&Nee6i25 z@MC+KDnFVzFTSET`}|SvoNMNB)7N@^5Mf+aaN=6wUYXVU54JR@xn;1j1%xPulpo*h zSQeL8qTg2|E;`#qp6w?4DNQFW3$AkvtJ7xbXa6`*ve)@TxRrufmEd`)w3t?ylM_w(Qy28HQY4wy!TUhgbUVEqAi8viK-0wE4yYe~*5KP15qG z4*e6$b+KgHV&eIyR)SsmV6;b9$Fdpx)20dE(LTy`E413Spl^9CqlcEk^Vy86Y7JNu zIob|MUKe3(z0cixd3lzHXyhg-&n*9t-11phHvVXFc4FF~si$f8piDV^+O6A5NxxSihkh(8%tS)Zsb)cZSSdl%hmtl z_JI}af)yJZ7^OM|wsDErf7|alJJ%pIWD%oEAkQMF{&%OmCmZh0OsEKa)NN+_ZhuVD zXKms33FeQd#RmPmdVZ&z-jS_KrJhZ9zIEVt+|o*;4W~8UZ`1yv_GOiAksD))UZ-)} zvX~H=j8>OMWXK#t2$y#q-U* z{`dOPyAKX>o7VXLw7hwC8f#1Df*bnQRj+vXqB8x@)*a4>Y@AX1`s0TU3*S%vzILIn zmXLnLy){9T-mfjR&GPq;(Np_#`>W6L!Yw~0MO<~-wAY_QtHac6?IVv>=Np%4FA3ID z*Qsk0a+1mXDo|t1ZL{aa*NO*g-O`n#*twdG16%)x%v8B&vUDl;f62~phll&OyzPzd zoNHmjJK?9>gr>fNr&{jH#cNmA|M^xF`ggBH%Dkf?^*g2d&h>gt-gVD=!~2e1yDFL1 z+`QfB!aa4l+`}lvGfX0$52pxAHq~}C_eo?=ozHQ%SQ8l3U!}r-O=&kL7wz zuKF;O|DnZop?2{V?R+zhSU=0G%UB~C_I-~2+0PEgz9>7Ut`vA+sxo>3X%Y{8sfmb-c zSX|bWU%<*$K3PZh@3oaPWu@N)cdkm!d$Fm)D?aVA z@9LXJy(OiK99&k`Y1zz?d~<#75mTkuho6nA{3m@r{KR^9REUlSS6Ke3&V>amaWji5 zk*)4i@@08zsk2}q7vF_WHh0E;r+=(dK7@%uCfqhTwnH@D1ey

`XIp(?AZGq=10gHs#Ddk?L#H?jU#X8zOnEuZsY6Sti=6{g(!uu=KI z6T9ouA=6$@cq)E#gIG@LgO*L2(T7bAyuPI5y!y`m!^zEhZJ%A8IP=7%=N~ehvV!eQ zhl5*>%4xMFH@C_9o#9ig@?cm!Q~$_?wU@cN`&X$7uNQDNeR-ksao6o#XSE$>2mjdn zBJp<)C&!|@42wE$9^GW=bl~jW%AICfGd|ZZe4QoA|KG%AmH$h(_Gg={axU(+%2lF|p0fPYQsB5-y4zD{HpdM9vc?&zb?hB?^Za%(P00SQ z8FbjN`DgxT#wY6cdRBEUSoitEmp$1#vU{H!a3qQQC}asdR(Knscz)Il*99-0xr?fX ze9mF$c)4<(j#RSNdZyLF>!kPHEPbxn{kiRV)ReP7-S`|Ey7q3Cx!- z#`gKLK*U1lJ10^uZc08Krr`DEphW0?FOKl?TP=0ZFV(Hkh&_7m_B~(4)fcABStNLd zC-F-J)5A4U8xn4mhcR%CBBgYk<*fwsd6 zb9aSPdp6p?*q^v(?gfp#leR`&Ua7AA?|`sl$!@2rY^{);tOtv~b}gvazWeQ+o`qVn zy|eDTBO8i$-#T*9_Rsdkdwyz}bUkpIp7K__RCe(~>0OxyMJmn}8>i|C8tgc#d$MBei2JT6BMY=yg&H-@tDF7ZFEAyqW0<{d zVHp4F)n1wNBw{#XmX<%}i9P&p=CpOUdwX9`Q3w|eZ%g5?(v4>=Fl$@xWHaO8*4I%k zc99_pK4(JAZ~Cv&&*YdT`DM$yY@6hQkadS=ho>Cq=wz90FK^PcAX-`Ql!D^*<$WtI zYAo8P#q7GmTj1d|m#T&x39+khNXj0Sv-?^ddv%wG*vF#RuNe)mq%mIf-jU=OGViS3 znS&YjHBP^)xANUr@3_kPPRLr~BI5u>J3AV8F@=jU2aK?2@i_==yovdg5H*`Nv#n}>ugrqe@U6o|MvQ~DRc@HE>i6ka zaw78LyY?MiFX4H1WBhC0T**_$%<5V!Qx#afLJspUIkNwAz*aAQ@eTWT8peGMEYU3% zES_+y+0uK}*F{{+2bUh7CFQbOq3F*;w@Zcbv44|f3bU&Dp1Z3EREge^&`g$8Y`&ny zWzg4|?yUbyJ8bux27!$Q8Oxp+Io3XW{PELUv0nb;W^#8}kGwg=nK1EOQN-QlmjzE4 zeW~i5vG#htq{LGm?qf^0?_73l!nV+v?h5;urp0YgJ1MpGb9aMSwA(dyehqUS)>D@k zA6O8~bX#0+$)gx2BL#7deR(nmR5xTZ{U{OHbZ@E}ms9JxEt?8uSM${OC#Dghp^2Z!|QY%i6jyl!STFIeboC{TAUW72_(mUDdme7RI~(?sa}eUmoU6$ZzB z@627TqCPF_)1w!%+1#vG?G~MRx-rU;v0?uI$6Goh1!dkC&XJY2u~|B8h51vqUdLCD zsyr_JY`@UcsCjnbR+jb)G3$ScE?KB<)Lz^ozQE1NZgb7ySy6w&9!NQgCQq7ZyXC;~ z85fOaG%J)YkNF(PpBwBkuQ}hMFsFP&Rl(HH(!4JoJlep+iT-awe!gk9-tDq!TD|^K zG#gWUz-IX)9F2jiy|yQXDY93$F5fnzXjlKX<(Y?tD2YnWPNj&$~_hC zce4{@sUB~a;$^2Vds~c0Pb&BQQ!=i~SNjUTv+u- zRIjB_-9A+0)4z<{?dFRkuC0I1pSxToEmE?gaQD4{jS(CMauywrG}_{HyLCmoWP2ve zKdfq9suU)t`I@J5McJEe_TBbwzKPW~bBc0fS(QI1{hR%6&xuFMyqZ~GJ0-6t%6wVL zBI-KpEAwIgH|GOn6`Zyn`hV77+3K!^ht8Zmxb^j{y4}b7Kjajpoz;lF@M67^--FDs z7LEA!dt15uIK5p21RM1wCRf(Xx%{ay)bx*q;X;x0n-jF>wOOoBdXU|yX!z&bgqqWb zLMDi*`6U#ZrbXX1KV#LA%k@c1{*>%l&cnUOXKNo3uzjspd;Q=-t>D%3x{l7MGbmBE zDC}GMx+`$mLU$RbmX8|^GXf12=S(a+%rsBwSJ3YBu139Aw+4K@^LTmo%Uc|QUthDc zhb-8z=E-UY@p)-#RUYv|n`iHJJ-Fgtz`-Tk-gi$c&QzU$r8DEOkoM^SXD?OH#@?yl zW-ysJF-UVYvz$|Cn>yEQ&BoJ#+K;!_$dYOW8*7X8xR}_D4DVvuTtaC@~TAXLo?;pjh4778;H?-yKG%aF1zv9+fpX%G5HS6!~ z)!Vo$s^V0gQHM;`myIWvGcJ~k6@J{>=hr-Of70!BzLMA9uL){;dfP%wPR*IMy8d;* zp>tBx7d^^zRVjMe89y<}<(?G(wtHcEzCS|$Zi$r*w_SR{>B=O*+&xS!R)22o4g9rU zp(!FJ^38;!eaCF&?Cs86Gq=ulT_SW_k>lWUwp;yc?X-O^Sf_rh)wwWXM{KY!!%Exs zmNRA_Zf;dL73eGQef_*y_g1?;S(GHU(LA;1`X;V6k6oof z_a6s}+wNKUr~hS$j)?ebr2|@Tug-`&q4Dn*uTAjvAFF#rSuX!yawt&L!@A<^^ERVX z&VBRyLpV2ANQ&i{EP5?=@2A5|j(#4mi5m<{%fu{sK1@A#l1W-~$AfOMEuU&09+@F4 z*mjKJz}4eQM>Z{Dj+h#5dHm%Koyw&TWAD2zU|lc$=zsY$=QBJV zDK9p;%yuZ0WHFnUnp}LWYu3s~rnz#9m<~OwFL$hv@s8Z#F7ffY8Rz2fh1aLr#;Q6L zKX|cYw)ghkjBM{(SugcWtuZwa{3E?dBZ%SAw`qIt3PpPVjADDz%YS_Pj0>q1rfzu$ zIyE0ROh3w^ADFeJGcweHSLPLyt>=oS{4aA@^la9A*l~B~fzktu-(AWswC>>DH~agd zB`yq4q2)goWNEohGMYK#S(;S#4Sn;HCG4-IzbHTdd(OhN^unY6-xc}AlLeT+SBm7# zShT)bcm^YjxzL%3Z`Nl7evD~asrexJYi4uytvr9_y=%N(L%n|P6OR)J6=Ltvkzadv z_QEcC(WtN&t57~_5S6@Yj?07V{Fu_YSW$h2|uB~PfiyumM*>vCO z=iTiwmuu3G+750h^`Dzo-xX_mt8nyiXS{)g_nI}gJ}NIMo%~p0;?;m-KMUPjcD3fl zvoAGWY_A;gRq3#$alo4^td}~tuB})kv0tk+P9|_=&o{O?R-bNei`0J28FFQv)Rv6m z;|s4%Og4Kf)TF*CwfwN%+uGHer5uy$->Wk3wh8+n@Jr#7Whh&2`9-zPFGX6aF~T=? z9MX@=+_d2KzUem~d@>LX%2C9Dgo-xqI&!_vHH`N1VRR%JvjKHf!0u@XkNHzh>~PE_$c7f2~GyT*zjv z!(~pc^KXjIS}Aj|&8&j8BdA1^RWf4(Q}A_jwh2-V9tKKA-R)eN>@gQA=48bDu-a;M zd{xf1h!0#h#Hu4d7`}-0{K^?3%>*x_0g=v#(4WqqebdJmGj3w^cd}% zuvL^h^UAe-Zx5Wl)2boq+~WL_U+3NLV#!F3_21%bcs9xFa7T0}M?5~#n9HI$VJ+wJ z+W9KE`mxz8|D=1(l>_D<77>??u8iZFFRm2EZ*kqtDSnTW7ysSa9#?L&7du}*CF#e( zvU$NXyV-5A@06x3(rn3k$+y8KI#PPok0A5SM`~7Y`ub8WsKM>W%TmTJ2ff67Of$^C z1RHK*(|)&lm2e?@<>gI0PdIGfeky!Ct4r?DR#U^|T-YeD7XA$=BV`)XM#!{tlAIZIs1E<8= z-aBtNd3#=|>+1!}R;8rOnRamhKGD*v3V#+%x2)WGm)w7S@z{zv&luM{-g$&AY2Wbx zowWNY6NA{Q_=NW-#nv9)cVg!YuhTX68|++fv`kI-(rm}K{`=-x@yXFyhMk4CZiIWx z?(SN=XRi5q8O@b%c;w%B9tmE-A@}E=bemFT_U<-)-830znKgT>7W1Bsy3*Bi;QVsF zS?}MwYTC`+ba>-=Z{F8G_WqF(Iu%^Gi|Mj@?T|nygDWS zJx#TLlE$%LAM2!U2d_{FTdLUnMBll)b8(A&V87j>4cZ$NZ4%fX746H>zI(h~Ja$z? z)9tgXHT*t0g{|b8^I$52TH2z(Hs#YHTF%e(ne(PxxW#robv46wwaQnI8`?@|aGebc zcz9)k1;f3I2lgrMjj&xxJO%Gi^|PgaBOAB=5>BaHCMxYj4D=jB|qYy z#mckB>e+0Cm;#Hveg^*(jz&m)Wa<@WE1a==-Mb5`l#E!fy?wM;NPc?eU#7!Sb3;|c zkFAI{Rr|NI=hU2~n_lrvV{+%Z>C!U&=L&9Tt!^d0nUc4EUSNL{^=T`USHR9&9E$&z zT*|M!pm;Fp@oZH-rGTh9!KGIhG|ik}S`@IlAh&mG)d5+@B}ozLAB1nP@7|mrRqy#p zG$!r_>%~J>@-6uln~r)ks@0q;tqj(P-V7z`s8UyFE+PRw|c+8(2i^;opJ4BH$ zLri=d564B-)?O`>!0HoM_SQT-$hBQQE2%X)>98>WFVC7LB^wWqS~FLLb_+q*#JfAH zlTL}M#Fkp!i7B43JupJiBqwFf&sF~(hOV_zWDSjX@V3}f9{#kK+hryn=Xt}K-Itd% ztrJ&UuvlZi>ruW3ZGum9CQfeFkM6I$eLgoa+W1W{-V1BpW656n1F(^WT=(m%D#% ze9V01`)nUls}wpygY(ZhoiWXfWjP=cYrNb66ZrU9WVf`ywuCI$4 zxz`@xDT?@X`$%$dm7LOpB&KLiwO1|=^X;YOXIxw;vU;_^b1k;uQ0qH33qwrva&2xe z(*0ogU178`gy#U3${dyxUCA<7o4zxyys6norRRsQT$>DgTxCX^GU)M<17%e~UeK zy^UX5=!}GWv;6z-3=8Ae-G2UYwn3NJMj<}k6*)2AqXoUi)SBkbTa%YluD$Szr_DX> zE6@4dYOXKqy3#B1=w?`CU;K>7yu5&x2+o?yczla#j{@#&Px*%%*2VUX&w6<4k zG;}zZ2j9K9N^eg2`Uvf&UBRJmzsv3AVm!ET$y4#guNi)qc%9Kz-H@!`cjk_}#@$nk z#Md2N-Llm8Bv;s+TYb9aHScfin4xea>7nKUk%`_;KX3nD?p5=D(v%6Ze9S>Y?{-X) z?agp{_xeb$5AU9BKX*v^&61iEZv2|lhh?H*bi<6^=TAL^x$L-tJx)lQ^KD^jTftPL z)w)*u#!udo%ZsXQoX$LZ&B)om-TpkY;og=LqMm*WSYzW8#rW4(Px6*XcGj_W_$zRj zzmG$0QdrE-S1(s;%x_F>k-K0rrC#y&%;~dk-d>|nk+SzxfV2AU2kQ@6X2%>Xo%?v> zw<~ksd#9W@<|%poWB$9GjK?K8lF|zn8nT4G)}Oz^$|wBdOjWgZ`}ARs!_oSWfuXuC&%|4b|tGica+hUu2a`MHtPGLEf=v@8b zgU9XfPgi9b%kbWwx;t*~Pd$cNTsLZVNb;!eVOx9lh{>B|RkNGwRb zgI=cTcfUBh2$y8EEuHo!{zSXLu1ziB!g_HJR-ItzQsb=i=I2>lHbrHIHM_F);)aHt zFANi&Y~+3Du%zrHI(Jm zL)TLm{xa;H@^Y72%j~=p4)=A=I_~eio>-c7XU9wi@wG3Qi&JvTxlCs{1UC!1Szln9 zk>zD@=rgafCeQYNU4oLO+uFYgI@Sjm&n!2}*?MN(_SyT5R@~fv)?850@%OII6N}E+ zu`3%)wv0M>lv}@|gJG(FW1Fi~z`|y!)4IoXS8khU&y*CrgtK9_YR~?Y)oZ7@pVwuH zeEBd$V%#|j);o5ws4fLM>@;R+JPkTb-<0zxK?HrnFp$pD`c>U*sTg;QQ z-nX~jQ_^V52z&VaV9Q%4(QO&r!jIImn3PU#n0YzSLSE&ZrTvDWrCIg$lX{=u`f#vW zDQuzHfvO4H*6ayiwQb(%ik%(u)d?TjI|Tv+6M|YVFm=rD2-UZbzt_n!{*Y|x^*^>G`F zwztokyQ`-v`QeiEJ+B%huLPgGo-EiZ<&@#N(>$c~@s3u`Ghg!xPMj)ODd_j|jt8?e z)7cddt?E?X%nIkrh;RC{wu7fP@aP=h_2wmK*8SC9a^T<3nngyl3mcDqj$L@QdDoJ; zYO}X5FkfaR|2REr*DCFf3+HCOa((G~QhV~zNtfNFoH(BqShy8*o?6WN%H{aP+tt%O;I$%()W0ZSIr+}yNdBPI^4YJl<3xaKW=7!q3 zzU_5SOWpW+ig;ve!PRG-5VXR<%-E2jbQtec4vG>~of4Z(r{nB6n}oqO&pzN6&#f#cCUEq9gom{v$$Gnyot+;a2Jjms-mN0hy0sqfkP zi}yCi4Cx;xYQ_Q|QhN`lls3NBEWc;wB%r9u_iX>vo%@I<}|0tiQbDpnh1;?(2URygrp|XRh;pPsgFIbXAGS#**Ek5ssFVpFR~zm~O3M zVfbiXubI&upMYa#%7I(g+gvzw@`7`ovA3^a*K4t@r*Ei98g@)vR{zoq<3W$MBWw7kY}Y$iag>4I z#n?QNRpwm^_Y;BBXSg$OPLLHz6nGT1BcasM`G{}f;*QS~epan}tJB(Pdb;Z4RE`RH z!yE5nHhKGHU5sYrn9H?U+~~^Xy)6sgf4$eT{ijIByVj|dC$qjbn5BB!?G)6|NOu2R z#*i&%?V)pOa@+N{if13yo!+FRw}0Zs*YC?!v%dPbt>8Z(siYCavphDqP@$tnFKI{f zRP7&0p=+c5x9?=VytZZD-;L|qia)WQF5Jg@)+kKgr6ZzJ|3gaX60PUm66|U^hi0>D zwaK>V&EL+K%N(*g#cJZuy1SJtInD&FufKIoS6}f{HQT~qgW@u;ts>cy+xDMg(!X;4 z_|_HriyRis+I#(2@S{VboB^h5qS`;O1@bVy-?;M4WYvH@F)!TsvJ?L-xb!7*?=o(V zxEoqeF3E*`@HJ!)d?4i`zV6?nM4Pfx~vwO$^>{aRC8Hl#~cOQ$h5|GI51 z^d{@Q&&?~dj_)&@8|T)P9DN|`vs!Q0LZKYfur2@YuTA)J>$a?Q- zF;hhjt!6v8PE-HfmX$M71-=|R_HIr2g=vuze;O3(L+-9z;hiVlcvsLhQA_!L%&*eO zL{-(A-!iFJuY|eZ zW%YL5&)r(G!?R81etD@e6X$A0l>=J)xgtuBuVnCQhz~XY=G(aL-Q@Fj+&dq(6+U)4 zecI?^_B;larkHMf6S1_DqQiNXQ++{46 zxt)Xd&I>4DJ$dTYt(XUz%PbE6T6E-s^Sx-4o5JB`?Dau6|5Cblr)2zvR*|Azw51>*0}S{suM1PmLAv zvq?Hxaq;bi&99%uU;0vK77^%b+4v!?Q&F0=G=ixlcg4I5_gGA~e)rXQ6V*1Ehkco7 zLdc#t=G9LaD2V| z>5t%#6>pCkO?m3l;d7%&r)Sw(6K`iF%`Hxsu810WEEB(^EH-hC#c6|BHsLk)S1Qh} zcAn*!VRcVojq8$m43=g~f*wY;n{Kr-%yFK>=KsW>!8c~xs(`HzzbT~dw^eEDHMHKN zRp9nWXAZaYF;;jSGMG^8 z>~?eB)9z&6(`O#I%Wbc%;oma%N95F~iH7n$RRz`X`Y>A6RpVJ8heB1k$LYjom7FXTNb~*KTY(%)`O$RIlE({>Uz?cbcAHqDlo0z z&9Y(X4Aw37S9sSqT`Y}tyXm_6U?*ew^Nm;HOb>M12v|Ib-DNJkgZ04S2@9iiSClPe z30bSb%kB1H@4TQ#JhxmsrK3}LcL?@}Z!36R99OqNc<-SvKYlgd)|NWWJ-Ow}Nw5 zLt=ble*Yq^-jv?khXbQ@#o3k^=!S^SKe}r6pCr-hWVMFFCz})6MFaZ|h@N@0YEFK_%+iI)(?9QOWq-Ap zHMBm-AV!GqG@D{q;~^^@UC?3xQ>AO0UM##R?e+MbtM{O z%*;jpF*$9Upl@CDyJ3?KqcMZX*5_fbO*{T=3y{!Glx5%N15H;?7jx zVRXcF@5R$BVFFjzsYVC{zYM(Ad*s4The%vjAjP9Sl^vdqY` zx>NIPdygqUR1Q6Gx~BEJqXWCp%z%s-ji!GVfx9OD-*CI@Q*_|H`|?|&{(fJsw$1tX z%J;M7+dVtE*PRLdB~dZk`Q*C8o?l)V*539qzv8jjtE12Ozq@7sh11|Ap3Q+Dcz{ z`)I@&qM37;E62ll+5Il1b)wS@)*D5wdEew-x6v&7K);!<=4P9q>r?qO=0|+3Gl-kX zd3ep9?*Vq6`?wZ(wXHUvE+|r-9QGmj$L3uEoX0gia!xA#nf>wxYiY4XAD_Y5qXmke zep)%55U`PPGk1^h-n;V6f_me<9wxfRzt>)LV3EDUoOrcea?>91{MR+-BJ%T^ za`Bnmy`mDI^ia>$0wq)SdrY5qJMz18-vp6@KY>m!I^!40C8VprR%iURDC2skQ{dWN zoozhl%{>pAD(rI*e5js&KfJ!>$Jyh$E^5c-FPrcn;G^R78PY6C7dm}{6%LeUhbgY; z>RsdEVaK~r+;2|Lw>O)0B{nv2o@e5?w_5G(W2F$)<{OiQ*`I4ghzlO$c({rw{%IYD z;CvUxhBfmn9krG9US)A^GL_`pEn@KFYK6<3QwGe9;m^HSH@)N(O3k>bZMoaucFvLR zlUlxtv-qdRh8eI5y>Ytuo1vZmX)#0nq(u+ba2!55RhHx6$E`n?+>m-xx!>bq-oE+C zz3FV7(g(it>TaC5mb*G2iQ)P2w0&C`xld;Iq)Bjpi0(Og zd(W?szQwB|@v-ZD&ZBnS>mF-11)SV)yZi1Twc8G?QZF2Zv_$I76t!EFCgr+oSBOox z`gUb}ud8RLz@-m$YkduqHrrT#kw|0ylEtUIu15RtC$$IB?K2O{T(4l_?C2^jMJdFqe*<#U_(`q=heeS4|vuFX#|u`82b`c77ln=-9n<9{d7fW3$9d$N{@$u9`f z6Vb%gw z7mLt`TAG$>F=oO+$tt3$g0g7}0fqA$Z%lm=6}Z3k=$WKX-{zRS5p?{{`q8H0)uzg8 zrMr&2uh3{PjXGrek=-gmYQlAI37-w3(o1?1YcyWm6m>kpvZbrwLhsv7`@J_0aij@o zT%6>#;7n82Ouh)u%O9^SxwYf9wrKG-l^OGln({q5bVRn~9paZ=)4B4QRfJlR&EKWo z8Rr@HYA;t@_eSC)%QKmkQzWAj#(9F@lQHK!LHU#yj|QB_(bIUw0z4M zN6uz%f0U;wyhD=VWufWpMTQLxn;)*JNi8%wTKiVMZlkpR{c_$F-WmNL<;s7!Zo4uq zhk1?Lj&H%cZ>2n)&L^mH;p8^QaJ~&S@n3h({`xZAGSz0om2b72OBp3x*|%BhOrKU{-ll8`fx39ij7k_ltobp^ly_3zX${QCPxbm=J zGTY%Rd>ihub0qqFvfNR+$ZT8MjgZw&t6SB6u?3oQu}rbzy0hSr2kUA_4%s!0cTX4S zmF$yZus(W@#bzP<_Sx~*3^u1T)*s+1y!EP(VWpza$BmDK_vc>>4i#9qeb)P)5X}QJ zs{^lg#cq66XfAYOSwp{2(01>ap%Z`a_*|{_!0OP)_w0{Em-7GhyrO)kCc{=fFTZ;I z+1skoA1AKZX|TAEKWAgrm%Q4z2d!zjv)bQj{&*xS-~KTx#BtWD&%56!_8s^Vw{g`B zjfd)4uF`5{8<{H`J~e48UgMC{eJ^+=ULyRp+s<)P<-kG1JC^JInMX`H-CDhD{_mK&%%0j z_M8Y63)LfC-7%}%pLK8K^)cVF?8WN95Uxbk>)tknojMxMv!9$~qTPUw*P*FL38S;9P!ShhhGWx!Hv?eVe+w=3bRHZ~DjoGL-jaaH-VV zcTd``bI?~HhJ?nf= zM-s=oZAE$)jdvMcetTtY^T}$b8>vr!f8O@;z!$;CGo1^*PFuY8*EPSlhV65YdkH-b zo&KjOsrA`b)2}M++q(^3ZMf>i`78J7w*BGej;AX`d^go~Fno;L-WoLJbnEr?uN0g5 z3Nz+wt8Z%ywaZIlw2sfTs5H9Z*QL2#JE=X>-q>;P8Nao=&P?gplH?U^l)V0GZ-vHOL7oXsTkOIX${dLF zch@jY5O1FMG$slubsjn z6S8X6X6^iFSRJ|2XKCo4ujR4ZrfBl+|M!;pWZmC67gRrN7l^an=ory25cEUkK)JBz zhI;nt8#6e{_&=}Id%&Suu+hfWV5XnsKE3BJMLsb(t;-V?+;e`}Qf`si3uFIoO5j@6 zz1hAwUZF3B-D_V%bHMjS5BjJ2t-fC7C7#sWQgqk&-Jxv@9sbNzWaFN^{P-JQu`Bl? z%!CEjMtPK;*L6zhopH2ulI_$Chm`w=9!^?#qj{B=#o}cv3is;h8$P`>$^H2$F7d@D zi*LH?zdynCb6VouLXC$ZXB+*uf1h>Zn&PV6^Nt5>bI4`iFva``fA7VQhn1#o73zsF z5IJGGEL`Q?uesi-vJwZneOVRqKZWGWXG~AE+$C}IlwqvK1E0PO(KRQ^r#BZ*{qjlr zyIR8?k=+yd=S^HG7wLX)Rg~tP^H$rJ8}R#r4UF&(cHulXbO?K;jA4R8aZF*=qC+qm(1!;~kyVluG+;A-L?WY_6RFY2iMS9xo z$o#TH%xQ_ij@NJ6&N!WZzs$c$`{NOgTR%Lv-c($(J=JcVs*H|)&o4EvrhOh-N}&<) zGe0@3T+C)Luij=)Jm2XA$*}y634hfDm*^yxTwc|?+4^PREVv28P zo9=Zm&{fOt!A{4F-i+03nh!+zUT6dtKkj`bTr!=}@UBwRTK*o%wIAn%`>v_FuaR!N z=GpDVQ+bYmvRQNTq`s1S;e*GA1do-*J%4_rEnL-`J*lEP$nNiMmRr5jJ8pW08b^Ng z%wOZu#h9A9zMtL9Z;3$#7oYKD%dc4%6qjAlGPHSi^75>;<_1fiZC2n|9ttVWa@MTJoePq@ioc&d8S+)&QlHrK z%AFx=!+v($KVEXm=lz1U-dE<{>iO*T%(SjOC4Bb1kFQ!(MQ@59;#Wvd+;i*T)-9XE zy1P_47+(jTT~WyUNTbnu&H;rMvxzO&81|G+HqP4lwJ7D)?uh4mTr|p7d@dE>aQ>ZL zcrp7n8-r}#?bB`%B6qheT9G&FT1C!6OVzE1x-RoNg>~0gUifzF%pt)Shacrk+s3~} z=F0nz#|_p{g=&5vly7l9M zva2RXZBJkCQEd7aX5I44YN53J6P_&5%)eV4&R$udLpE zQ)=mpbEWwaQR>QXrmfsy$H^}F=yJP7(_xu~IvlGLT^A<)Y`A`0-mi7#m8M75GERK1 zL3;03t$VlZ+4ep89zmMC!u4(ElY3_CzWtc9_WV}C-)1VkT;feX-l-k4m70A+bpDcO zeg&sKmrZPla4X!?wbm!e;mfQGiza+qa;D?B{@8lUY)wi_3>?%r^G2n>GuNc4|e&N z2NX=w_q<-?6)NaCo!>jOd7t-+E7mGOyOm0hvs(rz$DB^?&^MUf_ChB+tn%^h$6s&r zG5nk)E?xC1%-LVZVyc4W{E3>YmkR%QY_@U1G}Vkv`K1PZv0E)NuZMp~TX8jU#^p~l z+pPIK+j*}{&^mg6iFx^KhG)+C2AT_&Hr~0a+gt5V!nu@!bi9LruGi9ut ze&|`3P(|^BBgbwotivN;da)e{TMVG7B1Cpw~v@;4lUc*zGxXbGvlXmuwi4U9> z<+wO`N9wzr30TptF)iLBV0ll-Y|W_2n?p7BY_imTq8Z`E%j#P6W__u-{~X4EPKj^r z`mXxN^cGqf95LT#eD9m}(&rvCIqyx9x!F56)X6fwGR^g>2j`*(`__ejp3=Z2WUjLC z#1k)rnW9YP2Sg%bHr%=!kiBwBzn-wI6*h>an6g}){w`5-3 z8rBCKwoCi%vsL4{9pvr*US%?LoL^AA@|w_5X^SoUOj$o_d2TxTwe8`o$&DPpHh9h7 zq_~q~(~II6JDqZQ)^D$rZmbe*?JD8Dl7FYwTm0i0hPTWt%x!L&y)Rd@1uvKSuR5=A zuB}mAn*O%F%cs6>U2P*E`Q=vgLvFY2=iY2cSU7ca$laYQB3^9Wp6tCm?9sorl_rgx z9BK;{nBDJaRc&Z4W(wUnMRr$0VdSgwW|QySmQvv@-kuXeN=n{d*&2EJ*2Bgvmft6- z`UWyCV4V4Q-xRkLj@XHN4_8<(d>P5Zds=E?(zN+j-~3bG?QmqDzGIWvY`)U6)Yp|2 z(VO;da9(~{;{~hg(XfJOIs3IG9NQW-+plyxoO&C}5&!M{v<2VTYQv_KCtX=L=io=1 zXC*w0iypb&miTN z8IQn^vi~x5gl0uPW-@RRJmqA%#d$8*qsXO=r{{->u4otApfz(^v&^*PbEn(2z4w`7 z>7T>nXLgqPd$Uu|Mq!7xi$NtE7jh=?&ecd-q;c)U%F{k){{IdxQZ-;YwQ2r7w~sqy zHTG|Mm-3}i@!6`Smc=Z(=OeuGCpD~2(+=5`Ta!BF^M#LHB2jbm-3*t>vxVid-qks{ zD!Y9@pL_4*X@P>=30a*T0Vc+tp&S*yhje{51iQX$cHh0xv}wynWoaML1I=si#Gc4K z?>DzIg?Vwq&#Xg+N1m%}TpI6U_)YbzEH8s6!)Ys%qbq~TZ}M`4T8S#|*ITZr^D^vH z=fw})^ZWLkI>(aocUb}3x2m>owaD-%aTDTqyC$#u#j2g5@suZ4d)2-PzeAijo35V@ z@3?I9s442AFIx(;zp!4~iucTd+A?KZPhLHgrfd9b>%xz%OUCr+tweZ(VMuY8AL(W`9@b_ILPq$VwR&%Qps@?uhGSff(e z=R%89;nF@+<)mFF{$<_6|A=RS+e@9R=YE|!%N*Uab<>H1%o^ugv_n~e;+AHl>@sl}|{Ku-o3WBuQhxa6?w)@$@QJ!3Z7w zD>a$tSA96NZPKIA)&$#@*vM53s_ndsj%75i_gcB|`0d-Xr|g-Mb4N5UmUZGM4dnvP z8~-LI#Xjtg-aA3=UGyo2RRtTket+(1RDSItJI?{sjL>f**!bH@x!+e zNt3Aso4q%;nF!@tEp1;mohiogrCNQ3RQXc#dFR%-9?L1cC3D5l;nd;T?+P}iU-5mz ze^4%cc67~}q{!BZV)-i)RPMDsb3b5j#{BQsI%dw~x{7^!Vm`mHoqzan%umg?H-4V= z=u^&nF7Ty{Q~TKM56SI;b?$;!-~P+WK4#X%b>-dh(mkhmO^lXYD0!gfAa%*vRGTNb zK_IO;;@*WrGd{$pG@IqeOgC-Vlo9agNbsgFR_4wg$L5^sdS+0)pfo|!Xf>Bhc6dec z{vT%|Zu3k(nkI26O+Q-m>!I66E_Y{aV`S}ij4o_5HdfEtReNLF=ff-4`HAaIzGZyG zWZQ}gtJHly`VD(`Ez03pAM@tQx6dgHo*BBB%QZgwYWUKd?a$L}-^4JMEjQj@y}0p* zqy~%A?DhnXh11SnYwj0o=Hpq%t=-GzD`VZZ%_8XMlc~~r@n^+DJDM6#g{q~mnjjK& z>6qSRyD7InpO@YyUyxXCCXpCC`AYrqx8h89cTG(SGg$lD_;uo)8+xh>pPa7R_-w|V zZE{*$;#gYWyIZ_`s`gknJIr;VL}FOTwfR>n-JA20Zu8#!+NZm}a@Dmtt_evAei@Rx zpI-M1pCPTz=aBl#TRvaUW9pL?QcqmE-$kwVEZ(U$CGXwWf*q%OJ%u)HwTxa7eWRCm zsziN<`WEfqn8APG4Uq=MNqU>)Jq50xV9e?}yD+$sd7YNVQ3IauUv}w! z5O@;buizPdL34R$&-yoN606SrWl_F0H-77Ro|Z427flz5ml;S*3C(ax-5cc*KJ_cx zUjgGMJSqY6^_~Rh{o>|T-`y6ubx2>p+ZCQC?HdBPW zlFa-whvwe-)O^c%1z+0rxyQ?|_HN>cD69+II63RJ_#W@zs@sK5t^pa}t!F8S$bGr} z&1;3iVs)OqNq1MAmA$W_FQOVddshq7p(|{%D_2dPVJdYcQ>0F!UUL5#S;@bTH`pjm z6`S>_)#>iKD#O~ahK4s~LboezrXOPD^S78>nyb?iGxvQJvih(%?_*5vC`4Z02{1$DJ-TCD5l5#^L}JYQ_5Cbw`7a2Hj7eO@L-x5Yg6C!@6Mj#Jh_I- z2?F0H?0s7;85pX0b3xDJzXzGM%Ci=JU#a`u%}C~ksZw3-v(HSe;7}vA(+WM%sS!bRLonuQBp0`M_I={zqt~rZ`%c7GWk3@SL*b~RRx3Y1%9B_@aQD++|XQl*$ zl8bLXd5Yf1GP%Q@pc*Ipaj%YwB=cX{({EO>9DLf&wM(x}sF6qV(}wJan_S8@kE8{J zFaFaIonrHV_1vN*=ejo?KNfJ>v1h*X#HNie0ybZo7;;%Vb?3GX%txuR#UgKtBB6{JvxoDMWm*#yd&gT=>Fi8eh z3I0_nQ1tTtn7mwMQAn(OIOoUehliKgetsR9~hc}DP@RjRyx%1F4 zD^A<%!a=1ru^9ospUu=zzFP2mYBOI3;TrzFuE~X^`k8>t${y9aIF|aYC`rxK5)7@ousoPJfsnuxw zIyLC*F7w%L2ciU9Qtp}MNXsqazS&W)-8sRjLF3SAKkh0CX`d=rzGtX-JBi47Nyk93-RLa_grCoPetm< zg2Ul+!qz7IUbXpH*BXPzk>9&oCNJ-N9;1F-$k_T=kCoCrG27m4raQwF*zYaPI9sCh zw5}jO@n!dkSptc#CHCys+?86K{pJ19o56;G->q^&H2v>NrY?*<)z90xP0!$v?1m-6 z2PL$X`I%Twb471mD0(@|M6UI8(wT~lF7M<^pZ7$iCrm$Q+^%Rg$74t7Ppb_)2e!T0 z9GN|<;Leema|InLi-Xh?3b@>Utty%rqHE-MgU!pwJv&d~*w?IIk4nsH6%Ln9T$Qm~ zYntZg4bQ?>&d8Y)6tR?V{%*IT{rNK5Q}k`y+@m%&TsYFW-@L-~#ZxA}Jt6<(-yJkx z?3GxvzeN9nQ(M@A1E*HJI%yQ4(DXyeOQSX+_0`WHzC5XTar4--tIfjvtlvF&GXKX_ z{(#jNBK^wck9U0A$MdXrMt_>3z}+{DPH(E4+cKKxoM1WYX>4zK=IBp7&8`BS(<%(R z9M@DVT+7j}eZ(WGCgahxyLmgLp8U((u}jHs?=7#RA1%HpPU+!!6rj&lA{ywmC%d9- z$AunVZIi%_GEb|Oe;+s?vHsEnlR3Mi7o}OrI1AC6u^QTpT?*S+~4yP0U}~qWR$D zrdO#i^{%96Jk@>H`Ze~u4vWCrrudFh&dmz0AA0vQhK255vp)6u2mYk#mSV*@HF2{a z27dVaTH%O8#j#y&J91i74sX#}B4xC&yxWYwU-GPp$r&S^6Bi0p@^V6(Qv`pwKX`hp z^?d@%yFl6gjw{Im3bT2mXFTk@tMu@6-b#scoi`R#_I_W*=3hIj_KmAy8^b}F!+s~i zxsn)Ne3q#iO|f1tv9M#Y@6vjU>?xUz-v0z2>~)G3s%A~yva6NZv}IP@`5mSQmS0@C zMA>Wi@etQNovFRCW-aT^H&X6j80TIHwVH~m-R zL+!r_${Sak_GGj?*i|4_Rw5T@)Ta~>WVE|w`L#7SrPn9S_#)I|tSj;C_>^S@9F^BZ zL{Ix4&2I5No3UfIL=S87#2=zlPOEfzA97gmS%ybI^aHDh!&;-PhI?MOU9+zpzF1>6 zmDe)qp^Y#Dm#yWFn!}EN6&|sRO_AnlTO-Dk`1y@)usYTQVYN8}Gc1Jvkh& z(gO>dzOtUm4&9skbkC}zAuG-lIO;!P4++z?W11v&Mfv8^sDf-^X0Oxxj3z2gU*Ek% z{#58(#aCNCIFyI}c{t;C(ZN#_q9w{c&hxuj6t=$G^ElrQ|5y?4j_n7X&YagWWqWp9 zdBv=NnF~c4^j?Ix7OgrjX2baKX>P*(2f_j~raXMAG;?-ja-2>U&k9-PIp-H^>HC=| zul#U;=SKD#=IwQBH_Y^K+4J+XP~C#bp0%m`hjM#b3J&$2;TE}lb#)v+yJ*Flpv@~j zc5^5&d`#k68+}Hjk6)-d?Z8ErqYHwy+cz$pGEFF<`li__cdK*C1~UR2wrV*}XV3h^ zZhEU(c)=X5Y|Uwg=Bqn{8_x)DZvM-x$+k4lM&^T=t9zexh|C=EGEr+Q^J|=U)oVS^ zTDj((>6-E&xby*sU{jphw*r&uxUJG0VYg;wv>GzsSM6VEd!%OrtLov5o3mDFXh+QE zG}#akVh~{Mr)qg+nqT7PSla-COl->;c$n@xMPN+`U%6wL?)Iw4IeVpd=mw1&`S~8k;Un(-4y}X!H!}L1_QO=8h`NR|JHmN#ug2E09$WV^VKvtR zC4q!Qg$mGt2lhP&9bPH)P5ksL|L%E{P7D6$Rhf>vbe{avc<|a&FLL_4{nJFts(utp zUSqH4O5Ehlwb0i?Z(8tqpVrpp-#^QnE_!9b|9$NmiRNjI#ak+^nQOFX7_QCc-#t~hW)JnCru3Jm*SBA2R9No$|DY@YHK^@iV^>6Q|U*=u0AZ=};rlVdw_oPVgHDV{8 zENO96DYu*ORq;>itqm6*nHefvJLUZ9qrIOFN8`t7vv+W>R2RN}Scj=!`F>o=SMG}^ zT}xjt<_T1FjMVbcmyLIysM`@z@3o>rH1xiLOmNZd+q?&v1g7sel6G`Q;^|d(d%4$M z(BK!Fd9rfqR9?sTW!dkR{JWjqs`lF`ZX!?d-5I}E$@N=ZlL(1?nIU>ysx+skw#)So z(`*q@>xdbS>(jhND)aVUblcAUaAk=Qr|~9zx!M&`@i#edd@wuOa`eisMQ1$bzcS0$ za$5J|GtIcHo zUUo{FY<~J73&V(-GqYa5J~Bn@^KLQR(vPNEz2$bXNo29Dly`Jmo&BNWN3j)tel}?GCaQ@7c zpwBa>FM4+0eX&j2pL|(czFLd=_#0Pr&pPahI-qv2*4o4RVyBGzgfslwwx{!!&Ty!? z%U^Kt-&4`P^I8YH7)nAftooN2RiSk|X1o0MEi$JVg6ubo^GwNFSX0UTeL>;w#$Efa zC8&rW<`CF>Uyi?RO12&M${V*@;>=yjUMi@_C0zcS`8Q5DBH~PUvCVe5!VJOatj`Cz z*8XBBNIdv7-c~+p&*4g~)~U~bt+|tVt=D&!?7KZLOBg<{efZf>!nUsTTEo>=-5dIS z{s!_Mu`M~LOMX5(X<-mDJ`Ki`HS2~rqiTPngYSfC z%o#gRep2F6es#1-{viLWiC2aD*6sSh#O$h|axh72OR~U=Nnbdba$OTbUAtrpEsJd} zZZ9%_*(oXd?Y|Mv+499M&I@|Fb$mi~J~v!h@5Fr3b^gtnzC9URx0nmPW0h&T75PL+ z{Pd&xK<=PQp%NbbwmB`23s&7;#I z{oZl~Ma_>6+rwJ5f5(mwS}WqO{_uTuIQQiHT^FrP%0u463YhRjQxUGLI&$Vb%^PwoyiVx1~!b8{i znNL}x#Kg5a!1VZ<$FmP^?UU~|)AnC;P$?z7|E@*3Tu$XL(R-;1TTir2`lWeTZ=ayq zlRrl$ojkQBKhV%nQ>SXf;SJ`?Pu-Q89W8n6N-VR+BiAjvl9tEp%<O$0P{|g0~ ze3>5pfBP0XCvFKnm8aGxRP;t9A!n87#gNQtCl2z{S#q0o^!#6N zwxu6gX#YxN*$Qi(AKtr{{PulbnmXmI%;6ORiOrHn6eNxvl)B2&{HxM2Y^xf>gHw9v zdpT0M{8B7pGh+#O<=awHT zvcoLRdy(Kd7Af7%AP!$XUx9^3jh?jxzG`0E{!>}-;Eexo`894R2z?GYQewlgKHY*Z zcqYplh28tIs~yV=)jg6Vyk^hXbg6mQ?mK(7P5Lx3qHc@w(|O@u5>b;2zx4gr6)*Xd zTKdC?>8SsVb)lxsFHTPlo0=$kWXV+bo{JjIDX|5IM0*bM2+8}MZZ9j;yzKqGWsIBn07vK@iifFi)+>z*M6+tdf3xv1J|PmSKggg z{pF#?$De2EU@dJXDCME8Y}~U*az@*g{ZbYmwg`E-?3>^Ad0Vo`8BOutUAInXm<#rB zRV(hBd-55B-F@**o3xcTwiHZn*twPeeb@hcr&cdZ*194v;r_<6YiCt1PRjM3Z{^Fb zR4U(VG(+&8=5F0<8V1?FCku!~@@X4N{WG8C$sg9D^plYxyfWI9Aw*Trp}zN6l3Vg( z-LkbxhmtOZi-)D^8>xC}geolEb-$!W)IK}8U3Go}ONjoVz%`2VrY*VAdVu}^!nd2e zxrN>IG(%24l@BYKksB>|(9)2(`OHyf(Z!i@jq}d-3(qiG6m_)uma1NwVcwA&Kblh2 zW(lsjq&YP`#AjL0exqW>D=My&Sm*uyz4~N(tHACSx2{JH{1=sYQtK85nf^;i)~0@a6UzmA|4%w@AsfJ*tGMLDg~d|# zx78l5)7QDs6*SY&*7%fx)YGF&qIv2TEW5n&hMvyOXnm8Mwl<#bjoa)EDn7EXOnKQP z|7Ce_W^8F*^`eL>=P=u~GW#X{-f>=>>9cBArAXNS)T5np8b!Ko>lwB$`EbHQcfLe# zL`V0Gb-sKj*7o%zy=1)|AI~*;@y4G4u}+fg-*|niyi%&d`JU>hnyfU~`EJQIL)|1- zes-~g$CTG8GJKet^YLl2Q0#@eXGvx+lz#^vP>Und z*8<7=3L(<6aT1H#F2v|bI!IJjK04}T_~mo`9F1Qebk9qt@Nq2;xP9}@|GQDWY5`*V z7xp+SZO-A-Qh6h@3qsA-`juTWWQ@dC(3Ir^JL@b?YDdG7h^J zuiY{0(Yk*O44apj3vIAjUi6OV>W5=u#zh4#*K)iXo=j@y(DO~&>=0-xzWuIt!qG-! z-A#Wae~ACi5Ab;}wkBsw-pZ+mcI^;6e6Ql_CB@j={M%$|mrsABQJ?>}X_id=@wAD& zDI5*EH?NV~tkrU0@?y2%Ql02qrf;VnSe$%>XHv_K&HN`j8<%aGP~!7+|DuQ1>vu{1 zE`G(M6HxGelJ?4v5|fYhSzNxPymq>$fQ(~@j;2;xNnDy)SlP98r&(Ac9(gjZ_KCgx z-OAkgMP9|S4KI%D*tb1bkMqEkz~7zrXB4kW&YaD1<@D1(lZ=bkDkRmn)Hd$jY1VOh zxm@28ts^gT`d3W)FXne=$+Z6~b3J0M1aI?ON~G9XPOu3Qm>!#HsIcQncYXYPVeU@N zogB#v4mD*5&)TN9!;dYkD;#Hl~<=@?sm{gW{caqU(kDprnX(gxk95{NVbgzU(e$ON)MuDo+lVror zPOZG1uy9pZux$sw)%U4i>dt@NRCudiN_@)lwXc^1^0{$EaI*hXW)SL?-K2jyZdzaM z2Mfi3Cht(O68$a0b7YPkwh?qIn=)Hqv8G`}M z4=UWua&cjIRcmDX12mdM56xR~U9kC>xb)6X63xG)Cy9JfU3ut$x_r#))=0m_E%7|j z+|h?Q#hp|SKJ>UV>8n;3x7LQ#%);l5#$HdqMu+sA&~naW_n5JC#cJimmYz)ta>wMV z6BwRc_|SaF^TKs=Egmt)Dj}V;Pz4#uLp2FqB`^2CHJigZk7LHnYgZR5eN!p7UN3gb zE!aUX^WE2iZM%+cxEDCL_pyUh$BC)3!7KG+cp4LwvQ~O{Ty;NKwY%cUSNo{oIol#a zSvQnabO&hadzG0OZ;0NzrD(qDh3ginH`g+R#NLhNQ&keG=jszOi1?-yrX_XssK?Bz z=_Y^I22ZR#`PfZ6+v(Kha^He0Vrs8$Tn-MG-#k|?z-(dT7CoNl+J`uLB$ z-tcvD%g!uZCn~GFO?Kz?_{BzPcM?J@j<{_PKB<_0aIXvd-)($PuZjdN_(ZS^FMoBrgc60lKrkzf8D}UTCWtDArxVX43lz&f# zc-V}u&y)|o*l6{2UFLGt^N!cAT5dii@n9iCReSM8kyZ&FgSGkodlZ=&Gn_Q{R;e)= zL~IJ5&T1qUx3lKpVv85sC443v(+Fix4>u5HeYuEHs&MZEy~oemt$i%*SC%)6%koTI zu%qMiHt`=OLSMo*cPrJ}&Da|5Z_``!*e1?xUMQD}+KxpGfqjo|ty>l1=O-z1$@S=> z2Rj}uKF@u-u)+6V!#$I2Rh^nz^ZF|y)-Eb&U);99;qt5wo^Zw{l~~uMyYk%D+9ilS z{kGCQ;nmF>|7W@6c5Yv#kQ1F4a)aNbR4wMK#r(RUncJtB&dBAzS+zm&li-su#S=5V zU-Q1wX1>8`^rK|2&rLQhw5+Q7dx!RIf?<*4@fQMOP|P`MDRY+^eWlv#@=k zS8d8Wfz2J9XQ!>3Rbo)W*L&9ZvNK!YZ8^CB`Mzo|-bK&Ulq3YDl9<-@d_F|gjjVk!7@O{{2s$*a{X)+^kXPVbbO zAfR`A7e~h1;x{#QtVyf3Zn);=5W=x(LP6rHLqD%ih&ZUaXJbXvS&oFM2j-u5%h-94 zaq|K}H5UP!rC%haPq8VbJ!|S;JKZB^R?x258+@{>X3zGu^H9+~<}#tJ_^Ji-p>Ce} zKT`Ae-??!5al4j=ccldLwY2qnl0LZJ?J;xm{hZ>?&VRf}%eh^k>TK5RZY2?oEu~)< z)c)9V;`NN)c(qfjdA09smcGU|DqK!ZQ&1iGhiZlP{;hOq&TNu;Xpz1YF zMJ(%ga~mE$E?BmzDLUw*;G6?KqEotN#WQRU;7u+$$1eQhm#8>jjQ8qB-_yG@Lm%tj z4|wFU;FXd_$A_s5(N@76e^y9~sTRwu5I_W95hk!fc{3Z{rJ`jn8M@n_09 z=`)|quIDcZ-JW!M`{G!8kEPFEDkp~=%MU%HEuFVZs`cfstzpkg8NDopzi6yvRc2*g z$ed=;7P?>!+mYk7jsZ>Sulhc&+oTp6y6eD>q%XWZ8b_Y&KApO#h`QH<(fkKAG+A;o{i883SNG$ z$$NME>EyJ-J-=MOP21Ad6nSm0=+m#yc!iv_x_NsT*0>&3ReE)YsW-gq<{fd(mU8Vi zxeTqxHZknY*)eU+L%#1%i(a>Lg%w&ZEzCZW&nqy+`Hb88AH36xGTSWU{+8W6y>)ij z>LAHtjeMK+{k}St{Jxqh6KV}DMHD6-=UsV2Ms&h`k1uVPIFn;ML|fJ^j+ARJs$HpF z9AAHkk<&S?LP43wdV;{WC94gL&CWe?QT_SFM^QsnvE730+1i5*N86OU3RW@8$2;XN z@|~km5OMOVmcQR7`H)8v6Z+1x>z;O+)u&&DrxZE*;&i9AwF9uBm&NAFaM=v$ti{la#RXdk3F#pNyz{ zCE`%mVpG+bddJ!8-~V;u8}i=e{_aTgyiw^;t@@Ko;S%@1nMaCi`|Sm!KQmX?=BSEs zd|wtkwdq)_yw4=b$bFVigAPosY>UNPg_Ro0iM zCNeg(aLZVB8Tn=HohyGV!C->bj%jK=7DwH0zdKMd)9tGZ%Xgz=w~y?ZDEN5B!A-k% za8_4X?bb2)`c0;QsmsdvW9wU1^Lu5!r;l%E7Kr#EUa@J>qlA|23xauT#Fmw}rf>c7 zph;_Kbabq=^+E%~SDfEZ?@E zvZRpBIZ>Z09q;W_bCYGQ=+UvuI9$Y3worDSznaNB;lLS6hS^*JTIEOPMBjMk#=od% z->i)D&qHT*?X>Rn&U$)R<8@zyzL`q$)NFN$l9ziqbfmZL4&K7r{d4YfXK|MUPv$MR zKK}XP=LMW9+a#9!OcZUK*`^YelJJ@*-a1nL%dHC?TjduDd{g)lzW=+})!M0Z@2)Yn zy3;PG?ZBg~am~;5tJCJciflDU;`b@|##EiT>-po%sx~FW=J~3LB`dyqo^GC2dAD?P{hRk0kx!X-zh+tdL&;A5p<-s*n|FUUdtnWc**YS!U|h(yvEui#VnJeQMI2d7p2~Op)9-v+r_0 zsej>cUi(6G^DNfXP>FZX_g`$7E+wsF|KpIv{;%9VxwT&(B^@|1DdbVP#Ug>Hw}gwn zg;YIMy!t~lllx6Bzq-SX>%Uo+*Rv?|_HrViTdWUs~6V0c}F~-d~qo;RME;wIw*{qP64^Hhny_+?pe#SYU z%{d>s+_jgTNnJbTZQh$urXyYwy>DJE|9m1KPQ#4N^=OY*;37T|)d{Q%SB1I@geW^0 zs^sVVdACR?;DN=Jh*uMr7aPyfkL>FDS()ghxh5dS@XV&RN`cCiI~QD!|JmnS7rDcCeQ9tWPyRm2Z6bT3u^g z=Kf1v32YkWyXUPtI7ew;#*gI61&X>9DY zUbrFT)V?y`(p{fE3-!x39SHT0uXt4Y>)MGvfsKwLS!crTd8-z@Iql2pbVuIWy!+ho z;?qB~q_#9M`!T2-GmmTBy*+(~)SCOVW7o%c=Sxpiynd)pa0$odty+xo&Sw`S^&Fb< z=0y9nGYy`9Z;9t++(|fSyn9`p!J_0Z@lP*3ewdRHYAkiA_So@F-$W*zpR`daspQbI z34f=>eRhm(7PPdKIlXxIuh_C2569|B^9442<$Jy+Y1M(KTSxEacZX`8VqP0_K0P5p zD2ww{QkARKE=v_lQ^)Pn(`D<`UA8jmw%a|)dhqLt+1Kf}0@D?_FL2#idgS`a{ePRd z3-?~T3aAI{c_syoGuNSLxPZMVS$jzT!u`|1wyL#qV|bgg*)YrBo@b}7;x=Stnyctk6Tt~6z8OPF@;*5}<^&flyo0wd<1^$-@` zcUOLM-4X6pKf``p6TKkv!I(94)dF|LoEZuY|J-@QLq9ZpKDRu1Q%`r{|7o|bU*}yG z@^#1D?3xtb9j&bw-jk2kfx(bVPr)vL3ry3@3HA`d zpNyN_4>V|m9rB)GqTyP!VhYdY-Gx0S-*;K;edn;H-uULaotHxNH2WtAHDu15vYlz` z)zxwQ&n2wI=A4Z$X|Jk$bR@Kf{g=CB`$ws&uc~6}%^pWyU(@LOH_J1-ls)9MpH6Oj z%=ef)sm-f{o);^MgbJ_pYFe;aM#1RCaqCGgew^+Ln4`A$v9=zbYNU1G-W-34ZTB9| z|5SQI`^|B#i0QTln;mp5L;p(gDZdO6bNHkh^dMGL(10tWGe7QC-W#p&+D=^6 z-uB%{{!)i&u9BJeDQ^iKAMQW#@7e?}6ohzjPCkA;$dM~GMZu9{761BFUXHJ7(`DjW z*G$WKv#NVvQFMXPPod0IcU2fjh zsdXQu9HLJJhDc~Jo{rj~ArifJM%l*J)V>+|B1^4`rPeM=6OTZm!%PeGgvy zylyqTwl{WSQ-X6+s@n_44HwUK@~;;+dy%)>mtVx7MD6jTeSOl`LtX9U_OA|?ZIqC) zdF1ry#GaJs=FMD+8zb~xGIBIc4hK%(SbylJOQnGBVykTzTrXzL>&p0QD%Z2v=D?~` z2d6eoyeA~~LxQ1f`Nl#XPJa)zM2(zkNo8iUeg(A&RxNkg6e>jW->F@Zl#A%+IMcPx zpFKqP=Ajp1%KeAj7i%O(w|VwHT5-+f)T>8MjZ>F|-w!BiQut6abz1C8?O#`ySBMpb zx2L_DdS<@7+r+!qg(k%tUlF}kdTsS9i`T~PE#a)!r^W3xOyCuk&SKJ3+f}$ku55-- z+vApsoQcPJ8`7Q{_FI`>5vyvvb61+}b7#YgaxeW&drsWzsCOtkK2h@c##tXyW>;4n z?f6}*9o56 zcU1}U+o`Ukoff;##-wy%#^KIwrbd^SJ$cz(Rmic*v-FDI?%CPV@7p%_FPz&Xbx&U@ zLva0**Bf-tY-(I}yQX2r#m&4ti@s)0-g>4-zaws8ILopt2LwfH>>vCyD{B%Ic<@mn zOE=;b&xW_(-rRCrapY0r#-74IdNxr@ABiuWpun?e3A^-()h_Mx1>JKB9nZXavE|k2 z&89mt8-#jVG#Bm63*TFHT7czPeZRZjS?4THT-R$sp z^KGJ5?`9Eq-y)e6y0UkLZGNrHJKn`CD}p@ME-us*pSNFZ--b6EP88N^_Nb&oy-vrazx{<2}TYRK;l?qr_E=F*j$ zO5QtiZP)w2%NG?MzCpFQplF`B{Hltky}LW2wyx3>UBBo+r7*`niR7ZaEf;jP`tJC? z;TOEJ{_3xf#a&FRFST)nJ`BoT)8)4`*sIvXSxd6IT|f84rd1klDT#3pGS9JWl|00~ zAiYQ4*TzCl)Z<3`=F3V~73EAkZ|H_tU)i|5XH}Yuqdn8Ir0R-~>fG+ZPs{DL$%+K- z4ew5S!p*tBUM?qdkMwQf_1&8*bEj|%T@^S#Eop&3Q)G|BimVkIVlE|I zaP3ktNntIQJ564_)9Rf$jw^uc~kd}vSsUCZpE!& zxzThmTBqW!VJFj>%g5>x4(LZ;IV;S*X%hRgqPKVNFHk%Doj9zFb!{41Yz z%MGC>{Y`r1m-0@erJwn-<)QT@S(%18Kek=ta5~uHa%`#D5d-1HD>U~0-F8AVv_bWZ zYQJyjDbJUUlc#d3%H%N!PTT(Yka$=Ar>M&(RYlk4B-3$i$!S;chXTb8a^vuw@0^4=(2dk4veBhexH9NV6LI<-luebLES>4bpA z)4DiXqV)utly=emJ$?zjr z{=eARy3*jG{ej0*7+TKm+Ow>@Xof{i+QiZiTNA^Bb=Il>PV74xy1!rAO5bmtrOf3g zuMS<1xVCbEqOS5^m56eKL_zVE$=r9YTi?;WQ*`|x>ysNRFP?RNtaV&pYg_Z`SIqgZ zSv7oIy%YCXyndWDl|$(rccPAvnO10inE1OMi>S{f59BVitg;N#oLaEg_}Gs2Pd;xP ze_WVz>NB@a`#FwH%XX>Ed64_#IXgp?M<#0|=blR&XEImE-c4Vz>OvOZY*D5)4^pdk zb$4lIZMpGDw;^1s&Xrk|`=G|%iISEvKI_E;*&Vo4o_*SP*CG5~r%<^C=4{o>+60!xZ!ZhHU9EN{YwzdKsvZGX7tSM25_;P9 z56=CTiT-N*mFE`!x&D#}Yd9Ef=xadruG5?e(BgxP=@(=cf z>KBARyx+UJXhyS3uz2qERjXdP+;10IQxkAk<87mkla|1Xh7VU8doNs*a5(euum+RX zEa|B?+I_Av`<(i9B)H{KYebHiMA@gP^5HjDm`n2 ze&{hQusytR<1c5P?5})FOS+STb7w9sU3G4QM%+QgCh<4=qOl+NDlR9!DAC$6PsZ=j zdgc3P1XiuHteYX9zbck7@NDLE!Ga;Te6!@8N^dT_7IvVmj)~`9;c-sc0*AP= zZO0x49uw!)e$VUZn#98s*At!em{ZiLd*ej@w6xMiw`ZSW@RHQHyfkoi#?EQ2ks@z4 zGQMC>6uJ3wV@`GLHuK#TkvdN}os5+1XU$_0SF2cHAe}}?C{r1N#TRF}x zdjG0MYPTN0#+TB%ock%xCFY@8lJD+Gh_d`W_T66Ln_1(5!O?vZDS%)V-)D$j8 zOKnSv?GAYK_Dko)r?N4ZjI>3BE@@s-dvHyT(frW!kL+eyTJ}pmpKUnI8me-tmt@NEB$p5s5>pbP+Gm~q(xcgdbMu8 z^9*u5hXYvrT_X=2>tjD&H{T<|&sp>34#SgmdmeBeX7E)DtA)U|ta z{PFt%Pc~H)NA@^RlrQqrjawWLB=qp6$g*7%4J1~{C2g7I@MdXO_p*pBhcbBBX4bfz zUzSj<%Meq17IZ%Ea@Lsy!8x8YSKjPQQ<49&cTUznD~3Fy z<*9Z%rLD~SbRMN;T&SOtlGSP6Yr(Ljh-ubE-nit3dC9-kf**XHlsvn^+=lVxnIjHP zCJ}DRnkI99iqFyM+i<~yRiH&N=fe8_?&#cb#h+gr$@`Wka#m>}K+C;u}%4xo%FC8U5A;V#x-MNyB>|*oo?wMW@wR;Ai zlKc&YG>$Vjxnw&1%cf?YnB~8E#)=tY|C{2z%`!bObxyW0D3ldM>EH_eI?lC+QAF z)1auMOU|(Glz)3D@x`SNyw|E)oqUbN^5*=#Wi5QO&~1Z2)WzK2Pc%$fT+iPAv0jaB zWu(6SsReJJOG@tYD>&R|kmkxBH{kFD|%0EsL0O|i$1%O zCChfhCaa^5*58?;*$Ja*R$&o4TIRo+#d&Vm8iDR9 z>Z@8rtCoB$3ZJ}T)+3dU9l=&}+dnH^ViY{`;_4rkZ|Xuu+Pl;ba!d*h?F^JzDC2id zhvP+Qn(~@1$&-oCIFv8C^nI}CQSYv2y|!r8TCFu1C9YAdd}Si3bFMsE&A!lCmM6`; za{1r2mCc22Z*QOWV~l?hWHTWs@X@SQI${&29IQWes^HoY=|cfs+w;6mX1?9lrtTX6&Lv;$1LwKQ7g`XhFBy6{EKS-PRA ztGhtwQ<%a3r_)lV$Am=sKG}HWSpS9XuKTPyn>5*16nQ_HJ^#Gh4aUDgCmH#B7wGr; zZoR$eb*}D&meb~|-PGOc1*|30gtsabtUTuNPyfjESG*}kGeW*yTI&@vUqZr_`_n^- z-tB4Zy(fZ#4KB1@-v6hN+i%zXbg?$|b!$b|`JUmn4PSowiTWIi&BwkRaJX4}q-JT# z8OHO!jZ||_Z_IH#_;5nV{>ALFCOh0FaHtu3Ii6YmaJssVXSdnHMLeayZv4Bm_LcdL z4HwRwlNP*qBjA1Nsf5MJ`zt$lxmGQC_iXdr@*OX&y6dXX8cBxF`!v_Eq3YJdw$p)A za^27VYIMHU8_9L&&t@(jZS6MwUhzw=a?1>MO74FcA{)l7spYppDaUaR%d}0^szGa> z^eXM0aYAd#FV(vqQNg=d*%$X4uRW1va&z`CR>p%{Z}6Porh0Ktre})fqepcw6HmQu zXLrx!IrsfE*YBg|9L3Uw6QlB(=1#P#nb(z8x@Xe^me^E<#QBr&Y;_mo+Z7YC_|VMm zQ{pvO)LJ|zCDcS2UiQtM&ZC<4#qwsb$1*b>xg|$gFS9=Ux>jOmfXSA9NgLmA*UoXh z;Lf-$N?KC(ua||w@gwsR!-6iC)wL{rKjG7^WnY-i>Q<%;rxoXj&#SWV@;}Cz&d|1@ zwL-#kN51C+3uX z%PEBnDQ;~72aZ~IE&8VQK7yktO+>p(@t%#6^0BRtj%k!W<6X2ZlP`RyFT?rd)O$x_ zWomwIsO=L~tV(=6#Wg{+K49DKy3p18^m#hAu2x$&D>b6@j?TGgM;Hh#{T$5STe;*l(%eZ!#lI6MC}!Bp=59N%8(&ffU&6t~Df@kMFBOy`IFPItZh zHpz6#CEWn2ry_@6hl@^1*nj6x+vburtVbM5E!a#ZH(kGUGgg=Pa8&)|!_yh*~3=d4@S@8R1XE`wuPppJad)LP6!hJJ8C!G)4DatM^y+?CT(#Ir@gKkdB@elq# z?Ou>5kQuw}ryK8s2Wry}sJ>n~LDlrM*Sg)i&z)0#8vKJ{YE^FO11|P|vyT+NeIlNo zC=wJj?TOy{*axRmvya|;bxc-+PiBuY|EkyXum4=wc$CBa&e;o%bw-@h>!*YTFo`Ul zq;Iru@8tsq9$_;+MN4M1ukS1ijbpF*`imaj5oL4dciB&daTARWdEF zjJ#S;wv~o56bZAr^u*s)e(0wi(BrUo+Xaz{nvW!v9b4Ki=viGz*Je=ooUn*BnlUi8 z^zaX#`yU_f+HyMj{)w#Seew1aB4V!OOdLj zB5^Up_(?(BeZ7fu^|u7tXlyWeezh$^(mOT3R`3o(L$;i`$L-jAtx0nH;q2V~nM?mY z%Y4PQ%k2LobDPhx&MVs;PBiY)IU1myH&HD6lK7kJWpP@iuhy2w$}VUTII{BdBDIHi zm>Q2stXR8e-tQb`!LWH17o{GZVVgAji!a|gmkT;8*YU1Zm@qX&kSXDcahTVNbMyLH zq@{xc&IoAxt=v3e?FP#~L3ewkymj=&j~#IC>5bm{iCIzm`{Hcpjsx3D3^#oP~#_{~f^4t{f{PJNr_m*v!%A6uObJNfg>=S+t;UY9?GuAjf1zvaq2jdh!@lK9&h{w1|3G`;q$lbv3Om}l zRTGs?riI$q+-o{yx8qit>`CwQPi1Q!NQAH7`n>4EF4%j6xXu6azO{Msd4APCi-pe48sU2uY0CLKEUy3YVph$W+6Aws zef!OjUQ@FE!(U0h3t_d(79OsNc*M5o@+xkp67@&>R_k4e{rJ%5V)VNz7L7ksTCzTe z3eKDT`YmfhJx6|(#Ny^v-1=Sjy)JMWdFKQik#>8Wx^U*>@U#;9TM`j=L6bLqRu^Xr z(4Bb3Xv>a$59Zu^!4NEc>&A80>jBw*=T6THPuw*}%!A8y)5EAszb$f9_ZK<*-jl7< zAtKtIBj%WMc-|E8Tm_!@3%R?O=q68y6aF}>E`V{T&c=eor50ySo)zWI-kzEk5|}4x zF{{+My!Up9t@qKB-iOW39ay8~6dv@y`@iCZUCtblLSA!wl^qzDJQEDtaEMhVMdRwk zS^8ZDE5k1S4m)sD!{yrkq$4Ny9aI*X9^4YH?^Ege>e2ao2}+mFsMJjttGM2?!0cOR z>64m^(gxe-Uf3h&(3Y`q@*$q{O%vtPT(@|Aatsw^yP~%4E|I=t=4Rx%96-r-*)^#i$$nTy<1TCr9$42JFMACkD0?#g`dZ6 zn0ZC*mSKp;gxcU^E59x~aNDU-uJW)5OQ@nxCjVi#)${TXuafb5u}EKR^}|o1%O6i= z;q$SZwo+!Zkjdc{A2)^^yHq1N{d-YoNZ!}9XD+@y3_kLa4qqGOCSTfRw6Cjs_I9qV z8*k6szbZdUChYA4#h0cRIo94)-@3x}=;y?ay)1L1FV>#9bHm}Zs$XQ{J#=b1TMak6X2k`wGapp_ zDomHPHVZGye3_MT+LR&pg1^qqKk7>!{&Dx@4r-DyN?BsRJ+5LJ=eOEycMG-1@_k&j zUr$e;wa6*U+&S6J}tMKhN-r(`kTdcSAk#uEF8#!j0Q5tgijx>G{AH%`)FRNtP)_J;M6 z*5Rx^)05%%&uyM0oU9=(YFxhH>`a3$kE(~BG8-d=tXWpFmL- z0m3a(b=u*IC;liUv0Qrn>#<4AxyFNTGM#0@@q2;!`X8RmvcA%gXcd1tNHWiMHRs|Pbs2UZ`Acpb$#9ah zTH<}3Ng@30TJ4?lADT?&v}xXS>S(+&_X%#v-8(u%o^e%*SUmZt#vyFk7E0>}WMzU@1#MV*U2Wz~p_J#1+xMv2 zENx2H-_n9+odx3CCa%kK%s+hnqB+m;S8bN7_UxGYSL>Ejnz&$GPsynj1}j3%{@n?U z|FAD{&))U*kDJ>KSU8t=eRtsU70$Jsdi?z4n(J*VBDjt*?8(pXTi~H3#KWX!;HULH@)FCEv>J8w zZP8h#a&D59Tc>oVcchk?o>$5?w*C|R4Av}Wq^Gg|CC&Sy7y zNNX{Byk>6upnFm$p)CA?sGQC|L3ZU2iEn0};`MVgD!JG%wmCC1{2#*xmk5CviMYQL zRFmDNiq?Maa(!`#adm>)yJO;t++ttarKMJWTDU9p0smY1Q;QxwT=zig43GG-s#!{j zUMEAlH8L^{{1-`XyOY!-q+b89CDXNR#kBba_skWGgm+(ensKGsm-oAavG-TWZBysx z>gjr1I5hQ8_*!8mx1)_AyIelT1)kxEVxGva8t(Y?@@l6AkNU!%%-MP|mCNgi^G(;F zLWf(Q)?f3u{+z8%SowuxVXYK*sB`9p>78|lB27AFkFDm*j_J5%6SduZcOB0Xxw@P0 z3h$TRGE+>rI`On(dZ4%G=f&L%Cibqm_i*~#NYOunB3xOCQ|A3>)@b(Jx%$`fTHES| zX;Ust={>pY5Vulzv&cND$Gi_koE2hN^Gfo$E8aijf4=6R*fb*@htrb#hgmN50L*tu_TcwSUTlt#du@AG^xlv}Rw5>UxJ8 z&;MKeE4KT`qjgXE--*ZD%uWO@5B}80t#Y)x^jyQ*Q*R=)T%WpeNk49SEZoKz@SFWg zwW8^>{Z6-1d8T~*b0v$-t89T0V+xDuhk{v8i%fC_Hg$fDTA2Gaue@~I6f?yYsX_HE z9O;R#UneB&wfy;kkMB)ccewmak$+u%a*0gOUt5<2q(zwUB!%Sgt>jS%xnBHt)m4dJ z)5B-aM($8txT@ag&$RbXvwW7@9XWm0`{X(KSsnRo|0MjArnde*`dB*d+XuDgxh(}kr&V3;aRx5aboe;vNv?$-Cs;oFyoaH@MxefaIj{1DNM6&W#GQfI8$!V<7- znyuLB7e-eTW_qnltX048Ph)+v59`*@^)s%lRg9C`wXgJ=hcqbYRy`^ClY z*Ku368Sn<5yLwyuL6ONm{+rs?isDQko+&7rA9Vli_&=6GT?t;Z^zeUFiMW z{ULX5^cCdVFEw$Vl4h-OupJRg^PDjmUHCSXRLyi zK97A*v28x!*uSV}a#a5FcD~XJUQxfRCm;9gIOjiilBwzL7OB*a28#8oa(g#31a6P*TZY6`<39%~<)6)Is*1Y#E#0u}fbg2>${RKO_S{r&a1v=0R7_{R zzVzpYZAq^D=FwNfrPB8COn%Fo(ro{2U39~9W~UV#tKtm2nsxQ=9Dn$0)&uRW2fXfc za{UtXS#{`I28)*|YtHGY-kiw++6~tfSwFG|sx=A~|7&%;FM8|TwV%fdlJ0BH@M}Dp zaCMbHu=7z4kF^hF8~HBW<~to16}m4<+xfWPLf;KbKRhqkJ~b#|%KjHK)_&eUE%ee| zh1EA6u4!7O`m^BYmfZ5Ieg_WU-^k5y<&?$apc;ow1 zZZp={*iEgB4?LH%#9UUdse%rS9QL5Q0%ShW4g_xzz zD_q{L+{-!1!`t}W7p~lx&)%(^Yo#8aic^Yrkn7mrB#`z$XKh(nv!He0d$<&1QrW0TwJ;E zM8VcD=4|Ed8Z4FTc=x^BoaN+p@>Jiu>)W!$MSgFQegEtnqf+_J-=?8aVfi0gZr{*w zD2tx|Gp41JMW-pX(_GFwpJ)Ha>yh@QoLN<#rklJ@YFCP1>19}A(-NSx^1`jqx#mKO zaUpMx9=#?xb%*5MuethDV{(M8yBbpswF`tcf6VHgeQec&X#%rOtHw7k`ry6jn*7XF z$BvwR^|Y7AV6#)%1F@JM(F1028~1m9+PgqPDM#stI&(%=c+H%@dA?zXDyzd1l`j4d zO~00rqEO?p+NZn7wb$dyot1ZrRx7kI{`(c-SfF6ok;E-ub8TJF-=jq$huSqR39b{; z6{GZ7Ai6)zU-P`9;xGeN~>n?%(frDCsx>mJUuuoUEBKFatZcGwHGp0MPhy@PRcUd zs|ufUaNF^@I6glwqqknk;gE*d`d{B(T}k+QC8=>yq4CG5ulEE>^DPyBe{tbdYhR`X zNp2-Ye7@aX&2@857#KXg{CN(ifoHbMo0bZhz1!wqpFit<9KUMN>*+>9I(%U`RcRysJtMvg+fwonOkTXbIYIUO%)lKOn(%!|!I z4_%+Xa$r5Kl=tSUuYh~9a@1z`@rI~#8?ORywdr#x;%%ZCs)23(nXm*&`uMp4Iha)BY?e52W#i`~|F5(}Pr6#*qP)S{X=2B<+`|c-f*ZL0)s%Go z*nP+|NOcFt#4DG>qHGOrWG!w#aB-7=Y~<4iQRa#UJ0stCCC@(XE65YN=~?At^P5gu zcc)cs$h+sTeed!e`sNXae;qhiTxHv8dZ*t=jBRH6wWydC52eLGMC@Z##-3hENdLQ_IkTzn;Zbvn1NVb^(<{SDLpoVa`B zpTL>OV;uUYe@T6gy!LI4 zy?$)3-vWo7Rr)a(Kj>R9q(0#CGur3=mbvK=Zwt5ipM4yj2M)er5-4<3IQ7TK^+1FG z%lE}!mX(DFYOz(l-20^7-s36P(+3FxOh$JmGRyg$+Q{T}^ZYb9W#1_m4jIRrv6L+o zh~<&p@^8cMZLEoP^;z3*XJs=ll|B08xo7gktbHF!cz3;GdLHTYpyj|l^Z(-4c9^uA zEK%2((Y~SSlF+92r@3TG_g6}|-QO{P@?J$Z!?oA;Mt(aS(tSAW$xIKeo6?`&N!+`Z z_p&kP=t0}c9Y$HT8$PZ}W%3G%Jtz>iII^%TZ8g_v?zwA|R!iq*>}7cp@PSp%v%Bl= z6ONyjVflxath3mmD)s-_$)h*KjLi7DRhK_9nIzNS!obLXZJTqroGHsS$N!A>Y*XKC zte&t_X!ce8q>fEy&20jQ(${M$N(9eZ#KjKL^tofz@7Q;bj){A34a)fV@YK5C(hK4HqpWtQxPPjjV{q0wWW}>?C;nGFy!U@Z-@EXm zeR)~hwFO4&7xpZ6R(fc0;ofY&J&YIH>R2X-KIoLU5HY-Eb@%Y$b?+lo-1W`iaD$^a!feJs_Imyo!hpa-QMOea^xuE zCx~^!hua5l#O-8@ z=)CQ#w`b$leD}Ru7W|D%Fq*)&X{*W4do6o+JyvH3^$YmbCeAW-nnoDEz*%SF&~aluj3gM8n7Pde@|? z-WE~bm20ty(c|FTi5z?EBm~@4_1G$(XPT{Vjb9xT@+(dFiJ-~PR`W;OJ0lK1_@#3t z*jmSA>#We}OZeg%CL}wvu&*-LU-T&KVAJj+# z#ii-bh-aOU^>(U!*P+Jr%eVPoTner`N&VF!pTfiDi{*`YwF?c=L6OpjF)wv~fcX-T7nF#{PZw{t!dH>|7 zw;Yp%=aVFD9d%KrX5ST+uiVy2o^WkCaYlW5@ck*-=Iq(}QreP%fzRaC%d!*HEqmT2 zw8Sk>uz6dTbY;cka|ZT&QqFIrw78^u44FPQ_SN;cw{AS6di_aZ#>cB-h4E9ZCmCNB z>PdMU^C3wk`66fFi*;)p-Y-AG%@Or`)t`1DGkMcbF`M=sb(~qAr5@9^LM3#~yxZ#) z_SS^1fA@%oVP?Mf_gAbldWF{I{Ar4gJ8N_KCU3jKQ9C6?;}Ao|+4&kpz7HQWAIYkH zbB$HgWSdO0Vw2#4z46=pRxYhGuQ*(DVQTf=4T;w-)zoZaN(eoEzvYq0?G=hS*LJR& zKfTvtYJ*MR9b2)DM%l*-uf%k7YCjKcXMX!``SGV)4XX`yExxO3{qyP*N3r-hfyXMI zvtE{yTl#3py0AG74(aWND-L}0SU&Z7{dN8F+f@%lI2Qe0*15)PZzfC zYjtMt__#hOtNZ4(Ci!Jk<9RmTJ}MDdo&Dgu-t=jbhqU)zow{>rY@)R{O(y2)=w`Sy*SK8@%eEU_oFleL3q-cS;K{F2qmtJe! zIzg!GsQ>gfn>(e7y&suFQsx)fX?LH!bbZmuV1L;d1EH%&4z0cT?V;!?IlhALzarir zj$HJvO;Fx3XX)dECzMxo-#t1ld#lO+^|qfTDgIb_BlI)pjh28{f`0e5ZhaAcYtEaAk($ z-Ghf$7IF4mJ{`GHvgMg)@sUzqw`X}%?(EQNI=NuKB;U-{yLD3pV!xTM61=OhX0^H6 z+1Gb?6n%2fip^RUzigsaP6pquO&d6u*#+`!{rT%LPsst+l-uhI-W51JpZTG+cpbBg z@q6F>LHn=m?L4hNH+AxiS^d!|_jDv5^d#mjxW&JI-mF8KQ#x0t{PSg=;`vwWrp37f z&9gTBw2pSxUK8n)F6ZyT^Y++$12J{SP3~M-Rt$a(c`;e#>(D|hHyTs|>t!I{lULW}%Y=q``)3Y*({Bqd;j-admvPdL7a7rniG zecj=A-X*^4f0}G-Jt6-@e1bkpYR`pDn+hJ5cxu*kJ7smgb{Ce4Dc^Lr_jgcB?xDh~ zZxVRl%wuq#sC zt#O~d>flD*n$&$4x_i3?P30!2CjFH-yj<<2lV!0xTaQNLyB!Q$FRkKvJy-CW17E1q zvu{FInJ;Z!Z56B{8kBHj-O9o@2bCT7?^e8>)hsH;{w()e4ePIfe^FCBcJr_bJ9Nzo z@NwXN`0!BqQA9!@Ue`ntPHrxa;y?EJr6@%-Np zCwDwQbc^q=*1wiu;yY9=Uom?-nUilvUFO({l_V7iXxanq_xFwz3KYjW) z{RsY;SbnY2ne)-c+ZpX z%zVGZjgMo>d&FNfJl;_;e|d0I_}{Jyl?Td(8kgEQmiq1f_~G*Ae2*gyaf|Kxp3JIY z&2em=Id@hA$8GhTsHdgdzc1?j>*mLNSwHaZcgLps`*YSh9OE@VH(}9rmF3Khn=j6J zX`;IE|Lwa+RN|()2d!Bu@pM}E!F?NJw=0V@T@cvPuzRMs=;nYV(Rmv!3tSR^wB&Qh zYMs!z{dEponI;F*hD{c(!mkb{oJeAH(D-ohO)T3nyFVHS!aGz}biF7Po| zPMaGfYLxHSKg6@DDf)*7L#*2jUfqHm&?>G^Pvw6xyjsBdXWMky=*!bsKbjs%)K9x< z;JBsZi=x%y16Q-%cTS9RiOh6;?{}by^_H`&{=W@|uiXqL{b+Q^aQ2Q}ziX3s=^85r z@0lzGN=~eQ!?`w_1=LguJx+366ZbiF#h*1DOhHVJ*EyH*SA<6v&uE_zmKSwH^D67& zh^0Pfq%KZRxst)fRQLDMVirF43g^|v9KNaywLd29QrLRL+b46QMQUzUtO0}G-ERu5 zLT&0U(kE6;*?zYniR(_US7gBI*8IkKXI4sioV`5niiPp|`AS;V3A-Z=(9;X7jX9)`Mb_op?2(Q?fT;2FHtuXd}W>5d0(tM<1YGN`%Bt-wH*me zHViGAOy&VTiiCd0V_ihw8$sPgT#|N!7Q| zUTy1R@l5ITQ#Zr++JRysIhHrS`_?I%0^d#*#FNfX>Gj< zRu2{P{DW(@9oX;kKtI3b2=@jFE&1n<(3~vf(BN) zEVzE~!=&|^#*K}uJ-ZlYFSzd(X4kS|%$rJta-F)u2cHGNz=(dgBY4&>R+ppZ8 z-M=o3Qc(IDY@_X1zchDAYs8bFz6^oxt}~yCSluVeTVB{uxva2h{?Y@}PIKJG%}?7!iO6#F0`$h<)$%r zos@{ouC1@lzGYsFn96zh`gH9`1CP@Or^qcj6Kc5As`}_*@l^#8%T|h{9o{(GEw&`s zXp?@(>I!`o_cz-5^EQ=E=NL6~ywQ@=c<71AzjqzI6F)6!Z(oXD(xO3@H{+mBnI!><5 z2o@D>`P6o)=v9K#>-79=&xe~9-3ql1vaQ)_7$4yE%HP8MZdsVj{8_>6%sE#p<0_jX zzgJ!tD_(kG#;(st)Rp;7}M-dv>Z)XIctI!mv8*G31?f=YW4aurcQ2WVA;A$LeMtikV65- zg6qO5PlarzpYGXW_i)kAn5Z)~Y_^O=XQYx=x_`OA5gNZWr7T26`pBjWD{Wt$7TmGw z*6hHob*5qp-b?3boPM;b%gdAB=D9(!!<`-muDhpFU%MX>NDba@{f(la3qd zX?;8YTI`Be=%o1qhxpo^U-(u$TACd!y{?QYRy8a?)uxW!@4?lr$`iJ=h+8dC%H;{D zab-TckMq?Vh6u*nG7pp(kN;ro^Xz>xz4UZ1ACsk=gv@H)Lt?q%lXIOj?M&Bia65V@ z>2d_yf{vegD}}dIY5T8FKJ}qc;bp=7Q*pnN_AD{j`|TlXtLS}o`Gwit21}Fonmy2x zQhmB=;oV~i66YcW4u)4O`y70|UHIQ-t;j8Nx^6wYq%GxeN_ge5(~kNomu5^5uFJ9I zIM}nX)MBwjh}YW3-5jPWk3UU!n0m6bHqXL$t;eyeiydb%FM6};WzVDD=m}w2rth{q zd?p?7XGOsR9gi4G@58aI0?AgBn&v1^S)yM4qfPxxYRrm&#x;7Y{DY_7Vcz9aHSvdP z$)^NoJ&!wi=TpnQQy8{0KRxI_!HwslTLSx8yKTohVpnReW!KokAEbTIBk+TQS@E9> zfmbwq0)7Wb?_Sfk;%Sf6)nhTfTes$hc~;o3c3JzdbIY}}ee0qQMVxte@yqHHdbaAe zOX7mgJe-ro&@H=4X{t5T%)Q}fA2|Hp)Xb9U8TDS5$xDX)=AuW8+k+hJ8{XV|`2N@% zuO$W#VrNKPITz8cX?BIFY_I*_`Xvo(9&Ff>mX;q}^D>*YDpJ%zM*s+SVy+k)GYL zaM7uM3jaUltv>F$$sj;3{AO=u@Pp|`SMEr2^^p=4tWIEfn&3K3#OLxNFaONBSs!`* zHtpIT;(sPU)_vpR8%<(m_j&}L39M`g;JKh`(EO?K%+0461z!6eRs=G2PdXf-@pwa* ze!t)LN4sC${kpsGr*qWt-8r1MDmWM2O;!Ez{GG^dAwkincgrsBN}IWM!S9x~gn}%O zuwQHTZpd4)saaQqbE5LknwW#9^cAP|TmNzL3@f=TaAxfzY1M$gt7_zrq|9;ey!m^} zI`L+OvQ(Qn?`H>Fl=8ofpZ4~}3|3Rl_3!)r`0I>6b4e8hO526-3O=85Oxt7H>zw+- zN|#EG=G1qW81B^7I>w@RwtYs`iK7xvKYA@;PyEJ{I+crMJLAe5FBb7Mxmvw={p0DS zrf=I%`_|d5OxpTzy~9yn&8yS9zul}*PF+|r?`VYaB(C*ox5MRY6eGkwb+VoMB{65l zQT4R}>y7qwS3lBNu22@XGIqn2I}@DT0|GB|9*>ApKUdMT;zkPBpOe3HO{JDCnB(>D zg3tNxmg83<_2R-4Tpc_cIS&hYC7yFAZ4N5_Q>M==w=`w5=GwLoyIxIbNnN{x>-Lvd zk}uc2F{sKm)suE9nk=fwn=nnrR7clQKH%Pov@JZ7&R&{OxH4ebp<7&zFCL}^znkIS z|Mt^jRmaD(UF14G_{Df$TyFKkfA5@GN3N=DZ8wO4k|Ft#`~XZC`x!&BCdBZ?p*exivM}T+`R=L`oH+MNbAp(;bYGmkw0L2qQ~U15$*q2wGsSM4Uw{5hX@@Z5-qj85 zD-SH@*}S~{=Bg$0KJC*xBog{6Qf$*2S!KSB2L$BPEi?oZw_Grlt%>Y9C|YWD@Sy-t zldr`w7G2}^pR8Yhe>!|Im1Xx%>j&4JN@9FD)*in5GXA?)n(#3(-Q`uwQa#?Boo#yQ zgt>(=w}C^z#rr?=7zV4~7+-iK;B4XPljwqg0 za@L&2xo`*fac#C0>%umC@-SS*V{MxA*;B&wQBUfO4sn~!=jOEJoCYBl4(KMJ$L54@f9YUS6+osm1Y>@xVeqgCPIw$x?eXCe)28k$$mTKB|Y{nF(} zT6!K{k&C->A@pZqRE*N?wxlm}DtB%P_!wjKhSlZii(8_PE{pbDlZk5X_*}~~dG)g| zV!yZ<_g`UBY}9hy{~?rjNoz?|F_UBngGQc=3v9@4|M=*%E8k zUyTh}Be`{(ok(lI8p|l-!nO&!ixzaxKkWFp{lE^}jEff&QrLv%A5EK7I*E%l9(Q#1%-92Z_Ul*duAFU9@GAB@k5l#Km0XuE{M@oj&SqLg zw0NNI!x4eL@X;;i1Kj{NUpTl{p@v@c%H)5o(;DuB&@}X zUv4Y9|AO(fdw)^5SldL4b?5f&^Ia`ak(xL8+=~b6P8A)tFxA|;g$Da3lORUy9 zEjSi;L{+#><@1D1hc*T1=uT7$YDiQvy4#n#G~rcuV6dS5l!%L=Gxb(63yJrn)f`|= z`?O4~Aft%Qu_>Z8n$2%{`H%LA7w&f%Wq42NIKg54l=0@|_XUZElq;GGEIte6hm=*6 z>9K6H=vsDcjZ~AEcGpbibem5f+vR3o(vQEqvVY^{{fFk>k+waPhXsA})R=N1URi7i*s})hl6i=uLg{LE%D;51%&Y#2$;Q zExg69dMhMiCtNYeux<#yVdA0dx2a^~h3NfZ`nC6Nompg?r|fXMx&5V8+m=Yyo$mf8 zKbIBH>^9(f&}6V&BMXpoAjq2Fy`)Su5+KUL3HuFYd2cve!8%$GlNm+mqFc| z9~&02Tnhd0_1meK%80v?UQ=R@+cTIjHyxCV*uCGZ_`(~vjcqGa4rPBod%{4Lul-r3 zjoyX}RvsqSz6Z@6*)~kMwm{)jNNM4+tB+Rtz1Mm6iT`cx&EvNMG|JW(6uo@6;7=2|V=pM4WH<+C~JMY(>M;i|q_J4$<-gyHK4 z$C8ArA|`B@C3547<4ePs5E&<*1XbZ9uhw&1J>H(kdfw*6500FPItSJ@2u=)$UX}0h zW9kIMm1;3_(w<+^GH>O4^ern_Mf20L!lZDy-qyz-Ln3Xmy{DQ7#`_y?T>7^2`T2~l zk4!73#7}tN%E#-*8Ga$(_y3*Q8s1m$?`u=JzN}R|q4AZI>?;%PbJw02z07h;4Da}N z#awG;qG~|??ed z=~r(o^g9@ntZ7#5XOr-I`%H;MzEF?qFYHmq-#;naXubMzZuZ0GpYnH&a^0_Yhuk;V zBGx*O_sVYn9%+{!Z)f`}AM{@OPxY07>Ymcfs-I2)j0YMDk3XJ1`NE}b=6Qhv89U8l zmhM{l;88}0(VUk;@3KUaUokJ+aR2g;&Bgh5UQe0+ZO8rT?4}IU9=<8K+4d;rV?!Nl z(=v%OHbw?Y$Fz5;f1S&6)I?v?;L?JXDJO*c9Syr&!d;45ojF#i<^}!^_t+c1^!Y|Z zS^iJ8jw@yEs;twRz|8nrS;dSg+&!eaGGW&|8Tl_-Y3icAI(75XQ(Q_OTkj}qi4M6^ zs{TP;VDpPt4;$11R?iMj+j>Lba%iu~;+pEOk5_RDcgws=ZZ`kTY+Gl$Nut4tQ}&?46soQtGsWChKYYxBc&kDO#t)-vkNjxd#{5#-{D~>Egolga7JYfyL^sdNZgFAx z9o>u$kK09O9t>#_T;3M2O8KY z8TnbV=f{ED=S>af&(eE#=pbX%Z2wH{6#`*T0vAVARO)Y$-p;0}(PW!?l-zl^&mtWQ-%()@>%)+lwC^~Da)2dzb zbY&`MPL$BOGw+#H9q*r2U#DF*bJpPD>c3ys_{-?%io+VO+BPY>d0cRp5ENp$!KZ(B zQqtPhKc#lPuy##&Vz=Z(YM`PtLdPeLv?-c%Oeqp|x}V{RQ{;`Bki3 zcqHR7hig>yY0Y)=o7G>Gr}?DopYz`FQfU9`$i-6smu$cM?%OT#>+{YpMLGmefPDXjcwuQjgD`g$42a%x96+$gIFJJHSRsXL?Tjgg^kR9bDJ7DF$K#JlI=w%a$Ae|p^=y0LOH z^Xz#yd)K7@O7dIZJk{JXM9lBJokY*OsiwtQ|Bk+OG;rD9Y+X=vqvGX3@2e*7&em-B zd)GPJ!;$Mw_sXU0Q|$IQ%;bDEh374|iuP~Cy?Gw%-ey%ahzm_WVR=OSh`f(LvVhqX z(Y%!4=T{lD4#>FuX-PYj_@Y$p?;3MM9hu*$r3+FSqx3vy<#6?hd{E~RbU9M;_372i z=g#VHG0{Jf!W9|zN8Fg>*pwR$oku6Qg`Un@eZBMi1de$-UdFzh8gSL)@7u;Ot^cje z@o7z;HkuVr`|^!7YEtCXds-qoof8i5Ovt=-h|`Wu>gr#X^lLKtLg&w|e`#NK@j8p2 zo|O4#EBo|)8_ucumg&Aw+O^k=Z{Kv;<*ke|fniyjcEnvhH*Mj8#AR&ip_{)4J>$;c z2>exWtu=2I=lZ)x!m15wmcDUQ6n*Wfv10uNQT2N#)I|H#zOOYpb$<#={>fNX|VW_w^lb zcGNAf(beL+nZp~d&akj0u|F@-o25lH^J!_!EN7{pqG~x0x7Vzb4T8V{9c8~ib7Vc692bIy_%%^&yh|2xkllKjAT)w-_rM?-@*^Ok zCzrD!^Lhc9Rm>Z0Mczrit}d13zrA@vg?*?y7o|gMw8vc9Sj?m_?h=??@c(${3&>) zVV(HNz*Ucf8T?IOx*p<^Q*mF{V*GWDi|w{72|2x{KQA5}wQn9dO>GZH%B_7dD+W&oTO>O?*5SS2`AIGhA#PaNcpN;dDWbIw0$xy@g1)O?mgnD`D}zE^7BrnoOTw5fYP--H&g^<};L zPgU9+p1C*NBJgxl(;vnQO<%(#4=7hfN&HOnjGwkj=XQuY$L_b;6}drM&W z$?Ja^q@Eq+GK!T_t6_cRd?)<}L$%qa;yV>QsypZISnWAk?(_HV&kT#BZif6Q{w=+V z*Uf9WoapN}WzyPQnjz=qdR8?i^u45# zUQEO)g$}`jMVqgzTgvdVL}$(Yn6G~#?(rHdztV6#T2An(*5+4jNk^-z1-~XGbRV!a z`OdXAe8HjFk=jh#YHd$%d~B=ZDj;5x*VtoZ#h6#)bJg5TY2%d}FTy+uHbu(K<1)9d zPE33z_+fE!QFlmsO^3q86+J)hDs(>-EczomTZ@f5K1cJ{8bRMDX2z9^n^sN{4SYH^ zDsPX&agO7`GR-$Wd1+kUv@NjYQ^^f|4QBR+%T7CkEI&>8aPaS;4`!|G&sqI*i;9o> zRy)nV<)*%r&1q>~vE}}TdBNxQRVn`1759*rW9b&DLIJ^xla?8#DBlr~l6EMWxWaRP zcU<|jCXKI~R?Il1P;xl%0Q+Av(QNHDjd)X+Q#Bj>_HJ0vp7K~L@3gAU!mDR}qg!2$ zne14$^3kexkA9x1byv_8GRye2x`n0MnwNO91l_1w`#*$K808cPjBZxsXUj0mci< zo}XpXdlC}eeY+~3|HBj$XEJiMpEWZH(pH& zTkD_T^V&_@G^m47DOZHKBFaZ9=igb`hqpN@ds3$QMLdsNR>x6zCiY7rlbL?tDdR1X zerXRBZ+Zz=D($`?rT*#n;Wwqq{~b>^Tnb@0HTTH$b=fD(6)#8eBnwwq{Vim(*AibE z_~5|1Z})1KJ#&%}q z1;LHs$0bbWIAqjR6f9jaLn7*H0aw4FNc77Ycg2KXOpoRX3wnAaYVGsb!;JZ7h5TDv zo1RX&v3%9NQ1`SGGZ~w{Zk1q>w^rUB?PS1I#KalFTkNz&qG6U)6z{|Aqnq!aTfTXX zTX(|SR_W)eo12$CeQ5Cd@AKk(Ad>l z)|XfQn0msDKY}MTPrsNq%yj?GZ4a&+u@R?VEXFTSHB-F zdG6LDlQHAQ8|O9aZI@ZsiytzX&GhrC$l-#X=k@biK81Vamp4T|_PV-xA{)p6J-*AAik1G%jYf6^81XuGre~U1Uy+2Uf1`b z@l%%WKLP3AhUJX@y=QuQGAH%BoH_h{wcyIzuF}r0^%rth+RX5)MKk`Ug9to&IJR_IBmhuT9BIaH~JGb)9*Ddw$nrw!S+j z9&eiy@h^S#QSL;~#8Ykyz)@OToGiovm1S^itUH6$$TBIPUb6xNHJH}h&4EF}@ znBm*e{pdY+#jZEIcP3WWUl!fgq_?GVj=`Sn`RfiTuF${w*zcK$^6sB=_|FtGe^4^& zxe+ekcIcMsHD}o`Lh0{2He2ZK*>RBL`bTEdr@<2X_vXECzj1p~yz7oldlPm^>`waU zx16KV%8*-SO2IX^HH#x0G<~kjJ{k~^BB6eIUskL}4D){$pZ&hk=?2$7R^4|JI9;3D zR-U36H&6KB-D7W+jN77@1@949XyVru^X&ll)}ImB+E4 zP75P_Z*70`Jon;6A)%W0KffuzlKq!==HC*(-pIf9<~H7hO{PD;ESX zS~-_hOLdrLz0VN8uF4u^F@4vn%b}aYa!yZ=+jQ@4A^hCcRZ z5@>Sxu=e?sfC(#gwq9Q;o3VIPv26VFNes1K)=D4bzZ<*Wz2mTU;nzD8xK=N%X?@;q zXxIMp+au=J&rVe)HpPm(TjrYcsc*?WCb4yD#`A6Y^!aV?PVj$x=WYDn3pGW(p&Cvr zFP^?^yJ^qt;7cbLb(uczzI{A+W=>Y<8Z%~1LD#20%RVwY^XW`mz2U}{bPu1N!`#Io zOB2GjG&r(flF|*C%G3*_J?QtDmRp3j1c<*BAWOQTM&TDlZ~$Rm76b2|4?Un~r41 zX z9U__2K1NA-&HEVr@N(RZUH07bW7@hF1mEVa7wQQpc+c^0MUugyZ@M>E8|}9Gm04i5 zb~k5;P4L7(bE8dFPBXkR*v>jsc}``}`Kc$It{j}`*n48CjlD)q?|sPw-$R>z|N0Tm zqW&T5^H-T^*H}%$e>_|=CwyLz^_S;o1MSN1_AI{adQd3(?dEg7QVgq3UGJ}ns(qq$ zs;EOXcv|N2it}Z!GqhNuq>9*OrR?2yUD)GS)$!%)pQd*OC1-Zc*k$9eW7XSJi>1OD z`lq?KPCZd7TYG9%>-34Q^2|77K3{HMcw~|O&+`|w7|pWR3x&u&&*u|7Db*ynu{+wE z{Dr^P#YmRyOBmz5K?V2SvD|K5fb787^?JHRw& literal 0 HcmV?d00001 diff --git a/tests/data/zip_ppmd.bin b/tests/data/zip_ppmd.bin new file mode 100644 index 0000000000000000000000000000000000000000..73cfcd8c6c7e7bc338b0ad61074050c580ea4569 GIT binary patch literal 38627 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 av}ENN|5 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 QnQ~|_`89B`u<$Yf0P&8P(EtDd literal 0 HcmV?d00001 diff --git a/tests/zip/zip.cpp b/tests/zip/zip.cpp new file mode 100644 index 0000000..ec9d4b4 --- /dev/null +++ b/tests/zip/zip.cpp @@ -0,0 +1,193 @@ +/* + * 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 +#include + +#include "../../library.h" +#include "../crc32.h" +#include "gtest/gtest.h" + +/* alice29.txt: 152089 bytes, CRC32 = 0x66007dba */ +#define EXPECTED_CRC32 0x66007dba +#define EXPECTED_OUTPUT_SIZE 152089 + +/* ZIP Shrink test: PKZIP1 ES.ZIP (method 1) */ +#define SHRINK_COMPRESSED_SIZE 65014 + +static const uint8_t *shrink_buffer; + +class ZipShrinkFixture : public ::testing::Test +{ +protected: + void SetUp() + { + char path[PATH_MAX]; + char filename[PATH_MAX]; + getcwd(path, PATH_MAX); + snprintf(filename, PATH_MAX, "%s/data/zip_shrink.bin", path); + + FILE *file = fopen(filename, "rb"); + shrink_buffer = (const uint8_t *)malloc(SHRINK_COMPRESSED_SIZE); + fread((void *)shrink_buffer, 1, SHRINK_COMPRESSED_SIZE, file); + fclose(file); + } + + void TearDown() { free((void *)shrink_buffer); } +}; + +TEST_F(ZipShrinkFixture, ZipShrink) +{ + size_t destLen = EXPECTED_OUTPUT_SIZE; + auto *outBuf = (uint8_t *)malloc(EXPECTED_OUTPUT_SIZE); + + auto err = AARU_zip_shrink_decode_buffer(outBuf, &destLen, shrink_buffer, SHRINK_COMPRESSED_SIZE); + + EXPECT_EQ(err, 0); + EXPECT_EQ(destLen, EXPECTED_OUTPUT_SIZE); + + auto crc = crc32_data(outBuf, EXPECTED_OUTPUT_SIZE); + free(outBuf); + + EXPECT_EQ(crc, EXPECTED_CRC32); +} + +/* ZIP Implode test: PKZIP1 EI.ZIP (method 6, flags=0x0006: 8K dict + literals) */ +#define IMPLODE_COMPRESSED_SIZE 60488 + +static const uint8_t *implode_buffer; + +class ZipImplodeFixture : public ::testing::Test +{ +protected: + void SetUp() + { + char path[PATH_MAX]; + char filename[PATH_MAX]; + getcwd(path, PATH_MAX); + snprintf(filename, PATH_MAX, "%s/data/zip_implode.bin", path); + + FILE *file = fopen(filename, "rb"); + implode_buffer = (const uint8_t *)malloc(IMPLODE_COMPRESSED_SIZE); + fread((void *)implode_buffer, 1, IMPLODE_COMPRESSED_SIZE, file); + fclose(file); + } + + void TearDown() { free((void *)implode_buffer); } +}; + +TEST_F(ZipImplodeFixture, ZipImplode) +{ + size_t destLen = EXPECTED_OUTPUT_SIZE; + auto *outBuf = (uint8_t *)malloc(EXPECTED_OUTPUT_SIZE); + + /* large_dictionary=1 (8K), has_literals=1 (3 trees) from flags 0x0006 */ + auto err = AARU_zip_implode_decode_buffer(outBuf, &destLen, implode_buffer, IMPLODE_COMPRESSED_SIZE, 1, 1); + + EXPECT_EQ(err, 0); + EXPECT_EQ(destLen, EXPECTED_OUTPUT_SIZE); + + auto crc = crc32_data(outBuf, EXPECTED_OUTPUT_SIZE); + free(outBuf); + + EXPECT_EQ(crc, EXPECTED_CRC32); +} + +/* ZIP Deflate64 test: 7-Zip DEFLATE64 1FILE.ZIP (method 9) */ +#define DEFLATE64_COMPRESSED_SIZE 50564 + +static const uint8_t *deflate64_buffer; + +class ZipDeflate64Fixture : public ::testing::Test +{ +protected: + void SetUp() + { + char path[PATH_MAX]; + char filename[PATH_MAX]; + getcwd(path, PATH_MAX); + snprintf(filename, PATH_MAX, "%s/data/zip_deflate64.bin", path); + + FILE *file = fopen(filename, "rb"); + deflate64_buffer = (const uint8_t *)malloc(DEFLATE64_COMPRESSED_SIZE); + fread((void *)deflate64_buffer, 1, DEFLATE64_COMPRESSED_SIZE, file); + fclose(file); + } + + void TearDown() { free((void *)deflate64_buffer); } +}; + +TEST_F(ZipDeflate64Fixture, ZipDeflate64) +{ + size_t destLen = EXPECTED_OUTPUT_SIZE; + auto *outBuf = (uint8_t *)malloc(EXPECTED_OUTPUT_SIZE); + + auto err = AARU_zip_deflate64_decode_buffer(outBuf, &destLen, deflate64_buffer, DEFLATE64_COMPRESSED_SIZE); + + EXPECT_EQ(err, 0); + EXPECT_EQ(destLen, EXPECTED_OUTPUT_SIZE); + + auto crc = crc32_data(outBuf, EXPECTED_OUTPUT_SIZE); + free(outBuf); + + EXPECT_EQ(crc, EXPECTED_CRC32); +} + +/* ZIP PPMd test: 7-Zip PPMd 1FILE.ZIP (method 98, variant I) */ +/* Parameters from info word 0x0037: maxorder=8, suballocsize=4MB, restoration=0 */ +#define PPMD_COMPRESSED_SIZE 38627 + +static const uint8_t *ppmd_buffer; + +class ZipPPMdFixture : public ::testing::Test +{ +protected: + void SetUp() + { + char path[PATH_MAX]; + char filename[PATH_MAX]; + getcwd(path, PATH_MAX); + snprintf(filename, PATH_MAX, "%s/data/zip_ppmd.bin", path); + + FILE *file = fopen(filename, "rb"); + ppmd_buffer = (const uint8_t *)malloc(PPMD_COMPRESSED_SIZE); + fread((void *)ppmd_buffer, 1, PPMD_COMPRESSED_SIZE, file); + fclose(file); + } + + void TearDown() { free((void *)ppmd_buffer); } +}; + +TEST_F(ZipPPMdFixture, ZipPPMd) +{ + auto *outBuf = (uint8_t *)malloc(EXPECTED_OUTPUT_SIZE); + + /* maxorder=8, suballocsize=4194304 (4MB), restoration=0 (restart) */ + auto err = AARU_zip_ppmd_decode_buffer(outBuf, EXPECTED_OUTPUT_SIZE, ppmd_buffer, PPMD_COMPRESSED_SIZE, 8, + 4194304, 0); + + EXPECT_EQ(err, 0); + + auto crc = crc32_data(outBuf, EXPECTED_OUTPUT_SIZE); + free(outBuf); + + EXPECT_EQ(crc, EXPECTED_CRC32); +} diff --git a/wavpack/common_utils.c b/wavpack/common_utils.c new file mode 100644 index 0000000..4e432f5 --- /dev/null +++ b/wavpack/common_utils.c @@ -0,0 +1,713 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// common_utils.c + +// This module provides a lot of the trivial WavPack API functions and several +// functions that are common to both reading and writing WavPack files (like +// WavpackCloseFile()). Functions here are restricted to those that have few +// external dependancies and this is done so that applications that statically +// link to the WavPack library (like the command-line utilities on Windows) +// do not need to include the entire library image if they only use a subset +// of it. This module will be loaded for ANY WavPack application. + +#include +#include +#include + +#include "wavpack_local.h" + +#ifndef LIBWAVPACK_VERSION_STRING +#include "wavpack_version.h" +#endif + +///////////////////////////// local table storage //////////////////////////// + +const uint32_t sample_rates[] = {6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, + 32000, 44100, 48000, 64000, 88200, 96000, 192000}; + +///////////////////////////// executable code //////////////////////////////// + +// This function obtains general information about an open input file and +// returns a mask with the following bit values: + +// MODE_WVC: a .wvc file has been found and will be used for lossless +// MODE_LOSSLESS: file is lossless (either pure or hybrid) +// MODE_HYBRID: file is hybrid mode (either lossy or lossless) +// MODE_FLOAT: audio data is 32-bit ieee floating point +// MODE_VALID_TAG: file conatins a valid ID3v1 or APEv2 tag +// MODE_HIGH: file was created in "high" mode (information only) +// MODE_FAST: file was created in "fast" mode (information only) +// MODE_EXTRA: file was created using "extra" mode (information only) +// MODE_APETAG: file contains a valid APEv2 tag +// MODE_SFX: file was created as a "self-extracting" executable +// MODE_VERY_HIGH: file was created in the "very high" mode (or in +// the "high" mode prior to 4.4) +// MODE_MD5: file contains an MD5 checksum +// MODE_XMODE: level used for extra mode (1-6, 0=unknown) +// MODE_DNS: dynamic noise shaping + +int WavpackGetMode(WavpackContext *wpc) +{ + int mode = 0; + + if(wpc) + { + if(wpc->config.flags & CONFIG_HYBRID_FLAG) + mode |= MODE_HYBRID; + else if(!(wpc->config.flags & CONFIG_LOSSY_MODE)) + mode |= MODE_LOSSLESS; + + if(wpc->wvc_flag) mode |= (MODE_LOSSLESS | MODE_WVC); + + if(wpc->lossy_blocks) mode &= ~MODE_LOSSLESS; + + if(wpc->config.flags & CONFIG_FLOAT_DATA) mode |= MODE_FLOAT; + + if(wpc->config.flags & (CONFIG_HIGH_FLAG | CONFIG_VERY_HIGH_FLAG)) + { + mode |= MODE_HIGH; + + if((wpc->config.flags & CONFIG_VERY_HIGH_FLAG) || + (wpc->streams && wpc->streams[0] && wpc->streams[0]->wphdr.version < 0x405)) + mode |= MODE_VERY_HIGH; + } + + if(wpc->config.flags & CONFIG_FAST_FLAG) mode |= MODE_FAST; + + if(wpc->config.flags & CONFIG_EXTRA_MODE) mode |= (MODE_EXTRA | (wpc->config.xmode << 12)); + + if(wpc->config.flags & CONFIG_CREATE_EXE) mode |= MODE_SFX; + + if(wpc->config.flags & CONFIG_MD5_CHECKSUM) mode |= MODE_MD5; + + if((wpc->config.flags & CONFIG_HYBRID_FLAG) && (wpc->config.flags & CONFIG_DYNAMIC_SHAPING) && wpc->streams && + wpc->streams[0] && wpc->streams[0]->wphdr.version >= 0x407) + mode |= MODE_DNS; + +#ifndef NO_TAGS + if(valid_tag(&wpc->m_tag)) + { + mode |= MODE_VALID_TAG; + + if(valid_tag(&wpc->m_tag) == 'A') mode |= MODE_APETAG; + } +#endif + + mode |= (wpc->config.qmode << 16) & 0xFF0000; + } + + return mode; +} + +// This function obtains information about specific file features that were +// added for version 5.0, specifically qualifications added to support CAF +// and DSD files. Except for indicating the presence of DSD data, these +// bits are meant to simply indicate the format of the data in the original +// source file and do NOT indicate how the library will return the data to +// the appication (which is always the same). This means that in general an +// application that simply wants to play or process the audio data need not +// be concerned about these. If the file is DSD audio, then either of the +// QMDOE_DSD_LSB_FIRST or QMODE_DSD_MSB_FIRST bits will be set (but the +// DSD audio is always returned to the caller MSB first). + +// QMODE_BIG_ENDIAN 0x1 // big-endian data format (opposite of WAV format) +// QMODE_SIGNED_BYTES 0x2 // 8-bit audio data is signed (opposite of WAV format) +// QMODE_UNSIGNED_WORDS 0x4 // audio data (other than 8-bit) is unsigned (opposite of WAV format) +// QMODE_REORDERED_CHANS 0x8 // source channels were not Microsoft order, so they were reordered +// QMODE_DSD_LSB_FIRST 0x10 // DSD bytes, LSB first (most Sony .dsf files) +// QMODE_DSD_MSB_FIRST 0x20 // DSD bytes, MSB first (Philips .dff files) +// QMODE_DSD_IN_BLOCKS 0x40 // DSD data is blocked by channels (Sony .dsf only) + +int WavpackGetQualifyMode(WavpackContext *wpc) { return wpc->config.qmode & 0xFF; } + +// This function returns a pointer to a string describing the last error +// generated by WavPack. + +char *WavpackGetErrorMessage(WavpackContext *wpc) { return wpc->error_message; } + +// Get total number of samples contained in the WavPack file, or -1 if unknown + +uint32_t WavpackGetNumSamples(WavpackContext *wpc) { return (uint32_t)WavpackGetNumSamples64(wpc); } + +int64_t WavpackGetNumSamples64(WavpackContext *wpc) { return wpc ? wpc->total_samples : -1; } + +// Get the current sample index position, or -1 if unknown + +uint32_t WavpackGetSampleIndex(WavpackContext *wpc) { return (uint32_t)WavpackGetSampleIndex64(wpc); } + +int64_t WavpackGetSampleIndex64(WavpackContext *wpc) +{ + if(wpc) + { +#ifdef ENABLE_LEGACY + if(wpc->stream3) + return get_sample_index3(wpc); + else if(wpc->streams && wpc->streams[0]) + return wpc->streams[0]->sample_index; +#else + if(wpc->streams && wpc->streams[0]) return wpc->streams[0]->sample_index; +#endif + } + + return -1; +} + +// Get the number of errors encountered so far + +int WavpackGetNumErrors(WavpackContext *wpc) { return wpc ? wpc->crc_errors : 0; } + +// return TRUE if any uncorrected lossy blocks were actually written or read + +int WavpackLossyBlocks(WavpackContext *wpc) { return wpc ? wpc->lossy_blocks : 0; } + +// Calculate the progress through the file as a double from 0.0 (for begin) +// to 1.0 (for done). A return value of -1.0 indicates that the progress is +// unknown. + +double WavpackGetProgress(WavpackContext *wpc) +{ + if(wpc && wpc->total_samples != -1 && wpc->total_samples != 0) + return (double)WavpackGetSampleIndex64(wpc) / wpc->total_samples; + else + return -1.0; +} + +// Return the total size of the WavPack file(s) in bytes. + +uint32_t WavpackGetFileSize(WavpackContext *wpc) { return (uint32_t)(wpc ? wpc->filelen + wpc->file2len : 0); } + +int64_t WavpackGetFileSize64(WavpackContext *wpc) { return wpc ? wpc->filelen + wpc->file2len : 0; } + +// Calculate the ratio of the specified WavPack file size to the size of the +// original audio data as a double greater than 0.0 and (usually) smaller than +// 1.0. A value greater than 1.0 represents "negative" compression and a +// return value of 0.0 indicates that the ratio cannot be determined. + +double WavpackGetRatio(WavpackContext *wpc) +{ + if(wpc && wpc->total_samples != -1 && wpc->filelen) + { + double output_size = (double)wpc->total_samples * wpc->config.num_channels * wpc->config.bytes_per_sample; + double input_size = (double)wpc->filelen + wpc->file2len; + + if(output_size >= 1.0 && input_size >= 1.0) return input_size / output_size; + } + + return 0.0; +} + +// Calculate the average bitrate of the WavPack file in bits per second. A +// return of 0.0 indicates that the bitrate cannot be determined. An option is +// provided to use (or not use) any attendant .wvc file. + +double WavpackGetAverageBitrate(WavpackContext *wpc, int count_wvc) +{ + if(wpc && wpc->total_samples != -1 && wpc->filelen) + { + double output_time = (double)wpc->total_samples / WavpackGetSampleRate(wpc); + double input_size = (double)wpc->filelen + (count_wvc ? wpc->file2len : 0); + + if(output_time >= 0.1 && input_size >= 1.0) return input_size * 8.0 / output_time; + } + + return 0.0; +} + +// Calculate the bitrate of the current WavPack file block in bits per second. +// This can be used for an "instant" bit display and gets updated from about +// 1 to 4 times per second. A return of 0.0 indicates that the bitrate cannot +// be determined. + +double WavpackGetInstantBitrate(WavpackContext *wpc) +{ + if(wpc && wpc->stream3) return WavpackGetAverageBitrate(wpc, TRUE); + + if(wpc && wpc->streams && wpc->streams[0] && wpc->streams[0]->wphdr.block_samples) + { + double output_time = (double)wpc->streams[0]->wphdr.block_samples / WavpackGetSampleRate(wpc); + double input_size = 0; + int si; + + for(si = 0; si < wpc->num_streams; ++si) + { + if(wpc->streams[si]->blockbuff) input_size += ((WavpackHeader *)wpc->streams[si]->blockbuff)->ckSize; + + if(wpc->streams[si]->block2buff) input_size += ((WavpackHeader *)wpc->streams[si]->block2buff)->ckSize; + } + + if(output_time > 0.0 && input_size >= 1.0) return input_size * 8.0 / output_time; + } + + return 0.0; +} + +// This function allows retrieving the Core Audio File channel layout, many of which do not +// conform to the Microsoft ordering standard that WavPack requires internally (at least for +// those channels present in the "channel mask"). In addition to the layout tag, this function +// returns the reordering string (if stored in the file) to allow the unpacker to reorder the +// channels back to the specified layout (if it wants to restore the CAF order). The number of +// channels in the layout is determined from the lower nybble of the layout word (and should +// probably match the number of channels in the file), and if a reorder string is requested +// then that much space must be allocated. Note that all the reordering is actually done +// outside of this library, and that if reordering is done then the appropriate qmode bit +// will be set. +// +// Note: Normally this function would not be used by an application unless it specifically +// wanted to restore a non-standard channel order (to check an MD5, for example) or obtain +// the Core Audio channel layout ID. For simple file decoding for playback, the channel_mask +// should provide all the information required unless there are non-Microsoft channels +// involved, in which case WavpackGetChannelIdentities() will provide the identities of +// the other channels (if they are known). + +uint32_t WavpackGetChannelLayout(WavpackContext *wpc, unsigned char *reorder) +{ + if((wpc->channel_layout & 0xff) && wpc->channel_reordering && reorder) + memcpy(reorder, wpc->channel_reordering, wpc->channel_layout & 0xff); + + return wpc->channel_layout; +} + +// This function provides the identities of ALL the channels in the file, including the +// standard Microsoft channels (which come first, in order, and are numbered 1-18) and also +// any non-Microsoft channels (which can be in any order and have values from 33-254). The +// value 0x00 is invalid and 0xFF indicates an "unknown" or "unnassigned" channel. The +// string is NULL terminated so the caller must supply enough space for the number +// of channels indicated by WavpackGetNumChannels(), plus one. +// +// Note that this function returns the actual order of the channels in the Wavpack file +// (i.e., the order returned by WavpackUnpackSamples()). If the file includes a "reordering" +// string because the source file was not in Microsoft order that is NOT taken into account +// here and really only needs to be considered if doing an MD5 verification or if it's +// required to restore the original order/file (like wvunpack does). + +void WavpackGetChannelIdentities(WavpackContext *wpc, unsigned char *identities) +{ + int num_channels = wpc->config.num_channels, index = 1; + uint32_t channel_mask = wpc->config.channel_mask; + unsigned char *src = wpc->channel_identities; + + while(num_channels--) + { + if(channel_mask) + { + while(!(channel_mask & 1)) + { + channel_mask >>= 1; + index++; + } + + *identities++ = index++; + channel_mask >>= 1; + } + else if(src && *src) + *identities++ = *src++; + else + *identities++ = 0xff; + } + + *identities = 0; +} + +// Close the specified WavPack file and release all resources used by it. +// Returns NULL. + +WavpackContext *WavpackCloseFile(WavpackContext *wpc) +{ + if(wpc->streams) + { + free_streams(wpc); + + if(wpc->streams[0]) free(wpc->streams[0]); + + free(wpc->streams); + } + +#ifdef ENABLE_LEGACY + if(wpc->stream3) free_stream3(wpc); +#endif + + if(wpc->reader && wpc->reader->close && wpc->wv_in) wpc->reader->close(wpc->wv_in); + + if(wpc->reader && wpc->reader->close && wpc->wvc_in) wpc->reader->close(wpc->wvc_in); + + WavpackFreeWrapper(wpc); + + if(wpc->channel_reordering) free(wpc->channel_reordering); + +#ifndef NO_TAGS + free_tag(&wpc->m_tag); +#endif + +#ifdef ENABLE_DSD + if(wpc->decimation_context) decimate_dsd_destroy(wpc->decimation_context); +#endif + + free(wpc); + + return NULL; +} + +// These routines are used to access (and free) header and trailer data that +// was retrieved from the Wavpack file. The header will be available before +// the samples are decoded and the trailer will be available after all samples +// have been read. + +uint32_t WavpackGetWrapperBytes(WavpackContext *wpc) { return wpc ? wpc->wrapper_bytes : 0; } + +unsigned char *WavpackGetWrapperData(WavpackContext *wpc) { return wpc ? wpc->wrapper_data : NULL; } + +void WavpackFreeWrapper(WavpackContext *wpc) +{ + if(wpc && wpc->wrapper_data) + { + free(wpc->wrapper_data); + wpc->wrapper_data = NULL; + wpc->wrapper_bytes = 0; + } +} + +// Returns the sample rate of the specified WavPack file + +uint32_t WavpackGetSampleRate(WavpackContext *wpc) +{ + return wpc ? (wpc->dsd_multiplier ? wpc->config.sample_rate * wpc->dsd_multiplier : wpc->config.sample_rate) + : 44100; +} + +// Returns the native sample rate of the specified WavPack file +// (provides the native rate for DSD files rather than the "byte" rate that's used for +// seeking, duration, etc. and would generally be used just for user facing reports) + +uint32_t WavpackGetNativeSampleRate(WavpackContext *wpc) +{ + return wpc ? (wpc->dsd_multiplier ? wpc->config.sample_rate * wpc->dsd_multiplier * 8 : wpc->config.sample_rate) + : 44100; +} + +// Returns the number of channels of the specified WavPack file. Note that +// this is the actual number of channels contained in the file even if the +// OPEN_2CH_MAX flag was specified when the file was opened. + +int WavpackGetNumChannels(WavpackContext *wpc) { return wpc ? wpc->config.num_channels : 2; } + +// Returns the standard Microsoft channel mask for the specified WavPack +// file. A value of zero indicates that there is no speaker assignment +// information. + +int WavpackGetChannelMask(WavpackContext *wpc) { return wpc ? wpc->config.channel_mask : 0; } + +// Return the normalization value for floating point data (valid only +// if floating point data is present). A value of 127 indicates that +// the floating point range is +/- 1.0. Higher values indicate a +// larger floating point range. + +int WavpackGetFloatNormExp(WavpackContext *wpc) { return wpc->config.float_norm_exp; } + +// Returns the actual number of valid bits per sample contained in the +// original file, which may or may not be a multiple of 8. Floating data +// always has 32 bits, integers may be from 1 to 32 bits each. When this +// value is not a multiple of 8, then the "extra" bits are located in the +// LSBs of the results. That is, values are right justified when unpacked +// into ints, but are left justified in the number of bytes used by the +// original data. + +int WavpackGetBitsPerSample(WavpackContext *wpc) { return wpc ? wpc->config.bits_per_sample : 16; } + +// Returns the number of bytes used for each sample (1 to 4) in the original +// file. This is required information for the user of this module because the +// audio data is returned in the LOWER bytes of the long buffer and must be +// left-shifted 8, 16, or 24 bits if normalized longs are required. + +int WavpackGetBytesPerSample(WavpackContext *wpc) { return wpc ? wpc->config.bytes_per_sample : 2; } + +// If the OPEN_2CH_MAX flag is specified when opening the file, this function +// will return the actual number of channels decoded from the file (which may +// or may not be less than the actual number of channels, but will always be +// 1 or 2). Normally, this will be the front left and right channels of a +// multichannel file. + +int WavpackGetReducedChannels(WavpackContext *wpc) +{ + if(wpc) + return wpc->reduced_channels ? wpc->reduced_channels : wpc->config.num_channels; + else + return 2; +} + +// Free all memory allocated for raw WavPack blocks (for all allocated streams) +// and free all additonal streams. This does not free the default stream ([0]) +// which is always kept around. + +void free_streams(WavpackContext *wpc) +{ + int si = wpc->num_streams; + + while(si--) + { + if(wpc->streams[si]->blockbuff) + { + free(wpc->streams[si]->blockbuff); + wpc->streams[si]->blockbuff = NULL; + } + + if(wpc->streams[si]->block2buff) + { + free(wpc->streams[si]->block2buff); + wpc->streams[si]->block2buff = NULL; + } + + if(wpc->streams[si]->sample_buffer) + { + free(wpc->streams[si]->sample_buffer); + wpc->streams[si]->sample_buffer = NULL; + } + + if(wpc->streams[si]->dc.shaping_data) + { + free(wpc->streams[si]->dc.shaping_data); + wpc->streams[si]->dc.shaping_data = NULL; + } + +#ifdef ENABLE_DSD + if(wpc->streams[si]->dsd.probabilities) + { + free(wpc->streams[si]->dsd.probabilities); + wpc->streams[si]->dsd.probabilities = NULL; + } + + if(wpc->streams[si]->dsd.summed_probabilities) + { + free(wpc->streams[si]->dsd.summed_probabilities); + wpc->streams[si]->dsd.summed_probabilities = NULL; + } + + if(wpc->streams[si]->dsd.value_lookup) + { + int i; + + for(i = 0; i < wpc->streams[si]->dsd.history_bins; ++i) + if(wpc->streams[si]->dsd.value_lookup[i]) free(wpc->streams[si]->dsd.value_lookup[i]); + + free(wpc->streams[si]->dsd.value_lookup); + wpc->streams[si]->dsd.value_lookup = NULL; + } + + if(wpc->streams[si]->dsd.ptable) + { + free(wpc->streams[si]->dsd.ptable); + wpc->streams[si]->dsd.ptable = NULL; + } +#endif + + if(si) + { + wpc->num_streams--; + free(wpc->streams[si]); + wpc->streams[si] = NULL; + } + } + + wpc->current_stream = 0; +} + +void WavpackFloatNormalize(int32_t *values, int32_t num_values, int delta_exp) +{ + f32 *fvalues = (f32 *)values; + int exp; + + if(!delta_exp) return; + + while(num_values--) + { + if((exp = get_exponent(*fvalues)) == 0 || exp + delta_exp <= 0) + *fvalues = 0; + else if(exp == 255 || (exp += delta_exp) >= 255) + { + set_exponent(*fvalues, 255); + set_mantissa(*fvalues, 0); + } + else + set_exponent(*fvalues, exp); + + fvalues++; + } +} + +void WavpackLittleEndianToNative(void *data, char *format) +{ + unsigned char *cp = (unsigned char *)data; + int64_t temp; + + while(*format) + { + switch(*format) + { + case 'D': + temp = cp[0] + ((int64_t)cp[1] << 8) + ((int64_t)cp[2] << 16) + ((int64_t)cp[3] << 24) + + ((int64_t)cp[4] << 32) + ((int64_t)cp[5] << 40) + ((int64_t)cp[6] << 48) + + ((int64_t)cp[7] << 56); + *(int64_t *)cp = temp; + cp += 8; + break; + + case 'L': + temp = cp[0] + ((int32_t)cp[1] << 8) + ((int32_t)cp[2] << 16) + ((int32_t)cp[3] << 24); + *(int32_t *)cp = (int32_t)temp; + cp += 4; + break; + + case 'S': + temp = cp[0] + (cp[1] << 8); + *(int16_t *)cp = (int16_t)temp; + cp += 2; + break; + + default: + if(isdigit(*format)) cp += *format - '0'; + + break; + } + + format++; + } +} + +void WavpackNativeToLittleEndian(void *data, char *format) +{ + unsigned char *cp = (unsigned char *)data; + int64_t temp; + + while(*format) + { + switch(*format) + { + case 'D': + temp = *(int64_t *)cp; + *cp++ = (unsigned char)temp; + *cp++ = (unsigned char)(temp >> 8); + *cp++ = (unsigned char)(temp >> 16); + *cp++ = (unsigned char)(temp >> 24); + *cp++ = (unsigned char)(temp >> 32); + *cp++ = (unsigned char)(temp >> 40); + *cp++ = (unsigned char)(temp >> 48); + *cp++ = (unsigned char)(temp >> 56); + break; + + case 'L': + temp = *(int32_t *)cp; + *cp++ = (unsigned char)temp; + *cp++ = (unsigned char)(temp >> 8); + *cp++ = (unsigned char)(temp >> 16); + *cp++ = (unsigned char)(temp >> 24); + break; + + case 'S': + temp = *(int16_t *)cp; + *cp++ = (unsigned char)temp; + *cp++ = (unsigned char)(temp >> 8); + break; + + default: + if(isdigit(*format)) cp += *format - '0'; + + break; + } + + format++; + } +} + +void WavpackBigEndianToNative(void *data, char *format) +{ + unsigned char *cp = (unsigned char *)data; + int64_t temp; + + while(*format) + { + switch(*format) + { + case 'D': + temp = cp[7] + ((int64_t)cp[6] << 8) + ((int64_t)cp[5] << 16) + ((int64_t)cp[4] << 24) + + ((int64_t)cp[3] << 32) + ((int64_t)cp[2] << 40) + ((int64_t)cp[1] << 48) + + ((int64_t)cp[0] << 56); + *(int64_t *)cp = temp; + cp += 8; + break; + + case 'L': + temp = cp[3] + ((int32_t)cp[2] << 8) + ((int32_t)cp[1] << 16) + ((int32_t)cp[0] << 24); + *(int32_t *)cp = (int32_t)temp; + cp += 4; + break; + + case 'S': + temp = cp[1] + (cp[0] << 8); + *(int16_t *)cp = (int16_t)temp; + cp += 2; + break; + + default: + if(isdigit(*format)) cp += *format - '0'; + + break; + } + + format++; + } +} + +void WavpackNativeToBigEndian(void *data, char *format) +{ + unsigned char *cp = (unsigned char *)data; + int64_t temp; + + while(*format) + { + switch(*format) + { + case 'D': + temp = *(int64_t *)cp; + *cp++ = (unsigned char)(temp >> 56); + *cp++ = (unsigned char)(temp >> 48); + *cp++ = (unsigned char)(temp >> 40); + *cp++ = (unsigned char)(temp >> 32); + *cp++ = (unsigned char)(temp >> 24); + *cp++ = (unsigned char)(temp >> 16); + *cp++ = (unsigned char)(temp >> 8); + *cp++ = (unsigned char)temp; + break; + + case 'L': + temp = *(int32_t *)cp; + *cp++ = (unsigned char)(temp >> 24); + *cp++ = (unsigned char)(temp >> 16); + *cp++ = (unsigned char)(temp >> 8); + *cp++ = (unsigned char)temp; + break; + + case 'S': + temp = *(int16_t *)cp; + *cp++ = (unsigned char)(temp >> 8); + *cp++ = (unsigned char)temp; + break; + + default: + if(isdigit(*format)) cp += *format - '0'; + + break; + } + + format++; + } +} + +uint32_t WavpackGetLibraryVersion(void) +{ return (LIBWAVPACK_MAJOR << 16) | (LIBWAVPACK_MINOR << 8) | (LIBWAVPACK_MICRO << 0); } + +const char *WavpackGetLibraryVersionString(void) { return LIBWAVPACK_VERSION_STRING; } diff --git a/wavpack/decorr_utils.c b/wavpack/decorr_utils.c new file mode 100644 index 0000000..db6c0b3 --- /dev/null +++ b/wavpack/decorr_utils.c @@ -0,0 +1,210 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// decorr_utils.c + +// This module contains the functions that process metadata blocks that are +// specific to the decorrelator. These would be called any time a WavPack +// block was parsed. These are in a module separate from the actual unpack +// decorrelation code (unpack.c) so that if an application just wants to get +// information from WavPack files (rather than actually decoding audio) then +// less code needs to be linked. + +#include +#include + +#include "wavpack_local.h" + +///////////////////////////// executable code //////////////////////////////// + +// Read decorrelation terms from specified metadata block into the +// decorr_passes array. The terms range from -3 to 8, plus 17 & 18; +// other values are reserved and generate errors for now. The delta +// ranges from 0 to 7 with all values valid. Note that the terms are +// stored in the opposite order in the decorr_passes array compared +// to packing. + +int read_decorr_terms(WavpackStream *wps, WavpackMetadata *wpmd) +{ + int termcnt = wpmd->byte_length; + unsigned char *byteptr = wpmd->data; + struct decorr_pass *dpp; + + if(termcnt > MAX_NTERMS) return FALSE; + + wps->num_terms = termcnt; + + for(dpp = wps->decorr_passes + termcnt - 1; termcnt--; dpp--) + { + dpp->term = (int)(*byteptr & 0x1f) - 5; + dpp->delta = (*byteptr++ >> 5) & 0x7; + + if(!dpp->term || dpp->term < -3 || (dpp->term > MAX_TERM && dpp->term < 17) || dpp->term > 18 || + ((wps->wphdr.flags & MONO_DATA) && dpp->term < 0)) + return FALSE; + } + + return TRUE; +} + +// Read decorrelation weights from specified metadata block into the +// decorr_passes array. The weights range +/-1024, but are rounded and +// truncated to fit in signed chars for metadata storage. Weights are +// separate for the two channels and are specified from the "last" term +// (first during encode). Unspecified weights are set to zero. + +int read_decorr_weights(WavpackStream *wps, WavpackMetadata *wpmd) +{ + int termcnt = wpmd->byte_length, tcount; + char *byteptr = wpmd->data; + struct decorr_pass *dpp; + + if(!(wps->wphdr.flags & MONO_DATA)) termcnt /= 2; + + if(termcnt > wps->num_terms) return FALSE; + + for(tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) dpp->weight_A = dpp->weight_B = 0; + + while(--dpp >= wps->decorr_passes && termcnt--) + { + dpp->weight_A = restore_weight(*byteptr++); + + if(!(wps->wphdr.flags & MONO_DATA)) dpp->weight_B = restore_weight(*byteptr++); + } + + return TRUE; +} + +// Read decorrelation samples from specified metadata block into the +// decorr_passes array. The samples are signed 32-bit values, but are +// converted to signed log2 values for storage in metadata. Values are +// stored for both channels and are specified from the "last" term +// (first during encode) with unspecified samples set to zero. The +// number of samples stored varies with the actual term value, so +// those must obviously come first in the metadata. + +int read_decorr_samples(WavpackStream *wps, WavpackMetadata *wpmd) +{ + unsigned char *byteptr = wpmd->data; + unsigned char *endptr = byteptr + wpmd->byte_length; + struct decorr_pass *dpp; + int tcount; + + for(tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + { + CLEAR(dpp->samples_A); + CLEAR(dpp->samples_B); + } + + if(wps->wphdr.version == 0x402 && (wps->wphdr.flags & HYBRID_FLAG)) + { + if(byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr) return FALSE; + + wps->dc.error[0] = wp_exp2s((int16_t)(byteptr[0] + (byteptr[1] << 8))); + byteptr += 2; + + if(!(wps->wphdr.flags & MONO_DATA)) + { + wps->dc.error[1] = wp_exp2s((int16_t)(byteptr[0] + (byteptr[1] << 8))); + byteptr += 2; + } + } + + while(dpp-- > wps->decorr_passes && byteptr < endptr) + if(dpp->term > MAX_TERM) + { + if(byteptr + (wps->wphdr.flags & MONO_DATA ? 4 : 8) > endptr) return FALSE; + + dpp->samples_A[0] = wp_exp2s((int16_t)(byteptr[0] + (byteptr[1] << 8))); + dpp->samples_A[1] = wp_exp2s((int16_t)(byteptr[2] + (byteptr[3] << 8))); + byteptr += 4; + + if(!(wps->wphdr.flags & MONO_DATA)) + { + dpp->samples_B[0] = wp_exp2s((int16_t)(byteptr[0] + (byteptr[1] << 8))); + dpp->samples_B[1] = wp_exp2s((int16_t)(byteptr[2] + (byteptr[3] << 8))); + byteptr += 4; + } + } + else if(dpp->term < 0) + { + if(byteptr + 4 > endptr) return FALSE; + + dpp->samples_A[0] = wp_exp2s((int16_t)(byteptr[0] + (byteptr[1] << 8))); + dpp->samples_B[0] = wp_exp2s((int16_t)(byteptr[2] + (byteptr[3] << 8))); + byteptr += 4; + } + else + { + int m = 0, cnt = dpp->term; + + while(cnt--) + { + if(byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr) return FALSE; + + dpp->samples_A[m] = wp_exp2s((int16_t)(byteptr[0] + (byteptr[1] << 8))); + byteptr += 2; + + if(!(wps->wphdr.flags & MONO_DATA)) + { + dpp->samples_B[m] = wp_exp2s((int16_t)(byteptr[0] + (byteptr[1] << 8))); + byteptr += 2; + } + + m++; + } + } + + return byteptr == endptr; +} + +// Read the shaping weights from specified metadata block into the +// WavpackStream structure. Note that there must be two values (even +// for mono streams) and that the values are stored in the same +// manner as decorrelation weights. These would normally be read from +// the "correction" file and are used for lossless reconstruction of +// hybrid data. + +int read_shaping_info(WavpackStream *wps, WavpackMetadata *wpmd) +{ + if(wpmd->byte_length == 2) + { + char *byteptr = wpmd->data; + + wps->dc.shaping_acc[0] = (int32_t)restore_weight(*byteptr++) << 16; + wps->dc.shaping_acc[1] = (int32_t)restore_weight(*byteptr++) << 16; + return TRUE; + } + else if(wpmd->byte_length >= (wps->wphdr.flags & MONO_DATA ? 4 : 8)) + { + unsigned char *byteptr = wpmd->data; + + wps->dc.error[0] = wp_exp2s((int16_t)(byteptr[0] + (byteptr[1] << 8))); + wps->dc.shaping_acc[0] = wp_exp2s((int16_t)(byteptr[2] + (byteptr[3] << 8))); + byteptr += 4; + + if(!(wps->wphdr.flags & MONO_DATA)) + { + wps->dc.error[1] = wp_exp2s((int16_t)(byteptr[0] + (byteptr[1] << 8))); + wps->dc.shaping_acc[1] = wp_exp2s((int16_t)(byteptr[2] + (byteptr[3] << 8))); + byteptr += 4; + } + + if(wpmd->byte_length == (wps->wphdr.flags & MONO_DATA ? 6 : 12)) + { + wps->dc.shaping_delta[0] = wp_exp2s((int16_t)(byteptr[0] + (byteptr[1] << 8))); + + if(!(wps->wphdr.flags & MONO_DATA)) + wps->dc.shaping_delta[1] = wp_exp2s((int16_t)(byteptr[2] + (byteptr[3] << 8))); + } + + return TRUE; + } + + return FALSE; +} diff --git a/wavpack/entropy_utils.c b/wavpack/entropy_utils.c new file mode 100644 index 0000000..7dd5a5a --- /dev/null +++ b/wavpack/entropy_utils.c @@ -0,0 +1,369 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// entropy_utils.c + +// This module contains the functions that process metadata blocks that are +// specific to the entropy decoder; these would be called any time a WavPack +// block was parsed. Additionally, it contains tables and functions that are +// common to both entropy coding and decoding. These are in a module separate +// from the actual entropy encoder (write_words.c) and decoder (read_words.c) +// so that if applications that just do a subset of the full WavPack reading +// and writing can link with a subset of the library. + +#include +#include + +#include "wavpack_local.h" + +///////////////////////////// local table storage //////////////////////////// + +const uint32_t bitset[] = {1L << 0, 1L << 1, 1L << 2, 1L << 3, 1L << 4, 1L << 5, 1L << 6, 1L << 7, + 1L << 8, 1L << 9, 1L << 10, 1L << 11, 1L << 12, 1L << 13, 1L << 14, 1L << 15, + 1L << 16, 1L << 17, 1L << 18, 1L << 19, 1L << 20, 1L << 21, 1L << 22, 1L << 23, + 1L << 24, 1L << 25, 1L << 26, 1L << 27, 1L << 28, 1L << 29, 1L << 30, 1L << 31}; + +const uint32_t bitmask[] = { + (1L << 0) - 1, (1L << 1) - 1, (1L << 2) - 1, (1L << 3) - 1, (1L << 4) - 1, (1L << 5) - 1, (1L << 6) - 1, + (1L << 7) - 1, (1L << 8) - 1, (1L << 9) - 1, (1L << 10) - 1, (1L << 11) - 1, (1L << 12) - 1, (1L << 13) - 1, + (1L << 14) - 1, (1L << 15) - 1, (1L << 16) - 1, (1L << 17) - 1, (1L << 18) - 1, (1L << 19) - 1, (1L << 20) - 1, + (1L << 21) - 1, (1L << 22) - 1, (1L << 23) - 1, (1L << 24) - 1, (1L << 25) - 1, (1L << 26) - 1, (1L << 27) - 1, + (1L << 28) - 1, (1L << 29) - 1, (1L << 30) - 1, 0x7fffffff}; + +const char nbits_table[] = { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, // 0 - 15 + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 16 - 31 + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 32 - 47 + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 48 - 63 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 64 - 79 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 80 - 95 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 96 - 111 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 112 - 127 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 128 - 143 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 144 - 159 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 160 - 175 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 176 - 191 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 192 - 207 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 208 - 223 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 224 - 239 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 // 240 - 255 +}; + +static const unsigned char log2_table[] = { + 0x00, 0x01, 0x03, 0x04, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19, + 0x1a, 0x1c, 0x1d, 0x1e, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2c, 0x2d, 0x2e, 0x2f, 0x31, 0x32, + 0x33, 0x34, 0x36, 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x41, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48, 0x49, + 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, + 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xc0, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, + 0xcd, 0xce, 0xcf, 0xd0, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, + 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe4, 0xe5, 0xe6, 0xe7, 0xe7, 0xe8, 0xe9, 0xea, 0xea, + 0xeb, 0xec, 0xed, 0xee, 0xee, 0xef, 0xf0, 0xf1, 0xf1, 0xf2, 0xf3, 0xf4, 0xf4, 0xf5, 0xf6, 0xf7, 0xf7, 0xf8, 0xf9, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xff}; + +static const unsigned char exp2_table[] = { + 0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x19, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28, 0x28, 0x29, 0x2a, + 0x2b, 0x2c, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, + 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x48, 0x49, 0x4a, + 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, + 0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, + 0x82, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa8, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc2, + 0xc3, 0xc4, 0xc5, 0xc6, 0xc8, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf, 0xd0, 0xd2, 0xd3, 0xd4, 0xd6, 0xd7, 0xd8, 0xd9, + 0xdb, 0xdc, 0xdd, 0xde, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe8, 0xe9, 0xea, 0xec, 0xed, 0xee, 0xf0, 0xf1, 0xf2, + 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xff}; + +///////////////////////////// executable code //////////////////////////////// + +// Read the median log2 values from the specifed metadata structure, convert +// them back to 32-bit unsigned values and store them. If length is not +// exactly correct then we flag and return an error. + +int read_entropy_vars(WavpackStream *wps, WavpackMetadata *wpmd) +{ + unsigned char *byteptr = wpmd->data; + + if(wpmd->byte_length != ((wps->wphdr.flags & MONO_DATA) ? 6 : 12)) return FALSE; + + wps->w.c[0].median[0] = wp_exp2s(byteptr[0] + (byteptr[1] << 8)); + wps->w.c[0].median[1] = wp_exp2s(byteptr[2] + (byteptr[3] << 8)); + wps->w.c[0].median[2] = wp_exp2s(byteptr[4] + (byteptr[5] << 8)); + + if(!(wps->wphdr.flags & MONO_DATA)) + { + wps->w.c[1].median[0] = wp_exp2s(byteptr[6] + (byteptr[7] << 8)); + wps->w.c[1].median[1] = wp_exp2s(byteptr[8] + (byteptr[9] << 8)); + wps->w.c[1].median[2] = wp_exp2s(byteptr[10] + (byteptr[11] << 8)); + } + + return TRUE; +} + +// Read the hybrid related values from the specifed metadata structure, convert +// them back to their internal formats and store them. The extended profile +// stuff is not implemented yet, so return an error if we get more data than +// we know what to do with. + +int read_hybrid_profile(WavpackStream *wps, WavpackMetadata *wpmd) +{ + unsigned char *byteptr = wpmd->data; + unsigned char *endptr = byteptr + wpmd->byte_length; + + if(wps->wphdr.flags & HYBRID_BITRATE) + { + if(byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr) return FALSE; + + wps->w.c[0].slow_level = wp_exp2s(byteptr[0] + (byteptr[1] << 8)); + byteptr += 2; + + if(!(wps->wphdr.flags & MONO_DATA)) + { + wps->w.c[1].slow_level = wp_exp2s(byteptr[0] + (byteptr[1] << 8)); + byteptr += 2; + } + } + + if(byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr) return FALSE; + + wps->w.bitrate_acc[0] = (int32_t)(byteptr[0] + (byteptr[1] << 8)) << 16; + byteptr += 2; + + if(!(wps->wphdr.flags & MONO_DATA)) + { + wps->w.bitrate_acc[1] = (int32_t)(byteptr[0] + (byteptr[1] << 8)) << 16; + byteptr += 2; + } + + if(byteptr < endptr) + { + if(byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr) return FALSE; + + wps->w.bitrate_delta[0] = wp_exp2s((int16_t)(byteptr[0] + (byteptr[1] << 8))); + byteptr += 2; + + if(!(wps->wphdr.flags & MONO_DATA)) + { + wps->w.bitrate_delta[1] = wp_exp2s((int16_t)(byteptr[0] + (byteptr[1] << 8))); + byteptr += 2; + } + + if(byteptr < endptr) return FALSE; + } + else + wps->w.bitrate_delta[0] = wps->w.bitrate_delta[1] = 0; + + return TRUE; +} + +// This function is called during both encoding and decoding of hybrid data to +// update the "error_limit" variable which determines the maximum sample error +// allowed in the main bitstream. In the HYBRID_BITRATE mode (which is the only +// currently implemented) this is calculated from the slow_level values and the +// bitrate accumulators. Note that the bitrate accumulators can be changing. + +void update_error_limit(WavpackStream *wps) +{ + int bitrate_0 = (wps->w.bitrate_acc[0] += wps->w.bitrate_delta[0]) >> 16; + + if(wps->wphdr.flags & MONO_DATA) + { + if(wps->wphdr.flags & HYBRID_BITRATE) + { + int slow_log_0 = (wps->w.c[0].slow_level + SLO) >> SLS; + + if(slow_log_0 - bitrate_0 > -0x100) + wps->w.c[0].error_limit = wp_exp2s(slow_log_0 - bitrate_0 + 0x100); + else + wps->w.c[0].error_limit = 0; + } + else + wps->w.c[0].error_limit = wp_exp2s(bitrate_0); + } + else + { + int bitrate_1 = (wps->w.bitrate_acc[1] += wps->w.bitrate_delta[1]) >> 16; + + if(wps->wphdr.flags & HYBRID_BITRATE) + { + int slow_log_0 = (wps->w.c[0].slow_level + SLO) >> SLS; + int slow_log_1 = (wps->w.c[1].slow_level + SLO) >> SLS; + + if(wps->wphdr.flags & HYBRID_BALANCE) + { + int balance = (slow_log_1 - slow_log_0 + bitrate_1 + 1) >> 1; + + if(balance > bitrate_0) + { + bitrate_1 = bitrate_0 * 2; + bitrate_0 = 0; + } + else if(-balance > bitrate_0) + { + bitrate_0 = bitrate_0 * 2; + bitrate_1 = 0; + } + else + { + bitrate_1 = bitrate_0 + balance; + bitrate_0 = bitrate_0 - balance; + } + } + + if(slow_log_0 - bitrate_0 > -0x100) + wps->w.c[0].error_limit = wp_exp2s(slow_log_0 - bitrate_0 + 0x100); + else + wps->w.c[0].error_limit = 0; + + if(slow_log_1 - bitrate_1 > -0x100) + wps->w.c[1].error_limit = wp_exp2s(slow_log_1 - bitrate_1 + 0x100); + else + wps->w.c[1].error_limit = 0; + } + else + { + wps->w.c[0].error_limit = wp_exp2s(bitrate_0); + wps->w.c[1].error_limit = wp_exp2s(bitrate_1); + } + } +} + +// The concept of a base 2 logarithm is used in many parts of WavPack. It is +// a way of sufficiently accurately representing 32-bit signed and unsigned +// values storing only 16 bits (actually fewer). It is also used in the hybrid +// mode for quickly comparing the relative magnitude of large values (i.e. +// division) and providing smooth exponentials using only addition. + +// These are not strict logarithms in that they become linear around zero and +// can therefore represent both zero and negative values. They have 8 bits +// of precision and in "roundtrip" conversions the total error never exceeds 1 +// part in 225 except for the cases of +/-115 and +/-195 (which error by 1). + +// This function returns the log2 for the specified 32-bit unsigned value. +// The maximum value allowed is about 0xff800000 and returns 8447. + +int FASTCALL wp_log2(uint32_t avalue) +{ + int dbits; + + if((avalue += avalue >> 9) < (1 << 8)) + { + dbits = nbits_table[avalue]; + return (dbits << 8) + log2_table[(avalue << (9 - dbits)) & 0xff]; + } + else + { + if(avalue < (1L << 16)) + dbits = nbits_table[avalue >> 8] + 8; + else if(avalue < (1L << 24)) + dbits = nbits_table[avalue >> 16] + 16; + else + dbits = nbits_table[avalue >> 24] + 24; + + return (dbits << 8) + log2_table[(avalue >> (dbits - 9)) & 0xff]; + } +} + +// This function scans a buffer of longs and accumulates the total log2 value +// of all the samples. This is useful for determining maximum compression +// because the bitstream storage required for entropy coding is proportional +// to the base 2 log of the samples. On some platforms there is an assembly +// version of this. + +#if !defined(OPT_ASM_X86) && !defined(OPT_ASM_X64) + +uint32_t log2buffer(int32_t *samples, uint32_t num_samples, int limit) +{ + uint32_t result = 0, avalue; + int dbits; + + while(num_samples--) + { + avalue = abs(*samples++); + + if((avalue += avalue >> 9) < (1 << 8)) + { + dbits = nbits_table[avalue]; + result += (dbits << 8) + log2_table[(avalue << (9 - dbits)) & 0xff]; + } + else + { + if(avalue < (1L << 16)) + dbits = nbits_table[avalue >> 8] + 8; + else if(avalue < (1L << 24)) + dbits = nbits_table[avalue >> 16] + 16; + else + dbits = nbits_table[avalue >> 24] + 24; + + result += dbits = (dbits << 8) + log2_table[(avalue >> (dbits - 9)) & 0xff]; + + if(limit && dbits >= limit) return (uint32_t)-1; + } + } + + return result; +} + +#endif + +// This function returns the log2 for the specified 32-bit signed value. +// All input values are valid and the return values are in the range of +// +/- 8192. + +int wp_log2s(int32_t value) { return (value < 0) ? -wp_log2(-value) : wp_log2(value); } + +// This function returns the original integer represented by the supplied +// logarithm (at least within the provided accuracy). The log is signed, +// but since a full 32-bit value is returned this can be used for unsigned +// conversions as well (i.e. the input range is -8192 to +8447). + +int32_t wp_exp2s(int log) +{ + uint32_t value; + + if(log < 0) return -wp_exp2s(-log); + + value = exp2_table[log & 0xff] | 0x100; + + if((log >>= 8) <= 9) + return value >> (9 - log); + else + return value << (log - 9); +} + +// These two functions convert internal weights (which are normally +/-1024) +// to and from an 8-bit signed character version for storage in metadata. The +// weights are clipped here in the case that they are outside that range. + +signed char store_weight(int weight) +{ + if(weight > 1024) + weight = 1024; + else if(weight < -1024) + weight = -1024; + + if(weight > 0) weight -= (weight + 64) >> 7; + + return (weight + 4) >> 3; +} + +int restore_weight(signed char weight) +{ + int result; + + if((result = (int)weight << 3) > 0) result += (result + 64) >> 7; + + return result; +} diff --git a/wavpack/open_legacy.c b/wavpack/open_legacy.c new file mode 100644 index 0000000..937a5b2 --- /dev/null +++ b/wavpack/open_legacy.c @@ -0,0 +1,116 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2016 David Bryant. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// open_legacy.c + +// This code provides an interface between the new reader callback mechanism that +// WavPack uses internally and the old reader callback functions that did not +// provide large file support. + +#include +#include + +#include "wavpack_local.h" + +typedef struct +{ + WavpackStreamReader *reader; + void *id; +} WavpackReaderTranslator; + +static int32_t trans_read_bytes(void *id, void *data, int32_t bcount) +{ + WavpackReaderTranslator *trans = id; + return trans->reader->read_bytes(trans->id, data, bcount); +} + +static int32_t trans_write_bytes(void *id, void *data, int32_t bcount) +{ + WavpackReaderTranslator *trans = id; + return trans->reader->write_bytes(trans->id, data, bcount); +} + +static int64_t trans_get_pos(void *id) +{ + WavpackReaderTranslator *trans = id; + return trans->reader->get_pos(trans->id); +} + +static int trans_set_pos_abs(void *id, int64_t pos) +{ + WavpackReaderTranslator *trans = id; + return trans->reader->set_pos_abs(trans->id, (uint32_t)pos); +} + +static int trans_set_pos_rel(void *id, int64_t delta, int mode) +{ + WavpackReaderTranslator *trans = id; + return trans->reader->set_pos_rel(trans->id, (int32_t)delta, mode); +} + +static int trans_push_back_byte(void *id, int c) +{ + WavpackReaderTranslator *trans = id; + return trans->reader->push_back_byte(trans->id, c); +} + +static int64_t trans_get_length(void *id) +{ + WavpackReaderTranslator *trans = id; + return trans->reader->get_length(trans->id); +} + +static int trans_can_seek(void *id) +{ + WavpackReaderTranslator *trans = id; + return trans->reader->can_seek(trans->id); +} + +static int trans_close_stream(void *id) +{ + free(id); + return 0; +} + +static WavpackStreamReader64 trans_reader = { + trans_read_bytes, trans_write_bytes, trans_get_pos, trans_set_pos_abs, trans_set_pos_rel, trans_push_back_byte, + trans_get_length, trans_can_seek, NULL, trans_close_stream}; + +// This function is identical to WavpackOpenFileInput64() except that instead +// of providing the new 64-bit reader callbacks, the old reader callbacks are +// utilized and a translation layer is employed. It is provided as a compatibility +// function for existing applications. To ensure that streaming applications using +// this function continue to work, the OPEN_NO_CHECKSUM flag is forced on when +// the OPEN_STREAMING flag is set. + +WavpackContext *WavpackOpenFileInputEx(WavpackStreamReader *reader, void *wv_id, void *wvc_id, char *error, int flags, + int norm_offset) +{ + WavpackReaderTranslator *trans_wv = NULL, *trans_wvc = NULL; + + // this prevents existing streaming applications from failing if they try to pass + // in blocks that have been modified from the original (e.g., Matroska blocks) + + if(flags & OPEN_STREAMING) flags |= OPEN_NO_CHECKSUM; + + if(wv_id) + { + trans_wv = malloc(sizeof(WavpackReaderTranslator)); + trans_wv->reader = reader; + trans_wv->id = wv_id; + } + + if(wvc_id) + { + trans_wvc = malloc(sizeof(WavpackReaderTranslator)); + trans_wvc->reader = reader; + trans_wvc->id = wvc_id; + } + + return WavpackOpenFileInputEx64(&trans_reader, trans_wv, trans_wvc, error, flags, norm_offset); +} diff --git a/wavpack/open_utils.c b/wavpack/open_utils.c new file mode 100644 index 0000000..70cf387 --- /dev/null +++ b/wavpack/open_utils.c @@ -0,0 +1,1327 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2016 David Bryant. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// open_utils.c + +// This module provides all the code required to open an existing WavPack file +// for reading by using a reader callback mechanism (NOT a filename). This +// includes the code required to find and parse WavPack blocks, process any +// included metadata, and queue up the bitstreams containing the encoded audio +// data. It does not the actual code to unpack audio data and this was done so +// that programs that just want to query WavPack files for information (like, +// for example, taggers) don't need to link in a lot of unnecessary code. + +#include +#include + +#include "wavpack_local.h" + +// This function is identical to WavpackOpenFileInput() except that instead +// of providing a filename to open, the caller provides a pointer to a set of +// reader callbacks and instances of up to two streams. The first of these +// streams is required and contains the regular WavPack data stream; the second +// contains the "correction" file if desired. Unlike the standard open +// function which handles the correction file transparently, in this case it +// is the responsibility of the caller to be aware of correction files. + +static int seek_eof_information(WavpackContext *wpc, int64_t *final_index, int get_wrapper); + +WavpackContext *WavpackOpenFileInputEx64(WavpackStreamReader64 *reader, void *wv_id, void *wvc_id, char *error, + int flags, int norm_offset) +{ + WavpackContext *wpc = malloc(sizeof(WavpackContext)); + WavpackStream *wps; + int num_blocks = 0; + unsigned char first_byte; + uint32_t bcount; + + if(!wpc) + { + if(error) strcpy(error, "can't allocate memory"); + return NULL; + } + + CLEAR(*wpc); + wpc->wv_in = wv_id; + wpc->wvc_in = wvc_id; + wpc->reader = reader; + wpc->total_samples = -1; + wpc->norm_offset = norm_offset; + wpc->max_streams = OLD_MAX_STREAMS; // use this until overwritten with actual number + wpc->open_flags = flags; + + wpc->filelen = wpc->reader->get_length(wpc->wv_in); + +#ifndef NO_TAGS + if((flags & (OPEN_TAGS | OPEN_EDIT_TAGS)) && wpc->reader->can_seek(wpc->wv_in)) + { + load_tag(wpc); + wpc->reader->set_pos_abs(wpc->wv_in, 0); + + if((flags & OPEN_EDIT_TAGS) && !editable_tag(&wpc->m_tag)) + { + if(error) strcpy(error, "can't edit tags located at the beginning of files!"); + return WavpackCloseFile(wpc); + } + } +#endif + + if(wpc->reader->read_bytes(wpc->wv_in, &first_byte, 1) != 1) + { + if(error) strcpy(error, "can't read all of WavPack file!"); + return WavpackCloseFile(wpc); + } + + wpc->reader->push_back_byte(wpc->wv_in, first_byte); + + if(first_byte == 'R') + { +#ifdef ENABLE_LEGACY + return open_file3(wpc, error); +#else + if(error) strcpy(error, "this legacy WavPack file is deprecated, use version 4.80.0 to transcode"); + return WavpackCloseFile(wpc); +#endif + } + + wpc->streams = malloc((wpc->num_streams = 1) * sizeof(wpc->streams[0])); + if(!wpc->streams) + { + if(error) strcpy(error, "can't allocate memory"); + return WavpackCloseFile(wpc); + } + + wpc->streams[0] = wps = malloc(sizeof(WavpackStream)); + if(!wps) + { + if(error) strcpy(error, "can't allocate memory"); + return WavpackCloseFile(wpc); + } + CLEAR(*wps); + + while(!wps->wphdr.block_samples) + { + + wpc->filepos = wpc->reader->get_pos(wpc->wv_in); + bcount = read_next_header(wpc->reader, wpc->wv_in, &wps->wphdr); + + if(bcount == (uint32_t)-1 || (!wps->wphdr.block_samples && num_blocks++ > 16)) + { + if(error) strcpy(error, "not compatible with this version of WavPack file!"); + return WavpackCloseFile(wpc); + } + + wpc->filepos += bcount; + wps->blockbuff = malloc(wps->wphdr.ckSize + 8); + if(!wps->blockbuff) + { + if(error) strcpy(error, "can't allocate memory"); + return WavpackCloseFile(wpc); + } + memcpy(wps->blockbuff, &wps->wphdr, 32); + + if(wpc->reader->read_bytes(wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != wps->wphdr.ckSize - 24) + { + if(error) strcpy(error, "can't read all of WavPack file!"); + return WavpackCloseFile(wpc); + } + + // if block does not verify, flag error, free buffer, and continue + if(!WavpackVerifySingleBlock(wps->blockbuff, !(flags & OPEN_NO_CHECKSUM))) + { + wps->wphdr.block_samples = 0; + free(wps->blockbuff); + wps->blockbuff = NULL; + wpc->crc_errors++; + continue; + } + + wps->init_done = FALSE; + + if(wps->wphdr.block_samples) + { + if(flags & OPEN_STREAMING) + SET_BLOCK_INDEX(wps->wphdr, 0); + else if(wpc->total_samples == -1) + { + if(GET_BLOCK_INDEX(wps->wphdr) || GET_TOTAL_SAMPLES(wps->wphdr) == -1) + { + wpc->initial_index = GET_BLOCK_INDEX(wps->wphdr); + SET_BLOCK_INDEX(wps->wphdr, 0); + + if(wpc->reader->can_seek(wpc->wv_in)) + { + int64_t final_index = -1; + + seek_eof_information(wpc, &final_index, FALSE); + + if(final_index != -1) wpc->total_samples = final_index - wpc->initial_index; + } + } + else + wpc->total_samples = GET_TOTAL_SAMPLES(wps->wphdr); + } + } + else if(wpc->total_samples == -1 && !GET_BLOCK_INDEX(wps->wphdr) && GET_TOTAL_SAMPLES(wps->wphdr)) + wpc->total_samples = GET_TOTAL_SAMPLES(wps->wphdr); + + if(wpc->wvc_in && wps->wphdr.block_samples && (wps->wphdr.flags & HYBRID_FLAG)) + { + unsigned char ch; + + if(wpc->reader->read_bytes(wpc->wvc_in, &ch, 1) == 1) + { + wpc->reader->push_back_byte(wpc->wvc_in, ch); + wpc->file2len = wpc->reader->get_length(wpc->wvc_in); + wpc->wvc_flag = TRUE; + } + } + + if(wpc->wvc_flag && !read_wvc_block(wpc)) + { + if(error) strcpy(error, "not compatible with this version of correction file!"); + return WavpackCloseFile(wpc); + } + + if(!wps->init_done && !unpack_init(wpc)) + { + if(error) + strcpy(error, wpc->error_message[0] ? wpc->error_message + : "not compatible with this version of WavPack file!"); + + return WavpackCloseFile(wpc); + } + + wps->init_done = TRUE; + } + + wpc->config.flags &= ~0xff; + wpc->config.flags |= wps->wphdr.flags & 0xff; + + if(!wpc->config.num_channels) + { + wpc->config.num_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2; + wpc->config.channel_mask = 0x5 - wpc->config.num_channels; + } + + if((flags & OPEN_2CH_MAX) && !(wps->wphdr.flags & FINAL_BLOCK)) + wpc->reduced_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2; + + if(wps->wphdr.flags & DSD_FLAG) + { +#ifdef ENABLE_DSD + if(flags & OPEN_DSD_NATIVE) + { + wpc->config.bytes_per_sample = 1; + wpc->config.bits_per_sample = 8; + } + else if(flags & OPEN_DSD_AS_PCM) + { + wpc->decimation_context = + decimate_dsd_init(wpc->reduced_channels ? wpc->reduced_channels : wpc->config.num_channels); + + wpc->config.bytes_per_sample = 3; + wpc->config.bits_per_sample = 24; + } + else + { + if(error) strcpy(error, "not configured to handle DSD WavPack files!"); + return WavpackCloseFile(wpc); + } +#else + if(error) strcpy(error, "not configured to handle DSD WavPack files!"); + return WavpackCloseFile(wpc); +#endif + } + else + { + wpc->config.bytes_per_sample = (wps->wphdr.flags & BYTES_STORED) + 1; + wpc->config.float_norm_exp = wps->float_norm_exp; + + wpc->config.bits_per_sample = + (wpc->config.bytes_per_sample * 8) - ((wps->wphdr.flags & SHIFT_MASK) >> SHIFT_LSB); + } + + if(!wpc->config.sample_rate) + { + if(!wps->wphdr.block_samples || (wps->wphdr.flags & SRATE_MASK) == SRATE_MASK) + wpc->config.sample_rate = 44100; + else + wpc->config.sample_rate = sample_rates[(wps->wphdr.flags & SRATE_MASK) >> SRATE_LSB]; + } + + return wpc; +} + +// This function returns the major version number of the WavPack program +// (or library) that created the open file. Currently, this can be 1 to 5. +// Minor versions are not recorded in WavPack files. + +int WavpackGetVersion(WavpackContext *wpc) +{ + if(wpc) + { +#ifdef ENABLE_LEGACY + if(wpc->stream3) return get_version3(wpc); +#endif + return wpc->version_five ? 5 : 4; + } + + return 0; +} + +// Return the file format specified in the call to WavpackSetFileInformation() +// when the file was created. For all files created prior to WavPack 5.0 this +// will 0 (WP_FORMAT_WAV). + +unsigned char WavpackGetFileFormat(WavpackContext *wpc) { return wpc->file_format; } + +// Return a string representing the recommended file extension for the open +// WavPack file. For all files created prior to WavPack 5.0 this will be "wav", +// even for raw files with no RIFF into. This string is specified in the +// call to WavpackSetFileInformation() when the file was created. + +char *WavpackGetFileExtension(WavpackContext *wpc) +{ + if(wpc && wpc->file_extension[0]) + return wpc->file_extension; + else + return "wav"; +} + +// This function initializes everything required to unpack a WavPack block +// and must be called before unpack_samples() is called to obtain audio data. +// It is assumed that the WavpackHeader has been read into the wps->wphdr +// (in the current WavpackStream) and that the entire block has been read at +// wps->blockbuff. If a correction file is available (wpc->wvc_flag = TRUE) +// then the corresponding correction block must be read into wps->block2buff +// and its WavpackHeader has overwritten the header at wps->wphdr. This is +// where all the metadata blocks are scanned including those that contain +// bitstream data. + +static int read_metadata_buff(WavpackMetadata *wpmd, unsigned char *blockbuff, unsigned char **buffptr); +static int process_metadata(WavpackContext *wpc, WavpackMetadata *wpmd); +static void bs_open_read(Bitstream *bs, void *buffer_start, void *buffer_end); + +int unpack_init(WavpackContext *wpc) +{ + WavpackStream *wps = wpc->streams[wpc->current_stream]; + unsigned char *blockptr, *block2ptr; + WavpackMetadata wpmd; + + wps->num_terms = 0; + wps->mute_error = FALSE; + wps->crc = wps->crc_x = 0xffffffff; + wps->dsd.ready = 0; + CLEAR(wps->wvbits); + CLEAR(wps->wvcbits); + CLEAR(wps->wvxbits); + CLEAR(wps->decorr_passes); + CLEAR(wps->dc); + CLEAR(wps->w); + + if(!(wps->wphdr.flags & MONO_FLAG) && wpc->config.num_channels && wps->wphdr.block_samples && + (wpc->reduced_channels == 1 || wpc->config.num_channels == 1)) + { + wps->mute_error = TRUE; + return FALSE; + } + + if((wps->wphdr.flags & UNKNOWN_FLAGS) || (wps->wphdr.flags & MONO_DATA) == MONO_DATA) + { + wps->mute_error = TRUE; + return FALSE; + } + + blockptr = wps->blockbuff + sizeof(WavpackHeader); + + while(read_metadata_buff(&wpmd, wps->blockbuff, &blockptr)) + if(!process_metadata(wpc, &wpmd)) + { + wps->mute_error = TRUE; + return FALSE; + } + + if(wps->wphdr.block_samples && wpc->wvc_flag && wps->block2buff) + { + block2ptr = wps->block2buff + sizeof(WavpackHeader); + + while(read_metadata_buff(&wpmd, wps->block2buff, &block2ptr)) + if(!process_metadata(wpc, &wpmd)) + { + wps->mute_error = TRUE; + return FALSE; + } + } + + if(wps->wphdr.block_samples && ((wps->wphdr.flags & DSD_FLAG) ? !wps->dsd.ready : !bs_is_open(&wps->wvbits))) + { + if(bs_is_open(&wps->wvcbits)) strcpy(wpc->error_message, "can't unpack correction files alone!"); + + wps->mute_error = TRUE; + return FALSE; + } + + if(wps->wphdr.block_samples && !bs_is_open(&wps->wvxbits)) + { + if((wps->wphdr.flags & INT32_DATA) && wps->int32_sent_bits) wpc->lossy_blocks = TRUE; + + if((wps->wphdr.flags & FLOAT_DATA) && + wps->float_flags & (FLOAT_EXCEPTIONS | FLOAT_ZEROS_SENT | FLOAT_SHIFT_SENT | FLOAT_SHIFT_SAME)) + wpc->lossy_blocks = TRUE; + } + + if(wps->wphdr.block_samples) wps->sample_index = GET_BLOCK_INDEX(wps->wphdr); + + return TRUE; +} + +//////////////////////////////// matadata handlers /////////////////////////////// + +// These functions handle specific metadata types and are called directly +// during WavPack block parsing by process_metadata() at the bottom. + +// This function initialzes the main bitstream for audio samples, which must +// be in the "wv" file. + +static int init_wv_bitstream(WavpackStream *wps, WavpackMetadata *wpmd) +{ + if(!wpmd->byte_length || (wpmd->byte_length & 1)) return FALSE; + + bs_open_read(&wps->wvbits, wpmd->data, (unsigned char *)wpmd->data + wpmd->byte_length); + return TRUE; +} + +// This function initialzes the "correction" bitstream for audio samples, +// which currently must be in the "wvc" file. + +static int init_wvc_bitstream(WavpackStream *wps, WavpackMetadata *wpmd) +{ + if(!wpmd->byte_length || (wpmd->byte_length & 1)) return FALSE; + + bs_open_read(&wps->wvcbits, wpmd->data, (unsigned char *)wpmd->data + wpmd->byte_length); + return TRUE; +} + +// This function initialzes the "extra" bitstream for audio samples which +// contains the information required to losslessly decompress 32-bit float data +// or integer data that exceeds 24 bits. This bitstream is in the "wv" file +// for pure lossless data or the "wvc" file for hybrid lossless. This data +// would not be used for hybrid lossy mode. There is also a 32-bit CRC stored +// in the first 4 bytes of these blocks. + +static int init_wvx_bitstream(WavpackStream *wps, WavpackMetadata *wpmd) +{ + unsigned char *cp = wpmd->data; + + if(wpmd->byte_length <= 4 || (wpmd->byte_length & 1)) return FALSE; + + wps->crc_wvx = *cp++; + wps->crc_wvx |= (int32_t)*cp++ << 8; + wps->crc_wvx |= (int32_t)*cp++ << 16; + wps->crc_wvx |= (int32_t)*cp++ << 24; + + bs_open_read(&wps->wvxbits, cp, (unsigned char *)wpmd->data + wpmd->byte_length); + return TRUE; +} + +// Read the int32 data from the specified metadata into the specified stream. +// This data is used for integer data that has more than 24 bits of magnitude +// or, in some cases, used to eliminate redundant bits from any audio stream. + +static int read_int32_info(WavpackStream *wps, WavpackMetadata *wpmd) +{ + int bytecnt = wpmd->byte_length; + char *byteptr = wpmd->data; + + if(bytecnt != 4) return FALSE; + + wps->int32_sent_bits = *byteptr++; + wps->int32_zeros = *byteptr++; + wps->int32_ones = *byteptr++; + wps->int32_dups = *byteptr; + + return TRUE; +} + +static int read_float_info(WavpackStream *wps, WavpackMetadata *wpmd) +{ + int bytecnt = wpmd->byte_length; + char *byteptr = wpmd->data; + + if(bytecnt != 4) return FALSE; + + wps->float_flags = *byteptr++; + wps->float_shift = *byteptr++; + wps->float_max_exp = *byteptr++; + wps->float_norm_exp = *byteptr; + return TRUE; +} + +// Read multichannel information from metadata. The first byte is the total +// number of channels and the following bytes represent the channel_mask +// as described for Microsoft WAVEFORMATEX. + +static int read_channel_info(WavpackContext *wpc, WavpackMetadata *wpmd) +{ + int bytecnt = wpmd->byte_length, shift = 0, mask_bits; + unsigned char *byteptr = wpmd->data; + uint32_t mask = 0; + + if(!bytecnt || bytecnt > 7) return FALSE; + + if(!wpc->config.num_channels) + { + + // if bytecnt is 6 or 7 we are using new configuration with "unlimited" streams + + if(bytecnt >= 6) + { + wpc->config.num_channels = (byteptr[0] | ((byteptr[2] & 0xf) << 8)) + 1; + wpc->max_streams = (byteptr[1] | ((byteptr[2] & 0xf0) << 4)) + 1; + + if(wpc->config.num_channels < wpc->max_streams) return FALSE; + + byteptr += 3; + mask = *byteptr++; + mask |= (uint32_t)*byteptr++ << 8; + mask |= (uint32_t)*byteptr++ << 16; + + if(bytecnt == 7) // this was introduced in 5.0 + mask |= (uint32_t)*byteptr << 24; + } + else + { + wpc->config.num_channels = *byteptr++; + + while(--bytecnt) + { + mask |= (uint32_t)*byteptr++ << shift; + shift += 8; + } + } + + if(wpc->config.num_channels > wpc->max_streams * 2) return FALSE; + + wpc->config.channel_mask = mask; + + for(mask_bits = 0; mask; mask >>= 1) + if((mask & 1) && ++mask_bits > wpc->config.num_channels) return FALSE; + } + + return TRUE; +} + +// Read multichannel identity information from metadata. Data is an array of +// unsigned characters representing any channels in the file that DO NOT +// match one the 18 Microsoft standard channels (and are represented in the +// channel mask). A value of 0 is not allowed and 0xff means an unknown or +// undefined channel identity. + +static int read_channel_identities(WavpackContext *wpc, WavpackMetadata *wpmd) +{ + if(!wpc->channel_identities) + { + wpc->channel_identities = malloc(wpmd->byte_length + 1); + memcpy(wpc->channel_identities, wpmd->data, wpmd->byte_length); + wpc->channel_identities[wpmd->byte_length] = 0; + } + + return TRUE; +} + +// Read configuration information from metadata. + +static int read_config_info(WavpackContext *wpc, WavpackMetadata *wpmd) +{ + int bytecnt = wpmd->byte_length; + unsigned char *byteptr = wpmd->data; + + if(bytecnt >= 3) + { + wpc->config.flags &= 0xff; + wpc->config.flags |= (int32_t)*byteptr++ << 8; + wpc->config.flags |= (int32_t)*byteptr++ << 16; + wpc->config.flags |= (int32_t)*byteptr++ << 24; + bytecnt -= 3; + + if(bytecnt && (wpc->config.flags & CONFIG_EXTRA_MODE)) + { + wpc->config.xmode = *byteptr++; + bytecnt--; + } + + // we used an extra config byte here for the 5.0.0 alpha, so still + // honor it now (but this has been replaced with NEW_CONFIG) + + if(bytecnt) + { + wpc->config.qmode = (wpc->config.qmode & ~0xff) | *byteptr; + wpc->version_five = 1; + } + } + + return TRUE; +} + +// Read "new" configuration information from metadata. + +static int read_new_config_info(WavpackContext *wpc, WavpackMetadata *wpmd) +{ + int bytecnt = wpmd->byte_length; + unsigned char *byteptr = wpmd->data; + + wpc->version_five = 1; // just having this block signals version 5.0 + + wpc->file_format = wpc->config.qmode = wpc->channel_layout = 0; + + if(wpc->channel_reordering) + { + free(wpc->channel_reordering); + wpc->channel_reordering = NULL; + } + + // if there's any data, the first two bytes are file_format and qmode flags + + if(bytecnt >= 2) + { + wpc->file_format = *byteptr++; + wpc->config.qmode = (wpc->config.qmode & ~0xff) | *byteptr++; + bytecnt -= 2; + + // another byte indicates a channel layout + + if(bytecnt) + { + int nchans, i; + + wpc->channel_layout = (int32_t)*byteptr++ << 16; + bytecnt--; + + // another byte means we have a channel count for the layout and maybe a reordering + + if(bytecnt) + { + wpc->channel_layout += nchans = *byteptr++; + bytecnt--; + + // any more means there's a reordering string + + if(bytecnt) + { + if(bytecnt > nchans) return FALSE; + + wpc->channel_reordering = malloc(nchans); + + // note that redundant reordering info is not stored, so we fill in the rest + + if(wpc->channel_reordering) + { + for(i = 0; i < nchans; ++i) + if(bytecnt) + { + wpc->channel_reordering[i] = *byteptr++; + + if(wpc->channel_reordering[i] >= nchans) // make sure index is in range + wpc->channel_reordering[i] = 0; + + bytecnt--; + } + else + wpc->channel_reordering[i] = i; + } + } + } + else + wpc->channel_layout += wpc->config.num_channels; + } + } + + return TRUE; +} + +// Read non-standard sampling rate from metadata. + +static int read_sample_rate(WavpackContext *wpc, WavpackMetadata *wpmd) +{ + int bytecnt = wpmd->byte_length; + unsigned char *byteptr = wpmd->data; + + if(bytecnt == 3 || bytecnt == 4) + { + wpc->config.sample_rate = (int32_t)*byteptr++; + wpc->config.sample_rate |= (int32_t)*byteptr++ << 8; + wpc->config.sample_rate |= (int32_t)*byteptr++ << 16; + + // for sampling rates > 16777215 (non-audio probably, or ...) + + if(bytecnt == 4) wpc->config.sample_rate |= (int32_t)(*byteptr & 0x7f) << 24; + } + + return TRUE; +} + +// Read wrapper data from metadata. Currently, this consists of the RIFF +// header and trailer that wav files contain around the audio data but could +// be used for other formats as well. Because WavPack files contain all the +// information required for decoding and playback, this data can probably +// be ignored except when an exact wavefile restoration is needed. + +static int read_wrapper_data(WavpackContext *wpc, WavpackMetadata *wpmd) +{ + if((wpc->open_flags & OPEN_WRAPPER) && wpc->wrapper_bytes < MAX_WRAPPER_BYTES && wpmd->byte_length) + { + wpc->wrapper_data = realloc(wpc->wrapper_data, wpc->wrapper_bytes + wpmd->byte_length); + if(!wpc->wrapper_data) return FALSE; + memcpy(wpc->wrapper_data + wpc->wrapper_bytes, wpmd->data, wpmd->byte_length); + wpc->wrapper_bytes += wpmd->byte_length; + } + + return TRUE; +} + +static int read_metadata_buff(WavpackMetadata *wpmd, unsigned char *blockbuff, unsigned char **buffptr) +{ + WavpackHeader *wphdr = (WavpackHeader *)blockbuff; + unsigned char *buffend = blockbuff + wphdr->ckSize + 8; + + if(buffend - *buffptr < 2) return FALSE; + + wpmd->id = *(*buffptr)++; + wpmd->byte_length = *(*buffptr)++ << 1; + + if(wpmd->id & ID_LARGE) + { + wpmd->id &= ~ID_LARGE; + + if(buffend - *buffptr < 2) return FALSE; + + wpmd->byte_length += *(*buffptr)++ << 9; + wpmd->byte_length += *(*buffptr)++ << 17; + } + + if(wpmd->id & ID_ODD_SIZE) + { + if(!wpmd->byte_length) // odd size and zero length makes no sense + return FALSE; + wpmd->id &= ~ID_ODD_SIZE; + wpmd->byte_length--; + } + + if(wpmd->byte_length) + { + if(buffend - *buffptr < wpmd->byte_length + (wpmd->byte_length & 1)) + { + wpmd->data = NULL; + return FALSE; + } + + wpmd->data = *buffptr; + (*buffptr) += wpmd->byte_length + (wpmd->byte_length & 1); + } + else + wpmd->data = NULL; + + return TRUE; +} + +static int process_metadata(WavpackContext *wpc, WavpackMetadata *wpmd) +{ + WavpackStream *wps = wpc->streams[wpc->current_stream]; + + switch(wpmd->id) + { + case ID_DUMMY: + return TRUE; + + case ID_DECORR_TERMS: + return read_decorr_terms(wps, wpmd); + + case ID_DECORR_WEIGHTS: + return read_decorr_weights(wps, wpmd); + + case ID_DECORR_SAMPLES: + return read_decorr_samples(wps, wpmd); + + case ID_ENTROPY_VARS: + return read_entropy_vars(wps, wpmd); + + case ID_HYBRID_PROFILE: + return read_hybrid_profile(wps, wpmd); + + case ID_SHAPING_WEIGHTS: + return read_shaping_info(wps, wpmd); + + case ID_FLOAT_INFO: + return read_float_info(wps, wpmd); + + case ID_INT32_INFO: + return read_int32_info(wps, wpmd); + + case ID_CHANNEL_INFO: + return read_channel_info(wpc, wpmd); + + case ID_CHANNEL_IDENTITIES: + return read_channel_identities(wpc, wpmd); + + case ID_CONFIG_BLOCK: + return read_config_info(wpc, wpmd); + + case ID_NEW_CONFIG_BLOCK: + return read_new_config_info(wpc, wpmd); + + case ID_SAMPLE_RATE: + return read_sample_rate(wpc, wpmd); + + case ID_WV_BITSTREAM: + return init_wv_bitstream(wps, wpmd); + + case ID_WVC_BITSTREAM: + return init_wvc_bitstream(wps, wpmd); + + case ID_WVX_BITSTREAM: + return init_wvx_bitstream(wps, wpmd); + + case ID_DSD_BLOCK: +#ifdef ENABLE_DSD + return init_dsd_block(wpc, wpmd); +#else + strcpy(wpc->error_message, "not configured to handle DSD WavPack files!"); + return FALSE; +#endif + + case ID_ALT_HEADER: + case ID_ALT_TRAILER: + if(!(wpc->open_flags & OPEN_ALT_TYPES)) return TRUE; + + case ID_RIFF_HEADER: + case ID_RIFF_TRAILER: + return read_wrapper_data(wpc, wpmd); + + case ID_ALT_MD5_CHECKSUM: + if(!(wpc->open_flags & OPEN_ALT_TYPES)) return TRUE; + + case ID_MD5_CHECKSUM: + if(wpmd->byte_length == 16) + { + memcpy(wpc->config.md5_checksum, wpmd->data, 16); + wpc->config.flags |= CONFIG_MD5_CHECKSUM; + wpc->config.md5_read = 1; + } + + return TRUE; + + case ID_ALT_EXTENSION: + if(wpmd->byte_length && wpmd->byte_length < sizeof(wpc->file_extension)) + { + memcpy(wpc->file_extension, wpmd->data, wpmd->byte_length); + wpc->file_extension[wpmd->byte_length] = 0; + } + + return TRUE; + + // we don't actually verify the checksum here (it's done right after the + // block is read), but it's a good indicator of version 5 files + + case ID_BLOCK_CHECKSUM: + wpc->version_five = 1; + return TRUE; + + default: + return (wpmd->id & ID_OPTIONAL_DATA) ? TRUE : FALSE; + } +} + +//////////////////////////////// bitstream management /////////////////////////////// + +// Open the specified BitStream and associate with the specified buffer. + +static void bs_read(Bitstream *bs); + +static void bs_open_read(Bitstream *bs, void *buffer_start, void *buffer_end) +{ + bs->error = bs->sr = bs->bc = 0; + bs->ptr = (bs->buf = buffer_start) - 1; + bs->end = buffer_end; + bs->wrap = bs_read; +} + +// This function is only called from the getbit() and getbits() macros when +// the BitStream has been exhausted and more data is required. Sinve these +// bistreams no longer access files, this function simple sets an error and +// resets the buffer. + +static void bs_read(Bitstream *bs) +{ + bs->ptr = bs->buf; + bs->error = 1; +} + +// This function is called to close the bitstream. It returns the number of +// full bytes actually read as bits. + +uint32_t bs_close_read(Bitstream *bs) +{ + uint32_t bytes_read; + + if(bs->bc < sizeof(*(bs->ptr)) * 8) bs->ptr++; + + bytes_read = (uint32_t)(bs->ptr - bs->buf) * sizeof(*(bs->ptr)); + + if(!(bytes_read & 1)) ++bytes_read; + + CLEAR(*bs); + return bytes_read; +} + +// Normally the trailing wrapper will not be available when a WavPack file is first +// opened for reading because it is stored in the final block of the file. This +// function forces a seek to the end of the file to pick up any trailing wrapper +// stored there (then use WavPackGetWrapper**() to obtain). This can obviously only +// be used for seekable files (not pipes) and is not available for pre-4.0 WavPack +// files. + +void WavpackSeekTrailingWrapper(WavpackContext *wpc) +{ + if((wpc->open_flags & OPEN_WRAPPER) && wpc->reader->can_seek(wpc->wv_in) && !wpc->stream3) + seek_eof_information(wpc, NULL, TRUE); +} + +// Get any MD5 checksum stored in the metadata (should be called after reading +// last sample or an extra seek will occur). A return value of FALSE indicates +// that no MD5 checksum was stored. + +int WavpackGetMD5Sum(WavpackContext *wpc, unsigned char data[16]) +{ + if(wpc->config.flags & CONFIG_MD5_CHECKSUM) + { + if(!wpc->config.md5_read && wpc->reader->can_seek(wpc->wv_in)) seek_eof_information(wpc, NULL, FALSE); + + if(wpc->config.md5_read) + { + memcpy(data, wpc->config.md5_checksum, 16); + return TRUE; + } + } + + return FALSE; +} + +// Read from current file position until a valid 32-byte WavPack 4.0 header is +// found and read into the specified pointer. The number of bytes skipped is +// returned. If no WavPack header is found within 1 meg, then a -1 is returned +// to indicate the error. No additional bytes are read past the header and it +// is returned in the processor's native endian mode. Seeking is not required. + +uint32_t read_next_header(WavpackStreamReader64 *reader, void *id, WavpackHeader *wphdr) +{ + unsigned char buffer[sizeof(*wphdr)], *sp = buffer + sizeof(*wphdr), *ep = sp; + uint32_t bytes_skipped = 0; + int bleft; + + while(1) + { + if(sp < ep) + { + bleft = (int)(ep - sp); + memmove(buffer, sp, bleft); + } + else + bleft = 0; + + if(reader->read_bytes(id, buffer + bleft, sizeof(*wphdr) - bleft) != sizeof(*wphdr) - bleft) return -1; + + sp = buffer; + + if(*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' && !(*++sp & 1) && sp[2] < 16 && !sp[3] && + (sp[2] || sp[1] || *sp >= 24) && sp[5] == 4 && sp[4] >= (MIN_STREAM_VERS & 0xff) && + sp[4] <= (MAX_STREAM_VERS & 0xff) && sp[18] < 3 && !sp[19]) + { + memcpy(wphdr, buffer, sizeof(*wphdr)); + WavpackLittleEndianToNative(wphdr, WavpackHeaderFormat); + return bytes_skipped; + } + + while(sp < ep && *sp != 'w') sp++; + + if((bytes_skipped += (uint32_t)(sp - buffer)) > 1024 * 1024) return -1; + } +} + +// Compare the regular wv file block header to a potential matching wvc +// file block header and return action code based on analysis: +// +// 0 = use wvc block (assuming rest of block is readable) +// 1 = bad match; try to read next wvc block +// -1 = bad match; ignore wvc file for this block and backup fp (if +// possible) and try to use this block next time + +static int match_wvc_header(WavpackHeader *wv_hdr, WavpackHeader *wvc_hdr) +{ + if(GET_BLOCK_INDEX(*wv_hdr) == GET_BLOCK_INDEX(*wvc_hdr) && wv_hdr->block_samples == wvc_hdr->block_samples) + { + int wvi = 0, wvci = 0; + + if(wv_hdr->flags == wvc_hdr->flags) return 0; + + if(wv_hdr->flags & INITIAL_BLOCK) wvi -= 1; + + if(wv_hdr->flags & FINAL_BLOCK) wvi += 1; + + if(wvc_hdr->flags & INITIAL_BLOCK) wvci -= 1; + + if(wvc_hdr->flags & FINAL_BLOCK) wvci += 1; + + return (wvci - wvi < 0) ? 1 : -1; + } + + if(((GET_BLOCK_INDEX(*wvc_hdr) - GET_BLOCK_INDEX(*wv_hdr)) << 24) < 0) + return 1; + else + return -1; +} + +// Read the wvc block that matches the regular wv block that has been +// read for the current stream. If an exact match is not found then +// we either keep reading or back up and (possibly) use the block +// later. The skip_wvc flag is set if not matching wvc block is found +// so that we can still decode using only the lossy version (although +// we flag this as an error). A return of FALSE indicates a serious +// error (not just that we missed one wvc block). + +int read_wvc_block(WavpackContext *wpc) +{ + WavpackStream *wps = wpc->streams[wpc->current_stream]; + int64_t bcount, file2pos; + WavpackHeader orig_wphdr; + WavpackHeader wphdr; + int compare_result; + + while(1) + { + file2pos = wpc->reader->get_pos(wpc->wvc_in); + bcount = read_next_header(wpc->reader, wpc->wvc_in, &wphdr); + + if(bcount == (uint32_t)-1) + { + wps->wvc_skip = TRUE; + wpc->crc_errors++; + return FALSE; + } + + memcpy(&orig_wphdr, &wphdr, 32); // save original header for verify step + + if(wpc->open_flags & OPEN_STREAMING) + SET_BLOCK_INDEX(wphdr, wps->sample_index = 0); + else + SET_BLOCK_INDEX(wphdr, GET_BLOCK_INDEX(wphdr) - wpc->initial_index); + + if(wphdr.flags & INITIAL_BLOCK) wpc->file2pos = file2pos + bcount; + + compare_result = match_wvc_header(&wps->wphdr, &wphdr); + + if(!compare_result) + { + wps->block2buff = malloc(wphdr.ckSize + 8); + if(!wps->block2buff) return FALSE; + + if(wpc->reader->read_bytes(wpc->wvc_in, wps->block2buff + 32, wphdr.ckSize - 24) != wphdr.ckSize - 24) + { + free(wps->block2buff); + wps->block2buff = NULL; + wps->wvc_skip = TRUE; + wpc->crc_errors++; + return FALSE; + } + + memcpy(wps->block2buff, &orig_wphdr, 32); + + // don't use corrupt blocks + if(!WavpackVerifySingleBlock(wps->block2buff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) + { + free(wps->block2buff); + wps->block2buff = NULL; + wps->wvc_skip = TRUE; + wpc->crc_errors++; + return TRUE; + } + + wps->wvc_skip = FALSE; + memcpy(wps->block2buff, &wphdr, 32); + memcpy(&wps->wphdr, &wphdr, 32); + return TRUE; + } + else if(compare_result == -1) + { + wps->wvc_skip = TRUE; + wpc->reader->set_pos_rel(wpc->wvc_in, -32, SEEK_CUR); + wpc->crc_errors++; + return TRUE; + } + } +} + +// This function is used to seek to end of a file to obtain certain information +// that is stored there at the file creation time because it is not known at +// the start. This includes the MD5 sum and and trailing part of the file +// wrapper, and in some rare cases may include the total number of samples in +// the file (although we usually try to back up and write that at the front of +// the file). Note this function restores the file position to its original +// location (and obviously requires a seekable file). The normal return value +// is TRUE indicating no errors, although this does not actually mean that any +// information was retrieved. An error return of FALSE usually means the file +// terminated unexpectedly. Note that this could be used to get all three +// types of information in one go, but it's not actually used that way now. + +static int seek_eof_information(WavpackContext *wpc, int64_t *final_index, int get_wrapper) +{ + int64_t restore_pos, last_pos = -1; + WavpackStreamReader64 *reader = wpc->reader; + int alt_types = wpc->open_flags & OPEN_ALT_TYPES; + uint32_t blocks = 0, audio_blocks = 0; + void *id = wpc->wv_in; + WavpackHeader wphdr; + + restore_pos = reader->get_pos(id); // we restore file position when done + + // start 1MB from the end-of-file, or from the start if the file is not that big + + if(reader->get_length(id) > (int64_t)1048576) + reader->set_pos_rel(id, -1048576, SEEK_END); + else + reader->set_pos_abs(id, 0); + + // Note that we go backward (without parsing inside blocks) until we find a block + // with audio (careful to not get stuck in a loop). Only then do we go forward + // parsing all blocks in their entirety. + + while(1) + { + uint32_t bcount = read_next_header(reader, id, &wphdr); + int64_t current_pos = reader->get_pos(id); + + // if we just got to the same place as last time, we're stuck and need to give up + + if(current_pos == last_pos) + { + reader->set_pos_abs(id, restore_pos); + return FALSE; + } + + last_pos = current_pos; + + // We enter here if we just read 1 MB without seeing any WavPack block headers. + // Since WavPack blocks are < 1 MB, that means we're in a big APE tag, or we got + // to the end-of-file. + + if(bcount == (uint32_t)-1) + { + + // if we have not seen any blocks at all yet, back up almost 2 MB (or to the + // beginning of the file) and try again + + if(!blocks) + { + if(current_pos > (int64_t)2000000) + reader->set_pos_rel(id, -2000000, SEEK_CUR); + else + reader->set_pos_abs(id, 0); + + continue; + } + + // if we have seen WavPack blocks, then this means we've done all we can do here + + reader->set_pos_abs(id, restore_pos); + return TRUE; + } + + blocks++; + + // If the block has audio samples, calculate a final index, although this is not + // final since this may not be the last block with audio. On the other hand, if + // this block does not have audio, and we haven't seen one with audio, we have + // to go back some more. + + if(wphdr.block_samples) + { + if(final_index) *final_index = GET_BLOCK_INDEX(wphdr) + wphdr.block_samples; + + audio_blocks++; + } + else if(!audio_blocks) + { + if(current_pos > (int64_t)1048576) + reader->set_pos_rel(id, -1048576, SEEK_CUR); + else + reader->set_pos_abs(id, 0); + + continue; + } + + // at this point we have seen at least one block with audio, so we parse the + // entire block looking for MD5 metadata or (conditionally) trailing wrappers + + bcount = wphdr.ckSize - sizeof(WavpackHeader) + 8; + + while(bcount >= 2) + { + unsigned char meta_id, c1, c2; + uint32_t meta_bc, meta_size; + + if(reader->read_bytes(id, &meta_id, 1) != 1 || reader->read_bytes(id, &c1, 1) != 1) + { + reader->set_pos_abs(id, restore_pos); + return FALSE; + } + + meta_bc = c1 << 1; + bcount -= 2; + + if(meta_id & ID_LARGE) + { + if(bcount < 2 || reader->read_bytes(id, &c1, 1) != 1 || reader->read_bytes(id, &c2, 1) != 1) + { + reader->set_pos_abs(id, restore_pos); + return FALSE; + } + + meta_bc += ((uint32_t)c1 << 9) + ((uint32_t)c2 << 17); + bcount -= 2; + } + + meta_size = (meta_id & ID_ODD_SIZE) ? meta_bc - 1 : meta_bc; + meta_id &= ID_UNIQUE; + + if(get_wrapper && (meta_id == ID_RIFF_TRAILER || (alt_types && meta_id == ID_ALT_TRAILER)) && meta_bc) + { + wpc->wrapper_data = realloc(wpc->wrapper_data, wpc->wrapper_bytes + meta_bc); + + if(!wpc->wrapper_data) + { + reader->set_pos_abs(id, restore_pos); + return FALSE; + } + + if(reader->read_bytes(id, wpc->wrapper_data + wpc->wrapper_bytes, meta_bc) == meta_bc) + wpc->wrapper_bytes += meta_size; + else + { + reader->set_pos_abs(id, restore_pos); + return FALSE; + } + } + else if(meta_id == ID_MD5_CHECKSUM || (alt_types && meta_id == ID_ALT_MD5_CHECKSUM)) + { + if(meta_bc == 16 && bcount >= 16) + { + if(reader->read_bytes(id, wpc->config.md5_checksum, 16) == 16) + wpc->config.md5_read = TRUE; + else + { + reader->set_pos_abs(id, restore_pos); + return FALSE; + } + } + else + reader->set_pos_rel(id, meta_bc, SEEK_CUR); + } + else + reader->set_pos_rel(id, meta_bc, SEEK_CUR); + + bcount -= meta_bc; + } + } +} + +// Quickly verify the referenced block. It is assumed that the WavPack header has been converted +// to native endian format. If a block checksum is performed, that is done in little-endian +// (file) format. It is also assumed that the caller has made sure that the block length +// indicated in the header is correct (we won't overflow the buffer). If a checksum is present, +// then it is checked, otherwise we just check that all the metadata blocks are formatted +// correctly (without looking at their contents). Returns FALSE for bad block. + +int WavpackVerifySingleBlock(unsigned char *buffer, int verify_checksum) +{ + WavpackHeader *wphdr = (WavpackHeader *)buffer; + uint32_t checksum_passed = 0, bcount, meta_bc; + unsigned char *dp, meta_id, c1, c2; + + if(strncmp(wphdr->ckID, "wvpk", 4) || wphdr->ckSize + 8 < sizeof(WavpackHeader)) return FALSE; + + bcount = wphdr->ckSize - sizeof(WavpackHeader) + 8; + dp = (unsigned char *)(wphdr + 1); + + while(bcount >= 2) + { + meta_id = *dp++; + c1 = *dp++; + + meta_bc = c1 << 1; + bcount -= 2; + + if(meta_id & ID_LARGE) + { + if(bcount < 2) return FALSE; + + c1 = *dp++; + c2 = *dp++; + meta_bc += ((uint32_t)c1 << 9) + ((uint32_t)c2 << 17); + bcount -= 2; + } + + if(bcount < meta_bc) return FALSE; + + if(verify_checksum && (meta_id & ID_UNIQUE) == ID_BLOCK_CHECKSUM) + { +#ifdef BITSTREAM_SHORTS + uint16_t *csptr = (uint16_t *)buffer; +#else + unsigned char *csptr = buffer; +#endif + int wcount = (int)(dp - 2 - buffer) >> 1; + uint32_t csum = (uint32_t)-1; + + if((meta_id & ID_ODD_SIZE) || meta_bc < 2 || meta_bc > 4) return FALSE; + +#ifdef BITSTREAM_SHORTS + while(wcount--) csum = (csum * 3) + *csptr++; +#else + WavpackNativeToLittleEndian((WavpackHeader *)buffer, WavpackHeaderFormat); + + while(wcount--) + { + csum = (csum * 3) + csptr[0] + (csptr[1] << 8); + csptr += 2; + } + + WavpackLittleEndianToNative((WavpackHeader *)buffer, WavpackHeaderFormat); +#endif + + if(meta_bc == 4) + { + if(*dp++ != (csum & 0xff) || *dp++ != ((csum >> 8) & 0xff) || *dp++ != ((csum >> 16) & 0xff) || + *dp++ != ((csum >> 24) & 0xff)) + return FALSE; + } + else + { + csum ^= csum >> 16; + + if(*dp++ != (csum & 0xff) || *dp++ != ((csum >> 8) & 0xff)) return FALSE; + } + + checksum_passed++; + } + + bcount -= meta_bc; + dp += meta_bc; + } + + return (bcount == 0) && (!verify_checksum || !(wphdr->flags & HAS_CHECKSUM) || checksum_passed); +} diff --git a/wavpack/read_words.c b/wavpack/read_words.c new file mode 100644 index 0000000..a4b1583 --- /dev/null +++ b/wavpack/read_words.c @@ -0,0 +1,637 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// read_words.c + +// This module provides entropy word decoding functions using +// a variation on the Rice method. This was introduced in version 3.93 +// because it allows splitting the data into a "lossy" stream and a +// "correction" stream in a very efficient manner and is therefore ideal +// for the "hybrid" mode. For 4.0, the efficiency of this method was +// significantly improved by moving away from the normal Rice restriction of +// using powers of two for the modulus divisions and now the method can be +// used for both hybrid and pure lossless encoding. + +// Samples are divided by median probabilities at 5/7 (71.43%), 10/49 (20.41%), +// and 20/343 (5.83%). Each zone has 3.5 times fewer samples than the +// previous. Using standard Rice coding on this data would result in 1.4 +// bits per sample average (not counting sign bit). However, there is a +// very simple encoding that is over 99% efficient with this data and +// results in about 1.22 bits per sample. + +#include +#include + +#include "wavpack_local.h" + +#if defined(HAVE___BUILTIN_CTZ) || defined(_WIN64) +#define USE_CTZ_OPTIMIZATION // use ctz intrinsic (or Windows equivalent) to count trailing ones +#else +#define USE_NEXT8_OPTIMIZATION // optimization using a table to count trailing ones +#endif + +#define USE_BITMASK_TABLES // use tables instead of shifting for certain masking operations + +///////////////////////////// local table storage //////////////////////////// + +#ifdef USE_NEXT8_OPTIMIZATION +static const char ones_count_table[] = { + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, + 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, + 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, + 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, + 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 8}; +#endif + +///////////////////////////// executable code //////////////////////////////// + +static uint32_t __inline read_code(Bitstream *bs, uint32_t maxcode); + +// Read the next word from the bitstream "wvbits" and return the value. This +// function can be used for hybrid or lossless streams, but since an +// optimized version is available for lossless this function would normally +// be used for hybrid only. If a hybrid lossless stream is being read then +// the "correction" offset is written at the specified pointer. A return value +// of WORD_EOF indicates that the end of the bitstream was reached (all 1s) or +// some other error occurred. + +int32_t FASTCALL get_word(WavpackStream *wps, int chan, int32_t *correction) +{ + register struct entropy_data *c = wps->w.c + chan; + uint32_t ones_count, low, mid, high; + int32_t value; + int sign; + + if(!wps->wvbits.ptr) return WORD_EOF; + + if(correction) *correction = 0; + + if(!(wps->w.c[0].median[0] & ~1) && !wps->w.holding_zero && !wps->w.holding_one && !(wps->w.c[1].median[0] & ~1)) + { + uint32_t mask; + int cbits; + + if(wps->w.zeros_acc) + { + if(--wps->w.zeros_acc) + { + c->slow_level -= (c->slow_level + SLO) >> SLS; + return 0; + } + } + else + { + for(cbits = 0; cbits < 33 && getbit(&wps->wvbits); ++cbits); + + if(cbits == 33) return WORD_EOF; + + if(cbits < 2) + wps->w.zeros_acc = cbits; + else + { + for(mask = 1, wps->w.zeros_acc = 0; --cbits; mask <<= 1) + if(getbit(&wps->wvbits)) wps->w.zeros_acc |= mask; + + wps->w.zeros_acc |= mask; + } + + if(wps->w.zeros_acc) + { + c->slow_level -= (c->slow_level + SLO) >> SLS; + CLEAR(wps->w.c[0].median); + CLEAR(wps->w.c[1].median); + return 0; + } + } + } + + if(wps->w.holding_zero) + ones_count = wps->w.holding_zero = 0; + else + { +#ifdef USE_CTZ_OPTIMIZATION + while(wps->wvbits.bc < LIMIT_ONES) + { + if(++(wps->wvbits.ptr) == wps->wvbits.end) wps->wvbits.wrap(&wps->wvbits); + + wps->wvbits.sr |= *(wps->wvbits.ptr) << wps->wvbits.bc; + wps->wvbits.bc += sizeof(*(wps->wvbits.ptr)) * 8; + } + +#ifdef _WIN32 + _BitScanForward(&ones_count, ~wps->wvbits.sr); +#else + ones_count = __builtin_ctz(~wps->wvbits.sr); +#endif + + if(ones_count >= LIMIT_ONES) + { + wps->wvbits.bc -= ones_count; + wps->wvbits.sr >>= ones_count; + + for(; ones_count < (LIMIT_ONES + 1) && getbit(&wps->wvbits); ++ones_count); + + if(ones_count == (LIMIT_ONES + 1)) return WORD_EOF; + + if(ones_count == LIMIT_ONES) + { + uint32_t mask; + int cbits; + + for(cbits = 0; cbits < 33 && getbit(&wps->wvbits); ++cbits); + + if(cbits == 33) return WORD_EOF; + + if(cbits < 2) + ones_count = cbits; + else + { + for(mask = 1, ones_count = 0; --cbits; mask <<= 1) + if(getbit(&wps->wvbits)) ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } + } + else + { + wps->wvbits.bc -= ones_count + 1; + wps->wvbits.sr >>= ones_count + 1; + } +#elif defined(USE_NEXT8_OPTIMIZATION) + int next8; + + if(wps->wvbits.bc < 8) + { + if(++(wps->wvbits.ptr) == wps->wvbits.end) wps->wvbits.wrap(&wps->wvbits); + + next8 = (wps->wvbits.sr |= *(wps->wvbits.ptr) << wps->wvbits.bc) & 0xff; + wps->wvbits.bc += sizeof(*(wps->wvbits.ptr)) * 8; + } + else + next8 = wps->wvbits.sr & 0xff; + + if(next8 == 0xff) + { + wps->wvbits.bc -= 8; + wps->wvbits.sr >>= 8; + + for(ones_count = 8; ones_count < (LIMIT_ONES + 1) && getbit(&wps->wvbits); ++ones_count); + + if(ones_count == (LIMIT_ONES + 1)) return WORD_EOF; + + if(ones_count == LIMIT_ONES) + { + uint32_t mask; + int cbits; + + for(cbits = 0; cbits < 33 && getbit(&wps->wvbits); ++cbits); + + if(cbits == 33) return WORD_EOF; + + if(cbits < 2) + ones_count = cbits; + else + { + for(mask = 1, ones_count = 0; --cbits; mask <<= 1) + if(getbit(&wps->wvbits)) ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } + } + else + { + wps->wvbits.bc -= (ones_count = ones_count_table[next8]) + 1; + wps->wvbits.sr >>= ones_count + 1; + } +#else + for(ones_count = 0; ones_count < (LIMIT_ONES + 1) && getbit(&wps->wvbits); ++ones_count); + + if(ones_count >= LIMIT_ONES) + { + uint32_t mask; + int cbits; + + if(ones_count == (LIMIT_ONES + 1)) return WORD_EOF; + + for(cbits = 0; cbits < 33 && getbit(&wps->wvbits); ++cbits); + + if(cbits == 33) return WORD_EOF; + + if(cbits < 2) + ones_count = cbits; + else + { + for(mask = 1, ones_count = 0; --cbits; mask <<= 1) + if(getbit(&wps->wvbits)) ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } +#endif + + if(wps->w.holding_one) + { + wps->w.holding_one = ones_count & 1; + ones_count = (ones_count >> 1) + 1; + } + else + { + wps->w.holding_one = ones_count & 1; + ones_count >>= 1; + } + + wps->w.holding_zero = ~wps->w.holding_one & 1; + } + + if((wps->wphdr.flags & HYBRID_FLAG) && !chan) update_error_limit(wps); + + if(ones_count == 0) + { + low = 0; + high = GET_MED(0) - 1; + DEC_MED0(); + } + else + { + low = GET_MED(0); + INC_MED0(); + + if(ones_count == 1) + { + high = low + GET_MED(1) - 1; + DEC_MED1(); + } + else + { + low += GET_MED(1); + INC_MED1(); + + if(ones_count == 2) + { + high = low + GET_MED(2) - 1; + DEC_MED2(); + } + else + { + low += (ones_count - 2) * GET_MED(2); + high = low + GET_MED(2) - 1; + INC_MED2(); + } + } + } + + low &= 0x7fffffff; + high &= 0x7fffffff; + + if(low > high) // make sure high and low make sense + high = low; + + mid = (high + low + 1) >> 1; + + if(!c->error_limit) + mid = read_code(&wps->wvbits, high - low) + low; + else + while(high - low > c->error_limit) + { + if(getbit(&wps->wvbits)) + mid = (high + (low = mid) + 1) >> 1; + else + mid = ((high = mid - 1) + low + 1) >> 1; + } + + sign = getbit(&wps->wvbits); + + if(bs_is_open(&wps->wvcbits) && c->error_limit) + { + value = read_code(&wps->wvcbits, high - low) + low; + + if(correction) *correction = sign ? (mid - value) : (value - mid); + } + + if(wps->wphdr.flags & HYBRID_BITRATE) + { + c->slow_level -= (c->slow_level + SLO) >> SLS; + c->slow_level += wp_log2(mid); + } + + return sign ? ~mid : mid; +} + +// This is an optimized version of get_word() that is used for lossless only +// (error_limit == 0). Also, rather than obtaining a single sample, it can be +// used to obtain an entire buffer of either mono or stereo samples. + +int32_t get_words_lossless(WavpackStream *wps, int32_t *buffer, int32_t nsamples) +{ + struct entropy_data *c = wps->w.c; + uint32_t ones_count, low, high; + Bitstream *bs = &wps->wvbits; + int32_t csamples; +#ifdef USE_NEXT8_OPTIMIZATION + int32_t next8; +#endif + + if(nsamples && !bs->ptr) + { + memset(buffer, 0, (wps->wphdr.flags & MONO_DATA) ? nsamples * 4 : nsamples * 8); + return nsamples; + } + + if(!(wps->wphdr.flags & MONO_DATA)) nsamples *= 2; + + for(csamples = 0; csamples < nsamples; ++csamples) + { + if(!(wps->wphdr.flags & MONO_DATA)) c = wps->w.c + (csamples & 1); + + if(wps->w.holding_zero) + { + wps->w.holding_zero = 0; + low = read_code(bs, GET_MED(0) - 1); + DEC_MED0(); + buffer[csamples] = (getbit(bs)) ? ~low : low; + + if(++csamples == nsamples) break; + + if(!(wps->wphdr.flags & MONO_DATA)) c = wps->w.c + (csamples & 1); + } + + if(wps->w.c[0].median[0] < 2 && !wps->w.holding_one && wps->w.c[1].median[0] < 2) + { + uint32_t mask; + int cbits; + + if(wps->w.zeros_acc) + { + if(--wps->w.zeros_acc) + { + buffer[csamples] = 0; + continue; + } + } + else + { + for(cbits = 0; cbits < 33 && getbit(bs); ++cbits); + + if(cbits == 33) break; + + if(cbits < 2) + wps->w.zeros_acc = cbits; + else + { + for(mask = 1, wps->w.zeros_acc = 0; --cbits; mask <<= 1) + if(getbit(bs)) wps->w.zeros_acc |= mask; + + wps->w.zeros_acc |= mask; + } + + if(wps->w.zeros_acc) + { + CLEAR(wps->w.c[0].median); + CLEAR(wps->w.c[1].median); + buffer[csamples] = 0; + continue; + } + } + } + +#ifdef USE_CTZ_OPTIMIZATION + while(bs->bc < LIMIT_ONES) + { + if(++(bs->ptr) == bs->end) bs->wrap(bs); + + bs->sr |= *(bs->ptr) << bs->bc; + bs->bc += sizeof(*(bs->ptr)) * 8; + } + +#ifdef _WIN32 + _BitScanForward(&ones_count, ~wps->wvbits.sr); +#else + ones_count = __builtin_ctz(~wps->wvbits.sr); +#endif + + if(ones_count >= LIMIT_ONES) + { + bs->bc -= ones_count; + bs->sr >>= ones_count; + + for(; ones_count < (LIMIT_ONES + 1) && getbit(bs); ++ones_count); + + if(ones_count == (LIMIT_ONES + 1)) break; + + if(ones_count == LIMIT_ONES) + { + uint32_t mask; + int cbits; + + for(cbits = 0; cbits < 33 && getbit(bs); ++cbits); + + if(cbits == 33) break; + + if(cbits < 2) + ones_count = cbits; + else + { + for(mask = 1, ones_count = 0; --cbits; mask <<= 1) + if(getbit(bs)) ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } + } + else + { + bs->bc -= ones_count + 1; + bs->sr >>= ones_count + 1; + } +#elif defined(USE_NEXT8_OPTIMIZATION) + if(bs->bc < 8) + { + if(++(bs->ptr) == bs->end) bs->wrap(bs); + + next8 = (bs->sr |= *(bs->ptr) << bs->bc) & 0xff; + bs->bc += sizeof(*(bs->ptr)) * 8; + } + else + next8 = bs->sr & 0xff; + + if(next8 == 0xff) + { + bs->bc -= 8; + bs->sr >>= 8; + + for(ones_count = 8; ones_count < (LIMIT_ONES + 1) && getbit(bs); ++ones_count); + + if(ones_count == (LIMIT_ONES + 1)) break; + + if(ones_count == LIMIT_ONES) + { + uint32_t mask; + int cbits; + + for(cbits = 0; cbits < 33 && getbit(bs); ++cbits); + + if(cbits == 33) break; + + if(cbits < 2) + ones_count = cbits; + else + { + for(mask = 1, ones_count = 0; --cbits; mask <<= 1) + if(getbit(bs)) ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } + } + else + { + bs->bc -= (ones_count = ones_count_table[next8]) + 1; + bs->sr >>= ones_count + 1; + } +#else + for(ones_count = 0; ones_count < (LIMIT_ONES + 1) && getbit(bs); ++ones_count); + + if(ones_count >= LIMIT_ONES) + { + uint32_t mask; + int cbits; + + if(ones_count == (LIMIT_ONES + 1)) break; + + for(cbits = 0; cbits < 33 && getbit(bs); ++cbits); + + if(cbits == 33) break; + + if(cbits < 2) + ones_count = cbits; + else + { + for(mask = 1, ones_count = 0; --cbits; mask <<= 1) + if(getbit(bs)) ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } +#endif + + low = wps->w.holding_one; + wps->w.holding_one = ones_count & 1; + wps->w.holding_zero = ~ones_count & 1; + ones_count = (ones_count >> 1) + low; + + if(ones_count == 0) + { + low = 0; + high = GET_MED(0) - 1; + DEC_MED0(); + } + else + { + low = GET_MED(0); + INC_MED0(); + + if(ones_count == 1) + { + high = low + GET_MED(1) - 1; + DEC_MED1(); + } + else + { + low += GET_MED(1); + INC_MED1(); + + if(ones_count == 2) + { + high = low + GET_MED(2) - 1; + DEC_MED2(); + } + else + { + low += (ones_count - 2) * GET_MED(2); + high = low + GET_MED(2) - 1; + INC_MED2(); + } + } + } + + low += read_code(bs, high - low); + buffer[csamples] = (getbit(bs)) ? ~low : low; + } + + return (wps->wphdr.flags & MONO_DATA) ? csamples : (csamples / 2); +} + +// Read a single unsigned value from the specified bitstream with a value +// from 0 to maxcode. If there are exactly a power of two number of possible +// codes then this will read a fixed number of bits; otherwise it reads the +// minimum number of bits and then determines whether another bit is needed +// to define the code. + +static uint32_t __inline read_code(Bitstream *bs, uint32_t maxcode) +{ + unsigned long local_sr; + uint32_t extras, code; + int bitcount; + + if(maxcode < 2) return maxcode ? getbit(bs) : 0; + + bitcount = count_bits(maxcode); +#ifdef USE_BITMASK_TABLES + extras = bitset[bitcount] - maxcode - 1; +#else + extras = (1 << bitcount) - maxcode - 1; +#endif + + local_sr = bs->sr; + + while(bs->bc < bitcount) + { + if(++(bs->ptr) == bs->end) bs->wrap(bs); + + local_sr |= (long)*(bs->ptr) << bs->bc; + bs->bc += sizeof(*(bs->ptr)) * 8; + } + +#ifdef USE_BITMASK_TABLES + if((code = local_sr & bitmask[bitcount - 1]) >= extras) +#else + if((code = local_sr & ((1 << (bitcount - 1)) - 1)) >= extras) +#endif + code = (code << 1) - extras + ((local_sr >> (bitcount - 1)) & 1); + else + bitcount--; + + if(sizeof(local_sr) < 8 && bs->bc > sizeof(local_sr) * 8) + { + bs->bc -= bitcount; + bs->sr = *(bs->ptr) >> (sizeof(*(bs->ptr)) * 8 - bs->bc); + } + else + { + bs->bc -= bitcount; + bs->sr = local_sr >> bitcount; + } + + return code; +} diff --git a/wavpack/tags.c b/wavpack/tags.c new file mode 100644 index 0000000..d0100ad --- /dev/null +++ b/wavpack/tags.c @@ -0,0 +1,189 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// tags.c + +// This module provides support for reading metadata tags (either ID3v1 or +// APEv2) from WavPack files. No actual creation or manipulation of the tags +// is done in this module; this is just internal code to load the tags into +// memory. The high-level API functions are in the tag_utils.c module. + +#include +#include + +#include "wavpack_local.h" + +// This function attempts to load an ID3v1 or APEv2 tag from the specified +// file into the specified M_Tag structure. The ID3 tag fits in completely, +// but an APEv2 tag is variable length and so space must be allocated here +// to accomodate the data, and this will need to be freed later. A return +// value of TRUE indicates a valid tag was found and loaded. Note that the +// file pointer is undefined when this function exits. + +int load_tag(WavpackContext *wpc) +{ + int ape_tag_length, ape_tag_items; + M_Tag *m_tag = &wpc->m_tag; + + CLEAR(*m_tag); + + // This is a loop because we can try up to three times to look for an APEv2 tag. In order, we look: + // + // 1. At the end of the file for a APEv2 footer (this is the preferred location) + // 2. If there's instead an ID3v1 tag at the end of the file, try looking for an APEv2 footer right before that + // 3. If all else fails, look for an APEv2 header the the beginning of the file (use is strongly discouraged) + + while(1) + { + + // seek based on specific location that we are looking for tag (see above list) + + if(m_tag->tag_begins_file) // case #3 + wpc->reader->set_pos_abs(wpc->wv_in, 0); + else if(m_tag->id3_tag.tag_id[0] == 'T') // case #2 + wpc->reader->set_pos_rel(wpc->wv_in, -(int32_t)(sizeof(APE_Tag_Hdr) + sizeof(ID3_Tag)), SEEK_END); + else // case #1 + wpc->reader->set_pos_rel(wpc->wv_in, -(int32_t)sizeof(APE_Tag_Hdr), SEEK_END); + + // read a possible APEv2 tag header/footer and see if there's one there... + + if(wpc->reader->read_bytes(wpc->wv_in, &m_tag->ape_tag_hdr, sizeof(APE_Tag_Hdr)) == sizeof(APE_Tag_Hdr) && + !strncmp(m_tag->ape_tag_hdr.ID, "APETAGEX", 8)) + { + + WavpackLittleEndianToNative(&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format); + + if(m_tag->ape_tag_hdr.version == 2000 && m_tag->ape_tag_hdr.item_count && + m_tag->ape_tag_hdr.length > sizeof(m_tag->ape_tag_hdr) && + m_tag->ape_tag_hdr.length <= APE_TAG_MAX_LENGTH && + (m_tag->ape_tag_data = malloc(m_tag->ape_tag_hdr.length)) != NULL) + { + + ape_tag_items = m_tag->ape_tag_hdr.item_count; + ape_tag_length = m_tag->ape_tag_hdr.length; + + // If this is a APEv2 footer (which is normal if we are searching at the end of the file)... + + if(!(m_tag->ape_tag_hdr.flags & APE_TAG_THIS_IS_HEADER)) + { + + if(m_tag->id3_tag.tag_id[0] == 'T') + m_tag->tag_file_pos = -(int32_t)sizeof(ID3_Tag); + else + m_tag->tag_file_pos = 0; + + m_tag->tag_file_pos -= ape_tag_length; + + // if the footer claims there is a header present also, we will read that and use it + // instead of the footer (after verifying it, of course) for enhanced robustness + + if(m_tag->ape_tag_hdr.flags & APE_TAG_CONTAINS_HEADER) m_tag->tag_file_pos -= sizeof(APE_Tag_Hdr); + + wpc->reader->set_pos_rel(wpc->wv_in, m_tag->tag_file_pos, SEEK_END); + + if(m_tag->ape_tag_hdr.flags & APE_TAG_CONTAINS_HEADER) + { + if(wpc->reader->read_bytes(wpc->wv_in, &m_tag->ape_tag_hdr, sizeof(APE_Tag_Hdr)) != + sizeof(APE_Tag_Hdr) || + strncmp(m_tag->ape_tag_hdr.ID, "APETAGEX", 8)) + { + free(m_tag->ape_tag_data); + CLEAR(*m_tag); + return FALSE; // something's wrong... + } + + WavpackLittleEndianToNative(&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format); + + if(m_tag->ape_tag_hdr.version != 2000 || m_tag->ape_tag_hdr.item_count != ape_tag_items || + m_tag->ape_tag_hdr.length != ape_tag_length) + { + free(m_tag->ape_tag_data); + CLEAR(*m_tag); + return FALSE; // something's wrong... + } + } + } + + if(wpc->reader->read_bytes(wpc->wv_in, m_tag->ape_tag_data, ape_tag_length - sizeof(APE_Tag_Hdr)) != + ape_tag_length - sizeof(APE_Tag_Hdr)) + { + free(m_tag->ape_tag_data); + CLEAR(*m_tag); + return FALSE; // something's wrong... + } + else + { + CLEAR(m_tag->id3_tag); // ignore ID3v1 tag if we found APEv2 tag + return TRUE; + } + } + } + + // we come here if the search for the APEv2 tag failed (otherwise we would have returned with it) + + if(m_tag->id3_tag.tag_id[0] == 'T') + { // settle for the ID3v1 tag that we found + CLEAR(m_tag->ape_tag_hdr); + return TRUE; + } + + // if this was the search for the APEv2 tag at the beginning of the file (which is our + // last resort) then we have nothing, so return failure + + if(m_tag->tag_begins_file) + { + CLEAR(*m_tag); + return FALSE; + } + + // If we get here, then we have failed the first APEv2 tag search (at end of file) and so now we + // look for an ID3v1 tag at the same position. If that succeeds, then we'll loop back and look for + // an APEv2 tag immediately before the ID3v1 tag, otherwise our last resort is to look for an + // APEv2 tag at the beginning of the file. These are strongly discouraged (and not editable) but + // they have been seen in the wild so we attempt to handle them here (at least well enough to + // allow a proper transcoding). + + m_tag->tag_file_pos = -(int32_t)sizeof(ID3_Tag); + wpc->reader->set_pos_rel(wpc->wv_in, m_tag->tag_file_pos, SEEK_END); + + if(wpc->reader->read_bytes(wpc->wv_in, &m_tag->id3_tag, sizeof(ID3_Tag)) != sizeof(ID3_Tag) || + strncmp(m_tag->id3_tag.tag_id, "TAG", 3)) + { + m_tag->tag_begins_file = 1; // failed ID3v1, so look for APEv2 at beginning of file + CLEAR(m_tag->id3_tag); + } + } +} + +// Return TRUE is a valid ID3v1 or APEv2 tag has been loaded. + +int valid_tag(M_Tag *m_tag) +{ + if(m_tag->ape_tag_hdr.ID[0] == 'A') + return 'A'; + else if(m_tag->id3_tag.tag_id[0] == 'T') + return 'T'; + else + return 0; +} + +// Return FALSE if a valid APEv2 tag was only found at the beginning of the file (these are read-only +// because they cannot be edited without possibly shifting the entire file) + +int editable_tag(M_Tag *m_tag) { return !m_tag->tag_begins_file; } + +// Free the data for any APEv2 tag that was allocated. + +void free_tag(M_Tag *m_tag) +{ + if(m_tag->ape_tag_data) + { + free(m_tag->ape_tag_data); + m_tag->ape_tag_data = NULL; + } +} diff --git a/wavpack/unpack.c b/wavpack/unpack.c new file mode 100644 index 0000000..05e81bb --- /dev/null +++ b/wavpack/unpack.c @@ -0,0 +1,872 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// unpack.c + +// This module actually handles the decompression of the audio data, except for +// the entropy decoding which is handled by the read_words.c module. For better +// efficiency, the conversion is isolated to tight loops that handle an entire +// buffer. + +#include +#include + +#include "wavpack_local.h" + +#ifdef OPT_ASM_X86 +#define DECORR_STEREO_PASS_CONT unpack_decorr_stereo_pass_cont_x86 +#define DECORR_STEREO_PASS_CONT_AVAILABLE unpack_cpu_has_feature_x86(CPU_FEATURE_MMX) +#define DECORR_MONO_PASS_CONT unpack_decorr_mono_pass_cont_x86 +#elif defined(OPT_ASM_X64) && (defined(_WIN64) || defined(__CYGWIN__) || defined(__MINGW64__)) +#define DECORR_STEREO_PASS_CONT unpack_decorr_stereo_pass_cont_x64win +#define DECORR_STEREO_PASS_CONT_AVAILABLE 1 +#define DECORR_MONO_PASS_CONT unpack_decorr_mono_pass_cont_x64win +#elif defined(OPT_ASM_X64) +#define DECORR_STEREO_PASS_CONT unpack_decorr_stereo_pass_cont_x64 +#define DECORR_STEREO_PASS_CONT_AVAILABLE 1 +#define DECORR_MONO_PASS_CONT unpack_decorr_mono_pass_cont_x64 +#elif defined(OPT_ASM_ARM) +#define DECORR_STEREO_PASS_CONT unpack_decorr_stereo_pass_cont_armv7 +#define DECORR_STEREO_PASS_CONT_AVAILABLE 1 +#define DECORR_MONO_PASS_CONT unpack_decorr_mono_pass_cont_armv7 +#endif + +#ifdef DECORR_STEREO_PASS_CONT +extern void DECORR_STEREO_PASS_CONT(struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count, int32_t long_math); +extern void DECORR_MONO_PASS_CONT(struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count, int32_t long_math); +#endif + +// This flag provides the functionality of terminating the decoding and muting +// the output when a lossy sample appears to be corrupt. This is automatic +// for lossless files because a corrupt sample is unambigious, but for lossy +// data it might be possible for this to falsely trigger (although I have never +// seen it). + +#define LOSSY_MUTE + +///////////////////////////// executable code //////////////////////////////// + +// This monster actually unpacks the WavPack bitstream(s) into the specified +// buffer as 32-bit integers or floats (depending on orignal data). Lossy +// samples will be clipped to their original limits (i.e. 8-bit samples are +// clipped to -128/+127) but are still returned in longs. It is up to the +// caller to potentially reformat this for the final output including any +// multichannel distribution, block alignment or endian compensation. The +// function unpack_init() must have been called and the entire WavPack block +// must still be visible (although wps->blockbuff will not be accessed again). +// For maximum clarity, the function is broken up into segments that handle +// various modes. This makes for a few extra infrequent flag checks, but +// makes the code easier to follow because the nesting does not become so +// deep. For maximum efficiency, the conversion is isolated to tight loops +// that handle an entire buffer. The function returns the total number of +// samples unpacked, which can be less than the number requested if an error +// occurs or the end of the block is reached. + +static void decorr_stereo_pass(struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); +static void decorr_mono_pass(struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); +static void fixup_samples(WavpackContext *wpc, int32_t *buffer, uint32_t sample_count); + +int32_t unpack_samples(WavpackContext *wpc, int32_t *buffer, uint32_t sample_count) +{ + WavpackStream *wps = wpc->streams[wpc->current_stream]; + uint32_t flags = wps->wphdr.flags, crc = wps->crc, i; + int32_t mute_limit = (1L << ((flags & MAG_MASK) >> MAG_LSB)) + 2; + int32_t correction[2], read_word, *bptr; + struct decorr_pass *dpp; + int tcount, m = 0; + + // don't attempt to decode past the end of the block, but watch out for overflow! + + if(wps->sample_index + sample_count > GET_BLOCK_INDEX(wps->wphdr) + wps->wphdr.block_samples && + GET_BLOCK_INDEX(wps->wphdr) + wps->wphdr.block_samples - wps->sample_index < sample_count) + sample_count = (uint32_t)(GET_BLOCK_INDEX(wps->wphdr) + wps->wphdr.block_samples - wps->sample_index); + + if(GET_BLOCK_INDEX(wps->wphdr) > wps->sample_index || wps->wphdr.block_samples < sample_count) + wps->mute_error = TRUE; + + if(wps->mute_error) + { + if(wpc->reduced_channels == 1 || wpc->config.num_channels == 1 || (flags & MONO_FLAG)) + memset(buffer, 0, sample_count * 4); + else + memset(buffer, 0, sample_count * 8); + + wps->sample_index += sample_count; + return sample_count; + } + + if((flags & HYBRID_FLAG) && !wps->block2buff) mute_limit = (mute_limit * 2) + 128; + + //////////////// handle lossless or hybrid lossy mono data ///////////////// + + if(!wps->block2buff && (flags & MONO_DATA)) + { + int32_t *eptr = buffer + sample_count; + + if(flags & HYBRID_FLAG) + { + i = sample_count; + + for(bptr = buffer; bptr < eptr;) + if((*bptr++ = get_word(wps, 0, NULL)) == WORD_EOF) + { + i = (uint32_t)(bptr - buffer); + break; + } + } + else + i = get_words_lossless(wps, buffer, sample_count); + +#ifdef DECORR_MONO_PASS_CONT + if(sample_count < 16) + for(tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + decorr_mono_pass(dpp, buffer, sample_count); + else + for(tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + { + int pre_samples = (dpp->term > MAX_TERM) ? 2 : dpp->term; + + decorr_mono_pass(dpp, buffer, pre_samples); + + DECORR_MONO_PASS_CONT(dpp, buffer + pre_samples, sample_count - pre_samples, + ((flags & MAG_MASK) >> MAG_LSB) > 15); + } +#else + for(tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + decorr_mono_pass(dpp, buffer, sample_count); +#endif + +#ifndef LOSSY_MUTE + if(!(flags & HYBRID_FLAG)) +#endif + for(bptr = buffer; bptr < eptr; ++bptr) + { + if(labs(bptr[0]) > mute_limit) + { + i = (uint32_t)(bptr - buffer); + break; + } + + crc = crc * 3 + bptr[0]; + } +#ifndef LOSSY_MUTE + else + for(bptr = buffer; bptr < eptr; ++bptr) crc = crc * 3 + bptr[0]; +#endif + } + + /////////////// handle lossless or hybrid lossy stereo data /////////////// + + else if(!wps->block2buff && !(flags & MONO_DATA)) + { + int32_t *eptr = buffer + (sample_count * 2); + + if(flags & HYBRID_FLAG) + { + i = sample_count; + + for(bptr = buffer; bptr < eptr; bptr += 2) + if((bptr[0] = get_word(wps, 0, NULL)) == WORD_EOF || (bptr[1] = get_word(wps, 1, NULL)) == WORD_EOF) + { + i = (uint32_t)(bptr - buffer) / 2; + break; + } + } + else + i = get_words_lossless(wps, buffer, sample_count); + +#ifdef DECORR_STEREO_PASS_CONT + if(sample_count < 16 || !DECORR_STEREO_PASS_CONT_AVAILABLE) + { + for(tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + decorr_stereo_pass(dpp, buffer, sample_count); + + m = sample_count & (MAX_TERM - 1); + } + else + for(tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + { + int pre_samples = (dpp->term < 0 || dpp->term > MAX_TERM) ? 2 : dpp->term; + + decorr_stereo_pass(dpp, buffer, pre_samples); + + DECORR_STEREO_PASS_CONT(dpp, buffer + pre_samples * 2, sample_count - pre_samples, + ((flags & MAG_MASK) >> MAG_LSB) >= 16); + } +#else + for(tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + decorr_stereo_pass(dpp, buffer, sample_count); + + m = sample_count & (MAX_TERM - 1); +#endif + + if(flags & JOINT_STEREO) + for(bptr = buffer; bptr < eptr; bptr += 2) + { + bptr[0] += (bptr[1] -= (bptr[0] >> 1)); + crc += (crc << 3) + (bptr[0] << 1) + bptr[0] + bptr[1]; + } + else + for(bptr = buffer; bptr < eptr; bptr += 2) crc += (crc << 3) + (bptr[0] << 1) + bptr[0] + bptr[1]; + +#ifndef LOSSY_MUTE + if(!(flags & HYBRID_FLAG)) +#endif + for(bptr = buffer; bptr < eptr; bptr += 16) + if(labs(bptr[0]) > mute_limit || labs(bptr[1]) > mute_limit) + { + i = (uint32_t)(bptr - buffer) / 2; + break; + } + } + + /////////////////// handle hybrid lossless mono data //////////////////// + + else if((flags & HYBRID_FLAG) && (flags & MONO_DATA)) + for(bptr = buffer, i = 0; i < sample_count; ++i) + { + + if((read_word = get_word(wps, 0, correction)) == WORD_EOF) break; + + for(tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + { + int32_t sam, temp; + int k; + + if(dpp->term > MAX_TERM) + { + if(dpp->term & 1) + sam = 2 * dpp->samples_A[0] - dpp->samples_A[1]; + else + sam = (3 * dpp->samples_A[0] - dpp->samples_A[1]) >> 1; + + dpp->samples_A[1] = dpp->samples_A[0]; + k = 0; + } + else + { + sam = dpp->samples_A[m]; + k = (m + dpp->term) & (MAX_TERM - 1); + } + + temp = apply_weight(dpp->weight_A, sam) + read_word; + update_weight(dpp->weight_A, dpp->delta, sam, read_word); + dpp->samples_A[k] = read_word = temp; + } + + m = (m + 1) & (MAX_TERM - 1); + + if(flags & HYBRID_SHAPE) + { + int shaping_weight = (wps->dc.shaping_acc[0] += wps->dc.shaping_delta[0]) >> 16; + int32_t temp = -apply_weight(shaping_weight, wps->dc.error[0]); + + if((flags & NEW_SHAPING) && shaping_weight < 0 && temp) + { + if(temp == wps->dc.error[0]) temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error[0] = temp - correction[0]; + } + else + wps->dc.error[0] = -correction[0]; + + read_word += correction[0] - temp; + } + else + read_word += correction[0]; + + crc += (crc << 1) + read_word; + + if(labs(read_word) > mute_limit) break; + + *bptr++ = read_word; + } + + //////////////////// handle hybrid lossless stereo data /////////////////// + + else if(wps->block2buff && !(flags & MONO_DATA)) + for(bptr = buffer, i = 0; i < sample_count; ++i) + { + int32_t left, right, left2, right2; + int32_t left_c = 0, right_c = 0; + + if((left = get_word(wps, 0, correction)) == WORD_EOF || + (right = get_word(wps, 1, correction + 1)) == WORD_EOF) + break; + + if(flags & CROSS_DECORR) + { + left_c = left + correction[0]; + right_c = right + correction[1]; + + for(tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + { + int32_t sam_A, sam_B; + + if(dpp->term > 0) + { + if(dpp->term > MAX_TERM) + { + if(dpp->term & 1) + { + sam_A = 2 * dpp->samples_A[0] - dpp->samples_A[1]; + sam_B = 2 * dpp->samples_B[0] - dpp->samples_B[1]; + } + else + { + sam_A = (3 * dpp->samples_A[0] - dpp->samples_A[1]) >> 1; + sam_B = (3 * dpp->samples_B[0] - dpp->samples_B[1]) >> 1; + } + } + else + { + sam_A = dpp->samples_A[m]; + sam_B = dpp->samples_B[m]; + } + + left_c += apply_weight(dpp->weight_A, sam_A); + right_c += apply_weight(dpp->weight_B, sam_B); + } + else if(dpp->term == -1) + { + left_c += apply_weight(dpp->weight_A, dpp->samples_A[0]); + right_c += apply_weight(dpp->weight_B, left_c); + } + else + { + right_c += apply_weight(dpp->weight_B, dpp->samples_B[0]); + + if(dpp->term == -3) + left_c += apply_weight(dpp->weight_A, dpp->samples_A[0]); + else + left_c += apply_weight(dpp->weight_A, right_c); + } + } + + if(flags & JOINT_STEREO) left_c += (right_c -= (left_c >> 1)); + } + + for(tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + { + int32_t sam_A, sam_B; + + if(dpp->term > 0) + { + int k; + + if(dpp->term > MAX_TERM) + { + if(dpp->term & 1) + { + sam_A = 2 * dpp->samples_A[0] - dpp->samples_A[1]; + sam_B = 2 * dpp->samples_B[0] - dpp->samples_B[1]; + } + else + { + sam_A = (3 * dpp->samples_A[0] - dpp->samples_A[1]) >> 1; + sam_B = (3 * dpp->samples_B[0] - dpp->samples_B[1]) >> 1; + } + + dpp->samples_A[1] = dpp->samples_A[0]; + dpp->samples_B[1] = dpp->samples_B[0]; + k = 0; + } + else + { + sam_A = dpp->samples_A[m]; + sam_B = dpp->samples_B[m]; + k = (m + dpp->term) & (MAX_TERM - 1); + } + + left2 = apply_weight(dpp->weight_A, sam_A) + left; + right2 = apply_weight(dpp->weight_B, sam_B) + right; + + update_weight(dpp->weight_A, dpp->delta, sam_A, left); + update_weight(dpp->weight_B, dpp->delta, sam_B, right); + + dpp->samples_A[k] = left = left2; + dpp->samples_B[k] = right = right2; + } + else if(dpp->term == -1) + { + left2 = left + apply_weight(dpp->weight_A, dpp->samples_A[0]); + update_weight_clip(dpp->weight_A, dpp->delta, dpp->samples_A[0], left); + left = left2; + right2 = right + apply_weight(dpp->weight_B, left2); + update_weight_clip(dpp->weight_B, dpp->delta, left2, right); + dpp->samples_A[0] = right = right2; + } + else + { + right2 = right + apply_weight(dpp->weight_B, dpp->samples_B[0]); + update_weight_clip(dpp->weight_B, dpp->delta, dpp->samples_B[0], right); + right = right2; + + if(dpp->term == -3) + { + right2 = dpp->samples_A[0]; + dpp->samples_A[0] = right; + } + + left2 = left + apply_weight(dpp->weight_A, right2); + update_weight_clip(dpp->weight_A, dpp->delta, right2, left); + dpp->samples_B[0] = left = left2; + } + } + + m = (m + 1) & (MAX_TERM - 1); + + if(!(flags & CROSS_DECORR)) + { + left_c = left + correction[0]; + right_c = right + correction[1]; + + if(flags & JOINT_STEREO) left_c += (right_c -= (left_c >> 1)); + } + + if(flags & JOINT_STEREO) left += (right -= (left >> 1)); + + if(flags & HYBRID_SHAPE) + { + int shaping_weight; + int32_t temp; + + correction[0] = left_c - left; + shaping_weight = (wps->dc.shaping_acc[0] += wps->dc.shaping_delta[0]) >> 16; + temp = -apply_weight(shaping_weight, wps->dc.error[0]); + + if((flags & NEW_SHAPING) && shaping_weight < 0 && temp) + { + if(temp == wps->dc.error[0]) temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error[0] = temp - correction[0]; + } + else + wps->dc.error[0] = -correction[0]; + + left = left_c - temp; + correction[1] = right_c - right; + shaping_weight = (wps->dc.shaping_acc[1] += wps->dc.shaping_delta[1]) >> 16; + temp = -apply_weight(shaping_weight, wps->dc.error[1]); + + if((flags & NEW_SHAPING) && shaping_weight < 0 && temp) + { + if(temp == wps->dc.error[1]) temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error[1] = temp - correction[1]; + } + else + wps->dc.error[1] = -correction[1]; + + right = right_c - temp; + } + else + { + left = left_c; + right = right_c; + } + + if(labs(left) > mute_limit || labs(right) > mute_limit) break; + + crc += (crc << 3) + (left << 1) + left + right; + *bptr++ = left; + *bptr++ = right; + } + else + i = 0; /* this line can't execute, but suppresses compiler warning */ + + if(i != sample_count) + { + memset(buffer, 0, sample_count * (flags & MONO_FLAG ? 4 : 8)); + wps->mute_error = TRUE; + i = sample_count; + + if(bs_is_open(&wps->wvxbits)) bs_close_read(&wps->wvxbits); + } + + if(m) + for(tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + if(dpp->term > 0 && dpp->term <= MAX_TERM) + { + int32_t temp_A[MAX_TERM], temp_B[MAX_TERM]; + int k; + + memcpy(temp_A, dpp->samples_A, sizeof(dpp->samples_A)); + memcpy(temp_B, dpp->samples_B, sizeof(dpp->samples_B)); + + for(k = 0; k < MAX_TERM; k++) + { + dpp->samples_A[k] = temp_A[m]; + dpp->samples_B[k] = temp_B[m]; + m = (m + 1) & (MAX_TERM - 1); + } + } + + fixup_samples(wpc, buffer, i); + + if((flags & FLOAT_DATA) && (wpc->open_flags & OPEN_NORMALIZE)) + WavpackFloatNormalize(buffer, (flags & MONO_DATA) ? i : i * 2, 127 - wps->float_norm_exp + wpc->norm_offset); + + if(flags & FALSE_STEREO) + { + int32_t *dptr = buffer + i * 2; + int32_t *sptr = buffer + i; + int32_t c = i; + + while(c--) + { + *--dptr = *--sptr; + *--dptr = *sptr; + } + } + + wps->sample_index += i; + wps->crc = crc; + + return i; +} + +// General function to perform mono decorrelation pass on specified buffer +// (although since this is the reverse function it might technically be called +// "correlation" instead). This version handles all sample resolutions and +// weight deltas. The dpp->samples_X[] data is returned normalized for term +// values 1-8. + +static void decorr_mono_pass(struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) +{ + int32_t delta = dpp->delta, weight_A = dpp->weight_A; + int32_t *bptr, *eptr = buffer + sample_count, sam_A; + int m, k; + + switch(dpp->term) + { + + case 17: + for(bptr = buffer; bptr < eptr; bptr++) + { + sam_A = 2 * dpp->samples_A[0] - dpp->samples_A[1]; + dpp->samples_A[1] = dpp->samples_A[0]; + dpp->samples_A[0] = apply_weight(weight_A, sam_A) + bptr[0]; + update_weight(weight_A, delta, sam_A, bptr[0]); + bptr[0] = dpp->samples_A[0]; + } + + break; + + case 18: + for(bptr = buffer; bptr < eptr; bptr++) + { + sam_A = (3 * dpp->samples_A[0] - dpp->samples_A[1]) >> 1; + dpp->samples_A[1] = dpp->samples_A[0]; + dpp->samples_A[0] = apply_weight(weight_A, sam_A) + bptr[0]; + update_weight(weight_A, delta, sam_A, bptr[0]); + bptr[0] = dpp->samples_A[0]; + } + + break; + + default: + for(m = 0, k = dpp->term & (MAX_TERM - 1), bptr = buffer; bptr < eptr; bptr++) + { + sam_A = dpp->samples_A[m]; + dpp->samples_A[k] = apply_weight(weight_A, sam_A) + bptr[0]; + update_weight(weight_A, delta, sam_A, bptr[0]); + bptr[0] = dpp->samples_A[k]; + m = (m + 1) & (MAX_TERM - 1); + k = (k + 1) & (MAX_TERM - 1); + } + + if(m) + { + int32_t temp_samples[MAX_TERM]; + + memcpy(temp_samples, dpp->samples_A, sizeof(dpp->samples_A)); + + for(k = 0; k < MAX_TERM; k++, m++) dpp->samples_A[k] = temp_samples[m & (MAX_TERM - 1)]; + } + + break; + } + + dpp->weight_A = weight_A; +} + +// General function to perform stereo decorrelation pass on specified buffer +// (although since this is the reverse function it might technically be called +// "correlation" instead). This version handles all sample resolutions and +// weight deltas. The dpp->samples_X[] data is *not* returned normalized for +// term values 1-8, so it should be normalized if it is going to be used to +// call this function again. + +static void decorr_stereo_pass(struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) +{ + int32_t *bptr, *eptr = buffer + (sample_count * 2); + int m, k; + + switch(dpp->term) + { + case 17: + for(bptr = buffer; bptr < eptr; bptr += 2) + { + int32_t sam, tmp; + + sam = 2 * dpp->samples_A[0] - dpp->samples_A[1]; + dpp->samples_A[1] = dpp->samples_A[0]; + bptr[0] = dpp->samples_A[0] = apply_weight(dpp->weight_A, sam) + (tmp = bptr[0]); + update_weight(dpp->weight_A, dpp->delta, sam, tmp); + + sam = 2 * dpp->samples_B[0] - dpp->samples_B[1]; + dpp->samples_B[1] = dpp->samples_B[0]; + bptr[1] = dpp->samples_B[0] = apply_weight(dpp->weight_B, sam) + (tmp = bptr[1]); + update_weight(dpp->weight_B, dpp->delta, sam, tmp); + } + + break; + + case 18: + for(bptr = buffer; bptr < eptr; bptr += 2) + { + int32_t sam, tmp; + + sam = dpp->samples_A[0] + ((dpp->samples_A[0] - dpp->samples_A[1]) >> 1); + dpp->samples_A[1] = dpp->samples_A[0]; + bptr[0] = dpp->samples_A[0] = apply_weight(dpp->weight_A, sam) + (tmp = bptr[0]); + update_weight(dpp->weight_A, dpp->delta, sam, tmp); + + sam = dpp->samples_B[0] + ((dpp->samples_B[0] - dpp->samples_B[1]) >> 1); + dpp->samples_B[1] = dpp->samples_B[0]; + bptr[1] = dpp->samples_B[0] = apply_weight(dpp->weight_B, sam) + (tmp = bptr[1]); + update_weight(dpp->weight_B, dpp->delta, sam, tmp); + } + + break; + + default: + for(m = 0, k = dpp->term & (MAX_TERM - 1), bptr = buffer; bptr < eptr; bptr += 2) + { + int32_t sam; + + sam = dpp->samples_A[m]; + dpp->samples_A[k] = apply_weight(dpp->weight_A, sam) + bptr[0]; + update_weight(dpp->weight_A, dpp->delta, sam, bptr[0]); + bptr[0] = dpp->samples_A[k]; + + sam = dpp->samples_B[m]; + dpp->samples_B[k] = apply_weight(dpp->weight_B, sam) + bptr[1]; + update_weight(dpp->weight_B, dpp->delta, sam, bptr[1]); + bptr[1] = dpp->samples_B[k]; + + m = (m + 1) & (MAX_TERM - 1); + k = (k + 1) & (MAX_TERM - 1); + } + + break; + + case -1: + for(bptr = buffer; bptr < eptr; bptr += 2) + { + int32_t sam; + + sam = bptr[0] + apply_weight(dpp->weight_A, dpp->samples_A[0]); + update_weight_clip(dpp->weight_A, dpp->delta, dpp->samples_A[0], bptr[0]); + bptr[0] = sam; + dpp->samples_A[0] = bptr[1] + apply_weight(dpp->weight_B, sam); + update_weight_clip(dpp->weight_B, dpp->delta, sam, bptr[1]); + bptr[1] = dpp->samples_A[0]; + } + + break; + + case -2: + for(bptr = buffer; bptr < eptr; bptr += 2) + { + int32_t sam; + + sam = bptr[1] + apply_weight(dpp->weight_B, dpp->samples_B[0]); + update_weight_clip(dpp->weight_B, dpp->delta, dpp->samples_B[0], bptr[1]); + bptr[1] = sam; + dpp->samples_B[0] = bptr[0] + apply_weight(dpp->weight_A, sam); + update_weight_clip(dpp->weight_A, dpp->delta, sam, bptr[0]); + bptr[0] = dpp->samples_B[0]; + } + + break; + + case -3: + for(bptr = buffer; bptr < eptr; bptr += 2) + { + int32_t sam_A, sam_B; + + sam_A = bptr[0] + apply_weight(dpp->weight_A, dpp->samples_A[0]); + update_weight_clip(dpp->weight_A, dpp->delta, dpp->samples_A[0], bptr[0]); + sam_B = bptr[1] + apply_weight(dpp->weight_B, dpp->samples_B[0]); + update_weight_clip(dpp->weight_B, dpp->delta, dpp->samples_B[0], bptr[1]); + bptr[0] = dpp->samples_B[0] = sam_A; + bptr[1] = dpp->samples_A[0] = sam_B; + } + + break; + } +} + +// This is a helper function for unpack_samples() that applies several final +// operations. First, if the data is 32-bit float data, then that conversion +// is done in the float.c module (whether lossy or lossless) and we return. +// Otherwise, if the extended integer data applies, then that operation is +// executed first. If the unpacked data is lossy (and not corrected) then +// it is clipped and shifted in a single operation. Otherwise, if it's +// lossless then the last step is to apply the final shift (if any). + +static void fixup_samples(WavpackContext *wpc, int32_t *buffer, uint32_t sample_count) +{ + WavpackStream *wps = wpc->streams[wpc->current_stream]; + uint32_t flags = wps->wphdr.flags; + int lossy_flag = (flags & HYBRID_FLAG) && !wps->block2buff; + int shift = (flags & SHIFT_MASK) >> SHIFT_LSB; + + if(flags & FLOAT_DATA) + { + float_values(wps, buffer, (flags & MONO_DATA) ? sample_count : sample_count * 2); + return; + } + + if(flags & INT32_DATA) + { + uint32_t count = (flags & MONO_DATA) ? sample_count : sample_count * 2; + int sent_bits = wps->int32_sent_bits, zeros = wps->int32_zeros; + int ones = wps->int32_ones, dups = wps->int32_dups; + uint32_t data, mask = (1 << sent_bits) - 1; + int32_t *dptr = buffer; + + if(bs_is_open(&wps->wvxbits)) + { + uint32_t crc = wps->crc_x; + + while(count--) + { + // if (sent_bits) { + getbits(&data, sent_bits, &wps->wvxbits); + *dptr = (*dptr << sent_bits) | (data & mask); + // } + + if(zeros) + *dptr <<= zeros; + else if(ones) + *dptr = ((*dptr + 1) << ones) - 1; + else if(dups) + *dptr = ((*dptr + (*dptr & 1)) << dups) - (*dptr & 1); + + crc = crc * 9 + (*dptr & 0xffff) * 3 + ((*dptr >> 16) & 0xffff); + dptr++; + } + + wps->crc_x = crc; + } + else if(!sent_bits && (zeros + ones + dups)) + { + while(lossy_flag && (flags & BYTES_STORED) == 3 && shift < 8) + { + if(zeros) + zeros--; + else if(ones) + ones--; + else if(dups) + dups--; + else + break; + + shift++; + } + + while(count--) + { + if(zeros) + *dptr <<= zeros; + else if(ones) + *dptr = ((*dptr + 1) << ones) - 1; + else if(dups) + *dptr = ((*dptr + (*dptr & 1)) << dups) - (*dptr & 1); + + dptr++; + } + } + else + shift += zeros + sent_bits + ones + dups; + } + + if(lossy_flag) + { + int32_t min_value, max_value, min_shifted, max_shifted; + + switch(flags & BYTES_STORED) + { + case 0: + min_shifted = (min_value = -128 >> shift) << shift; + max_shifted = (max_value = 127 >> shift) << shift; + break; + + case 1: + min_shifted = (min_value = -32768 >> shift) << shift; + max_shifted = (max_value = 32767 >> shift) << shift; + break; + + case 2: + min_shifted = (min_value = -8388608 >> shift) << shift; + max_shifted = (max_value = 8388607 >> shift) << shift; + break; + + case 3: + default: /* "default" suppresses compiler warning */ + min_shifted = (min_value = (int32_t)0x80000000 >> shift) << shift; + max_shifted = (max_value = (int32_t)0x7fffffff >> shift) << shift; + break; + } + + if(!(flags & MONO_DATA)) sample_count *= 2; + + while(sample_count--) + { + if(*buffer < min_value) + *buffer++ = min_shifted; + else if(*buffer > max_value) + *buffer++ = max_shifted; + else + *buffer++ <<= shift; + } + } + else if(shift) + { + if(!(flags & MONO_DATA)) sample_count *= 2; + + while(sample_count--) *buffer++ <<= shift; + } +} + +// This function checks the crc value(s) for an unpacked block, returning the +// number of actual crc errors detected for the block. The block must be +// completely unpacked before this test is valid. For losslessly unpacked +// blocks of float or extended integer data the extended crc is also checked. +// Note that WavPack's crc is not a CCITT approved polynomial algorithm, but +// is a much simpler method that is virtually as robust for real world data. + +int check_crc_error(WavpackContext *wpc) +{ + int result = 0, stream; + + for(stream = 0; stream < wpc->num_streams; stream++) + { + WavpackStream *wps = wpc->streams[stream]; + + if(wps->crc != wps->wphdr.crc) + ++result; + else if(bs_is_open(&wps->wvxbits) && wps->crc_x != wps->crc_wvx) + ++result; + } + + return result; +} diff --git a/wavpack/unpack_floats.c b/wavpack/unpack_floats.c new file mode 100644 index 0000000..70d666b --- /dev/null +++ b/wavpack/unpack_floats.c @@ -0,0 +1,154 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// unpack_floats.c + +// This module deals with the restoration of floating-point data. Note that no +// floating point math is involved here...the values are only processed with +// the macros that directly access the mantissa, exponent, and sign fields. +// That's why we use the f32 type instead of the built-in float type. + +#include + +#include "wavpack_local.h" + +static void float_values_nowvx(WavpackStream *wps, int32_t *values, int32_t num_values); + +void float_values(WavpackStream *wps, int32_t *values, int32_t num_values) +{ + uint32_t crc = wps->crc_x; + + if(!bs_is_open(&wps->wvxbits)) + { + float_values_nowvx(wps, values, num_values); + return; + } + + while(num_values--) + { + int shift_count = 0, exp = wps->float_max_exp; + f32 outval = 0; + uint32_t temp; + + if(*values == 0) + { + if(wps->float_flags & FLOAT_ZEROS_SENT) + { + if(getbit(&wps->wvxbits)) + { + getbits(&temp, 23, &wps->wvxbits); + set_mantissa(outval, temp); + + if(exp >= 25) + { + getbits(&temp, 8, &wps->wvxbits); + set_exponent(outval, temp); + } + + set_sign(outval, getbit(&wps->wvxbits)); + } + else if(wps->float_flags & FLOAT_NEG_ZEROS) + set_sign(outval, getbit(&wps->wvxbits)); + } + } + else + { + *values <<= wps->float_shift; + + if(*values < 0) + { + *values = -*values; + set_sign(outval, 1); + } + + if(*values == 0x1000000) + { + if(getbit(&wps->wvxbits)) + { + getbits(&temp, 23, &wps->wvxbits); + set_mantissa(outval, temp); + } + + set_exponent(outval, 255); + } + else + { + if(exp) + while(!(*values & 0x800000) && --exp) + { + shift_count++; + *values <<= 1; + } + + if(shift_count) + { + if((wps->float_flags & FLOAT_SHIFT_ONES) || + ((wps->float_flags & FLOAT_SHIFT_SAME) && getbit(&wps->wvxbits))) + *values |= ((1 << shift_count) - 1); + else if(wps->float_flags & FLOAT_SHIFT_SENT) + { + getbits(&temp, shift_count, &wps->wvxbits); + *values |= temp & ((1 << shift_count) - 1); + } + } + + set_mantissa(outval, *values); + set_exponent(outval, exp); + } + } + + crc = crc * 27 + get_mantissa(outval) * 9 + get_exponent(outval) * 3 + get_sign(outval); + *(f32 *)values++ = outval; + } + + wps->crc_x = crc; +} + +static void float_values_nowvx(WavpackStream *wps, int32_t *values, int32_t num_values) +{ + while(num_values--) + { + int shift_count = 0, exp = wps->float_max_exp; + f32 outval = 0; + + if(*values) + { + *values <<= wps->float_shift; + + if(*values < 0) + { + *values = -*values; + set_sign(outval, 1); + } + + if(*values >= 0x1000000) + { + while(*values & 0xf000000) + { + *values >>= 1; + ++exp; + } + } + else if(exp) + { + while(!(*values & 0x800000) && --exp) + { + shift_count++; + *values <<= 1; + } + + if(shift_count && (wps->float_flags & FLOAT_SHIFT_ONES)) *values |= ((1 << shift_count) - 1); + } + + set_mantissa(outval, *values); + set_exponent(outval, exp); + } + + *(f32 *)values++ = outval; + } +} diff --git a/wavpack/unpack_seek.c b/wavpack/unpack_seek.c new file mode 100644 index 0000000..82e9ef3 --- /dev/null +++ b/wavpack/unpack_seek.c @@ -0,0 +1,404 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// unpack_seek.c + +// This module provides the high-level API for unpacking audio data from +// a specific sample index (i.e., seeking). + +#ifndef NO_SEEKING + +#include +#include + +#include "wavpack_local.h" + +///////////////////////////// executable code //////////////////////////////// + +static int64_t find_sample(WavpackContext *wpc, void *infile, int64_t header_pos, int64_t sample); + +// Seek to the specifed sample index, returning TRUE on success. Note that +// files generated with version 4.0 or newer will seek almost immediately. +// Older files can take quite long if required to seek through unplayed +// portions of the file, but will create a seek map so that reverse seeks +// (or forward seeks to already scanned areas) will be very fast. After a +// FALSE return the file should not be accessed again (other than to close +// it); this is a fatal error. + +int WavpackSeekSample(WavpackContext *wpc, uint32_t sample) { return WavpackSeekSample64(wpc, sample); } + +int WavpackSeekSample64(WavpackContext *wpc, int64_t sample) +{ + WavpackStream *wps = wpc->streams ? wpc->streams[wpc->current_stream = 0] : NULL; + uint32_t bcount, samples_to_skip, samples_to_decode = 0; + int32_t *buffer; + + if(wpc->total_samples == -1 || sample >= wpc->total_samples || !wpc->reader->can_seek(wpc->wv_in) || + (wpc->open_flags & OPEN_STREAMING) || (wpc->wvc_flag && !wpc->reader->can_seek(wpc->wvc_in))) + return FALSE; + +#ifdef ENABLE_LEGACY + if(wpc->stream3) return seek_sample3(wpc, (uint32_t)sample); +#endif + +#ifdef ENABLE_DSD + if(wpc->decimation_context) + { // the decimation code needs some context to be sample accurate + if(sample < 16) + { + samples_to_decode = (uint32_t)sample; + sample = 0; + } + else + { + samples_to_decode = 16; + sample -= 16; + } + } +#endif + + if(!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || sample < GET_BLOCK_INDEX(wps->wphdr) || + sample >= GET_BLOCK_INDEX(wps->wphdr) + wps->wphdr.block_samples) + { + + free_streams(wpc); + wpc->filepos = find_sample(wpc, wpc->wv_in, wpc->filepos, sample); + + if(wpc->filepos == -1) return FALSE; + + if(wpc->wvc_flag) + { + wpc->file2pos = find_sample(wpc, wpc->wvc_in, 0, sample); + + if(wpc->file2pos == -1) return FALSE; + } + } + + if(!wps->blockbuff) + { + wpc->reader->set_pos_abs(wpc->wv_in, wpc->filepos); + wpc->reader->read_bytes(wpc->wv_in, &wps->wphdr, sizeof(WavpackHeader)); + WavpackLittleEndianToNative(&wps->wphdr, WavpackHeaderFormat); + wps->blockbuff = malloc(wps->wphdr.ckSize + 8); + memcpy(wps->blockbuff, &wps->wphdr, sizeof(WavpackHeader)); + + if(wpc->reader->read_bytes(wpc->wv_in, wps->blockbuff + sizeof(WavpackHeader), wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) + { + free_streams(wpc); + return FALSE; + } + + // render corrupt blocks harmless + if(!WavpackVerifySingleBlock(wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) + { + wps->wphdr.ckSize = sizeof(WavpackHeader) - 8; + wps->wphdr.block_samples = 0; + memcpy(wps->blockbuff, &wps->wphdr, 32); + } + + SET_BLOCK_INDEX(wps->wphdr, GET_BLOCK_INDEX(wps->wphdr) - wpc->initial_index); + memcpy(wps->blockbuff, &wps->wphdr, sizeof(WavpackHeader)); + wps->init_done = FALSE; + + if(wpc->wvc_flag) + { + wpc->reader->set_pos_abs(wpc->wvc_in, wpc->file2pos); + wpc->reader->read_bytes(wpc->wvc_in, &wps->wphdr, sizeof(WavpackHeader)); + WavpackLittleEndianToNative(&wps->wphdr, WavpackHeaderFormat); + wps->block2buff = malloc(wps->wphdr.ckSize + 8); + memcpy(wps->block2buff, &wps->wphdr, sizeof(WavpackHeader)); + + if(wpc->reader->read_bytes(wpc->wvc_in, wps->block2buff + sizeof(WavpackHeader), wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) + { + free_streams(wpc); + return FALSE; + } + + // render corrupt blocks harmless + if(!WavpackVerifySingleBlock(wps->block2buff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) + { + wps->wphdr.ckSize = sizeof(WavpackHeader) - 8; + wps->wphdr.block_samples = 0; + memcpy(wps->block2buff, &wps->wphdr, 32); + } + + SET_BLOCK_INDEX(wps->wphdr, GET_BLOCK_INDEX(wps->wphdr) - wpc->initial_index); + memcpy(wps->block2buff, &wps->wphdr, sizeof(WavpackHeader)); + } + + if(!wps->init_done && !unpack_init(wpc)) + { + free_streams(wpc); + return FALSE; + } + + wps->init_done = TRUE; + } + + while(!wpc->reduced_channels && !(wps->wphdr.flags & FINAL_BLOCK)) + { + if(++wpc->current_stream == wpc->num_streams) + { + + if(wpc->num_streams == wpc->max_streams) + { + free_streams(wpc); + return FALSE; + } + + wpc->streams = realloc(wpc->streams, (wpc->num_streams + 1) * sizeof(wpc->streams[0])); + wps = wpc->streams[wpc->num_streams++] = malloc(sizeof(WavpackStream)); + CLEAR(*wps); + bcount = read_next_header(wpc->reader, wpc->wv_in, &wps->wphdr); + + if(bcount == (uint32_t)-1) + { + free_streams(wpc); + return FALSE; + } + + wps->blockbuff = malloc(wps->wphdr.ckSize + 8); + memcpy(wps->blockbuff, &wps->wphdr, 32); + + if(wpc->reader->read_bytes(wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) + { + free_streams(wpc); + return FALSE; + } + + // render corrupt blocks harmless + if(!WavpackVerifySingleBlock(wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) + { + wps->wphdr.ckSize = sizeof(WavpackHeader) - 8; + wps->wphdr.block_samples = 0; + memcpy(wps->blockbuff, &wps->wphdr, 32); + } + + wps->init_done = FALSE; + + if(wpc->wvc_flag && !read_wvc_block(wpc)) + { + free_streams(wpc); + return FALSE; + } + + if(!wps->init_done && !unpack_init(wpc)) + { + free_streams(wpc); + return FALSE; + } + + wps->init_done = TRUE; + } + else + wps = wpc->streams[wpc->current_stream]; + } + + if(sample < wps->sample_index) + { + for(wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++) + if(!unpack_init(wpc)) + return FALSE; + else + wpc->streams[wpc->current_stream]->init_done = TRUE; + } + + samples_to_skip = (uint32_t)(sample - wps->sample_index); + + if(samples_to_skip > 131072) + { + free_streams(wpc); + return FALSE; + } + + if(samples_to_skip) + { + buffer = malloc(samples_to_skip * 8); + + for(wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++) +#ifdef ENABLE_DSD + if(wpc->streams[wpc->current_stream]->wphdr.flags & DSD_FLAG) + unpack_dsd_samples(wpc, buffer, samples_to_skip); + else +#endif + unpack_samples(wpc, buffer, samples_to_skip); + + free(buffer); + } + + wpc->current_stream = 0; + +#ifdef ENABLE_DSD + if(wpc->decimation_context) decimate_dsd_reset(wpc->decimation_context); + + if(samples_to_decode) + { + buffer = malloc(samples_to_decode * wpc->config.num_channels * 4); + + if(buffer) + { + WavpackUnpackSamples(wpc, buffer, samples_to_decode); + free(buffer); + } + } +#endif + + return TRUE; +} + +// Find a valid WavPack header, searching either from the current file position +// (or from the specified position if not -1) and store it (endian corrected) +// at the specified pointer. The return value is the exact file position of the +// header, although we may have actually read past it. Because this function +// is used for seeking to a specific audio sample, it only considers blocks +// that contain audio samples for the initial stream to be valid. + +#define BUFSIZE 4096 + +static int64_t find_header(WavpackStreamReader64 *reader, void *id, int64_t filepos, WavpackHeader *wphdr) +{ + unsigned char *buffer = malloc(BUFSIZE), *sp = buffer, *ep = buffer; + + if(filepos != (uint32_t)-1 && reader->set_pos_abs(id, filepos)) + { + free(buffer); + return -1; + } + + while(1) + { + int bleft; + + if(sp < ep) + { + bleft = (int)(ep - sp); + memcpy(buffer, sp, bleft); + ep -= (sp - buffer); + sp = buffer; + } + else + { + if(sp > ep) + if(reader->set_pos_rel(id, (int32_t)(sp - ep), SEEK_CUR)) + { + free(buffer); + return -1; + } + + sp = ep = buffer; + bleft = 0; + } + + ep += reader->read_bytes(id, ep, BUFSIZE - bleft); + + if(ep - sp < 32) + { + free(buffer); + return -1; + } + + while(sp + 32 <= ep) + if(*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' && !(*++sp & 1) && sp[2] < 16 && !sp[3] && + (sp[2] || sp[1] || *sp >= 24) && sp[5] == 4 && sp[4] >= (MIN_STREAM_VERS & 0xff) && + sp[4] <= (MAX_STREAM_VERS & 0xff) && sp[18] < 3 && !sp[19]) + { + memcpy(wphdr, sp - 4, sizeof(*wphdr)); + WavpackLittleEndianToNative(wphdr, WavpackHeaderFormat); + + if(wphdr->block_samples && (wphdr->flags & INITIAL_BLOCK)) + { + free(buffer); + return reader->get_pos(id) - (ep - sp + 4); + } + + if(wphdr->ckSize > 1024) sp += wphdr->ckSize - 1024; + } + } +} + +// Find the WavPack block that contains the specified sample. If "header_pos" +// is zero, then no information is assumed except the total number of samples +// in the file and its size in bytes. If "header_pos" is non-zero then we +// assume that it is the file position of the valid header image contained in +// the first stream and we can limit our search to either the portion above +// or below that point. If a .wvc file is being used, then this must be called +// for that file also. + +static int64_t find_sample(WavpackContext *wpc, void *infile, int64_t header_pos, int64_t sample) +{ + WavpackStream *wps = wpc->streams[wpc->current_stream]; + int64_t file_pos1 = 0, file_pos2 = wpc->reader->get_length(infile); + int64_t sample_pos1 = 0, sample_pos2 = wpc->total_samples; + double ratio = 0.96; + int file_skip = 0; + + if(sample >= wpc->total_samples) return -1; + + if(header_pos && wps->wphdr.block_samples) + { + if(GET_BLOCK_INDEX(wps->wphdr) > sample) + { + sample_pos2 = GET_BLOCK_INDEX(wps->wphdr); + file_pos2 = header_pos; + } + else if(GET_BLOCK_INDEX(wps->wphdr) + wps->wphdr.block_samples <= sample) + { + sample_pos1 = GET_BLOCK_INDEX(wps->wphdr); + file_pos1 = header_pos; + } + else + return header_pos; + } + + while(1) + { + double bytes_per_sample; + int64_t seek_pos; + + bytes_per_sample = (double)file_pos2 - file_pos1; + bytes_per_sample /= sample_pos2 - sample_pos1; + seek_pos = file_pos1 + (file_skip ? 32 : 0); + seek_pos += (int64_t)(bytes_per_sample * (sample - sample_pos1) * ratio); + seek_pos = find_header(wpc->reader, infile, seek_pos, &wps->wphdr); + + if(seek_pos != (int64_t)-1) SET_BLOCK_INDEX(wps->wphdr, GET_BLOCK_INDEX(wps->wphdr) - wpc->initial_index); + + if(seek_pos == (int64_t)-1 || seek_pos >= file_pos2) + { + if(ratio > 0.0) + { + if((ratio -= 0.24) < 0.0) ratio = 0.0; + } + else + return -1; + } + else if(GET_BLOCK_INDEX(wps->wphdr) > sample) + { + sample_pos2 = GET_BLOCK_INDEX(wps->wphdr); + file_pos2 = seek_pos; + } + else if(GET_BLOCK_INDEX(wps->wphdr) + wps->wphdr.block_samples <= sample) + { + + if(seek_pos == file_pos1) + file_skip = 1; + else + { + sample_pos1 = GET_BLOCK_INDEX(wps->wphdr); + file_pos1 = seek_pos; + } + } + else + return seek_pos; + } +} + +#endif diff --git a/wavpack/unpack_utils.c b/wavpack/unpack_utils.c new file mode 100644 index 0000000..551f0ce --- /dev/null +++ b/wavpack/unpack_utils.c @@ -0,0 +1,417 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// unpack_utils.c + +// This module provides the high-level API for unpacking audio data from +// WavPack files. It manages the buffers used to interleave the data passed +// back to the application from the individual streams. The actual audio +// stream decompression is handled in the unpack.c module. + +#include +#include + +#include "wavpack_local.h" + +///////////////////////////// executable code //////////////////////////////// + +// Unpack the specified number of samples from the current file position. +// Note that "samples" here refers to "complete" samples, which would be +// 2 longs for stereo files or even more for multichannel files, so the +// required memory at "buffer" is 4 * samples * num_channels bytes. The +// audio data is returned right-justified in 32-bit longs in the endian +// mode native to the executing processor. So, if the original data was +// 16-bit, then the values returned would be +/-32k. Floating point data +// can also be returned if the source was floating point data (and this +// can be optionally normalized to +/-1.0 by using the appropriate flag +// in the call to WavpackOpenFileInput ()). The actual number of samples +// unpacked is returned, which should be equal to the number requested unless +// the end of fle is encountered or an error occurs. After all samples have +// been unpacked then 0 will be returned. + +uint32_t WavpackUnpackSamples(WavpackContext *wpc, int32_t *buffer, uint32_t samples) +{ + WavpackStream *wps = wpc->streams ? wpc->streams[wpc->current_stream = 0] : NULL; + int num_channels = wpc->config.num_channels, file_done = FALSE; + uint32_t bcount, samples_unpacked = 0, samples_to_unpack; + int32_t *bptr = buffer; + +#ifdef ENABLE_LEGACY + if(wpc->stream3) return unpack_samples3(wpc, buffer, samples); +#endif + + while(samples) + { + + // if the current block has no audio, or it's not the first block of a multichannel + // sequence, or the sample we're on is past the last sample in this block...we need + // to free up the streams and read the next block + + if(!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || + wps->sample_index >= GET_BLOCK_INDEX(wps->wphdr) + wps->wphdr.block_samples) + { + + int64_t nexthdrpos; + + if(wpc->wrapper_bytes >= MAX_WRAPPER_BYTES) break; + + free_streams(wpc); + nexthdrpos = wpc->reader->get_pos(wpc->wv_in); + bcount = read_next_header(wpc->reader, wpc->wv_in, &wps->wphdr); + + if(bcount == (uint32_t)-1) break; + + wpc->filepos = nexthdrpos + bcount; + + // allocate the memory for the entire raw block and read it in + + wps->blockbuff = malloc(wps->wphdr.ckSize + 8); + + if(!wps->blockbuff) break; + + memcpy(wps->blockbuff, &wps->wphdr, 32); + + if(wpc->reader->read_bytes(wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) + { + strcpy(wpc->error_message, "can't read all of last block!"); + wps->wphdr.block_samples = 0; + wps->wphdr.ckSize = 24; + break; + } + + // render corrupt blocks harmless + if(!WavpackVerifySingleBlock(wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) + { + wps->wphdr.ckSize = sizeof(WavpackHeader) - 8; + wps->wphdr.block_samples = 0; + memcpy(wps->blockbuff, &wps->wphdr, 32); + } + + // potentially adjusting block_index must be done AFTER verifying block + + if(wpc->open_flags & OPEN_STREAMING) + SET_BLOCK_INDEX(wps->wphdr, wps->sample_index = 0); + else + SET_BLOCK_INDEX(wps->wphdr, GET_BLOCK_INDEX(wps->wphdr) - wpc->initial_index); + + memcpy(wps->blockbuff, &wps->wphdr, 32); + wps->init_done = FALSE; // we have not yet called unpack_init() for this block + + // if this block has audio, but not the sample index we were expecting, flag an error + + if(wps->wphdr.block_samples && wps->sample_index != GET_BLOCK_INDEX(wps->wphdr)) wpc->crc_errors++; + + // if this block has audio, and we're in hybrid lossless mode, read the matching wvc block + + if(wps->wphdr.block_samples && wpc->wvc_flag) read_wvc_block(wpc); + + // if the block does NOT have any audio, call unpack_init() to process non-audio stuff + + if(!wps->wphdr.block_samples) + { + if(!wps->init_done && !unpack_init(wpc)) wpc->crc_errors++; + + wps->init_done = TRUE; + } + } + + // if the current block has no audio, or it's not the first block of a multichannel + // sequence, or the sample we're on is past the last sample in this block...we need + // to loop back and read the next block + + if(!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || + wps->sample_index >= GET_BLOCK_INDEX(wps->wphdr) + wps->wphdr.block_samples) + continue; + + // There seems to be some missing data, like a block was corrupted or something. + // If it's not too much data, just fill in with silence here and loop back. + + if(wps->sample_index < GET_BLOCK_INDEX(wps->wphdr)) + { + int32_t zvalue = (wps->wphdr.flags & DSD_FLAG) ? 0x55 : 0; + + samples_to_unpack = (uint32_t)(GET_BLOCK_INDEX(wps->wphdr) - wps->sample_index); + + if(!samples_to_unpack || samples_to_unpack > 262144) + { + strcpy(wpc->error_message, "discontinuity found, aborting file!"); + wps->wphdr.block_samples = 0; + wps->wphdr.ckSize = 24; + break; + } + + if(samples_to_unpack > samples) samples_to_unpack = samples; + + wps->sample_index += samples_to_unpack; + samples_unpacked += samples_to_unpack; + samples -= samples_to_unpack; + + samples_to_unpack *= (wpc->reduced_channels ? wpc->reduced_channels : num_channels); + + while(samples_to_unpack--) *bptr++ = zvalue; + + continue; + } + + // calculate number of samples to process from this block, then initialize the decoder for + // this block if we haven't already + + samples_to_unpack = (uint32_t)(GET_BLOCK_INDEX(wps->wphdr) + wps->wphdr.block_samples - wps->sample_index); + + if(samples_to_unpack > samples) samples_to_unpack = samples; + + if(!wps->init_done && !unpack_init(wpc)) wpc->crc_errors++; + + wps->init_done = TRUE; + + // if this block is not the final block of a multichannel sequence (and we're not truncating + // to stereo), then enter this conditional block...otherwise we just unpack the samples directly + + if(!wpc->reduced_channels && !(wps->wphdr.flags & FINAL_BLOCK)) + { + int32_t *temp_buffer = malloc(samples_to_unpack * 8), *src, *dst; + int offset = 0; // offset to next channel in sequence (0 to num_channels - 1) + uint32_t samcnt; + + // since we are getting samples from multiple bocks in a multichannel sequence, we must + // allocate a temporary buffer to unpack to so that we can re-interleave the samples + + if(!temp_buffer) break; + + // loop through all the streams... + + while(1) + { + + // if the stream has not been allocated and corresponding block read, do that here... + + if(wpc->current_stream == wpc->num_streams) + { + wpc->streams = realloc(wpc->streams, (wpc->num_streams + 1) * sizeof(wpc->streams[0])); + + if(!wpc->streams) break; + + wps = wpc->streams[wpc->num_streams++] = malloc(sizeof(WavpackStream)); + + if(!wps) break; + + CLEAR(*wps); + bcount = read_next_header(wpc->reader, wpc->wv_in, &wps->wphdr); + + if(bcount == (uint32_t)-1) + { + wpc->streams[0]->wphdr.block_samples = 0; + wpc->streams[0]->wphdr.ckSize = 24; + file_done = TRUE; + break; + } + + wps->blockbuff = malloc(wps->wphdr.ckSize + 8); + + if(!wps->blockbuff) break; + + memcpy(wps->blockbuff, &wps->wphdr, 32); + + if(wpc->reader->read_bytes(wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) + { + wpc->streams[0]->wphdr.block_samples = 0; + wpc->streams[0]->wphdr.ckSize = 24; + file_done = TRUE; + break; + } + + // render corrupt blocks harmless + if(!WavpackVerifySingleBlock(wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) + { + wps->wphdr.ckSize = sizeof(WavpackHeader) - 8; + wps->wphdr.block_samples = 0; + memcpy(wps->blockbuff, &wps->wphdr, 32); + } + + // potentially adjusting block_index must be done AFTER verifying block + + if(wpc->open_flags & OPEN_STREAMING) + SET_BLOCK_INDEX(wps->wphdr, wps->sample_index = 0); + else + SET_BLOCK_INDEX(wps->wphdr, GET_BLOCK_INDEX(wps->wphdr) - wpc->initial_index); + + memcpy(wps->blockbuff, &wps->wphdr, 32); + + // if this block has audio, and we're in hybrid lossless mode, read the matching wvc block + + if(wpc->wvc_flag) read_wvc_block(wpc); + + // initialize the unpacker for this block + + if(!unpack_init(wpc)) wpc->crc_errors++; + + wps->init_done = TRUE; + } + else + wps = wpc->streams[wpc->current_stream]; + + // unpack the correct number of samples (either mono or stereo) into the temp buffer + +#ifdef ENABLE_DSD + if(wps->wphdr.flags & DSD_FLAG) + unpack_dsd_samples(wpc, src = temp_buffer, samples_to_unpack); + else +#endif + unpack_samples(wpc, src = temp_buffer, samples_to_unpack); + + samcnt = samples_to_unpack; + dst = bptr + offset; + + // if the block is mono, copy the samples from the single channel into the destination + // using num_channels as the stride + + if(wps->wphdr.flags & MONO_FLAG) + { + while(samcnt--) + { + dst[0] = *src++; + dst += num_channels; + } + + offset++; + } + + // if the block is stereo, and we don't have room for two more channels, just copy one + // and flag an error + + else if(offset == num_channels - 1) + { + while(samcnt--) + { + dst[0] = src[0]; + dst += num_channels; + src += 2; + } + + wpc->crc_errors++; + offset++; + } + + // otherwise copy the stereo samples into the destination + + else + { + while(samcnt--) + { + dst[0] = *src++; + dst[1] = *src++; + dst += num_channels; + } + + offset += 2; + } + + // check several clues that we're done with this set of blocks and exit if we are; else do next stream + + if((wps->wphdr.flags & FINAL_BLOCK) || wpc->current_stream == wpc->max_streams - 1 || + offset == num_channels) + break; + else + wpc->current_stream++; + } + + // if we didn't get all the channels we expected, mute the buffer and flag an error + + if(offset != num_channels) + { + if(wps->wphdr.flags & DSD_FLAG) + { + int samples_to_zero = samples_to_unpack * num_channels; + int32_t *zptr = bptr; + + while(samples_to_zero--) *zptr++ = 0x55; + } + else + memset(bptr, 0, samples_to_unpack * num_channels * 4); + + wpc->crc_errors++; + } + + // go back to the first stream (we're going to leave them all loaded for now because they might have more + // samples) and free the temp buffer + + wps = wpc->streams[wpc->current_stream = 0]; + free(temp_buffer); + } + // catch the error situation where we have only one channel but run into a stereo block + // (this avoids overwriting the caller's buffer) + else if(!(wps->wphdr.flags & MONO_FLAG) && (num_channels == 1 || wpc->reduced_channels == 1)) + { + memset(bptr, 0, samples_to_unpack * sizeof(*bptr)); + wps->sample_index += samples_to_unpack; + wpc->crc_errors++; + } +#ifdef ENABLE_DSD + else if(wps->wphdr.flags & DSD_FLAG) + unpack_dsd_samples(wpc, bptr, samples_to_unpack); +#endif + else + unpack_samples(wpc, bptr, samples_to_unpack); + + if(file_done) + { + strcpy(wpc->error_message, "can't read all of last block!"); + break; + } + + if(wpc->reduced_channels) + bptr += samples_to_unpack * wpc->reduced_channels; + else + bptr += samples_to_unpack * num_channels; + + samples_unpacked += samples_to_unpack; + samples -= samples_to_unpack; + + // if we just finished a block, check for a calculated crc error + // (and back up the streams a little if possible in case we passed a header) + + if(wps->sample_index == GET_BLOCK_INDEX(wps->wphdr) + wps->wphdr.block_samples) + { + if(check_crc_error(wpc)) + { + int32_t *zptr = bptr, zvalue = (wps->wphdr.flags & DSD_FLAG) ? 0x55 : 0; + uint32_t samples_to_zero = wps->wphdr.block_samples; + + if(samples_to_zero > samples_to_unpack) samples_to_zero = samples_to_unpack; + + samples_to_zero *= (wpc->reduced_channels ? wpc->reduced_channels : num_channels); + + while(samples_to_zero--) *--zptr = zvalue; + + if(wps->blockbuff && wpc->reader->can_seek(wpc->wv_in)) + { + int32_t rseek = ((WavpackHeader *)wps->blockbuff)->ckSize / 3; + wpc->reader->set_pos_rel(wpc->wv_in, (rseek > 16384) ? -16384 : -rseek, SEEK_CUR); + } + + if(wpc->wvc_flag && wps->block2buff && wpc->reader->can_seek(wpc->wvc_in)) + { + int32_t rseek = ((WavpackHeader *)wps->block2buff)->ckSize / 3; + wpc->reader->set_pos_rel(wpc->wvc_in, (rseek > 16384) ? -16384 : -rseek, SEEK_CUR); + } + + wpc->crc_errors++; + } + } + + if(wpc->total_samples != -1 && wps->sample_index == wpc->total_samples) break; + } + +#ifdef ENABLE_DSD + if(wpc->decimation_context) decimate_dsd_run(wpc->decimation_context, buffer, samples_unpacked); +#endif + + return samples_unpacked; +} diff --git a/wavpack/wavpack.h b/wavpack/wavpack.h new file mode 100644 index 0000000..51ca3ed --- /dev/null +++ b/wavpack/wavpack.h @@ -0,0 +1,436 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2016 David Bryant. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// wavpack.h + +#ifndef WAVPACK_H +#define WAVPACK_H + +// This header file contains all the definitions required to use the +// functions in "wputils.c" to read and write WavPack files and streams. + +#include + +#if defined(_MSC_VER) && _MSC_VER < 1600 +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef __int64 int64_t; +typedef __int32 int32_t; +typedef __int16 int16_t; +typedef __int8 int8_t; +#else +#include +#endif + +// RIFF / wav header formats (these occur at the beginning of both wav files +// and pre-4.0 WavPack files that are not in the "raw" mode). Generally, an +// application using the library to read or write WavPack files will not be +// concerned with any of these. + +typedef struct +{ + char ckID[4]; + uint32_t ckSize; + char formType[4]; +} RiffChunkHeader; + +/*typedef struct { + char ckID [4]; + uint32_t ckSize; +} ChunkHeader;*/ + +#define ChunkHeaderFormat "4L" + +typedef struct +{ + uint16_t FormatTag, NumChannels; + uint32_t SampleRate, BytesPerSecond; + uint16_t BlockAlign, BitsPerSample; + uint16_t cbSize, ValidBitsPerSample; + int32_t ChannelMask; + uint16_t SubFormat; + char GUID[14]; +} WaveHeader; + +#define WaveHeaderFormat "SSLLSSSSLS" + +// This is the ONLY structure that occurs in WavPack files (as of version +// 4.0), and is the preamble to every block in both the .wv and .wvc +// files (in little-endian format). Normally, this structure has no use +// to an application using the library to read or write WavPack files, +// but if an application needs to manually parse WavPack files then this +// would be used (with appropriate endian correction). + +typedef struct +{ + char ckID[4]; + uint32_t ckSize; + int16_t version; + unsigned char block_index_u8; + unsigned char total_samples_u8; + uint32_t total_samples, block_index, block_samples, flags, crc; +} WavpackHeader; + +#define WavpackHeaderFormat "4LS2LLLLL" + +// Macros to access the 40-bit block_index field + +#define GET_BLOCK_INDEX(hdr) ((int64_t)(hdr).block_index + ((int64_t)(hdr).block_index_u8 << 32)) + +#define SET_BLOCK_INDEX(hdr, value) \ + do \ + { \ + int64_t tmp = (value); \ + (hdr).block_index = (uint32_t)tmp; \ + (hdr).block_index_u8 = (unsigned char)(tmp >> 32); \ + } while(0) + +// Macros to access the 40-bit total_samples field, which is complicated by the fact that +// all 1's in the lower 32 bits indicates "unknown" (regardless of upper 8 bits) + +#define GET_TOTAL_SAMPLES(hdr) \ + (((hdr).total_samples == (uint32_t)-1) \ + ? -1 \ + : (int64_t)(hdr).total_samples + ((int64_t)(hdr).total_samples_u8 << 32) - (hdr).total_samples_u8) + +#define SET_TOTAL_SAMPLES(hdr, value) \ + do \ + { \ + int64_t tmp = (value); \ + if(tmp < 0) \ + (hdr).total_samples = (uint32_t)-1; \ + else \ + { \ + tmp += (tmp / 0xffffffffLL); \ + (hdr).total_samples = (uint32_t)tmp; \ + (hdr).total_samples_u8 = (unsigned char)(tmp >> 32); \ + } \ + } while(0) + +// or-values for WavpackHeader.flags +#define BYTES_STORED 3 // 1-4 bytes/sample +#define MONO_FLAG 4 // not stereo +#define HYBRID_FLAG 8 // hybrid mode +#define JOINT_STEREO 0x10 // joint stereo +#define CROSS_DECORR 0x20 // no-delay cross decorrelation +#define HYBRID_SHAPE 0x40 // noise shape (hybrid mode only) +#define FLOAT_DATA 0x80 // ieee 32-bit floating point data + +#define INT32_DATA 0x100 // special extended int handling +#define HYBRID_BITRATE 0x200 // bitrate noise (hybrid mode only) +#define HYBRID_BALANCE 0x400 // balance noise (hybrid stereo mode only) + +#define INITIAL_BLOCK 0x800 // initial block of multichannel segment +#define FINAL_BLOCK 0x1000 // final block of multichannel segment + +#define SHIFT_LSB 13 +#define SHIFT_MASK (0x1fL << SHIFT_LSB) + +#define MAG_LSB 18 +#define MAG_MASK (0x1fL << MAG_LSB) + +#define SRATE_LSB 23 +#define SRATE_MASK (0xfL << SRATE_LSB) + +#define FALSE_STEREO 0x40000000 // block is stereo, but data is mono +#define NEW_SHAPING 0x20000000 // use IIR filter for negative shaping + +#define MONO_DATA (MONO_FLAG | FALSE_STEREO) + +// Introduced in WavPack 5.0: +#define HAS_CHECKSUM 0x10000000 // block contains a trailing checksum +#define DSD_FLAG 0x80000000 // block is encoded DSD (1-bit PCM) + +#define IGNORED_FLAGS 0x08000000 // reserved, but ignore if encountered +#define UNKNOWN_FLAGS 0x00000000 // we no longer have any of these spares + +#define MIN_STREAM_VERS 0x402 // lowest stream version we'll decode +#define MAX_STREAM_VERS 0x410 // highest stream version we'll decode or encode + +// These are the mask bit definitions for the metadata chunk id byte (see format.txt) + +#define ID_UNIQUE 0x3f +#define ID_OPTIONAL_DATA 0x20 +#define ID_ODD_SIZE 0x40 +#define ID_LARGE 0x80 + +#define ID_DUMMY 0x0 +#define ID_ENCODER_INFO 0x1 +#define ID_DECORR_TERMS 0x2 +#define ID_DECORR_WEIGHTS 0x3 +#define ID_DECORR_SAMPLES 0x4 +#define ID_ENTROPY_VARS 0x5 +#define ID_HYBRID_PROFILE 0x6 +#define ID_SHAPING_WEIGHTS 0x7 +#define ID_FLOAT_INFO 0x8 +#define ID_INT32_INFO 0x9 +#define ID_WV_BITSTREAM 0xa +#define ID_WVC_BITSTREAM 0xb +#define ID_WVX_BITSTREAM 0xc +#define ID_CHANNEL_INFO 0xd + +#define ID_RIFF_HEADER (ID_OPTIONAL_DATA | 0x1) +#define ID_RIFF_TRAILER (ID_OPTIONAL_DATA | 0x2) +#define ID_ALT_HEADER (ID_OPTIONAL_DATA | 0x3) +#define ID_ALT_TRAILER (ID_OPTIONAL_DATA | 0x4) +#define ID_CONFIG_BLOCK (ID_OPTIONAL_DATA | 0x5) +#define ID_MD5_CHECKSUM (ID_OPTIONAL_DATA | 0x6) +#define ID_SAMPLE_RATE (ID_OPTIONAL_DATA | 0x7) +#define ID_ALT_EXTENSION (ID_OPTIONAL_DATA | 0x8) +#define ID_ALT_MD5_CHECKSUM (ID_OPTIONAL_DATA | 0x9) +#define ID_NEW_CONFIG_BLOCK (ID_OPTIONAL_DATA | 0xa) +#define ID_BLOCK_CHECKSUM (ID_OPTIONAL_DATA | 0xf) + +///////////////////////// WavPack Configuration /////////////////////////////// + +// This external structure is used during encode to provide configuration to +// the encoding engine and during decoding to provide fle information back to +// the higher level functions. Not all fields are used in both modes. + +typedef struct +{ + float bitrate, shaping_weight; + int bits_per_sample, bytes_per_sample; + int qmode, flags, xmode, num_channels, float_norm_exp; + int32_t block_samples, extra_flags, sample_rate, channel_mask; + unsigned char md5_checksum[16], md5_read; + int num_tag_strings; // this field is not used + char **tag_strings; // this field is not used +} WavpackConfig; + +#define CONFIG_HYBRID_FLAG 8 // hybrid mode +#define CONFIG_JOINT_STEREO 0x10 // joint stereo +#define CONFIG_CROSS_DECORR 0x20 // no-delay cross decorrelation +#define CONFIG_HYBRID_SHAPE 0x40 // noise shape (hybrid mode only) +#define CONFIG_FAST_FLAG 0x200 // fast mode +#define CONFIG_HIGH_FLAG 0x800 // high quality mode +#define CONFIG_VERY_HIGH_FLAG 0x1000 // very high +#define CONFIG_BITRATE_KBPS 0x2000 // bitrate is kbps, not bits / sample +#define CONFIG_SHAPE_OVERRIDE 0x8000 // shaping mode specified +#define CONFIG_JOINT_OVERRIDE 0x10000 // joint-stereo mode specified +#define CONFIG_DYNAMIC_SHAPING 0x20000 // dynamic noise shaping +#define CONFIG_CREATE_EXE 0x40000 // create executable +#define CONFIG_CREATE_WVC 0x80000 // create correction file +#define CONFIG_OPTIMIZE_WVC 0x100000 // maximize bybrid compression +#define CONFIG_COMPATIBLE_WRITE 0x400000 // write files for decoders < 4.3 +#define CONFIG_CALC_NOISE 0x800000 // calc noise in hybrid mode +#define CONFIG_EXTRA_MODE 0x2000000 // extra processing mode +#define CONFIG_SKIP_WVX 0x4000000 // no wvx stream w/ floats & big ints +#define CONFIG_MD5_CHECKSUM 0x8000000 // store MD5 signature +#define CONFIG_MERGE_BLOCKS 0x10000000 // merge blocks of equal redundancy (for lossyWAV) +#define CONFIG_PAIR_UNDEF_CHANS 0x20000000 // encode undefined channels in stereo pairs +#define CONFIG_OPTIMIZE_MONO 0x80000000 // optimize for mono streams posing as stereo + +// The lower 8 bits of qmode indicate the use of new features in version 5 that (presently) +// only apply to Core Audio Files (CAF) and DSD files, but could apply to other things too. +// These flags are stored in the file and can be retrieved by a decoder that is aware of +// them, but the individual bits are meaningless to the library. If ANY of these bits are +// set then the MD5 sum is written with a new ID so that old decoders will not see it +// (because these features will cause the MD5 sum to be different and fail). + +#define QMODE_BIG_ENDIAN 0x1 // big-endian data format (opposite of WAV format) +#define QMODE_SIGNED_BYTES 0x2 // 8-bit audio data is signed (opposite of WAV format) +#define QMODE_UNSIGNED_WORDS 0x4 // audio data (other than 8-bit) is unsigned (opposite of WAV format) +#define QMODE_REORDERED_CHANS 0x8 // source channels were not Microsoft order, so they were reordered +#define QMODE_DSD_LSB_FIRST 0x10 // DSD bytes, LSB first (most Sony .dsf files) +#define QMODE_DSD_MSB_FIRST 0x20 // DSD bytes, MSB first (Philips .dff files) +#define QMODE_DSD_IN_BLOCKS 0x40 // DSD data is blocked by channels (Sony .dsf only) +#define QMODE_DSD_AUDIO (QMODE_DSD_LSB_FIRST | QMODE_DSD_MSB_FIRST) + +// The rest of the qmode word is reserved for the private use of the command-line programs +// and are ignored by the library (and not stored either). They really should not be defined +// here, but I thought it would be a good idea to have all the definitions together. + +#define QMODE_ADOBE_MODE 0x100 // user specified Adobe mode +#define QMODE_NO_STORE_WRAPPER 0x200 // user specified to not store audio file wrapper (RIFF, CAFF, etc.) +#define QMODE_CHANS_UNASSIGNED 0x400 // user specified "..." in --channel-order option +#define QMODE_IGNORE_LENGTH 0x800 // user specified to ignore length in file header +#define QMODE_RAW_PCM 0x1000 // user specified raw PCM format (no header present) + +////////////// Callbacks used for reading & writing WavPack streams ////////// + +typedef struct +{ + int32_t (*read_bytes)(void *id, void *data, int32_t bcount); + uint32_t (*get_pos)(void *id); + int (*set_pos_abs)(void *id, uint32_t pos); + int (*set_pos_rel)(void *id, int32_t delta, int mode); + int (*push_back_byte)(void *id, int c); + uint32_t (*get_length)(void *id); + int (*can_seek)(void *id); + + // this callback is for writing edited tags only + int32_t (*write_bytes)(void *id, void *data, int32_t bcount); +} WavpackStreamReader; + +// Extended version of structure for handling large files and added +// functionality for truncating and closing files + +typedef struct +{ + int32_t (*read_bytes)(void *id, void *data, int32_t bcount); + int32_t (*write_bytes)(void *id, void *data, int32_t bcount); + int64_t (*get_pos)(void *id); // new signature for large files + int (*set_pos_abs)(void *id, int64_t pos); // new signature for large files + int (*set_pos_rel)(void *id, int64_t delta, int mode); // new signature for large files + int (*push_back_byte)(void *id, int c); + int64_t (*get_length)(void *id); // new signature for large files + int (*can_seek)(void *id); + int (*truncate_here)(void *id); // new function to truncate file at current position + int (*close)(void *id); // new function to close file +} WavpackStreamReader64; + +typedef int (*WavpackBlockOutput)(void *id, void *data, int32_t bcount); + +//////////////////////////// function prototypes ///////////////////////////// + +typedef void WavpackContext; + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MAX_WAVPACK_SAMPLES ((1LL << 40) - 257) + + WavpackContext *WavpackOpenRawDecoder(void *main_data, int32_t main_size, void *corr_data, int32_t corr_size, + int16_t version, char *error, int flags, int norm_offset); + + WavpackContext *WavpackOpenFileInputEx64(WavpackStreamReader64 *reader, void *wv_id, void *wvc_id, char *error, + int flags, int norm_offset); + WavpackContext *WavpackOpenFileInputEx(WavpackStreamReader *reader, void *wv_id, void *wvc_id, char *error, + int flags, int norm_offset); + WavpackContext *WavpackOpenFileInput(const char *infilename, char *error, int flags, int norm_offset); + +#define OPEN_WVC 0x1 // open/read "correction" file +#define OPEN_TAGS 0x2 // read ID3v1 / APEv2 tags (seekable file) +#define OPEN_WRAPPER 0x4 // make audio wrapper available (i.e. RIFF) +#define OPEN_2CH_MAX 0x8 // open multichannel as stereo (no downmix) +#define OPEN_NORMALIZE 0x10 // normalize floating point data to +/- 1.0 +#define OPEN_STREAMING \ + 0x20 // "streaming" mode blindly unpacks blocks + // w/o regard to header file position info +#define OPEN_EDIT_TAGS 0x40 // allow editing of tags +#define OPEN_FILE_UTF8 0x80 // assume filenames are UTF-8 encoded, not ANSI (Windows only) + + // new for version 5 + +#define OPEN_DSD_NATIVE \ + 0x100 // open DSD files as bitstreams + // (returned as 8-bit "samples" stored in 32-bit words) +#define OPEN_DSD_AS_PCM 0x200 // open DSD files as 24-bit PCM (decimated 8x) +#define OPEN_ALT_TYPES \ + 0x400 // application is aware of alternate file types & qmode + // (just affects retrieving wrappers & MD5 checksums) +#define OPEN_NO_CHECKSUM 0x800 // don't verify block checksums before decoding + + int WavpackGetMode(WavpackContext *wpc); + +#define MODE_WVC 0x1 +#define MODE_LOSSLESS 0x2 +#define MODE_HYBRID 0x4 +#define MODE_FLOAT 0x8 +#define MODE_VALID_TAG 0x10 +#define MODE_HIGH 0x20 +#define MODE_FAST 0x40 +#define MODE_EXTRA 0x80 // extra mode used, see MODE_XMODE for possible level +#define MODE_APETAG 0x100 +#define MODE_SFX 0x200 +#define MODE_VERY_HIGH 0x400 +#define MODE_MD5 0x800 +#define MODE_XMODE 0x7000 // mask for extra level (1-6, 0=unknown) +#define MODE_DNS 0x8000 + + int WavpackVerifySingleBlock(unsigned char *buffer, int verify_checksum); + int WavpackGetQualifyMode(WavpackContext *wpc); + char *WavpackGetErrorMessage(WavpackContext *wpc); + int WavpackGetVersion(WavpackContext *wpc); + char *WavpackGetFileExtension(WavpackContext *wpc); + unsigned char WavpackGetFileFormat(WavpackContext *wpc); + uint32_t WavpackUnpackSamples(WavpackContext *wpc, int32_t *buffer, uint32_t samples); + uint32_t WavpackGetNumSamples(WavpackContext *wpc); + int64_t WavpackGetNumSamples64(WavpackContext *wpc); + uint32_t WavpackGetNumSamplesInFrame(WavpackContext *wpc); + uint32_t WavpackGetSampleIndex(WavpackContext *wpc); + int64_t WavpackGetSampleIndex64(WavpackContext *wpc); + int WavpackGetNumErrors(WavpackContext *wpc); + int WavpackLossyBlocks(WavpackContext *wpc); + int WavpackSeekSample(WavpackContext *wpc, uint32_t sample); + int WavpackSeekSample64(WavpackContext *wpc, int64_t sample); + WavpackContext *WavpackCloseFile(WavpackContext *wpc); + uint32_t WavpackGetSampleRate(WavpackContext *wpc); + uint32_t WavpackGetNativeSampleRate(WavpackContext *wpc); + int WavpackGetBitsPerSample(WavpackContext *wpc); + int WavpackGetBytesPerSample(WavpackContext *wpc); + int WavpackGetNumChannels(WavpackContext *wpc); + int WavpackGetChannelMask(WavpackContext *wpc); + int WavpackGetReducedChannels(WavpackContext *wpc); + int WavpackGetFloatNormExp(WavpackContext *wpc); + int WavpackGetMD5Sum(WavpackContext *wpc, unsigned char data[16]); + void WavpackGetChannelIdentities(WavpackContext *wpc, unsigned char *identities); + uint32_t WavpackGetChannelLayout(WavpackContext *wpc, unsigned char *reorder); + uint32_t WavpackGetWrapperBytes(WavpackContext *wpc); + unsigned char *WavpackGetWrapperData(WavpackContext *wpc); + void WavpackFreeWrapper(WavpackContext *wpc); + void WavpackSeekTrailingWrapper(WavpackContext *wpc); + double WavpackGetProgress(WavpackContext *wpc); + uint32_t WavpackGetFileSize(WavpackContext *wpc); + int64_t WavpackGetFileSize64(WavpackContext *wpc); + double WavpackGetRatio(WavpackContext *wpc); + double WavpackGetAverageBitrate(WavpackContext *wpc, int count_wvc); + double WavpackGetInstantBitrate(WavpackContext *wpc); + int WavpackGetNumTagItems(WavpackContext *wpc); + int WavpackGetTagItem(WavpackContext *wpc, const char *item, char *value, int size); + int WavpackGetTagItemIndexed(WavpackContext *wpc, int index, char *item, int size); + int WavpackGetNumBinaryTagItems(WavpackContext *wpc); + int WavpackGetBinaryTagItem(WavpackContext *wpc, const char *item, char *value, int size); + int WavpackGetBinaryTagItemIndexed(WavpackContext *wpc, int index, char *item, int size); + int WavpackAppendTagItem(WavpackContext *wpc, const char *item, const char *value, int vsize); + int WavpackAppendBinaryTagItem(WavpackContext *wpc, const char *item, const char *value, int vsize); + int WavpackDeleteTagItem(WavpackContext *wpc, const char *item); + int WavpackWriteTag(WavpackContext *wpc); + + WavpackContext *WavpackOpenFileOutput(WavpackBlockOutput blockout, void *wv_id, void *wvc_id); + void WavpackSetFileInformation(WavpackContext *wpc, char *file_extension, unsigned char file_format); + +#define WP_FORMAT_WAV 0 // Microsoft RIFF, including BWF and RF64 varients +#define WP_FORMAT_W64 1 // Sony Wave64 +#define WP_FORMAT_CAF 2 // Apple CoreAudio +#define WP_FORMAT_DFF 3 // Philips DSDIFF +#define WP_FORMAT_DSF 4 // Sony DSD Format + + int WavpackSetConfiguration(WavpackContext *wpc, WavpackConfig *config, uint32_t total_samples); + int WavpackSetConfiguration64(WavpackContext *wpc, WavpackConfig *config, int64_t total_samples, + const unsigned char *chan_ids); + int WavpackSetChannelLayout(WavpackContext *wpc, uint32_t layout_tag, const unsigned char *reorder); + int WavpackAddWrapper(WavpackContext *wpc, void *data, uint32_t bcount); + int WavpackStoreMD5Sum(WavpackContext *wpc, unsigned char data[16]); + int WavpackPackInit(WavpackContext *wpc); + int WavpackPackSamples(WavpackContext *wpc, int32_t *sample_buffer, uint32_t sample_count); + int WavpackFlushSamples(WavpackContext *wpc); + void WavpackUpdateNumSamples(WavpackContext *wpc, void *first_block); + void *WavpackGetWrapperLocation(void *first_block, uint32_t *size); + double WavpackGetEncodedNoise(WavpackContext *wpc, double *peak); + + void WavpackFloatNormalize(int32_t *values, int32_t num_values, int delta_exp); + + void WavpackLittleEndianToNative(void *data, char *format); + void WavpackNativeToLittleEndian(void *data, char *format); + void WavpackBigEndianToNative(void *data, char *format); + void WavpackNativeToBigEndian(void *data, char *format); + + uint32_t WavpackGetLibraryVersion(void); + const char *WavpackGetLibraryVersionString(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wavpack/wavpack_local.h b/wavpack/wavpack_local.h new file mode 100644 index 0000000..4605627 --- /dev/null +++ b/wavpack/wavpack_local.h @@ -0,0 +1,945 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2013 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// wavpack_local.h + +#ifndef WAVPACK_LOCAL_H +#define WAVPACK_LOCAL_H + +#if defined(_WIN32) +#define strdup(x) _strdup(x) +#define FASTCALL __fastcall +#else +#define FASTCALL +#endif + +#if defined(_WIN32) || (defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && (BYTE_ORDER == LITTLE_ENDIAN)) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) +#define BITSTREAM_SHORTS // use 16-bit "shorts" for reading/writing bitstreams (instead of chars) +// (only works on little-endian machines) +#endif + +#include + +// This header file contains all the definitions required by WavPack. + +#if defined(_MSC_VER) && _MSC_VER < 1600 +#include +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef __int64 int64_t; +typedef __int32 int32_t; +typedef __int16 int16_t; +typedef __int8 int8_t; +#else +#include +#endif + +// Because the C99 specification states that "The order of allocation of +// bit-fields within a unit (high-order to low-order or low-order to +// high-order) is implementation-defined" (6.7.2.1), I decided to change +// the representation of floating-point values from a structure of +// bit-fields to a 32-bit integer with access macros. Note that the WavPack +// library doesn't use any floating-point math to implement compression of +// floating-point data (although a little floating-point math is used in +// high-level functions unrelated to the codec). + +typedef int32_t f32; + +#define get_mantissa(f) ((f) & 0x7fffff) +#define get_magnitude(f) ((f) & 0x7fffffff) +#define get_exponent(f) (((f) >> 23) & 0xff) +#define get_sign(f) (((f) >> 31) & 0x1) + +#define set_mantissa(f, v) (f) ^= (((f) ^ (v)) & 0x7fffff) +#define set_exponent(f, v) (f) ^= (((f) ^ ((v) << 23)) & 0x7f800000) +#define set_sign(f, v) (f) ^= (((f) ^ ((v) << 31)) & 0x80000000) + +#include + +#define FALSE 0 +#define TRUE 1 + +// ID3v1 and APEv2 TAG formats (may occur at the end of WavPack files) + +typedef struct +{ + char tag_id[3], title[30], artist[30], album[30]; + char year[4], comment[30], genre; +} ID3_Tag; + +typedef struct +{ + char ID[8]; + int32_t version, length, item_count, flags; + char res[8]; +} APE_Tag_Hdr; + +#define APE_Tag_Hdr_Format "8LLLL" + +#define APE_TAG_TYPE_TEXT 0x0 +#define APE_TAG_TYPE_BINARY 0x1 +#define APE_TAG_THIS_IS_HEADER 0x20000000 +#define APE_TAG_CONTAINS_HEADER 0x80000000 +#define APE_TAG_MAX_LENGTH (1024 * 1024 * 16) + +typedef struct +{ + int64_t tag_file_pos; + int tag_begins_file; + ID3_Tag id3_tag; + APE_Tag_Hdr ape_tag_hdr; + unsigned char *ape_tag_data; +} M_Tag; + +// RIFF / wav header formats (these occur at the beginning of both wav files +// and pre-4.0 WavPack files that are not in the "raw" mode) + +typedef struct +{ + char ckID[4]; + uint32_t ckSize; + char formType[4]; +} RiffChunkHeader; + +typedef struct +{ + char ckID[4]; + uint32_t ckSize; +} ChunkHeader; + +#define ChunkHeaderFormat "4L" + +typedef struct +{ + uint16_t FormatTag, NumChannels; + uint32_t SampleRate, BytesPerSecond; + uint16_t BlockAlign, BitsPerSample; + uint16_t cbSize, ValidBitsPerSample; + int32_t ChannelMask; + uint16_t SubFormat; + char GUID[14]; +} WaveHeader; + +#define WaveHeaderFormat "SSLLSSSSLS" + +////////////////////////////// WavPack Header ///////////////////////////////// + +// Note that this is the ONLY structure that is written to (or read from) +// WavPack 4.0 files, and is the preamble to every block in both the .wv +// and .wvc files. + +typedef struct +{ + char ckID[4]; + uint32_t ckSize; + int16_t version; + unsigned char block_index_u8; + unsigned char total_samples_u8; + uint32_t total_samples, block_index, block_samples, flags, crc; +} WavpackHeader; + +#define WavpackHeaderFormat "4LS2LLLLL" + +// Macros to access the 40-bit block_index field + +#define GET_BLOCK_INDEX(hdr) ((int64_t)(hdr).block_index + ((int64_t)(hdr).block_index_u8 << 32)) + +#define SET_BLOCK_INDEX(hdr, value) \ + do \ + { \ + int64_t tmp = (value); \ + (hdr).block_index = (uint32_t)tmp; \ + (hdr).block_index_u8 = (unsigned char)(tmp >> 32); \ + } while(0) + +// Macros to access the 40-bit total_samples field, which is complicated by the fact that +// all 1's in the lower 32 bits indicates "unknown" (regardless of upper 8 bits) + +#define GET_TOTAL_SAMPLES(hdr) \ + (((hdr).total_samples == (uint32_t)-1) \ + ? -1 \ + : (int64_t)(hdr).total_samples + ((int64_t)(hdr).total_samples_u8 << 32) - (hdr).total_samples_u8) + +#define SET_TOTAL_SAMPLES(hdr, value) \ + do \ + { \ + int64_t tmp = (value); \ + if(tmp < 0) \ + (hdr).total_samples = (uint32_t)-1; \ + else \ + { \ + tmp += (tmp / (int64_t)0xffffffff); \ + (hdr).total_samples = (uint32_t)tmp; \ + (hdr).total_samples_u8 = (unsigned char)(tmp >> 32); \ + } \ + } while(0) + +// or-values for "flags" + +#define BYTES_STORED 3 // 1-4 bytes/sample +#define MONO_FLAG 4 // not stereo +#define HYBRID_FLAG 8 // hybrid mode +#define JOINT_STEREO 0x10 // joint stereo +#define CROSS_DECORR 0x20 // no-delay cross decorrelation +#define HYBRID_SHAPE 0x40 // noise shape (hybrid mode only) +#define FLOAT_DATA 0x80 // ieee 32-bit floating point data + +#define INT32_DATA 0x100 // special extended int handling +#define HYBRID_BITRATE 0x200 // bitrate noise (hybrid mode only) +#define HYBRID_BALANCE 0x400 // balance noise (hybrid stereo mode only) + +#define INITIAL_BLOCK 0x800 // initial block of multichannel segment +#define FINAL_BLOCK 0x1000 // final block of multichannel segment + +#define SHIFT_LSB 13 +#define SHIFT_MASK (0x1fL << SHIFT_LSB) + +#define MAG_LSB 18 +#define MAG_MASK (0x1fL << MAG_LSB) + +#define SRATE_LSB 23 +#define SRATE_MASK (0xfL << SRATE_LSB) + +#define FALSE_STEREO 0x40000000 // block is stereo, but data is mono +#define NEW_SHAPING 0x20000000 // use IIR filter for negative shaping + +#define MONO_DATA (MONO_FLAG | FALSE_STEREO) + +// Introduced in WavPack 5.0: +#define HAS_CHECKSUM 0x10000000 // block contains a trailing checksum +#define DSD_FLAG 0x80000000 // block is encoded DSD (1-bit PCM) + +#define IGNORED_FLAGS 0x08000000 // reserved, but ignore if encountered +#define UNKNOWN_FLAGS 0x00000000 // we no longer have any of these spares + +#define MIN_STREAM_VERS 0x402 // lowest stream version we'll decode +#define MAX_STREAM_VERS 0x410 // highest stream version we'll decode or encode +// (only stream version to support mono optimization) +#define CUR_STREAM_VERS 0x407 // universally compatible stream version + +//////////////////////////// WavPack Metadata ///////////////////////////////// + +// This is an internal representation of metadata. + +typedef struct +{ + int32_t byte_length; + void *data; + unsigned char id; +} WavpackMetadata; + +#define ID_UNIQUE 0x3f +#define ID_OPTIONAL_DATA 0x20 +#define ID_ODD_SIZE 0x40 +#define ID_LARGE 0x80 + +#define ID_DUMMY 0x0 +#define ID_ENCODER_INFO 0x1 +#define ID_DECORR_TERMS 0x2 +#define ID_DECORR_WEIGHTS 0x3 +#define ID_DECORR_SAMPLES 0x4 +#define ID_ENTROPY_VARS 0x5 +#define ID_HYBRID_PROFILE 0x6 +#define ID_SHAPING_WEIGHTS 0x7 +#define ID_FLOAT_INFO 0x8 +#define ID_INT32_INFO 0x9 +#define ID_WV_BITSTREAM 0xa +#define ID_WVC_BITSTREAM 0xb +#define ID_WVX_BITSTREAM 0xc +#define ID_CHANNEL_INFO 0xd +#define ID_DSD_BLOCK 0xe + +#define ID_RIFF_HEADER (ID_OPTIONAL_DATA | 0x1) +#define ID_RIFF_TRAILER (ID_OPTIONAL_DATA | 0x2) +#define ID_ALT_HEADER (ID_OPTIONAL_DATA | 0x3) +#define ID_ALT_TRAILER (ID_OPTIONAL_DATA | 0x4) +#define ID_CONFIG_BLOCK (ID_OPTIONAL_DATA | 0x5) +#define ID_MD5_CHECKSUM (ID_OPTIONAL_DATA | 0x6) +#define ID_SAMPLE_RATE (ID_OPTIONAL_DATA | 0x7) +#define ID_ALT_EXTENSION (ID_OPTIONAL_DATA | 0x8) +#define ID_ALT_MD5_CHECKSUM (ID_OPTIONAL_DATA | 0x9) +#define ID_NEW_CONFIG_BLOCK (ID_OPTIONAL_DATA | 0xa) +#define ID_CHANNEL_IDENTITIES (ID_OPTIONAL_DATA | 0xb) +#define ID_BLOCK_CHECKSUM (ID_OPTIONAL_DATA | 0xf) + +///////////////////////// WavPack Configuration /////////////////////////////// + +// This internal structure is used during encode to provide configuration to +// the encoding engine and during decoding to provide fle information back to +// the higher level functions. Not all fields are used in both modes. + +typedef struct +{ + float bitrate, shaping_weight; + int bits_per_sample, bytes_per_sample; + int qmode, flags, xmode, num_channels, float_norm_exp; + int32_t block_samples, extra_flags, sample_rate, channel_mask; + unsigned char md5_checksum[16], md5_read; + int num_tag_strings; + char **tag_strings; +} WavpackConfig; + +#define CONFIG_BYTES_STORED 3 // 1-4 bytes/sample +#define CONFIG_MONO_FLAG 4 // not stereo +#define CONFIG_HYBRID_FLAG 8 // hybrid mode +#define CONFIG_JOINT_STEREO 0x10 // joint stereo +#define CONFIG_CROSS_DECORR 0x20 // no-delay cross decorrelation +#define CONFIG_HYBRID_SHAPE 0x40 // noise shape (hybrid mode only) +#define CONFIG_FLOAT_DATA 0x80 // ieee 32-bit floating point data + +#define CONFIG_FAST_FLAG 0x200 // fast mode +#define CONFIG_HIGH_FLAG 0x800 // high quality mode +#define CONFIG_VERY_HIGH_FLAG 0x1000 // very high +#define CONFIG_BITRATE_KBPS 0x2000 // bitrate is kbps, not bits / sample +#define CONFIG_AUTO_SHAPING 0x4000 // automatic noise shaping +#define CONFIG_SHAPE_OVERRIDE 0x8000 // shaping mode specified +#define CONFIG_JOINT_OVERRIDE 0x10000 // joint-stereo mode specified +#define CONFIG_DYNAMIC_SHAPING 0x20000 // dynamic noise shaping +#define CONFIG_CREATE_EXE 0x40000 // create executable +#define CONFIG_CREATE_WVC 0x80000 // create correction file +#define CONFIG_OPTIMIZE_WVC 0x100000 // maximize bybrid compression +#define CONFIG_COMPATIBLE_WRITE 0x400000 // write files for decoders < 4.3 +#define CONFIG_CALC_NOISE 0x800000 // calc noise in hybrid mode +#define CONFIG_LOSSY_MODE 0x1000000 // obsolete (for information) +#define CONFIG_EXTRA_MODE 0x2000000 // extra processing mode +#define CONFIG_SKIP_WVX 0x4000000 // no wvx stream w/ floats & big ints +#define CONFIG_MD5_CHECKSUM 0x8000000 // compute & store MD5 signature +#define CONFIG_MERGE_BLOCKS 0x10000000 // merge blocks of equal redundancy (for lossyWAV) +#define CONFIG_PAIR_UNDEF_CHANS 0x20000000 // encode undefined channels in stereo pairs +#define CONFIG_OPTIMIZE_MONO 0x80000000 // optimize for mono streams posing as stereo + +#define QMODE_DSD_AUDIO 0x30 // if either of these is set in qmode (version 5.0) + +/* + * These config flags were never actually used, or are no longer used, or are + * used for something else now. They may be used in the future for what they + * say, or for something else. WavPack files in the wild *may* have some of + * these bit set in their config flags (with these older meanings), but only + * if the stream version is 0x410 or less than 0x407. Of course, this is not + * very important because once the file has been encoded, the config bits are + * just for information purposes (i.e., they do not affect decoding), + * + #define CONFIG_ADOBE_MODE 0x100 // "adobe" mode for 32-bit floats + #define CONFIG_VERY_FAST_FLAG 0x400 // double fast + #define CONFIG_COPY_TIME 0x20000 // copy file-time from source + #define CONFIG_QUALITY_MODE 0x200000 // psychoacoustic quality mode + #define CONFIG_RAW_FLAG 0x400000 // raw mode (not implemented yet) + #define CONFIG_QUIET_MODE 0x10000000 // don't report progress % + #define CONFIG_IGNORE_LENGTH 0x20000000 // ignore length in wav header + #define CONFIG_NEW_RIFF_HEADER 0x40000000 // generate new RIFF wav header + * + */ + +#define EXTRA_SCAN_ONLY 1 +#define EXTRA_STEREO_MODES 2 +#define EXTRA_TRY_DELTAS 8 +#define EXTRA_ADJUST_DELTAS 16 +#define EXTRA_SORT_FIRST 32 +#define EXTRA_BRANCHES 0x1c0 +#define EXTRA_SKIP_8TO16 512 +#define EXTRA_TERMS 0x3c00 +#define EXTRA_DUMP_TERMS 16384 +#define EXTRA_SORT_LAST 32768 + +//////////////////////////////// WavPack Stream /////////////////////////////// + +// This internal structure contains everything required to handle a WavPack +// "stream", which is defined as a stereo or mono stream of audio samples. For +// multichannel audio several of these would be required. Each stream contains +// pointers to hold a complete allocated block of WavPack data, although it's +// possible to decode WavPack blocks without buffering an entire block. + +typedef struct bs +{ +#ifdef BITSTREAM_SHORTS + uint16_t *buf, *end, *ptr; +#else + unsigned char *buf, *end, *ptr; +#endif + void (*wrap)(struct bs *bs); + int error, bc; + uint32_t sr; +} Bitstream; + +#define MAX_WRAPPER_BYTES 16777216 +#define NEW_MAX_STREAMS 4096 +#define OLD_MAX_STREAMS 8 +#define MAX_NTERMS 16 +#define MAX_TERM 8 + +// Note that this structure is directly accessed in assembly files, so modify with care + +struct decorr_pass +{ + int32_t term, delta, weight_A, weight_B; + int32_t samples_A[MAX_TERM], samples_B[MAX_TERM]; + int32_t aweight_A, aweight_B; + int32_t sum_A, sum_B; +}; + +typedef struct +{ + signed char joint_stereo, delta, terms[MAX_NTERMS + 1]; +} WavpackDecorrSpec; + +struct entropy_data +{ + uint32_t median[3], slow_level, error_limit; +}; + +struct words_data +{ + uint32_t bitrate_delta[2], bitrate_acc[2]; + uint32_t pend_data, holding_one, zeros_acc; + int holding_zero, pend_count; + struct entropy_data c[2]; +}; + +typedef struct +{ + int32_t value, filter0, filter1, filter2, filter3, filter4, filter5, filter6, factor, byte; +} DSDfilters; + +typedef struct +{ + WavpackHeader wphdr; + struct words_data w; + + unsigned char *blockbuff, *blockend; + unsigned char *block2buff, *block2end; + int32_t *sample_buffer; + + int64_t sample_index; + int bits, num_terms, mute_error, joint_stereo, false_stereo, shift; + int num_decorrs, num_passes, best_decorr, mask_decorr; + uint32_t crc, crc_x, crc_wvx; + Bitstream wvbits, wvcbits, wvxbits; + int init_done, wvc_skip; + float delta_decay; + + unsigned char int32_sent_bits, int32_zeros, int32_ones, int32_dups; + unsigned char float_flags, float_shift, float_max_exp, float_norm_exp; + + struct + { + int32_t shaping_acc[2], shaping_delta[2], error[2]; + double noise_sum, noise_ave, noise_max; + int16_t *shaping_data, *shaping_array; + int32_t shaping_samples; + } dc; + + struct decorr_pass decorr_passes[MAX_NTERMS], analysis_pass; + const WavpackDecorrSpec *decorr_specs; + + struct + { + unsigned char *byteptr, *endptr, (*probabilities)[256], **value_lookup, mode, ready; + int history_bins, p0, p1; + int16_t (*summed_probabilities)[256]; + uint32_t low, high, value; + DSDfilters filters[2]; + int32_t *ptable; + } dsd; + +} WavpackStream; + +// flags for float_flags: + +#define FLOAT_SHIFT_ONES 1 // bits left-shifted into float = '1' +#define FLOAT_SHIFT_SAME 2 // bits left-shifted into float are the same +#define FLOAT_SHIFT_SENT 4 // bits shifted into float are sent literally +#define FLOAT_ZEROS_SENT 8 // "zeros" are not all real zeros +#define FLOAT_NEG_ZEROS 0x10 // contains negative zeros +#define FLOAT_EXCEPTIONS 0x20 // contains exceptions (inf, nan, etc.) + +/////////////////////////////// WavPack Context /////////////////////////////// + +// This internal structure holds everything required to encode or decode WavPack +// files. It is recommended that direct access to this structure be minimized +// and the provided utilities used instead. + +typedef struct +{ + int32_t (*read_bytes)(void *id, void *data, int32_t bcount); + uint32_t (*get_pos)(void *id); + int (*set_pos_abs)(void *id, uint32_t pos); + int (*set_pos_rel)(void *id, int32_t delta, int mode); + int (*push_back_byte)(void *id, int c); + uint32_t (*get_length)(void *id); + int (*can_seek)(void *id); + + // this callback is for writing edited tags only + int32_t (*write_bytes)(void *id, void *data, int32_t bcount); +} WavpackStreamReader; + +// Extended version of structure for handling large files and added +// functionality for truncating and closing files + +typedef struct +{ + int32_t (*read_bytes)(void *id, void *data, int32_t bcount); + int32_t (*write_bytes)(void *id, void *data, int32_t bcount); + int64_t (*get_pos)(void *id); // new signature for large files + int (*set_pos_abs)(void *id, int64_t pos); // new signature for large files + int (*set_pos_rel)(void *id, int64_t delta, int mode); // new signature for large files + int (*push_back_byte)(void *id, int c); + int64_t (*get_length)(void *id); // new signature for large files + int (*can_seek)(void *id); + int (*truncate_here)(void *id); // new function to truncate file at current position + int (*close)(void *id); // new function to close file +} WavpackStreamReader64; + +typedef int (*WavpackBlockOutput)(void *id, void *data, int32_t bcount); + +typedef struct +{ + WavpackConfig config; + + WavpackMetadata *metadata; + uint32_t metabytes; + int metacount; + + unsigned char *wrapper_data; + uint32_t wrapper_bytes; + + WavpackBlockOutput blockout; + void *wv_out, *wvc_out; + + WavpackStreamReader64 *reader; + void *wv_in, *wvc_in; + + int64_t filelen, file2len, filepos, file2pos, total_samples, initial_index; + uint32_t crc_errors, first_flags; + int wvc_flag, open_flags, norm_offset, reduced_channels, lossy_blocks, version_five; + uint32_t block_samples, ave_block_samples, block_boundary, max_samples, acc_samples, riff_trailer_bytes; + int riff_header_added, riff_header_created; + M_Tag m_tag; + + int current_stream, num_streams, max_streams, stream_version; + WavpackStream **streams; + void *stream3; + + // these items were added in 5.0 to support alternate file types (especially CAF & DSD) + unsigned char file_format, *channel_reordering, *channel_identities; + uint32_t channel_layout, dsd_multiplier; + void *decimation_context; + char file_extension[8]; + + char error_message[80]; +} WavpackContext; + +//////////////////////// function prototypes and macros ////////////////////// + +#define CLEAR(destin) memset(&destin, 0, sizeof(destin)); + +//////////////////////////////// decorrelation ////////////////////////////// +// modules: pack.c, unpack.c, unpack_floats.c, extra1.c, extra2.c + +// #define SKIP_DECORRELATION // experimental switch to disable all decorrelation on encode + +// These macros implement the weight application and update operations +// that are at the heart of the decorrelation loops. Note that there are +// sometimes two and even three versions of each macro. Theses should be +// equivalent and produce identical results, but some may perform better +// or worse on a given architecture. + +#if 1 // PERFCOND - apply decorrelation weight when no 32-bit overflow possible +#define apply_weight_i(weight, sample) ((weight * sample + 512) >> 10) +#else +#define apply_weight_i(weight, sample) ((((weight * sample) >> 8) + 2) >> 2) +#endif + +#if 1 // PERFCOND - apply decorrelation weight when 32-bit overflow is possible +#define apply_weight_f(weight, sample) \ + (((((sample & 0xffff) * weight) >> 9) + (((sample & ~0xffff) >> 9) * weight) + 1) >> 1) +#elif 1 +#define apply_weight_f(weight, sample) ((int32_t)((weight * (int64_t)sample + 512) >> 10)) +#else +#define apply_weight_f(weight, sample) ((int32_t)floor(((double)weight * sample + 512.0) / 1024.0)) +#endif + +#if 1 // PERFCOND - universal version that checks input magnitude or always uses long version +#define apply_weight(weight, sample) \ + (sample != (int16_t)sample ? apply_weight_f(weight, sample) : apply_weight_i(weight, sample)) +#else +#define apply_weight(weight, sample) (apply_weight_f(weight, sample)) +#endif + +#if 1 // PERFCOND +#define update_weight(weight, delta, source, result) \ + if(source && result) \ + { \ + int32_t s = (int32_t)(source ^ result) >> 31; \ + weight = (delta ^ s) + (weight - s); \ + } +#elif 1 +#define update_weight(weight, delta, source, result) \ + if(source && result) weight += (((source ^ result) >> 30) | 1) * delta; +#else +#define update_weight(weight, delta, source, result) \ + if(source && result) (source ^ result) < 0 ? (weight -= delta) : (weight += delta); +#endif + +#define update_weight_clip(weight, delta, source, result) \ + if(source && result) \ + { \ + const int32_t s = (source ^ result) >> 31; \ + if((weight = (weight ^ s) + (delta - s)) > 1024) weight = 1024; \ + weight = (weight ^ s) - s; \ + } + +void pack_init(WavpackContext *wpc); +int pack_block(WavpackContext *wpc, int32_t *buffer); +void send_general_metadata(WavpackContext *wpc); +void free_metadata(WavpackMetadata *wpmd); +int copy_metadata(WavpackMetadata *wpmd, unsigned char *buffer_start, unsigned char *buffer_end); +double WavpackGetEncodedNoise(WavpackContext *wpc, double *peak); +int unpack_init(WavpackContext *wpc); +int read_decorr_terms(WavpackStream *wps, WavpackMetadata *wpmd); +int read_decorr_weights(WavpackStream *wps, WavpackMetadata *wpmd); +int read_decorr_samples(WavpackStream *wps, WavpackMetadata *wpmd); +int read_shaping_info(WavpackStream *wps, WavpackMetadata *wpmd); +int32_t unpack_samples(WavpackContext *wpc, int32_t *buffer, uint32_t sample_count); +int check_crc_error(WavpackContext *wpc); +int scan_float_data(WavpackStream *wps, f32 *values, int32_t num_values); +void send_float_data(WavpackStream *wps, f32 *values, int32_t num_values); +void float_values(WavpackStream *wps, int32_t *values, int32_t num_values); +void dynamic_noise_shaping(WavpackContext *wpc, int32_t *buffer, int shortening_allowed); +void execute_stereo(WavpackContext *wpc, int32_t *samples, int no_history, int do_samples); +void execute_mono(WavpackContext *wpc, int32_t *samples, int no_history, int do_samples); + +////////////////////////// DSD related (including decimation) ////////////////////////// +// modules: pack_dsd.c unpack_dsd.c + +void pack_dsd_init(WavpackContext *wpc); +int pack_dsd_block(WavpackContext *wpc, int32_t *buffer); +int init_dsd_block(WavpackContext *wpc, WavpackMetadata *wpmd); +int32_t unpack_dsd_samples(WavpackContext *wpc, int32_t *buffer, uint32_t sample_count); + +void *decimate_dsd_init(int num_channels); +void decimate_dsd_reset(void *decimate_context); +void decimate_dsd_run(void *decimate_context, int32_t *samples, int num_samples); +void decimate_dsd_destroy(void *decimate_context); + +///////////////////////////////// CPU feature detection //////////////////////////////// + +int unpack_cpu_has_feature_x86(int findex), pack_cpu_has_feature_x86(int findex); + +#define CPU_FEATURE_MMX 23 + +///////////////////////////// pre-4.0 version decoding //////////////////////////// +// modules: unpack3.c, unpack3_open.c, unpack3_seek.c + +WavpackContext *open_file3(WavpackContext *wpc, char *error); +int32_t unpack_samples3(WavpackContext *wpc, int32_t *buffer, uint32_t sample_count); +int seek_sample3(WavpackContext *wpc, uint32_t desired_index); +uint32_t get_sample_index3(WavpackContext *wpc); +void free_stream3(WavpackContext *wpc); +int get_version3(WavpackContext *wpc); + +////////////////////////////// bitstream macros & functions ///////////////////////////// + +#define bs_is_open(bs) ((bs)->ptr != NULL) +uint32_t bs_close_read(Bitstream *bs); + +#define getbit(bs) \ + ((((bs)->bc) ? ((bs)->bc--, (bs)->sr & 1) \ + : (((++((bs)->ptr) != (bs)->end) ? (void)0 : (bs)->wrap(bs)), \ + (bs)->bc = sizeof(*((bs)->ptr)) * 8 - 1, ((bs)->sr = *((bs)->ptr)) & 1)) \ + ? ((bs)->sr >>= 1, 1) \ + : ((bs)->sr >>= 1, 0)) + +#define getbits(value, nbits, bs) \ + do \ + { \ + while((nbits) > (bs)->bc) \ + { \ + if(++((bs)->ptr) == (bs)->end) (bs)->wrap(bs); \ + (bs)->sr |= (int32_t)*((bs)->ptr) << (bs)->bc; \ + (bs)->bc += sizeof(*((bs)->ptr)) * 8; \ + } \ + *(value) = (bs)->sr; \ + if((bs)->bc > 32) \ + { \ + (bs)->bc -= (nbits); \ + (bs)->sr = *((bs)->ptr) >> (sizeof(*((bs)->ptr)) * 8 - (bs)->bc); \ + } \ + else \ + { \ + (bs)->bc -= (nbits); \ + (bs)->sr >>= (nbits); \ + } \ + } while(0) + +#define putbit(bit, bs) \ + do \ + { \ + if(bit) (bs)->sr |= (1 << (bs)->bc); \ + if(++((bs)->bc) == sizeof(*((bs)->ptr)) * 8) \ + { \ + *((bs)->ptr) = (bs)->sr; \ + (bs)->sr = (bs)->bc = 0; \ + if(++((bs)->ptr) == (bs)->end) (bs)->wrap(bs); \ + } \ + } while(0) + +#define putbit_0(bs) \ + do \ + { \ + if(++((bs)->bc) == sizeof(*((bs)->ptr)) * 8) \ + { \ + *((bs)->ptr) = (bs)->sr; \ + (bs)->sr = (bs)->bc = 0; \ + if(++((bs)->ptr) == (bs)->end) (bs)->wrap(bs); \ + } \ + } while(0) + +#define putbit_1(bs) \ + do \ + { \ + (bs)->sr |= (1 << (bs)->bc); \ + if(++((bs)->bc) == sizeof(*((bs)->ptr)) * 8) \ + { \ + *((bs)->ptr) = (bs)->sr; \ + (bs)->sr = (bs)->bc = 0; \ + if(++((bs)->ptr) == (bs)->end) (bs)->wrap(bs); \ + } \ + } while(0) + +#define putbits(value, nbits, bs) \ + do \ + { \ + (bs)->sr |= (int32_t)(value) << (bs)->bc; \ + if(((bs)->bc += (nbits)) >= sizeof(*((bs)->ptr)) * 8) do \ + { \ + *((bs)->ptr) = (bs)->sr; \ + (bs)->sr >>= sizeof(*((bs)->ptr)) * 8; \ + if(((bs)->bc -= sizeof(*((bs)->ptr)) * 8) > 32 - sizeof(*((bs)->ptr)) * 8) \ + (bs)->sr |= ((value) >> ((nbits) - (bs)->bc)); \ + if(++((bs)->ptr) == (bs)->end) (bs)->wrap(bs); \ + } while((bs)->bc >= sizeof(*((bs)->ptr)) * 8); \ + } while(0) + +///////////////////////////// entropy encoder / decoder //////////////////////////// +// modules: entropy_utils.c, read_words.c, write_words.c + +// these control the time constant "slow_level" which is used for hybrid mode +// that controls bitrate as a function of residual level (HYBRID_BITRATE). +#define SLS 8 +#define SLO ((1 << (SLS - 1))) + +#define LIMIT_ONES 16 // maximum consecutive 1s sent for "div" data + +// these control the time constant of the 3 median level breakpoints +#define DIV0 128 // 5/7 of samples +#define DIV1 64 // 10/49 of samples +#define DIV2 32 // 20/343 of samples + +// this macro retrieves the specified median breakpoint (without frac; min = 1) +#define GET_MED(med) (((c->median[med]) >> 4) + 1) + +// These macros update the specified median breakpoints. Note that the median +// is incremented when the sample is higher than the median, else decremented. +// They are designed so that the median will never drop below 1 and the value +// is essentially stationary if there are 2 increments for every 5 decrements. + +#define INC_MED0() (c->median[0] += ((c->median[0] + DIV0) / DIV0) * 5) +#define DEC_MED0() (c->median[0] -= ((c->median[0] + (DIV0 - 2)) / DIV0) * 2) +#define INC_MED1() (c->median[1] += ((c->median[1] + DIV1) / DIV1) * 5) +#define DEC_MED1() (c->median[1] -= ((c->median[1] + (DIV1 - 2)) / DIV1) * 2) +#define INC_MED2() (c->median[2] += ((c->median[2] + DIV2) / DIV2) * 5) +#define DEC_MED2() (c->median[2] -= ((c->median[2] + (DIV2 - 2)) / DIV2) * 2) + +#ifdef HAVE___BUILTIN_CLZ +#define count_bits(av) ((av) ? 32 - __builtin_clz(av) : 0) +#elif defined(_WIN64) +static __inline int count_bits(uint32_t av) +{ + unsigned long res; + return _BitScanReverse(&res, av) ? (int)(res + 1) : 0; +} +#else +#define count_bits(av) \ + ((av) < (1 << 8) \ + ? nbits_table[av] \ + : ((av) < (1L << 16) ? nbits_table[(av) >> 8] + 8 \ + : ((av) < (1L << 24) ? nbits_table[(av) >> 16] + 16 : nbits_table[(av) >> 24] + 24))) +#endif + +void init_words(WavpackStream *wps); +void write_entropy_vars(WavpackStream *wps, WavpackMetadata *wpmd); +void write_hybrid_profile(WavpackStream *wps, WavpackMetadata *wpmd); +int read_entropy_vars(WavpackStream *wps, WavpackMetadata *wpmd); +int read_hybrid_profile(WavpackStream *wps, WavpackMetadata *wpmd); +int32_t FASTCALL send_word(WavpackStream *wps, int32_t value, int chan); +void send_words_lossless(WavpackStream *wps, int32_t *buffer, int32_t nsamples); +int32_t FASTCALL get_word(WavpackStream *wps, int chan, int32_t *correction); +int32_t get_words_lossless(WavpackStream *wps, int32_t *buffer, int32_t nsamples); +void flush_word(WavpackStream *wps); +int32_t nosend_word(WavpackStream *wps, int32_t value, int chan); +void scan_word(WavpackStream *wps, int32_t *samples, uint32_t num_samples, int dir); +void update_error_limit(WavpackStream *wps); + +extern const uint32_t bitset[32]; +extern const uint32_t bitmask[32]; +extern const char nbits_table[256]; + +int wp_log2s(int32_t value); +int32_t wp_exp2s(int log); +int FASTCALL wp_log2(uint32_t avalue); + +#ifdef OPT_ASM_X86 +#define LOG2BUFFER log2buffer_x86 +#elif defined(OPT_ASM_X64) && (defined(_WIN64) || defined(__CYGWIN__) || defined(__MINGW64__)) +#define LOG2BUFFER log2buffer_x64win +#elif defined(OPT_ASM_X64) +#define LOG2BUFFER log2buffer_x64 +#else +#define LOG2BUFFER log2buffer +#endif + +uint32_t LOG2BUFFER(int32_t *samples, uint32_t num_samples, int limit); + +signed char store_weight(int weight); +int restore_weight(signed char weight); + +#define WORD_EOF ((int32_t)(1L << 31)) + +void WavpackFloatNormalize(int32_t *values, int32_t num_values, int delta_exp); + +/////////////////////////// high-level unpacking API and support //////////////////////////// +// modules: open_utils.c, unpack_utils.c, unpack_seek.c, unpack_floats.c + +WavpackContext *WavpackOpenFileInputEx64(WavpackStreamReader64 *reader, void *wv_id, void *wvc_id, char *error, + int flags, int norm_offset); +WavpackContext *WavpackOpenFileInputEx(WavpackStreamReader *reader, void *wv_id, void *wvc_id, char *error, int flags, + int norm_offset); +WavpackContext *WavpackOpenFileInput(const char *infilename, char *error, int flags, int norm_offset); + +#define OPEN_WVC 0x1 // open/read "correction" file +#define OPEN_TAGS 0x2 // read ID3v1 / APEv2 tags (seekable file) +#define OPEN_WRAPPER 0x4 // make audio wrapper available (i.e. RIFF) +#define OPEN_2CH_MAX 0x8 // open multichannel as stereo (no downmix) +#define OPEN_NORMALIZE 0x10 // normalize floating point data to +/- 1.0 +#define OPEN_STREAMING 0x20 // "streaming" mode blindly unpacks blocks +// w/o regard to header file position info +#define OPEN_EDIT_TAGS 0x40 // allow editing of tags +#define OPEN_FILE_UTF8 0x80 // assume filenames are UTF-8 encoded, not ANSI (Windows only) + +// new for version 5 + +#define OPEN_DSD_NATIVE 0x100 // open DSD files as bitstreams +// (returned as 8-bit "samples" stored in 32-bit words) +#define OPEN_DSD_AS_PCM 0x200 // open DSD files as 24-bit PCM (decimated 8x) +#define OPEN_ALT_TYPES 0x400 // application is aware of alternate file types & qmode +// (just affects retrieving wrappers & MD5 checksums) +#define OPEN_NO_CHECKSUM 0x800 // don't verify block checksums before decoding + +int WavpackGetMode(WavpackContext *wpc); + +#define MODE_WVC 0x1 +#define MODE_LOSSLESS 0x2 +#define MODE_HYBRID 0x4 +#define MODE_FLOAT 0x8 +#define MODE_VALID_TAG 0x10 +#define MODE_HIGH 0x20 +#define MODE_FAST 0x40 +#define MODE_EXTRA 0x80 // extra mode used, see MODE_XMODE for possible level +#define MODE_APETAG 0x100 +#define MODE_SFX 0x200 +#define MODE_VERY_HIGH 0x400 +#define MODE_MD5 0x800 +#define MODE_XMODE 0x7000 // mask for extra level (1-6, 0=unknown) +#define MODE_DNS 0x8000 + +int WavpackGetQualifyMode(WavpackContext *wpc); +int WavpackGetVersion(WavpackContext *wpc); +uint32_t WavpackUnpackSamples(WavpackContext *wpc, int32_t *buffer, uint32_t samples); +int WavpackSeekSample(WavpackContext *wpc, uint32_t sample); +int WavpackSeekSample64(WavpackContext *wpc, int64_t sample); +int WavpackGetMD5Sum(WavpackContext *wpc, unsigned char data[16]); + +int WavpackVerifySingleBlock(unsigned char *buffer, int verify_checksum); +uint32_t read_next_header(WavpackStreamReader64 *reader, void *id, WavpackHeader *wphdr); +int read_wvc_block(WavpackContext *wpc); + +/////////////////////////// high-level packing API and support //////////////////////////// +// modules: pack_utils.c, pack_floats.c + +WavpackContext *WavpackOpenFileOutput(WavpackBlockOutput blockout, void *wv_id, void *wvc_id); +int WavpackSetConfiguration(WavpackContext *wpc, WavpackConfig *config, uint32_t total_samples); +int WavpackSetConfiguration64(WavpackContext *wpc, WavpackConfig *config, int64_t total_samples, + const unsigned char *chan_ids); +int WavpackPackInit(WavpackContext *wpc); +int WavpackAddWrapper(WavpackContext *wpc, void *data, uint32_t bcount); +int WavpackPackSamples(WavpackContext *wpc, int32_t *sample_buffer, uint32_t sample_count); +int WavpackFlushSamples(WavpackContext *wpc); +int WavpackStoreMD5Sum(WavpackContext *wpc, unsigned char data[16]); +void WavpackSeekTrailingWrapper(WavpackContext *wpc); +void WavpackUpdateNumSamples(WavpackContext *wpc, void *first_block); +void *WavpackGetWrapperLocation(void *first_block, uint32_t *size); + +/////////////////////////////////// common utilities //////////////////////////////////// +// module: common_utils.c + +extern const uint32_t sample_rates[16]; +uint32_t WavpackGetLibraryVersion(void); +const char *WavpackGetLibraryVersionString(void); +uint32_t WavpackGetSampleRate(WavpackContext *wpc); +int WavpackGetBitsPerSample(WavpackContext *wpc); +int WavpackGetBytesPerSample(WavpackContext *wpc); +int WavpackGetNumChannels(WavpackContext *wpc); +int WavpackGetChannelMask(WavpackContext *wpc); +int WavpackGetReducedChannels(WavpackContext *wpc); +int WavpackGetFloatNormExp(WavpackContext *wpc); +uint32_t WavpackGetNumSamples(WavpackContext *wpc); +int64_t WavpackGetNumSamples64(WavpackContext *wpc); +uint32_t WavpackGetSampleIndex(WavpackContext *wpc); +int64_t WavpackGetSampleIndex64(WavpackContext *wpc); +char *WavpackGetErrorMessage(WavpackContext *wpc); +int WavpackGetNumErrors(WavpackContext *wpc); +int WavpackLossyBlocks(WavpackContext *wpc); +uint32_t WavpackGetWrapperBytes(WavpackContext *wpc); +unsigned char *WavpackGetWrapperData(WavpackContext *wpc); +void WavpackFreeWrapper(WavpackContext *wpc); +double WavpackGetProgress(WavpackContext *wpc); +uint32_t WavpackGetFileSize(WavpackContext *wpc); +int64_t WavpackGetFileSize64(WavpackContext *wpc); +double WavpackGetRatio(WavpackContext *wpc); +double WavpackGetAverageBitrate(WavpackContext *wpc, int count_wvc); +double WavpackGetInstantBitrate(WavpackContext *wpc); +WavpackContext *WavpackCloseFile(WavpackContext *wpc); +void WavpackLittleEndianToNative(void *data, char *format); +void WavpackNativeToLittleEndian(void *data, char *format); +void WavpackBigEndianToNative(void *data, char *format); +void WavpackNativeToBigEndian(void *data, char *format); + +void free_streams(WavpackContext *wpc); + +/////////////////////////////////// tag utilities //////////////////////////////////// +// modules: tags.c, tag_utils.c + +int WavpackGetNumTagItems(WavpackContext *wpc); +int WavpackGetTagItem(WavpackContext *wpc, const char *item, char *value, int size); +int WavpackGetTagItemIndexed(WavpackContext *wpc, int index, char *item, int size); +int WavpackGetNumBinaryTagItems(WavpackContext *wpc); +int WavpackGetBinaryTagItem(WavpackContext *wpc, const char *item, char *value, int size); +int WavpackGetBinaryTagItemIndexed(WavpackContext *wpc, int index, char *item, int size); +int WavpackAppendTagItem(WavpackContext *wpc, const char *item, const char *value, int vsize); +int WavpackAppendBinaryTagItem(WavpackContext *wpc, const char *item, const char *value, int vsize); +int WavpackDeleteTagItem(WavpackContext *wpc, const char *item); +int WavpackWriteTag(WavpackContext *wpc); +int load_tag(WavpackContext *wpc); +void free_tag(M_Tag *m_tag); +int valid_tag(M_Tag *m_tag); +int editable_tag(M_Tag *m_tag); + +#endif diff --git a/wavpack/wavpack_version.h b/wavpack/wavpack_version.h new file mode 100644 index 0000000..6e9139e --- /dev/null +++ b/wavpack/wavpack_version.h @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2006 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// wavpack_version.h + +#ifndef WAVPACK_VERSION_H +#define WAVPACK_VERSION_H + +#define LIBWAVPACK_MAJOR 5 +#define LIBWAVPACK_MINOR 1 +#define LIBWAVPACK_MICRO 0 +#define LIBWAVPACK_VERSION_STRING "5.1.0" + +#endif diff --git a/winzipjpeg/ArithmeticDecoder.c b/winzipjpeg/ArithmeticDecoder.c new file mode 100644 index 0000000..3e55e17 --- /dev/null +++ b/winzipjpeg/ArithmeticDecoder.c @@ -0,0 +1,696 @@ +/* + * ArithmeticDecoder.c + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#include "ArithmeticDecoder.h" + +// Arithmetic decoder based on US patent 4791403. + +static void InitDec(WinZipJPEGArithmeticDecoder *self); + +static unsigned int LogDecoder(WinZipJPEGArithmeticDecoder *self, WinZipJPEGContext *context); + +static void UpdateMPS(WinZipJPEGArithmeticDecoder *self, WinZipJPEGContext *context); +static void QSmaller(WinZipJPEGArithmeticDecoder *self, WinZipJPEGContext *context); + +static void UpdateLPS(WinZipJPEGArithmeticDecoder *self, WinZipJPEGContext *context); +static void QBigger(WinZipJPEGArithmeticDecoder *self, WinZipJPEGContext *context); +static void IncrIndex(int *i, int *incrsv); +static void DblIndex(int *i, int *incrsv); + +static void LRMBig(WinZipJPEGArithmeticDecoder *self); +static void Renorm(WinZipJPEGArithmeticDecoder *self); +static uint8_t ByteIn(WinZipJPEGArithmeticDecoder *self); + +static uint32_t AntilogX(int16_t lr); +static int16_t LogX(uint32_t x); + +static uint16_t logp[]; // log p at index i +static uint16_t lqp[]; // log q - log p +static uint16_t nmaxlp[]; // nmax * lp +static uint8_t halfi[]; // pointer to q halved +static uint8_t dbli[]; // pointer to q doubled + +static uint16_t alogtbl[]; +static uint16_t logtbl[]; +static uint8_t chartbl[]; + +void InitializeWinZipJPEGArithmeticDecoder(WinZipJPEGArithmeticDecoder *self, WinZipJPEGReadFunction *readfunc, + void *inputcontext) +{ + self->readfunc = readfunc; + self->inputcontext = inputcontext; + self->eof = false; + + InitDec(self); +} + +void InitializeWinZipJPEGContext(WinZipJPEGContext *self) +{ + self->dlrm = nmaxlp[0]; + self->i = 0; + self->k = 0; + self->mps = 0; +} + +void InitializeWinZipJPEGContexts(WinZipJPEGContext *first, size_t bytes) +{ + for(int i = 0; i < bytes / sizeof(WinZipJPEGContext); i++) InitializeWinZipJPEGContext(first + i); +} + +void InitializeFixedWinZipJPEGContext(WinZipJPEGContext *self) +{ + self->dlrm = nmaxlp[0]; + self->i = 48; + self->k = 0; + self->mps = 0; +} + +int NextBitFromWinZipJPEGArithmeticDecoder(WinZipJPEGArithmeticDecoder *self, WinZipJPEGContext *context) +{ + self->dx = 0; // Otherwise tests don't pass. + int bit = LogDecoder(self, context); + self->lp = logp[context->i]; + return bit; +} + +void FlushWinZipJPEGArithmeticDecoder(WinZipJPEGArithmeticDecoder *self) +{ + Renorm(self); + + if(self->currbyte == 0xff && self->lastbyte == 0xff) ByteIn(self); +} + +static void InitDec(WinZipJPEGArithmeticDecoder *self) +{ + self->kmin2 = 0; + self->kmin1 = 1; + self->kmin = 5; + self->kmax = 11; + + uint8_t b1 = ByteIn(self); + uint8_t b2 = ByteIn(self); + self->x = (b1 << 8) | b2; + + self->lr = 0x1001; + self->lrm = self->lr; + self->lx = LogX(self->x); + + if(self->x == 0xffff) ByteIn(self); +} + +static unsigned int LogDecoder(WinZipJPEGArithmeticDecoder *self, WinZipJPEGContext *context) +{ + self->lrm = self->lr + context->dlrm; + LRMBig(self); + + self->lr += logp[context->i]; + + unsigned int bit = context->mps; + + int lrt; + if(self->lx < self->lrm) + lrt = self->lx; + else + lrt = self->lrm; + + if(self->lr >= lrt) + { + if(self->lr < self->lx) { UpdateMPS(self, context); } + else + { + Renorm(self); + if(self->lr < self->lx) + { + if(self->lr >= self->lrm) UpdateMPS(self, context); + } + else + { + bit ^= 1; + + context->k++; + + uint32_t dx = AntilogX(self->lr); + self->x -= dx; + self->lx = LogX(self->x); + self->dx = dx; // for tests + + UpdateLPS(self, context); + } + } + } + + context->dlrm = self->lrm - self->lr; + + return bit; +} + +static void UpdateMPS(WinZipJPEGArithmeticDecoder *self, WinZipJPEGContext *context) +{ + if(context->k <= self->kmin) QSmaller(self, context); + context->k = 0; + self->lrm = self->lr + nmaxlp[context->i]; + LRMBig(self); +} + +static void QSmaller(WinZipJPEGArithmeticDecoder *self, WinZipJPEGContext *context) +{ + if(context->i >= 47) return; // WinZip modification. + + context->i++; + + if(context->k <= self->kmin1) + { + context->i += halfi[context->i]; + if(context->k <= self->kmin2) { context->i += halfi[context->i]; } + } +} + +static void UpdateLPS(WinZipJPEGArithmeticDecoder *self, WinZipJPEGContext *context) +{ + self->lr += lqp[context->i]; + self->lrm += lqp[context->i]; + + if(context->k >= self->kmax) + { + QBigger(self, context); + context->k = 0; + self->lrm = self->lr + nmaxlp[context->i]; + } + else + { + if(self->lrm < self->lr) self->lrm = self->lr; + } +} + +static void QBigger(WinZipJPEGArithmeticDecoder *self, WinZipJPEGContext *context) +{ + if(context->i >= 48) return; // WinZip modification. + + int32_t dlrm = self->lrm - self->lr; + int incrsv = 0; + + if(dlrm >= nmaxlp[context->i] / 2) + { + dlrm = nmaxlp[context->i] - dlrm; + if(dlrm <= nmaxlp[context->i] / 4) DblIndex(&context->i, &incrsv); + DblIndex(&context->i, &incrsv); + } + else + { + if(dlrm >= nmaxlp[context->i] / 4) IncrIndex(&context->i, &incrsv); + IncrIndex(&context->i, &incrsv); + } + + if(context->i <= 0) + { + context->i = incrsv; + context->mps = context->mps ^ 1; + } + + self->lrm = self->lr + dlrm; +} + +static void IncrIndex(int *i, int *incrsv) +{ + if(*i > 0) + (*i)--; + else + (*incrsv)++; +} + +static void DblIndex(int *i, int *incrsv) +{ + if(*i > 0) + *i -= dbli[*i]; + else + *incrsv += dbli[*i]; +} + +static void LRMBig(WinZipJPEGArithmeticDecoder *self) +{ + if(self->lrm > 0x7ff) Renorm(self); +} + +static void Renorm(WinZipJPEGArithmeticDecoder *self) +{ + while(self->lr > 0x1fff) + { + if(self->currbyte == 0xff && self->lastbyte == 0xff) { self->x += ByteIn(self); } + self->x = (self->x << 8) | ByteIn(self); + self->lr -= 0x2000; + self->lrm -= 0x2000; + } + + self->lx = LogX(self->x); +} + +static uint8_t ByteIn(WinZipJPEGArithmeticDecoder *self) +{ + self->lastbyte = self->currbyte; + + if(self->readfunc(self->inputcontext, &self->currbyte, 1) != 1) + { + self->eof = true; + self->currbyte = 0; + } + + return self->currbyte; +} + +static int16_t LogX(uint32_t x) +{ + unsigned int highbits = x >> 12; + if(highbits == 0) return 0x2000; + + int whole; + if(highbits < 512) + whole = chartbl[highbits]; + else + whole = 0; + + int shift = 8 - whole; + + int negfraction; + if(shift >= 0) + negfraction = logtbl[(x >> shift) & 0xfff]; + else + negfraction = logtbl[(x << -shift) & 0xfff]; // Is this necessary? No idea. + + return (whole << 10) - negfraction; +} + +static uint32_t AntilogX(int16_t lr) +{ + int whole = lr >> 10; + unsigned int fraction = lr & 0x3ff; + + int shift = 7 - whole; + if(shift >= 0) + return alogtbl[fraction] << shift; + else + return alogtbl[fraction] >> -shift; // Is this necessary? No idea. +} + +static uint16_t logp[49] = // log p at index i + { + 1024, 895, 795, 706, 628, 559, 493, 437, 379, 331, 287, 247, 212, 186, 158, 143, 127, + 110, 98, 84, 72, 65, 59, 53, 48, 45, 42, 40, 37, 35, 33, 30, 28, 26, + 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 4, 3, 2, 1, 1024, +}; + +static uint16_t lqp[49] = // log q - log p + { + 0, 272, 502, 726, 941, 1150, 1371, 1578, 1819, 2044, 2278, 2521, 2765, 2971, 3227, 3382, 3566, + 3788, 3965, 4200, 4435, 4590, 4737, 4899, 5050, 5147, 5250, 5325, 5441, 5527, 5617, 5758, 5863, 5976, + 6157, 6295, 6447, 6616, 6806, 7024, 7278, 7585, 7972, 8495, 8884, 9309, 10065, 11689, 0, +}; + +static uint16_t nmaxlp[49] = // nmax * lp + { + 16384, 16110, 15105, 14826, 14444, 13975, 13804, 13547, 13265, 13240, 12915, 12844, 12720, + 12648, 12482, 12441, 12319, 12320, 12250, 12180, 12168, 12155, 12154, 12084, 12096, 12105, + 12096, 12080, 12062, 12075, 12078, 12060, 12068, 12090, 12075, 12075, 12103, 12121, 12150, + 12181, 12221, 12294, 12411, 12615, 13120, 13113, 14574, 21860, 0, +}; + +static uint8_t halfi[49] = // pointer to q halved + { + 8, 8, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 9, 10, 10, 10, 10, + 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 6, 5, 5, 4, 4, 3, 3, 3, 3, 2, 2, 1, 0, 0, +}; + +static uint8_t dbli[49] = // pointer to q doubled + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 8, 8, 9, 9, 10, 10, 10, 10, 10, 10, 9, 8, 7, 6, 6, 5, 4, 3, 3, 3, 2, 1, 0, +}; + +static uint16_t alogtbl[1024] = { + 0x2000, 0x1ffd, 0x1ff7, 0x1ff1, 0x1fec, 0x1fe6, 0x1fe1, 0x1fdb, 0x1fd6, 0x1fd0, 0x1fcb, 0x1fc5, 0x1fc0, 0x1fba, + 0x1fb5, 0x1faf, 0x1faa, 0x1fa4, 0x1f9f, 0x1f99, 0x1f94, 0x1f8e, 0x1f89, 0x1f83, 0x1f7e, 0x1f79, 0x1f73, 0x1f6e, + 0x1f68, 0x1f63, 0x1f5d, 0x1f58, 0x1f53, 0x1f4d, 0x1f48, 0x1f42, 0x1f3d, 0x1f37, 0x1f32, 0x1f2d, 0x1f27, 0x1f22, + 0x1f1c, 0x1f17, 0x1f12, 0x1f0c, 0x1f07, 0x1f02, 0x1efc, 0x1ef7, 0x1ef1, 0x1eec, 0x1ee7, 0x1ee1, 0x1edc, 0x1ed7, + 0x1ed1, 0x1ecc, 0x1ec7, 0x1ec1, 0x1ebc, 0x1eb7, 0x1eb1, 0x1eac, 0x1ea7, 0x1ea1, 0x1e9c, 0x1e97, 0x1e92, 0x1e8c, + 0x1e87, 0x1e82, 0x1e7c, 0x1e77, 0x1e72, 0x1e6d, 0x1e67, 0x1e62, 0x1e5d, 0x1e58, 0x1e52, 0x1e4d, 0x1e48, 0x1e43, + 0x1e3d, 0x1e38, 0x1e33, 0x1e2e, 0x1e28, 0x1e23, 0x1e1e, 0x1e19, 0x1e14, 0x1e0e, 0x1e09, 0x1e04, 0x1dff, 0x1dfa, + 0x1df4, 0x1def, 0x1dea, 0x1de5, 0x1de0, 0x1dda, 0x1dd5, 0x1dd0, 0x1dcb, 0x1dc6, 0x1dc1, 0x1dbc, 0x1db6, 0x1db1, + 0x1dac, 0x1da7, 0x1da2, 0x1d9d, 0x1d98, 0x1d93, 0x1d8d, 0x1d88, 0x1d83, 0x1d7e, 0x1d79, 0x1d74, 0x1d6f, 0x1d6a, + 0x1d65, 0x1d5f, 0x1d5a, 0x1d55, 0x1d50, 0x1d4b, 0x1d46, 0x1d41, 0x1d3c, 0x1d37, 0x1d32, 0x1d2d, 0x1d28, 0x1d23, + 0x1d1e, 0x1d19, 0x1d14, 0x1d0e, 0x1d09, 0x1d04, 0x1cff, 0x1cfa, 0x1cf5, 0x1cf0, 0x1ceb, 0x1ce6, 0x1ce1, 0x1cdc, + 0x1cd7, 0x1cd2, 0x1ccd, 0x1cc8, 0x1cc3, 0x1cbf, 0x1cba, 0x1cb5, 0x1cb0, 0x1cab, 0x1ca6, 0x1ca1, 0x1c9c, 0x1c97, + 0x1c92, 0x1c8d, 0x1c88, 0x1c83, 0x1c7e, 0x1c79, 0x1c74, 0x1c6f, 0x1c6a, 0x1c65, 0x1c61, 0x1c5c, 0x1c57, 0x1c52, + 0x1c4d, 0x1c48, 0x1c43, 0x1c3e, 0x1c39, 0x1c34, 0x1c2f, 0x1c2b, 0x1c26, 0x1c21, 0x1c1c, 0x1c17, 0x1c12, 0x1c0d, + 0x1c09, 0x1c04, 0x1bff, 0x1bfa, 0x1bf5, 0x1bf0, 0x1bec, 0x1be7, 0x1be2, 0x1bdd, 0x1bd8, 0x1bd3, 0x1bcf, 0x1bca, + 0x1bc5, 0x1bc0, 0x1bbb, 0x1bb7, 0x1bb2, 0x1bad, 0x1ba8, 0x1ba3, 0x1b9f, 0x1b9a, 0x1b95, 0x1b90, 0x1b8c, 0x1b87, + 0x1b82, 0x1b7d, 0x1b78, 0x1b74, 0x1b6f, 0x1b6a, 0x1b66, 0x1b61, 0x1b5c, 0x1b57, 0x1b53, 0x1b4e, 0x1b49, 0x1b44, + 0x1b40, 0x1b3b, 0x1b36, 0x1b32, 0x1b2d, 0x1b28, 0x1b23, 0x1b1f, 0x1b1a, 0x1b15, 0x1b11, 0x1b0c, 0x1b07, 0x1b03, + 0x1afe, 0x1af9, 0x1af5, 0x1af0, 0x1aeb, 0x1ae7, 0x1ae2, 0x1add, 0x1ad9, 0x1ad4, 0x1acf, 0x1acb, 0x1ac6, 0x1ac1, + 0x1abd, 0x1ab8, 0x1ab4, 0x1aaf, 0x1aaa, 0x1aa6, 0x1aa1, 0x1a9c, 0x1a98, 0x1a93, 0x1a8f, 0x1a8a, 0x1a85, 0x1a81, + 0x1a7c, 0x1a78, 0x1a73, 0x1a6f, 0x1a6a, 0x1a65, 0x1a61, 0x1a5c, 0x1a58, 0x1a53, 0x1a4f, 0x1a4a, 0x1a46, 0x1a41, + 0x1a3c, 0x1a38, 0x1a33, 0x1a2f, 0x1a2a, 0x1a26, 0x1a21, 0x1a1d, 0x1a18, 0x1a14, 0x1a0f, 0x1a0b, 0x1a06, 0x1a02, + 0x19fd, 0x19f9, 0x19f4, 0x19f0, 0x19eb, 0x19e7, 0x19e2, 0x19de, 0x19d9, 0x19d5, 0x19d0, 0x19cc, 0x19c7, 0x19c3, + 0x19be, 0x19ba, 0x19b6, 0x19b1, 0x19ad, 0x19a8, 0x19a4, 0x199f, 0x199b, 0x1996, 0x1992, 0x198e, 0x1989, 0x1985, + 0x1980, 0x197c, 0x1978, 0x1973, 0x196f, 0x196a, 0x1966, 0x1962, 0x195d, 0x1959, 0x1954, 0x1950, 0x194c, 0x1947, + 0x1943, 0x193e, 0x193a, 0x1936, 0x1931, 0x192d, 0x1929, 0x1924, 0x1920, 0x191c, 0x1917, 0x1913, 0x190f, 0x190a, + 0x1906, 0x1902, 0x18fd, 0x18f9, 0x18f5, 0x18f0, 0x18ec, 0x18e8, 0x18e3, 0x18df, 0x18db, 0x18d6, 0x18d2, 0x18ce, + 0x18ca, 0x18c5, 0x18c1, 0x18bd, 0x18b8, 0x18b4, 0x18b0, 0x18ac, 0x18a7, 0x18a3, 0x189f, 0x189b, 0x1896, 0x1892, + 0x188e, 0x188a, 0x1885, 0x1881, 0x187d, 0x1879, 0x1874, 0x1870, 0x186c, 0x1868, 0x1863, 0x185f, 0x185b, 0x1857, + 0x1853, 0x184e, 0x184a, 0x1846, 0x1842, 0x183e, 0x1839, 0x1835, 0x1831, 0x182d, 0x1829, 0x1824, 0x1820, 0x181c, + 0x1818, 0x1814, 0x180f, 0x180b, 0x1807, 0x1803, 0x17ff, 0x17fb, 0x17f7, 0x17f2, 0x17ee, 0x17ea, 0x17e6, 0x17e2, + 0x17de, 0x17da, 0x17d5, 0x17d1, 0x17cd, 0x17c9, 0x17c5, 0x17c1, 0x17bd, 0x17b9, 0x17b5, 0x17b0, 0x17ac, 0x17a8, + 0x17a4, 0x17a0, 0x179c, 0x1798, 0x1794, 0x1790, 0x178c, 0x1788, 0x1784, 0x177f, 0x177b, 0x1777, 0x1773, 0x176f, + 0x176b, 0x1767, 0x1763, 0x175f, 0x175b, 0x1757, 0x1753, 0x174f, 0x174b, 0x1747, 0x1743, 0x173f, 0x173b, 0x1737, + 0x1733, 0x172f, 0x172b, 0x1727, 0x1723, 0x171f, 0x171b, 0x1717, 0x1713, 0x170f, 0x170b, 0x1707, 0x1703, 0x16ff, + 0x16fb, 0x16f7, 0x16f3, 0x16ef, 0x16eb, 0x16e7, 0x16e3, 0x16df, 0x16db, 0x16d7, 0x16d3, 0x16cf, 0x16cb, 0x16c7, + 0x16c3, 0x16bf, 0x16bb, 0x16b7, 0x16b3, 0x16b0, 0x16ac, 0x16a8, 0x16a4, 0x16a0, 0x169c, 0x1698, 0x1694, 0x1690, + 0x168c, 0x1688, 0x1685, 0x1681, 0x167d, 0x1679, 0x1675, 0x1671, 0x166d, 0x1669, 0x1665, 0x1662, 0x165e, 0x165a, + 0x1656, 0x1652, 0x164e, 0x164a, 0x1647, 0x1643, 0x163f, 0x163b, 0x1637, 0x1633, 0x162f, 0x162c, 0x1628, 0x1624, + 0x1620, 0x161c, 0x1618, 0x1615, 0x1611, 0x160d, 0x1609, 0x1605, 0x1602, 0x15fe, 0x15fa, 0x15f6, 0x15f2, 0x15ee, + 0x15eb, 0x15e7, 0x15e3, 0x15df, 0x15dc, 0x15d8, 0x15d4, 0x15d0, 0x15cc, 0x15c9, 0x15c5, 0x15c1, 0x15bd, 0x15b9, + 0x15b6, 0x15b2, 0x15ae, 0x15aa, 0x15a7, 0x15a3, 0x159f, 0x159c, 0x1598, 0x1594, 0x1590, 0x158d, 0x1589, 0x1585, + 0x1581, 0x157e, 0x157a, 0x1576, 0x1573, 0x156f, 0x156b, 0x1567, 0x1564, 0x1560, 0x155c, 0x1559, 0x1555, 0x1551, + 0x154e, 0x154a, 0x1546, 0x1542, 0x153f, 0x153b, 0x1537, 0x1534, 0x1530, 0x152c, 0x1529, 0x1525, 0x1521, 0x151e, + 0x151a, 0x1516, 0x1513, 0x150f, 0x150b, 0x1508, 0x1504, 0x1501, 0x14fd, 0x14f9, 0x14f6, 0x14f2, 0x14ee, 0x14eb, + 0x14e7, 0x14e4, 0x14e0, 0x14dc, 0x14d9, 0x14d5, 0x14d2, 0x14ce, 0x14ca, 0x14c7, 0x14c3, 0x14c0, 0x14bc, 0x14b8, + 0x14b5, 0x14b1, 0x14ae, 0x14aa, 0x14a6, 0x14a3, 0x149f, 0x149c, 0x1498, 0x1495, 0x1491, 0x148d, 0x148a, 0x1486, + 0x1483, 0x147f, 0x147c, 0x1478, 0x1475, 0x1471, 0x146e, 0x146a, 0x1466, 0x1463, 0x145f, 0x145c, 0x1458, 0x1455, + 0x1451, 0x144e, 0x144a, 0x1447, 0x1443, 0x1440, 0x143c, 0x1439, 0x1435, 0x1432, 0x142e, 0x142b, 0x1427, 0x1424, + 0x1420, 0x141d, 0x1419, 0x1416, 0x1412, 0x140f, 0x140b, 0x1408, 0x1405, 0x1401, 0x13fd, 0x13fa, 0x13f7, 0x13f3, + 0x13f0, 0x13ec, 0x13e9, 0x13e5, 0x13e2, 0x13de, 0x13db, 0x13d8, 0x13d4, 0x13d1, 0x13cd, 0x13ca, 0x13c7, 0x13c3, + 0x13c0, 0x13bc, 0x13b9, 0x13b5, 0x13b2, 0x13ae, 0x13ab, 0x13a8, 0x13a4, 0x13a1, 0x139e, 0x139a, 0x1397, 0x1393, + 0x1390, 0x138d, 0x1389, 0x1386, 0x1382, 0x137f, 0x137c, 0x1378, 0x1375, 0x1372, 0x136e, 0x136b, 0x1367, 0x1364, + 0x1361, 0x135d, 0x135a, 0x1357, 0x1353, 0x1350, 0x134d, 0x1349, 0x1346, 0x1343, 0x133f, 0x133c, 0x1339, 0x1335, + 0x1332, 0x132f, 0x132b, 0x1328, 0x1325, 0x1321, 0x131e, 0x131b, 0x1317, 0x1314, 0x1311, 0x130e, 0x130a, 0x1307, + 0x1304, 0x1300, 0x12fd, 0x12fa, 0x12f7, 0x12f3, 0x12f0, 0x12ed, 0x12e9, 0x12e6, 0x12e3, 0x12df, 0x12dc, 0x12d9, + 0x12d6, 0x12d2, 0x12cf, 0x12cc, 0x12c9, 0x12c5, 0x12c2, 0x12bf, 0x12bc, 0x12b8, 0x12b5, 0x12b2, 0x12af, 0x12ac, + 0x12a8, 0x12a5, 0x12a2, 0x129f, 0x129b, 0x1298, 0x1295, 0x1292, 0x128e, 0x128b, 0x1288, 0x1285, 0x1282, 0x127e, + 0x127b, 0x1278, 0x1275, 0x1272, 0x126e, 0x126b, 0x1268, 0x1265, 0x1262, 0x125e, 0x125b, 0x1258, 0x1255, 0x1252, + 0x124f, 0x124b, 0x1248, 0x1245, 0x1242, 0x123f, 0x123c, 0x1238, 0x1235, 0x1232, 0x122f, 0x122c, 0x1229, 0x1226, + 0x1222, 0x121f, 0x121c, 0x1219, 0x1216, 0x1213, 0x1210, 0x120c, 0x1209, 0x1206, 0x1203, 0x1200, 0x11fd, 0x11fa, + 0x11f7, 0x11f4, 0x11f0, 0x11ed, 0x11ea, 0x11e7, 0x11e4, 0x11e1, 0x11de, 0x11db, 0x11d8, 0x11d5, 0x11d1, 0x11ce, + 0x11cb, 0x11c8, 0x11c5, 0x11c2, 0x11bf, 0x11bc, 0x11b9, 0x11b6, 0x11b3, 0x11b0, 0x11ad, 0x11a9, 0x11a6, 0x11a3, + 0x11a0, 0x119d, 0x119a, 0x1197, 0x1194, 0x1191, 0x118e, 0x118b, 0x1188, 0x1185, 0x1182, 0x117f, 0x117c, 0x1179, + 0x1176, 0x1173, 0x1170, 0x116d, 0x116a, 0x1167, 0x1164, 0x1161, 0x115e, 0x115b, 0x1158, 0x1155, 0x1152, 0x114f, + 0x114c, 0x1149, 0x1146, 0x1143, 0x1140, 0x113d, 0x113a, 0x1137, 0x1134, 0x1131, 0x112e, 0x112b, 0x1128, 0x1125, + 0x1122, 0x111f, 0x111c, 0x1119, 0x1116, 0x1113, 0x1110, 0x110d, 0x110a, 0x1107, 0x1104, 0x1101, 0x10fe, 0x10fb, + 0x10f8, 0x10f5, 0x10f2, 0x10ef, 0x10ec, 0x10e9, 0x10e6, 0x10e3, 0x10e0, 0x10de, 0x10db, 0x10d8, 0x10d5, 0x10d2, + 0x10cf, 0x10cc, 0x10c9, 0x10c6, 0x10c3, 0x10c0, 0x10bd, 0x10ba, 0x10b8, 0x10b5, 0x10b2, 0x10af, 0x10ac, 0x10a9, + 0x10a6, 0x10a3, 0x10a0, 0x109e, 0x109b, 0x1098, 0x1095, 0x1092, 0x108f, 0x108c, 0x1089, 0x1086, 0x1084, 0x1081, + 0x107e, 0x107b, 0x1078, 0x1075, 0x1072, 0x1070, 0x106d, 0x106a, 0x1067, 0x1064, 0x1061, 0x105e, 0x105c, 0x1059, + 0x1056, 0x1053, 0x1050, 0x104d, 0x104a, 0x1048, 0x1045, 0x1042, 0x103f, 0x103c, 0x1039, 0x1037, 0x1034, 0x1031, + 0x102e, 0x102b, 0x1028, 0x1026, 0x1023, 0x1020, 0x101d, 0x101a, 0x1018, 0x1015, 0x1012, 0x100f, 0x100c, 0x100a, + 0x1007, 0x1004, +}; + +static uint16_t logtbl[4096] = { + 0x000, 0x000, 0x000, 0x000, 0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x003, 0x003, 0x004, 0x004, 0x004, 0x005, + 0x005, 0x005, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007, 0x008, 0x008, 0x009, 0x009, 0x009, 0x00a, 0x00a, 0x00a, + 0x00b, 0x00b, 0x00b, 0x00c, 0x00c, 0x00c, 0x00d, 0x00d, 0x00e, 0x00e, 0x00e, 0x00f, 0x00f, 0x00f, 0x010, 0x010, + 0x010, 0x011, 0x011, 0x011, 0x012, 0x012, 0x012, 0x013, 0x013, 0x014, 0x014, 0x014, 0x015, 0x015, 0x015, 0x016, + 0x016, 0x016, 0x017, 0x017, 0x017, 0x018, 0x018, 0x018, 0x019, 0x019, 0x01a, 0x01a, 0x01a, 0x01b, 0x01b, 0x01b, + 0x01c, 0x01c, 0x01c, 0x01d, 0x01d, 0x01d, 0x01e, 0x01e, 0x01e, 0x01f, 0x01f, 0x01f, 0x020, 0x020, 0x021, 0x021, + 0x021, 0x022, 0x022, 0x022, 0x023, 0x023, 0x023, 0x024, 0x024, 0x024, 0x025, 0x025, 0x025, 0x026, 0x026, 0x026, + 0x027, 0x027, 0x028, 0x028, 0x028, 0x029, 0x029, 0x029, 0x02a, 0x02a, 0x02a, 0x02b, 0x02b, 0x02b, 0x02c, 0x02c, + 0x02c, 0x02d, 0x02d, 0x02d, 0x02e, 0x02e, 0x02f, 0x02f, 0x02f, 0x030, 0x030, 0x030, 0x031, 0x031, 0x031, 0x032, + 0x032, 0x032, 0x033, 0x033, 0x033, 0x034, 0x034, 0x034, 0x035, 0x035, 0x035, 0x036, 0x036, 0x036, 0x037, 0x037, + 0x038, 0x038, 0x038, 0x039, 0x039, 0x039, 0x03a, 0x03a, 0x03a, 0x03b, 0x03b, 0x03b, 0x03c, 0x03c, 0x03c, 0x03d, + 0x03d, 0x03d, 0x03e, 0x03e, 0x03e, 0x03f, 0x03f, 0x03f, 0x040, 0x040, 0x041, 0x041, 0x041, 0x042, 0x042, 0x042, + 0x043, 0x043, 0x043, 0x044, 0x044, 0x044, 0x045, 0x045, 0x045, 0x046, 0x046, 0x046, 0x047, 0x047, 0x047, 0x048, + 0x048, 0x048, 0x049, 0x049, 0x049, 0x04a, 0x04a, 0x04a, 0x04b, 0x04b, 0x04b, 0x04c, 0x04c, 0x04c, 0x04d, 0x04d, + 0x04e, 0x04e, 0x04e, 0x04f, 0x04f, 0x04f, 0x050, 0x050, 0x050, 0x051, 0x051, 0x051, 0x052, 0x052, 0x052, 0x053, + 0x053, 0x053, 0x054, 0x054, 0x054, 0x055, 0x055, 0x055, 0x056, 0x056, 0x056, 0x057, 0x057, 0x057, 0x058, 0x058, + 0x058, 0x059, 0x059, 0x059, 0x05a, 0x05a, 0x05a, 0x05b, 0x05b, 0x05b, 0x05c, 0x05c, 0x05c, 0x05d, 0x05d, 0x05d, + 0x05e, 0x05e, 0x05e, 0x05f, 0x05f, 0x05f, 0x060, 0x060, 0x060, 0x061, 0x061, 0x061, 0x062, 0x062, 0x062, 0x063, + 0x063, 0x063, 0x064, 0x064, 0x064, 0x065, 0x065, 0x065, 0x066, 0x066, 0x066, 0x067, 0x067, 0x067, 0x068, 0x068, + 0x068, 0x069, 0x069, 0x069, 0x06a, 0x06a, 0x06a, 0x06b, 0x06b, 0x06b, 0x06c, 0x06c, 0x06c, 0x06d, 0x06d, 0x06d, + 0x06e, 0x06e, 0x06e, 0x06f, 0x06f, 0x06f, 0x070, 0x070, 0x070, 0x071, 0x071, 0x071, 0x072, 0x072, 0x072, 0x073, + 0x073, 0x073, 0x074, 0x074, 0x074, 0x075, 0x075, 0x075, 0x076, 0x076, 0x076, 0x077, 0x077, 0x077, 0x078, 0x078, + 0x078, 0x079, 0x079, 0x079, 0x07a, 0x07a, 0x07a, 0x07b, 0x07b, 0x07b, 0x07c, 0x07c, 0x07c, 0x07d, 0x07d, 0x07d, + 0x07e, 0x07e, 0x07e, 0x07f, 0x07f, 0x07f, 0x080, 0x080, 0x080, 0x081, 0x081, 0x081, 0x082, 0x082, 0x082, 0x083, + 0x083, 0x083, 0x084, 0x084, 0x084, 0x085, 0x085, 0x085, 0x086, 0x086, 0x086, 0x087, 0x087, 0x087, 0x088, 0x088, + 0x088, 0x089, 0x089, 0x089, 0x08a, 0x08a, 0x08a, 0x08b, 0x08b, 0x08b, 0x08c, 0x08c, 0x08c, 0x08d, 0x08d, 0x08d, + 0x08e, 0x08e, 0x08e, 0x08f, 0x08f, 0x08f, 0x090, 0x090, 0x090, 0x091, 0x091, 0x091, 0x091, 0x092, 0x092, 0x092, + 0x093, 0x093, 0x093, 0x094, 0x094, 0x094, 0x095, 0x095, 0x095, 0x096, 0x096, 0x096, 0x097, 0x097, 0x097, 0x098, + 0x098, 0x098, 0x099, 0x099, 0x099, 0x09a, 0x09a, 0x09a, 0x09b, 0x09b, 0x09b, 0x09c, 0x09c, 0x09c, 0x09d, 0x09d, + 0x09d, 0x09e, 0x09e, 0x09e, 0x09e, 0x09f, 0x09f, 0x09f, 0x0a0, 0x0a0, 0x0a0, 0x0a1, 0x0a1, 0x0a1, 0x0a2, 0x0a2, + 0x0a2, 0x0a3, 0x0a3, 0x0a3, 0x0a4, 0x0a4, 0x0a4, 0x0a5, 0x0a5, 0x0a5, 0x0a6, 0x0a6, 0x0a6, 0x0a7, 0x0a7, 0x0a7, + 0x0a8, 0x0a8, 0x0a8, 0x0a8, 0x0a9, 0x0a9, 0x0a9, 0x0aa, 0x0aa, 0x0aa, 0x0ab, 0x0ab, 0x0ab, 0x0ac, 0x0ac, 0x0ac, + 0x0ad, 0x0ad, 0x0ad, 0x0ae, 0x0ae, 0x0ae, 0x0af, 0x0af, 0x0af, 0x0b0, 0x0b0, 0x0b0, 0x0b1, 0x0b1, 0x0b1, 0x0b1, + 0x0b2, 0x0b2, 0x0b2, 0x0b3, 0x0b3, 0x0b3, 0x0b4, 0x0b4, 0x0b4, 0x0b5, 0x0b5, 0x0b5, 0x0b6, 0x0b6, 0x0b6, 0x0b7, + 0x0b7, 0x0b7, 0x0b8, 0x0b8, 0x0b8, 0x0b8, 0x0b9, 0x0b9, 0x0b9, 0x0ba, 0x0ba, 0x0ba, 0x0bb, 0x0bb, 0x0bb, 0x0bc, + 0x0bc, 0x0bc, 0x0bd, 0x0bd, 0x0bd, 0x0be, 0x0be, 0x0be, 0x0bf, 0x0bf, 0x0bf, 0x0bf, 0x0c0, 0x0c0, 0x0c0, 0x0c1, + 0x0c1, 0x0c1, 0x0c2, 0x0c2, 0x0c2, 0x0c3, 0x0c3, 0x0c3, 0x0c4, 0x0c4, 0x0c4, 0x0c5, 0x0c5, 0x0c5, 0x0c5, 0x0c6, + 0x0c6, 0x0c6, 0x0c7, 0x0c7, 0x0c7, 0x0c8, 0x0c8, 0x0c8, 0x0c9, 0x0c9, 0x0c9, 0x0ca, 0x0ca, 0x0ca, 0x0cb, 0x0cb, + 0x0cb, 0x0cb, 0x0cc, 0x0cc, 0x0cc, 0x0cd, 0x0cd, 0x0cd, 0x0ce, 0x0ce, 0x0ce, 0x0cf, 0x0cf, 0x0cf, 0x0d0, 0x0d0, + 0x0d0, 0x0d0, 0x0d1, 0x0d1, 0x0d1, 0x0d2, 0x0d2, 0x0d2, 0x0d3, 0x0d3, 0x0d3, 0x0d4, 0x0d4, 0x0d4, 0x0d5, 0x0d5, + 0x0d5, 0x0d5, 0x0d6, 0x0d6, 0x0d6, 0x0d7, 0x0d7, 0x0d7, 0x0d8, 0x0d8, 0x0d8, 0x0d9, 0x0d9, 0x0d9, 0x0da, 0x0da, + 0x0da, 0x0da, 0x0db, 0x0db, 0x0db, 0x0dc, 0x0dc, 0x0dc, 0x0dd, 0x0dd, 0x0dd, 0x0de, 0x0de, 0x0de, 0x0de, 0x0df, + 0x0df, 0x0df, 0x0e0, 0x0e0, 0x0e0, 0x0e1, 0x0e1, 0x0e1, 0x0e2, 0x0e2, 0x0e2, 0x0e2, 0x0e3, 0x0e3, 0x0e3, 0x0e4, + 0x0e4, 0x0e4, 0x0e5, 0x0e5, 0x0e5, 0x0e6, 0x0e6, 0x0e6, 0x0e7, 0x0e7, 0x0e7, 0x0e7, 0x0e8, 0x0e8, 0x0e8, 0x0e9, + 0x0e9, 0x0e9, 0x0ea, 0x0ea, 0x0ea, 0x0eb, 0x0eb, 0x0eb, 0x0eb, 0x0ec, 0x0ec, 0x0ec, 0x0ed, 0x0ed, 0x0ed, 0x0ee, + 0x0ee, 0x0ee, 0x0ef, 0x0ef, 0x0ef, 0x0ef, 0x0f0, 0x0f0, 0x0f0, 0x0f1, 0x0f1, 0x0f1, 0x0f2, 0x0f2, 0x0f2, 0x0f3, + 0x0f3, 0x0f3, 0x0f3, 0x0f4, 0x0f4, 0x0f4, 0x0f5, 0x0f5, 0x0f5, 0x0f6, 0x0f6, 0x0f6, 0x0f6, 0x0f7, 0x0f7, 0x0f7, + 0x0f8, 0x0f8, 0x0f8, 0x0f9, 0x0f9, 0x0f9, 0x0f9, 0x0fa, 0x0fa, 0x0fa, 0x0fb, 0x0fb, 0x0fb, 0x0fc, 0x0fc, 0x0fc, + 0x0fd, 0x0fd, 0x0fd, 0x0fd, 0x0fe, 0x0fe, 0x0fe, 0x0ff, 0x0ff, 0x0ff, 0x100, 0x100, 0x100, 0x100, 0x101, 0x101, + 0x101, 0x102, 0x102, 0x102, 0x103, 0x103, 0x103, 0x104, 0x104, 0x104, 0x104, 0x105, 0x105, 0x105, 0x106, 0x106, + 0x106, 0x107, 0x107, 0x107, 0x107, 0x108, 0x108, 0x108, 0x109, 0x109, 0x109, 0x10a, 0x10a, 0x10a, 0x10a, 0x10b, + 0x10b, 0x10b, 0x10c, 0x10c, 0x10c, 0x10d, 0x10d, 0x10d, 0x10d, 0x10e, 0x10e, 0x10e, 0x10f, 0x10f, 0x10f, 0x110, + 0x110, 0x110, 0x110, 0x111, 0x111, 0x111, 0x112, 0x112, 0x112, 0x113, 0x113, 0x113, 0x113, 0x114, 0x114, 0x114, + 0x115, 0x115, 0x115, 0x116, 0x116, 0x116, 0x116, 0x117, 0x117, 0x117, 0x118, 0x118, 0x118, 0x119, 0x119, 0x119, + 0x119, 0x11a, 0x11a, 0x11a, 0x11b, 0x11b, 0x11b, 0x11c, 0x11c, 0x11c, 0x11c, 0x11d, 0x11d, 0x11d, 0x11e, 0x11e, + 0x11e, 0x11e, 0x11f, 0x11f, 0x11f, 0x120, 0x120, 0x120, 0x121, 0x121, 0x121, 0x121, 0x122, 0x122, 0x122, 0x123, + 0x123, 0x123, 0x124, 0x124, 0x124, 0x124, 0x125, 0x125, 0x125, 0x126, 0x126, 0x126, 0x126, 0x127, 0x127, 0x127, + 0x128, 0x128, 0x128, 0x129, 0x129, 0x129, 0x129, 0x12a, 0x12a, 0x12a, 0x12b, 0x12b, 0x12b, 0x12b, 0x12c, 0x12c, + 0x12c, 0x12d, 0x12d, 0x12d, 0x12e, 0x12e, 0x12e, 0x12e, 0x12f, 0x12f, 0x12f, 0x130, 0x130, 0x130, 0x131, 0x131, + 0x131, 0x131, 0x132, 0x132, 0x132, 0x133, 0x133, 0x133, 0x133, 0x134, 0x134, 0x134, 0x135, 0x135, 0x135, 0x135, + 0x136, 0x136, 0x136, 0x137, 0x137, 0x137, 0x137, 0x138, 0x138, 0x138, 0x139, 0x139, 0x139, 0x13a, 0x13a, 0x13a, + 0x13a, 0x13b, 0x13b, 0x13b, 0x13c, 0x13c, 0x13c, 0x13c, 0x13d, 0x13d, 0x13d, 0x13e, 0x13e, 0x13e, 0x13f, 0x13f, + 0x13f, 0x13f, 0x140, 0x140, 0x140, 0x141, 0x141, 0x141, 0x141, 0x142, 0x142, 0x142, 0x143, 0x143, 0x143, 0x143, + 0x144, 0x144, 0x144, 0x145, 0x145, 0x145, 0x145, 0x146, 0x146, 0x146, 0x147, 0x147, 0x147, 0x148, 0x148, 0x148, + 0x148, 0x149, 0x149, 0x149, 0x149, 0x14a, 0x14a, 0x14a, 0x14b, 0x14b, 0x14b, 0x14c, 0x14c, 0x14c, 0x14c, 0x14d, + 0x14d, 0x14d, 0x14e, 0x14e, 0x14e, 0x14e, 0x14f, 0x14f, 0x14f, 0x150, 0x150, 0x150, 0x150, 0x151, 0x151, 0x151, + 0x152, 0x152, 0x152, 0x152, 0x153, 0x153, 0x153, 0x154, 0x154, 0x154, 0x154, 0x155, 0x155, 0x155, 0x156, 0x156, + 0x156, 0x156, 0x157, 0x157, 0x157, 0x158, 0x158, 0x158, 0x158, 0x159, 0x159, 0x159, 0x15a, 0x15a, 0x15a, 0x15a, + 0x15b, 0x15b, 0x15b, 0x15c, 0x15c, 0x15c, 0x15c, 0x15d, 0x15d, 0x15d, 0x15e, 0x15e, 0x15e, 0x15e, 0x15f, 0x15f, + 0x15f, 0x160, 0x160, 0x160, 0x160, 0x161, 0x161, 0x161, 0x162, 0x162, 0x162, 0x162, 0x163, 0x163, 0x163, 0x164, + 0x164, 0x164, 0x164, 0x165, 0x165, 0x165, 0x166, 0x166, 0x166, 0x166, 0x167, 0x167, 0x167, 0x167, 0x168, 0x168, + 0x168, 0x169, 0x169, 0x169, 0x169, 0x16a, 0x16a, 0x16a, 0x16b, 0x16b, 0x16b, 0x16b, 0x16c, 0x16c, 0x16c, 0x16d, + 0x16d, 0x16d, 0x16d, 0x16e, 0x16e, 0x16e, 0x16f, 0x16f, 0x16f, 0x16f, 0x170, 0x170, 0x170, 0x171, 0x171, 0x171, + 0x171, 0x172, 0x172, 0x172, 0x172, 0x173, 0x173, 0x173, 0x174, 0x174, 0x174, 0x174, 0x175, 0x175, 0x175, 0x176, + 0x176, 0x176, 0x176, 0x177, 0x177, 0x177, 0x178, 0x178, 0x178, 0x178, 0x179, 0x179, 0x179, 0x179, 0x17a, 0x17a, + 0x17a, 0x17b, 0x17b, 0x17b, 0x17b, 0x17c, 0x17c, 0x17c, 0x17d, 0x17d, 0x17d, 0x17d, 0x17e, 0x17e, 0x17e, 0x17e, + 0x17f, 0x17f, 0x17f, 0x180, 0x180, 0x180, 0x180, 0x181, 0x181, 0x181, 0x182, 0x182, 0x182, 0x182, 0x183, 0x183, + 0x183, 0x183, 0x184, 0x184, 0x184, 0x185, 0x185, 0x185, 0x185, 0x186, 0x186, 0x186, 0x187, 0x187, 0x187, 0x187, + 0x188, 0x188, 0x188, 0x188, 0x189, 0x189, 0x189, 0x18a, 0x18a, 0x18a, 0x18a, 0x18b, 0x18b, 0x18b, 0x18c, 0x18c, + 0x18c, 0x18c, 0x18d, 0x18d, 0x18d, 0x18d, 0x18e, 0x18e, 0x18e, 0x18f, 0x18f, 0x18f, 0x18f, 0x190, 0x190, 0x190, + 0x190, 0x191, 0x191, 0x191, 0x192, 0x192, 0x192, 0x192, 0x193, 0x193, 0x193, 0x194, 0x194, 0x194, 0x194, 0x195, + 0x195, 0x195, 0x195, 0x196, 0x196, 0x196, 0x197, 0x197, 0x197, 0x197, 0x198, 0x198, 0x198, 0x198, 0x199, 0x199, + 0x199, 0x19a, 0x19a, 0x19a, 0x19a, 0x19b, 0x19b, 0x19b, 0x19b, 0x19c, 0x19c, 0x19c, 0x19d, 0x19d, 0x19d, 0x19d, + 0x19e, 0x19e, 0x19e, 0x19e, 0x19f, 0x19f, 0x19f, 0x1a0, 0x1a0, 0x1a0, 0x1a0, 0x1a1, 0x1a1, 0x1a1, 0x1a1, 0x1a2, + 0x1a2, 0x1a2, 0x1a3, 0x1a3, 0x1a3, 0x1a3, 0x1a4, 0x1a4, 0x1a4, 0x1a4, 0x1a5, 0x1a5, 0x1a5, 0x1a5, 0x1a6, 0x1a6, + 0x1a6, 0x1a7, 0x1a7, 0x1a7, 0x1a7, 0x1a8, 0x1a8, 0x1a8, 0x1a8, 0x1a9, 0x1a9, 0x1a9, 0x1aa, 0x1aa, 0x1aa, 0x1aa, + 0x1ab, 0x1ab, 0x1ab, 0x1ab, 0x1ac, 0x1ac, 0x1ac, 0x1ad, 0x1ad, 0x1ad, 0x1ad, 0x1ae, 0x1ae, 0x1ae, 0x1ae, 0x1af, + 0x1af, 0x1af, 0x1af, 0x1b0, 0x1b0, 0x1b0, 0x1b1, 0x1b1, 0x1b1, 0x1b1, 0x1b2, 0x1b2, 0x1b2, 0x1b2, 0x1b3, 0x1b3, + 0x1b3, 0x1b4, 0x1b4, 0x1b4, 0x1b4, 0x1b5, 0x1b5, 0x1b5, 0x1b5, 0x1b6, 0x1b6, 0x1b6, 0x1b6, 0x1b7, 0x1b7, 0x1b7, + 0x1b8, 0x1b8, 0x1b8, 0x1b8, 0x1b9, 0x1b9, 0x1b9, 0x1b9, 0x1ba, 0x1ba, 0x1ba, 0x1ba, 0x1bb, 0x1bb, 0x1bb, 0x1bc, + 0x1bc, 0x1bc, 0x1bc, 0x1bd, 0x1bd, 0x1bd, 0x1bd, 0x1be, 0x1be, 0x1be, 0x1bf, 0x1bf, 0x1bf, 0x1bf, 0x1c0, 0x1c0, + 0x1c0, 0x1c0, 0x1c1, 0x1c1, 0x1c1, 0x1c1, 0x1c2, 0x1c2, 0x1c2, 0x1c3, 0x1c3, 0x1c3, 0x1c3, 0x1c4, 0x1c4, 0x1c4, + 0x1c4, 0x1c5, 0x1c5, 0x1c5, 0x1c5, 0x1c6, 0x1c6, 0x1c6, 0x1c6, 0x1c7, 0x1c7, 0x1c7, 0x1c8, 0x1c8, 0x1c8, 0x1c8, + 0x1c9, 0x1c9, 0x1c9, 0x1c9, 0x1ca, 0x1ca, 0x1ca, 0x1ca, 0x1cb, 0x1cb, 0x1cb, 0x1cb, 0x1cc, 0x1cc, 0x1cc, 0x1cd, + 0x1cd, 0x1cd, 0x1cd, 0x1ce, 0x1ce, 0x1ce, 0x1ce, 0x1cf, 0x1cf, 0x1cf, 0x1cf, 0x1d0, 0x1d0, 0x1d0, 0x1d1, 0x1d1, + 0x1d1, 0x1d1, 0x1d2, 0x1d2, 0x1d2, 0x1d2, 0x1d3, 0x1d3, 0x1d3, 0x1d3, 0x1d4, 0x1d4, 0x1d4, 0x1d4, 0x1d5, 0x1d5, + 0x1d5, 0x1d5, 0x1d6, 0x1d6, 0x1d6, 0x1d7, 0x1d7, 0x1d7, 0x1d7, 0x1d8, 0x1d8, 0x1d8, 0x1d8, 0x1d9, 0x1d9, 0x1d9, + 0x1d9, 0x1da, 0x1da, 0x1da, 0x1da, 0x1db, 0x1db, 0x1db, 0x1dc, 0x1dc, 0x1dc, 0x1dc, 0x1dd, 0x1dd, 0x1dd, 0x1dd, + 0x1de, 0x1de, 0x1de, 0x1de, 0x1df, 0x1df, 0x1df, 0x1df, 0x1e0, 0x1e0, 0x1e0, 0x1e0, 0x1e1, 0x1e1, 0x1e1, 0x1e2, + 0x1e2, 0x1e2, 0x1e2, 0x1e3, 0x1e3, 0x1e3, 0x1e3, 0x1e4, 0x1e4, 0x1e4, 0x1e4, 0x1e5, 0x1e5, 0x1e5, 0x1e5, 0x1e6, + 0x1e6, 0x1e6, 0x1e6, 0x1e7, 0x1e7, 0x1e7, 0x1e7, 0x1e8, 0x1e8, 0x1e8, 0x1e9, 0x1e9, 0x1e9, 0x1e9, 0x1ea, 0x1ea, + 0x1ea, 0x1ea, 0x1eb, 0x1eb, 0x1eb, 0x1eb, 0x1ec, 0x1ec, 0x1ec, 0x1ec, 0x1ed, 0x1ed, 0x1ed, 0x1ed, 0x1ee, 0x1ee, + 0x1ee, 0x1ee, 0x1ef, 0x1ef, 0x1ef, 0x1f0, 0x1f0, 0x1f0, 0x1f0, 0x1f1, 0x1f1, 0x1f1, 0x1f1, 0x1f2, 0x1f2, 0x1f2, + 0x1f2, 0x1f3, 0x1f3, 0x1f3, 0x1f3, 0x1f4, 0x1f4, 0x1f4, 0x1f4, 0x1f5, 0x1f5, 0x1f5, 0x1f5, 0x1f6, 0x1f6, 0x1f6, + 0x1f6, 0x1f7, 0x1f7, 0x1f7, 0x1f7, 0x1f8, 0x1f8, 0x1f8, 0x1f9, 0x1f9, 0x1f9, 0x1f9, 0x1fa, 0x1fa, 0x1fa, 0x1fa, + 0x1fb, 0x1fb, 0x1fb, 0x1fb, 0x1fc, 0x1fc, 0x1fc, 0x1fc, 0x1fd, 0x1fd, 0x1fd, 0x1fd, 0x1fe, 0x1fe, 0x1fe, 0x1fe, + 0x1ff, 0x1ff, 0x1ff, 0x1ff, 0x200, 0x200, 0x200, 0x200, 0x201, 0x201, 0x201, 0x201, 0x202, 0x202, 0x202, 0x202, + 0x203, 0x203, 0x203, 0x204, 0x204, 0x204, 0x204, 0x205, 0x205, 0x205, 0x205, 0x206, 0x206, 0x206, 0x206, 0x207, + 0x207, 0x207, 0x207, 0x208, 0x208, 0x208, 0x208, 0x209, 0x209, 0x209, 0x209, 0x20a, 0x20a, 0x20a, 0x20a, 0x20b, + 0x20b, 0x20b, 0x20b, 0x20c, 0x20c, 0x20c, 0x20c, 0x20d, 0x20d, 0x20d, 0x20d, 0x20e, 0x20e, 0x20e, 0x20e, 0x20f, + 0x20f, 0x20f, 0x20f, 0x210, 0x210, 0x210, 0x210, 0x211, 0x211, 0x211, 0x211, 0x212, 0x212, 0x212, 0x212, 0x213, + 0x213, 0x213, 0x213, 0x214, 0x214, 0x214, 0x214, 0x215, 0x215, 0x215, 0x215, 0x216, 0x216, 0x216, 0x216, 0x217, + 0x217, 0x217, 0x217, 0x218, 0x218, 0x218, 0x218, 0x219, 0x219, 0x219, 0x219, 0x21a, 0x21a, 0x21a, 0x21a, 0x21b, + 0x21b, 0x21b, 0x21b, 0x21c, 0x21c, 0x21c, 0x21c, 0x21d, 0x21d, 0x21d, 0x21d, 0x21e, 0x21e, 0x21e, 0x21e, 0x21f, + 0x21f, 0x21f, 0x21f, 0x220, 0x220, 0x220, 0x220, 0x221, 0x221, 0x221, 0x221, 0x222, 0x222, 0x222, 0x222, 0x223, + 0x223, 0x223, 0x223, 0x224, 0x224, 0x224, 0x224, 0x225, 0x225, 0x225, 0x225, 0x226, 0x226, 0x226, 0x226, 0x227, + 0x227, 0x227, 0x227, 0x228, 0x228, 0x228, 0x228, 0x229, 0x229, 0x229, 0x229, 0x22a, 0x22a, 0x22a, 0x22a, 0x22b, + 0x22b, 0x22b, 0x22b, 0x22c, 0x22c, 0x22c, 0x22c, 0x22d, 0x22d, 0x22d, 0x22d, 0x22e, 0x22e, 0x22e, 0x22e, 0x22f, + 0x22f, 0x22f, 0x22f, 0x230, 0x230, 0x230, 0x230, 0x231, 0x231, 0x231, 0x231, 0x232, 0x232, 0x232, 0x232, 0x233, + 0x233, 0x233, 0x233, 0x234, 0x234, 0x234, 0x234, 0x235, 0x235, 0x235, 0x235, 0x236, 0x236, 0x236, 0x236, 0x237, + 0x237, 0x237, 0x237, 0x237, 0x238, 0x238, 0x238, 0x238, 0x239, 0x239, 0x239, 0x239, 0x23a, 0x23a, 0x23a, 0x23a, + 0x23b, 0x23b, 0x23b, 0x23b, 0x23c, 0x23c, 0x23c, 0x23c, 0x23d, 0x23d, 0x23d, 0x23d, 0x23e, 0x23e, 0x23e, 0x23e, + 0x23f, 0x23f, 0x23f, 0x23f, 0x240, 0x240, 0x240, 0x240, 0x241, 0x241, 0x241, 0x241, 0x242, 0x242, 0x242, 0x242, + 0x243, 0x243, 0x243, 0x243, 0x243, 0x244, 0x244, 0x244, 0x244, 0x245, 0x245, 0x245, 0x245, 0x246, 0x246, 0x246, + 0x246, 0x247, 0x247, 0x247, 0x247, 0x248, 0x248, 0x248, 0x248, 0x249, 0x249, 0x249, 0x249, 0x24a, 0x24a, 0x24a, + 0x24a, 0x24b, 0x24b, 0x24b, 0x24b, 0x24c, 0x24c, 0x24c, 0x24c, 0x24c, 0x24d, 0x24d, 0x24d, 0x24d, 0x24e, 0x24e, + 0x24e, 0x24e, 0x24f, 0x24f, 0x24f, 0x24f, 0x250, 0x250, 0x250, 0x250, 0x251, 0x251, 0x251, 0x251, 0x252, 0x252, + 0x252, 0x252, 0x253, 0x253, 0x253, 0x253, 0x253, 0x254, 0x254, 0x254, 0x254, 0x255, 0x255, 0x255, 0x255, 0x256, + 0x256, 0x256, 0x256, 0x257, 0x257, 0x257, 0x257, 0x258, 0x258, 0x258, 0x258, 0x259, 0x259, 0x259, 0x259, 0x25a, + 0x25a, 0x25a, 0x25a, 0x25a, 0x25b, 0x25b, 0x25b, 0x25b, 0x25c, 0x25c, 0x25c, 0x25c, 0x25d, 0x25d, 0x25d, 0x25d, + 0x25e, 0x25e, 0x25e, 0x25e, 0x25f, 0x25f, 0x25f, 0x25f, 0x25f, 0x260, 0x260, 0x260, 0x260, 0x261, 0x261, 0x261, + 0x261, 0x262, 0x262, 0x262, 0x262, 0x263, 0x263, 0x263, 0x263, 0x264, 0x264, 0x264, 0x264, 0x264, 0x265, 0x265, + 0x265, 0x265, 0x266, 0x266, 0x266, 0x266, 0x267, 0x267, 0x267, 0x267, 0x268, 0x268, 0x268, 0x268, 0x269, 0x269, + 0x269, 0x269, 0x269, 0x26a, 0x26a, 0x26a, 0x26a, 0x26b, 0x26b, 0x26b, 0x26b, 0x26c, 0x26c, 0x26c, 0x26c, 0x26d, + 0x26d, 0x26d, 0x26d, 0x26e, 0x26e, 0x26e, 0x26e, 0x26e, 0x26f, 0x26f, 0x26f, 0x26f, 0x270, 0x270, 0x270, 0x270, + 0x271, 0x271, 0x271, 0x271, 0x272, 0x272, 0x272, 0x272, 0x272, 0x273, 0x273, 0x273, 0x273, 0x274, 0x274, 0x274, + 0x274, 0x275, 0x275, 0x275, 0x275, 0x276, 0x276, 0x276, 0x276, 0x276, 0x277, 0x277, 0x277, 0x277, 0x278, 0x278, + 0x278, 0x278, 0x279, 0x279, 0x279, 0x279, 0x27a, 0x27a, 0x27a, 0x27a, 0x27a, 0x27b, 0x27b, 0x27b, 0x27b, 0x27c, + 0x27c, 0x27c, 0x27c, 0x27d, 0x27d, 0x27d, 0x27d, 0x27e, 0x27e, 0x27e, 0x27e, 0x27e, 0x27f, 0x27f, 0x27f, 0x27f, + 0x280, 0x280, 0x280, 0x280, 0x281, 0x281, 0x281, 0x281, 0x282, 0x282, 0x282, 0x282, 0x282, 0x283, 0x283, 0x283, + 0x283, 0x284, 0x284, 0x284, 0x284, 0x285, 0x285, 0x285, 0x285, 0x285, 0x286, 0x286, 0x286, 0x286, 0x287, 0x287, + 0x287, 0x287, 0x288, 0x288, 0x288, 0x288, 0x289, 0x289, 0x289, 0x289, 0x289, 0x28a, 0x28a, 0x28a, 0x28a, 0x28b, + 0x28b, 0x28b, 0x28b, 0x28c, 0x28c, 0x28c, 0x28c, 0x28c, 0x28d, 0x28d, 0x28d, 0x28d, 0x28e, 0x28e, 0x28e, 0x28e, + 0x28f, 0x28f, 0x28f, 0x28f, 0x28f, 0x290, 0x290, 0x290, 0x290, 0x291, 0x291, 0x291, 0x291, 0x292, 0x292, 0x292, + 0x292, 0x292, 0x293, 0x293, 0x293, 0x293, 0x294, 0x294, 0x294, 0x294, 0x295, 0x295, 0x295, 0x295, 0x295, 0x296, + 0x296, 0x296, 0x296, 0x297, 0x297, 0x297, 0x297, 0x298, 0x298, 0x298, 0x298, 0x298, 0x299, 0x299, 0x299, 0x299, + 0x29a, 0x29a, 0x29a, 0x29a, 0x29b, 0x29b, 0x29b, 0x29b, 0x29b, 0x29c, 0x29c, 0x29c, 0x29c, 0x29d, 0x29d, 0x29d, + 0x29d, 0x29e, 0x29e, 0x29e, 0x29e, 0x29e, 0x29f, 0x29f, 0x29f, 0x29f, 0x2a0, 0x2a0, 0x2a0, 0x2a0, 0x2a1, 0x2a1, + 0x2a1, 0x2a1, 0x2a1, 0x2a2, 0x2a2, 0x2a2, 0x2a2, 0x2a3, 0x2a3, 0x2a3, 0x2a3, 0x2a3, 0x2a4, 0x2a4, 0x2a4, 0x2a4, + 0x2a5, 0x2a5, 0x2a5, 0x2a5, 0x2a6, 0x2a6, 0x2a6, 0x2a6, 0x2a6, 0x2a7, 0x2a7, 0x2a7, 0x2a7, 0x2a8, 0x2a8, 0x2a8, + 0x2a8, 0x2a8, 0x2a9, 0x2a9, 0x2a9, 0x2a9, 0x2aa, 0x2aa, 0x2aa, 0x2aa, 0x2ab, 0x2ab, 0x2ab, 0x2ab, 0x2ab, 0x2ac, + 0x2ac, 0x2ac, 0x2ac, 0x2ad, 0x2ad, 0x2ad, 0x2ad, 0x2ad, 0x2ae, 0x2ae, 0x2ae, 0x2ae, 0x2af, 0x2af, 0x2af, 0x2af, + 0x2b0, 0x2b0, 0x2b0, 0x2b0, 0x2b0, 0x2b1, 0x2b1, 0x2b1, 0x2b1, 0x2b2, 0x2b2, 0x2b2, 0x2b2, 0x2b2, 0x2b3, 0x2b3, + 0x2b3, 0x2b3, 0x2b4, 0x2b4, 0x2b4, 0x2b4, 0x2b5, 0x2b5, 0x2b5, 0x2b5, 0x2b5, 0x2b6, 0x2b6, 0x2b6, 0x2b6, 0x2b7, + 0x2b7, 0x2b7, 0x2b7, 0x2b7, 0x2b8, 0x2b8, 0x2b8, 0x2b8, 0x2b9, 0x2b9, 0x2b9, 0x2b9, 0x2b9, 0x2ba, 0x2ba, 0x2ba, + 0x2ba, 0x2bb, 0x2bb, 0x2bb, 0x2bb, 0x2bb, 0x2bc, 0x2bc, 0x2bc, 0x2bc, 0x2bd, 0x2bd, 0x2bd, 0x2bd, 0x2be, 0x2be, + 0x2be, 0x2be, 0x2be, 0x2bf, 0x2bf, 0x2bf, 0x2bf, 0x2c0, 0x2c0, 0x2c0, 0x2c0, 0x2c0, 0x2c1, 0x2c1, 0x2c1, 0x2c1, + 0x2c2, 0x2c2, 0x2c2, 0x2c2, 0x2c2, 0x2c3, 0x2c3, 0x2c3, 0x2c3, 0x2c4, 0x2c4, 0x2c4, 0x2c4, 0x2c4, 0x2c5, 0x2c5, + 0x2c5, 0x2c5, 0x2c6, 0x2c6, 0x2c6, 0x2c6, 0x2c6, 0x2c7, 0x2c7, 0x2c7, 0x2c7, 0x2c8, 0x2c8, 0x2c8, 0x2c8, 0x2c8, + 0x2c9, 0x2c9, 0x2c9, 0x2c9, 0x2ca, 0x2ca, 0x2ca, 0x2ca, 0x2ca, 0x2cb, 0x2cb, 0x2cb, 0x2cb, 0x2cc, 0x2cc, 0x2cc, + 0x2cc, 0x2cc, 0x2cd, 0x2cd, 0x2cd, 0x2cd, 0x2ce, 0x2ce, 0x2ce, 0x2ce, 0x2ce, 0x2cf, 0x2cf, 0x2cf, 0x2cf, 0x2d0, + 0x2d0, 0x2d0, 0x2d0, 0x2d0, 0x2d1, 0x2d1, 0x2d1, 0x2d1, 0x2d2, 0x2d2, 0x2d2, 0x2d2, 0x2d2, 0x2d3, 0x2d3, 0x2d3, + 0x2d3, 0x2d4, 0x2d4, 0x2d4, 0x2d4, 0x2d4, 0x2d5, 0x2d5, 0x2d5, 0x2d5, 0x2d6, 0x2d6, 0x2d6, 0x2d6, 0x2d6, 0x2d7, + 0x2d7, 0x2d7, 0x2d7, 0x2d8, 0x2d8, 0x2d8, 0x2d8, 0x2d8, 0x2d9, 0x2d9, 0x2d9, 0x2d9, 0x2da, 0x2da, 0x2da, 0x2da, + 0x2da, 0x2db, 0x2db, 0x2db, 0x2db, 0x2db, 0x2dc, 0x2dc, 0x2dc, 0x2dc, 0x2dd, 0x2dd, 0x2dd, 0x2dd, 0x2dd, 0x2de, + 0x2de, 0x2de, 0x2de, 0x2df, 0x2df, 0x2df, 0x2df, 0x2df, 0x2e0, 0x2e0, 0x2e0, 0x2e0, 0x2e1, 0x2e1, 0x2e1, 0x2e1, + 0x2e1, 0x2e2, 0x2e2, 0x2e2, 0x2e2, 0x2e3, 0x2e3, 0x2e3, 0x2e3, 0x2e3, 0x2e4, 0x2e4, 0x2e4, 0x2e4, 0x2e4, 0x2e5, + 0x2e5, 0x2e5, 0x2e5, 0x2e6, 0x2e6, 0x2e6, 0x2e6, 0x2e6, 0x2e7, 0x2e7, 0x2e7, 0x2e7, 0x2e8, 0x2e8, 0x2e8, 0x2e8, + 0x2e8, 0x2e9, 0x2e9, 0x2e9, 0x2e9, 0x2ea, 0x2ea, 0x2ea, 0x2ea, 0x2ea, 0x2eb, 0x2eb, 0x2eb, 0x2eb, 0x2eb, 0x2ec, + 0x2ec, 0x2ec, 0x2ec, 0x2ed, 0x2ed, 0x2ed, 0x2ed, 0x2ed, 0x2ee, 0x2ee, 0x2ee, 0x2ee, 0x2ef, 0x2ef, 0x2ef, 0x2ef, + 0x2ef, 0x2f0, 0x2f0, 0x2f0, 0x2f0, 0x2f0, 0x2f1, 0x2f1, 0x2f1, 0x2f1, 0x2f2, 0x2f2, 0x2f2, 0x2f2, 0x2f2, 0x2f3, + 0x2f3, 0x2f3, 0x2f3, 0x2f3, 0x2f4, 0x2f4, 0x2f4, 0x2f4, 0x2f5, 0x2f5, 0x2f5, 0x2f5, 0x2f5, 0x2f6, 0x2f6, 0x2f6, + 0x2f6, 0x2f7, 0x2f7, 0x2f7, 0x2f7, 0x2f7, 0x2f8, 0x2f8, 0x2f8, 0x2f8, 0x2f8, 0x2f9, 0x2f9, 0x2f9, 0x2f9, 0x2fa, + 0x2fa, 0x2fa, 0x2fa, 0x2fa, 0x2fb, 0x2fb, 0x2fb, 0x2fb, 0x2fb, 0x2fc, 0x2fc, 0x2fc, 0x2fc, 0x2fd, 0x2fd, 0x2fd, + 0x2fd, 0x2fd, 0x2fe, 0x2fe, 0x2fe, 0x2fe, 0x2fe, 0x2ff, 0x2ff, 0x2ff, 0x2ff, 0x300, 0x300, 0x300, 0x300, 0x300, + 0x301, 0x301, 0x301, 0x301, 0x301, 0x302, 0x302, 0x302, 0x302, 0x303, 0x303, 0x303, 0x303, 0x303, 0x304, 0x304, + 0x304, 0x304, 0x304, 0x305, 0x305, 0x305, 0x305, 0x306, 0x306, 0x306, 0x306, 0x306, 0x307, 0x307, 0x307, 0x307, + 0x307, 0x308, 0x308, 0x308, 0x308, 0x309, 0x309, 0x309, 0x309, 0x309, 0x30a, 0x30a, 0x30a, 0x30a, 0x30a, 0x30b, + 0x30b, 0x30b, 0x30b, 0x30c, 0x30c, 0x30c, 0x30c, 0x30c, 0x30d, 0x30d, 0x30d, 0x30d, 0x30d, 0x30e, 0x30e, 0x30e, + 0x30e, 0x30e, 0x30f, 0x30f, 0x30f, 0x30f, 0x310, 0x310, 0x310, 0x310, 0x310, 0x311, 0x311, 0x311, 0x311, 0x311, + 0x312, 0x312, 0x312, 0x312, 0x313, 0x313, 0x313, 0x313, 0x313, 0x314, 0x314, 0x314, 0x314, 0x314, 0x315, 0x315, + 0x315, 0x315, 0x315, 0x316, 0x316, 0x316, 0x316, 0x317, 0x317, 0x317, 0x317, 0x317, 0x318, 0x318, 0x318, 0x318, + 0x318, 0x319, 0x319, 0x319, 0x319, 0x319, 0x31a, 0x31a, 0x31a, 0x31a, 0x31b, 0x31b, 0x31b, 0x31b, 0x31b, 0x31c, + 0x31c, 0x31c, 0x31c, 0x31c, 0x31d, 0x31d, 0x31d, 0x31d, 0x31e, 0x31e, 0x31e, 0x31e, 0x31e, 0x31f, 0x31f, 0x31f, + 0x31f, 0x31f, 0x320, 0x320, 0x320, 0x320, 0x320, 0x321, 0x321, 0x321, 0x321, 0x321, 0x322, 0x322, 0x322, 0x322, + 0x323, 0x323, 0x323, 0x323, 0x323, 0x324, 0x324, 0x324, 0x324, 0x324, 0x325, 0x325, 0x325, 0x325, 0x325, 0x326, + 0x326, 0x326, 0x326, 0x327, 0x327, 0x327, 0x327, 0x327, 0x328, 0x328, 0x328, 0x328, 0x328, 0x329, 0x329, 0x329, + 0x329, 0x329, 0x32a, 0x32a, 0x32a, 0x32a, 0x32a, 0x32b, 0x32b, 0x32b, 0x32b, 0x32c, 0x32c, 0x32c, 0x32c, 0x32c, + 0x32d, 0x32d, 0x32d, 0x32d, 0x32d, 0x32e, 0x32e, 0x32e, 0x32e, 0x32e, 0x32f, 0x32f, 0x32f, 0x32f, 0x32f, 0x330, + 0x330, 0x330, 0x330, 0x331, 0x331, 0x331, 0x331, 0x331, 0x332, 0x332, 0x332, 0x332, 0x332, 0x333, 0x333, 0x333, + 0x333, 0x333, 0x334, 0x334, 0x334, 0x334, 0x334, 0x335, 0x335, 0x335, 0x335, 0x335, 0x336, 0x336, 0x336, 0x336, + 0x337, 0x337, 0x337, 0x337, 0x337, 0x338, 0x338, 0x338, 0x338, 0x338, 0x339, 0x339, 0x339, 0x339, 0x339, 0x33a, + 0x33a, 0x33a, 0x33a, 0x33a, 0x33b, 0x33b, 0x33b, 0x33b, 0x33b, 0x33c, 0x33c, 0x33c, 0x33c, 0x33d, 0x33d, 0x33d, + 0x33d, 0x33d, 0x33e, 0x33e, 0x33e, 0x33e, 0x33e, 0x33f, 0x33f, 0x33f, 0x33f, 0x33f, 0x340, 0x340, 0x340, 0x340, + 0x340, 0x341, 0x341, 0x341, 0x341, 0x341, 0x342, 0x342, 0x342, 0x342, 0x342, 0x343, 0x343, 0x343, 0x343, 0x344, + 0x344, 0x344, 0x344, 0x344, 0x345, 0x345, 0x345, 0x345, 0x345, 0x346, 0x346, 0x346, 0x346, 0x346, 0x347, 0x347, + 0x347, 0x347, 0x347, 0x348, 0x348, 0x348, 0x348, 0x348, 0x349, 0x349, 0x349, 0x349, 0x349, 0x34a, 0x34a, 0x34a, + 0x34a, 0x34a, 0x34b, 0x34b, 0x34b, 0x34b, 0x34b, 0x34c, 0x34c, 0x34c, 0x34c, 0x34c, 0x34d, 0x34d, 0x34d, 0x34d, + 0x34d, 0x34e, 0x34e, 0x34e, 0x34e, 0x34f, 0x34f, 0x34f, 0x34f, 0x34f, 0x350, 0x350, 0x350, 0x350, 0x350, 0x351, + 0x351, 0x351, 0x351, 0x351, 0x352, 0x352, 0x352, 0x352, 0x352, 0x353, 0x353, 0x353, 0x353, 0x353, 0x354, 0x354, + 0x354, 0x354, 0x354, 0x355, 0x355, 0x355, 0x355, 0x355, 0x356, 0x356, 0x356, 0x356, 0x356, 0x357, 0x357, 0x357, + 0x357, 0x357, 0x358, 0x358, 0x358, 0x358, 0x358, 0x359, 0x359, 0x359, 0x359, 0x359, 0x35a, 0x35a, 0x35a, 0x35a, + 0x35a, 0x35b, 0x35b, 0x35b, 0x35b, 0x35b, 0x35c, 0x35c, 0x35c, 0x35c, 0x35c, 0x35d, 0x35d, 0x35d, 0x35d, 0x35d, + 0x35e, 0x35e, 0x35e, 0x35e, 0x35e, 0x35f, 0x35f, 0x35f, 0x35f, 0x35f, 0x360, 0x360, 0x360, 0x360, 0x360, 0x361, + 0x361, 0x361, 0x361, 0x362, 0x362, 0x362, 0x362, 0x362, 0x363, 0x363, 0x363, 0x363, 0x363, 0x364, 0x364, 0x364, + 0x364, 0x364, 0x365, 0x365, 0x365, 0x365, 0x365, 0x366, 0x366, 0x366, 0x366, 0x366, 0x367, 0x367, 0x367, 0x367, + 0x367, 0x368, 0x368, 0x368, 0x368, 0x368, 0x369, 0x369, 0x369, 0x369, 0x369, 0x36a, 0x36a, 0x36a, 0x36a, 0x36a, + 0x36b, 0x36b, 0x36b, 0x36b, 0x36b, 0x36c, 0x36c, 0x36c, 0x36c, 0x36c, 0x36d, 0x36d, 0x36d, 0x36d, 0x36d, 0x36e, + 0x36e, 0x36e, 0x36e, 0x36e, 0x36f, 0x36f, 0x36f, 0x36f, 0x36f, 0x370, 0x370, 0x370, 0x370, 0x370, 0x371, 0x371, + 0x371, 0x371, 0x371, 0x371, 0x372, 0x372, 0x372, 0x372, 0x372, 0x373, 0x373, 0x373, 0x373, 0x373, 0x374, 0x374, + 0x374, 0x374, 0x374, 0x375, 0x375, 0x375, 0x375, 0x375, 0x376, 0x376, 0x376, 0x376, 0x376, 0x377, 0x377, 0x377, + 0x377, 0x377, 0x378, 0x378, 0x378, 0x378, 0x378, 0x379, 0x379, 0x379, 0x379, 0x379, 0x37a, 0x37a, 0x37a, 0x37a, + 0x37a, 0x37b, 0x37b, 0x37b, 0x37b, 0x37b, 0x37c, 0x37c, 0x37c, 0x37c, 0x37c, 0x37d, 0x37d, 0x37d, 0x37d, 0x37d, + 0x37e, 0x37e, 0x37e, 0x37e, 0x37e, 0x37f, 0x37f, 0x37f, 0x37f, 0x37f, 0x380, 0x380, 0x380, 0x380, 0x380, 0x381, + 0x381, 0x381, 0x381, 0x381, 0x381, 0x382, 0x382, 0x382, 0x382, 0x382, 0x383, 0x383, 0x383, 0x383, 0x383, 0x384, + 0x384, 0x384, 0x384, 0x384, 0x385, 0x385, 0x385, 0x385, 0x385, 0x386, 0x386, 0x386, 0x386, 0x386, 0x387, 0x387, + 0x387, 0x387, 0x387, 0x388, 0x388, 0x388, 0x388, 0x388, 0x389, 0x389, 0x389, 0x389, 0x389, 0x38a, 0x38a, 0x38a, + 0x38a, 0x38a, 0x38a, 0x38b, 0x38b, 0x38b, 0x38b, 0x38b, 0x38c, 0x38c, 0x38c, 0x38c, 0x38c, 0x38d, 0x38d, 0x38d, + 0x38d, 0x38d, 0x38e, 0x38e, 0x38e, 0x38e, 0x38e, 0x38f, 0x38f, 0x38f, 0x38f, 0x38f, 0x390, 0x390, 0x390, 0x390, + 0x390, 0x391, 0x391, 0x391, 0x391, 0x391, 0x392, 0x392, 0x392, 0x392, 0x392, 0x392, 0x393, 0x393, 0x393, 0x393, + 0x393, 0x394, 0x394, 0x394, 0x394, 0x394, 0x395, 0x395, 0x395, 0x395, 0x395, 0x396, 0x396, 0x396, 0x396, 0x396, + 0x397, 0x397, 0x397, 0x397, 0x397, 0x398, 0x398, 0x398, 0x398, 0x398, 0x399, 0x399, 0x399, 0x399, 0x399, 0x399, + 0x39a, 0x39a, 0x39a, 0x39a, 0x39a, 0x39b, 0x39b, 0x39b, 0x39b, 0x39b, 0x39c, 0x39c, 0x39c, 0x39c, 0x39c, 0x39d, + 0x39d, 0x39d, 0x39d, 0x39d, 0x39e, 0x39e, 0x39e, 0x39e, 0x39e, 0x39e, 0x39f, 0x39f, 0x39f, 0x39f, 0x39f, 0x3a0, + 0x3a0, 0x3a0, 0x3a0, 0x3a0, 0x3a1, 0x3a1, 0x3a1, 0x3a1, 0x3a1, 0x3a2, 0x3a2, 0x3a2, 0x3a2, 0x3a2, 0x3a3, 0x3a3, + 0x3a3, 0x3a3, 0x3a3, 0x3a3, 0x3a4, 0x3a4, 0x3a4, 0x3a4, 0x3a4, 0x3a5, 0x3a5, 0x3a5, 0x3a5, 0x3a5, 0x3a6, 0x3a6, + 0x3a6, 0x3a6, 0x3a6, 0x3a7, 0x3a7, 0x3a7, 0x3a7, 0x3a7, 0x3a8, 0x3a8, 0x3a8, 0x3a8, 0x3a8, 0x3a8, 0x3a9, 0x3a9, + 0x3a9, 0x3a9, 0x3a9, 0x3aa, 0x3aa, 0x3aa, 0x3aa, 0x3aa, 0x3ab, 0x3ab, 0x3ab, 0x3ab, 0x3ab, 0x3ac, 0x3ac, 0x3ac, + 0x3ac, 0x3ac, 0x3ac, 0x3ad, 0x3ad, 0x3ad, 0x3ad, 0x3ad, 0x3ae, 0x3ae, 0x3ae, 0x3ae, 0x3ae, 0x3af, 0x3af, 0x3af, + 0x3af, 0x3af, 0x3b0, 0x3b0, 0x3b0, 0x3b0, 0x3b0, 0x3b0, 0x3b1, 0x3b1, 0x3b1, 0x3b1, 0x3b1, 0x3b2, 0x3b2, 0x3b2, + 0x3b2, 0x3b2, 0x3b3, 0x3b3, 0x3b3, 0x3b3, 0x3b3, 0x3b4, 0x3b4, 0x3b4, 0x3b4, 0x3b4, 0x3b4, 0x3b5, 0x3b5, 0x3b5, + 0x3b5, 0x3b5, 0x3b6, 0x3b6, 0x3b6, 0x3b6, 0x3b6, 0x3b7, 0x3b7, 0x3b7, 0x3b7, 0x3b7, 0x3b8, 0x3b8, 0x3b8, 0x3b8, + 0x3b8, 0x3b8, 0x3b9, 0x3b9, 0x3b9, 0x3b9, 0x3b9, 0x3ba, 0x3ba, 0x3ba, 0x3ba, 0x3ba, 0x3bb, 0x3bb, 0x3bb, 0x3bb, + 0x3bb, 0x3bb, 0x3bc, 0x3bc, 0x3bc, 0x3bc, 0x3bc, 0x3bd, 0x3bd, 0x3bd, 0x3bd, 0x3bd, 0x3be, 0x3be, 0x3be, 0x3be, + 0x3be, 0x3bf, 0x3bf, 0x3bf, 0x3bf, 0x3bf, 0x3bf, 0x3c0, 0x3c0, 0x3c0, 0x3c0, 0x3c0, 0x3c1, 0x3c1, 0x3c1, 0x3c1, + 0x3c1, 0x3c2, 0x3c2, 0x3c2, 0x3c2, 0x3c2, 0x3c2, 0x3c3, 0x3c3, 0x3c3, 0x3c3, 0x3c3, 0x3c4, 0x3c4, 0x3c4, 0x3c4, + 0x3c4, 0x3c5, 0x3c5, 0x3c5, 0x3c5, 0x3c5, 0x3c5, 0x3c6, 0x3c6, 0x3c6, 0x3c6, 0x3c6, 0x3c7, 0x3c7, 0x3c7, 0x3c7, + 0x3c7, 0x3c8, 0x3c8, 0x3c8, 0x3c8, 0x3c8, 0x3c8, 0x3c9, 0x3c9, 0x3c9, 0x3c9, 0x3c9, 0x3ca, 0x3ca, 0x3ca, 0x3ca, + 0x3ca, 0x3cb, 0x3cb, 0x3cb, 0x3cb, 0x3cb, 0x3cb, 0x3cc, 0x3cc, 0x3cc, 0x3cc, 0x3cc, 0x3cd, 0x3cd, 0x3cd, 0x3cd, + 0x3cd, 0x3ce, 0x3ce, 0x3ce, 0x3ce, 0x3ce, 0x3ce, 0x3cf, 0x3cf, 0x3cf, 0x3cf, 0x3cf, 0x3d0, 0x3d0, 0x3d0, 0x3d0, + 0x3d0, 0x3d0, 0x3d1, 0x3d1, 0x3d1, 0x3d1, 0x3d1, 0x3d2, 0x3d2, 0x3d2, 0x3d2, 0x3d2, 0x3d3, 0x3d3, 0x3d3, 0x3d3, + 0x3d3, 0x3d3, 0x3d4, 0x3d4, 0x3d4, 0x3d4, 0x3d4, 0x3d5, 0x3d5, 0x3d5, 0x3d5, 0x3d5, 0x3d6, 0x3d6, 0x3d6, 0x3d6, + 0x3d6, 0x3d6, 0x3d7, 0x3d7, 0x3d7, 0x3d7, 0x3d7, 0x3d8, 0x3d8, 0x3d8, 0x3d8, 0x3d8, 0x3d8, 0x3d9, 0x3d9, 0x3d9, + 0x3d9, 0x3d9, 0x3da, 0x3da, 0x3da, 0x3da, 0x3da, 0x3db, 0x3db, 0x3db, 0x3db, 0x3db, 0x3db, 0x3dc, 0x3dc, 0x3dc, + 0x3dc, 0x3dc, 0x3dd, 0x3dd, 0x3dd, 0x3dd, 0x3dd, 0x3dd, 0x3de, 0x3de, 0x3de, 0x3de, 0x3de, 0x3df, 0x3df, 0x3df, + 0x3df, 0x3df, 0x3df, 0x3e0, 0x3e0, 0x3e0, 0x3e0, 0x3e0, 0x3e1, 0x3e1, 0x3e1, 0x3e1, 0x3e1, 0x3e2, 0x3e2, 0x3e2, + 0x3e2, 0x3e2, 0x3e2, 0x3e3, 0x3e3, 0x3e3, 0x3e3, 0x3e3, 0x3e4, 0x3e4, 0x3e4, 0x3e4, 0x3e4, 0x3e4, 0x3e5, 0x3e5, + 0x3e5, 0x3e5, 0x3e5, 0x3e6, 0x3e6, 0x3e6, 0x3e6, 0x3e6, 0x3e6, 0x3e7, 0x3e7, 0x3e7, 0x3e7, 0x3e7, 0x3e8, 0x3e8, + 0x3e8, 0x3e8, 0x3e8, 0x3e9, 0x3e9, 0x3e9, 0x3e9, 0x3e9, 0x3e9, 0x3ea, 0x3ea, 0x3ea, 0x3ea, 0x3ea, 0x3eb, 0x3eb, + 0x3eb, 0x3eb, 0x3eb, 0x3eb, 0x3ec, 0x3ec, 0x3ec, 0x3ec, 0x3ec, 0x3ed, 0x3ed, 0x3ed, 0x3ed, 0x3ed, 0x3ed, 0x3ee, + 0x3ee, 0x3ee, 0x3ee, 0x3ee, 0x3ef, 0x3ef, 0x3ef, 0x3ef, 0x3ef, 0x3ef, 0x3f0, 0x3f0, 0x3f0, 0x3f0, 0x3f0, 0x3f1, + 0x3f1, 0x3f1, 0x3f1, 0x3f1, 0x3f1, 0x3f2, 0x3f2, 0x3f2, 0x3f2, 0x3f2, 0x3f3, 0x3f3, 0x3f3, 0x3f3, 0x3f3, 0x3f3, + 0x3f4, 0x3f4, 0x3f4, 0x3f4, 0x3f4, 0x3f5, 0x3f5, 0x3f5, 0x3f5, 0x3f5, 0x3f5, 0x3f6, 0x3f6, 0x3f6, 0x3f6, 0x3f6, + 0x3f7, 0x3f7, 0x3f7, 0x3f7, 0x3f7, 0x3f7, 0x3f8, 0x3f8, 0x3f8, 0x3f8, 0x3f8, 0x3f9, 0x3f9, 0x3f9, 0x3f9, 0x3f9, + 0x3f9, 0x3fa, 0x3fa, 0x3fa, 0x3fa, 0x3fa, 0x3fb, 0x3fb, 0x3fb, 0x3fb, 0x3fb, 0x3fb, 0x3fc, 0x3fc, 0x3fc, 0x3fc, + 0x3fc, 0x3fd, 0x3fd, 0x3fd, 0x3fd, 0x3fd, 0x3fd, 0x3fe, 0x3fe, 0x3fe, 0x3fe, 0x3fe, 0x3fe, 0x3ff, 0x3ff, 0x3ff, +}; + +static uint8_t chartbl[512] = { + 9, 8, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; diff --git a/winzipjpeg/ArithmeticDecoder.h b/winzipjpeg/ArithmeticDecoder.h new file mode 100644 index 0000000..d4d25b2 --- /dev/null +++ b/winzipjpeg/ArithmeticDecoder.h @@ -0,0 +1,71 @@ +/* + * ArithmeticDecoder.h + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef __WINZIP_JPEG_ARITHMETIC_DECODER_H__ +#define __WINZIP_JPEG_ARITHMETIC_DECODER_H__ + +#include "InputStream.h" + +#include +#include + +typedef struct WinZipJPEGArithmeticDecoder +{ + WinZipJPEGReadFunction *readfunc; + void *inputcontext; + + bool eof; + + uint8_t currbyte, lastbyte; + + uint8_t kmin2; // LPS count for reduction of Q by 4 + uint8_t kmin1; // LPS count for reduction of Q by 2 + uint8_t kmin; // largest LSP[sic] count for smaller Q + // uint8_t kavg; // expected average LPS count + uint8_t kmax; // smallest LPS count for larger Q + uint32_t x; // finite pricesion window on code stream + int32_t lp; // minus log p --- used only for testing + int32_t lr; // minus log of the range + int32_t lrm; // maximum lr before change index + int32_t lx; // decoder - log x + uint32_t dx; // antilog of lr -- used only for testing +} WinZipJPEGArithmeticDecoder; + +typedef struct WinZipJPEGContext +{ + int i; + int32_t dlrm; // difference between lrm and lr + uint8_t mps; // most probable symbol value - 0 or 1 + uint8_t k; // least probable symbol count +} WinZipJPEGContext; + +void InitializeWinZipJPEGArithmeticDecoder(WinZipJPEGArithmeticDecoder *self, WinZipJPEGReadFunction *readfunc, + void *inputcontext); +void InitializeWinZipJPEGContext(WinZipJPEGContext *self); +void InitializeWinZipJPEGContexts(WinZipJPEGContext *first, size_t bytes); +void InitializeFixedWinZipJPEGContext(WinZipJPEGContext *self); + +int NextBitFromWinZipJPEGArithmeticDecoder(WinZipJPEGArithmeticDecoder *self, WinZipJPEGContext *context); + +void FlushWinZipJPEGArithmeticDecoder(WinZipJPEGArithmeticDecoder *self); + +static inline bool WinZipJPEGArithmeticDecoderEncounteredEOF(WinZipJPEGArithmeticDecoder *self) { return self->eof; } + +#endif diff --git a/winzipjpeg/Decompressor.c b/winzipjpeg/Decompressor.c new file mode 100644 index 0000000..e22b924 --- /dev/null +++ b/winzipjpeg/Decompressor.c @@ -0,0 +1,1079 @@ +/* + * Decompressor.c + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#include "Decompressor.h" +#include "LZMA.h" + +#define analyser_assert(x) ((void)0) + +#include +#include + + + +// +// Constructor and destructor. +// + +WinZipJPEGDecompressor *AllocWinZipJPEGDecompressor(WinZipJPEGReadFunction *readfunc,void *inputcontext) +{ + WinZipJPEGDecompressor *self=calloc(sizeof(WinZipJPEGDecompressor),1); + if(!self) return NULL; + + self->readfunc=readfunc; + self->inputcontext=inputcontext; + + self->metadatalength=0; + self->metadatabytes=NULL; + + self->isfirstbundle=true; + self->reachedend=false; + + self->slicesavailable=false; + + memset(self->blocks,0,sizeof(self->blocks)); + + self->mcusavailable=false; + + InitializeFixedWinZipJPEGContext(&self->fixedcontext); + + return self; +} + +void FreeWinZipJPEGDecompressor(WinZipJPEGDecompressor *self) +{ + if(!self) return; + + free(self->metadatabytes); + for(int i=0;i<4;i++) free(self->blocks[i]); + free(self); +} + + + + +// +// Header and bundle reading. +// + +// Little endian integer parsing functions. +static inline uint16_t LittleEndianUInt16(uint8_t *ptr) { return ptr[0]|(ptr[1]<<8); } +static inline uint32_t LittleEndianUInt32(uint8_t *ptr) { return ptr[0]|(ptr[1]<<8)|(ptr[2]<<16)|(ptr[3]<<24); } + +// Allocator functions for LZMA. +static void *Alloc(ISzAllocPtr p,size_t size) { (void)p; return malloc(size); } +static void Free(ISzAllocPtr p,void *address) { (void)p; return free(address); } +static ISzAlloc lzmaallocator={Alloc,Free}; + +// Helper functions for reading from the input stream. +static int FullRead(WinZipJPEGDecompressor *self,uint8_t *buffer,size_t length); +static int SkipBytes(WinZipJPEGDecompressor *self,size_t length); + +int ReadWinZipJPEGHeader(WinZipJPEGDecompressor *self) +{ + // Read 4-byte header. + uint8_t header[4]; + int error=FullRead(self,header,sizeof(header)); + if(error) return error; + + // Sanity check the header, and make sure it contains only versions we can handle. + if(header[0]<4) return WinZipJPEGInvalidHeaderError; + if(header[1]!=0x10) return WinZipJPEGInvalidHeaderError; + if(header[2]!=0x01) return WinZipJPEGInvalidHeaderError; + if(header[3]&0xe0) return WinZipJPEGInvalidHeaderError; + + // The header can possibly be bigger than 4 bytes, so skip the rest. + // (Unlikely to happen). + if(header[0]>4) + { + int error=SkipBytes(self,header[0]-4); + if(error) return error; + } + + // Parse slice value. + self->slicevalue=header[3]&0x1f; + + return WinZipJPEGNoError; +} + +int ReadNextWinZipJPEGBundle(WinZipJPEGDecompressor *self) +{ + // Free and clear any old metadata. + free(self->metadatabytes); + self->metadatalength=0; + self->metadatabytes=NULL; + + // Free and clear old slices. + for(int i=0;i<4;i++) free(self->blocks[i]); + memset(self->blocks,0,sizeof(self->blocks)); + + // Read bundle header. + uint8_t header[4]; + int error=FullRead(self,header,sizeof(header)); + if(error) return error; + + // Parse metadata sizes from header. + uint32_t uncompressedsize=LittleEndianUInt16(&header[0]); + uint32_t compressedsize=LittleEndianUInt16(&header[2]); + + // If the sizes do not fit in 16 bits, both are set to 0xffff and + // an 8-byte 32-bit header is appended. + if(uncompressedsize==0xffff && compressedsize==0xffff) + { + uint8_t header[8]; + int error=FullRead(self,header,sizeof(header)); + if(error) return error; + + uncompressedsize=LittleEndianUInt32(&header[0]); + compressedsize=LittleEndianUInt32(&header[4]); + } + + // Allocate space for the uncompressed metadata. + self->metadatabytes=malloc(uncompressedsize); + if(!self->metadatabytes) return WinZipJPEGOutOfMemoryError; + self->metadatalength=uncompressedsize; + + // NOTE: The spec does not mention this, but a compressed + // size of 0 means uncompressed data is stored. + if(compressedsize) + { + // Allocate temporary space for the compressed metadata, and read it. + uint8_t *compressedbytes=malloc(compressedsize); + if(!compressedbytes) return WinZipJPEGOutOfMemoryError; + + error=FullRead(self,compressedbytes,compressedsize); + if(error) { free(compressedbytes); return error; } + + // Calculate the dictionary size used for the LZMA coding. + int dictionarysize=(uncompressedsize+511)&~511; + if(dictionarysize<1024) dictionarysize=1024; // Silly - LZMA enforces a lower limit of 4096. + if(dictionarysize>512*1024) dictionarysize=512*1024; + + // Create properties chunk for LZMA, using the dictionary size and default settings (lc=3, lp=0, pb=2). + uint8_t properties[5]={3+0*9+2*5*9,dictionarysize,dictionarysize>>8,dictionarysize>>16,dictionarysize>>24}; + + // Run LZMA decompressor. + SizeT destlen=uncompressedsize,srclen=compressedsize; + ELzmaStatus status; + SRes res=LzmaDecode(self->metadatabytes,&destlen,compressedbytes,&srclen, + properties,sizeof(properties),LZMA_FINISH_END,&status,&lzmaallocator); + + // Free temporary buffer. + free(compressedbytes); + + // Check if LZMA decoding succeeded. + if(res!=SZ_OK) return WinZipJPEGLZMAError; + } + else + { + // Read uncompressed metadata. + error=FullRead(self,self->metadatabytes,uncompressedsize); + if(error) return error; + } + + // Parse the JPEG structure. If this is the first bundle, + // we have to first find the start marker. + const uint8_t *metadatastart; + if(self->isfirstbundle) + { + metadatastart=FindStartOfWinZipJPEGImage(self->metadatabytes,self->metadatalength); + if(!metadatastart) return WinZipJPEGParseError; + + self->isfirstbundle=false; + } + else + { + metadatastart=self->metadatabytes; + } + + int parseres=ParseWinZipJPEGMetadata(&self->jpeg,metadatastart, + self->metadatabytes+self->metadatalength-metadatastart); + if(parseres==WinZipJPEGMetadataParsingFailed) return WinZipJPEGParseError; + + // If we encountered an End Of Image marker, there will be + // no further scans or bundles, so set a flag and return. + if(parseres==WinZipJPEGMetadataFoundEndOfImage) + { + self->reachedend=true; + return WinZipJPEGNoError; + } + + // Initialize arithmetic decoder contexts. + InitializeWinZipJPEGContexts(&self->eobbins[0][0][0],sizeof(self->eobbins)); + InitializeWinZipJPEGContexts(&self->zerobins[0][0][0][0],sizeof(self->zerobins)); + InitializeWinZipJPEGContexts(&self->pivotbins[0][0][0][0],sizeof(self->pivotbins)); + InitializeWinZipJPEGContexts(&self->acmagnitudebins[0][0][0][0][0],sizeof(self->acmagnitudebins)); + InitializeWinZipJPEGContexts(&self->acremainderbins[0][0][0][0],sizeof(self->acremainderbins)); + InitializeWinZipJPEGContexts(&self->acsignbins[0][0][0][0],sizeof(self->acsignbins)); + InitializeWinZipJPEGContexts(&self->dcmagnitudebins[0][0][0],sizeof(self->dcmagnitudebins)); + InitializeWinZipJPEGContexts(&self->dcremainderbins[0][0][0],sizeof(self->dcremainderbins)); + InitializeWinZipJPEGContexts(&self->dcsignbins[0][0][0][0],sizeof(self->dcsignbins)); + + // Calculate slize size, if any. + if(self->slicevalue) + { + int64_t pow2size=1LL<<(self->slicevalue+6); + int64_t div1=pow2size/self->jpeg.horizontalmcus; + if(div1<1) div1=1; + int64_t div2=(self->jpeg.verticalmcus+div1-1)/div1; + self->sliceheight=(unsigned int)((self->jpeg.verticalmcus+div2-1)/div2); + } + else + { + self->sliceheight=self->jpeg.verticalmcus; + } + + // Allocate memory for each component in a slice. + for(int i=0;ijpeg.numscancomponents;i++) + { + self->blocks[i]=malloc(self->jpeg.horizontalmcus*self->sliceheight* + self->jpeg.scancomponents[i].component->horizontalfactor* + self->jpeg.scancomponents[i].component->verticalfactor* + sizeof(WinZipJPEGBlock)); + + if(!self->blocks[i]) return WinZipJPEGOutOfMemoryError; + } + + self->slicesavailable=true; + self->finishedrows=0; + + self->mcucounter=0; + self->restartmarkerindex=0; + self->writerestartmarker=false; + + memset(self->predicted,0,sizeof(self->predicted)); + + self->bitstring=0; + self->bitlength=0; + self->needsstuffing=false; + + return WinZipJPEGNoError; +} + +// Helper function that makes sure to read as much data as requested, even +// if the read function returns short buffers, and reports an error if it +// reaches EOF prematurely. +static int FullRead(WinZipJPEGDecompressor *self,uint8_t *buffer,size_t length) +{ + size_t totalread=0; + while(totalreadreadfunc(self->inputcontext,&buffer[totalread],length-totalread); + if(actual==0) return WinZipJPEGEndOfStreamError; + totalread+=actual; + } + + return WinZipJPEGNoError; +} + +// Helper function to skip data by reading and discarding. +static int SkipBytes(WinZipJPEGDecompressor *self,size_t length) +{ + uint8_t buffer[1024]; + + size_t totalread=0; + while(totalreadsizeof(buffer)) numbytes=sizeof(buffer); + size_t actual=self->readfunc(self->inputcontext,buffer,numbytes); + if(actual==0) return WinZipJPEGEndOfStreamError; + totalread+=actual; + } + + return WinZipJPEGNoError; +} + + + + +// +// Block decoding. +// + +// Decoder functions. +static void DecodeBlock(WinZipJPEGDecompressor *self,int comp, +WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west, +const WinZipJPEGQuantizationTable *quantization); + +static int DecodeACComponent(WinZipJPEGDecompressor *self,int comp,unsigned int k,bool canbezero, +const WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west, +const WinZipJPEGQuantizationTable *quantization); + +static int DecodeACSign(WinZipJPEGDecompressor *self,int comp,unsigned int k,int absvalue, +const WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west, +const WinZipJPEGQuantizationTable *quantization); + +static int DecodeDCComponent(WinZipJPEGDecompressor *self,int comp, +const WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west, +const WinZipJPEGQuantizationTable *quantization); + +static unsigned int DecodeBinarization(WinZipJPEGArithmeticDecoder *decoder, +WinZipJPEGContext *magnitudebins,WinZipJPEGContext *remainderbins,int maxbits,int cap); + +// Coefficient zig-zag ordering functions. +static bool IsFirstRow(unsigned int k); +static bool IsFirstColumn(unsigned int k); +static bool IsFirstRowOrColumn(unsigned int k); +static bool IsSecondRow(unsigned int k); +static bool IsSecondColumn(unsigned int k); + +static unsigned int Left(unsigned int k); +static unsigned int Up(unsigned int k); +static unsigned int UpAndLeft(unsigned int k); +static unsigned int Right(unsigned int k); +static unsigned int Down(unsigned int k); + +static unsigned int ZigZag(unsigned int row,unsigned int column); +static unsigned int Row(unsigned int k); +static unsigned int Column(unsigned int k); + +// Compression primitive functions. +static int Min(int a,int b); +static int Abs(int x); +static int Sign(int x); +static unsigned int Category(unsigned int val); + +static int Sum(unsigned int k,const WinZipJPEGBlock *block); +static int Average(unsigned int k, +const WinZipJPEGBlock *north,const WinZipJPEGBlock *west, +const WinZipJPEGQuantizationTable *quantization); +static int BDR(unsigned int k,const WinZipJPEGBlock *current, +const WinZipJPEGBlock *north,const WinZipJPEGBlock *west, +const WinZipJPEGQuantizationTable *quantization); + +static const WinZipJPEGBlock ZeroBlock={0}; + +int ReadNextWinZipJPEGSlice(WinZipJPEGDecompressor *self) +{ + self->currheight=self->sliceheight; + if(self->finishedrows+self->currheight>=self->jpeg.verticalmcus) + { + self->currheight=self->jpeg.verticalmcus-self->finishedrows; + self->slicesavailable=false; + } + + for(int i=0;ijpeg.numscancomponents;i++) + { + InitializeWinZipJPEGArithmeticDecoder(&self->decoder,self->readfunc,self->inputcontext); + + int hblocks=self->jpeg.scancomponents[i].component->horizontalfactor; + int vblocks=self->jpeg.scancomponents[i].component->verticalfactor; + int blocksperrow=self->jpeg.horizontalmcus*hblocks; + + const WinZipJPEGQuantizationTable *quantization=self->jpeg.scancomponents[i].component->quantizationtable; + + // NOTE: Blocks are processed in cartesian order, not MCU order. + for(int y=0;ycurrheight*vblocks;y++) + for(int x=0;xjpeg.horizontalmcus*hblocks;x++) + { + WinZipJPEGBlock *currblock=&self->blocks[i][x+y*blocksperrow]; + + const WinZipJPEGBlock *northblock; + if(y!=0) northblock=&self->blocks[i][x+(y-1)*blocksperrow]; + else if(self->finishedrows!=0) northblock=&self->blocks[i][x+(self->sliceheight*vblocks-1)*blocksperrow]; + else northblock=NULL; + + const WinZipJPEGBlock *westblock; + if(x!=0) westblock=&self->blocks[i][x-1+y*blocksperrow]; + else westblock=NULL; + + DecodeBlock(self,i,currblock,northblock,westblock,quantization); + + if(WinZipJPEGArithmeticDecoderEncounteredEOF(&self->decoder)) return WinZipJPEGEndOfStreamError; + } + + FlushWinZipJPEGArithmeticDecoder(&self->decoder); + } + + self->finishedrows+=self->currheight; + + // Initialize writer state. + self->mcusavailable=true; + self->currblock=self->blocks[0]; + + self->mcurow=0; + self->mcucol=0; + self->mcucomp=0; + self->mcux=0; + self->mcuy=0; + self->mcucoeff=0; + + // TODO: Error handling. + return WinZipJPEGNoError; +} + +static void DecodeBlock(WinZipJPEGDecompressor *self,int comp, +WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west, +const WinZipJPEGQuantizationTable *quantization) +{ + // Decode End Of Block value to find out how many AC components there are. (5.6.5) + + // Calculate EOB context. (5.6.5.2) + int average; + if(!north&&!west) average=0; + else if(!north) average=Sum(0,west); + else if(!west) average=Sum(0,north); + else average=(Sum(0,north)+Sum(0,west)+1)/2; + + int eobcontext=Min(Category(average),12); + + // Decode EOB bits using binary tree. (5.6.5.1) + unsigned int bitstring=1; + for(int i=0;i<6;i++) + { + bitstring=(bitstring<<1)|NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder, + &self->eobbins[comp][eobcontext][bitstring-1]); + } + unsigned int eob=bitstring&0x3f; + current->eob=eob; + + // Fill out the elided block entries with 0. + for(unsigned int k=eob+1;k<=63;k++) current->c[k]=0; + + // Decode AC components in decreasing order, if any. (5.6.6) + for(unsigned int k=eob;k>=1;k--) + { + current->c[k]=DecodeACComponent(self,comp,k,k!=eob,current,north,west,quantization); + } + + // Decode DC component. + current->c[0]=DecodeDCComponent(self,comp,current,north,west,quantization); +} + +static int DecodeACComponent(WinZipJPEGDecompressor *self,int comp,unsigned int k,bool canbezero, +const WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west, +const WinZipJPEGQuantizationTable *quantization) +{ + if(!north) north=&ZeroBlock; + if(!west) west=&ZeroBlock; + + int val1; + if(IsFirstRowOrColumn(k)) val1=Abs(BDR(k,current,north,west,quantization)); + else val1=Average(k,north,west,quantization); + + int val2=Sum(k,current); + + if(canbezero) + { + // Decode zero/non-zero bit. (5.6.6.1) + int zerocontext1=Min(Category(val1),2); + int zerocontext2=Min(Category(val2),5); + + int nonzero=NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder, + &self->zerobins[comp][k-1][zerocontext1][zerocontext2]); + + // If this component is zero, there is no need to decode further parameters. + if(!nonzero) return 0; + } + + // This component is not zero. Proceed with decoding absolute value. + int absvalue; + + // Decode pivot (abs>=2). (5.6.6.2) + int pivotcontext1=Min(Category(val1),4); + int pivotcontext2=Min(Category(val2),6); + + int pivot=NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder, + &self->pivotbins[comp][k-1][pivotcontext1][pivotcontext2]); + + if(!pivot) + { + // The absolute of this component is not >=2. It must therefore be 1, + // and there is no need to decode the value. + absvalue=1; + } + else + { + // The absolute of this component is >=2. Proceed with decoding + // the absolute value. (5.6.6.3) + int val3,n; + if(IsFirstRow(k)) { val3=Column(k)-1; n=0; } + else if(IsFirstColumn(k)) { val3=Row(k)-1; n=1; } + else { val3=Category(k-4); n=2; } + + int magnitudecontext1=Min(Category(val1),8); + int magnitudecontext2=Min(Category(val2),8); + int remaindercontext=val3; + + // Decode absolute value. + absvalue=DecodeBinarization(&self->decoder, + self->acmagnitudebins[comp][n][magnitudecontext1][magnitudecontext2], + self->acremainderbins[comp][n][remaindercontext], + 14,9)+2; + } + + if(DecodeACSign(self,comp,k,absvalue,current,north,west,quantization)) return -absvalue; + else return absvalue; +} + +static int DecodeACSign(WinZipJPEGDecompressor *self,int comp,unsigned int k,int absvalue, +const WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west, +const WinZipJPEGQuantizationTable *quantization) +{ + // Decode sign. (5.6.6.4) + + // Calculate sign context, or decode with fixed probability. (5.6.6.4.1) + int predictedsign; + if(IsFirstRowOrColumn(k)) + { + int bdr=BDR(k,current,north,west,quantization); + + if(bdr==0) return NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,&self->fixedcontext); + + predictedsign=(bdr<0); + } + else if(k==4) + { + int sign1=Sign(north->c[k]); + int sign2=Sign(west->c[k]); + + if(sign1+sign2==0) return NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,&self->fixedcontext); + + predictedsign=(sign1+sign2<0); + } + else if(IsSecondRow(k)) + { + if(north->c[k]==0) return NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,&self->fixedcontext); + + predictedsign=(north->c[k]<0); + } + else if(IsSecondColumn(k)) + { + if(west->c[k]==0) return NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,&self->fixedcontext); + + predictedsign=(west->c[k]<0); + } + else + { + return NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder,&self->fixedcontext); + } + + static const int n_for_k[64]={ + 0, + 0, 1, + 2, 3, 4, + 5, 6, 7, 8, + 9,10, 0,11,12, + 13,14, 0, 0,15,16, + 17,18, 0, 0, 0,19,20, + 21,22, 0, 0, 0, 0,23,24, + 25, 0, 0, 0, 0, 0,26, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, + 0, 0, + 0, + }; + int n=n_for_k[k]; + + int signcontext1=Min(Category(absvalue)/2,2); + + return NextBitFromWinZipJPEGArithmeticDecoder(&self->decoder, + &self->acsignbins[comp][n][signcontext1][predictedsign]); +} + +static int DecodeDCComponent(WinZipJPEGDecompressor *self,int comp, +const WinZipJPEGBlock *current,const WinZipJPEGBlock *north,const WinZipJPEGBlock *west, +const WinZipJPEGQuantizationTable *quantization) +{ + // Decode DC component. (5.6.7) + + // DC prediction. (5.6.7.1) + int predicted; + if(!north&&!west) + { + predicted=0; + } + else if(!north) + { + // NOTE: Spec says west[1]-current[1]. + int t1=west->c[0]*10000-11038*quantization->c[1]*(west->c[1]+current->c[1])/quantization->c[0]; + int p1=((t1<0)?(t1-5000):(t1+5000))/10000; + predicted=p1; + } + else if(!west) + { + // NOTE: Spec says north->c[2]-current->c[2]. + int t0=north->c[0]*10000-11038*quantization->c[2]*(north->c[2]+current->c[2])/quantization->c[0]; + int p0=((t0<0)?(t0-5000):(t0+5000))/10000; + predicted=p0; + } + else + { + // NOTE: Spec says north[2]-current[2] and west[1]-current[1]. + int t0=north->c[0]*10000-11038*quantization->c[2]*(north->c[2]+current->c[2])/quantization->c[0]; + int p0=((t0<0)?(t0-5000):(t0+5000))/10000; + + int t1=west->c[0]*10000-11038*quantization->c[1]*(west->c[1]+current->c[1])/quantization->c[0]; + int p1=((t1<0)?(t1-5000):(t1+5000))/10000; + + // Prediction refinement. (5.6.7.2) + int d0=0,d1=0; + for(int i=1;i<8;i++) + { + // Note: Spec says Abs(Abs(north->c[ZigZag(i,0)])- + // Abs(current->c[ZigZag(i,0)])) and similarly for west. + d0+=Abs(north->c[ZigZag(i,0)]-current->c[ZigZag(i,0)]); + d1+=Abs(west->c[ZigZag(0,i)]-current->c[ZigZag(0,i)]); + } + + if(d0>d1) + { + int64_t weight=1LL<decoder, + self->dcmagnitudebins[comp][valuecontext], + self->dcremainderbins[comp][valuecontext], + 15,10); + if(absvalue==0) return predicted; + + // Decode sign. (5.6.7.3.2) + // NOTE: Spec says north[0]<0 and west[0]<0. + if(!north) north=&ZeroBlock; + if(!west) west=&ZeroBlock; + int northsign=(north->c[0]c[0]decoder, + &self->dcsignbins[comp][northsign][westsign][predictedsign]); + + if(sign) return predicted-absvalue; + else return predicted+absvalue; +} + +static unsigned int DecodeBinarization(WinZipJPEGArithmeticDecoder *decoder, +WinZipJPEGContext *magnitudebins,WinZipJPEGContext *remainderbins,int maxbits,int cap) +{ + // Decode binarization. (5.6.4, and additional reverse engineering + // as the spec does not describe the process in sufficient detail.) + + // Decode unary magnitude. + int ones=0; + while(ones=cap) context=cap-1; + + int unary=NextBitFromWinZipJPEGArithmeticDecoder(decoder,&magnitudebins[context]); + if(unary==1) ones++; + else break; + } + + // Decode remainder bits, if any. + if(ones==0) return 0; + else if(ones==1) return 1; + else + { + int numbits=ones-1; + int val=1<=0;i--) + { + int bit=NextBitFromWinZipJPEGArithmeticDecoder(decoder,&remainderbins[i]); + val|=bit<=8||column>=8) return 0; // Can't happen. + static const int table[8][8]= + { + { 0, 1, 5, 6,14,15,27,28, }, + { 2, 4, 7,13,16,26,29,42, }, + { 3, 8,12,17,25,30,41,43, }, + { 9,11,18,24,31,40,44,53, }, + { 10,19,23,32,39,45,52,54, }, + { 20,22,33,38,46,51,55,60, }, + { 21,34,37,47,50,56,59,61, }, + { 35,36,48,49,57,58,62,63, }, + }; + return table[row][column]; +} + +static unsigned int Row(unsigned int k) +{ + if(k>=64) return 0; // Can't happen. + static const int table[64]= + { + 0,0,1,2,1,0,0,1,2,3,4,3,2,1,0,0, + 1,2,3,4,5,6,5,4,3,2,1,0,0,1,2,3, + 4,5,6,7,7,6,5,4,3,2,1,2,3,4,5,6, + 7,7,6,5,4,3,4,5,6,7,7,6,5,6,7,7, + }; + return table[k]; +} + +static unsigned int Column(unsigned int k) +{ + if(k>=64) return 0; // Can't happen. + static const int table[64]= + { + 0,1,0,0,1,2,3,2,1,0,0,1,2,3,4,5, + 4,3,2,1,0,0,1,2,3,4,5,6,7,6,5,4, + 3,2,1,0,1,2,3,4,5,6,7,7,6,5,4,3, + 2,3,4,5,6,7,7,6,5,4,5,6,7,7,6,7, + }; + return table[k]; +} + +static int Min(int a,int b) +{ + if(a=0) return x; + else return -x; +} + +static int Sign(int x) +{ + if(x>0) return 1; + else if(x<0) return -1; + else return 0; +} + +// CAT (5.6.3) +static unsigned int Category(unsigned int val) +{ + if(val==0) return 0; + + unsigned int cat=0; + if(val&0xffff0000) { val>>=16; cat|=16; } + if(val&0xff00) { val>>=8; cat|=8; } + if(val&0xf0) { val>>=4; cat|=4; } + if(val&0xc) { val>>=2; cat|=2; } + if(val&0x2) { /*val>>=1;*/ cat|=1; } + return cat+1; +} + +// SUM (5.6.2.1) +static int Sum(unsigned int k,const WinZipJPEGBlock *block) +{ + int sum=0; + for(unsigned int i=0;i<64;i++) + { + if(i!=k && Row(i)>=Row(k) && Column(i)>=Column(k)) + sum+=Abs(block->c[i]); + } + return sum; +} + +// AVG (5.6.2.2) +// NOTE: This assumes that the expression given for 'sum' is incorrect, and that +// Bw[k] should actually be Bw[x]. Also, the spec does not explicitly mention +// that the DC component never contributes. +static int Average(unsigned int k, +const WinZipJPEGBlock *north,const WinZipJPEGBlock *west, +const WinZipJPEGQuantizationTable *quantization) +{ + if(k==0||k==1||k==2) + { + return (Abs(north->c[k])+Abs(west->c[k])+1)/2; + } + else if(IsFirstRow(k)) + { + return ( + (Abs(north->c[Left(k)])+Abs(west->c[Left(k)]))*quantization->c[Left(k)]/quantization->c[k]+ + Abs(north->c[k])+Abs(west->c[k])+ + 2 + )/(2*2); + } + else if(IsFirstColumn(k)) + { + return ( + (Abs(north->c[Up(k)])+Abs(west->c[Up(k)]))*quantization->c[Up(k)]/quantization->c[k]+ + Abs(north->c[k])+Abs(west->c[k])+ + 2 + )/(2*2); + } + else if(k==4) + { + return ( + (Abs(north->c[Up(k)])+Abs(west->c[Up(k)]))*quantization->c[Up(k)]/quantization->c[k]+ + (Abs(north->c[Left(k)])+Abs(west->c[Left(k)]))*quantization->c[Left(k)]/quantization->c[k]+ + Abs(north->c[k])+Abs(west->c[k])+ + 3 + )/(2*3); + } + else + { + return ( + (Abs(north->c[Up(k)])+Abs(west->c[Up(k)]))*quantization->c[Up(k)]/quantization->c[k]+ + (Abs(north->c[Left(k)])+Abs(west->c[Left(k)]))*quantization->c[Left(k)]/quantization->c[k]+ + (Abs(north->c[UpAndLeft(k)])+Abs(west->c[UpAndLeft(k)]))*quantization->c[UpAndLeft(k)]/quantization->c[k]+ + Abs(north->c[k])+Abs(west->c[k])+ + 4 + )/(2*4); + } +} + +// BDR (5.6.2.3) +static int BDR(unsigned int k,const WinZipJPEGBlock *current, +const WinZipJPEGBlock *north,const WinZipJPEGBlock *west, +const WinZipJPEGQuantizationTable *quantization) +{ + if(IsFirstRow(k)) + { + return north->c[k]-(north->c[Down(k)]+current->c[Down(k)])*quantization->c[Down(k)]/quantization->c[k]; + } + else if(IsFirstColumn(k)) + { + return west->c[k]-(west->c[Right(k)]+current->c[Right(k)])*quantization->c[Right(k)]/quantization->c[k]; + } + else return 0; // Can't happen. +} + + + +// +// Block encoding. +// + +static void PushEncodedValue(WinZipJPEGDecompressor *self,WinZipJPEGHuffmanTable *table, +int value,unsigned int highbits); +static void PushHuffmanCode(WinZipJPEGDecompressor *self,WinZipJPEGHuffmanTable *table,unsigned int code); +static void PushBitString(WinZipJPEGDecompressor *self,uint32_t bitstring,unsigned int length); + +size_t EncodeWinZipJPEGBlocksToBuffer(WinZipJPEGDecompressor *self,void *bytes,size_t length) +{ + uint8_t *start=bytes; + uint8_t *ptr=bytes; + uint8_t *end=ptr+length; + + while(ptrneedsstuffing) + { + // If we need to add a byte of stuffing, do so. + *ptr++=0x00; + self->needsstuffing=false; + } + else if(self->bitlength>=8) + { + // If there are enough buffered bits, output one byte. + uint8_t byte=self->bitstring>>56LL; + *ptr++=byte; + self->bitstring<<=8; + self->bitlength-=8; + + if(byte==0xff) self->needsstuffing=true; + } + else if(self->writerestartmarker) + { + // Output the first half of a restart marker. This has to be done + // separately, to avoid stuffing. + *ptr++=0xff; + + // Next, push the rest of the marker into the bitstream as usual, as it + // will not trigger bit stuffing. + PushBitString(self,0xd0+self->restartmarkerindex,8); + + // Cycle the restart marker indexes, reset the MCU counter, and clear predictors. + self->restartmarkerindex=(self->restartmarkerindex+1)&7; + self->mcucounter=0; + memset(self->predicted,0,sizeof(self->predicted)); + + self->writerestartmarker=false; + } + else if(self->jpeg.restartinterval && self->mcucounter==self->jpeg.restartinterval && + (self->mcusavailable || self->slicesavailable)) + { + // If it is time for a restart marker, and if we have not reached the very + // end of the scan, start outputting one. + + // First, pad with ones to a byte border if needed. + if(self->bitlength) + { + int n=8-self->bitlength; + PushBitString(self,(1<writerestartmarker=true; + } + else if(self->mcusavailable) + { + // If there are still MCUs left to process output either a DC or AC coefficient as appropriate. + if(self->mcucoeff==0) + { + // Output DC coefficient. + int diff=self->currblock->c[0]-self->predicted[self->mcucomp]; + + PushEncodedValue(self,self->jpeg.scancomponents[self->mcucomp].dctable,diff,0); + + self->predicted[self->mcucomp]=self->currblock->c[0]; + self->mcucoeff=1; + } + else if(self->mcucoeff>self->currblock->eob && self->currblock->eob!=63) + { + // Output EOB marker. + PushHuffmanCode(self,self->jpeg.scancomponents[self->mcucomp].actable,0x00); + + self->mcucoeff=64; + } + else + { + // Output AC coefficient. + + // Find the next non-zero coefficient. + int firstcoeff=self->mcucoeff; + int endrun=self->mcucoeff+15; + while(self->mcucoeff<63 && self->mcucoeffcurrblock->c[self->mcucoeff]==0) self->mcucoeff++; + + int zeroes=self->mcucoeff-firstcoeff; + int val=self->currblock->c[self->mcucoeff]; + + PushEncodedValue(self,self->jpeg.scancomponents[self->mcucomp].actable,val,zeroes); + + self->mcucoeff++; + } + + // If we have output all coefficients, update position. + if(self->mcucoeff>=64) + { + int hblocks=self->jpeg.scancomponents[self->mcucomp].component->horizontalfactor; + int vblocks=self->jpeg.scancomponents[self->mcucomp].component->verticalfactor; + + self->mcucoeff=0; self->mcux++; + if(self->mcux>=hblocks) + { + self->mcux=0; self->mcuy++; + if(self->mcuy>=vblocks) + { + self->mcuy=0; self->mcucomp++; + if(self->mcucomp>=self->jpeg.numscancomponents) + { + self->mcucomp=0; self->mcucol++; + if(self->mcucol>=self->jpeg.horizontalmcus) + { + self->mcucol=0; self->mcurow++; + if(self->mcurow>=self->currheight) + { + self->mcusavailable=false; + + if(!self->slicesavailable) + { + // If we reached the very end, pad with ones + // to a byte boundary to finish the stream. + int n=(-self->bitlength)&7; + PushBitString(self,(1<mcucounter++; + } + } + } + + // Find the new block. + hblocks=self->jpeg.scancomponents[self->mcucomp].component->horizontalfactor; + vblocks=self->jpeg.scancomponents[self->mcucomp].component->verticalfactor; + int blocksperrow=self->jpeg.horizontalmcus*hblocks; + + int blockx=self->mcucol*hblocks+self->mcux; + int blocky=self->mcurow*vblocks+self->mcuy; + + self->currblock=&self->blocks[self->mcucomp][blockx+blocky*blocksperrow]; + } + } + else + { + // Nothing left to do. Return the partial length of output data. + return ptr-start; + } + } + return length; +} + +static void PushEncodedValue(WinZipJPEGDecompressor *self,WinZipJPEGHuffmanTable *table, +int value,unsigned int highbits) +{ + unsigned int category,bitstring; + if(value>=0) + { + category=Category(value); + analyser_assert(category>=1 && category<=32); + unsigned int mask=(1ull<=1 && category<=32); + unsigned int mask=(1ull<codes[code].code,table->codes[code].length); +} + +static void PushBitString(WinZipJPEGDecompressor *self,uint32_t bitstring,unsigned int length) +{ + self->bitstring|=(uint64_t)bitstring<<(64-self->bitlength-length); + self->bitlength+=length; +} diff --git a/winzipjpeg/Decompressor.h b/winzipjpeg/Decompressor.h new file mode 100644 index 0000000..558d113 --- /dev/null +++ b/winzipjpeg/Decompressor.h @@ -0,0 +1,102 @@ +/* + * Decompressor.h + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef __WINZIP_JPEG_DECOMPRESSOR_H__ +#define __WINZIP_JPEG_DECOMPRESSOR_H__ + +#include "ArithmeticDecoder.h" +#include "InputStream.h" +#include "JPEG.h" + +#include +#include + +#define WinZipJPEGNoError 0 +#define WinZipJPEGEndOfStreamError 1 +#define WinZipJPEGOutOfMemoryError 2 +#define WinZipJPEGInvalidHeaderError 3 +#define WinZipJPEGLZMAError 4 +#define WinZipJPEGParseError 5 + +typedef struct WinZipJPEGDecompressor +{ + WinZipJPEGReadFunction *readfunc; + void *inputcontext; + + uint32_t metadatalength; + uint8_t *metadatabytes; + + bool isfirstbundle, reachedend; + WinZipJPEGMetadata jpeg; + + bool slicesavailable; + unsigned int slicevalue, sliceheight; + unsigned int currheight, finishedrows; + + int predicted[4]; + + uint64_t bitstring; + unsigned int bitlength; + bool needsstuffing; + + WinZipJPEGArithmeticDecoder decoder; + + WinZipJPEGContext eobbins[4][13][63]; // 321 in WinZip. + WinZipJPEGContext zerobins[4][62][3][6]; // 1140 in WinZip. + WinZipJPEGContext pivotbins[4][63][5][7]; // 2256 in WinZip. + WinZipJPEGContext acmagnitudebins[4][3][9][9][9]; + WinZipJPEGContext acremainderbins[4][3][7][13]; + WinZipJPEGContext acsignbins[4][27][3][2]; + WinZipJPEGContext dcmagnitudebins[4][13][10]; // 1 in WinZip. + WinZipJPEGContext dcremainderbins[4][13][14]; // 131 in WinZip. + WinZipJPEGContext dcsignbins[4][2][2][2]; // 313 in WinZip. + WinZipJPEGContext fixedcontext; // 0 in WinZip. + + WinZipJPEGBlock *blocks[4]; + + WinZipJPEGBlock *currblock; + bool mcusavailable; + unsigned int mcurow, mcucol, mcucomp, mcux, mcuy, mcucoeff; + unsigned int mcucounter, restartmarkerindex; + bool writerestartmarker; +} WinZipJPEGDecompressor; + +WinZipJPEGDecompressor *AllocWinZipJPEGDecompressor(WinZipJPEGReadFunction *readfunc, void *inputcontext); +void FreeWinZipJPEGDecompressor(WinZipJPEGDecompressor *self); + +int ReadWinZipJPEGHeader(WinZipJPEGDecompressor *self); +int ReadNextWinZipJPEGBundle(WinZipJPEGDecompressor *self); +int ReadNextWinZipJPEGSlice(WinZipJPEGDecompressor *self); + +size_t EncodeWinZipJPEGBlocksToBuffer(WinZipJPEGDecompressor *self, void *bytes, size_t length); + +static inline bool IsFinalWinZipJPEGBundle(WinZipJPEGDecompressor *self) { return self->reachedend; } + +static inline bool AreMoreWinZipJPEGSlicesAvailable(WinZipJPEGDecompressor *self) +{ return !self->reachedend && self->slicesavailable; } + +static inline bool AreMoreWinZipJPEGBytesAvailable(WinZipJPEGDecompressor *self) +{ return self->mcusavailable || self->bitlength >= 8 || self->needsstuffing || self->writerestartmarker; } + +static inline uint32_t WinZipJPEGBundleMetadataLength(WinZipJPEGDecompressor *self) { return self->metadatalength; } + +static inline uint8_t *WinZipJPEGBundleMetadataBytes(WinZipJPEGDecompressor *self) { return self->metadatabytes; } + +#endif diff --git a/winzipjpeg/InputStream.h b/winzipjpeg/InputStream.h new file mode 100644 index 0000000..0e487e6 --- /dev/null +++ b/winzipjpeg/InputStream.h @@ -0,0 +1,29 @@ +/* + * InputStream.h + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef __WINZIP_JPEG_INPUT_STREAM_H__ +#define __WINZIP_JPEG_INPUT_STREAM_H__ + +#include +#include + +typedef size_t WinZipJPEGReadFunction(void *context, uint8_t *buffer, size_t length); + +#endif diff --git a/winzipjpeg/JPEG.c b/winzipjpeg/JPEG.c new file mode 100644 index 0000000..e0be058 --- /dev/null +++ b/winzipjpeg/JPEG.c @@ -0,0 +1,329 @@ +/* + * JPEG.c + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#include "JPEG.h" + +#include + +// #include +// #define DebugPrint(...) fprintf(stderr,__VA_ARGS__) +#define DebugPrint(...) + +static const uint8_t *FindNextMarker(const uint8_t *ptr, const uint8_t *end); +static int ParseSize(const uint8_t *ptr, const uint8_t *end); + +static inline uint16_t ParseUInt16(const uint8_t *ptr) { return (ptr[0] << 8) | ptr[1]; } + +const void *FindStartOfWinZipJPEGImage(const void *bytes, size_t length) +{ + const uint8_t *ptr = bytes; + const uint8_t *end = ptr + length; + + while(ptr + 2 <= end) + { + if(ptr[0] == 0xff && ptr[1] == 0xd8) return ptr; + ptr++; + } + + return NULL; +} + +void InitializeWinZipJPEGMetadata(WinZipJPEGMetadata *self) { memset(self, 0, sizeof(*self)); } + +int ParseWinZipJPEGMetadata(WinZipJPEGMetadata *self, const void *bytes, size_t length) +{ + const uint8_t *ptr = bytes; + const uint8_t *end = ptr + length; + + for(;;) + { + ptr = FindNextMarker(ptr, end); + if(!ptr) return WinZipJPEGMetadataParsingFailed; + + switch(*ptr++) + { + case 0xd8: // Start of image + // Empty marker, do nothing. + DebugPrint("Start of image\n"); + break; + + case 0xc4: // Define huffman table + { + int size = ParseSize(ptr, end); + if(!size) return WinZipJPEGMetadataParsingFailed; + const uint8_t *next = ptr + size; + + ptr += 2; + + DebugPrint("Define huffman table(s)\n"); + + while(ptr + 17 <= next) + { + int class = *ptr >> 4; + int index = *ptr & 0x0f; + ptr++; + + if(class != 0 && class != 1) return WinZipJPEGMetadataParsingFailed; + if(index >= 4) return WinZipJPEGMetadataParsingFailed; + + int numcodes[16]; + int totalcodes = 0; + for(int i = 0; i < 16; i++) + { + numcodes[i] = ptr[i]; + totalcodes += numcodes[i]; + } + ptr += 16; + + if(ptr + totalcodes > next) return WinZipJPEGMetadataParsingFailed; + + DebugPrint(" > %s table at %d with %d codes\n", class == 0 ? "DC" : "AC", index, totalcodes); + + unsigned int code = 0; + for(int i = 0; i < 16; i++) + { + for(int j = 0; j < numcodes[i]; j++) + { + int value = *ptr++; + + self->huffmantables[class][index].codes[value].code = code; + self->huffmantables[class][index].codes[value].length = i + 1; + // DebugPrint(" >> Code %x length %d for %d\n",code,i+1,value); + + code++; + } + + code <<= 1; + } + } + + ptr = next; + } + break; + + case 0xdb: // Define quantization table(s) + { + int size = ParseSize(ptr, end); + if(!size) return WinZipJPEGMetadataParsingFailed; + const uint8_t *next = ptr + size; + + ptr += 2; + + DebugPrint("Define quantization table(s)\n"); + + while(ptr + 1 <= next) + { + int precision = *ptr >> 4; + int index = *ptr & 0x0f; + ptr++; + + if(index >= 4) return WinZipJPEGMetadataParsingFailed; + + if(precision == 0) + { + DebugPrint(" > 8 bit table at %d\n", index); + + if(ptr + 64 > next) return WinZipJPEGMetadataParsingFailed; + for(int i = 0; i < 64; i++) self->quantizationtables[index].c[i] = ptr[i]; + ptr += 64; + } + else if(precision == 1) + { + DebugPrint(" > 16 bit table at %d\n", index); + + if(ptr + 128 > next) return WinZipJPEGMetadataParsingFailed; + for(int i = 0; i < 64; i++) self->quantizationtables[index].c[i] = ParseUInt16(&ptr[2 * i]); + ptr += 128; + } + else + return WinZipJPEGMetadataParsingFailed; + } + + ptr = next; + } + break; + + case 0xdd: // Define restart interval + { + int size = ParseSize(ptr, end); + if(!size) return WinZipJPEGMetadataParsingFailed; + const uint8_t *next = ptr + size; + + self->restartinterval = ParseUInt16(&ptr[2]); + + ptr = next; + + DebugPrint("Define restart interval: %d\n", self->restartinterval); + } + break; + + case 0xc0: // Start of frame 0 + case 0xc1: // Start of frame 1 + { + int size = ParseSize(ptr, end); + if(!size) return WinZipJPEGMetadataParsingFailed; + const uint8_t *next = ptr + size; + + if(size < 8) return WinZipJPEGMetadataParsingFailed; + self->bits = ptr[2]; + self->height = ParseUInt16(&ptr[3]); + self->width = ParseUInt16(&ptr[5]); + self->numcomponents = ptr[7]; + + if(self->numcomponents < 1 || self->numcomponents > 4) return WinZipJPEGMetadataParsingFailed; + if(size < 8 + self->numcomponents * 3) return WinZipJPEGMetadataParsingFailed; + + self->maxhorizontalfactor = 1; + self->maxverticalfactor = 1; + + DebugPrint("Start of frame: %dx%d %d bits %d comps\n", self->width, self->height, self->bits, + self->numcomponents); + + for(int i = 0; i < self->numcomponents; i++) + { + self->components[i].identifier = ptr[8 + i * 3]; + self->components[i].horizontalfactor = ptr[9 + i * 3] >> 4; + self->components[i].verticalfactor = ptr[9 + i * 3] & 0x0f; + int quantizationindex = ptr[10 + i * 3]; + self->components[i].quantizationtable = &self->quantizationtables[quantizationindex]; + + if(self->components[i].horizontalfactor > self->maxhorizontalfactor) + self->maxhorizontalfactor = self->components[i].horizontalfactor; + + if(self->components[i].verticalfactor > self->maxverticalfactor) + self->maxverticalfactor = self->components[i].verticalfactor; + + DebugPrint(" > Component id %d, %dx%d, quant %d\n", self->components[i].identifier, + self->components[i].horizontalfactor, self->components[i].verticalfactor, + quantizationindex); + } + + // TODO: This is a kludge for strange one-component files with + // 2x2 sampling factor, that are still stored in exactly the same + // way as 1x1. Figure out how to actually handle this properly. + if(self->numcomponents == 1) + { + self->components[0].horizontalfactor /= self->maxhorizontalfactor; + self->components[0].verticalfactor /= self->maxverticalfactor; + self->maxhorizontalfactor = 1; + self->maxverticalfactor = 1; + } + + int mcuwidth = self->maxhorizontalfactor * 8; + int mcuheight = self->maxverticalfactor * 8; + self->horizontalmcus = (self->width + mcuwidth - 1) / mcuwidth; + self->verticalmcus = (self->height + mcuheight - 1) / mcuheight; + + DebugPrint(" > MCU size %dx%d, %d horizontal MCUs, %d vertical MCUs.\n", mcuwidth, mcuheight, + self->horizontalmcus, self->verticalmcus); + + ptr = next; + } + break; + + case 0xda: // Start of scan + { + int size = ParseSize(ptr, end); + if(!size) return WinZipJPEGMetadataParsingFailed; + + if(size < 6) return WinZipJPEGMetadataParsingFailed; + + self->numscancomponents = ptr[2]; + if(self->numscancomponents < 1 || self->numscancomponents > 4) return WinZipJPEGMetadataParsingFailed; + if(size < 6 + self->numscancomponents * 2) return WinZipJPEGMetadataParsingFailed; + + DebugPrint("Start of scan: %d comps\n", self->numscancomponents); + + for(int i = 0; i < self->numscancomponents; i++) + { + int identifier = ptr[3 + i * 2]; + WinZipJPEGComponent *component = NULL; + for(int j = 0; j < self->numcomponents; j++) + { + if(self->components[j].identifier == identifier) + { + component = &self->components[j]; + break; + } + } + if(!component) return WinZipJPEGMetadataParsingFailed; + + self->scancomponents[i].component = component; + + int dcindex = ptr[4 + i * 2] >> 4; + int acindex = ptr[4 + i * 2] & 0x0f; + self->scancomponents[i].dctable = &self->huffmantables[0][dcindex]; + self->scancomponents[i].actable = &self->huffmantables[1][acindex]; + + DebugPrint(" > Component id %d, %dx%d, DC %d, AC %d\n", identifier, + self->scancomponents[i].component->horizontalfactor, + self->scancomponents[i].component->verticalfactor, dcindex, acindex); + } + + if(ptr[3 + self->numscancomponents * 2] != 0) return WinZipJPEGMetadataParsingFailed; + if(ptr[4 + self->numscancomponents * 2] != 63) return WinZipJPEGMetadataParsingFailed; + if(ptr[5 + self->numscancomponents * 2] != 0) return WinZipJPEGMetadataParsingFailed; + + return WinZipJPEGMetadataFoundStartOfScan; + } + break; + + case 0xd9: // End of image + return WinZipJPEGMetadataFoundEndOfImage; + + default: + { + int size = ParseSize(ptr, end); + if(!size) return WinZipJPEGMetadataParsingFailed; + ptr += size; + + DebugPrint("Unknown marker %02x\n", ptr[-1]); + } + break; + } + } +} + +// Find next marker, skipping pad bytes. +static const uint8_t *FindNextMarker(const uint8_t *ptr, const uint8_t *end) +{ + if(ptr >= end) return NULL; + if(*ptr != 0xff) return NULL; + + while(*ptr == 0xff) + { + ptr++; + if(ptr >= end) return NULL; + } + + return ptr; +} + +// Parse and sanity check the size of a marker. +static int ParseSize(const uint8_t *ptr, const uint8_t *end) +{ + if(ptr + 2 > end) return 0; + + int size = ParseUInt16(ptr); + if(size < 2) return 0; + if(ptr + size > end) return 0; + + return size; +} diff --git a/winzipjpeg/JPEG.h b/winzipjpeg/JPEG.h new file mode 100644 index 0000000..fa1c561 --- /dev/null +++ b/winzipjpeg/JPEG.h @@ -0,0 +1,89 @@ +/* + * JPEG.h + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef __WINZIP_JPEG_JPEG_H__ +#define __WINZIP_JPEG_JPEG_H__ + +#include +#include +#include + +#define WinZipJPEGMetadataFoundStartOfScan 1 +#define WinZipJPEGMetadataFoundEndOfImage 2 +#define WinZipJPEGMetadataParsingFailed 3 + +typedef struct WinZipJPEGBlock +{ + int16_t c[64]; + uint8_t eob; +} WinZipJPEGBlock; + +typedef struct WinZipJPEGQuantizationTable +{ + int16_t c[64]; +} WinZipJPEGQuantizationTable; + +typedef struct WinZipJPEGHuffmanCode +{ + unsigned int code, length; +} WinZipJPEGHuffmanCode; + +typedef struct WinZipJPEGHuffmanTable +{ + WinZipJPEGHuffmanCode codes[256]; +} WinZipJPEGHuffmanTable; + +typedef struct WinZipJPEGComponent +{ + unsigned int identifier; + unsigned int horizontalfactor, verticalfactor; + WinZipJPEGQuantizationTable *quantizationtable; +} WinZipJPEGComponent; + +typedef struct WinZipJPEGScanComponent +{ + WinZipJPEGComponent *component; + WinZipJPEGHuffmanTable *dctable, *actable; +} WinZipJPEGScanComponent; + +typedef struct WinZipJPEGMetadata +{ + unsigned int width, height, bits; + unsigned int restartinterval; + + unsigned int maxhorizontalfactor, maxverticalfactor; + unsigned int horizontalmcus, verticalmcus; + + unsigned int numcomponents; + WinZipJPEGComponent components[4]; + + unsigned int numscancomponents; + WinZipJPEGScanComponent scancomponents[4]; + + WinZipJPEGQuantizationTable quantizationtables[4]; + WinZipJPEGHuffmanTable huffmantables[2][4]; +} WinZipJPEGMetadata; + +const void *FindStartOfWinZipJPEGImage(const void *bytes, size_t length); + +void InitializeWinZipJPEGMetadata(WinZipJPEGMetadata *self); +int ParseWinZipJPEGMetadata(WinZipJPEGMetadata *self, const void *bytes, size_t length); + +#endif diff --git a/winzipjpeg/LZMA.h b/winzipjpeg/LZMA.h new file mode 100644 index 0000000..9977044 --- /dev/null +++ b/winzipjpeg/LZMA.h @@ -0,0 +1,32 @@ +/* + * LZMA.h + * + * Copyright (c) 2017-present, MacPaw Inc. All rights reserved. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef __WINZIP_JPEG_LZMA_H__ +#define __WINZIP_JPEG_LZMA_H__ + +#if !__LP64__ +#define _LZMA_UINT32_IS_ULONG +#endif + +#define Byte LzmaByte +#include "../3rdparty/lzma/C/LzmaDec.h" +#undef Byte + +#endif diff --git a/zip/deflate64.c b/zip/deflate64.c new file mode 100644 index 0000000..620c68c --- /dev/null +++ b/zip/deflate64.c @@ -0,0 +1,350 @@ +/* + * 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 "deflate64.h" + +#include +#include + +#include "../pak/bitstream.h" +#include "../pak/prefixcode.h" + +/* Deflate64 window size: 64KB */ +#define DEFLATE64_WINDOW_SIZE 65536 +#define DEFLATE64_WINDOW_MASK (DEFLATE64_WINDOW_SIZE - 1) + +/* Block types */ +#define BLOCK_STORED 0 +#define BLOCK_FIXED 1 +#define BLOCK_DYNAMIC 2 + +/* Number of symbols */ +#define NUM_LITLEN_SYMBOLS 288 +#define NUM_DIST_SYMBOLS 32 +#define MAX_LITLEN_SYMBOLS 286 /* Only 0-285 are defined for Deflate64 */ + +/* Length base values for codes 257-285 */ +static const int length_base[29] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, + 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 3}; + +/* Length extra bits for codes 257-285 (Deflate64: code 285 has 16 extra bits) */ +static const int length_extra[29] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, + 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 16}; + +/* Distance base values for codes 0-31 */ +static const int dist_base[32] = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, + 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, + 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 32769, 49153}; + +/* Distance extra bits for codes 0-31 */ +static const int dist_extra[32] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14}; + +/* Order of code length codes for dynamic Huffman tables */ +static const int codelen_order[19] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* Build static fixed Huffman tables for Deflate */ +static PrefixCode *build_fixed_litlen_code(void) +{ + int lengths[NUM_LITLEN_SYMBOLS]; + int i; + + for(i = 0; i <= 143; i++) lengths[i] = 8; + for(i = 144; i <= 255; i++) lengths[i] = 9; + for(i = 256; i <= 279; i++) lengths[i] = 7; + for(i = 280; i <= 287; i++) lengths[i] = 8; + + return prefix_code_alloc_with_lengths(lengths, NUM_LITLEN_SYMBOLS, 9, true); +} + +static PrefixCode *build_fixed_dist_code(void) +{ + int lengths[NUM_DIST_SYMBOLS]; + + for(int i = 0; i < NUM_DIST_SYMBOLS; i++) lengths[i] = 5; + + return prefix_code_alloc_with_lengths(lengths, NUM_DIST_SYMBOLS, 5, true); +} + +/* Build dynamic Huffman tables from the stream */ +static int build_dynamic_tables(BitStream *bs, PrefixCode **litlen_code_out, PrefixCode **dist_code_out) +{ + int hlit = (int)bitstream_read_bits_le(bs, 5) + 257; + int hdist = (int)bitstream_read_bits_le(bs, 5) + 1; + int hclen = (int)bitstream_read_bits_le(bs, 4) + 4; + + /* Read code length code lengths */ + int codelen_lengths[19]; + memset(codelen_lengths, 0, sizeof(codelen_lengths)); + + for(int i = 0; i < hclen; i++) codelen_lengths[codelen_order[i]] = (int)bitstream_read_bits_le(bs, 3); + + /* Build code length code */ + PrefixCode *codelen_code = prefix_code_alloc_with_lengths(codelen_lengths, 19, 7, true); + + if(!codelen_code) return -1; + + /* Read literal/length and distance code lengths */ + int total_codes = hlit + hdist; + int *all_lengths = (int *)calloc(total_codes, sizeof(int)); + + if(!all_lengths) + { + prefix_code_free(codelen_code); + return -1; + } + + int idx = 0; + + while(idx < total_codes) + { + int sym = prefix_code_read_symbol_le(bs, codelen_code); + + if(sym < 0) + { + free(all_lengths); + prefix_code_free(codelen_code); + return -1; + } + + if(sym < 16) + { + /* Literal length value */ + all_lengths[idx++] = sym; + } + else if(sym == 16) + { + /* Repeat previous length 3-6 times */ + int repeat = (int)bitstream_read_bits_le(bs, 2) + 3; + int prev = idx > 0 ? all_lengths[idx - 1] : 0; + + for(int i = 0; i < repeat && idx < total_codes; i++) all_lengths[idx++] = prev; + } + else if(sym == 17) + { + /* Repeat zero 3-10 times */ + int repeat = (int)bitstream_read_bits_le(bs, 3) + 3; + + for(int i = 0; i < repeat && idx < total_codes; i++) all_lengths[idx++] = 0; + } + else if(sym == 18) + { + /* Repeat zero 11-138 times */ + int repeat = (int)bitstream_read_bits_le(bs, 7) + 11; + + for(int i = 0; i < repeat && idx < total_codes; i++) all_lengths[idx++] = 0; + } + } + + prefix_code_free(codelen_code); + + /* Find max code lengths */ + int max_litlen = 0; + + for(int i = 0; i < hlit; i++) + { + if(all_lengths[i] > max_litlen) max_litlen = all_lengths[i]; + } + + if(max_litlen == 0) max_litlen = 1; + + int max_dist = 0; + + for(int i = hlit; i < total_codes; i++) + { + if(all_lengths[i] > max_dist) max_dist = all_lengths[i]; + } + + if(max_dist == 0) max_dist = 1; + + /* Build literal/length code */ + *litlen_code_out = prefix_code_alloc_with_lengths(all_lengths, hlit, max_litlen, true); + + /* Build distance code */ + *dist_code_out = prefix_code_alloc_with_lengths(all_lengths + hlit, hdist, max_dist, true); + + free(all_lengths); + + if(!*litlen_code_out || !*dist_code_out) + { + if(*litlen_code_out) + { + prefix_code_free(*litlen_code_out); + *litlen_code_out = NULL; + } + + if(*dist_code_out) + { + prefix_code_free(*dist_code_out); + *dist_code_out = NULL; + } + + return -1; + } + + return 0; +} + +int zip_deflate64_decompress(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len) +{ + BitStream bs; + size_t out_pos = 0; + size_t out_size = *out_len; + int result = 0; + int is_final = 0; + + if(!in_buf || !out_buf || !out_len) return -1; + + bitstream_init(&bs, in_buf, in_len); + + while(!is_final && out_pos < out_size) + { + PrefixCode *litlen_code = NULL; + PrefixCode *dist_code = NULL; + int free_codes = 0; + + is_final = (int)bitstream_read_bits_le(&bs, 1); + int block_type = (int)bitstream_read_bits_le(&bs, 2); + + if(block_type == BLOCK_STORED) + { + /* Skip to byte boundary */ + if(bs.bitcount > 0) + { + bs.bitbuffer = 0; + bs.bitcount = 0; + } + + uint16_t len = bitstream_read_uint16_le(&bs); + uint16_t nlen = bitstream_read_uint16_le(&bs); + + (void)nlen; /* Complement check can be skipped; we trust the data */ + + for(uint16_t i = 0; i < len && out_pos < out_size; i++) + { + if(bs.pos >= bs.length) break; + + out_buf[out_pos++] = bs.data[bs.pos++]; + } + + continue; + } + else if(block_type == BLOCK_FIXED) + { + litlen_code = build_fixed_litlen_code(); + dist_code = build_fixed_dist_code(); + free_codes = 1; + + if(!litlen_code || !dist_code) + { + result = -1; + goto block_cleanup; + } + } + else if(block_type == BLOCK_DYNAMIC) + { + if(build_dynamic_tables(&bs, &litlen_code, &dist_code) != 0) + { + result = -1; + goto block_cleanup; + } + + free_codes = 1; + } + else + { + result = -1; + break; + } + + /* Decode symbols */ + while(out_pos < out_size) + { + int sym = prefix_code_read_symbol_le(&bs, litlen_code); + + if(sym < 0) + { + result = -1; + goto block_cleanup; + } + + if(sym < 256) + { + /* Literal byte */ + out_buf[out_pos++] = (uint8_t)sym; + } + else if(sym == 256) + { + /* End of block */ + break; + } + else + { + /* Length/distance pair */ + int length_code_idx = sym - 257; + + if(length_code_idx < 0 || length_code_idx >= 29) + { + result = -1; + goto block_cleanup; + } + + int length = length_base[length_code_idx]; + + if(length_extra[length_code_idx] > 0) + length += (int)bitstream_read_bits_le(&bs, length_extra[length_code_idx]); + + /* Read distance */ + int dist_sym = prefix_code_read_symbol_le(&bs, dist_code); + + if(dist_sym < 0 || dist_sym >= 32) + { + result = -1; + goto block_cleanup; + } + + int distance = dist_base[dist_sym]; + + if(dist_extra[dist_sym] > 0) distance += (int)bitstream_read_bits_le(&bs, dist_extra[dist_sym]); + + /* Copy from history */ + for(int i = 0; i < length && out_pos < out_size; i++) + { + if((size_t)distance > out_pos) + out_buf[out_pos] = 0; + else + out_buf[out_pos] = out_buf[out_pos - distance]; + + out_pos++; + } + } + } + + block_cleanup: + if(free_codes) + { + if(litlen_code) prefix_code_free(litlen_code); + if(dist_code) prefix_code_free(dist_code); + } + + if(result != 0) break; + } + + *out_len = out_pos; + return result; +} diff --git a/zip/deflate64.h b/zip/deflate64.h new file mode 100644 index 0000000..f87df59 --- /dev/null +++ b/zip/deflate64.h @@ -0,0 +1,39 @@ +/* + * 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_COMPRESSION_NATIVE_ZIP_DEFLATE64_H +#define AARU_COMPRESSION_NATIVE_ZIP_DEFLATE64_H + +#include +#include + +/** + * Decompress Deflate64 (ZIP method 9) data. + * + * Deflate64 is an extension of Deflate with a 64KB window, + * additional distance codes 30-31, and code 285 meaning 3 + 16 extra bits. + * + * @param in_buf Compressed input buffer (raw deflate64 stream, no zlib/gzip header) + * @param in_len Length of compressed input + * @param out_buf Decompressed output buffer (must be pre-allocated) + * @param out_len On entry: size of output buffer. On exit: bytes actually written. + * @return 0 on success, non-zero on error + */ +int zip_deflate64_decompress(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len); + +#endif /* AARU_COMPRESSION_NATIVE_ZIP_DEFLATE64_H */ diff --git a/zip/implode.c b/zip/implode.c new file mode 100644 index 0000000..efc89c7 --- /dev/null +++ b/zip/implode.c @@ -0,0 +1,199 @@ +/* + * 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 "implode.h" + +#include +#include + +#include "../pak/bitstream.h" +#include "../pak/prefixcode.h" + +/* + * Parse a Shannon-Fano tree from the ZIP Implode bitstream. + * + * Format: 1 byte = number of groups - 1. + * Each group: 1 byte where high nibble = (count - 1), low nibble = (code length - 1). + * Codes are assigned from highest value down (shortest code = all ones). + */ +static PrefixCode *implode_read_tree(BitStream *bs, int num_symbols) +{ + int lengths[256]; + int num_groups; + int symbol; + int max_length; + + memset(lengths, 0, sizeof(lengths)); + + num_groups = (int)bitstream_read_bits_le(bs, 8) + 1; + + symbol = 0; + max_length = 0; + + for(int g = 0; g < num_groups; g++) + { + int group_byte = (int)bitstream_read_bits_le(bs, 8); + int count = ((group_byte >> 4) & 0x0F) + 1; + int length = (group_byte & 0x0F) + 1; + + for(int i = 0; i < count && symbol < num_symbols; i++) + { + lengths[symbol++] = length; + + if(length > max_length) max_length = length; + } + } + + /* Build prefix code tree. shortestCodeIsZeros=false means codes assigned from highest value down. */ + return prefix_code_alloc_with_lengths(lengths, symbol, max_length, false); +} + +int zip_implode_decompress(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len, + int large_dictionary, int has_literals) +{ + BitStream bs; + PrefixCode *literal_code = NULL; + PrefixCode *length_code = NULL; + PrefixCode *distance_code = NULL; + int offset_bits; /* Number of low offset bits read raw */ + size_t out_pos = 0; + size_t out_size = *out_len; + int result = 0; + + if(!in_buf || !out_buf || !out_len) return -1; + + offset_bits = large_dictionary ? 7 : 6; + + bitstream_init(&bs, in_buf, in_len); + + /* Read trees */ + if(has_literals) + { + literal_code = implode_read_tree(&bs, 256); + + if(!literal_code) + { + result = -1; + goto cleanup; + } + } + + length_code = implode_read_tree(&bs, 64); + + if(!length_code) + { + result = -1; + goto cleanup; + } + + distance_code = implode_read_tree(&bs, 64); + + if(!distance_code) + { + result = -1; + goto cleanup; + } + + /* Decompress */ + while(out_pos < out_size && !bitstream_eof(&bs)) + { + uint32_t flag = bitstream_read_bits_le(&bs, 1); + + if(flag) /* Literal */ + { + uint8_t byte; + + if(has_literals) + { + int sym = prefix_code_read_symbol_le(&bs, literal_code); + + if(sym < 0) + { + result = -1; + goto cleanup; + } + + byte = (uint8_t)sym; + } + else + { + byte = (uint8_t)bitstream_read_bits_le(&bs, 8); + } + + out_buf[out_pos++] = byte; + } + else /* Match */ + { + /* Read low offset bits raw */ + uint32_t low_offset = bitstream_read_bits_le(&bs, offset_bits); + + /* Read high offset bits from distance tree */ + int high_offset = prefix_code_read_symbol_le(&bs, distance_code); + + if(high_offset < 0) + { + result = -1; + goto cleanup; + } + + size_t offset = ((size_t)high_offset << offset_bits) | low_offset; + offset += 1; /* Offset is 1-based */ + + /* Read length from tree */ + int length = prefix_code_read_symbol_le(&bs, length_code); + + if(length < 0) + { + result = -1; + goto cleanup; + } + + length += 2; /* Minimum match length is 2 */ + + /* If length value was 63 (max), read additional byte */ + if(length == 65) + { + int extra = (int)bitstream_read_bits_le(&bs, 8); + length += extra; + } + + /* If literal tree present, add 1 to length */ + if(has_literals) length++; + + /* Copy from history */ + for(int i = 0; i < length && out_pos < out_size; i++) + { + if(offset > out_pos) + out_buf[out_pos] = 0; + else + out_buf[out_pos] = out_buf[out_pos - offset]; + + out_pos++; + } + } + } + + *out_len = out_pos; + +cleanup: + if(literal_code) prefix_code_free(literal_code); + if(length_code) prefix_code_free(length_code); + if(distance_code) prefix_code_free(distance_code); + + return result; +} diff --git a/zip/implode.h b/zip/implode.h new file mode 100644 index 0000000..ac6424f --- /dev/null +++ b/zip/implode.h @@ -0,0 +1,41 @@ +/* + * 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_COMPRESSION_NATIVE_ZIP_IMPLODE_H +#define AARU_COMPRESSION_NATIVE_ZIP_IMPLODE_H + +#include +#include + +/** + * Decompress ZIP Implode (method 6) data. + * + * ZIP Implode uses Shannon-Fano coded LZSS with LSB-first bitstream. + * + * @param in_buf Compressed input buffer + * @param in_len Length of compressed input + * @param out_buf Decompressed output buffer (must be pre-allocated) + * @param out_len On entry: size of output buffer. On exit: bytes actually written. + * @param large_dictionary Non-zero for 8K sliding dictionary (flag bit 1), zero for 4K + * @param has_literals Non-zero if literal tree is present (flag bit 2) + * @return 0 on success, non-zero on error + */ +int zip_implode_decompress(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len, + int large_dictionary, int has_literals); + +#endif /* AARU_COMPRESSION_NATIVE_ZIP_IMPLODE_H */ diff --git a/zip/reduce.c b/zip/reduce.c new file mode 100644 index 0000000..a3aad0e --- /dev/null +++ b/zip/reduce.c @@ -0,0 +1,161 @@ +/* + * 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 "reduce.h" + +#include "../pak/bitstream.h" + +/* DLE escape byte used by ZIP Reduce */ +#define REDUCE_DLE 0x90 + +/* Minimum number of bits needed to represent (n-1) */ +static int reduce_b_value(int n) +{ + if(n > 16) return 5; + if(n > 8) return 4; + if(n > 4) return 3; + if(n > 2) return 2; + if(n > 0) return 1; + return 0; +} + +int zip_reduce_decompress(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len, int comp_factor) +{ + BitStream bs; + uint8_t follower_sets[256][32]; + uint8_t follower_count[256]; + size_t out_pos = 0; + size_t out_size = *out_len; + int v_len_bits; + int v_len_mask; + + if(!in_buf || !out_buf || !out_len) return -1; + if(comp_factor < 1 || comp_factor > 4) return -1; + + v_len_bits = 8 - comp_factor; + v_len_mask = (1 << v_len_bits) - 1; + + bitstream_init(&bs, in_buf, in_len); + + /* Read follower sets in reverse order (255 down to 0) */ + for(int i = 255; i >= 0; i--) + { + follower_count[i] = (uint8_t)bitstream_read_bits_le(&bs, 6); + + if(follower_count[i] > 32) + { + *out_len = 0; + return -1; + } + + for(int j = 0; j < follower_count[i]; j++) follower_sets[i][j] = (uint8_t)bitstream_read_bits_le(&bs, 8); + } + + /* Decode using follower sets + LZ77 state machine */ + uint8_t last_char = 0; + int state = 0; + uint8_t v_byte = 0; + size_t match_len = 0; + + while(out_pos < out_size && !bitstream_eof(&bs)) + { + /* Read next byte through follower sets */ + uint8_t c; + + if(follower_count[last_char] == 0) { c = (uint8_t)bitstream_read_bits_le(&bs, 8); } + else + { + uint32_t flag = bitstream_read_bits_le(&bs, 1); + + if(flag == 1) { c = (uint8_t)bitstream_read_bits_le(&bs, 8); } + else + { + int bw = reduce_b_value(follower_count[last_char]); + int idx = (int)bitstream_read_bits_le(&bs, bw); + + if(idx >= follower_count[last_char]) + { + *out_len = out_pos; + return -1; + } + + c = follower_sets[last_char][idx]; + } + } + + last_char = c; + + /* LZ77 state machine */ + switch(state) + { + case 0: + if(c != REDUCE_DLE) { out_buf[out_pos++] = c; } + else + { + state = 1; + } + break; + + case 1: + if(c != 0) + { + v_byte = c; + match_len = v_byte & v_len_mask; + + if(match_len == (size_t)v_len_mask) + state = 2; /* Need extra length byte */ + else + state = 3; /* Read distance byte next */ + } + else + { + /* Escaped DLE: output literal 0x90 */ + out_buf[out_pos++] = REDUCE_DLE; + state = 0; + } + break; + + case 2: + match_len += c; + state = 3; + break; + + case 3: + { + size_t dist = ((size_t)(v_byte >> v_len_bits)) * 256 + c + 1; + match_len += 3; + + for(size_t i = 0; i < match_len && out_pos < out_size; i++) + { + if(dist > out_pos) + out_buf[out_pos] = 0; /* Before start of output = zeros */ + else + out_buf[out_pos] = out_buf[out_pos - dist]; + + out_pos++; + } + + state = 0; + break; + } + } + } + + *out_len = out_pos; + return 0; +} diff --git a/zip/reduce.h b/zip/reduce.h new file mode 100644 index 0000000..c4235ba --- /dev/null +++ b/zip/reduce.h @@ -0,0 +1,40 @@ +/* + * 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_COMPRESSION_NATIVE_ZIP_REDUCE_H +#define AARU_COMPRESSION_NATIVE_ZIP_REDUCE_H + +#include +#include + +/** + * Decompress ZIP Reduce (methods 2-5) data. + * + * ZIP Reduce uses a two-stage compression: probabilistic follower sets + * followed by LZ77 with DLE (0x90) escape codes. + * + * @param in_buf Compressed input buffer + * @param in_len Length of compressed input + * @param out_buf Decompressed output buffer (must be pre-allocated) + * @param out_len On entry: size of output buffer. On exit: bytes actually written. + * @param comp_factor Compression factor 1-4 (from ZIP method 2-5: factor = method - 1) + * @return 0 on success, non-zero on error + */ +int zip_reduce_decompress(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len, int comp_factor); + +#endif /* AARU_COMPRESSION_NATIVE_ZIP_REDUCE_H */ diff --git a/zip/shrink.c b/zip/shrink.c new file mode 100644 index 0000000..5721a98 --- /dev/null +++ b/zip/shrink.c @@ -0,0 +1,227 @@ +/* + * 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 "shrink.h" + +#include +#include + +#include "../pak/bitstream.h" + +/* LZW constants for ZIP Shrink */ +#define SHRINK_MAX_BITS 13 +#define SHRINK_INIT_BITS 9 +#define SHRINK_MAX_CODES 8192 /* 2^13 */ +#define SHRINK_CONTROL 256 /* Control code */ +#define SHRINK_FIRST_CODE 257 /* First user code */ +#define SHRINK_GROW_CODE 1 /* Increment code size */ +#define SHRINK_CLEAR_CODE 2 /* Partial clear */ + +/* LZW dictionary entry */ +typedef struct +{ + int16_t parent; /* Parent code, -1 for root entries */ + uint8_t chr; /* Character at this node */ + uint8_t used; /* Whether this entry is in use */ +} ShrinkEntry; + +/* Reverse-walk the dictionary chain and write to a stack buffer, return length */ +static int shrink_decode_string(ShrinkEntry *dict, int code, uint8_t *stack, int max_stack) +{ + int count = 0; + + while(code >= 0 && count < max_stack) + { + stack[count++] = dict[code].chr; + code = dict[code].parent; + } + + return count; +} + +/* Partial clear: mark entries whose parent chains include non-root entries + that are not themselves parents of other entries */ +static void shrink_partial_clear(ShrinkEntry *dict, int num_codes) +{ + /* Mark entries that are referenced as parents */ + uint8_t *is_parent = (uint8_t *)calloc(SHRINK_MAX_CODES, 1); + if(!is_parent) return; + + for(int i = SHRINK_FIRST_CODE; i < num_codes; i++) + { + if(dict[i].used && dict[i].parent >= SHRINK_FIRST_CODE) is_parent[dict[i].parent] = 1; + } + + /* Clear entries that are not parents of other entries */ + for(int i = SHRINK_FIRST_CODE; i < num_codes; i++) + { + if(dict[i].used && !is_parent[i]) dict[i].used = 0; + } + + free(is_parent); +} + +int zip_shrink_decompress(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len) +{ + BitStream bs; + ShrinkEntry *dict; + uint8_t stack[SHRINK_MAX_CODES]; + size_t out_pos = 0; + size_t out_size = *out_len; + int code_size; + int next_code; + int prev_code; + int code; + int len; + + if(!in_buf || !out_buf || !out_len) return -1; + + /* Allocate dictionary */ + dict = (ShrinkEntry *)calloc(SHRINK_MAX_CODES, sizeof(ShrinkEntry)); + if(!dict) return -1; + + /* Initialize dictionary with single-character entries */ + for(int i = 0; i < 256; i++) + { + dict[i].parent = -1; + dict[i].chr = (uint8_t)i; + dict[i].used = 1; + } + + /* Code 256 is the control code, mark as used */ + dict[SHRINK_CONTROL].parent = -1; + dict[SHRINK_CONTROL].chr = 0; + dict[SHRINK_CONTROL].used = 1; + + next_code = SHRINK_FIRST_CODE; + code_size = SHRINK_INIT_BITS; + + bitstream_init(&bs, in_buf, in_len); + + /* Read first code */ + prev_code = (int)bitstream_read_bits_le(&bs, code_size); + + if(prev_code < 256 && out_pos < out_size) out_buf[out_pos++] = (uint8_t)prev_code; + + while(out_pos < out_size && !bitstream_eof(&bs)) + { + code = (int)bitstream_read_bits_le(&bs, code_size); + + if(bitstream_eof(&bs)) break; + + /* Handle control code */ + if(code == SHRINK_CONTROL) + { + int subcode = (int)bitstream_read_bits_le(&bs, code_size); + + if(bitstream_eof(&bs)) break; + + if(subcode == SHRINK_GROW_CODE) + { + code_size++; + if(code_size > SHRINK_MAX_BITS) + { + free(dict); + return -1; + } + } + else if(subcode == SHRINK_CLEAR_CODE) + { + shrink_partial_clear(dict, next_code); + + /* Reset next_code to search from the beginning for freed entries */ + next_code = SHRINK_FIRST_CODE; + while(next_code < SHRINK_MAX_CODES && dict[next_code].used) next_code++; + } + + continue; + } + + /* Handle KwKwK case: code == next available and not yet in dictionary */ + if(code >= SHRINK_FIRST_CODE && !dict[code].used) + { + /* The code is the next one to be added: reconstruct prev_code string + first char */ + len = shrink_decode_string(dict, prev_code, stack, SHRINK_MAX_CODES); + + if(len <= 0) + { + free(dict); + return -1; + } + + /* First char of previous string is stack[len-1] (stack is in reverse order) */ + uint8_t first_char = stack[len - 1]; + + /* Output in forward order: stack is reversed */ + for(int i = len - 1; i >= 0; i--) + { + if(out_pos < out_size) out_buf[out_pos++] = stack[i]; + } + + if(out_pos < out_size) out_buf[out_pos++] = first_char; + + /* Add new dictionary entry */ + if(next_code < SHRINK_MAX_CODES) + { + dict[next_code].parent = (int16_t)prev_code; + dict[next_code].chr = first_char; + dict[next_code].used = 1; + + /* Find next free slot */ + next_code++; + while(next_code < SHRINK_MAX_CODES && dict[next_code].used) next_code++; + } + + prev_code = code; + continue; + } + + /* Normal case: decode the string */ + len = shrink_decode_string(dict, code, stack, SHRINK_MAX_CODES); + + if(len <= 0) + { + free(dict); + return -1; + } + + /* Output in forward order (stack is reversed) */ + for(int i = len - 1; i >= 0; i--) + { + if(out_pos < out_size) out_buf[out_pos++] = stack[i]; + } + + /* Add new dictionary entry: prev_code + first char of current string */ + if(next_code < SHRINK_MAX_CODES) + { + dict[next_code].parent = (int16_t)prev_code; + dict[next_code].chr = stack[len - 1]; /* first char of decoded string */ + dict[next_code].used = 1; + + /* Find next free slot (partial clear may have freed interior slots) */ + next_code++; + while(next_code < SHRINK_MAX_CODES && dict[next_code].used) next_code++; + } + + prev_code = code; + } + + free(dict); + *out_len = out_pos; + return 0; +} diff --git a/zip/shrink.h b/zip/shrink.h new file mode 100644 index 0000000..91c34b7 --- /dev/null +++ b/zip/shrink.h @@ -0,0 +1,39 @@ +/* + * 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_COMPRESSION_NATIVE_ZIP_SHRINK_H +#define AARU_COMPRESSION_NATIVE_ZIP_SHRINK_H + +#include +#include + +/** + * Decompress ZIP Shrink (method 1) data. + * + * ZIP Shrink uses a modified LZW algorithm with variable code sizes (9-13 bits), + * LSB-first bit reading, and control code 256 for code width growth and partial clear. + * + * @param in_buf Compressed input buffer + * @param in_len Length of compressed input + * @param out_buf Decompressed output buffer (must be pre-allocated) + * @param out_len On entry: size of output buffer. On exit: bytes actually written. + * @return 0 on success, non-zero on error + */ +int zip_shrink_decompress(const uint8_t *in_buf, size_t in_len, uint8_t *out_buf, size_t *out_len); + +#endif /* AARU_COMPRESSION_NATIVE_ZIP_SHRINK_H */ diff --git a/zip/zip.c b/zip/zip.c new file mode 100644 index 0000000..37a7f51 --- /dev/null +++ b/zip/zip.c @@ -0,0 +1,335 @@ +/* + * 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 "zip.h" + +#include +#include + +/* PPMd includes */ +#include "../ppmd/SubAllocatorVariantI.h" +#include "../ppmd/VariantI.h" + +/* WavPack includes */ +#include "../wavpack/wavpack.h" + +/* WinZipJPEG includes */ +#include "../winzipjpeg/Decompressor.h" + +/* ============== PPMd Wrapper ============== */ + +typedef struct +{ + const uint8_t *data; + size_t size; + size_t pos; +} PPMdBufferContext; + +static int ppmd_read_byte(void *context) +{ + PPMdBufferContext *ctx = (PPMdBufferContext *)context; + + if(ctx->pos >= ctx->size) return -1; + + return ctx->data[ctx->pos++]; +} + +int zip_ppmd_decompress(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer, size_t src_size, int max_order, + int sub_alloc_size, int restoration) +{ + PPMdSubAllocatorVariantI *alloc; + PPMdModelVariantI model; + PPMdBufferContext ctx; + + if(!dst_buffer || !src_buffer) return -1; + + ctx.data = src_buffer; + ctx.size = src_size; + ctx.pos = 0; + + alloc = CreateSubAllocatorVariantI(sub_alloc_size); + + if(!alloc) return -1; + + StartPPMdModelVariantI(&model, ppmd_read_byte, &ctx, alloc, max_order, restoration); + + for(size_t i = 0; i < dst_size; i++) + { + int byte = NextPPMdVariantIByte(&model); + + if(byte < 0) + { + FreeSubAllocatorVariantI(alloc); + return -1; + } + + dst_buffer[i] = (uint8_t)byte; + } + + FreeSubAllocatorVariantI(alloc); + return 0; +} + +/* ============== WavPack Wrapper ============== */ + +typedef struct +{ + const uint8_t *data; + size_t size; + size_t pos; + int pushback; + int has_pushback; +} WavpackBufferContext; + +static int32_t wavpack_read_bytes(void *id, void *data, int32_t bcount) +{ + WavpackBufferContext *ctx = (WavpackBufferContext *)id; + int32_t available = (int32_t)(ctx->size - ctx->pos); + int32_t to_read = bcount < available ? bcount : available; + + if(ctx->has_pushback && to_read > 0) + { + ((uint8_t *)data)[0] = (uint8_t)ctx->pushback; + ctx->has_pushback = 0; + memcpy((uint8_t *)data + 1, ctx->data + ctx->pos, to_read - 1); + ctx->pos += to_read - 1; + } + else + { + memcpy(data, ctx->data + ctx->pos, to_read); + ctx->pos += to_read; + } + + return to_read; +} + +static uint32_t wavpack_get_pos(void *id) +{ + WavpackBufferContext *ctx = (WavpackBufferContext *)id; + return (uint32_t)ctx->pos; +} + +static int wavpack_set_pos_abs(void *id, uint32_t pos) +{ + WavpackBufferContext *ctx = (WavpackBufferContext *)id; + + if(pos > ctx->size) return -1; + + ctx->pos = pos; + ctx->has_pushback = 0; + return 0; +} + +static int wavpack_set_pos_rel(void *id, int32_t delta, int mode) +{ + WavpackBufferContext *ctx = (WavpackBufferContext *)id; + int64_t newpos; + + switch(mode) + { + case 0: /* SEEK_SET */ + newpos = delta; + break; + case 1: /* SEEK_CUR */ + newpos = (int64_t)ctx->pos + delta; + break; + case 2: /* SEEK_END */ + newpos = (int64_t)ctx->size + delta; + break; + default: + return -1; + } + + if(newpos < 0 || (size_t)newpos > ctx->size) return -1; + + ctx->pos = (size_t)newpos; + ctx->has_pushback = 0; + return 0; +} + +static int wavpack_push_back_byte(void *id, int c) +{ + WavpackBufferContext *ctx = (WavpackBufferContext *)id; + ctx->pushback = c; + ctx->has_pushback = 1; + return c; +} + +static uint32_t wavpack_get_length(void *id) +{ + WavpackBufferContext *ctx = (WavpackBufferContext *)id; + return (uint32_t)ctx->size; +} + +static int wavpack_can_seek(void *id) +{ + (void)id; + return 1; +} + +int zip_wavpack_decompress(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, size_t src_size, + uint32_t num_samples, int bits_per_sample, int num_channels) +{ + WavpackBufferContext ctx; + WavpackStreamReader reader; + WavpackContext *wpc; + char error[80]; + int bytes_per_sample; + int32_t *sample_buf; + uint32_t decoded; + size_t out_pos; + + if(!dst_buffer || !dst_size || !src_buffer) return -1; + + ctx.data = src_buffer; + ctx.size = src_size; + ctx.pos = 0; + ctx.pushback = 0; + ctx.has_pushback = 0; + + reader.read_bytes = wavpack_read_bytes; + reader.get_pos = wavpack_get_pos; + reader.set_pos_abs = wavpack_set_pos_abs; + reader.set_pos_rel = wavpack_set_pos_rel; + reader.push_back_byte = wavpack_push_back_byte; + reader.get_length = wavpack_get_length; + reader.can_seek = wavpack_can_seek; + reader.write_bytes = NULL; + + wpc = WavpackOpenFileInputEx(&reader, &ctx, NULL, error, 0, 0); + + if(!wpc) return -1; + + bytes_per_sample = (bits_per_sample + 7) / 8; + + /* Allocate sample buffer for one block of samples */ + sample_buf = (int32_t *)malloc(num_samples * num_channels * sizeof(int32_t)); + + if(!sample_buf) + { + WavpackCloseFile(wpc); + return -1; + } + + decoded = WavpackUnpackSamples(wpc, sample_buf, num_samples); + out_pos = 0; + + /* Compact int32 samples to actual byte width */ + for(uint32_t i = 0; i < decoded * (uint32_t)num_channels; i++) + { + int32_t sample = sample_buf[i]; + + for(int b = 0; b < bytes_per_sample; b++) + { + if(out_pos < *dst_size) dst_buffer[out_pos++] = (uint8_t)(sample & 0xFF); + + sample >>= 8; + } + } + + *dst_size = out_pos; + + free(sample_buf); + WavpackCloseFile(wpc); + return 0; +} + +/* ============== WinZipJPEG Wrapper ============== */ + +typedef struct +{ + const uint8_t *data; + size_t size; + size_t pos; +} WinZipJPEGBufferContext; + +static size_t winzipjpeg_read(void *context, uint8_t *buffer, size_t length) +{ + WinZipJPEGBufferContext *ctx = (WinZipJPEGBufferContext *)context; + size_t remaining = ctx->size - ctx->pos; + size_t to_read = length < remaining ? length : remaining; + + memcpy(buffer, ctx->data + ctx->pos, to_read); + ctx->pos += to_read; + + return to_read; +} + +int zip_winzipjpeg_decompress(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, size_t src_size) +{ + WinZipJPEGBufferContext ctx; + WinZipJPEGDecompressor *decompressor; + size_t out_pos = 0; + size_t out_size = *dst_size; + int err; + + if(!dst_buffer || !dst_size || !src_buffer) return -1; + + ctx.data = src_buffer; + ctx.size = src_size; + ctx.pos = 0; + + decompressor = AllocWinZipJPEGDecompressor(winzipjpeg_read, &ctx); + + if(!decompressor) return -1; + + err = ReadWinZipJPEGHeader(decompressor); + + if(err != WinZipJPEGNoError) + { + FreeWinZipJPEGDecompressor(decompressor); + return err; + } + + /* Process bundles */ + while(!IsFinalWinZipJPEGBundle(decompressor)) + { + err = ReadNextWinZipJPEGBundle(decompressor); + + if(err != WinZipJPEGNoError) break; + + /* Copy metadata bytes */ + uint32_t meta_len = WinZipJPEGBundleMetadataLength(decompressor); + uint8_t *meta = WinZipJPEGBundleMetadataBytes(decompressor); + + for(uint32_t i = 0; i < meta_len && out_pos < out_size; i++) dst_buffer[out_pos++] = meta[i]; + + /* Process slices */ + while(AreMoreWinZipJPEGSlicesAvailable(decompressor)) + { + err = ReadNextWinZipJPEGSlice(decompressor); + + if(err != WinZipJPEGNoError) break; + + /* Encode blocks to output buffer */ + while(AreMoreWinZipJPEGBytesAvailable(decompressor) && out_pos < out_size) + { + size_t written = EncodeWinZipJPEGBlocksToBuffer(decompressor, dst_buffer + out_pos, out_size - out_pos); + out_pos += written; + + if(written == 0) break; + } + } + + if(err != WinZipJPEGNoError) break; + } + + *dst_size = out_pos; + FreeWinZipJPEGDecompressor(decompressor); + return (err == WinZipJPEGNoError) ? 0 : err; +} diff --git a/zip/zip.h b/zip/zip.h new file mode 100644 index 0000000..23c090e --- /dev/null +++ b/zip/zip.h @@ -0,0 +1,71 @@ +/* + * 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_COMPRESSION_NATIVE_ZIP_ZIP_H +#define AARU_COMPRESSION_NATIVE_ZIP_ZIP_H + +#include +#include + +#include "deflate64.h" +#include "implode.h" +#include "reduce.h" +#include "shrink.h" + +/** + * Decompress ZIP PPMd (method 98, variant I) data. + * + * @param dst_buffer Output buffer + * @param dst_size Size of output buffer / bytes to decompress + * @param src_buffer Compressed input buffer + * @param src_size Size of compressed input + * @param max_order PPMd model order (1-16) + * @param sub_alloc_size Sub-allocator memory size in bytes + * @param restoration Model restoration method (0=restart, 1=cutoff, 2=freeze) + * @return 0 on success, non-zero on error + */ +int zip_ppmd_decompress(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer, size_t src_size, int max_order, + int sub_alloc_size, int restoration); + +/** + * Decompress WinZip WavPack (ZIP method 97) data. + * + * @param dst_buffer Output buffer for raw audio samples + * @param dst_size On entry: size of output buffer. On exit: bytes written. + * @param src_buffer Compressed WavPack data + * @param src_size Size of compressed input + * @param num_samples Number of audio samples to decode + * @param bits_per_sample Bits per sample (8, 16, 24, 32) + * @param num_channels Number of audio channels + * @return 0 on success, non-zero on error + */ +int zip_wavpack_decompress(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, size_t src_size, + uint32_t num_samples, int bits_per_sample, int num_channels); + +/** + * Decompress WinZip JPEG (ZIP method 96) data. + * + * @param dst_buffer Output buffer for reconstructed JPEG data + * @param dst_size On entry: size of output buffer. On exit: bytes written. + * @param src_buffer Compressed WinZipJPEG data + * @param src_size Size of compressed input + * @return 0 on success, non-zero on error + */ +int zip_winzipjpeg_decompress(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, size_t src_size); + +#endif /* AARU_COMPRESSION_NATIVE_ZIP_ZIP_H */