#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" }; /* 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()); z_stream stream = { }; if (inflateInit(&stream) != 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 { 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 }); } }