summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Buren <braewoods+rb@braewoods.net>2021-08-05 15:57:07 +0000
committerWilliam Wilgus <me.theuser@yahoo.com>2021-08-07 03:03:27 +0000
commit60933d98c629ee96cb09bbaafe4be7cfa50358ed (patch)
tree5f726d6e24a63861ceef8069e8fe4a08f7383402
parent603e749c1dd61c9d396a6a5521ff626d923b5692 (diff)
downloadrockbox-60933d98c629ee96cb09bbaafe4be7cfa50358ed.tar.gz
rockbox-60933d98c629ee96cb09bbaafe4be7cfa50358ed.tar.bz2
rockbox-60933d98c629ee96cb09bbaafe4be7cfa50358ed.zip
inflate: import initial module for deflate decompression
This will eventually be used by the ZIP module and other things that support DEFLATE based streams. Change-Id: I4acc9561eb56c9c368d1defab9c14e0454d105e1
-rw-r--r--firmware/SOURCES1
-rw-r--r--firmware/common/inflate.c759
-rw-r--r--firmware/include/inflate.h46
3 files changed, 806 insertions, 0 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 71a11429cd..8d7232f206 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -270,6 +270,7 @@ common/unicode.c
common/vuprintf.c
common/zip.c
common/adler32.c
+common/inflate.c
/* Display */
scroll_engine.c
diff --git a/firmware/common/inflate.c b/firmware/common/inflate.c
new file mode 100644
index 0000000000..26fd191690
--- /dev/null
+++ b/firmware/common/inflate.c
@@ -0,0 +1,759 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by James Buren (libflate adaptations for RockBox)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* Copyright 2021 Plan 9 Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "inflate.h"
+#include <stdbool.h>
+#include "adler32.h"
+#include "crc32.h"
+#include "system.h"
+
+enum {
+ INFLATE_BUFFER_SIZE = 32768,
+ INFLATE_SYMBOL_MAX = 288,
+ INFLATE_OFFSET_MAX = 32,
+ INFLATE_CODELEN_MAX = 19,
+ INFLATE_HUFF_BITS = 17,
+ INFLATE_FLAT_BITS = 7,
+ INFLATE_SYMBOL_BITS = 7,
+ INFLATE_OFFSET_BITS = 6,
+ INFLATE_CODELEN_BITS = 6,
+};
+
+struct inflate_huff {
+ uint32_t max_bits;
+ uint32_t min_bits;
+ uint32_t flat_mask;
+ uint32_t flat[1 << INFLATE_FLAT_BITS];
+ uint32_t max_code[INFLATE_HUFF_BITS];
+ uint32_t last[INFLATE_HUFF_BITS];
+ uint32_t decode[INFLATE_SYMBOL_MAX];
+};
+
+struct inflate {
+ uint8_t in[INFLATE_BUFFER_SIZE];
+ uint8_t out[INFLATE_BUFFER_SIZE];
+ union {
+ struct {
+ uint8_t len[INFLATE_SYMBOL_MAX];
+ uint8_t off[INFLATE_OFFSET_MAX];
+ };
+ uint8_t combo[INFLATE_SYMBOL_MAX + INFLATE_OFFSET_MAX];
+ };
+ uint8_t clen[INFLATE_CODELEN_MAX];
+ struct inflate_huff lentab;
+ struct inflate_huff offtab;
+ struct inflate_huff clentab;
+ uint32_t bits[INFLATE_HUFF_BITS];
+ uint32_t codes[INFLATE_HUFF_BITS];
+};
+
+#define INFLATE_FILL(E) do { \
+ const uint32_t _size = read(is, sizeof(it->in), rctx); \
+ if (_size == 0) { \
+ rv = (E); \
+ goto bail; \
+ } \
+ ip = is; \
+ ie = is + _size; \
+} while (0)
+
+#define INFLATE_FLUSH(E) do { \
+ const uint32_t _size = (op - os); \
+ if (write(os, _size, wctx) != _size) { \
+ rv = (E); \
+ goto bail; \
+ } \
+ flushed = true; \
+ if (st == INFLATE_ZLIB) \
+ chksum = adler_32(os, _size, chksum); \
+ else if (st == INFLATE_GZIP) \
+ chksum = crc_32r(os, _size, chksum); \
+ op = os; \
+} while (0)
+
+#define INFLATE_GET_BYTE(E,C) do { \
+ if (ip == ie) \
+ INFLATE_FILL(E); \
+ (C) = ip[0]; \
+ ++ip; \
+} while (0)
+
+#define INFLATE_PUT_BYTE(E,B) do { \
+ if (op == oe) \
+ INFLATE_FLUSH(E); \
+ op[0] = (B); \
+ ++op; \
+} while (0)
+
+#define INFLATE_FILL_BITS(E,N) do { \
+ while ((N) > nbits) { \
+ uint8_t _byte; \
+ INFLATE_GET_BYTE(E, _byte); \
+ sreg |= (_byte << nbits); \
+ nbits += 8; \
+ } \
+} while (0)
+
+#define INFLATE_CONSUME_BITS(N) do { \
+ sreg >>= (N); \
+ nbits -= (N); \
+} while (0)
+
+#define INFLATE_EXTRACT_BITS(N,B) do { \
+ (B) = (sreg & ((1 << (N)) - 1)); \
+ INFLATE_CONSUME_BITS(N); \
+} while (0)
+
+#define INFLATE_CHECK_LENGTH(E,N) do { \
+ if ((N) > (ie - ip)) { \
+ rv = (E); \
+ goto bail; \
+ } \
+} while (0)
+
+#define INFLATE_REVERSE(C,B) ({ \
+ uint32_t _c = (C); \
+ _c <<= (16 - (B)); \
+ ((revtab[_c >> 8]) | (revtab[_c & 0xff] << 8)); \
+})
+
+#define INFLATE_DECODE(E,H) ({ \
+ __label__ _found; \
+ uint32_t _c = (H)->flat[sreg & (H)->flat_mask]; \
+ uint32_t _b = (_c & 0xff); \
+ uint32_t _code; \
+ if (_b == 0xff) { \
+ for (_b = (_c >> 8); _b <= (H)->max_bits; _b++) { \
+ _c = (revtab[sreg & 0xff] << 8); \
+ _c |= (revtab[(sreg >> 8) & 0xff]); \
+ _c >>= (16 - _b); \
+ if (_c <= (H)->max_code[_b]) { \
+ _code = (H)->decode[(H)->last[_b] - _c]; \
+ goto _found; \
+ } \
+ } \
+ rv = (E); \
+ goto bail; \
+ } \
+ _code = (_c >> 8); \
+_found: \
+ INFLATE_CONSUME_BITS(_b); \
+ _code; \
+})
+
+static int inflate_blocks(struct inflate* it, int st, inflate_reader read, void* rctx, inflate_writer write, void* wctx) {
+ int rv = 0;
+ uint8_t* is = it->in;
+ uint8_t* ip;
+ uint8_t* ie;
+ uint8_t* os = it->out;
+ uint8_t* op = os;
+ uint8_t* oe = os + sizeof(it->out);
+ bool flushed = false;
+ uint32_t chksum;
+ uint32_t nbits = 0;
+ uint32_t sreg = 0;
+ uint32_t i;
+ uint32_t j;
+ bool final;
+ uint8_t type;
+
+ INFLATE_FILL(-1);
+
+ if (st == INFLATE_ZLIB) {
+ uint16_t header;
+
+ INFLATE_CHECK_LENGTH(-2, 2);
+
+ header = (ip[0] << 8 | ip[1]);
+ ip += 2;
+
+ if ((header % 31) != 0) {
+ rv = -3;
+ goto bail;
+ }
+
+ if (((header & 0xf000) >> 12) > 7) {
+ rv = -4;
+ goto bail;
+ }
+
+ if (((header & 0x0f00) >> 8) != 8) {
+ rv = -5;
+ goto bail;
+ }
+
+ if (((header & 0x0020) >> 5) != 0) {
+ rv = -6;
+ goto bail;
+ }
+
+ chksum = 1;
+ } else if (st == INFLATE_GZIP) {
+ uint8_t flg;
+
+ INFLATE_CHECK_LENGTH(-7, 10);
+
+ if (ip[0] != 0x1f || ip[1] != 0x8b) {
+ rv = -8;
+ goto bail;
+ }
+
+ if (ip[2] != 8) {
+ rv = -9;
+ goto bail;
+ }
+
+ flg = ip[3];
+
+ if ((flg & 0xe0) != 0) {
+ rv = -10;
+ goto bail;
+ }
+
+ ip += 10;
+ chksum = 0xffffffff;
+
+ if ((flg & 0x04) != 0) {
+ uint16_t xlen;
+
+ INFLATE_CHECK_LENGTH(-11, 2);
+
+ xlen = (ip[0] | ip[1] << 8);
+ ip += 2;
+
+ while (xlen >= (ie - ip)) {
+ xlen -= (ie - ip);
+
+ if ((flg & 0x02) != 0)
+ chksum = crc_32r(is, ie - is, chksum);
+
+ INFLATE_FILL(-12);
+ }
+
+ ip += xlen;
+ }
+
+ if ((flg & 0x08) != 0) {
+ while (ip++[0] != '\0') {
+ if (ip == ie) {
+ if ((flg & 0x02) != 0)
+ chksum = crc_32r(is, ie - is, chksum);
+
+ INFLATE_FILL(-13);
+ }
+ }
+ }
+
+ if ((flg & 0x10) != 0) {
+ while (ip++[0] != '\0') {
+ if (ip == ie) {
+ if ((flg & 0x02) != 0)
+ chksum = crc_32r(is, ie - is, chksum);
+
+ INFLATE_FILL(-14);
+ }
+ }
+ }
+
+ if ((flg & 0x02) != 0) {
+ uint16_t crc16;
+
+ INFLATE_CHECK_LENGTH(-15, 2);
+
+ crc16 = (ip[0] | ip[1] << 8);
+ chksum = crc_32r(is, ip - is, chksum);
+ chksum &= 0xffff;
+ chksum ^= 0xffff;
+
+ if (crc16 != chksum) {
+ rv = -16;
+ goto bail;
+ }
+
+ ip += 2;
+ }
+
+ chksum = 0xffffffff;
+ } else {
+ chksum = 0;
+ }
+
+ do {
+ INFLATE_FILL_BITS(-17, 3);
+ final = (sreg & 0x01);
+ type = ((sreg & 0x06) >> 1);
+ INFLATE_CONSUME_BITS(3);
+
+ if (type == 0) {
+ uint8_t header[4];
+ uint32_t len;
+ uint32_t clen;
+
+ INFLATE_CONSUME_BITS(nbits & 0x07);
+
+ for (i = 0; i < 4; i++) {
+ if (nbits != 0)
+ INFLATE_EXTRACT_BITS(8, header[i]);
+ else
+ INFLATE_GET_BYTE(-18, header[i]);
+ }
+
+ len = (header[0] | (header[1] << 8));
+ clen = (header[2] | (header[3] << 8)) ^ 0xffff;
+
+ if (len != clen) {
+ rv = -19;
+ goto bail;
+ }
+
+ while (len != 0) {
+ if (ip == ie)
+ INFLATE_FILL(-20);
+
+ if (op == oe)
+ INFLATE_FLUSH(-21);
+
+ j = MIN(len, MIN((uint32_t) (ie - ip), (uint32_t) (oe - op)));
+ for (i = 0; i < j; i++)
+ op[i] = ip[i];
+
+ len -= j;
+ ip += j;
+ op += j;
+ }
+ } else if (type == 3) {
+ rv = -22;
+ goto bail;
+ } else {
+ static const uint8_t revtab[256] =
+ {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
+ };
+ uint8_t* tab[3];
+ uint32_t tab_lens[3];
+ uint32_t tab_bits[3];
+ struct inflate_huff* tab_huffs[3] = { &it->lentab, &it->offtab, &it->clentab, };
+ uint32_t c;
+ uint32_t k;
+
+ if (type == 2) {
+ static const uint32_t min_tab_sizes[3] = { 257, 1, 4, };
+ static const uint32_t min_tab_bits[3] = { 5, 5, 4, };
+ static const uint32_t clen_order[INFLATE_CODELEN_MAX] = {
+ 16, 17, 18, 0, 8,
+ 7, 9, 6, 10, 5,
+ 11, 4, 12, 3, 13,
+ 2, 14, 1, 15,
+ };
+
+ INFLATE_FILL_BITS(-23, 14);
+ for (i = 0; i < 3; i++) {
+ INFLATE_EXTRACT_BITS(min_tab_bits[i], tab_lens[i]);
+ tab_lens[i] += min_tab_sizes[i];
+ }
+
+ tab[2] = it->clen;
+ for (i = 0; i < INFLATE_CODELEN_MAX; i++)
+ tab[2][i] = 0;
+ for (i = 0; i < tab_lens[2]; i++) {
+ INFLATE_FILL_BITS(-24, 3);
+ INFLATE_EXTRACT_BITS(3, tab[2][clen_order[i]]);
+ }
+
+ tab[0] = it->combo;
+ tab_bits[0] = INFLATE_SYMBOL_BITS;
+
+ tab[1] = tab[0] + tab_lens[0];
+ tab_bits[1] = INFLATE_OFFSET_BITS;
+
+ tab_lens[2] = INFLATE_CODELEN_MAX;
+ tab_bits[2] = INFLATE_CODELEN_BITS;
+ } else {
+ tab[0] = it->len;
+ for (i = 0; i < 144; i++)
+ tab[0][i] = 8;
+ for (; i < 256; i++)
+ tab[0][i] = 9;
+ for (; i < 280; i++)
+ tab[0][i] = 7;
+ for (; i < INFLATE_SYMBOL_MAX; i++)
+ tab[0][i] = 8;
+ tab_lens[0] = INFLATE_SYMBOL_MAX;
+ tab_bits[0] = INFLATE_FLAT_BITS;
+
+ tab[1] = it->off;
+ for (i = 0; i < INFLATE_OFFSET_MAX; i++)
+ tab[1][i] = 5;
+ tab_lens[1] = INFLATE_OFFSET_MAX;
+ tab_bits[1] = INFLATE_FLAT_BITS;
+ }
+
+ for(; type != 0xff; --type) {
+ const uint8_t* hb = tab[type];
+ uint32_t hb_len = tab_lens[type];
+ uint32_t flat_bits = tab_bits[type];
+ struct inflate_huff* h = tab_huffs[type];
+ uint32_t* bits = it->bits;
+ uint32_t* codes = it->codes;
+ uint32_t max_bits = 0;
+ uint32_t min_bits = INFLATE_HUFF_BITS + 1;
+ uint32_t min_code;
+ uint32_t b;
+ uint32_t code;
+ uint32_t fc;
+ uint32_t ec;
+
+ for (i = 0; i < INFLATE_HUFF_BITS; i++)
+ bits[i] = 0;
+
+ for (i = 0; i < hb_len; i++) {
+ b = hb[i];
+
+ if (b != 0) {
+ bits[b]++;
+
+ if (b < min_bits)
+ min_bits = b;
+
+ if (b > max_bits)
+ max_bits = b;
+ }
+ }
+
+ if (max_bits == 0) {
+ h->flat_mask = h->min_bits = h->max_bits = 0;
+ goto table_done;
+ }
+
+ h->max_bits = max_bits;
+
+ for (b = c = code = 0; b <= max_bits; b++) {
+ h->last[b] = c;
+ c += bits[b];
+
+ min_code = (code << 1);
+ codes[b] = min_code;
+ code = (min_code + bits[b]);
+
+ if (code > (1U << b)) {
+ rv = -25;
+ goto bail;
+ }
+
+ h->max_code[b] = (code - 1);
+ h->last[b] += (code - 1);
+ }
+
+ if (flat_bits > max_bits)
+ flat_bits = max_bits;
+
+ h->flat_mask = ((1 << flat_bits) - 1);
+
+ if (min_bits > flat_bits)
+ min_bits = flat_bits;
+
+ h->min_bits = min_bits;
+
+ for (i = 0, b = (1 << flat_bits); i < b; i++)
+ h->flat[i] = 0xffffffff;
+
+ for (b = max_bits; b > flat_bits; b--) {
+ code = h->max_code[b];
+
+ if (code == 0xffffffff)
+ break;
+
+ min_code = ((code + 1) - bits[b]);
+ min_code >>= (b - flat_bits);
+ code >>= (b - flat_bits);
+
+ for (; min_code <= code; min_code++)
+ h->flat[INFLATE_REVERSE(min_code, flat_bits)] = ((b << 8) | 0xff);
+ }
+
+ for (i = 0; i < hb_len; i++) {
+ b = hb[i];
+
+ if (b == 0)
+ continue;
+
+ c = codes[b]++;
+
+ if (b <= flat_bits) {
+ code = ((i << 8) | b);
+ ec = ((c + 1) << (flat_bits - b));
+
+ if (ec > (1U << flat_bits)) {
+ rv = -26;
+ goto bail;
+ }
+
+ for (fc = (c << (flat_bits - b)); fc < ec; fc++)
+ h->flat[INFLATE_REVERSE(fc, flat_bits)] = code;
+ }
+
+ if (b > min_bits) {
+ c = h->last[b] - c;
+
+ if (c >= hb_len) {
+ rv = -27;
+ goto bail;
+ }
+
+ h->decode[c] = i;
+ }
+ }
+
+ table_done:
+
+ if (type == 2) {
+ static const uint32_t bits[3] = { 2, 3, 7, };
+ static const uint32_t bases[3] = { 3, 3, 11, };
+
+ for (i = 0, j = tab_lens[0] + tab_lens[1]; i < j; ) {
+ uint32_t len;
+ uint8_t byte;
+
+ INFLATE_FILL_BITS(-28, h->max_bits);
+
+ c = INFLATE_DECODE(-29, h);
+
+ if (c < 16) {
+ tab[0][i++] = c;
+ continue;
+ }
+
+ c -= 16;
+
+ if (c == 0 && i == 0) {
+ rv = -30;
+ goto bail;
+ }
+
+ INFLATE_FILL_BITS(-31, bits[c]);
+ INFLATE_EXTRACT_BITS(bits[c], len);
+ len += bases[c];
+
+ if ((i + len) > j) {
+ rv = -32;
+ goto bail;
+ }
+
+ byte = ((c == 0) ? tab[0][i - 1] : 0);
+ for (k = 0; k < len; k++)
+ tab[0][i + k] = byte;
+
+ i += len;
+ }
+ }
+ }
+
+ while (1) {
+ static const uint32_t lenbase[INFLATE_SYMBOL_MAX - 257] = {
+ 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, 258, 0, 0,
+ };
+ static const uint32_t lenextra[INFLATE_SYMBOL_MAX - 257] = {
+ 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, 0, 0, 0,
+ };
+ static const uint32_t offbase[INFLATE_OFFSET_MAX] = {
+ 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, 0, 0,
+ };
+ static const uint32_t offextra[INFLATE_OFFSET_MAX] = {
+ 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, 0, 0,
+ };
+ uint32_t len;
+ uint32_t off;
+ uint8_t* cp;
+
+ INFLATE_FILL_BITS(-33, tab_huffs[0]->max_bits);
+ c = INFLATE_DECODE(-34, tab_huffs[0]);
+
+ if (c < 256) {
+ INFLATE_PUT_BYTE(-35, c);
+ continue;
+ }
+
+ if (c == 256)
+ break;
+
+ if (c > 285) {
+ rv = -36;
+ goto bail;
+ }
+
+ c -= 257;
+ INFLATE_FILL_BITS(-37, lenextra[c]);
+ INFLATE_EXTRACT_BITS(lenextra[c], len);
+ len += lenbase[c];
+
+ INFLATE_FILL_BITS(-38, tab_huffs[1]->max_bits);
+ c = INFLATE_DECODE(-39, tab_huffs[1]);
+
+ if (c > 29) {
+ rv = -40;
+ goto bail;
+ }
+
+ INFLATE_FILL_BITS(-41, offextra[c]);
+ INFLATE_EXTRACT_BITS(offextra[c], off);
+ off += offbase[c];
+
+ cp = op - off;
+ if (cp < os) {
+ if (!flushed) {
+ rv = -42;
+ goto bail;
+ }
+
+ cp += sizeof(it->out);
+ }
+
+ while (len != 0) {
+ if (op == oe)
+ INFLATE_FLUSH(-43);
+
+ if (cp == oe)
+ cp = os;
+
+ j = MIN(len, MIN((uint32_t) (oe - op), (uint32_t) (oe - cp)));
+
+ for (i = 0; i < j; i++)
+ op[i] = cp[i];
+
+ op += j;
+ cp += j;
+ len -= j;
+ }
+ }
+ }
+ } while (!final);
+
+ INFLATE_FLUSH(-44);
+
+ if (st != INFLATE_RAW) {
+ uint8_t header[4];
+ uint32_t chksum2;
+
+ INFLATE_CONSUME_BITS(nbits & 0x07);
+
+ for (i = 0; i < 4; i++) {
+ if (nbits != 0)
+ INFLATE_EXTRACT_BITS(8, header[i]);
+ else
+ INFLATE_GET_BYTE(-45, header[i]);
+ }
+
+ if (st == INFLATE_ZLIB) {
+ chksum2 = ((header[0] << 24) | (header[1] << 16) | (header[2] << 8) | (header[3] << 0));
+
+ if (chksum != chksum2) {
+ rv = -46;
+ goto bail;
+ }
+ } else if (st == INFLATE_GZIP) {
+ chksum2 = ((header[3] << 24) | (header[2] << 16) | (header[1] << 8) | (header[0] << 0));
+
+ if (~chksum != chksum2) {
+ rv = -47;
+ goto bail;
+ }
+ }
+ }
+
+bail:
+ return rv;
+}
+
+const uint32_t inflate_size = sizeof(struct inflate);
+const uint32_t inflate_align = _Alignof(struct inflate);
+
+int inflate(struct inflate* it, int st, inflate_reader read, void* rctx, inflate_writer write, void* wctx) {
+ if (it == NULL || read == NULL || write == NULL || st < 0 || st > 2 || (((uintptr_t) it) & (_Alignof(struct inflate) - 1)) != 0)
+ return -48;
+
+ return inflate_blocks(it, st, read, rctx, write, wctx);
+}
diff --git a/firmware/include/inflate.h b/firmware/include/inflate.h
new file mode 100644
index 0000000000..b0685e8fbb
--- /dev/null
+++ b/firmware/include/inflate.h
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by James Buren (libflate adaptations for RockBox)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _INFLATE_H_
+#define _INFLATE_H_
+
+#include <stdint.h>
+
+enum {
+ INFLATE_RAW,
+ INFLATE_ZLIB,
+ INFLATE_GZIP,
+};
+
+struct inflate;
+
+typedef uint32_t inflate_reader(void* block, uint32_t block_size, void* ctx);
+typedef uint32_t inflate_writer(const void* block, uint32_t block_size, void* ctx);
+
+extern const uint32_t inflate_size;
+extern const uint32_t inflate_align;
+
+// 'it' must be allocated by the caller. it must be at least 'inflate_size' bytes
+// and aligned at 'inflate_align' bytes. 'st' is the type of stream to decompress;
+// see above enum for possible options.
+int inflate(struct inflate* it, int st, inflate_reader read, void* rctx, inflate_writer write, void* wctx);
+
+#endif