diff options
Diffstat (limited to 'core/io/compression.cpp')
-rw-r--r-- | core/io/compression.cpp | 218 |
1 files changed, 149 insertions, 69 deletions
diff --git a/core/io/compression.cpp b/core/io/compression.cpp index a6114e4f63..ac4a637597 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -35,11 +35,18 @@ #include "thirdparty/misc/fastlz.h" +#ifdef BROTLI_ENABLED +#include "thirdparty/brotli/include/brotli/decode.h" +#endif + #include <zlib.h> #include <zstd.h> int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, Mode p_mode) { switch (p_mode) { + case MODE_BROTLI: { + ERR_FAIL_V_MSG(-1, "Only brotli decompression is supported."); + } break; case MODE_FASTLZ: { if (p_src_size < 16) { uint8_t src[16]; @@ -95,6 +102,9 @@ int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, int Compression::get_max_compressed_buffer_size(int p_src_size, Mode p_mode) { switch (p_mode) { + case MODE_BROTLI: { + ERR_FAIL_V_MSG(-1, "Only brotli decompression is supported."); + } break; case MODE_FASTLZ: { int ss = p_src_size + p_src_size * 6 / 100; if (ss < 66) { @@ -129,6 +139,16 @@ int Compression::get_max_compressed_buffer_size(int p_src_size, Mode p_mode) { int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size, Mode p_mode) { switch (p_mode) { + case MODE_BROTLI: { +#ifdef BROTLI_ENABLED + size_t ret_size = p_dst_max_size; + BrotliDecoderResult res = BrotliDecoderDecompress(p_src_size, p_src, &ret_size, p_dst); + ERR_FAIL_COND_V(res != BROTLI_DECODER_RESULT_SUCCESS, -1); + return ret_size; +#else + ERR_FAIL_V_MSG(-1, "Godot was compiled without brotli support."); +#endif + } break; case MODE_FASTLZ: { int ret_size = 0; @@ -186,87 +206,147 @@ int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p This is much slower however than using Compression::decompress because it may result in multiple full copies of the output buffer. */ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_size, const uint8_t *p_src, int p_src_size, Mode p_mode) { - int ret; uint8_t *dst = nullptr; int out_mark = 0; - z_stream strm; ERR_FAIL_COND_V(p_src_size <= 0, Z_DATA_ERROR); - // This function only supports GZip and Deflate - int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; - ERR_FAIL_COND_V(p_mode != MODE_DEFLATE && p_mode != MODE_GZIP, Z_ERRNO); - - // Initialize the stream - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = 0; - strm.next_in = Z_NULL; - - int err = inflateInit2(&strm, window_bits); - ERR_FAIL_COND_V(err != Z_OK, -1); - - // Setup the stream inputs - strm.next_in = (Bytef *)p_src; - strm.avail_in = p_src_size; - - // Ensure the destination buffer is empty - p_dst_vect->clear(); - - // decompress until deflate stream ends or end of file - do { - // Add another chunk size to the output buffer - // This forces a copy of the whole buffer - p_dst_vect->resize(p_dst_vect->size() + gzip_chunk); - // Get pointer to the actual output buffer - dst = p_dst_vect->ptrw(); - - // Set the stream to the new output stream - // Since it was copied, we need to reset the stream to the new buffer - strm.next_out = &(dst[out_mark]); - strm.avail_out = gzip_chunk; - - // run inflate() on input until output buffer is full and needs to be resized - // or input runs out + if (p_mode == MODE_BROTLI) { +#ifdef BROTLI_ENABLED + BrotliDecoderResult ret; + BrotliDecoderState *state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); + ERR_FAIL_COND_V(state == nullptr, Z_DATA_ERROR); + + // Setup the stream inputs. + const uint8_t *next_in = p_src; + size_t avail_in = p_src_size; + uint8_t *next_out = nullptr; + size_t avail_out = 0; + size_t total_out = 0; + + // Ensure the destination buffer is empty. + p_dst_vect->clear(); + + // Decompress until stream ends or end of file. do { - ret = inflate(&strm, Z_SYNC_FLUSH); - - switch (ret) { - case Z_NEED_DICT: - ret = Z_DATA_ERROR; - [[fallthrough]]; - case Z_DATA_ERROR: - case Z_MEM_ERROR: - case Z_STREAM_ERROR: - case Z_BUF_ERROR: - if (strm.msg) { - WARN_PRINT(strm.msg); - } - (void)inflateEnd(&strm); - p_dst_vect->clear(); - return ret; + // Add another chunk size to the output buffer. + // This forces a copy of the whole buffer. + p_dst_vect->resize(p_dst_vect->size() + gzip_chunk); + // Get pointer to the actual output buffer. + dst = p_dst_vect->ptrw(); + + // Set the stream to the new output stream. + // Since it was copied, we need to reset the stream to the new buffer. + next_out = &(dst[out_mark]); + avail_out += gzip_chunk; + + ret = BrotliDecoderDecompressStream(state, &avail_in, &next_in, &avail_out, &next_out, &total_out); + if (ret == BROTLI_DECODER_RESULT_ERROR) { + WARN_PRINT(BrotliDecoderErrorString(BrotliDecoderGetErrorCode(state))); + BrotliDecoderDestroyInstance(state); + p_dst_vect->clear(); + return Z_DATA_ERROR; } - } while (strm.avail_out > 0 && strm.avail_in > 0); - out_mark += gzip_chunk; + out_mark += gzip_chunk - avail_out; - // Enforce max output size - if (p_max_dst_size > -1 && strm.total_out > (uint64_t)p_max_dst_size) { - (void)inflateEnd(&strm); - p_dst_vect->clear(); - return Z_BUF_ERROR; + // Enforce max output size. + if (p_max_dst_size > -1 && total_out > (uint64_t)p_max_dst_size) { + BrotliDecoderDestroyInstance(state); + p_dst_vect->clear(); + return Z_BUF_ERROR; + } + } while (ret != BROTLI_DECODER_RESULT_SUCCESS); + + // If all done successfully, resize the output if it's larger than the actual output. + if ((unsigned long)p_dst_vect->size() > total_out) { + p_dst_vect->resize(total_out); } - } while (ret != Z_STREAM_END); - // If all done successfully, resize the output if it's larger than the actual output - if ((unsigned long)p_dst_vect->size() > strm.total_out) { - p_dst_vect->resize(strm.total_out); - } + // Clean up and return. + BrotliDecoderDestroyInstance(state); + return Z_OK; +#else + ERR_FAIL_V_MSG(Z_ERRNO, "Godot was compiled without brotli support."); +#endif + } else { + // This function only supports GZip and Deflate. + ERR_FAIL_COND_V(p_mode != MODE_DEFLATE && p_mode != MODE_GZIP, Z_ERRNO); + + int ret; + z_stream strm; + int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; + + // Initialize the stream. + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + + int err = inflateInit2(&strm, window_bits); + ERR_FAIL_COND_V(err != Z_OK, -1); + + // Setup the stream inputs. + strm.next_in = (Bytef *)p_src; + strm.avail_in = p_src_size; + + // Ensure the destination buffer is empty. + p_dst_vect->clear(); + + // Decompress until deflate stream ends or end of file. + do { + // Add another chunk size to the output buffer. + // This forces a copy of the whole buffer. + p_dst_vect->resize(p_dst_vect->size() + gzip_chunk); + // Get pointer to the actual output buffer. + dst = p_dst_vect->ptrw(); + + // Set the stream to the new output stream. + // Since it was copied, we need to reset the stream to the new buffer. + strm.next_out = &(dst[out_mark]); + strm.avail_out = gzip_chunk; + + // Run inflate() on input until output buffer is full and needs to be resized or input runs out. + do { + ret = inflate(&strm, Z_SYNC_FLUSH); + + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; + [[fallthrough]]; + case Z_DATA_ERROR: + case Z_MEM_ERROR: + case Z_STREAM_ERROR: + case Z_BUF_ERROR: + if (strm.msg) { + WARN_PRINT(strm.msg); + } + (void)inflateEnd(&strm); + p_dst_vect->clear(); + return ret; + } + } while (strm.avail_out > 0 && strm.avail_in > 0); + + out_mark += gzip_chunk; + + // Enforce max output size. + if (p_max_dst_size > -1 && strm.total_out > (uint64_t)p_max_dst_size) { + (void)inflateEnd(&strm); + p_dst_vect->clear(); + return Z_BUF_ERROR; + } + } while (ret != Z_STREAM_END); + + // If all done successfully, resize the output if it's larger than the actual output. + if ((unsigned long)p_dst_vect->size() > strm.total_out) { + p_dst_vect->resize(strm.total_out); + } - // clean up and return - (void)inflateEnd(&strm); - return Z_OK; + // Clean up and return. + (void)inflateEnd(&strm); + return Z_OK; + } } int Compression::zlib_level = Z_DEFAULT_COMPRESSION; |