#pragma once #include #include // Return value for readAtMost(). enum class InflateStatus { Ok, // Output buffer full; more compressed data remains. Done, // Stream ended cleanly (TINF_DONE). produced may be < maxLen. Error, // Decompression failed. }; // Streaming deflate decompressor wrapping uzlib. // // Two modes: // init(false) — one-shot: input is a contiguous buffer, call read() once. // init(true) — streaming: allocates a 32KB ring buffer for back-references // across multiple read() / readAtMost() calls. // // Streaming callback pattern: // The uzlib read callback receives a `struct uzlib_uncomp*` with no separate // context pointer. To attach context, make InflateReader the *first member* of // your context struct, then cast inside the callback: // // struct MyCtx { // InflateReader reader; // must be first // FsFile* file; // // ... // }; // static int myCb(struct uzlib_uncomp* u) { // MyCtx* ctx = reinterpret_cast(u); // valid: reader.decomp is at offset 0 // // ... fill u->source / u->source_limit, return first byte // } // MyCtx ctx; // ctx.reader.init(true); // ctx.reader.setReadCallback(myCb); // class InflateReader { public: InflateReader() = default; ~InflateReader(); InflateReader(const InflateReader&) = delete; InflateReader& operator=(const InflateReader&) = delete; // Initialise decompressor. streaming=true allocates a 32KB ring buffer needed // when read() or readAtMost() will be called multiple times. // Returns false only in streaming mode if the ring buffer allocation fails. bool init(bool streaming = false); // Release the ring buffer and reset internal state. void deinit(); // Set the entire compressed input as a contiguous memory buffer. // Used in one-shot mode; not needed when a read callback is set. void setSource(const uint8_t* src, size_t len); // Set a uzlib-compatible read callback for streaming input. // See class-level comment for the expected callback/context struct pattern. void setReadCallback(int (*cb)(uzlib_uncomp*)); // Consume the 2-byte zlib header (CMF + FLG) from the input stream. // Call this once before the first read() when input is zlib-wrapped (e.g. PNG IDAT). void skipZlibHeader(); // Decompress exactly len bytes into dest. // Returns false if the stream ends before producing len bytes, or on error. bool read(uint8_t* dest, size_t len); // Decompress up to maxLen bytes into dest. // Sets *produced to the number of bytes written. // Returns Done when the stream ends cleanly, Ok when there is more to read, // and Error on failure. InflateStatus readAtMost(uint8_t* dest, size_t maxLen, size_t* produced); // Returns a pointer to the underlying TINF_DATA. // Useful for advanced streaming setups where the callback needs access to the // uzlib struct directly (e.g. updating source/source_limit). uzlib_uncomp* raw() { return &decomp; } private: uzlib_uncomp decomp = {}; uint8_t* ringBuffer = nullptr; };