From 950eaea8af9216a6e27b2d7cdbc7a356ee997e2b Mon Sep 17 00:00:00 2001 From: Nik Date: Sun, 31 Dec 2023 11:39:24 +0100 Subject: [PATCH] impr: Make decompression support actually useful (#1481) --- .github/workflows/build.yml | 1 + cmake/modules/ImHexPlugin.cmake | 17 +- dist/Arch/PKGBUILD | 2 +- dist/Brewfile | 5 +- dist/DEBIAN/control.in | 2 +- dist/ImHex-9999.ebuild | 5 +- dist/get_deps_archlinux.sh | 5 +- dist/get_deps_debian.sh | 5 +- dist/get_deps_fedora.sh | 5 +- dist/get_deps_msys2.sh | 5 +- dist/macOS/arm64.Dockerfile | 8 +- dist/msys2/PKGBUILD | 5 +- dist/rpm/imhex.spec | 5 +- dist/web/Dockerfile | 5 +- .../include/hex/api/plugin_manager.hpp | 8 + lib/libimhex/include/hex/plugin.hpp | 9 + lib/libimhex/source/api/plugin_manager.cpp | 10 + .../source/content/views/view_about.cpp | 32 ++- plugins/decompress/CMakeLists.txt | 40 ++- .../source/content/pl_functions.cpp | 240 +++++++++++++++--- .../decompress/source/plugin_decompress.cpp | 7 + 21 files changed, 358 insertions(+), 63 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9687eb62c..e6eb2a2ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -390,6 +390,7 @@ jobs: # Ubuntu cmake build - name: 🛠️ Build + shell: bash run: | set -x git config --global --add safe.directory '*' diff --git a/cmake/modules/ImHexPlugin.cmake b/cmake/modules/ImHexPlugin.cmake index 1b6e4165b..9e9582e32 100644 --- a/cmake/modules/ImHexPlugin.cmake +++ b/cmake/modules/ImHexPlugin.cmake @@ -2,7 +2,7 @@ macro(add_imhex_plugin) # Parse arguments set(options LIBRARY_PLUGIN) set(oneValueArgs NAME) - set(multiValueArgs SOURCES INCLUDES LIBRARIES) + set(multiValueArgs SOURCES INCLUDES LIBRARIES FEATURES) cmake_parse_arguments(IMHEX_PLUGIN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if (IMHEX_STATIC_LINK_PLUGINS) @@ -60,6 +60,11 @@ macro(add_imhex_plugin) set_target_properties(${LIBROMFS_LIBRARY} PROPERTIES POSITION_INDEPENDENT_CODE ON) target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE ${LIBROMFS_LIBRARY}) + foreach(feature ${IMHEX_PLUGIN_FEATURES}) + string(TOUPPER ${feature} feature) + add_definitions(-DIMHEX_PLUGIN_${IMHEX_PLUGIN_NAME}_FEATURE_${feature}=0) + endforeach() + # Add the new plugin to the main dependency list so it gets built by default if (TARGET imhex_all) add_dependencies(imhex_all ${IMHEX_PLUGIN_NAME}) @@ -70,4 +75,14 @@ macro(add_romfs_resource input output) configure_file(${input} ${CMAKE_CURRENT_BINARY_DIR}/romfs/${output} COPYONLY) list(APPEND LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/romfs) +endmacro() + +macro (enable_plugin_feature feature) + string(TOUPPER ${feature} feature) + if (NOT (feature IN_LIST IMHEX_PLUGIN_FEATURES)) + message(FATAL_ERROR "Feature ${feature} is not enabled for plugin ${IMHEX_PLUGIN_NAME}") + endif() + + remove_definitions(-DIMHEX_PLUGIN_${IMHEX_PLUGIN_NAME}_FEATURE_${feature}=0) + add_definitions(-DIMHEX_PLUGIN_${IMHEX_PLUGIN_NAME}_FEATURE_${feature}=1) endmacro() \ No newline at end of file diff --git a/dist/Arch/PKGBUILD b/dist/Arch/PKGBUILD index 70858f447..10240701c 100644 --- a/dist/Arch/PKGBUILD +++ b/dist/Arch/PKGBUILD @@ -8,7 +8,7 @@ pkgdesc="A Hex Editor for Reverse Engineers, Programmers and people who value th arch=("x86_64") url="https://github.com/WerWolv/ImHex" license=('GPL2') -depends=(glfw mbedtls freetype2 libglvnd dbus gtk3 curl fmt yara nlohmann-json libarchive) +depends=(glfw mbedtls freetype2 libglvnd dbus gtk3 curl fmt yara nlohmann-json zlib bzip2 xz zstd) makedepends=(git) provides=(imhex) conflicts=(imhex) diff --git a/dist/Brewfile b/dist/Brewfile index 3331f77fa..3930be2b6 100644 --- a/dist/Brewfile +++ b/dist/Brewfile @@ -10,4 +10,7 @@ brew "gcc@12" brew "llvm" brew "glfw" brew "ninja" -brew "libarchive" \ No newline at end of file +brew "zlib" +brew "xz" +brew "bzip2" +brew "zstd" \ No newline at end of file diff --git a/dist/DEBIAN/control.in b/dist/DEBIAN/control.in index 69080cf54..263e7ed53 100644 --- a/dist/DEBIAN/control.in +++ b/dist/DEBIAN/control.in @@ -4,7 +4,7 @@ Section: editors Priority: optional Architecture: amd64 License: GNU GPL-2 -Depends: libglfw3 | libglfw3-wayland, libmagic1, libmbedtls14, libfreetype6, libopengl0, libdbus-1-3, xdg-desktop-portal, libarchive13 +Depends: libglfw3 | libglfw3-wayland, libmagic1, libmbedtls14, libfreetype6, libopengl0, libdbus-1-3, xdg-desktop-portal Maintainer: WerWolv Description: ImHex Hex Editor A Hex Editor for Reverse Engineers, Programmers and diff --git a/dist/ImHex-9999.ebuild b/dist/ImHex-9999.ebuild index c5092593e..fcc2f0ec5 100644 --- a/dist/ImHex-9999.ebuild +++ b/dist/ImHex-9999.ebuild @@ -24,6 +24,9 @@ RDEPEND="${DEPEND} dev-cpp/nlohmann_json dbus xdg-desktop-portal - app-arch/libarchive + sys-libs/zlib + app-arch/bzip2 + app-arch/lzma + app-arch/zstd " BDEPEND="${DEPEND}" diff --git a/dist/get_deps_archlinux.sh b/dist/get_deps_archlinux.sh index 58dcbc796..83965bb87 100755 --- a/dist/get_deps_archlinux.sh +++ b/dist/get_deps_archlinux.sh @@ -15,4 +15,7 @@ pacman -S $@ --needed \ yara \ nlohmann-json \ ninja \ - libarchive + zlib \ + bzip2 \ + xz \ + zstd diff --git a/dist/get_deps_debian.sh b/dist/get_deps_debian.sh index 46b432988..4aeccc08c 100755 --- a/dist/get_deps_debian.sh +++ b/dist/get_deps_debian.sh @@ -23,4 +23,7 @@ apt install -y \ libcurl4-gnutls-dev \ libgtk-3-dev \ ninja-build \ - libarchive-dev + zlib1g-dev \ + libbz2-dev \ + liblzma-dev \ + libzstd-dev diff --git a/dist/get_deps_fedora.sh b/dist/get_deps_fedora.sh index a90e119e6..2a9b1a5db 100755 --- a/dist/get_deps_fedora.sh +++ b/dist/get_deps_fedora.sh @@ -13,4 +13,7 @@ dnf install -y \ lld \ mbedtls-devel \ gtk3-devel \ - libarchive-devel \ No newline at end of file + libzstd-devel \ + zlib-devel \ + bzip2-devel \ + xz-devel \ No newline at end of file diff --git a/dist/get_deps_msys2.sh b/dist/get_deps_msys2.sh index cc75295eb..1f985f437 100755 --- a/dist/get_deps_msys2.sh +++ b/dist/get_deps_msys2.sh @@ -13,4 +13,7 @@ pacman -S --needed --noconfirm \ mingw-w64-x86_64-dlfcn \ mingw-w64-x86_64-ninja \ mingw-w64-x86_64-capstone \ - mingw-w64-x86_64-libarchive + mingw-w64-x86_64-zlib \ + mingw-w64-x86_64-bzip2 \ + mingw-w64-x86_64-xz \ + mingw-w64-x86_64-zstd diff --git a/dist/macOS/arm64.Dockerfile b/dist/macOS/arm64.Dockerfile index d9203e133..b0b19d157 100644 --- a/dist/macOS/arm64.Dockerfile +++ b/dist/macOS/arm64.Dockerfile @@ -52,7 +52,10 @@ vcpkg install --triplet=arm-osx-mytriplet curl vcpkg install --triplet=arm-osx-mytriplet mbedtls vcpkg install --triplet=arm-osx-mytriplet freetype vcpkg install --triplet=arm-osx-mytriplet josuttis-jthread -vcpkg install --triplet=arm-osx-mytriplet libarchive +vcpkg install --triplet=arm-osx-mytriplet zlib +vcpkg install --triplet=arm-osx-mytriplet bzip2 +vcpkg install --triplet=arm-osx-mytriplet liblzma +vcpkg install --triplet=arm-osx-mytriplet zstd EOF ## Install glfw3 dep @@ -103,6 +106,9 @@ if [ "$CUSTOM_GLFW" ]; then fi EOF +RUN mkdir -p /vcpkg/installed/arm-osx-mytriplet/lib/pkgconfig +RUN mkdir -p /osxcross/target/macports/pkgs/vcpkg/installed/arm-osx-mytriplet/lib/pkgconfig + ## Build glfw RUN --mount=type=cache,target=/cache <&)> callback; }; + struct Feature { + std::string name; + bool enabled; + }; + struct PluginFunctions { using InitializePluginFunc = void (*)(); using InitializeLibraryFunc = void (*)(); @@ -27,6 +32,7 @@ namespace hex { using SetImGuiContextFunc = void (*)(ImGuiContext *); using IsBuiltinPluginFunc = bool (*)(); using GetSubCommandsFunc = void* (*)(); + using GetFeaturesFunc = void* (*)(); InitializePluginFunc initializePluginFunction = nullptr; InitializeLibraryFunc initializeLibraryFunction = nullptr; @@ -37,6 +43,7 @@ namespace hex { SetImGuiContextFunc setImGuiContextFunction = nullptr; IsBuiltinPluginFunc isBuiltinPluginFunction = nullptr; GetSubCommandsFunc getSubCommandsFunction = nullptr; + GetFeaturesFunc getFeaturesFunction = nullptr; }; class Plugin { @@ -65,6 +72,7 @@ namespace hex { [[nodiscard]] bool isLoaded() const; [[nodiscard]] std::span getSubCommands() const; + [[nodiscard]] std::span getFeatures() const; [[nodiscard]] bool isLibraryPlugin() const; diff --git a/lib/libimhex/include/hex/plugin.hpp b/lib/libimhex/include/hex/plugin.hpp index cdf46ccfe..d09d72fc0 100644 --- a/lib/libimhex/include/hex/plugin.hpp +++ b/lib/libimhex/include/hex/plugin.hpp @@ -92,3 +92,12 @@ return &g_subCommands; \ } \ std::vector g_subCommands + +#define IMHEX_FEATURE_ENABLED(feature) WOLV_TOKEN_CONCAT(WOLV_TOKEN_CONCAT(WOLV_TOKEN_CONCAT(IMHEX_PLUGIN_, IMHEX_PLUGIN_NAME), _FEATURE_), feature) +#define IMHEX_PLUGIN_FEATURES() IMHEX_PLUGIN_FEATURES_IMPL() +#define IMHEX_PLUGIN_FEATURES_IMPL() \ + extern std::vector g_features; \ + extern "C" [[gnu::visibility("default")]] void* getFeatures() { \ + return &g_features; \ + } \ + std::vector g_features diff --git a/lib/libimhex/source/api/plugin_manager.cpp b/lib/libimhex/source/api/plugin_manager.cpp index 43409f166..6da7f2387 100644 --- a/lib/libimhex/source/api/plugin_manager.cpp +++ b/lib/libimhex/source/api/plugin_manager.cpp @@ -44,6 +44,7 @@ namespace hex { m_functions.setImGuiContextFunction = getPluginFunction("setImGuiContext"); m_functions.isBuiltinPluginFunction = getPluginFunction("isBuiltinPlugin"); m_functions.getSubCommandsFunction = getPluginFunction("getSubCommands"); + m_functions.getFeaturesFunction = getPluginFunction("getFeatures"); } Plugin::Plugin(const hex::PluginFunctions &functions) { @@ -188,6 +189,15 @@ namespace hex { } } + std::span Plugin::getFeatures() const { + if (m_functions.getFeaturesFunction != nullptr) { + auto result = m_functions.getFeaturesFunction(); + return *static_cast*>(result); + } else { + return { }; + } + } + bool Plugin::isLibraryPlugin() const { return m_functions.initializeLibraryFunction != nullptr && m_functions.initializePluginFunction == nullptr; diff --git a/plugins/builtin/source/content/views/view_about.cpp b/plugins/builtin/source/content/views/view_about.cpp index fd62aab3b..36c1dcb1c 100644 --- a/plugins/builtin/source/content/views/view_about.cpp +++ b/plugins/builtin/source/content/views/view_about.cpp @@ -342,7 +342,7 @@ namespace hex::plugin::builtin { ImGuiExt::BeginSubWindow("hex.builtin.view.help.about.plugins"_lang); ImGui::PopStyleVar(); { - if (ImGui::BeginTable("plugins", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingFixedFit)) { + if (ImGui::BeginTable("plugins", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) { ImGui::TableSetupScrollFreeze(0, 1); ImGui::TableSetupColumn("hex.builtin.view.help.about.plugins.plugin"_lang); ImGui::TableSetupColumn("hex.builtin.view.help.about.plugins.author"_lang); @@ -355,18 +355,40 @@ namespace hex::plugin::builtin { if (plugin.isLibraryPlugin()) continue; + auto features = plugin.getFeatures(); + ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGuiExt::TextFormattedColored( - plugin.isBuiltinPlugin() ? ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_Highlight) : ImGui::GetStyleColorVec4(ImGuiCol_Text), - "{}", plugin.getPluginName().c_str() - ); + bool open = false; + + ImGui::PushStyleColor(ImGuiCol_Text, plugin.isBuiltinPlugin() ? ImGuiExt::GetCustomColorU32(ImGuiCustomCol_Highlight) : ImGui::GetColorU32(ImGuiCol_Text)); + if (features.empty()) + ImGui::BulletText("%s", plugin.getPluginName().c_str()); + else + open = ImGui::TreeNode(plugin.getPluginName().c_str()); + ImGui::PopStyleColor(); + ImGui::TableNextColumn(); ImGui::TextUnformatted(plugin.getPluginAuthor().c_str()); ImGui::TableNextColumn(); ImGui::TextUnformatted(plugin.getPluginDescription().c_str()); ImGui::TableNextColumn(); ImGui::TextUnformatted(plugin.isLoaded() ? ICON_VS_CHECK : ICON_VS_CLOSE); + + if (open) { + for (const auto &feature : plugin.getFeatures()) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted(" {}", feature.name.c_str()); + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(feature.enabled ? ICON_VS_CHECK : ICON_VS_CLOSE); + + } + + ImGui::TreePop(); + } } ImGui::EndTable(); diff --git a/plugins/decompress/CMakeLists.txt b/plugins/decompress/CMakeLists.txt index 093c540ad..b0a5d2975 100644 --- a/plugins/decompress/CMakeLists.txt +++ b/plugins/decompress/CMakeLists.txt @@ -2,17 +2,17 @@ cmake_minimum_required(VERSION 3.16) include(ImHexPlugin) -# Homebrew only ships a libarchive keg, include directories have to be set manually -if(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") - execute_process( - COMMAND brew --prefix libarchive - OUTPUT_VARIABLE LIBARCHIVE_PREFIX - OUTPUT_STRIP_TRAILING_WHITESPACE - COMMAND_ERROR_IS_FATAL ANY - ) - set(LibArchive_INCLUDE_DIR "${LIBARCHIVE_PREFIX}/include") -endif() -find_package(LibArchive REQUIRED) +macro(addOptionalLibrary package library) + find_package(${package}) + if (${package}_FOUND) + set_property(TARGET ${package}::${library} PROPERTY POSITION_INDEPENDENT_CODE ON) + string(TOUPPER ${package} PACKAGE) + set(LIBRARIES ${LIBRARIES} ${package}::${library}) + message(STATUS "Enabling decompression support using ${package} (${${package}_VERSION})") + enable_plugin_feature(${PACKAGE}) + endif() + +endmacro() add_imhex_plugin( NAME @@ -25,5 +25,21 @@ add_imhex_plugin( include LIBRARIES ui - LibArchive::LibArchive + FEATURES + ZLIB + BZIP2 + LIBLZMA + ZSTD ) + +find_package(zstd) +if (TARGET zstd::libzstd_static) + addOptionalLibrary(zstd libzstd_static) +elseif(TARGET zstd::libzstd_shared) + addOptionalLibrary(zstd libzstd_shared) +endif() + +addOptionalLibrary(ZLIB ZLIB) +addOptionalLibrary(BZip2 BZip2) +addOptionalLibrary(LibLZMA LibLZMA) +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 9d13974ea..b7f28daf7 100644 --- a/plugins/decompress/source/content/pl_functions.cpp +++ b/plugins/decompress/source/content/pl_functions.cpp @@ -1,58 +1,232 @@ #include +#include #include #include #include -#include -#include - #include #include #include +#if IMHEX_FEATURE_ENABLED(ZLIB) + #include +#endif +#if IMHEX_FEATURE_ENABLED(BZIP2) + #include +#endif +#if IMHEX_FEATURE_ENABLED(LIBLZMA) + #include +#endif +#if IMHEX_FEATURE_ENABLED(ZSTD) + #include +#endif + namespace hex::plugin::decompress { + namespace { + + std::vector getCompressedData(pl::core::Evaluator *evaluator, const pl::core::Token::Literal &literal) { + const auto inputPattern = literal.toPattern(); + + std::vector compressedData; + compressedData.resize(inputPattern->getSize()); + evaluator->readData(inputPattern->getOffset(), compressedData.data(), compressedData.size(), inputPattern->getSection()); + + return compressedData; + } + + } + void registerPatternLanguageFunctions() { using namespace pl::core; using FunctionParameterCount = pl::api::FunctionParameterCount; const pl::api::Namespace nsHexDec = { "builtin", "hex", "dec" }; - /* decompress() */ - ContentRegistry::PatternLanguage::addFunction(nsHexDec, "decompress", FunctionParameterCount::exactly(2), [](Evaluator *evaluator, auto params) -> std::optional { - const auto inputPattern = params[0].toPattern(); - auto §ion = evaluator->getSection(params[1].toUnsigned()); + /* zlib_decompress(compressed_pattern, section_id) */ + ContentRegistry::PatternLanguage::addFunction(nsHexDec, "zlib_decompress", FunctionParameterCount::exactly(2), [](Evaluator *evaluator, auto params) -> std::optional { + #if IMHEX_FEATURE_ENABLED(ZLIB) + auto compressedData = getCompressedData(evaluator, params[0]); + auto §ion = evaluator->getSection(params[1].toUnsigned()); - std::vector compressedData; - compressedData.resize(inputPattern->getSize()); - evaluator->readData(inputPattern->getOffset(), compressedData.data(), compressedData.size(), inputPattern->getSection()); - - auto inArchive = archive_read_new(); - ON_SCOPE_EXIT { - archive_read_close(inArchive); - archive_read_free(inArchive); - }; - - archive_read_support_filter_all(inArchive); - archive_read_support_format_raw(inArchive); - - archive_read_open_memory(inArchive, compressedData.data(), compressedData.size()); - - archive_entry *entry = nullptr; - while (archive_read_next_header(inArchive, &entry) == ARCHIVE_OK) { - const void *block = nullptr; - size_t size = 0x00; - i64 offset = 0x00; - - while (archive_read_data_block(inArchive, &block, &size, &offset) == ARCHIVE_OK) { - section.resize(section.size() + size); - std::memcpy(section.data(), block, size); + z_stream stream = { }; + if (inflateInit(&stream) != Z_OK) { + return false; } - } - return std::nullopt; + section.resize(100); + + stream.avail_in = compressedData.size(); + stream.avail_out = section.size(); + stream.next_in = compressedData.data(); + stream.next_out = section.data(); + + ON_SCOPE_EXIT { + inflateEnd(&stream); + }; + + while (stream.avail_in != 0) { + auto res = inflate(&stream, Z_NO_FLUSH); + if (res == Z_STREAM_END) { + section.resize(section.size() - stream.avail_out); + break; + } + if (res != Z_OK) + return false; + + if (stream.avail_out != 0) + break; + + section.resize(section.size() * 2); + stream.next_out = section.data(); + stream.avail_out = section.size(); + } + + return true; + #else + hex::unused(evaluator, params); + err::E0012.throwError("hex::dec::zlib_decompress is not available. Please recompile with zlib support."); + #endif + }); + + /* bzip_decompress(compressed_pattern, section_id) */ + ContentRegistry::PatternLanguage::addFunction(nsHexDec, "bzip_decompress", FunctionParameterCount::exactly(2), [](Evaluator *evaluator, auto params) -> std::optional { + #if IMHEX_FEATURE_ENABLED(BZIP2) + auto compressedData = getCompressedData(evaluator, params[0]); + auto §ion = evaluator->getSection(params[1].toUnsigned()); + + bz_stream stream = { }; + if (BZ2_bzDecompressInit(&stream, 0, 1) != Z_OK) { + return false; + } + + section.resize(100); + + stream.avail_in = compressedData.size(); + stream.avail_out = section.size(); + stream.next_in = reinterpret_cast(compressedData.data()); + stream.next_out = reinterpret_cast(section.data()); + + ON_SCOPE_EXIT { + BZ2_bzDecompressEnd(&stream); + }; + + while (stream.avail_in != 0) { + auto res = BZ2_bzDecompress(&stream); + if (res == BZ_STREAM_END) { + section.resize(section.size() - stream.avail_out); + break; + } + if (res != BZ_OK) + return false; + + if (stream.avail_out != 0) + break; + + section.resize(section.size() * 2); + stream.next_out = reinterpret_cast(section.data()); + stream.avail_out = section.size(); + } + + return true; + #else + hex::unused(evaluator, params); + err::E0012.throwError("hex::dec::bzlib_decompress is not available. Please recompile with bzip2 support."); + #endif + + }); + + /* lzma_decompress(compressed_pattern, section_id) */ + ContentRegistry::PatternLanguage::addFunction(nsHexDec, "lzma_decompress", FunctionParameterCount::exactly(2), [](Evaluator *evaluator, auto params) -> std::optional { + #if IMHEX_FEATURE_ENABLED(LIBLZMA) + auto compressedData = getCompressedData(evaluator, params[0]); + auto §ion = evaluator->getSection(params[1].toUnsigned()); + + lzma_stream stream = LZMA_STREAM_INIT; + if (lzma_auto_decoder(&stream, 0x10000, LZMA_IGNORE_CHECK) != Z_OK) { + return false; + } + + section.resize(100); + + stream.avail_in = compressedData.size(); + stream.avail_out = section.size(); + stream.next_in = compressedData.data(); + stream.next_out = section.data(); + + ON_SCOPE_EXIT { + lzma_end(&stream); + }; + + while (stream.avail_in != 0) { + auto res = lzma_code(&stream, LZMA_RUN); + if (res == BZ_STREAM_END) { + section.resize(section.size() - stream.avail_out); + break; + } + if (res != LZMA_OK && res != LZMA_STREAM_END) + return false; + + if (stream.avail_out != 0) + break; + + section.resize(section.size() * 2); + stream.next_out = compressedData.data(); + stream.avail_out = compressedData.size(); + } + + return true; + #else + hex::unused(evaluator, params); + err::E0012.throwError("hex::dec::lzma_decompress is not available. Please recompile with liblzma support."); + #endif + }); + + /* zstd_decompress(compressed_pattern, section_id) */ + ContentRegistry::PatternLanguage::addFunction(nsHexDec, "zstd_decompress", FunctionParameterCount::exactly(2), [](Evaluator *evaluator, auto params) -> std::optional { + #if IMHEX_FEATURE_ENABLED(ZSTD) + auto compressedData = getCompressedData(evaluator, params[0]); + auto §ion = evaluator->getSection(params[1].toUnsigned()); + + ZSTD_DCtx* dctx = ZSTD_createDCtx(); + if (dctx == nullptr) { + return false; + } + + ON_SCOPE_EXIT { + ZSTD_freeDCtx(dctx); + }; + + const u8* source = compressedData.data(); + size_t sourceSize = compressedData.size(); + + do { + size_t blockSize = ZSTD_getFrameContentSize(source, sourceSize); + + if (blockSize == ZSTD_CONTENTSIZE_ERROR) { + return false; + } + + section.resize(section.size() + blockSize); + + size_t decodedSize = ZSTD_decompressDCtx(dctx, section.data() + section.size() - blockSize, blockSize, source, sourceSize); + + if (ZSTD_isError(decodedSize)) { + return false; + } + + source = source + sourceSize; + sourceSize = 0; + + } while (sourceSize > 0); + + return true; + #else + hex::unused(evaluator, params); + err::E0012.throwError("hex::dec::zstd_decompress is not available. Please recompile with zstd support."); + #endif }); } diff --git a/plugins/decompress/source/plugin_decompress.cpp b/plugins/decompress/source/plugin_decompress.cpp index 17a992605..dc57de028 100644 --- a/plugins/decompress/source/plugin_decompress.cpp +++ b/plugins/decompress/source/plugin_decompress.cpp @@ -19,3 +19,10 @@ IMHEX_PLUGIN_SETUP("Decompressing", "WerWolv", "Support for decompressing data") registerPatternLanguageFunctions(); } + +IMHEX_PLUGIN_FEATURES() { + { "bzip2 Support", IMHEX_FEATURE_ENABLED(BZIP2) }, + { "zlib Support", IMHEX_FEATURE_ENABLED(ZLIB) }, + { "LZMA Support", IMHEX_FEATURE_ENABLED(LIBLZMA) }, + { "zstd Support", IMHEX_FEATURE_ENABLED(ZSTD) }, +}; \ No newline at end of file