diff options
Diffstat (limited to 'src/lib/util/unzip.cpp')
-rw-r--r-- | src/lib/util/unzip.cpp | 95 |
1 files changed, 93 insertions, 2 deletions
diff --git a/src/lib/util/unzip.cpp b/src/lib/util/unzip.cpp index 377a1040377..c1df469fed4 100644 --- a/src/lib/util/unzip.cpp +++ b/src/lib/util/unzip.cpp @@ -22,6 +22,7 @@ #include "lzma/C/LzmaDec.h" #include <zlib.h> +#include <zstd.h> #include <algorithm> #include <array> @@ -289,6 +290,7 @@ private: std::error_condition decompress_data_type_0(std::uint64_t offset, void *buffer, std::size_t length) noexcept; std::error_condition decompress_data_type_8(std::uint64_t offset, void *buffer, std::size_t length) noexcept; std::error_condition decompress_data_type_14(std::uint64_t offset, void *buffer, std::size_t length) noexcept; + std::error_condition decompress_data_type_93(std::uint64_t offset, void *buffer, std::size_t length) noexcept; struct file_header { @@ -929,6 +931,9 @@ std::error_condition zip_file_impl::decompress(void *buffer, std::size_t length) case 14: return decompress_data_type_14(offset, buffer, length); + case 93: + return decompress_data_type_93(offset, buffer, length); + default: osd_printf_error( "unzip: %s in %s uses unsupported compression method %u\n", @@ -1245,7 +1250,7 @@ std::error_condition zip_file_impl::decompress_data_type_8(std::uint64_t offset, return archive_file::error::DECOMPRESS_ERROR; } }; - std::uint64_t input_remaining = m_header.compressed_length; + std::uint64_t input_remaining(m_header.compressed_length); int zerr; // reset the stream @@ -1276,7 +1281,7 @@ std::error_condition zip_file_impl::decompress_data_type_8(std::uint64_t offset, auto const filerr = m_file->read_at( offset, &m_buffer[0], - std::size_t((std::min<std::uint64_t>)(input_remaining, m_buffer.size())), + std::size_t(std::min<std::uint64_t>(input_remaining, m_buffer.size())), read_length); if (filerr) { @@ -1541,6 +1546,92 @@ std::error_condition zip_file_impl::decompress_data_type_14(std::uint64_t offset } } + +/*------------------------------------------------- + decompress_data_type_93 - decompress + type 14 data (Zstandard) +-------------------------------------------------*/ + +std::error_condition zip_file_impl::decompress_data_type_93(std::uint64_t offset, void *buffer, std::size_t length) noexcept +{ + // create decompression stream + ZSTD_DStream *const stream(ZSTD_createDStream()); + if (!stream) + { + osd_printf_error( + "unzip: error allocating Zstandard stream to decompress %s from %s\n", + m_header.file_name, m_filename); + return std::errc::not_enough_memory; + } + + // loop until we're done + std::uint64_t input_remaining(m_header.compressed_length); + while (input_remaining && length) + { + // read in the next chunk of data + std::size_t read_length(0); + auto const filerr = m_file->read_at( + offset, + &m_buffer[0], + std::size_t(std::min<std::uint64_t>(input_remaining, m_buffer.size())), + read_length); + if (filerr) + { + osd_printf_error( + "unzip: error reading compressed data for %s in %s (%s:%d %s)\n", + m_header.file_name, m_filename, filerr.category().name(), filerr.value(), filerr.message()); + ZSTD_freeDStream(stream); + return filerr; + } + offset += read_length; + + // if we read nothing, but still have data left, the file is truncated + if (!read_length && input_remaining) + { + osd_printf_error( + "unzip: unexpectedly reached end-of-file while reading compressed data for %s in %s\n", + m_header.file_name, m_filename); + ZSTD_freeDStream(stream); + return archive_file::error::FILE_TRUNCATED; + } + + // fill out the input data + ZSTD_inBuffer input{ &m_buffer[0], read_length, 0 }; + input_remaining -= read_length; + + // now decompress + while ((input.pos < input.size) && length) + { + ZSTD_outBuffer output{ buffer, length, 0 }; + auto const result(ZSTD_decompressStream(stream, &output, &input)); + if (ZSTD_isError(result)) + { + osd_printf_error( + "unzip: error decompressing %s from %s (%u: %s)\n", + m_header.file_name, m_filename, result, ZSTD_getErrorName(result)); + ZSTD_freeDStream(stream); + return archive_file::error::DECOMPRESS_ERROR; + } + buffer = reinterpret_cast<std::uint8_t *>(buffer) + output.pos; + length -= output.pos; + } + } + + // free stream + ZSTD_freeDStream(stream); + + // if anything looks funny, report an error + if (length || input_remaining) + { + osd_printf_error( + "unzip: decompression of %s from %s doesn't appear to have completed correctly\n", + m_header.file_name, m_filename); + return archive_file::error::DECOMPRESS_ERROR; + } + + return std::error_condition(); +} + } // anonymous namespace |