diff options
Diffstat (limited to 'src/lib/util/chdcodec.cpp')
-rw-r--r-- | src/lib/util/chdcodec.cpp | 168 |
1 files changed, 167 insertions, 1 deletions
diff --git a/src/lib/util/chdcodec.cpp b/src/lib/util/chdcodec.cpp index fa46975285e..c59d880374c 100644 --- a/src/lib/util/chdcodec.cpp +++ b/src/lib/util/chdcodec.cpp @@ -21,6 +21,7 @@ #include "lzma/C/LzmaEnc.h" #include <zlib.h> +#include <zstd.h> #include <new> @@ -103,6 +104,44 @@ private: }; +// ======================> chd_zstd_compressor + +// Zstandard compressor +class chd_zstd_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_zstd_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy); + ~chd_zstd_compressor(); + + // core functionality + virtual uint32_t compress(const uint8_t *src, uint32_t srclen, uint8_t *dest) override; + +private: + // internal state + ZSTD_CStream * m_stream; +}; + + +// ======================> chd_zstd_decompressor + +// Zstandard decompressor +class chd_zstd_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_zstd_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy); + ~chd_zstd_decompressor(); + + // core functionality + virtual void decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) override; + +private: + // internal state + ZSTD_DStream * m_stream; +}; + + // ======================> chd_lzma_allocator // allocation helper clas for zlib @@ -493,12 +532,14 @@ const codec_entry f_codec_list[] = { // general codecs { CHD_CODEC_ZLIB, false, "Deflate", &codec_entry::construct_compressor<chd_zlib_compressor>, &codec_entry::construct_decompressor<chd_zlib_decompressor> }, + { CHD_CODEC_ZSTD, false, "Zstandard", &codec_entry::construct_compressor<chd_zstd_compressor>, &codec_entry::construct_decompressor<chd_zstd_decompressor> }, { CHD_CODEC_LZMA, false, "LZMA", &codec_entry::construct_compressor<chd_lzma_compressor>, &codec_entry::construct_decompressor<chd_lzma_decompressor> }, { CHD_CODEC_HUFFMAN, false, "Huffman", &codec_entry::construct_compressor<chd_huffman_compressor>, &codec_entry::construct_decompressor<chd_huffman_decompressor> }, { CHD_CODEC_FLAC, false, "FLAC", &codec_entry::construct_compressor<chd_flac_compressor>, &codec_entry::construct_decompressor<chd_flac_decompressor> }, // general codecs with CD frontend { CHD_CODEC_CD_ZLIB, false, "CD Deflate", &codec_entry::construct_compressor<chd_cd_compressor<chd_zlib_compressor, chd_zlib_compressor> >, &codec_entry::construct_decompressor<chd_cd_decompressor<chd_zlib_decompressor, chd_zlib_decompressor> > }, + { CHD_CODEC_CD_ZSTD, false, "CD Zstandard", &codec_entry::construct_compressor<chd_cd_compressor<chd_zstd_compressor, chd_zstd_compressor> >, &codec_entry::construct_decompressor<chd_cd_decompressor<chd_zstd_decompressor, chd_zstd_decompressor> > }, { CHD_CODEC_CD_LZMA, false, "CD LZMA", &codec_entry::construct_compressor<chd_cd_compressor<chd_lzma_compressor, chd_zlib_compressor> >, &codec_entry::construct_decompressor<chd_cd_decompressor<chd_lzma_decompressor, chd_zlib_decompressor> > }, { CHD_CODEC_CD_FLAC, false, "CD FLAC", &codec_entry::construct_compressor<chd_cd_flac_compressor>, &codec_entry::construct_decompressor<chd_cd_flac_decompressor> }, @@ -896,7 +937,7 @@ chd_zlib_compressor::~chd_zlib_compressor() uint32_t chd_zlib_compressor::compress(const uint8_t *src, uint32_t srclen, uint8_t *dest) { - // reset the decompressor + // reset the compressor m_deflater.next_in = const_cast<Bytef *>(src); m_deflater.avail_in = srclen; m_deflater.total_in = 0; @@ -984,6 +1025,131 @@ void chd_zlib_decompressor::decompress(const uint8_t *src, uint32_t complen, uin //************************************************************************** +// ZSTANDARD COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_zstd_compressor - constructor +//------------------------------------------------- + +chd_zstd_compressor::chd_zstd_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy) + : chd_compressor(chd, hunkbytes, lossy) + , m_stream(nullptr) +{ + // initialize the stream + m_stream = ZSTD_createCStream(); + + // convert errors + if (!m_stream) + throw std::bad_alloc(); +} + + +//------------------------------------------------- +// ~chd_zstd_compressor - destructor +//------------------------------------------------- + +chd_zstd_compressor::~chd_zstd_compressor() +{ + ZSTD_freeCStream(m_stream); +} + + +//------------------------------------------------- +// compress - compress data using the Zstandard +// codec +//------------------------------------------------- + +uint32_t chd_zstd_compressor::compress(const uint8_t *src, uint32_t srclen, uint8_t *dest) +{ + // reset the compressor + auto result = ZSTD_initCStream(m_stream, ZSTD_maxCLevel()); + if (ZSTD_isError(result)) + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); + + // do it + ZSTD_inBuffer input{ src, srclen, 0 }; + ZSTD_outBuffer output = { dest, srclen, 0 }; + while (output.pos < output.size) + { + result = ZSTD_compressStream2(m_stream, &output, &input, ZSTD_e_end); + if (ZSTD_isError(result)) + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); + else if (!result) + break; + } + + // if we ended up with more data than we started with, return an error + if (output.pos == output.size) + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); + + // otherwise, return the length + return output.pos; +} + + + +//************************************************************************** +// ZSTANDARD DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_zstd_compressor - constructor +//------------------------------------------------- + +chd_zstd_decompressor::chd_zstd_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy) + : chd_decompressor(chd, hunkbytes, lossy) + , m_stream(nullptr) +{ + // initialize the stream + m_stream = ZSTD_createDStream(); + + // convert errors + if (!m_stream) + throw std::bad_alloc(); +} + + +//------------------------------------------------- +// ~chd_zstd_decompressor - destructor +//------------------------------------------------- + +chd_zstd_decompressor::~chd_zstd_decompressor() +{ + ZSTD_freeDStream(m_stream); +} + + +//------------------------------------------------- +// decompress - decompress data using the +// Zstandard codec +//------------------------------------------------- + +void chd_zstd_decompressor::decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + // reset the decompressor + auto result = ZSTD_initDStream(m_stream); + if (ZSTD_isError(result)) + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); + + // do it + ZSTD_inBuffer input{ src, complen, 0 }; + ZSTD_outBuffer output = { dest, destlen, 0 }; + while ((input.pos < input.size) && (output.pos < output.size)) + { + result = ZSTD_decompressStream(m_stream, &output, &input); + if (ZSTD_isError(result)) + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); + } + + // ensure the expected amount of output was generated + if (output.pos != output.size) + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); +} + + + +//************************************************************************** // LZMA ALLOCATOR HELPER //************************************************************************** |