/* * CCompressedStream.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #pragma once #include "CInputStream.h" struct z_stream_s; VCMI_LIB_NAMESPACE_BEGIN class DecompressionException : public std::runtime_error { public: using runtime_error::runtime_error; }; /// Abstract class that provides buffer for one-directional input streams (e.g. compressed data) /// Used for zip archives support and in .lod deflate compression class CBufferedStream : public CInputStream { public: CBufferedStream(); /** * Reads n bytes from the stream into the data buffer. * * @param data A pointer to the destination data array. * @param size The number of bytes to read. * @return the number of bytes read actually. * * @throws std::runtime_error if the file decompression was not successful */ si64 read(ui8 * data, si64 size) override; /** * Seeks the internal read pointer to the specified position. * This will cause decompressing data till this position is found * * @param position The read position from the beginning. * @return the position actually moved to, -1 on error. */ si64 seek(si64 position) override; /** * Gets the current read position in the stream. * * @return the read position. */ si64 tell() override; /** * Skips delta numbers of bytes. * * @param delta The count of bytes to skip. * @return the count of bytes skipped actually. */ si64 skip(si64 delta) override; /** * Gets the length in bytes of the stream. * Causes complete data decompression * * @return the length in bytes of the stream. */ si64 getSize() override; protected: /** * @brief virtual method to get more data into the buffer * @return amount of bytes actually read, non-positive values are considered to be end-of-file mark */ virtual si64 readMore(ui8 * data, si64 size) = 0; /// resets all internal state void reset(); private: /// ensures that buffer contains at lest size of bytes. Calls readMore() to fill remaining part void ensureSize(si64 size); /** buffer with already decompressed data */ std::vector buffer; /** Current read position */ si64 position; bool endOfFileReached; }; /** * A class which provides method definitions for reading a gzip-compressed file * This class implements lazy loading - data will be decompressed (and cached) only by request */ class DLL_LINKAGE CCompressedStream : public CBufferedStream { public: /** * C-tor. * * @param stream - stream with compressed data * @param gzip - this is gzipp'ed file e.g. campaign or maps, false for files in .lod * @param decompressedSize - optional parameter to hint size of decompressed data */ CCompressedStream(std::unique_ptr stream, bool gzip, size_t decompressedSize=0); ~CCompressedStream(); /** * Prepare stream for decompression of next block (e.g. next part of h3c) * Applicable only for streams that contain multiple concatenated compressed data * * @return false if next block was not found, true othervice */ bool getNextBlock(); private: /** * Decompresses data to ensure that buffer has newSize bytes or end of stream was reached */ si64 readMore(ui8 * data, si64 size) override; /** The file stream with compressed data. */ std::unique_ptr gzipStream; /** buffer with not yet decompressed data*/ std::vector compressedBuffer; /** struct with current zlib inflate state */ z_stream_s * inflateState; enum EState { ERROR_OCCURRED, INITIALIZED, IN_PROGRESS, STREAM_END, FINISHED }; }; VCMI_LIB_NAMESPACE_END