diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 433769d24..7f2d12be3 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -29,6 +29,10 @@ find_package(mpg123 REQUIRED) find_package(PNG REQUIRED) find_package(ZLIB REQUIRED) +find_helper(ZSTD libzstd zstd.h zstd) +include_directories(${ZSTD_INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} PRIVATE ${ZSTD_LIBRARIES}) + if (USE_LIBSERIALPORT) target_compile_definitions(${PROJECT_NAME} PRIVATE USE_LIBSERIALPORT) find_helper(LIBSERIALPORT libserialport libserialport.h serialport) diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index 972bf3092..1c73f5612 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -27,7 +27,7 @@ set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_SOURCE_DIR}/packaging/dmg/AppDMGSet # Linux DEB settings set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT") -set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, libstdc++6, libsdl2-2.0-0, libsdl2-image-2.0-0, libsdl2-ttf-2.0-0, flac, libmpg123-0, libpng16-16, zlib1g, libserialport0, libportmidi0, libenet7, libmpeg2-4") # Adjust dependencies as needed +set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, libstdc++6, libsdl2-2.0-0, libsdl2-image-2.0-0, libsdl2-ttf-2.0-0, flac, libmpg123-0, libpng16-16, zlib1g, libzstd1, libserialport0, libportmidi0, libenet7, libmpeg2-4") # Adjust dependencies as needed if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "arm64") elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm") diff --git a/src/archivers/chd/chdcodec.cpp b/src/archivers/chd/chdcodec.cpp index 7cad74bbe..656892eb1 100644 --- a/src/archivers/chd/chdcodec.cpp +++ b/src/archivers/chd/chdcodec.cpp @@ -20,6 +20,7 @@ #include "7z/LzmaEnc.h" #include +#include #include @@ -102,6 +103,44 @@ namespace { }; +// ======================> 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 @@ namespace { const codec_entry f_codec_list[] = { // general codecs + { CHD_CODEC_ZSTD, false, "Zstd", &codec_entry::construct_compressor, &codec_entry::construct_decompressor }, { CHD_CODEC_ZLIB, false, "Deflate", &codec_entry::construct_compressor, &codec_entry::construct_decompressor }, { CHD_CODEC_LZMA, false, "LZMA", &codec_entry::construct_compressor, &codec_entry::construct_decompressor }, { CHD_CODEC_HUFFMAN, false, "Huffman", &codec_entry::construct_compressor, &codec_entry::construct_decompressor }, { CHD_CODEC_FLAC, false, "FLAC", &codec_entry::construct_compressor, &codec_entry::construct_decompressor }, // general codecs with CD frontend + { CHD_CODEC_CD_ZSTD, false, "CD Zstd", &codec_entry::construct_compressor >, &codec_entry::construct_decompressor > }, { CHD_CODEC_CD_ZLIB, false, "CD Deflate", &codec_entry::construct_compressor >, &codec_entry::construct_decompressor > }, { CHD_CODEC_CD_LZMA, false, "CD LZMA", &codec_entry::construct_compressor >, &codec_entry::construct_decompressor > }, { CHD_CODEC_CD_FLAC, false, "CD FLAC", &codec_entry::construct_compressor, &codec_entry::construct_decompressor }, @@ -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 //************************************************************************** diff --git a/src/archivers/chd/chdcodec.h b/src/archivers/chd/chdcodec.h index 2ae011933..ba444feff 100644 --- a/src/archivers/chd/chdcodec.h +++ b/src/archivers/chd/chdcodec.h @@ -155,12 +155,14 @@ constexpr chd_codec_type CHD_CODEC_NONE = 0; // general codecs constexpr chd_codec_type CHD_CODEC_ZLIB = CHD_MAKE_TAG('z', 'l', 'i', 'b'); +constexpr chd_codec_type CHD_CODEC_ZSTD = CHD_MAKE_TAG('z', 's', 't', 'd'); constexpr chd_codec_type CHD_CODEC_LZMA = CHD_MAKE_TAG('l', 'z', 'm', 'a'); constexpr chd_codec_type CHD_CODEC_HUFFMAN = CHD_MAKE_TAG('h', 'u', 'f', 'f'); constexpr chd_codec_type CHD_CODEC_FLAC = CHD_MAKE_TAG('f', 'l', 'a', 'c'); // general codecs with CD frontend constexpr chd_codec_type CHD_CODEC_CD_ZLIB = CHD_MAKE_TAG('c', 'd', 'z', 'l'); +constexpr chd_codec_type CHD_CODEC_CD_ZSTD = CHD_MAKE_TAG('c', 'd', 'z', 's'); constexpr chd_codec_type CHD_CODEC_CD_LZMA = CHD_MAKE_TAG('c', 'd', 'l', 'z'); constexpr chd_codec_type CHD_CODEC_CD_FLAC = CHD_MAKE_TAG('c', 'd', 'f', 'l'); diff --git a/src/main.cpp b/src/main.cpp index 2cd3912bd..0fb61c51e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1122,7 +1122,7 @@ static void parse_cmdline (int argc, TCHAR **argv) else { auto* const txt = parsetextpath(argv[++i]); - auto* const txt2 = xmalloc(TCHAR, _tcslen(txt) + 5); + auto* const txt2 = xmalloc(TCHAR, _tcslen(txt) + 7); _tcscpy(txt2, txt); if (_tcsrchr(txt2, ',') == nullptr) _tcscat(txt2, _T(",image"));