From f26e66c4f0e1569bd8ba50f8caebb24fbefde31e Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Fri, 8 May 2026 22:03:21 +0200 Subject: [PATCH 1/5] Vendor uzlib for firmware inflater work --- lib/uzlib/LICENSE | 65 ++++ lib/uzlib/README.md | 18 + lib/uzlib/library.json | 10 + lib/uzlib/src/adler32.c | 78 +++++ lib/uzlib/src/crc32.c | 63 ++++ lib/uzlib/src/defl_static.h | 37 ++ lib/uzlib/src/tinf.h | 3 + lib/uzlib/src/tinf_compat.h | 9 + lib/uzlib/src/tinflate.c | 674 ++++++++++++++++++++++++++++++++++++ lib/uzlib/src/tinfzlib.c | 66 ++++ lib/uzlib/src/uzlib.h | 173 +++++++++ lib/uzlib/src/uzlib_conf.h | 32 ++ platformio.ini | 1 - 13 files changed, 1228 insertions(+), 1 deletion(-) create mode 100644 lib/uzlib/LICENSE create mode 100644 lib/uzlib/README.md create mode 100644 lib/uzlib/library.json create mode 100644 lib/uzlib/src/adler32.c create mode 100644 lib/uzlib/src/crc32.c create mode 100644 lib/uzlib/src/defl_static.h create mode 100644 lib/uzlib/src/tinf.h create mode 100644 lib/uzlib/src/tinf_compat.h create mode 100644 lib/uzlib/src/tinflate.c create mode 100644 lib/uzlib/src/tinfzlib.c create mode 100644 lib/uzlib/src/uzlib.h create mode 100644 lib/uzlib/src/uzlib_conf.h diff --git a/lib/uzlib/LICENSE b/lib/uzlib/LICENSE new file mode 100644 index 0000000..c96e382 --- /dev/null +++ b/lib/uzlib/LICENSE @@ -0,0 +1,65 @@ +Introduction +------------ + +uzlib as a whole is licensed under the terms of Zlib license, listed in +the next section. It consists of substantial works of individuals whose +copyrights listed in the next session. Some portions of the uzlib codebase +originally were licensed under different license(s), however compatible +with the Zlib license. Such license(s) are listed at the end of this file. + +License +------- + +uzlib - Deflate/Zlib-compatible LZ77 compression/decompression library + +Copyright (c) 2003 Joergen Ibsen +Copyright (c) 1997-2014 Simon Tatham +Copyright (c) 2014-2020 Paul Sokolovsky + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. + +------- +Original license for src/defl_static.c: + +PuTTY is copyright 1997-2014 Simon Tatham. + +Portions copyright Robert de Bath, Joris van Rantwijk, Delian +Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, +Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus +Kuhn, Colin Watson, and CORE SDI S.A. + +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 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. diff --git a/lib/uzlib/README.md b/lib/uzlib/README.md new file mode 100644 index 0000000..af036f3 --- /dev/null +++ b/lib/uzlib/README.md @@ -0,0 +1,18 @@ +# uzlib + +Vendored copy of `pfalcon/uzlib` for OpenDisplay firmware. + +Upstream: https://github.com/pfalcon/uzlib +Commit: `6d60d651a4499a64f2e5b21b4cc08d98cb84b5c1` + +Only the files needed for current zlib/DEFLATE inflate usage are included: + +- `tinflate.c` +- `tinfzlib.c` +- `adler32.c` +- `crc32.c` +- public headers required by those sources + +Local changes should be kept in this folder so the firmware build is +self-contained and future streaming/nonblocking inflater patches can be +reviewed together with the firmware code. diff --git a/lib/uzlib/library.json b/lib/uzlib/library.json new file mode 100644 index 0000000..f324422 --- /dev/null +++ b/lib/uzlib/library.json @@ -0,0 +1,10 @@ +{ + "name": "uzlib", + "version": "0.0.0-opendisplay", + "description": "Vendored uzlib inflater snapshot for OpenDisplay firmware", + "license": "Zlib", + "build": { + "srcDir": "src", + "includeDir": "src" + } +} diff --git a/lib/uzlib/src/adler32.c b/lib/uzlib/src/adler32.c new file mode 100644 index 0000000..5ee7b6c --- /dev/null +++ b/lib/uzlib/src/adler32.c @@ -0,0 +1,78 @@ +/* + * Adler-32 checksum + * + * Copyright (c) 2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * + * http://www.ibsensoftware.com/ + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +/* + * Adler-32 algorithm taken from the zlib source, which is + * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + */ + +#include "tinf.h" + +#define A32_BASE 65521 +#define A32_NMAX 5552 + +uint32_t uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum /* 1 */) +{ + const unsigned char *buf = (const unsigned char *)data; + + unsigned int s1 = prev_sum & 0xffff; + unsigned int s2 = prev_sum >> 16; + + while (length > 0) + { + int k = length < A32_NMAX ? length : A32_NMAX; + int i; + + for (i = k / 16; i; --i, buf += 16) + { + s1 += buf[0]; s2 += s1; s1 += buf[1]; s2 += s1; + s1 += buf[2]; s2 += s1; s1 += buf[3]; s2 += s1; + s1 += buf[4]; s2 += s1; s1 += buf[5]; s2 += s1; + s1 += buf[6]; s2 += s1; s1 += buf[7]; s2 += s1; + + s1 += buf[8]; s2 += s1; s1 += buf[9]; s2 += s1; + s1 += buf[10]; s2 += s1; s1 += buf[11]; s2 += s1; + s1 += buf[12]; s2 += s1; s1 += buf[13]; s2 += s1; + s1 += buf[14]; s2 += s1; s1 += buf[15]; s2 += s1; + } + + for (i = k % 16; i; --i) { s1 += *buf++; s2 += s1; } + + s1 %= A32_BASE; + s2 %= A32_BASE; + + length -= k; + } + + return ((uint32_t)s2 << 16) | s1; +} diff --git a/lib/uzlib/src/crc32.c b/lib/uzlib/src/crc32.c new file mode 100644 index 0000000..1023118 --- /dev/null +++ b/lib/uzlib/src/crc32.c @@ -0,0 +1,63 @@ +/* + * CRC32 checksum + * + * Copyright (c) 1998-2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * + * http://www.ibsensoftware.com/ + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +/* + * CRC32 algorithm taken from the zlib source, which is + * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + */ + +#include "tinf.h" + +static const uint32_t tinf_crc32tab[16] = { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, + 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, + 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, + 0xbdbdf21c +}; + +/* crc is previous value for incremental computation, 0xffffffff initially */ +uint32_t uzlib_crc32(const void *data, unsigned int length, uint32_t crc) +{ + const unsigned char *buf = (const unsigned char *)data; + unsigned int i; + + for (i = 0; i < length; ++i) + { + crc ^= buf[i]; + crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4); + crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4); + } + + // return value suitable for passing in next time, for final value invert it + return crc/* ^ 0xffffffff*/; +} diff --git a/lib/uzlib/src/defl_static.h b/lib/uzlib/src/defl_static.h new file mode 100644 index 0000000..cdfa973 --- /dev/null +++ b/lib/uzlib/src/defl_static.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) uzlib authors + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +/* This files contains type declaration and prototypes for defl_static.c. + They may be altered/distinct from the originals used in PuTTY source + code. */ + +void outbits(struct uzlib_comp *ctx, unsigned long bits, int nbits); +void zlib_start_block(struct uzlib_comp *ctx); +void zlib_finish_block(struct uzlib_comp *ctx); +void zlib_literal(struct uzlib_comp *ctx, unsigned char c); +void zlib_match(struct uzlib_comp *ctx, int distance, int len); diff --git a/lib/uzlib/src/tinf.h b/lib/uzlib/src/tinf.h new file mode 100644 index 0000000..ae6e1c4 --- /dev/null +++ b/lib/uzlib/src/tinf.h @@ -0,0 +1,3 @@ +/* Compatibility header for the original tinf lib/older versions of uzlib. + Note: may be removed in the future, please migrate to uzlib.h. */ +#include "uzlib.h" diff --git a/lib/uzlib/src/tinf_compat.h b/lib/uzlib/src/tinf_compat.h new file mode 100644 index 0000000..f763804 --- /dev/null +++ b/lib/uzlib/src/tinf_compat.h @@ -0,0 +1,9 @@ +/* This header contains compatibility defines for the original tinf API + and uzlib 2.x and below API. These defines are deprecated and going + to be removed in the future, so applications should migrate to new + uzlib API. */ +#define TINF_DATA struct uzlib_uncomp + +#define destSize dest_size +#define destStart dest_start +#define readSource source_read_cb diff --git a/lib/uzlib/src/tinflate.c b/lib/uzlib/src/tinflate.c new file mode 100644 index 0000000..d719d80 --- /dev/null +++ b/lib/uzlib/src/tinflate.c @@ -0,0 +1,674 @@ +/* + * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) + * + * Copyright (c) 2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * http://www.ibsensoftware.com/ + * + * Copyright (c) 2014-2018 by Paul Sokolovsky + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +#include +#include +#include "tinf.h" + +#define UZLIB_DUMP_ARRAY(heading, arr, size) \ + { \ + printf("%s", heading); \ + for (int i = 0; i < size; ++i) { \ + printf(" %d", (arr)[i]); \ + } \ + printf("\n"); \ + } + +uint32_t tinf_get_le_uint32(TINF_DATA *d); +uint32_t tinf_get_be_uint32(TINF_DATA *d); + +/* --------------------------------------------------- * + * -- uninitialized global data (static structures) -- * + * --------------------------------------------------- */ + +#ifdef RUNTIME_BITS_TABLES + +/* extra bits and base tables for length codes */ +unsigned char length_bits[30]; +unsigned short length_base[30]; + +/* extra bits and base tables for distance codes */ +unsigned char dist_bits[30]; +unsigned short dist_base[30]; + +#else + +const unsigned char length_bits[30] = { + 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 +}; +const unsigned short length_base[30] = { + 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 +}; + +const unsigned char dist_bits[30] = { + 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 +}; +const unsigned short dist_base[30] = { + 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 +}; + +#endif + +/* special ordering of code length codes */ +const unsigned char clcidx[] = { + 16, 17, 18, 0, 8, 7, 9, 6, + 10, 5, 11, 4, 12, 3, 13, 2, + 14, 1, 15 +}; + +/* ----------------------- * + * -- utility functions -- * + * ----------------------- */ + +#ifdef RUNTIME_BITS_TABLES +/* build extra bits and base tables */ +static void tinf_build_bits_base(unsigned char *bits, unsigned short *base, int delta, int first) +{ + int i, sum; + + /* build bits table */ + for (i = 0; i < delta; ++i) bits[i] = 0; + for (i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta; + + /* build base table */ + for (sum = first, i = 0; i < 30; ++i) + { + base[i] = sum; + sum += 1 << bits[i]; + } +} +#endif + +/* build the fixed huffman trees */ +static void tinf_build_fixed_trees(TINF_TREE *lt, TINF_TREE *dt) +{ + int i; + + /* build fixed length tree */ + for (i = 0; i < 7; ++i) lt->table[i] = 0; + + lt->table[7] = 24; + lt->table[8] = 152; + lt->table[9] = 112; + + for (i = 0; i < 24; ++i) lt->trans[i] = 256 + i; + for (i = 0; i < 144; ++i) lt->trans[24 + i] = i; + for (i = 0; i < 8; ++i) lt->trans[24 + 144 + i] = 280 + i; + for (i = 0; i < 112; ++i) lt->trans[24 + 144 + 8 + i] = 144 + i; + + /* build fixed distance tree */ + for (i = 0; i < 5; ++i) dt->table[i] = 0; + + dt->table[5] = 32; + + for (i = 0; i < 32; ++i) dt->trans[i] = i; +} + +/* given an array of code lengths, build a tree */ +static void tinf_build_tree(TINF_TREE *t, const unsigned char *lengths, unsigned int num) +{ + unsigned short offs[16]; + unsigned int i, sum; + + /* clear code length count table */ + for (i = 0; i < 16; ++i) t->table[i] = 0; + + /* scan symbol lengths, and sum code length counts */ + for (i = 0; i < num; ++i) t->table[lengths[i]]++; + + #if UZLIB_CONF_DEBUG_LOG >= 2 + UZLIB_DUMP_ARRAY("codelen counts:", t->table, TINF_ARRAY_SIZE(t->table)); + #endif + + /* In the lengths array, 0 means unused code. So, t->table[0] now contains + number of unused codes. But table's purpose is to contain # of codes of + particular length, and there're 0 codes of length 0. */ + t->table[0] = 0; + + /* compute offset table for distribution sort */ + for (sum = 0, i = 0; i < 16; ++i) + { + offs[i] = sum; + sum += t->table[i]; + } + + #if UZLIB_CONF_DEBUG_LOG >= 2 + UZLIB_DUMP_ARRAY("codelen offsets:", offs, TINF_ARRAY_SIZE(offs)); + #endif + + /* create code->symbol translation table (symbols sorted by code) */ + for (i = 0; i < num; ++i) + { + if (lengths[i]) t->trans[offs[lengths[i]]++] = i; + } +} + +/* ---------------------- * + * -- decode functions -- * + * ---------------------- */ + +unsigned char uzlib_get_byte(TINF_DATA *d) +{ + /* If end of source buffer is not reached, return next byte from source + buffer. */ + if (d->source < d->source_limit) { + return *d->source++; + } + + /* Otherwise if there's callback and we haven't seen EOF yet, try to + read next byte using it. (Note: the callback can also update ->source + and ->source_limit). */ + if (d->readSource && !d->eof) { + int val = d->readSource(d); + if (val >= 0) { + return (unsigned char)val; + } + } + + /* Otherwise, we hit EOF (either from ->readSource() or from exhaustion + of the buffer), and it will be "sticky", i.e. further calls to this + function will end up here too. */ + d->eof = true; + + return 0; +} + +uint32_t tinf_get_le_uint32(TINF_DATA *d) +{ + uint32_t val = 0; + int i; + for (i = 4; i--;) { + val = val >> 8 | ((uint32_t)uzlib_get_byte(d)) << 24; + } + return val; +} + +uint32_t tinf_get_be_uint32(TINF_DATA *d) +{ + uint32_t val = 0; + int i; + for (i = 4; i--;) { + val = val << 8 | uzlib_get_byte(d); + } + return val; +} + +/* get one bit from source stream */ +static int tinf_getbit(TINF_DATA *d) +{ + unsigned int bit; + + /* check if tag is empty */ + if (!d->bitcount--) + { + /* load next tag */ + d->tag = uzlib_get_byte(d); + d->bitcount = 7; + } + + /* shift bit out of tag */ + bit = d->tag & 0x01; + d->tag >>= 1; + + return bit; +} + +/* read a num bit value from a stream and add base */ +static unsigned int tinf_read_bits(TINF_DATA *d, int num, int base) +{ + unsigned int val = 0; + + /* read num bits */ + if (num) + { + unsigned int limit = 1 << (num); + unsigned int mask; + + for (mask = 1; mask < limit; mask *= 2) + if (tinf_getbit(d)) val += mask; + } + + return val + base; +} + +/* given a data stream and a tree, decode a symbol */ +static int tinf_decode_symbol(TINF_DATA *d, TINF_TREE *t) +{ + int sum = 0, cur = 0, len = 0; + + /* get more bits while code value is above sum */ + do { + + cur = 2*cur + tinf_getbit(d); + + if (++len == TINF_ARRAY_SIZE(t->table)) { + return TINF_DATA_ERROR; + } + + sum += t->table[len]; + cur -= t->table[len]; + + } while (cur >= 0); + + sum += cur; + #if UZLIB_CONF_PARANOID_CHECKS + if (sum < 0 || sum >= TINF_ARRAY_SIZE(t->trans)) { + return TINF_DATA_ERROR; + } + #endif + + return t->trans[sum]; +} + +/* given a data stream, decode dynamic trees from it */ +static int tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) +{ + /* code lengths for 288 literal/len symbols and 32 dist symbols */ + unsigned char lengths[288+32]; + unsigned int hlit, hdist, hclen, hlimit; + unsigned int i, num, length; + + /* get 5 bits HLIT (257-286) */ + hlit = tinf_read_bits(d, 5, 257); + + /* get 5 bits HDIST (1-32) */ + hdist = tinf_read_bits(d, 5, 1); + + /* get 4 bits HCLEN (4-19) */ + hclen = tinf_read_bits(d, 4, 4); + + for (i = 0; i < 19; ++i) lengths[i] = 0; + + /* read code lengths for code length alphabet */ + for (i = 0; i < hclen; ++i) + { + /* get 3 bits code length (0-7) */ + unsigned int clen = tinf_read_bits(d, 3, 0); + + lengths[clcidx[i]] = clen; + } + + /* build code length tree, temporarily use length tree */ + tinf_build_tree(lt, lengths, 19); + + /* decode code lengths for the dynamic trees */ + hlimit = hlit + hdist; + for (num = 0; num < hlimit; ) + { + int sym = tinf_decode_symbol(d, lt); + unsigned char fill_value = 0; + int lbits, lbase = 3; + + /* error decoding */ + if (sym < 0) return sym; + + switch (sym) + { + case 16: + /* copy previous code length 3-6 times (read 2 bits) */ + if (num == 0) return TINF_DATA_ERROR; + fill_value = lengths[num - 1]; + lbits = 2; + break; + case 17: + /* repeat code length 0 for 3-10 times (read 3 bits) */ + lbits = 3; + break; + case 18: + /* repeat code length 0 for 11-138 times (read 7 bits) */ + lbits = 7; + lbase = 11; + break; + default: + /* values 0-15 represent the actual code lengths */ + lengths[num++] = sym; + /* continue the for loop */ + continue; + } + + /* special code length 16-18 are handled here */ + length = tinf_read_bits(d, lbits, lbase); + if (num + length > hlimit) return TINF_DATA_ERROR; + for (; length; --length) + { + lengths[num++] = fill_value; + } + } + + #if UZLIB_CONF_DEBUG_LOG >= 2 + printf("lit code lengths (%d):", hlit); + UZLIB_DUMP_ARRAY("", lengths, hlit); + printf("dist code lengths (%d):", hdist); + UZLIB_DUMP_ARRAY("", lengths + hlit, hdist); + #endif + + #if UZLIB_CONF_PARANOID_CHECKS + /* Check that there's "end of block" symbol */ + if (lengths[256] == 0) { + return TINF_DATA_ERROR; + } + #endif + + /* build dynamic trees */ + tinf_build_tree(lt, lengths, hlit); + tinf_build_tree(dt, lengths + hlit, hdist); + + return TINF_OK; +} + +/* ----------------------------- * + * -- block inflate functions -- * + * ----------------------------- */ + +/* given a stream and two trees, inflate next chunk of output (a byte or more) */ +static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) +{ + if (d->curlen == 0) { + unsigned int offs; + int dist; + int sym = tinf_decode_symbol(d, lt); + //printf("huff sym: %02x\n", sym); + + if (d->eof) { + return TINF_DATA_ERROR; + } + + /* literal byte */ + if (sym < 256) { + TINF_PUT(d, sym); + return TINF_OK; + } + + /* end of block */ + if (sym == 256) { + return TINF_DONE; + } + + /* substring from sliding dictionary */ + sym -= 257; + if (sym >= 29) { + return TINF_DATA_ERROR; + } + + /* possibly get more bits from length code */ + d->curlen = tinf_read_bits(d, length_bits[sym], length_base[sym]); + + dist = tinf_decode_symbol(d, dt); + if (dist >= 30) { + return TINF_DATA_ERROR; + } + + /* possibly get more bits from distance code */ + offs = tinf_read_bits(d, dist_bits[dist], dist_base[dist]); + + /* calculate and validate actual LZ offset to use */ + if (d->dict_ring) { + if (offs > d->dict_size) { + return TINF_DICT_ERROR; + } + /* Note: unlike full-dest-in-memory case below, we don't + try to catch offset which points to not yet filled + part of the dictionary here. Doing so would require + keeping another variable to track "filled in" size + of the dictionary. Appearance of such an offset cannot + lead to accessing memory outside of the dictionary + buffer, and clients which don't want to leak unrelated + information, should explicitly initialize dictionary + buffer passed to uzlib. */ + + d->lzOff = d->dict_idx - offs; + if (d->lzOff < 0) { + d->lzOff += d->dict_size; + } + } else { + /* catch trying to point before the start of dest buffer */ + if (offs > d->dest - d->destStart) { + return TINF_DATA_ERROR; + } + d->lzOff = -offs; + } + } + + /* copy next byte from dict substring */ + if (d->dict_ring) { + TINF_PUT(d, d->dict_ring[d->lzOff]); + if ((unsigned)++d->lzOff == d->dict_size) { + d->lzOff = 0; + } + } else { + #if UZLIB_CONF_USE_MEMCPY + /* copy as much as possible, in one memcpy() call */ + unsigned int to_copy = d->curlen, dest_len = d->dest_limit - d->dest; + if (to_copy > dest_len) { + to_copy = dest_len; + } + memcpy(d->dest, d->dest + d->lzOff, to_copy); + d->dest += to_copy; + d->curlen -= to_copy; + return TINF_OK; + #else + d->dest[0] = d->dest[d->lzOff]; + d->dest++; + #endif + } + d->curlen--; + return TINF_OK; +} + +/* inflate next byte from uncompressed block of data */ +static int tinf_inflate_uncompressed_block(TINF_DATA *d) +{ + if (d->curlen == 0) { + unsigned int length, invlength; + + /* get length */ + length = uzlib_get_byte(d); + length += 256 * uzlib_get_byte(d); + /* get one's complement of length */ + invlength = uzlib_get_byte(d); + invlength += 256 * uzlib_get_byte(d); + /* check length */ + if (length != (~invlength & 0x0000ffff)) return TINF_DATA_ERROR; + + /* increment length to properly return TINF_DONE below, without + producing data at the same time */ + d->curlen = length + 1; + + /* make sure we start next block on a byte boundary */ + d->bitcount = 0; + } + + if (--d->curlen == 0) { + return TINF_DONE; + } + + unsigned char c = uzlib_get_byte(d); + TINF_PUT(d, c); + return TINF_OK; +} + +/* ---------------------- * + * -- public functions -- * + * ---------------------- */ + +/* initialize global (static) data */ +void uzlib_init(void) +{ +#ifdef RUNTIME_BITS_TABLES + /* build extra bits and base tables */ + tinf_build_bits_base(length_bits, length_base, 4, 3); + tinf_build_bits_base(dist_bits, dist_base, 2, 1); + + /* fix a special case */ + length_bits[28] = 0; + length_base[28] = 258; +#endif +} + +/* initialize decompression structure */ +void uzlib_uncompress_init(TINF_DATA *d, void *dict, unsigned int dictLen) +{ + d->eof = 0; + d->bitcount = 0; + d->bfinal = 0; + d->btype = -1; + d->dict_size = dictLen; + d->dict_ring = dict; + d->dict_idx = 0; + d->curlen = 0; +} + +/* inflate next output bytes from compressed stream */ +int uzlib_uncompress(TINF_DATA *d) +{ + do { + int res; + + /* start a new block */ + if (d->btype == -1) { + int old_btype; +next_blk: + old_btype = d->btype; + /* read final block flag */ + d->bfinal = tinf_getbit(d); + /* read block type (2 bits) */ + d->btype = tinf_read_bits(d, 2, 0); + + #if UZLIB_CONF_DEBUG_LOG >= 1 + printf("Started new block: type=%d final=%d\n", d->btype, d->bfinal); + #endif + + if (d->btype == 1 && old_btype != 1) { + /* build fixed huffman trees */ + tinf_build_fixed_trees(&d->ltree, &d->dtree); + } else if (d->btype == 2) { + /* decode trees from stream */ + res = tinf_decode_trees(d, &d->ltree, &d->dtree); + if (res != TINF_OK) { + return res; + } + } + } + + /* process current block */ + switch (d->btype) + { + case 0: + /* decompress uncompressed block */ + res = tinf_inflate_uncompressed_block(d); + break; + case 1: + case 2: + /* decompress block with fixed/dynamic huffman trees */ + /* trees were decoded previously, so it's the same routine for both */ + res = tinf_inflate_block_data(d, &d->ltree, &d->dtree); + break; + default: + return TINF_DATA_ERROR; + } + + if (res == TINF_DONE && !d->bfinal) { + /* the block has ended (without producing more data), but we + can't return without data, so start procesing next block */ + goto next_blk; + } + + if (res != TINF_OK) { + return res; + } + + } while (d->dest < d->dest_limit); + + return TINF_OK; +} + +/* inflate next output bytes from compressed stream, updating + checksum, and at the end of stream, verify it */ +int uzlib_uncompress_chksum(TINF_DATA *d) +{ + int res; + unsigned char *data = d->dest; + + res = uzlib_uncompress(d); + + if (res < 0) return res; + + switch (d->checksum_type) { + + case TINF_CHKSUM_ADLER: + d->checksum = uzlib_adler32(data, d->dest - data, d->checksum); + break; + + case TINF_CHKSUM_CRC: + d->checksum = uzlib_crc32(data, d->dest - data, d->checksum); + break; + } + + if (res == TINF_DONE) { + unsigned int val; + + switch (d->checksum_type) { + + case TINF_CHKSUM_ADLER: + val = tinf_get_be_uint32(d); + if (d->checksum != val) { + return TINF_CHKSUM_ERROR; + } + break; + + case TINF_CHKSUM_CRC: + val = tinf_get_le_uint32(d); + if (~d->checksum != val) { + return TINF_CHKSUM_ERROR; + } + // Uncompressed size. TODO: Check + val = tinf_get_le_uint32(d); + break; + } + } + + return res; +} diff --git a/lib/uzlib/src/tinfzlib.c b/lib/uzlib/src/tinfzlib.c new file mode 100644 index 0000000..5cb8852 --- /dev/null +++ b/lib/uzlib/src/tinfzlib.c @@ -0,0 +1,66 @@ +/* + * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) + * + * Copyright (c) 2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * + * http://www.ibsensoftware.com/ + * + * Copyright (c) 2014-2018 by Paul Sokolovsky + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +#include "tinf.h" + +int uzlib_zlib_parse_header(TINF_DATA *d) +{ + unsigned char cmf, flg; + + /* -- get header bytes -- */ + + cmf = uzlib_get_byte(d); + flg = uzlib_get_byte(d); + + /* -- check format -- */ + + /* check checksum */ + if ((256*cmf + flg) % 31) return TINF_DATA_ERROR; + + /* check method is deflate */ + if ((cmf & 0x0f) != 8) return TINF_DATA_ERROR; + + /* check window size is valid */ + if ((cmf >> 4) > 7) return TINF_DATA_ERROR; + + /* check there is no preset dictionary */ + if (flg & 0x20) return TINF_DATA_ERROR; + + /* initialize for adler32 checksum */ + d->checksum_type = TINF_CHKSUM_ADLER; + d->checksum = 1; + + return cmf >> 4; +} diff --git a/lib/uzlib/src/uzlib.h b/lib/uzlib/src/uzlib.h new file mode 100644 index 0000000..681ad74 --- /dev/null +++ b/lib/uzlib/src/uzlib.h @@ -0,0 +1,173 @@ +/* + * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) + * + * Copyright (c) 2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * http://www.ibsensoftware.com/ + * + * Copyright (c) 2014-2018 by Paul Sokolovsky + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +#ifndef UZLIB_H_INCLUDED +#define UZLIB_H_INCLUDED + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "uzlib_conf.h" +#if UZLIB_CONF_DEBUG_LOG +#include +#endif + +/* calling convention */ +#ifndef TINFCC + #ifdef __WATCOMC__ + #define TINFCC __cdecl + #else + #define TINFCC + #endif +#endif + +/* ok status, more data produced */ +#define TINF_OK 0 +/* end of compressed stream reached */ +#define TINF_DONE 1 +#define TINF_DATA_ERROR (-3) +#define TINF_CHKSUM_ERROR (-4) +#define TINF_DICT_ERROR (-5) + +/* checksum types */ +#define TINF_CHKSUM_NONE 0 +#define TINF_CHKSUM_ADLER 1 +#define TINF_CHKSUM_CRC 2 + +/* helper macros */ +#define TINF_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*(arr))) + +/* data structures */ + +typedef struct { + unsigned short table[16]; /* table of code length counts */ + unsigned short trans[288]; /* code -> symbol translation table */ +} TINF_TREE; + +struct uzlib_uncomp { + /* Pointer to the next byte in the input buffer */ + const unsigned char *source; + /* Pointer to the next byte past the input buffer (source_limit = source + len) */ + const unsigned char *source_limit; + /* If source_limit == NULL, or source >= source_limit, this function + will be used to read next byte from source stream. The function may + also return -1 in case of EOF (or irrecoverable error). Note that + besides returning the next byte, it may also update source and + source_limit fields, thus allowing for buffered operation. */ + int (*source_read_cb)(struct uzlib_uncomp *uncomp); + + unsigned int tag; + unsigned int bitcount; + + /* Destination (output) buffer start */ + unsigned char *dest_start; + /* Current pointer in dest buffer */ + unsigned char *dest; + /* Pointer past the end of the dest buffer, similar to source_limit */ + unsigned char *dest_limit; + + /* Accumulating checksum */ + unsigned int checksum; + char checksum_type; + bool eof; + + int btype; + int bfinal; + unsigned int curlen; + int lzOff; + unsigned char *dict_ring; + unsigned int dict_size; + unsigned int dict_idx; + + TINF_TREE ltree; /* dynamic length/symbol tree */ + TINF_TREE dtree; /* dynamic distance tree */ +}; + +#include "tinf_compat.h" + +#define TINF_PUT(d, c) \ + { \ + *d->dest++ = c; \ + if (d->dict_ring) { d->dict_ring[d->dict_idx++] = c; if (d->dict_idx == d->dict_size) d->dict_idx = 0; } \ + } + +unsigned char TINFCC uzlib_get_byte(TINF_DATA *d); + +/* Decompression API */ + +void TINFCC uzlib_init(void); +void TINFCC uzlib_uncompress_init(TINF_DATA *d, void *dict, unsigned int dictLen); +int TINFCC uzlib_uncompress(TINF_DATA *d); +int TINFCC uzlib_uncompress_chksum(TINF_DATA *d); + +int TINFCC uzlib_zlib_parse_header(TINF_DATA *d); +int TINFCC uzlib_gzip_parse_header(TINF_DATA *d); + +/* Compression API */ + +typedef const uint8_t *uzlib_hash_entry_t; + +struct uzlib_comp { + unsigned char *outbuf; + int outlen, outsize; + unsigned long outbits; + int noutbits; + int comp_disabled; + + uzlib_hash_entry_t *hash_table; + unsigned int hash_bits; + unsigned int dict_size; +}; + +void TINFCC uzlib_compress(struct uzlib_comp *c, const uint8_t *src, unsigned slen); + +#include "defl_static.h" + +/* Checksum API */ + +/* prev_sum is previous value for incremental computation, 1 initially */ +uint32_t TINFCC uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum); +/* crc is previous value for incremental computation, 0xffffffff initially */ +uint32_t TINFCC uzlib_crc32(const void *data, unsigned int length, uint32_t crc); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* UZLIB_H_INCLUDED */ diff --git a/lib/uzlib/src/uzlib_conf.h b/lib/uzlib/src/uzlib_conf.h new file mode 100644 index 0000000..fd67dec --- /dev/null +++ b/lib/uzlib/src/uzlib_conf.h @@ -0,0 +1,32 @@ +/* + * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) + * + * Copyright (c) 2014-2018 by Paul Sokolovsky + */ + +#ifndef UZLIB_CONF_H_INCLUDED +#define UZLIB_CONF_H_INCLUDED + +#ifndef UZLIB_CONF_DEBUG_LOG +/* Debug logging level 0, 1, 2, etc. */ +#define UZLIB_CONF_DEBUG_LOG 0 +#endif + +#ifndef UZLIB_CONF_PARANOID_CHECKS +/* Perform extra checks on the input stream, even if they aren't proven + to be strictly required (== lack of them wasn't proven to lead to + crashes). */ +#define UZLIB_CONF_PARANOID_CHECKS 0 +#endif + +#ifndef UZLIB_CONF_USE_MEMCPY +/* Use memcpy() for copying data out of LZ window or uncompressed blocks, + instead of doing this byte by byte. For well-compressed data, this + may noticeably increase decompression speed. But for less compressed, + it can actually deteriorate it (due to the fact that many memcpy() + implementations are optimized for large blocks of data, and have + too much overhead for short strings of just a few bytes). */ +#define UZLIB_CONF_USE_MEMCPY 0 +#endif + +#endif /* UZLIB_CONF_H_INCLUDED */ diff --git a/platformio.ini b/platformio.ini index 2e2d1c1..2ee9870 100644 --- a/platformio.ini +++ b/platformio.ini @@ -5,7 +5,6 @@ extra_configs = [env] lib_deps = https://github.com/bitbank2/bb_epaper.git - https://github.com/pfalcon/uzlib [env:nrf52840custom] build_flags = From d56eda40c96227914659b0350d3d58d447091ab3 Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Fri, 8 May 2026 23:04:17 +0200 Subject: [PATCH 2/5] Add streaming zlib inflater --- lib/uzlib/src/adler32.c | 2 +- lib/uzlib/src/crc32.c | 2 +- lib/uzlib/src/defl_static.h | 37 -- lib/uzlib/src/od_zlib_stream.c | 675 +++++++++++++++++++++++++++++++++ lib/uzlib/src/tinf.h | 3 - lib/uzlib/src/tinf_compat.h | 9 - lib/uzlib/src/tinflate.c | 674 -------------------------------- lib/uzlib/src/tinfzlib.c | 66 ---- lib/uzlib/src/uzlib.h | 172 ++------- src/compression_config.h | 6 + src/display_service.cpp | 263 ++++++------- src/display_service.h | 1 - src/main.cpp | 27 +- src/main.h | 15 +- tools/test_zlib_stream.c | 215 +++++++++++ 15 files changed, 1045 insertions(+), 1122 deletions(-) delete mode 100644 lib/uzlib/src/defl_static.h create mode 100644 lib/uzlib/src/od_zlib_stream.c delete mode 100644 lib/uzlib/src/tinf.h delete mode 100644 lib/uzlib/src/tinf_compat.h delete mode 100644 lib/uzlib/src/tinflate.c delete mode 100644 lib/uzlib/src/tinfzlib.c create mode 100644 src/compression_config.h create mode 100644 tools/test_zlib_stream.c diff --git a/lib/uzlib/src/adler32.c b/lib/uzlib/src/adler32.c index 5ee7b6c..a362ead 100644 --- a/lib/uzlib/src/adler32.c +++ b/lib/uzlib/src/adler32.c @@ -36,7 +36,7 @@ * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler */ -#include "tinf.h" +#include "uzlib.h" #define A32_BASE 65521 #define A32_NMAX 5552 diff --git a/lib/uzlib/src/crc32.c b/lib/uzlib/src/crc32.c index 1023118..9358aae 100644 --- a/lib/uzlib/src/crc32.c +++ b/lib/uzlib/src/crc32.c @@ -36,7 +36,7 @@ * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler */ -#include "tinf.h" +#include "uzlib.h" static const uint32_t tinf_crc32tab[16] = { 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, diff --git a/lib/uzlib/src/defl_static.h b/lib/uzlib/src/defl_static.h deleted file mode 100644 index cdfa973..0000000 --- a/lib/uzlib/src/defl_static.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) uzlib authors - * - * This software is provided 'as-is', without any express - * or implied warranty. In no event will the authors be - * held liable for any damages arising from the use of - * this software. - * - * Permission is granted to anyone to use this software - * for any purpose, including commercial applications, - * and to alter it and redistribute it freely, subject to - * the following restrictions: - * - * 1. The origin of this software must not be - * misrepresented; you must not claim that you - * wrote the original software. If you use this - * software in a product, an acknowledgment in - * the product documentation would be appreciated - * but is not required. - * - * 2. Altered source versions must be plainly marked - * as such, and must not be misrepresented as - * being the original software. - * - * 3. This notice may not be removed or altered from - * any source distribution. - */ - -/* This files contains type declaration and prototypes for defl_static.c. - They may be altered/distinct from the originals used in PuTTY source - code. */ - -void outbits(struct uzlib_comp *ctx, unsigned long bits, int nbits); -void zlib_start_block(struct uzlib_comp *ctx); -void zlib_finish_block(struct uzlib_comp *ctx); -void zlib_literal(struct uzlib_comp *ctx, unsigned char c); -void zlib_match(struct uzlib_comp *ctx, int distance, int len); diff --git a/lib/uzlib/src/od_zlib_stream.c b/lib/uzlib/src/od_zlib_stream.c new file mode 100644 index 0000000..c2f795f --- /dev/null +++ b/lib/uzlib/src/od_zlib_stream.c @@ -0,0 +1,675 @@ +/* + * OpenDisplay streaming zlib inflater, based on uzlib/tinf. + */ + +#include +#include "uzlib.h" + +#define TINF_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*(arr))) + +typedef struct { + unsigned short table[16]; + unsigned short trans[288]; +} TINF_TREE; + +static const unsigned char length_bits[30] = { + 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 +}; + +static const unsigned short length_base[30] = { + 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 +}; + +static const unsigned char dist_bits[30] = { + 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 +}; + +static const unsigned short dist_base[30] = { + 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 +}; + +static const unsigned char clcidx[] = { + 16, 17, 18, 0, 8, 7, 9, 6, + 10, 5, 11, 4, 12, 3, 13, 2, + 14, 1, 15 +}; + +typedef enum { + ST_ZLIB_CMF, + ST_ZLIB_FLG, + ST_BLOCK_FINAL, + ST_BLOCK_TYPE, + ST_DYNAMIC_TREES, + ST_STORED_LEN, + ST_STORED_DATA, + ST_BLOCK_DATA, + ST_TRAILER, + ST_DONE, + ST_ERROR, +} inflate_stage_t; + +typedef enum { + BLK_SYMBOL, + BLK_LEN_EXTRA, + BLK_DIST_SYMBOL, + BLK_DIST_EXTRA, + BLK_MATCH_COPY, +} block_stage_t; + +typedef enum { + DYN_HLIT, + DYN_HDIST, + DYN_HCLEN, + DYN_CLEAR_CLEN, + DYN_READ_CLEN, + DYN_BUILD_CLTREE, + DYN_READ_LENGTHS, + DYN_REPEAT_BITS, + DYN_BUILD_TREES, +} dynamic_stage_t; + +typedef struct { + inflate_stage_t stage; + block_stage_t block_stage; + dynamic_stage_t dynamic_stage; + + const uint8_t *input; + size_t input_remaining; + bool input_final; + bool initialized; + + uint8_t bit_tag; + uint8_t bit_count; + + bool read_bits_active; + uint8_t read_bits_num; + uint8_t read_bits_pos; + unsigned int read_bits_base; + unsigned int read_bits_value; + + bool sym_active; + int sym_sum; + int sym_cur; + int sym_len; + + uint8_t cmf; + uint8_t bfinal; + uint8_t btype; + + TINF_TREE ltree; + union { + TINF_TREE dtree; + TINF_TREE cltree; + } tree; + + unsigned char lengths[288 + 32]; + unsigned int hlit; + unsigned int hdist; + unsigned int hclen; + unsigned int hlimit; + unsigned int dyn_i; + unsigned int dyn_num; + unsigned char dyn_fill_value; + unsigned int dyn_repeat_len; + unsigned int dyn_repeat_bits; + unsigned int dyn_repeat_base; + + uint8_t stored_header_index; + uint16_t stored_len; + uint16_t stored_invlen; + + unsigned int match_len; + unsigned int match_offset; + int length_sym; + int dist_sym; + + uint8_t window[OPENDISPLAY_ZLIB_WINDOW_SIZE]; + unsigned int window_pos; + uint32_t expected_output; + uint32_t output_count; + uint32_t adler; + uint8_t trailer_index; + uint32_t trailer_value; + + const char *error; +} od_zlib_stream_state_t; + +static od_zlib_stream_state_t s; + +static void set_error(const char *error) { + s.stage = ST_ERROR; + s.error = error; +} + +static void build_tree(TINF_TREE *t, const unsigned char *lengths, unsigned int num) { + unsigned short offs[16]; + unsigned int i, sum; + + for (i = 0; i < 16; ++i) t->table[i] = 0; + for (i = 0; i < num; ++i) t->table[lengths[i]]++; + t->table[0] = 0; + + for (sum = 0, i = 0; i < 16; ++i) { + offs[i] = sum; + sum += t->table[i]; + } + + for (i = 0; i < num; ++i) { + if (lengths[i]) t->trans[offs[lengths[i]]++] = i; + } +} + +static void build_fixed_trees(void) { + int i; + memset(&s.ltree, 0, sizeof(s.ltree)); + memset(&s.tree.dtree, 0, sizeof(s.tree.dtree)); + + s.ltree.table[7] = 24; + s.ltree.table[8] = 152; + s.ltree.table[9] = 112; + for (i = 0; i < 24; ++i) s.ltree.trans[i] = 256 + i; + for (i = 0; i < 144; ++i) s.ltree.trans[24 + i] = i; + for (i = 0; i < 8; ++i) s.ltree.trans[24 + 144 + i] = 280 + i; + for (i = 0; i < 112; ++i) s.ltree.trans[24 + 144 + 8 + i] = 144 + i; + + s.tree.dtree.table[5] = 32; + for (i = 0; i < 32; ++i) s.tree.dtree.trans[i] = i; +} + +static int read_byte(uint8_t *value) { + if (s.input_remaining > 0) { + *value = *s.input++; + s.input_remaining--; + return 1; + } + if (!s.input_final) return 0; + set_error("truncated zlib stream"); + return -1; +} + +static int read_bit(unsigned int *bit) { + uint8_t byte; + if (s.bit_count == 0) { + int rc = read_byte(&byte); + if (rc <= 0) return rc; + s.bit_tag = byte; + s.bit_count = 8; + } + *bit = s.bit_tag & 1u; + s.bit_tag >>= 1u; + s.bit_count--; + return 1; +} + +static int read_bits(unsigned int num, unsigned int base, unsigned int *value) { + if (!s.read_bits_active) { + s.read_bits_active = true; + s.read_bits_num = (uint8_t)num; + s.read_bits_pos = 0; + s.read_bits_base = base; + s.read_bits_value = 0; + } + + while (s.read_bits_pos < s.read_bits_num) { + unsigned int bit; + int rc = read_bit(&bit); + if (rc <= 0) return rc; + if (bit) s.read_bits_value += 1u << s.read_bits_pos; + s.read_bits_pos++; + } + + *value = s.read_bits_value + s.read_bits_base; + s.read_bits_active = false; + return 1; +} + +static int decode_symbol(TINF_TREE *tree, int *symbol) { + if (!s.sym_active) { + s.sym_active = true; + s.sym_sum = 0; + s.sym_cur = 0; + s.sym_len = 0; + } + + do { + unsigned int bit; + int rc = read_bit(&bit); + if (rc <= 0) return rc; + + s.sym_cur = 2 * s.sym_cur + (int)bit; + if (++s.sym_len == (int)TINF_ARRAY_SIZE(tree->table)) { + set_error("invalid huffman code"); + return -1; + } + + s.sym_sum += tree->table[s.sym_len]; + s.sym_cur -= tree->table[s.sym_len]; + } while (s.sym_cur >= 0); + + s.sym_sum += s.sym_cur; + if (s.sym_sum < 0 || s.sym_sum >= (int)TINF_ARRAY_SIZE(tree->trans)) { + set_error("invalid huffman symbol"); + return -1; + } + *symbol = tree->trans[s.sym_sum]; + s.sym_active = false; + return 1; +} + +static void reset_code_readers(void) { + s.read_bits_active = false; + s.sym_active = false; +} + +static void adler_update_byte(uint8_t byte) { + uint32_t s1 = s.adler & 0xffffu; + uint32_t s2 = s.adler >> 16; + s1 += byte; + if (s1 >= 65521u) s1 -= 65521u; + s2 += s1; + s2 %= 65521u; + s.adler = (s2 << 16) | s1; +} + +static bool put_output_byte(uint8_t byte, uint8_t *output, size_t capacity, size_t *produced) { + if (*produced >= capacity) return false; + if (s.output_count >= s.expected_output) { + set_error("decompressed output exceeds expected size"); + return false; + } + + output[(*produced)++] = byte; + s.window[s.window_pos] = byte; + s.window_pos = (s.window_pos + 1u) & (OPENDISPLAY_ZLIB_WINDOW_SIZE - 1u); + s.output_count++; + adler_update_byte(byte); + return true; +} + +static int process_dynamic_trees(void) { + int sym; + unsigned int value; + + for (;;) { + switch (s.dynamic_stage) { + case DYN_HLIT: + if (read_bits(5, 257, &s.hlit) <= 0) return s.stage == ST_ERROR ? -1 : 0; + s.dynamic_stage = DYN_HDIST; + break; + case DYN_HDIST: + if (read_bits(5, 1, &s.hdist) <= 0) return s.stage == ST_ERROR ? -1 : 0; + s.dynamic_stage = DYN_HCLEN; + break; + case DYN_HCLEN: + if (read_bits(4, 4, &s.hclen) <= 0) return s.stage == ST_ERROR ? -1 : 0; + if (s.hlit > 286 || s.hdist > 32 || s.hlit + s.hdist > TINF_ARRAY_SIZE(s.lengths)) { + set_error("invalid dynamic tree sizes"); + return -1; + } + s.dyn_i = 0; + s.dynamic_stage = DYN_CLEAR_CLEN; + break; + case DYN_CLEAR_CLEN: + while (s.dyn_i < 19) s.lengths[s.dyn_i++] = 0; + s.dyn_i = 0; + s.dynamic_stage = DYN_READ_CLEN; + break; + case DYN_READ_CLEN: + while (s.dyn_i < s.hclen) { + if (read_bits(3, 0, &value) <= 0) return s.stage == ST_ERROR ? -1 : 0; + s.lengths[clcidx[s.dyn_i++]] = (unsigned char)value; + } + s.dynamic_stage = DYN_BUILD_CLTREE; + break; + case DYN_BUILD_CLTREE: + build_tree(&s.tree.cltree, s.lengths, 19); + s.hlimit = s.hlit + s.hdist; + s.dyn_num = 0; + s.dynamic_stage = DYN_READ_LENGTHS; + reset_code_readers(); + break; + case DYN_READ_LENGTHS: + while (s.dyn_num < s.hlimit) { + int rc = decode_symbol(&s.tree.cltree, &sym); + if (rc <= 0) return s.stage == ST_ERROR ? -1 : 0; + if (sym < 16) { + s.lengths[s.dyn_num++] = (unsigned char)sym; + continue; + } + if (sym == 16) { + if (s.dyn_num == 0) { + set_error("invalid dynamic repeat"); + return -1; + } + s.dyn_fill_value = s.lengths[s.dyn_num - 1]; + s.dyn_repeat_bits = 2; + s.dyn_repeat_base = 3; + } else if (sym == 17) { + s.dyn_fill_value = 0; + s.dyn_repeat_bits = 3; + s.dyn_repeat_base = 3; + } else if (sym == 18) { + s.dyn_fill_value = 0; + s.dyn_repeat_bits = 7; + s.dyn_repeat_base = 11; + } else { + set_error("invalid dynamic code length symbol"); + return -1; + } + s.dynamic_stage = DYN_REPEAT_BITS; + break; + } + if (s.dynamic_stage != DYN_READ_LENGTHS) break; + s.dynamic_stage = DYN_BUILD_TREES; + break; + case DYN_REPEAT_BITS: + if (read_bits(s.dyn_repeat_bits, s.dyn_repeat_base, &s.dyn_repeat_len) <= 0) { + return s.stage == ST_ERROR ? -1 : 0; + } + if (s.dyn_num + s.dyn_repeat_len > s.hlimit) { + set_error("dynamic repeat exceeds tree size"); + return -1; + } + while (s.dyn_repeat_len--) s.lengths[s.dyn_num++] = s.dyn_fill_value; + s.dynamic_stage = DYN_READ_LENGTHS; + break; + case DYN_BUILD_TREES: + if (s.lengths[256] == 0) { + set_error("dynamic tree missing end-of-block"); + return -1; + } + build_tree(&s.ltree, s.lengths, s.hlit); + build_tree(&s.tree.dtree, s.lengths + s.hlit, s.hdist); + s.dynamic_stage = DYN_HLIT; + reset_code_readers(); + return 1; + } + } +} + +static int process_stored_header(void) { + uint8_t byte; + while (s.stored_header_index < 4) { + int rc = read_byte(&byte); + if (rc <= 0) return rc; + if (s.stored_header_index == 0) s.stored_len = byte; + else if (s.stored_header_index == 1) s.stored_len |= (uint16_t)byte << 8; + else if (s.stored_header_index == 2) s.stored_invlen = byte; + else s.stored_invlen |= (uint16_t)byte << 8; + s.stored_header_index++; + } + if (s.stored_len != (uint16_t)(~s.stored_invlen)) { + set_error("invalid stored block length"); + return -1; + } + s.stored_header_index = 0; + return 1; +} + +static int process_stored_data(uint8_t *output, size_t capacity, size_t *produced) { + while (s.stored_len > 0) { + uint8_t byte; + int rc; + if (*produced >= capacity) return 2; + rc = read_byte(&byte); + if (rc <= 0) return rc; + if (!put_output_byte(byte, output, capacity, produced)) return s.stage == ST_ERROR ? -1 : 2; + s.stored_len--; + } + return 1; +} + +static int process_block_data(uint8_t *output, size_t capacity, size_t *produced) { + TINF_TREE *lt = &s.ltree; + TINF_TREE *dt = &s.tree.dtree; + int sym; + + for (;;) { + if (*produced >= capacity) return 2; + switch (s.block_stage) { + case BLK_SYMBOL: { + int rc = decode_symbol(lt, &sym); + if (rc <= 0) return rc; + if (sym < 256) { + if (!put_output_byte((uint8_t)sym, output, capacity, produced)) return s.stage == ST_ERROR ? -1 : 2; + return 1; + } + if (sym == 256) { + s.block_stage = BLK_SYMBOL; + return 3; + } + sym -= 257; + if (sym < 0 || sym >= 29) { + set_error("invalid length symbol"); + return -1; + } + s.length_sym = sym; + s.block_stage = BLK_LEN_EXTRA; + break; + } + case BLK_LEN_EXTRA: + if (read_bits(length_bits[s.length_sym], length_base[s.length_sym], &s.match_len) <= 0) { + return s.stage == ST_ERROR ? -1 : 0; + } + s.block_stage = BLK_DIST_SYMBOL; + break; + case BLK_DIST_SYMBOL: { + int rc = decode_symbol(dt, &s.dist_sym); + if (rc <= 0) return rc; + if (s.dist_sym < 0 || s.dist_sym >= 30) { + set_error("invalid distance symbol"); + return -1; + } + s.block_stage = BLK_DIST_EXTRA; + break; + } + case BLK_DIST_EXTRA: + if (read_bits(dist_bits[s.dist_sym], dist_base[s.dist_sym], &s.match_offset) <= 0) { + return s.stage == ST_ERROR ? -1 : 0; + } + if (s.match_offset == 0 || s.match_offset > OPENDISPLAY_ZLIB_WINDOW_SIZE || s.match_offset > s.output_count) { + set_error("invalid back-reference distance"); + return -1; + } + s.block_stage = BLK_MATCH_COPY; + break; + case BLK_MATCH_COPY: + while (s.match_len > 0) { + uint8_t byte; + unsigned int source; + if (*produced >= capacity) return 2; + source = (s.window_pos + OPENDISPLAY_ZLIB_WINDOW_SIZE - s.match_offset) & (OPENDISPLAY_ZLIB_WINDOW_SIZE - 1u); + byte = s.window[source]; + if (!put_output_byte(byte, output, capacity, produced)) return s.stage == ST_ERROR ? -1 : 2; + s.match_len--; + } + s.block_stage = BLK_SYMBOL; + break; + } + } +} + +static int process_trailer(void) { + uint8_t byte; + while (s.trailer_index < 4) { + int rc = read_byte(&byte); + if (rc <= 0) return rc; + s.trailer_value = (s.trailer_value << 8) | byte; + s.trailer_index++; + } + if (s.trailer_value != s.adler) { + set_error("zlib adler32 mismatch"); + return -1; + } + if (s.output_count != s.expected_output) { + set_error("decompressed output size mismatch"); + return -1; + } + if (s.input_remaining > 0) { + set_error("input after end of zlib stream"); + return -1; + } + s.stage = ST_DONE; + return 1; +} + +void od_zlib_stream_reset(uint32_t expected_output_size) { + memset(&s, 0, sizeof(s)); + s.stage = ST_ZLIB_CMF; + s.block_stage = BLK_SYMBOL; + s.dynamic_stage = DYN_HLIT; + s.expected_output = expected_output_size; + s.adler = 1; + s.initialized = true; +} + +od_zlib_status_t od_zlib_stream_push(const uint8_t *input, size_t len, bool final) { + if (!s.initialized) { + set_error("zlib stream not initialized"); + return OD_ZLIB_STATUS_ERROR; + } + if (len > 0 && input == NULL) { + set_error("zlib stream input is null"); + return OD_ZLIB_STATUS_ERROR; + } + if (s.input_remaining > 0) { + set_error("previous input not fully consumed"); + return OD_ZLIB_STATUS_ERROR; + } + s.input = input; + s.input_remaining = len; + s.input_final = final; + if (s.stage == ST_DONE) { + if (len != 0) { + set_error("input after end of zlib stream"); + return OD_ZLIB_STATUS_ERROR; + } + return OD_ZLIB_STATUS_DONE; + } + return OD_ZLIB_STATUS_NEEDS_INPUT; +} + +od_zlib_status_t od_zlib_stream_poll(uint8_t *output, size_t capacity, size_t *produced) { + *produced = 0; + if (!s.initialized) { + set_error("zlib stream not initialized"); + return OD_ZLIB_STATUS_ERROR; + } + if (s.stage == ST_ERROR) return OD_ZLIB_STATUS_ERROR; + if (s.stage == ST_DONE) return OD_ZLIB_STATUS_DONE; + + for (;;) { + int rc; + unsigned int value; + uint8_t byte; + + if (*produced >= capacity) return OD_ZLIB_STATUS_OUTPUT_READY; + + switch (s.stage) { + case ST_ZLIB_CMF: + rc = read_byte(&s.cmf); + if (rc <= 0) return s.stage == ST_ERROR ? OD_ZLIB_STATUS_ERROR : OD_ZLIB_STATUS_NEEDS_INPUT; + s.stage = ST_ZLIB_FLG; + break; + case ST_ZLIB_FLG: + rc = read_byte(&byte); + if (rc <= 0) return s.stage == ST_ERROR ? OD_ZLIB_STATUS_ERROR : OD_ZLIB_STATUS_NEEDS_INPUT; + if (((256u * s.cmf + byte) % 31u) != 0 || (s.cmf & 0x0fu) != 8u || (byte & 0x20u) != 0) { + set_error("invalid zlib header"); + return OD_ZLIB_STATUS_ERROR; + } + if (((s.cmf >> 4) + 8u) > OPENDISPLAY_ZLIB_WINDOW_BITS) { + set_error("zlib stream window exceeds firmware limit"); + return OD_ZLIB_STATUS_ERROR; + } + s.stage = ST_BLOCK_FINAL; + break; + case ST_BLOCK_FINAL: + rc = read_bit(&value); + if (rc <= 0) return s.stage == ST_ERROR ? OD_ZLIB_STATUS_ERROR : OD_ZLIB_STATUS_NEEDS_INPUT; + s.bfinal = (uint8_t)value; + s.stage = ST_BLOCK_TYPE; + break; + case ST_BLOCK_TYPE: + rc = read_bits(2, 0, &value); + if (rc <= 0) return s.stage == ST_ERROR ? OD_ZLIB_STATUS_ERROR : OD_ZLIB_STATUS_NEEDS_INPUT; + s.btype = (uint8_t)value; + if (s.btype == 0) { + s.bit_count = 0; + s.stored_header_index = 0; + s.stored_len = 0; + s.stored_invlen = 0; + s.stage = ST_STORED_LEN; + } else if (s.btype == 1) { + build_fixed_trees(); + s.block_stage = BLK_SYMBOL; + reset_code_readers(); + s.stage = ST_BLOCK_DATA; + } else if (s.btype == 2) { + s.dynamic_stage = DYN_HLIT; + reset_code_readers(); + s.stage = ST_DYNAMIC_TREES; + } else { + set_error("invalid deflate block type"); + return OD_ZLIB_STATUS_ERROR; + } + break; + case ST_DYNAMIC_TREES: + rc = process_dynamic_trees(); + if (rc < 0) return OD_ZLIB_STATUS_ERROR; + if (rc == 0) return OD_ZLIB_STATUS_NEEDS_INPUT; + s.block_stage = BLK_SYMBOL; + s.stage = ST_BLOCK_DATA; + break; + case ST_STORED_LEN: + rc = process_stored_header(); + if (rc < 0) return OD_ZLIB_STATUS_ERROR; + if (rc == 0) return OD_ZLIB_STATUS_NEEDS_INPUT; + s.stage = ST_STORED_DATA; + break; + case ST_STORED_DATA: + rc = process_stored_data(output, capacity, produced); + if (rc < 0) return OD_ZLIB_STATUS_ERROR; + if (rc == 0) return *produced ? OD_ZLIB_STATUS_OUTPUT_READY : OD_ZLIB_STATUS_NEEDS_INPUT; + if (rc == 2) return OD_ZLIB_STATUS_OUTPUT_READY; + s.stage = s.bfinal ? ST_TRAILER : ST_BLOCK_FINAL; + break; + case ST_BLOCK_DATA: + rc = process_block_data(output, capacity, produced); + if (rc < 0) return OD_ZLIB_STATUS_ERROR; + if (rc == 0) return *produced ? OD_ZLIB_STATUS_OUTPUT_READY : OD_ZLIB_STATUS_NEEDS_INPUT; + if (rc == 2) return OD_ZLIB_STATUS_OUTPUT_READY; + if (rc == 3) s.stage = s.bfinal ? ST_TRAILER : ST_BLOCK_FINAL; + break; + case ST_TRAILER: + rc = process_trailer(); + if (rc < 0) return OD_ZLIB_STATUS_ERROR; + if (rc == 0) return *produced ? OD_ZLIB_STATUS_OUTPUT_READY : OD_ZLIB_STATUS_NEEDS_INPUT; + return OD_ZLIB_STATUS_DONE; + case ST_DONE: + return OD_ZLIB_STATUS_DONE; + case ST_ERROR: + return OD_ZLIB_STATUS_ERROR; + } + } +} + +const char *od_zlib_stream_error(void) { + return s.error ? s.error : ""; +} + +uint32_t od_zlib_stream_output_count(void) { + return s.output_count; +} diff --git a/lib/uzlib/src/tinf.h b/lib/uzlib/src/tinf.h deleted file mode 100644 index ae6e1c4..0000000 --- a/lib/uzlib/src/tinf.h +++ /dev/null @@ -1,3 +0,0 @@ -/* Compatibility header for the original tinf lib/older versions of uzlib. - Note: may be removed in the future, please migrate to uzlib.h. */ -#include "uzlib.h" diff --git a/lib/uzlib/src/tinf_compat.h b/lib/uzlib/src/tinf_compat.h deleted file mode 100644 index f763804..0000000 --- a/lib/uzlib/src/tinf_compat.h +++ /dev/null @@ -1,9 +0,0 @@ -/* This header contains compatibility defines for the original tinf API - and uzlib 2.x and below API. These defines are deprecated and going - to be removed in the future, so applications should migrate to new - uzlib API. */ -#define TINF_DATA struct uzlib_uncomp - -#define destSize dest_size -#define destStart dest_start -#define readSource source_read_cb diff --git a/lib/uzlib/src/tinflate.c b/lib/uzlib/src/tinflate.c deleted file mode 100644 index d719d80..0000000 --- a/lib/uzlib/src/tinflate.c +++ /dev/null @@ -1,674 +0,0 @@ -/* - * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) - * - * Copyright (c) 2003 by Joergen Ibsen / Jibz - * All Rights Reserved - * http://www.ibsensoftware.com/ - * - * Copyright (c) 2014-2018 by Paul Sokolovsky - * - * This software is provided 'as-is', without any express - * or implied warranty. In no event will the authors be - * held liable for any damages arising from the use of - * this software. - * - * Permission is granted to anyone to use this software - * for any purpose, including commercial applications, - * and to alter it and redistribute it freely, subject to - * the following restrictions: - * - * 1. The origin of this software must not be - * misrepresented; you must not claim that you - * wrote the original software. If you use this - * software in a product, an acknowledgment in - * the product documentation would be appreciated - * but is not required. - * - * 2. Altered source versions must be plainly marked - * as such, and must not be misrepresented as - * being the original software. - * - * 3. This notice may not be removed or altered from - * any source distribution. - */ - -#include -#include -#include "tinf.h" - -#define UZLIB_DUMP_ARRAY(heading, arr, size) \ - { \ - printf("%s", heading); \ - for (int i = 0; i < size; ++i) { \ - printf(" %d", (arr)[i]); \ - } \ - printf("\n"); \ - } - -uint32_t tinf_get_le_uint32(TINF_DATA *d); -uint32_t tinf_get_be_uint32(TINF_DATA *d); - -/* --------------------------------------------------- * - * -- uninitialized global data (static structures) -- * - * --------------------------------------------------- */ - -#ifdef RUNTIME_BITS_TABLES - -/* extra bits and base tables for length codes */ -unsigned char length_bits[30]; -unsigned short length_base[30]; - -/* extra bits and base tables for distance codes */ -unsigned char dist_bits[30]; -unsigned short dist_base[30]; - -#else - -const unsigned char length_bits[30] = { - 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 -}; -const unsigned short length_base[30] = { - 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 -}; - -const unsigned char dist_bits[30] = { - 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 -}; -const unsigned short dist_base[30] = { - 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 -}; - -#endif - -/* special ordering of code length codes */ -const unsigned char clcidx[] = { - 16, 17, 18, 0, 8, 7, 9, 6, - 10, 5, 11, 4, 12, 3, 13, 2, - 14, 1, 15 -}; - -/* ----------------------- * - * -- utility functions -- * - * ----------------------- */ - -#ifdef RUNTIME_BITS_TABLES -/* build extra bits and base tables */ -static void tinf_build_bits_base(unsigned char *bits, unsigned short *base, int delta, int first) -{ - int i, sum; - - /* build bits table */ - for (i = 0; i < delta; ++i) bits[i] = 0; - for (i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta; - - /* build base table */ - for (sum = first, i = 0; i < 30; ++i) - { - base[i] = sum; - sum += 1 << bits[i]; - } -} -#endif - -/* build the fixed huffman trees */ -static void tinf_build_fixed_trees(TINF_TREE *lt, TINF_TREE *dt) -{ - int i; - - /* build fixed length tree */ - for (i = 0; i < 7; ++i) lt->table[i] = 0; - - lt->table[7] = 24; - lt->table[8] = 152; - lt->table[9] = 112; - - for (i = 0; i < 24; ++i) lt->trans[i] = 256 + i; - for (i = 0; i < 144; ++i) lt->trans[24 + i] = i; - for (i = 0; i < 8; ++i) lt->trans[24 + 144 + i] = 280 + i; - for (i = 0; i < 112; ++i) lt->trans[24 + 144 + 8 + i] = 144 + i; - - /* build fixed distance tree */ - for (i = 0; i < 5; ++i) dt->table[i] = 0; - - dt->table[5] = 32; - - for (i = 0; i < 32; ++i) dt->trans[i] = i; -} - -/* given an array of code lengths, build a tree */ -static void tinf_build_tree(TINF_TREE *t, const unsigned char *lengths, unsigned int num) -{ - unsigned short offs[16]; - unsigned int i, sum; - - /* clear code length count table */ - for (i = 0; i < 16; ++i) t->table[i] = 0; - - /* scan symbol lengths, and sum code length counts */ - for (i = 0; i < num; ++i) t->table[lengths[i]]++; - - #if UZLIB_CONF_DEBUG_LOG >= 2 - UZLIB_DUMP_ARRAY("codelen counts:", t->table, TINF_ARRAY_SIZE(t->table)); - #endif - - /* In the lengths array, 0 means unused code. So, t->table[0] now contains - number of unused codes. But table's purpose is to contain # of codes of - particular length, and there're 0 codes of length 0. */ - t->table[0] = 0; - - /* compute offset table for distribution sort */ - for (sum = 0, i = 0; i < 16; ++i) - { - offs[i] = sum; - sum += t->table[i]; - } - - #if UZLIB_CONF_DEBUG_LOG >= 2 - UZLIB_DUMP_ARRAY("codelen offsets:", offs, TINF_ARRAY_SIZE(offs)); - #endif - - /* create code->symbol translation table (symbols sorted by code) */ - for (i = 0; i < num; ++i) - { - if (lengths[i]) t->trans[offs[lengths[i]]++] = i; - } -} - -/* ---------------------- * - * -- decode functions -- * - * ---------------------- */ - -unsigned char uzlib_get_byte(TINF_DATA *d) -{ - /* If end of source buffer is not reached, return next byte from source - buffer. */ - if (d->source < d->source_limit) { - return *d->source++; - } - - /* Otherwise if there's callback and we haven't seen EOF yet, try to - read next byte using it. (Note: the callback can also update ->source - and ->source_limit). */ - if (d->readSource && !d->eof) { - int val = d->readSource(d); - if (val >= 0) { - return (unsigned char)val; - } - } - - /* Otherwise, we hit EOF (either from ->readSource() or from exhaustion - of the buffer), and it will be "sticky", i.e. further calls to this - function will end up here too. */ - d->eof = true; - - return 0; -} - -uint32_t tinf_get_le_uint32(TINF_DATA *d) -{ - uint32_t val = 0; - int i; - for (i = 4; i--;) { - val = val >> 8 | ((uint32_t)uzlib_get_byte(d)) << 24; - } - return val; -} - -uint32_t tinf_get_be_uint32(TINF_DATA *d) -{ - uint32_t val = 0; - int i; - for (i = 4; i--;) { - val = val << 8 | uzlib_get_byte(d); - } - return val; -} - -/* get one bit from source stream */ -static int tinf_getbit(TINF_DATA *d) -{ - unsigned int bit; - - /* check if tag is empty */ - if (!d->bitcount--) - { - /* load next tag */ - d->tag = uzlib_get_byte(d); - d->bitcount = 7; - } - - /* shift bit out of tag */ - bit = d->tag & 0x01; - d->tag >>= 1; - - return bit; -} - -/* read a num bit value from a stream and add base */ -static unsigned int tinf_read_bits(TINF_DATA *d, int num, int base) -{ - unsigned int val = 0; - - /* read num bits */ - if (num) - { - unsigned int limit = 1 << (num); - unsigned int mask; - - for (mask = 1; mask < limit; mask *= 2) - if (tinf_getbit(d)) val += mask; - } - - return val + base; -} - -/* given a data stream and a tree, decode a symbol */ -static int tinf_decode_symbol(TINF_DATA *d, TINF_TREE *t) -{ - int sum = 0, cur = 0, len = 0; - - /* get more bits while code value is above sum */ - do { - - cur = 2*cur + tinf_getbit(d); - - if (++len == TINF_ARRAY_SIZE(t->table)) { - return TINF_DATA_ERROR; - } - - sum += t->table[len]; - cur -= t->table[len]; - - } while (cur >= 0); - - sum += cur; - #if UZLIB_CONF_PARANOID_CHECKS - if (sum < 0 || sum >= TINF_ARRAY_SIZE(t->trans)) { - return TINF_DATA_ERROR; - } - #endif - - return t->trans[sum]; -} - -/* given a data stream, decode dynamic trees from it */ -static int tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) -{ - /* code lengths for 288 literal/len symbols and 32 dist symbols */ - unsigned char lengths[288+32]; - unsigned int hlit, hdist, hclen, hlimit; - unsigned int i, num, length; - - /* get 5 bits HLIT (257-286) */ - hlit = tinf_read_bits(d, 5, 257); - - /* get 5 bits HDIST (1-32) */ - hdist = tinf_read_bits(d, 5, 1); - - /* get 4 bits HCLEN (4-19) */ - hclen = tinf_read_bits(d, 4, 4); - - for (i = 0; i < 19; ++i) lengths[i] = 0; - - /* read code lengths for code length alphabet */ - for (i = 0; i < hclen; ++i) - { - /* get 3 bits code length (0-7) */ - unsigned int clen = tinf_read_bits(d, 3, 0); - - lengths[clcidx[i]] = clen; - } - - /* build code length tree, temporarily use length tree */ - tinf_build_tree(lt, lengths, 19); - - /* decode code lengths for the dynamic trees */ - hlimit = hlit + hdist; - for (num = 0; num < hlimit; ) - { - int sym = tinf_decode_symbol(d, lt); - unsigned char fill_value = 0; - int lbits, lbase = 3; - - /* error decoding */ - if (sym < 0) return sym; - - switch (sym) - { - case 16: - /* copy previous code length 3-6 times (read 2 bits) */ - if (num == 0) return TINF_DATA_ERROR; - fill_value = lengths[num - 1]; - lbits = 2; - break; - case 17: - /* repeat code length 0 for 3-10 times (read 3 bits) */ - lbits = 3; - break; - case 18: - /* repeat code length 0 for 11-138 times (read 7 bits) */ - lbits = 7; - lbase = 11; - break; - default: - /* values 0-15 represent the actual code lengths */ - lengths[num++] = sym; - /* continue the for loop */ - continue; - } - - /* special code length 16-18 are handled here */ - length = tinf_read_bits(d, lbits, lbase); - if (num + length > hlimit) return TINF_DATA_ERROR; - for (; length; --length) - { - lengths[num++] = fill_value; - } - } - - #if UZLIB_CONF_DEBUG_LOG >= 2 - printf("lit code lengths (%d):", hlit); - UZLIB_DUMP_ARRAY("", lengths, hlit); - printf("dist code lengths (%d):", hdist); - UZLIB_DUMP_ARRAY("", lengths + hlit, hdist); - #endif - - #if UZLIB_CONF_PARANOID_CHECKS - /* Check that there's "end of block" symbol */ - if (lengths[256] == 0) { - return TINF_DATA_ERROR; - } - #endif - - /* build dynamic trees */ - tinf_build_tree(lt, lengths, hlit); - tinf_build_tree(dt, lengths + hlit, hdist); - - return TINF_OK; -} - -/* ----------------------------- * - * -- block inflate functions -- * - * ----------------------------- */ - -/* given a stream and two trees, inflate next chunk of output (a byte or more) */ -static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) -{ - if (d->curlen == 0) { - unsigned int offs; - int dist; - int sym = tinf_decode_symbol(d, lt); - //printf("huff sym: %02x\n", sym); - - if (d->eof) { - return TINF_DATA_ERROR; - } - - /* literal byte */ - if (sym < 256) { - TINF_PUT(d, sym); - return TINF_OK; - } - - /* end of block */ - if (sym == 256) { - return TINF_DONE; - } - - /* substring from sliding dictionary */ - sym -= 257; - if (sym >= 29) { - return TINF_DATA_ERROR; - } - - /* possibly get more bits from length code */ - d->curlen = tinf_read_bits(d, length_bits[sym], length_base[sym]); - - dist = tinf_decode_symbol(d, dt); - if (dist >= 30) { - return TINF_DATA_ERROR; - } - - /* possibly get more bits from distance code */ - offs = tinf_read_bits(d, dist_bits[dist], dist_base[dist]); - - /* calculate and validate actual LZ offset to use */ - if (d->dict_ring) { - if (offs > d->dict_size) { - return TINF_DICT_ERROR; - } - /* Note: unlike full-dest-in-memory case below, we don't - try to catch offset which points to not yet filled - part of the dictionary here. Doing so would require - keeping another variable to track "filled in" size - of the dictionary. Appearance of such an offset cannot - lead to accessing memory outside of the dictionary - buffer, and clients which don't want to leak unrelated - information, should explicitly initialize dictionary - buffer passed to uzlib. */ - - d->lzOff = d->dict_idx - offs; - if (d->lzOff < 0) { - d->lzOff += d->dict_size; - } - } else { - /* catch trying to point before the start of dest buffer */ - if (offs > d->dest - d->destStart) { - return TINF_DATA_ERROR; - } - d->lzOff = -offs; - } - } - - /* copy next byte from dict substring */ - if (d->dict_ring) { - TINF_PUT(d, d->dict_ring[d->lzOff]); - if ((unsigned)++d->lzOff == d->dict_size) { - d->lzOff = 0; - } - } else { - #if UZLIB_CONF_USE_MEMCPY - /* copy as much as possible, in one memcpy() call */ - unsigned int to_copy = d->curlen, dest_len = d->dest_limit - d->dest; - if (to_copy > dest_len) { - to_copy = dest_len; - } - memcpy(d->dest, d->dest + d->lzOff, to_copy); - d->dest += to_copy; - d->curlen -= to_copy; - return TINF_OK; - #else - d->dest[0] = d->dest[d->lzOff]; - d->dest++; - #endif - } - d->curlen--; - return TINF_OK; -} - -/* inflate next byte from uncompressed block of data */ -static int tinf_inflate_uncompressed_block(TINF_DATA *d) -{ - if (d->curlen == 0) { - unsigned int length, invlength; - - /* get length */ - length = uzlib_get_byte(d); - length += 256 * uzlib_get_byte(d); - /* get one's complement of length */ - invlength = uzlib_get_byte(d); - invlength += 256 * uzlib_get_byte(d); - /* check length */ - if (length != (~invlength & 0x0000ffff)) return TINF_DATA_ERROR; - - /* increment length to properly return TINF_DONE below, without - producing data at the same time */ - d->curlen = length + 1; - - /* make sure we start next block on a byte boundary */ - d->bitcount = 0; - } - - if (--d->curlen == 0) { - return TINF_DONE; - } - - unsigned char c = uzlib_get_byte(d); - TINF_PUT(d, c); - return TINF_OK; -} - -/* ---------------------- * - * -- public functions -- * - * ---------------------- */ - -/* initialize global (static) data */ -void uzlib_init(void) -{ -#ifdef RUNTIME_BITS_TABLES - /* build extra bits and base tables */ - tinf_build_bits_base(length_bits, length_base, 4, 3); - tinf_build_bits_base(dist_bits, dist_base, 2, 1); - - /* fix a special case */ - length_bits[28] = 0; - length_base[28] = 258; -#endif -} - -/* initialize decompression structure */ -void uzlib_uncompress_init(TINF_DATA *d, void *dict, unsigned int dictLen) -{ - d->eof = 0; - d->bitcount = 0; - d->bfinal = 0; - d->btype = -1; - d->dict_size = dictLen; - d->dict_ring = dict; - d->dict_idx = 0; - d->curlen = 0; -} - -/* inflate next output bytes from compressed stream */ -int uzlib_uncompress(TINF_DATA *d) -{ - do { - int res; - - /* start a new block */ - if (d->btype == -1) { - int old_btype; -next_blk: - old_btype = d->btype; - /* read final block flag */ - d->bfinal = tinf_getbit(d); - /* read block type (2 bits) */ - d->btype = tinf_read_bits(d, 2, 0); - - #if UZLIB_CONF_DEBUG_LOG >= 1 - printf("Started new block: type=%d final=%d\n", d->btype, d->bfinal); - #endif - - if (d->btype == 1 && old_btype != 1) { - /* build fixed huffman trees */ - tinf_build_fixed_trees(&d->ltree, &d->dtree); - } else if (d->btype == 2) { - /* decode trees from stream */ - res = tinf_decode_trees(d, &d->ltree, &d->dtree); - if (res != TINF_OK) { - return res; - } - } - } - - /* process current block */ - switch (d->btype) - { - case 0: - /* decompress uncompressed block */ - res = tinf_inflate_uncompressed_block(d); - break; - case 1: - case 2: - /* decompress block with fixed/dynamic huffman trees */ - /* trees were decoded previously, so it's the same routine for both */ - res = tinf_inflate_block_data(d, &d->ltree, &d->dtree); - break; - default: - return TINF_DATA_ERROR; - } - - if (res == TINF_DONE && !d->bfinal) { - /* the block has ended (without producing more data), but we - can't return without data, so start procesing next block */ - goto next_blk; - } - - if (res != TINF_OK) { - return res; - } - - } while (d->dest < d->dest_limit); - - return TINF_OK; -} - -/* inflate next output bytes from compressed stream, updating - checksum, and at the end of stream, verify it */ -int uzlib_uncompress_chksum(TINF_DATA *d) -{ - int res; - unsigned char *data = d->dest; - - res = uzlib_uncompress(d); - - if (res < 0) return res; - - switch (d->checksum_type) { - - case TINF_CHKSUM_ADLER: - d->checksum = uzlib_adler32(data, d->dest - data, d->checksum); - break; - - case TINF_CHKSUM_CRC: - d->checksum = uzlib_crc32(data, d->dest - data, d->checksum); - break; - } - - if (res == TINF_DONE) { - unsigned int val; - - switch (d->checksum_type) { - - case TINF_CHKSUM_ADLER: - val = tinf_get_be_uint32(d); - if (d->checksum != val) { - return TINF_CHKSUM_ERROR; - } - break; - - case TINF_CHKSUM_CRC: - val = tinf_get_le_uint32(d); - if (~d->checksum != val) { - return TINF_CHKSUM_ERROR; - } - // Uncompressed size. TODO: Check - val = tinf_get_le_uint32(d); - break; - } - } - - return res; -} diff --git a/lib/uzlib/src/tinfzlib.c b/lib/uzlib/src/tinfzlib.c deleted file mode 100644 index 5cb8852..0000000 --- a/lib/uzlib/src/tinfzlib.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) - * - * Copyright (c) 2003 by Joergen Ibsen / Jibz - * All Rights Reserved - * - * http://www.ibsensoftware.com/ - * - * Copyright (c) 2014-2018 by Paul Sokolovsky - * - * This software is provided 'as-is', without any express - * or implied warranty. In no event will the authors be - * held liable for any damages arising from the use of - * this software. - * - * Permission is granted to anyone to use this software - * for any purpose, including commercial applications, - * and to alter it and redistribute it freely, subject to - * the following restrictions: - * - * 1. The origin of this software must not be - * misrepresented; you must not claim that you - * wrote the original software. If you use this - * software in a product, an acknowledgment in - * the product documentation would be appreciated - * but is not required. - * - * 2. Altered source versions must be plainly marked - * as such, and must not be misrepresented as - * being the original software. - * - * 3. This notice may not be removed or altered from - * any source distribution. - */ - -#include "tinf.h" - -int uzlib_zlib_parse_header(TINF_DATA *d) -{ - unsigned char cmf, flg; - - /* -- get header bytes -- */ - - cmf = uzlib_get_byte(d); - flg = uzlib_get_byte(d); - - /* -- check format -- */ - - /* check checksum */ - if ((256*cmf + flg) % 31) return TINF_DATA_ERROR; - - /* check method is deflate */ - if ((cmf & 0x0f) != 8) return TINF_DATA_ERROR; - - /* check window size is valid */ - if ((cmf >> 4) > 7) return TINF_DATA_ERROR; - - /* check there is no preset dictionary */ - if (flg & 0x20) return TINF_DATA_ERROR; - - /* initialize for adler32 checksum */ - d->checksum_type = TINF_CHKSUM_ADLER; - d->checksum = 1; - - return cmf >> 4; -} diff --git a/lib/uzlib/src/uzlib.h b/lib/uzlib/src/uzlib.h index 681ad74..a978783 100644 --- a/lib/uzlib/src/uzlib.h +++ b/lib/uzlib/src/uzlib.h @@ -1,173 +1,51 @@ /* - * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) + * OpenDisplay streaming zlib inflater, based on uzlib/tinf. * - * Copyright (c) 2003 by Joergen Ibsen / Jibz - * All Rights Reserved - * http://www.ibsensoftware.com/ - * - * Copyright (c) 2014-2018 by Paul Sokolovsky - * - * This software is provided 'as-is', without any express - * or implied warranty. In no event will the authors be - * held liable for any damages arising from the use of - * this software. - * - * Permission is granted to anyone to use this software - * for any purpose, including commercial applications, - * and to alter it and redistribute it freely, subject to - * the following restrictions: - * - * 1. The origin of this software must not be - * misrepresented; you must not claim that you - * wrote the original software. If you use this - * software in a product, an acknowledgment in - * the product documentation would be appreciated - * but is not required. - * - * 2. Altered source versions must be plainly marked - * as such, and must not be misrepresented as - * being the original software. - * - * 3. This notice may not be removed or altered from - * any source distribution. + * The original uzlib one-shot/callback inflater API is intentionally not + * exposed in this vendored copy. Firmware uses one global streaming inflater. */ #ifndef UZLIB_H_INCLUDED #define UZLIB_H_INCLUDED -#include -#include #include +#include +#include #ifdef __cplusplus extern "C" { #endif #include "uzlib_conf.h" -#if UZLIB_CONF_DEBUG_LOG -#include -#endif -/* calling convention */ -#ifndef TINFCC - #ifdef __WATCOMC__ - #define TINFCC __cdecl - #else - #define TINFCC - #endif +#ifndef OPENDISPLAY_ZLIB_WINDOW_BITS +#define OPENDISPLAY_ZLIB_WINDOW_BITS 9 #endif -/* ok status, more data produced */ -#define TINF_OK 0 -/* end of compressed stream reached */ -#define TINF_DONE 1 -#define TINF_DATA_ERROR (-3) -#define TINF_CHKSUM_ERROR (-4) -#define TINF_DICT_ERROR (-5) - -/* checksum types */ -#define TINF_CHKSUM_NONE 0 -#define TINF_CHKSUM_ADLER 1 -#define TINF_CHKSUM_CRC 2 - -/* helper macros */ -#define TINF_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*(arr))) - -/* data structures */ - -typedef struct { - unsigned short table[16]; /* table of code length counts */ - unsigned short trans[288]; /* code -> symbol translation table */ -} TINF_TREE; - -struct uzlib_uncomp { - /* Pointer to the next byte in the input buffer */ - const unsigned char *source; - /* Pointer to the next byte past the input buffer (source_limit = source + len) */ - const unsigned char *source_limit; - /* If source_limit == NULL, or source >= source_limit, this function - will be used to read next byte from source stream. The function may - also return -1 in case of EOF (or irrecoverable error). Note that - besides returning the next byte, it may also update source and - source_limit fields, thus allowing for buffered operation. */ - int (*source_read_cb)(struct uzlib_uncomp *uncomp); - - unsigned int tag; - unsigned int bitcount; - - /* Destination (output) buffer start */ - unsigned char *dest_start; - /* Current pointer in dest buffer */ - unsigned char *dest; - /* Pointer past the end of the dest buffer, similar to source_limit */ - unsigned char *dest_limit; - - /* Accumulating checksum */ - unsigned int checksum; - char checksum_type; - bool eof; - - int btype; - int bfinal; - unsigned int curlen; - int lzOff; - unsigned char *dict_ring; - unsigned int dict_size; - unsigned int dict_idx; - - TINF_TREE ltree; /* dynamic length/symbol tree */ - TINF_TREE dtree; /* dynamic distance tree */ -}; - -#include "tinf_compat.h" - -#define TINF_PUT(d, c) \ - { \ - *d->dest++ = c; \ - if (d->dict_ring) { d->dict_ring[d->dict_idx++] = c; if (d->dict_idx == d->dict_size) d->dict_idx = 0; } \ - } - -unsigned char TINFCC uzlib_get_byte(TINF_DATA *d); - -/* Decompression API */ - -void TINFCC uzlib_init(void); -void TINFCC uzlib_uncompress_init(TINF_DATA *d, void *dict, unsigned int dictLen); -int TINFCC uzlib_uncompress(TINF_DATA *d); -int TINFCC uzlib_uncompress_chksum(TINF_DATA *d); - -int TINFCC uzlib_zlib_parse_header(TINF_DATA *d); -int TINFCC uzlib_gzip_parse_header(TINF_DATA *d); - -/* Compression API */ - -typedef const uint8_t *uzlib_hash_entry_t; - -struct uzlib_comp { - unsigned char *outbuf; - int outlen, outsize; - unsigned long outbits; - int noutbits; - int comp_disabled; - - uzlib_hash_entry_t *hash_table; - unsigned int hash_bits; - unsigned int dict_size; -}; +#if OPENDISPLAY_ZLIB_WINDOW_BITS < 9 || OPENDISPLAY_ZLIB_WINDOW_BITS > 15 +#error "OPENDISPLAY_ZLIB_WINDOW_BITS must be in range 9..15" +#endif -void TINFCC uzlib_compress(struct uzlib_comp *c, const uint8_t *src, unsigned slen); +#define OPENDISPLAY_ZLIB_WINDOW_SIZE (1u << OPENDISPLAY_ZLIB_WINDOW_BITS) -#include "defl_static.h" +typedef enum { + OD_ZLIB_STATUS_NEEDS_INPUT = 0, + OD_ZLIB_STATUS_OUTPUT_READY = 1, + OD_ZLIB_STATUS_DONE = 2, + OD_ZLIB_STATUS_ERROR = -1, +} od_zlib_status_t; -/* Checksum API */ +void od_zlib_stream_reset(uint32_t expected_output_size); +od_zlib_status_t od_zlib_stream_push(const uint8_t *input, size_t len, bool final); +od_zlib_status_t od_zlib_stream_poll(uint8_t *output, size_t capacity, size_t *produced); +const char *od_zlib_stream_error(void); +uint32_t od_zlib_stream_output_count(void); -/* prev_sum is previous value for incremental computation, 1 initially */ -uint32_t TINFCC uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum); -/* crc is previous value for incremental computation, 0xffffffff initially */ -uint32_t TINFCC uzlib_crc32(const void *data, unsigned int length, uint32_t crc); +uint32_t uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum); +uint32_t uzlib_crc32(const void *data, unsigned int length, uint32_t crc); #ifdef __cplusplus -} /* extern "C" */ +} #endif #endif /* UZLIB_H_INCLUDED */ diff --git a/src/compression_config.h b/src/compression_config.h new file mode 100644 index 0000000..f501083 --- /dev/null +++ b/src/compression_config.h @@ -0,0 +1,6 @@ +#ifndef COMPRESSION_CONFIG_H +#define COMPRESSION_CONFIG_H + +#define OPENDISPLAY_DECOMPRESSION_CHUNK_SIZE 4096 + +#endif diff --git a/src/display_service.cpp b/src/display_service.cpp index 387df44..5d7a532 100644 --- a/src/display_service.cpp +++ b/src/display_service.cpp @@ -10,6 +10,7 @@ #include "communication.h" #include "encryption.h" #include "boot_screen.h" +#include "compression_config.h" #include "uzlib.h" #if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) #include "display_seeed_gfx.h" @@ -37,9 +38,7 @@ extern bool connectionRequested; extern uint8_t mloopcounter; extern bool displayPowerState; extern uint32_t directWriteStartTime; -extern uint8_t* directWriteCompressedBuffer; extern uint32_t directWriteCompressedReceived; -extern uint32_t directWriteCompressedSize; extern uint8_t directWriteRefreshMode; extern uint32_t directWriteTotalBytes; extern uint16_t directWriteHeight; @@ -50,9 +49,7 @@ extern bool directWritePlane2; extern bool directWriteBitplanes; extern bool directWriteCompressed; extern bool directWriteActive; -extern uint8_t* compressedDataBuffer; -extern uint8_t dictionaryBuffer[]; -extern uint8_t decompressionChunk[]; +extern uint8_t decompressionChunk[OPENDISPLAY_DECOMPRESSION_CHUNK_SIZE]; extern uint32_t displayed_etag; @@ -78,6 +75,8 @@ struct PartialStreamContext { uint32_t expected_stream_size; uint32_t plane_size; uint32_t bytes_received; + uint32_t bytes_written; + uint8_t current_plane; }; uint32_t max_compressed_image_rx_bytes(uint8_t tm) { @@ -113,8 +112,10 @@ void flashLed(uint8_t color, uint8_t brightness); static void cleanup_partial_write_state(void); static bool partial_consume_bytes(uint8_t* data, uint32_t len); static void partial_prepare_panel_ram(void); -static bool partial_prepare_logical_stream(void); static bool partial_write_to_panel(int refreshMode); +static bool partial_write_stream_bytes(uint8_t* data, uint32_t len); +static bool zlib_stream_to_direct_write(const uint8_t* data, uint32_t len, bool final); +static bool zlib_stream_to_partial_write(const uint8_t* data, uint32_t len, bool final); static uint32_t calc_controller_plane_bytes(uint16_t width, uint16_t height); static uint32_t parse_be_u32(const uint8_t* data); static void send_direct_write_nack(uint8_t opcode, uint8_t error, bool cleanupState); @@ -1121,62 +1122,21 @@ void updatemsdata(){ } void handleDirectWriteCompressedData(uint8_t* data, uint16_t len) { - if (!compressedDataBuffer) { - cleanupDirectWriteState(false); - uint8_t errorResponse[] = {0xFF, 0xFF}; - sendResponse(errorResponse, sizeof(errorResponse)); - return; - } - uint32_t newTotalSize = directWriteCompressedReceived + len; - uint32_t cap = (globalConfig.display_count > 0) - ? max_compressed_image_rx_bytes(globalConfig.displays[0].transmission_modes) - : 0; - if (cap == 0 || newTotalSize > cap) { + uint32_t cap = (globalConfig.display_count > 0) ? max_compressed_image_rx_bytes(globalConfig.displays[0].transmission_modes) : 0; + if (cap == 0 || + directWriteCompressedReceived > cap || + len > cap - directWriteCompressedReceived || + !zlib_stream_to_direct_write(data, len, false)) { cleanupDirectWriteState(true); uint8_t errorResponse[] = {0xFF, 0xFF}; sendResponse(errorResponse, sizeof(errorResponse)); return; } - memcpy(directWriteCompressedBuffer + directWriteCompressedReceived, data, len); directWriteCompressedReceived += len; uint8_t ackResponse[] = {0x00, 0x71}; sendResponse(ackResponse, sizeof(ackResponse)); } -void decompressDirectWriteData() { - if (directWriteCompressedReceived == 0) return; - struct uzlib_uncomp d; - memset(&d, 0, sizeof(d)); - d.source = directWriteCompressedBuffer; - d.source_limit = directWriteCompressedBuffer + directWriteCompressedReceived; - d.source_read_cb = NULL; - uzlib_init(); - int hdr = uzlib_zlib_parse_header(&d); - if (hdr < 0) return; - uint16_t window = 0x100 << hdr; - if (window > (uint16_t)(32 * 1024)) window = (uint16_t)(32 * 1024); - uzlib_uncompress_init(&d, dictionaryBuffer, window); - int res; - do { - d.dest_start = decompressionChunk; - d.dest = decompressionChunk; - d.dest_limit = decompressionChunk + 4096; - res = uzlib_uncompress(&d); - size_t bytesOut = d.dest - d.dest_start; - if (bytesOut > 0) { -#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) - if (seeed_driver_used()) { - seeed_gfx_direct_write_chunk(decompressionChunk, (uint32_t)bytesOut); - } else -#endif - { - bbepWriteData(&bbep, decompressionChunk, bytesOut); - } - directWriteBytesWritten += bytesOut; - } - } while (res == TINF_OK && directWriteBytesWritten < directWriteTotalBytes); -} - void cleanupDirectWriteState(bool refreshDisplay) { directWriteActive = false; directWriteCompressed = false; @@ -1184,9 +1144,7 @@ void cleanupDirectWriteState(bool refreshDisplay) { directWritePlane2 = false; directWriteBytesWritten = 0; directWriteCompressedReceived = 0; - directWriteCompressedSize = 0; directWriteDecompressedTotal = 0; - directWriteCompressedBuffer = nullptr; directWriteWidth = 0; directWriteHeight = 0; directWriteTotalBytes = 0; @@ -1230,28 +1188,7 @@ void handleDirectWriteStart(uint8_t* data, uint16_t len) { else directWriteTotalBytes = (pixels + 7) / 8; } if (directWriteCompressed) { - if (!compressedDataBuffer) { - cleanupDirectWriteState(false); - uint8_t errorResponse[] = {0xFF, 0xFF}; - sendResponse(errorResponse, sizeof(errorResponse)); - return; - } memcpy(&directWriteDecompressedTotal, data, 4); - directWriteCompressedBuffer = compressedDataBuffer; - directWriteCompressedSize = 0; - directWriteCompressedReceived = 0; - if (len > 4) { - uint32_t compressedDataLen = len - 4; - uint32_t cap = max_compressed_image_rx_bytes(globalConfig.displays[0].transmission_modes); - if (compressedDataLen > cap) { - cleanupDirectWriteState(false); - uint8_t errorResponse[] = {0xFF, 0xFF}; - sendResponse(errorResponse, sizeof(errorResponse)); - return; - } - memcpy(directWriteCompressedBuffer, data + 4, compressedDataLen); - directWriteCompressedReceived = compressedDataLen; - } } directWriteActive = true; directWriteBytesWritten = 0; @@ -1273,6 +1210,20 @@ void handleDirectWriteStart(uint8_t* data, uint16_t len) { bbepSetAddrWindow(&bbep, 0, 0, globalConfig.displays[0].pixel_width, globalConfig.displays[0].pixel_height); bbepStartWrite(&bbep, directWriteBitplanes ? PLANE_0 : getplane()); } + if (directWriteCompressed) { + od_zlib_stream_reset(directWriteDecompressedTotal); + if (len > 4) { + uint32_t compressedDataLen = len - 4; + uint32_t cap = max_compressed_image_rx_bytes(globalConfig.displays[0].transmission_modes); + if (compressedDataLen > cap || !zlib_stream_to_direct_write(data + 4, compressedDataLen, false)) { + cleanupDirectWriteState(false); + uint8_t errorResponse[] = {0xFF, 0xFF}; + sendResponse(errorResponse, sizeof(errorResponse)); + return; + } + directWriteCompressedReceived = compressedDataLen; + } + } uint8_t ackResponse[] = {0x00, 0x70}; sendResponse(ackResponse, sizeof(ackResponse)); } @@ -1335,18 +1286,12 @@ void handlePartialWriteStart(uint8_t* data, uint16_t len) { uint32_t planeBytes = calc_controller_plane_bytes(rectW, rectH); uint32_t expectedLogicalSize = planeBytes * 2u; - if (!compressedDataBuffer || expectedLogicalSize > MAX_COMPRESSED_BUFFER_BYTES) { + if (expectedLogicalSize == 0) { uint8_t errResponse[] = {0xFF, 0xFF}; sendResponse(errResponse, sizeof(errResponse)); return; } - uint32_t rxOffset = ((flags & PARTIAL_FLAG_COMPRESSED) != 0) ? expectedLogicalSize : 0u; - if (rxOffset >= MAX_COMPRESSED_BUFFER_BYTES) { - send_direct_write_nack(0x76, ERR_PARTIAL_STREAM, false); - return; - } - memset(&partialCtx, 0, sizeof(partialCtx)); partialCtx.active = true; partialCtx.compressed = (flags & PARTIAL_FLAG_COMPRESSED) != 0; @@ -1358,8 +1303,10 @@ void handlePartialWriteStart(uint8_t* data, uint16_t len) { partialCtx.height = rectH; partialCtx.expected_stream_size = expectedLogicalSize; partialCtx.plane_size = planeBytes; - directWriteCompressedBuffer = compressedDataBuffer + rxOffset; - directWriteCompressedReceived = 0; + partialCtx.current_plane = 0xFF; + + partial_prepare_panel_ram(); + if (partialCtx.compressed) od_zlib_stream_reset(expectedLogicalSize); // Process optional initial stream bytes before ACK if (len > 17) { @@ -1370,8 +1317,6 @@ void handlePartialWriteStart(uint8_t* data, uint16_t len) { } } - partial_prepare_panel_ram(); - uint8_t ackResponse[] = {0x00, 0x76}; sendResponse(ackResponse, sizeof(ackResponse)); } @@ -1420,11 +1365,11 @@ void handleDirectWriteEnd(uint8_t* data, uint16_t len) { return; } if (partialCtx.compressed) { - if (partialCtx.bytes_received == 0 || !partial_prepare_logical_stream()) { + if (partialCtx.bytes_received == 0 || !zlib_stream_to_partial_write(nullptr, 0, true)) { send_direct_write_nack(0x72, ERR_PARTIAL_STREAM, true); return; } - } else if (partialCtx.bytes_received != partialCtx.expected_stream_size) { + } else if (partialCtx.bytes_written != partialCtx.expected_stream_size) { send_direct_write_nack(0x72, ERR_PARTIAL_STREAM, true); return; } @@ -1448,7 +1393,12 @@ void handleDirectWriteEnd(uint8_t* data, uint16_t len) { } if (!directWriteActive) return; directWriteStartTime = 0; - if (directWriteCompressed && directWriteCompressedReceived > 0) decompressDirectWriteData(); + if (directWriteCompressed && !zlib_stream_to_direct_write(nullptr, 0, true)) { + cleanupDirectWriteState(true); + uint8_t errorResponse[] = {0xFF, 0xFF}; + sendResponse(errorResponse, sizeof(errorResponse)); + return; + } int refreshMode = REFRESH_FULL; if (data != nullptr && len >= 1 && data[0] == 1) refreshMode = REFRESH_FAST; writeSerial("EPD refresh: ", false); @@ -1496,59 +1446,95 @@ void handleDirectWriteEnd(uint8_t* data, uint16_t len) { } static void cleanup_partial_write_state(void) { + bool powerDown = partialCtx.active && displayPowerState; memset(&partialCtx, 0, sizeof(partialCtx)); - directWriteCompressedBuffer = nullptr; - directWriteCompressedReceived = 0; + if (powerDown) { + bbepSleep(&bbep, 1); + delay(200); + displayPowerState = false; + pwrmgm(false); + } } static bool partial_consume_bytes(uint8_t* data, uint32_t len) { - if (!directWriteCompressedBuffer) return false; - uint32_t rxLimit = partialCtx.compressed - ? (MAX_COMPRESSED_BUFFER_BYTES - partialCtx.expected_stream_size) + uint32_t limit = partialCtx.compressed + ? max_compressed_image_rx_bytes(globalConfig.displays[0].transmission_modes) : partialCtx.expected_stream_size; - if (len > rxLimit - partialCtx.bytes_received) return false; - memcpy(directWriteCompressedBuffer + partialCtx.bytes_received, data, len); + if (limit == 0 || partialCtx.bytes_received > limit || len > limit - partialCtx.bytes_received) return false; partialCtx.bytes_received += len; - directWriteCompressedReceived = partialCtx.bytes_received; - return true; + if (partialCtx.compressed) return zlib_stream_to_partial_write(data, len, false); + return partial_write_stream_bytes(data, len); } -static bool partial_prepare_logical_stream(void) { - if (!directWriteCompressedBuffer || !compressedDataBuffer) return false; - if (!partialCtx.compressed) return partialCtx.bytes_received == partialCtx.expected_stream_size; - - struct uzlib_uncomp d; - memset(&d, 0, sizeof(d)); - d.source = directWriteCompressedBuffer; - d.source_limit = directWriteCompressedBuffer + partialCtx.bytes_received; - d.source_read_cb = NULL; - uzlib_init(); - int hdr = uzlib_zlib_parse_header(&d); - if (hdr < 0) return false; - uint16_t window = 0x100 << hdr; - if (window > (uint16_t)(32 * 1024)) window = (uint16_t)(32 * 1024); - uzlib_uncompress_init(&d, dictionaryBuffer, window); - - uint32_t bytesOutTotal = 0; - int res; - do { - d.dest_start = decompressionChunk; - d.dest = decompressionChunk; - d.dest_limit = decompressionChunk + 4096; - res = uzlib_uncompress(&d); - size_t bytesOut = d.dest - d.dest_start; +static bool zlib_stream_to_direct_write(const uint8_t* data, uint32_t len, bool final) { + od_zlib_status_t status = od_zlib_stream_push(data, len, final); + if (status == OD_ZLIB_STATUS_ERROR) { + writeSerial(String("zlib stream error: ") + od_zlib_stream_error(), true); + return false; + } + + for (;;) { + size_t bytesOut = 0; + status = od_zlib_stream_poll(decompressionChunk, OPENDISPLAY_DECOMPRESSION_CHUNK_SIZE, &bytesOut); if (bytesOut > 0) { - if (bytesOutTotal + bytesOut > partialCtx.expected_stream_size) return false; - memcpy(compressedDataBuffer + bytesOutTotal, decompressionChunk, bytesOut); - bytesOutTotal += bytesOut; +#if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) + if (seeed_driver_used()) { + seeed_gfx_direct_write_chunk(decompressionChunk, (uint32_t)bytesOut); + } else +#endif + { + bbepWriteData(&bbep, decompressionChunk, bytesOut); + } + directWriteBytesWritten += (uint32_t)bytesOut; + if (directWriteBytesWritten > directWriteDecompressedTotal) return false; + } + if (status == OD_ZLIB_STATUS_OUTPUT_READY) continue; + if (status == OD_ZLIB_STATUS_NEEDS_INPUT) return !final; + if (status == OD_ZLIB_STATUS_DONE) return directWriteBytesWritten == directWriteDecompressedTotal; + writeSerial(String("zlib stream error: ") + od_zlib_stream_error(), true); + return false; + } +} + +static bool zlib_stream_to_partial_write(const uint8_t* data, uint32_t len, bool final) { + od_zlib_status_t status = od_zlib_stream_push(data, len, final); + if (status == OD_ZLIB_STATUS_ERROR) { + writeSerial(String("partial zlib stream error: ") + od_zlib_stream_error(), true); + return false; + } + + for (;;) { + size_t bytesOut = 0; + status = od_zlib_stream_poll(decompressionChunk, OPENDISPLAY_DECOMPRESSION_CHUNK_SIZE, &bytesOut); + if (bytesOut > 0 && !partial_write_stream_bytes(decompressionChunk, (uint32_t)bytesOut)) return false; + if (status == OD_ZLIB_STATUS_OUTPUT_READY) continue; + if (status == OD_ZLIB_STATUS_NEEDS_INPUT) return !final; + if (status == OD_ZLIB_STATUS_DONE) return partialCtx.bytes_written == partialCtx.expected_stream_size; + writeSerial(String("partial zlib stream error: ") + od_zlib_stream_error(), true); + return false; + } +} + +static bool partial_write_stream_bytes(uint8_t* data, uint32_t len) { + uint32_t offset = 0; + while (offset < len) { + if (partialCtx.bytes_written >= partialCtx.expected_stream_size) return false; + + uint8_t targetPlane = partialCtx.bytes_written < partialCtx.plane_size ? PLANE_1 : PLANE_0; + if (partialCtx.current_plane != targetPlane) { + if (targetPlane == PLANE_0 && partialCtx.bytes_written != partialCtx.plane_size) return false; + bbepSetAddrWindow(&bbep, partialCtx.x, partialCtx.y, partialCtx.width, partialCtx.height); + bbepStartWrite(&bbep, targetPlane); + partialCtx.current_plane = targetPlane; } - } while (res == TINF_OK && bytesOutTotal < partialCtx.expected_stream_size); - if (res != TINF_DONE) return false; - if (bytesOutTotal != partialCtx.expected_stream_size) return false; - directWriteCompressedBuffer = compressedDataBuffer; - directWriteCompressedReceived = bytesOutTotal; - partialCtx.bytes_received = bytesOutTotal; + uint32_t planeEnd = targetPlane == PLANE_1 ? partialCtx.plane_size : partialCtx.expected_stream_size; + uint32_t chunk = planeEnd - partialCtx.bytes_written; + if (chunk > len - offset) chunk = len - offset; + bbepWriteData(&bbep, data + offset, (int)chunk); + partialCtx.bytes_written += chunk; + offset += chunk; + } return true; } @@ -1565,8 +1551,6 @@ static void partial_prepare_panel_ram(void) { } static bool partial_write_to_panel(int refreshMode) { - if (!compressedDataBuffer) return false; - writeSerial("EPD refresh: PARTIAL (raw rect ", false); writeSerial(String(partialCtx.x), false); writeSerial(",", false); @@ -1577,18 +1561,7 @@ static bool partial_write_to_panel(int refreshMode) { writeSerial(String(partialCtx.height), false); writeSerial(")", true); - if (!displayPowerState) { - pwrmgm(true); - bbepInitIO(&bbep, globalConfig.displays[0].dc_pin, globalConfig.displays[0].reset_pin, globalConfig.displays[0].busy_pin, globalConfig.displays[0].cs_pin, globalConfig.displays[0].data_pin, globalConfig.displays[0].clk_pin, 8000000); - bbepWakeUp(&bbep); - bbepSendCMDSequence(&bbep, bbep.pInitFull); - } - bbepSetAddrWindow(&bbep, partialCtx.x, partialCtx.y, partialCtx.width, partialCtx.height); - bbepStartWrite(&bbep, PLANE_1); - bbepWriteData(&bbep, compressedDataBuffer, partialCtx.plane_size); - bbepSetAddrWindow(&bbep, partialCtx.x, partialCtx.y, partialCtx.width, partialCtx.height); - bbepStartWrite(&bbep, PLANE_0); - bbepWriteData(&bbep, compressedDataBuffer + partialCtx.plane_size, partialCtx.plane_size); + if (partialCtx.bytes_written != partialCtx.expected_stream_size) return false; delay(20); bbepRefresh(&bbep, refreshMode); bool refreshSuccess = waitforrefresh(60); diff --git a/src/display_service.h b/src/display_service.h index d478fbe..10b433f 100644 --- a/src/display_service.h +++ b/src/display_service.h @@ -26,7 +26,6 @@ void writeTextAndFill(const char* text); void handleDirectWriteStart(uint8_t* data, uint16_t len); void handleDirectWriteData(uint8_t* data, uint16_t len); void handleDirectWriteCompressedData(uint8_t* data, uint16_t len); -void decompressDirectWriteData(); void cleanupDirectWriteState(bool refreshDisplay); void handleDirectWriteEnd(uint8_t* data, uint16_t len); void handlePartialWriteStart(uint8_t* data, uint16_t len); diff --git a/src/main.cpp b/src/main.cpp index 7d7b0c2..0fa5962 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,4 @@ #include "main.h" -#ifdef TARGET_ESP32 -#include -#endif #include "boot_screen.h" #include "communication.h" #include "device_control.h" @@ -20,26 +17,6 @@ static HardwareSerial LogSerialPort(1); #endif -#if defined(TARGET_NRF) -static uint8_t s_compressedDataStorage[MAX_COMPRESSED_BUFFER_BYTES]; -uint8_t* compressedDataBuffer = s_compressedDataStorage; -#elif defined(TARGET_ESP32) && defined(TARGET_LARGE_MEMORY) && defined(BOARD_HAS_PSRAM) -uint8_t* compressedDataBuffer = nullptr; -#else -static uint8_t s_compressedDataStorage[MAX_COMPRESSED_BUFFER_BYTES]; -uint8_t* compressedDataBuffer = s_compressedDataStorage; -#endif - -void allocCompressedDataBuffer(void) { -#if defined(TARGET_ESP32) && defined(TARGET_LARGE_MEMORY) && defined(BOARD_HAS_PSRAM) - if (compressedDataBuffer) return; - compressedDataBuffer = (uint8_t*)heap_caps_malloc(MAX_COMPRESSED_BUFFER_BYTES, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if (!compressedDataBuffer) { - compressedDataBuffer = (uint8_t*)heap_caps_malloc(MAX_COMPRESSED_BUFFER_BYTES, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); - } -#endif -} - void setup() { #if defined(TARGET_ESP32) && defined(OPENDISPLAY_LOG_UART) LogSerialPort.begin(115200, SERIAL_8N1, OPENDISPLAY_LOG_UART_RX, OPENDISPLAY_LOG_UART_TX); @@ -48,7 +25,6 @@ void setup() { Serial.begin(115200); delay(100); #endif - allocCompressedDataBuffer(); writeSerial("=== FIRMWARE INFO ==="); writeSerial("Firmware Version: " + String(getFirmwareMajor()) + "." + String(getFirmwareMinor())); const char* shaCStr = SHA_STRING; @@ -239,7 +215,6 @@ uint8_t pinToButtonIndex[64] = {0xFF}; // Map pin number to button index (max 6 #ifdef TARGET_ESP32 void minimalSetup() { - allocCompressedDataBuffer(); writeSerial("=== Minimal Setup (Deep Sleep Wake) ==="); full_config_init(); initio(); @@ -520,4 +495,4 @@ bool powerDownExternalFlash(uint8_t mosiPin, uint8_t misoPin, uint8_t sckPin, ui return false; #endif return false; -} \ No newline at end of file +} diff --git a/src/main.h b/src/main.h index 21d19ab..817b4a0 100644 --- a/src/main.h +++ b/src/main.h @@ -7,6 +7,7 @@ #include "config_parser.h" #include "ble_init.h" #include "wifi_service.h" +#include "compression_config.h" #ifndef BUILD_VERSION #define BUILD_VERSION "0.0" @@ -31,9 +32,6 @@ using namespace Adafruit_LittleFS_Namespace; #include -#define DECOMP_CHUNK 512 -#define DECOMP_CHUNK_SIZE 4096 -#define MAX_DICT_SIZE 32768 #define MAX_BLOCKS 64 // Text rendering constants #define FONT_CHAR_WIDTH 16 // 7 font columns + 1 blank column, each doubled (8*2) @@ -120,11 +118,7 @@ void bbepSendCMDSequence(BBEPDISP *pBBEP, const uint8_t *pSeq); void bbepSetAddrWindow(BBEPDISP *pBBEP, int x, int y, int cx, int cy); void bbepWriteData(BBEPDISP *pBBEP, uint8_t *pData, int iLen); -extern uint8_t* compressedDataBuffer; -void allocCompressedDataBuffer(void); - -uint8_t decompressionChunk[DECOMP_CHUNK_SIZE]; -uint8_t dictionaryBuffer[MAX_DICT_SIZE]; +uint8_t decompressionChunk[OPENDISPLAY_DECOMPRESSION_CHUNK_SIZE]; uint8_t bleResponseBuffer[94]; uint8_t mloopcounter = 0; uint8_t rebootFlag = 1; // Set to 1 after reboot, cleared to 0 after BLE connection @@ -191,11 +185,8 @@ uint16_t directWriteHeight = 0; // Display height in pixels uint32_t directWriteTotalBytes = 0; // Total bytes expected per plane (for bitplanes) or total (for others) uint8_t directWriteRefreshMode = 0; // 0 = FULL (default), 1 = FAST/PARTIAL (if supported) uint8_t directWriteDataKind = 0; // none; display_service.cpp tracks full vs partial 0x71 streams +uint32_t directWriteCompressedReceived = 0; // Total compressed bytes received for sanity limits -// Direct write compressed mode: use same buffer as regular image upload -uint32_t directWriteCompressedSize = 0; // Total compressed size expected -uint32_t directWriteCompressedReceived = 0; // Total compressed bytes received -uint8_t* directWriteCompressedBuffer = nullptr; // Points at compressedDataBuffer when compressed direct-write is active uint32_t directWriteStartTime = 0; // Timestamp when direct write started (for timeout detection) bool displayPowerState = false; // Track display power state (true = powered on, false = powered off) diff --git a/tools/test_zlib_stream.c b/tools/test_zlib_stream.c new file mode 100644 index 0000000..5c4a0da --- /dev/null +++ b/tools/test_zlib_stream.c @@ -0,0 +1,215 @@ +#include +#include +#include +#include +#include +#include + +#include "uzlib.h" + +static int make_zlib(const uint8_t *src, size_t src_len, int window_bits, int level, int strategy, uint8_t **out, size_t *out_len) { + z_stream zs; + size_t cap = src_len + 1024; + int rc; + memset(&zs, 0, sizeof(zs)); + *out = (uint8_t *)malloc(cap); + if (!*out) return 0; + + rc = deflateInit2(&zs, level, Z_DEFLATED, window_bits, 8, strategy); + if (rc != Z_OK) return 0; + zs.next_in = (Bytef *)src; + zs.avail_in = (uInt)src_len; + zs.next_out = *out; + zs.avail_out = (uInt)cap; + rc = deflate(&zs, Z_FINISH); + if (rc != Z_STREAM_END) { + deflateEnd(&zs); + return 0; + } + *out_len = zs.total_out; + deflateEnd(&zs); + return 1; +} + +static int run_decode_case(const char *name, const uint8_t *compressed, size_t compressed_len, + const uint8_t *expected, size_t expected_len, + size_t input_chunk, size_t output_chunk) { + uint8_t *actual = (uint8_t *)malloc(expected_len ? expected_len : 1); + size_t actual_len = 0; + size_t pos = 0; + bool final_sent = false; + od_zlib_status_t status = OD_ZLIB_STATUS_NEEDS_INPUT; + + if (!actual) return 0; + od_zlib_stream_reset((uint32_t)expected_len); + + while (status != OD_ZLIB_STATUS_DONE) { + if (status == OD_ZLIB_STATUS_NEEDS_INPUT) { + if (pos < compressed_len) { + size_t n = compressed_len - pos; + if (n > input_chunk) n = input_chunk; + status = od_zlib_stream_push(compressed + pos, n, false); + pos += n; + } else if (!final_sent) { + status = od_zlib_stream_push(NULL, 0, true); + final_sent = true; + } else { + fprintf(stderr, "%s: decoder requested input after final\n", name); + free(actual); + return 0; + } + if (status == OD_ZLIB_STATUS_ERROR) { + fprintf(stderr, "%s: push error: %s\n", name, od_zlib_stream_error()); + free(actual); + return 0; + } + } + + for (;;) { + uint8_t outbuf[4096]; + size_t produced = 0; + size_t cap = output_chunk < sizeof(outbuf) ? output_chunk : sizeof(outbuf); + status = od_zlib_stream_poll(outbuf, cap, &produced); + if (produced > 0) { + if (actual_len + produced > expected_len) { + fprintf(stderr, "%s: produced too much output\n", name); + free(actual); + return 0; + } + memcpy(actual + actual_len, outbuf, produced); + actual_len += produced; + } + if (status == OD_ZLIB_STATUS_OUTPUT_READY) continue; + break; + } + + if (status == OD_ZLIB_STATUS_ERROR) { + fprintf(stderr, "%s: poll error: %s (out=%u expected=%zu)\n", name, od_zlib_stream_error(), od_zlib_stream_output_count(), expected_len); + free(actual); + return 0; + } + } + + if (actual_len != expected_len || memcmp(actual, expected, expected_len) != 0) { + fprintf(stderr, "%s: output mismatch (%zu != %zu)\n", name, actual_len, expected_len); + size_t limit = actual_len < expected_len ? actual_len : expected_len; + for (size_t i = 0; i < limit; ++i) { + if (actual[i] != expected[i]) { + fprintf(stderr, "%s: first diff at %zu actual=%02x expected=%02x\n", name, i, actual[i], expected[i]); + break; + } + } + free(actual); + return 0; + } + free(actual); + return 1; +} + +static int expect_error(const char *name, const uint8_t *compressed, size_t compressed_len, size_t expected_len) { + size_t pos = 0; + od_zlib_status_t status = OD_ZLIB_STATUS_NEEDS_INPUT; + od_zlib_stream_reset((uint32_t)expected_len); + while (status != OD_ZLIB_STATUS_DONE) { + if (status == OD_ZLIB_STATUS_NEEDS_INPUT) { + size_t n = compressed_len - pos; + if (n > 3) n = 3; + status = od_zlib_stream_push(pos < compressed_len ? compressed + pos : NULL, pos < compressed_len ? n : 0, pos >= compressed_len); + pos += n; + if (status == OD_ZLIB_STATUS_ERROR) return 1; + } + uint8_t outbuf[7]; + size_t produced = 0; + status = od_zlib_stream_poll(outbuf, sizeof(outbuf), &produced); + if (status == OD_ZLIB_STATUS_ERROR) return 1; + if (pos >= compressed_len && status == OD_ZLIB_STATUS_NEEDS_INPUT) { + status = od_zlib_stream_push(NULL, 0, true); + } + } + fprintf(stderr, "%s: expected error but decode succeeded\n", name); + return 0; +} + +static int expect_error_one_push(const char *name, const uint8_t *compressed, size_t compressed_len, size_t expected_len) { + od_zlib_status_t status; + od_zlib_stream_reset((uint32_t)expected_len); + status = od_zlib_stream_push(compressed, compressed_len, true); + if (status == OD_ZLIB_STATUS_ERROR) return 1; + while (status != OD_ZLIB_STATUS_DONE) { + uint8_t outbuf[4096]; + size_t produced = 0; + status = od_zlib_stream_poll(outbuf, sizeof(outbuf), &produced); + if (status == OD_ZLIB_STATUS_ERROR) return 1; + } + fprintf(stderr, "%s: expected error but decode succeeded\n", name); + return 0; +} + +static void fill_fixture(uint8_t *buf, size_t len) { + for (size_t i = 0; i < len; ++i) { + if ((i % 97) < 64) buf[i] = (uint8_t)('A' + (i % 5)); + else buf[i] = (uint8_t)((i * 37u + i / 3u) & 0xffu); + } +} + +int main(void) { + uint8_t src[8192]; + uint8_t *compressed = NULL; + size_t compressed_len = 0; + int ok = 1; + fill_fixture(src, sizeof(src)); + + struct { + const char *name; + int level; + int strategy; + } cases[] = { + {"stored", 0, Z_DEFAULT_STRATEGY}, + {"fixed", 6, Z_FIXED}, + {"dynamic", 9, Z_DEFAULT_STRATEGY}, + }; + + for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); ++i) { + if (!make_zlib(src, sizeof(src), 9, cases[i].level, cases[i].strategy, &compressed, &compressed_len)) { + fprintf(stderr, "failed to create %s fixture\n", cases[i].name); + return 1; + } + ok &= run_decode_case(cases[i].name, compressed, compressed_len, src, sizeof(src), 1, 1); + ok &= run_decode_case(cases[i].name, compressed, compressed_len, src, sizeof(src), 23, 7); + ok &= run_decode_case(cases[i].name, compressed, compressed_len, src, sizeof(src), compressed_len, 4096); + free(compressed); + compressed = NULL; + } + + if (!make_zlib(src, sizeof(src), 10, 6, Z_DEFAULT_STRATEGY, &compressed, &compressed_len)) { + fprintf(stderr, "failed to create ws10 fixture\n"); + return 1; + } + ok &= expect_error("oversized-window", compressed, compressed_len, sizeof(src)); + free(compressed); + + if (!make_zlib(src, sizeof(src), 9, 6, Z_DEFAULT_STRATEGY, &compressed, &compressed_len)) { + fprintf(stderr, "failed to create corrupt fixtures\n"); + return 1; + } + ok &= expect_error("truncated", compressed, compressed_len / 2, sizeof(src)); + compressed[compressed_len - 1] ^= 0x55; + ok &= expect_error("bad-adler", compressed, compressed_len, sizeof(src)); + free(compressed); + + if (!make_zlib(src, sizeof(src), 9, 6, Z_DEFAULT_STRATEGY, &compressed, &compressed_len)) { + fprintf(stderr, "failed to create trailing fixture\n"); + return 1; + } + uint8_t *with_trailing = (uint8_t *)malloc(compressed_len + 1); + if (!with_trailing) return 1; + memcpy(with_trailing, compressed, compressed_len); + with_trailing[compressed_len] = 0xaa; + ok &= expect_error_one_push("trailing-input", with_trailing, compressed_len + 1, sizeof(src)); + free(with_trailing); + free(compressed); + + if (!ok) return 1; + printf("zlib stream tests passed\n"); + return 0; +} From e86a67cc411fb797536aa3f80058f4ce5f82cbe8 Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Fri, 8 May 2026 23:26:24 +0200 Subject: [PATCH 3/5] Reduce zlib streaming RAM --- lib/uzlib/src/od_zlib_stream.c | 67 ++++++++++++++++++++++------------ src/compression_config.h | 2 +- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/lib/uzlib/src/od_zlib_stream.c b/lib/uzlib/src/od_zlib_stream.c index c2f795f..0944ab7 100644 --- a/lib/uzlib/src/od_zlib_stream.c +++ b/lib/uzlib/src/od_zlib_stream.c @@ -10,7 +10,17 @@ typedef struct { unsigned short table[16]; unsigned short trans[288]; -} TINF_TREE; +} TINF_LITERAL_TREE; + +typedef struct { + unsigned short table[16]; + unsigned short trans[32]; +} TINF_DISTANCE_TREE; + +typedef struct { + unsigned short table[16]; + unsigned short trans[19]; +} TINF_CODELEN_TREE; static const unsigned char length_bits[30] = { 0, 0, 0, 0, 0, 0, 0, 0, @@ -108,10 +118,10 @@ typedef struct { uint8_t bfinal; uint8_t btype; - TINF_TREE ltree; + TINF_LITERAL_TREE ltree; union { - TINF_TREE dtree; - TINF_TREE cltree; + TINF_DISTANCE_TREE dtree; + TINF_CODELEN_TREE cltree; } tree; unsigned char lengths[288 + 32]; @@ -153,22 +163,33 @@ static void set_error(const char *error) { s.error = error; } -static void build_tree(TINF_TREE *t, const unsigned char *lengths, unsigned int num) { +static bool build_tree(unsigned short *table, unsigned short *trans, unsigned int trans_size, const unsigned char *lengths, unsigned int num) { unsigned short offs[16]; unsigned int i, sum; - for (i = 0; i < 16; ++i) t->table[i] = 0; - for (i = 0; i < num; ++i) t->table[lengths[i]]++; - t->table[0] = 0; + for (i = 0; i < 16; ++i) table[i] = 0; + for (i = 0; i < num; ++i) { + if (lengths[i] >= TINF_ARRAY_SIZE(offs)) { + set_error("invalid huffman code length"); + return false; + } + table[lengths[i]]++; + } + table[0] = 0; for (sum = 0, i = 0; i < 16; ++i) { offs[i] = sum; - sum += t->table[i]; + sum += table[i]; + } + if (sum > trans_size) { + set_error("huffman tree exceeds symbol storage"); + return false; } for (i = 0; i < num; ++i) { - if (lengths[i]) t->trans[offs[lengths[i]]++] = i; + if (lengths[i]) trans[offs[lengths[i]]++] = i; } + return true; } static void build_fixed_trees(void) { @@ -235,7 +256,7 @@ static int read_bits(unsigned int num, unsigned int base, unsigned int *value) { return 1; } -static int decode_symbol(TINF_TREE *tree, int *symbol) { +static int decode_symbol(const unsigned short *table, const unsigned short *trans, unsigned int trans_size, int *symbol) { if (!s.sym_active) { s.sym_active = true; s.sym_sum = 0; @@ -249,21 +270,21 @@ static int decode_symbol(TINF_TREE *tree, int *symbol) { if (rc <= 0) return rc; s.sym_cur = 2 * s.sym_cur + (int)bit; - if (++s.sym_len == (int)TINF_ARRAY_SIZE(tree->table)) { + if (++s.sym_len == 16) { set_error("invalid huffman code"); return -1; } - s.sym_sum += tree->table[s.sym_len]; - s.sym_cur -= tree->table[s.sym_len]; + s.sym_sum += table[s.sym_len]; + s.sym_cur -= table[s.sym_len]; } while (s.sym_cur >= 0); s.sym_sum += s.sym_cur; - if (s.sym_sum < 0 || s.sym_sum >= (int)TINF_ARRAY_SIZE(tree->trans)) { + if (s.sym_sum < 0 || s.sym_sum >= (int)trans_size) { set_error("invalid huffman symbol"); return -1; } - *symbol = tree->trans[s.sym_sum]; + *symbol = trans[s.sym_sum]; s.sym_active = false; return 1; } @@ -334,7 +355,7 @@ static int process_dynamic_trees(void) { s.dynamic_stage = DYN_BUILD_CLTREE; break; case DYN_BUILD_CLTREE: - build_tree(&s.tree.cltree, s.lengths, 19); + if (!build_tree(s.tree.cltree.table, s.tree.cltree.trans, TINF_ARRAY_SIZE(s.tree.cltree.trans), s.lengths, 19)) return -1; s.hlimit = s.hlit + s.hdist; s.dyn_num = 0; s.dynamic_stage = DYN_READ_LENGTHS; @@ -342,7 +363,7 @@ static int process_dynamic_trees(void) { break; case DYN_READ_LENGTHS: while (s.dyn_num < s.hlimit) { - int rc = decode_symbol(&s.tree.cltree, &sym); + int rc = decode_symbol(s.tree.cltree.table, s.tree.cltree.trans, TINF_ARRAY_SIZE(s.tree.cltree.trans), &sym); if (rc <= 0) return s.stage == ST_ERROR ? -1 : 0; if (sym < 16) { s.lengths[s.dyn_num++] = (unsigned char)sym; @@ -390,8 +411,8 @@ static int process_dynamic_trees(void) { set_error("dynamic tree missing end-of-block"); return -1; } - build_tree(&s.ltree, s.lengths, s.hlit); - build_tree(&s.tree.dtree, s.lengths + s.hlit, s.hdist); + if (!build_tree(s.ltree.table, s.ltree.trans, TINF_ARRAY_SIZE(s.ltree.trans), s.lengths, s.hlit)) return -1; + if (!build_tree(s.tree.dtree.table, s.tree.dtree.trans, TINF_ARRAY_SIZE(s.tree.dtree.trans), s.lengths + s.hlit, s.hdist)) return -1; s.dynamic_stage = DYN_HLIT; reset_code_readers(); return 1; @@ -432,15 +453,13 @@ static int process_stored_data(uint8_t *output, size_t capacity, size_t *produce } static int process_block_data(uint8_t *output, size_t capacity, size_t *produced) { - TINF_TREE *lt = &s.ltree; - TINF_TREE *dt = &s.tree.dtree; int sym; for (;;) { if (*produced >= capacity) return 2; switch (s.block_stage) { case BLK_SYMBOL: { - int rc = decode_symbol(lt, &sym); + int rc = decode_symbol(s.ltree.table, s.ltree.trans, TINF_ARRAY_SIZE(s.ltree.trans), &sym); if (rc <= 0) return rc; if (sym < 256) { if (!put_output_byte((uint8_t)sym, output, capacity, produced)) return s.stage == ST_ERROR ? -1 : 2; @@ -466,7 +485,7 @@ static int process_block_data(uint8_t *output, size_t capacity, size_t *produced s.block_stage = BLK_DIST_SYMBOL; break; case BLK_DIST_SYMBOL: { - int rc = decode_symbol(dt, &s.dist_sym); + int rc = decode_symbol(s.tree.dtree.table, s.tree.dtree.trans, TINF_ARRAY_SIZE(s.tree.dtree.trans), &s.dist_sym); if (rc <= 0) return rc; if (s.dist_sym < 0 || s.dist_sym >= 30) { set_error("invalid distance symbol"); diff --git a/src/compression_config.h b/src/compression_config.h index f501083..e6b83aa 100644 --- a/src/compression_config.h +++ b/src/compression_config.h @@ -1,6 +1,6 @@ #ifndef COMPRESSION_CONFIG_H #define COMPRESSION_CONFIG_H -#define OPENDISPLAY_DECOMPRESSION_CHUNK_SIZE 4096 +#define OPENDISPLAY_DECOMPRESSION_CHUNK_SIZE 256 #endif From bfff89d80a2bbed9ad4fa83c1daea3347afc3e83 Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Sat, 9 May 2026 00:26:35 +0200 Subject: [PATCH 4/5] Remove streaming compression input caps --- src/display_service.cpp | 61 +++++++++++++++++++++++++---------------- src/display_service.h | 2 -- src/main.h | 2 +- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/display_service.cpp b/src/display_service.cpp index 5d7a532..98a5e08 100644 --- a/src/display_service.cpp +++ b/src/display_service.cpp @@ -79,16 +79,6 @@ struct PartialStreamContext { uint8_t current_plane; }; -uint32_t max_compressed_image_rx_bytes(uint8_t tm) { - if ((tm & TRANSMISSION_MODE_ZIP) == 0) return 0; - if ((tm & TRANSMISSION_MODE_ZIPXL) != 0 && - MAX_COMPRESSED_BUFFER_BYTES > (54u * 1024u)) { - return MAX_COMPRESSED_BUFFER_BYTES; - } - uint32_t stdlim = 54u * 1024u; - return stdlim < MAX_COMPRESSED_BUFFER_BYTES ? stdlim : MAX_COMPRESSED_BUFFER_BYTES; -} - #ifdef TARGET_ESP32 extern BLEAdvertisementData* advertisementData; extern BLEServer* pServer; @@ -223,6 +213,7 @@ int mapEpd(int id){ case 0x003F: return EP31_240x320; case 0x0040: return EP75YR_800x480; case 0x0041: return EP_PANEL_UNDEFINED; + case 0x0042: return EP133_960x680; default: return EP_PANEL_UNDEFINED; } } @@ -1122,11 +1113,13 @@ void updatemsdata(){ } void handleDirectWriteCompressedData(uint8_t* data, uint16_t len) { - uint32_t cap = (globalConfig.display_count > 0) ? max_compressed_image_rx_bytes(globalConfig.displays[0].transmission_modes) : 0; - if (cap == 0 || - directWriteCompressedReceived > cap || - len > cap - directWriteCompressedReceived || - !zlib_stream_to_direct_write(data, len, false)) { + if (len > UINT32_MAX - directWriteCompressedReceived) { + cleanupDirectWriteState(true); + uint8_t errorResponse[] = {0xFF, 0xFF}; + sendResponse(errorResponse, sizeof(errorResponse)); + return; + } + if (!zlib_stream_to_direct_write(data, len, false)) { cleanupDirectWriteState(true); uint8_t errorResponse[] = {0xFF, 0xFF}; sendResponse(errorResponse, sizeof(errorResponse)); @@ -1188,7 +1181,19 @@ void handleDirectWriteStart(uint8_t* data, uint16_t len) { else directWriteTotalBytes = (pixels + 7) / 8; } if (directWriteCompressed) { + if ((globalConfig.displays[0].transmission_modes & TRANSMISSION_MODE_ZIP) == 0) { + cleanupDirectWriteState(false); + uint8_t errorResponse[] = {0xFF, 0xFF}; + sendResponse(errorResponse, sizeof(errorResponse)); + return; + } memcpy(&directWriteDecompressedTotal, data, 4); + if (directWriteDecompressedTotal != directWriteTotalBytes) { + cleanupDirectWriteState(false); + uint8_t errorResponse[] = {0xFF, 0xFF}; + sendResponse(errorResponse, sizeof(errorResponse)); + return; + } } directWriteActive = true; directWriteBytesWritten = 0; @@ -1214,8 +1219,7 @@ void handleDirectWriteStart(uint8_t* data, uint16_t len) { od_zlib_stream_reset(directWriteDecompressedTotal); if (len > 4) { uint32_t compressedDataLen = len - 4; - uint32_t cap = max_compressed_image_rx_bytes(globalConfig.displays[0].transmission_modes); - if (compressedDataLen > cap || !zlib_stream_to_direct_write(data + 4, compressedDataLen, false)) { + if (!zlib_stream_to_direct_write(data + 4, compressedDataLen, false)) { cleanupDirectWriteState(false); uint8_t errorResponse[] = {0xFF, 0xFF}; sendResponse(errorResponse, sizeof(errorResponse)); @@ -1457,10 +1461,14 @@ static void cleanup_partial_write_state(void) { } static bool partial_consume_bytes(uint8_t* data, uint32_t len) { - uint32_t limit = partialCtx.compressed - ? max_compressed_image_rx_bytes(globalConfig.displays[0].transmission_modes) - : partialCtx.expected_stream_size; - if (limit == 0 || partialCtx.bytes_received > limit || len > limit - partialCtx.bytes_received) return false; + if (partialCtx.compressed) { + if (len > UINT32_MAX - partialCtx.bytes_received) return false; + } else { + if (partialCtx.bytes_received > partialCtx.expected_stream_size || + len > partialCtx.expected_stream_size - partialCtx.bytes_received) { + return false; + } + } partialCtx.bytes_received += len; if (partialCtx.compressed) return zlib_stream_to_partial_write(data, len, false); return partial_write_stream_bytes(data, len); @@ -1486,11 +1494,18 @@ static bool zlib_stream_to_direct_write(const uint8_t* data, uint32_t len, bool bbepWriteData(&bbep, decompressionChunk, bytesOut); } directWriteBytesWritten += (uint32_t)bytesOut; - if (directWriteBytesWritten > directWriteDecompressedTotal) return false; + if (directWriteBytesWritten > directWriteDecompressedTotal) { + return false; + } } if (status == OD_ZLIB_STATUS_OUTPUT_READY) continue; if (status == OD_ZLIB_STATUS_NEEDS_INPUT) return !final; - if (status == OD_ZLIB_STATUS_DONE) return directWriteBytesWritten == directWriteDecompressedTotal; + if (status == OD_ZLIB_STATUS_DONE) { + if (directWriteBytesWritten != directWriteDecompressedTotal) { + return false; + } + return true; + } writeSerial(String("zlib stream error: ") + od_zlib_stream_error(), true); return false; } diff --git a/src/display_service.h b/src/display_service.h index 10b433f..a2137dd 100644 --- a/src/display_service.h +++ b/src/display_service.h @@ -4,8 +4,6 @@ #include #include -uint32_t max_compressed_image_rx_bytes(uint8_t transmission_modes); - bool seeed_driver_used(void); int mapEpd(int id); bool waitforrefresh(int timeout); diff --git a/src/main.h b/src/main.h index 817b4a0..c3cf10e 100644 --- a/src/main.h +++ b/src/main.h @@ -185,7 +185,7 @@ uint16_t directWriteHeight = 0; // Display height in pixels uint32_t directWriteTotalBytes = 0; // Total bytes expected per plane (for bitplanes) or total (for others) uint8_t directWriteRefreshMode = 0; // 0 = FULL (default), 1 = FAST/PARTIAL (if supported) uint8_t directWriteDataKind = 0; // none; display_service.cpp tracks full vs partial 0x71 streams -uint32_t directWriteCompressedReceived = 0; // Total compressed bytes received for sanity limits +uint32_t directWriteCompressedReceived = 0; // Total compressed bytes received for diagnostics/overflow guard uint32_t directWriteStartTime = 0; // Timestamp when direct write started (for timeout detection) bool displayPowerState = false; // Track display power state (true = powered on, false = powered off) From 230022373b1bb7cc1978f8cb40df508c68dbde52 Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Sat, 9 May 2026 00:35:16 +0200 Subject: [PATCH 5/5] Cleanup --- src/compression_config.h | 6 ------ src/display_service.cpp | 1 - src/display_service.h | 2 ++ src/main.h | 2 +- src/structs.h | 11 ++--------- 5 files changed, 5 insertions(+), 17 deletions(-) delete mode 100644 src/compression_config.h diff --git a/src/compression_config.h b/src/compression_config.h deleted file mode 100644 index e6b83aa..0000000 --- a/src/compression_config.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef COMPRESSION_CONFIG_H -#define COMPRESSION_CONFIG_H - -#define OPENDISPLAY_DECOMPRESSION_CHUNK_SIZE 256 - -#endif diff --git a/src/display_service.cpp b/src/display_service.cpp index 98a5e08..d24b3d5 100644 --- a/src/display_service.cpp +++ b/src/display_service.cpp @@ -10,7 +10,6 @@ #include "communication.h" #include "encryption.h" #include "boot_screen.h" -#include "compression_config.h" #include "uzlib.h" #if defined(TARGET_ESP32) && defined(OPENDISPLAY_SEEED_GFX) #include "display_seeed_gfx.h" diff --git a/src/display_service.h b/src/display_service.h index a2137dd..52c4403 100644 --- a/src/display_service.h +++ b/src/display_service.h @@ -4,6 +4,8 @@ #include #include +#define OPENDISPLAY_DECOMPRESSION_CHUNK_SIZE 256 + bool seeed_driver_used(void); int mapEpd(int id); bool waitforrefresh(int timeout); diff --git a/src/main.h b/src/main.h index c3cf10e..856eff1 100644 --- a/src/main.h +++ b/src/main.h @@ -7,7 +7,7 @@ #include "config_parser.h" #include "ble_init.h" #include "wifi_service.h" -#include "compression_config.h" +#include "display_service.h" #ifndef BUILD_VERSION #define BUILD_VERSION "0.0" diff --git a/src/structs.h b/src/structs.h index 45f165e..c336f6f 100644 --- a/src/structs.h +++ b/src/structs.h @@ -65,14 +65,7 @@ struct PowerOption { // display.color_scheme (config.yaml); use with matching panel (e.g. gray16 + panel_ic 3001). #define COLOR_SCHEME_GRAY16 6u -// display.transmission_modes (config.yaml bitfield). ZIPXL extends ZIP on builds with a larger buffer. -#if defined(TARGET_ESP32) && defined(TARGET_LARGE_MEMORY) && defined(BOARD_HAS_PSRAM) -#define MAX_COMPRESSED_BUFFER_BYTES (512u * 1024u) -#elif defined(TARGET_ESP32) && defined(TARGET_LARGE_MEMORY) -#define MAX_COMPRESSED_BUFFER_BYTES (256u * 1024u) -#else -#define MAX_COMPRESSED_BUFFER_BYTES (54u * 1024u) -#endif +// display.transmission_modes (config.yaml bitfield). #define TRANSMISSION_MODE_ZIPXL (1u << 0) #define TRANSMISSION_MODE_ZIP (1u << 1) #define TRANSMISSION_MODE_G5 (1u << 2) @@ -280,4 +273,4 @@ struct SecurityConfig { #define SECURITY_FLAG_RESET_PIN_PULLUP (1 << 4) #define SECURITY_FLAG_RESET_PIN_PULLDOWN (1 << 5) -#endif \ No newline at end of file +#endif