diff --git a/cmake/modules/FindLZ4.cmake b/cmake/modules/FindLZ4.cmake new file mode 100644 index 0000000000000..71cb755520e10 --- /dev/null +++ b/cmake/modules/FindLZ4.cmake @@ -0,0 +1,60 @@ +find_path(LZ4_INCLUDE_DIR + NAMES lz4.h + HINTS "${LZ4_INCLUDEDIR}" "${LZ4_HINTS}/include" + PATHS + /usr/local/include + /usr/include +) + +find_library(LZ4_LIBRARY + NAMES lz4 liblz4 + HINTS "${LZ4_LIBDIR}" "${LZ4_HINTS}/lib" + PATHS + /usr/local/lib + /usr/lib +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( LZ4 DEFAULT_MSG LZ4_LIBRARY LZ4_INCLUDE_DIR ) + +if( LZ4_FOUND ) + include( CheckIncludeFile ) + include( CMakePushCheckState ) + + set( LZ4_INCLUDE_DIRS ${LZ4_INCLUDE_DIR} ) + set( LZ4_LIBRARIES ${LZ4_LIBRARY} ) + + cmake_push_check_state() + set( CMAKE_REQUIRED_INCLUDES ${LZ4_INCLUDE_DIRS} ) + check_include_file( lz4frame.h HAVE_LZ4FRAME_H ) + cmake_pop_check_state() + + if (WIN32) + set ( LZ4_DLL_DIR "${LZ4_HINTS}/bin" + CACHE PATH "Path to LZ4 DLL" + ) + file( GLOB _lz4_dll RELATIVE "${LZ4_DLL_DIR}" + "${LZ4_DLL_DIR}/lz4*.dll" + ) + set ( LZ4_DLL ${_lz4_dll} + # We're storing filenames only. Should we use STRING instead? + CACHE FILEPATH "LZ4 DLL file name" + ) + file( GLOB _lz4_pdb RELATIVE "${LZ4_DLL_DIR}" + "${LZ4_DLL_DIR}/lz4*.pdb" + ) + set ( LZ4_PDB ${_lz4_pdb} + CACHE FILEPATH "LZ4 PDB file name" + ) + mark_as_advanced( LZ4_DLL_DIR LZ4_DLL LZ4_PDB ) + endif() +else() + set( LZ4_INCLUDE_DIRS ) + set( LZ4_LIBRARIES ) +endif() + +mark_as_advanced( LZ4_LIBRARIES LZ4_INCLUDE_DIRS ) + +add_library( LZ4::lz4 INTERFACE IMPORTED ) +set_property( TARGET LZ4::lz4 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${LZ4_INCLUDE_DIRS} ) +set_property( TARGET LZ4::lz4 PROPERTY INTERFACE_LINK_LIBRARIES ${LZ4_LIBRARIES} ) \ No newline at end of file diff --git a/cmake/modules/FindZSTD.cmake b/cmake/modules/FindZSTD.cmake index b7aa3804d3445..16fea42d7c156 100644 --- a/cmake/modules/FindZSTD.cmake +++ b/cmake/modules/FindZSTD.cmake @@ -38,4 +38,8 @@ if (ZSTD_FOUND) message(STATUS "Found Zstd: ${ZSTD_LIBRARY}") endif() -mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY) \ No newline at end of file +mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY) + +add_library(ZSTD::zstd INTERFACE IMPORTED) +set_property(TARGET ZSTD::zstd PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIR}) +set_property(TARGET ZSTD::zstd PROPERTY INTERFACE_LINK_LIBRARIES ${ZSTD_LIBRARY}) \ No newline at end of file diff --git a/dist/ImHex-9999.ebuild b/dist/ImHex-9999.ebuild index e73b5f5bc3cf1..6242f3a440f6c 100644 --- a/dist/ImHex-9999.ebuild +++ b/dist/ImHex-9999.ebuild @@ -28,5 +28,6 @@ RDEPEND="${DEPEND} app-arch/bzip2 app-arch/lzma app-arch/zstd + app-arch/lz4 " BDEPEND="${DEPEND}" diff --git a/dist/get_deps_archlinux.sh b/dist/get_deps_archlinux.sh index 83965bb87b129..15377e87689bb 100755 --- a/dist/get_deps_archlinux.sh +++ b/dist/get_deps_archlinux.sh @@ -18,4 +18,5 @@ pacman -S $@ --needed \ zlib \ bzip2 \ xz \ - zstd + zstd \ + lz4 diff --git a/dist/get_deps_debian.sh b/dist/get_deps_debian.sh index 4aeccc08cce21..286b42aa17183 100755 --- a/dist/get_deps_debian.sh +++ b/dist/get_deps_debian.sh @@ -26,4 +26,5 @@ apt install -y \ zlib1g-dev \ libbz2-dev \ liblzma-dev \ - libzstd-dev + libzstd-dev \ + liblz4-dev diff --git a/dist/get_deps_fedora.sh b/dist/get_deps_fedora.sh index 2a9b1a5db43b8..683ddd2d4849f 100755 --- a/dist/get_deps_fedora.sh +++ b/dist/get_deps_fedora.sh @@ -16,4 +16,5 @@ dnf install -y \ libzstd-devel \ zlib-devel \ bzip2-devel \ - xz-devel \ No newline at end of file + xz-devel \ + lz4-devel \ No newline at end of file diff --git a/dist/get_deps_msys2.sh b/dist/get_deps_msys2.sh index d63957847b53c..f4911848be484 100755 --- a/dist/get_deps_msys2.sh +++ b/dist/get_deps_msys2.sh @@ -17,4 +17,5 @@ pacboy -S --needed --noconfirm \ zlib:p \ bzip2:p \ xz:p \ - zstd:p + zstd:p \ + lz4:p diff --git a/dist/get_deps_tumbleweed.sh b/dist/get_deps_tumbleweed.sh index b096495094037..cbb8813ea51dd 100755 --- a/dist/get_deps_tumbleweed.sh +++ b/dist/get_deps_tumbleweed.sh @@ -16,4 +16,5 @@ zypper install \ libzstd-devel \ zlib-devel \ bzip3-devel \ - xz-devel + xz-devel \ + lz4-dev diff --git a/plugins/decompress/CMakeLists.txt b/plugins/decompress/CMakeLists.txt index 331c289400bb4..3da0b723d2d29 100644 --- a/plugins/decompress/CMakeLists.txt +++ b/plugins/decompress/CMakeLists.txt @@ -30,16 +30,16 @@ add_imhex_plugin( BZIP2 LIBLZMA ZSTD + LZ4 ) -find_package(ZSTD) -if(ZSTD_FOUND) - set(LIBRARIES ${LIBRARIES} "${ZSTD_LIBRARY}") - message(STATUS "Enabling decompression support using ZSTD (${ZSTD_VERSION})") - enable_plugin_feature(ZSTD) -endif() +set(LIBLZMA_HAS_AUTO_DECODER 1) +set(LIBLZMA_HAS_EASY_ENCODER 1) +set(LIBLZMA_HAS_LZMA_PRESET 1) addOptionalLibrary(ZLIB ZLIB) addOptionalLibrary(BZip2 BZip2) addOptionalLibrary(LibLZMA LibLZMA) +addOptionalLibrary(ZSTD zstd) +addOptionalLibrary(LZ4 lz4) target_link_libraries(decompress PRIVATE ${LIBRARIES}) diff --git a/plugins/decompress/source/content/pl_functions.cpp b/plugins/decompress/source/content/pl_functions.cpp index c57a1d20f1081..a14c11225c813 100644 --- a/plugins/decompress/source/content/pl_functions.cpp +++ b/plugins/decompress/source/content/pl_functions.cpp @@ -22,6 +22,11 @@ #if IMHEX_FEATURE_ENABLED(ZSTD) #include #endif +#if IMHEX_FEATURE_ENABLED(LZ4) + #include + #include +#endif + namespace hex::plugin::decompress { @@ -89,7 +94,7 @@ namespace hex::plugin::decompress { return true; #else hex::unused(evaluator, params); - err::E0012.throwError("hex::dec::zlib_decompress is not available. Please recompile with zlib support."); + err::E0012.throwError("hex::dec::zlib_decompress is not available. Please recompile ImHex with zlib support."); #endif }); @@ -136,7 +141,7 @@ namespace hex::plugin::decompress { return true; #else hex::unused(evaluator, params); - err::E0012.throwError("hex::dec::bzlib_decompress is not available. Please recompile with bzip2 support."); + err::E0012.throwError("hex::dec::bzlib_decompress is not available. Please recompile ImHex with bzip2 support."); #endif }); @@ -184,7 +189,7 @@ namespace hex::plugin::decompress { return true; #else hex::unused(evaluator, params); - err::E0012.throwError("hex::dec::lzma_decompress is not available. Please recompile with liblzma support."); + err::E0012.throwError("hex::dec::lzma_decompress is not available. Please recompile ImHex with liblzma support."); #endif }); @@ -229,7 +234,50 @@ namespace hex::plugin::decompress { return true; #else hex::unused(evaluator, params); - err::E0012.throwError("hex::dec::zstd_decompress is not available. Please recompile with zstd support."); + err::E0012.throwError("hex::dec::zstd_decompress is not available. Please recompile ImHex with zstd support."); + #endif + }); + + /* lz4_decompress(compressed_pattern, section_id) */ + ContentRegistry::PatternLanguage::addFunction(nsHexDec, "lz4_decompress", FunctionParameterCount::exactly(2), [](Evaluator *evaluator, auto params) -> std::optional { + #if IMHEX_FEATURE_ENABLED(LZ4) + auto compressedData = getCompressedData(evaluator, params[0]); + auto §ion = evaluator->getSection(params[1].toUnsigned()); + + // Create the decompression context + LZ4F_decompressionContext_t dctx; + LZ4F_errorCode_t err = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION); + if (LZ4F_isError(err)) { + return false; + } + + std::vector outBuffer(1024 * 1024); + + const u8* sourcePointer = compressedData.data(); + size_t srcSize = compressedData.size(); + + while (srcSize > 0) { + u8* dstPtr = outBuffer.data(); + size_t dstCapacity = outBuffer.size(); + + // Decompress the data + size_t ret = LZ4F_decompress(dctx, dstPtr, &dstCapacity, sourcePointer, &srcSize, nullptr); + if (LZ4F_isError(ret)) { + LZ4F_freeDecompressionContext(dctx); + return false; + } + + section.insert(section.end(), outBuffer.begin(), outBuffer.begin() + dstCapacity); + sourcePointer += (compressedData.size() - srcSize); + } + + // Free the decompression context + LZ4F_freeDecompressionContext(dctx); + + return true; + #else + hex::unused(evaluator, params); + err::E0012.throwError("hex::dec::lz4_decompress is not available. Please recompile ImHex with liblz4 support."); #endif }); }