mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-30 13:05:25 -05:00
impr: Make decompression support actually useful (#1481)
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -1,58 +1,232 @@
|
||||
#include <hex.hpp>
|
||||
#include <hex/plugin.hpp>
|
||||
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <pl/core/evaluator.hpp>
|
||||
#include <pl/patterns/pattern.hpp>
|
||||
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
|
||||
#include <wolv/utils/guards.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#if IMHEX_FEATURE_ENABLED(ZLIB)
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
#if IMHEX_FEATURE_ENABLED(BZIP2)
|
||||
#include <bzlib.h>
|
||||
#endif
|
||||
#if IMHEX_FEATURE_ENABLED(LIBLZMA)
|
||||
#include <lzma.h>
|
||||
#endif
|
||||
#if IMHEX_FEATURE_ENABLED(ZSTD)
|
||||
#include <zstd.h>
|
||||
#endif
|
||||
|
||||
namespace hex::plugin::decompress {
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<u8> getCompressedData(pl::core::Evaluator *evaluator, const pl::core::Token::Literal &literal) {
|
||||
const auto inputPattern = literal.toPattern();
|
||||
|
||||
std::vector<u8> 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<Token::Literal> {
|
||||
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<Token::Literal> {
|
||||
#if IMHEX_FEATURE_ENABLED(ZLIB)
|
||||
auto compressedData = getCompressedData(evaluator, params[0]);
|
||||
auto §ion = evaluator->getSection(params[1].toUnsigned());
|
||||
|
||||
std::vector<u8> 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<Token::Literal> {
|
||||
#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<char*>(compressedData.data());
|
||||
stream.next_out = reinterpret_cast<char*>(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<char*>(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<Token::Literal> {
|
||||
#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<Token::Literal> {
|
||||
#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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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) },
|
||||
};
|
||||
Reference in New Issue
Block a user