2023-12-24 13:14:51 +01:00
|
|
|
#include <hex.hpp>
|
2023-12-31 11:39:24 +01:00
|
|
|
#include <hex/plugin.hpp>
|
2023-12-24 13:14:51 +01:00
|
|
|
|
|
|
|
#include <hex/api/content_registry.hpp>
|
|
|
|
#include <pl/core/evaluator.hpp>
|
|
|
|
#include <pl/patterns/pattern.hpp>
|
|
|
|
|
|
|
|
#include <wolv/utils/guards.hpp>
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <optional>
|
|
|
|
|
2023-12-31 11:39:24 +01:00
|
|
|
#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
|
2024-07-07 10:26:24 +02:00
|
|
|
#if IMHEX_FEATURE_ENABLED(LZ4)
|
|
|
|
#include <lz4.h>
|
|
|
|
#include <lz4frame.h>
|
|
|
|
#endif
|
|
|
|
|
2023-12-31 11:39:24 +01:00
|
|
|
|
2023-12-24 13:14:51 +01:00
|
|
|
namespace hex::plugin::decompress {
|
|
|
|
|
2023-12-31 11:39:24 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-12-24 13:14:51 +01:00
|
|
|
void registerPatternLanguageFunctions() {
|
|
|
|
using namespace pl::core;
|
|
|
|
using FunctionParameterCount = pl::api::FunctionParameterCount;
|
|
|
|
|
|
|
|
const pl::api::Namespace nsHexDec = { "builtin", "hex", "dec" };
|
|
|
|
|
2023-12-31 11:39:24 +01:00
|
|
|
/* zlib_decompress(compressed_pattern, section_id) */
|
2024-01-27 16:12:02 +01:00
|
|
|
ContentRegistry::PatternLanguage::addFunction(nsHexDec, "zlib_decompress", FunctionParameterCount::exactly(3), [](Evaluator *evaluator, auto params) -> std::optional<Token::Literal> {
|
2023-12-31 11:39:24 +01:00
|
|
|
#if IMHEX_FEATURE_ENABLED(ZLIB)
|
|
|
|
auto compressedData = getCompressedData(evaluator, params[0]);
|
|
|
|
auto §ion = evaluator->getSection(params[1].toUnsigned());
|
2024-01-27 16:12:02 +01:00
|
|
|
auto windowSize = params[2].toUnsigned();
|
2023-12-24 13:14:51 +01:00
|
|
|
|
2023-12-31 11:39:24 +01:00
|
|
|
z_stream stream = { };
|
2024-01-27 16:12:02 +01:00
|
|
|
if (inflateInit2(&stream, windowSize) != Z_OK) {
|
2023-12-31 11:39:24 +01:00
|
|
|
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;
|
|
|
|
|
2024-03-22 17:34:49 +01:00
|
|
|
const auto prevSectionSize = section.size();
|
|
|
|
section.resize(prevSectionSize * 2);
|
|
|
|
stream.next_out = section.data() + prevSectionSize;
|
|
|
|
stream.avail_out = prevSectionSize;
|
2023-12-31 11:39:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
hex::unused(evaluator, params);
|
2024-07-07 10:26:24 +02:00
|
|
|
err::E0012.throwError("hex::dec::zlib_decompress is not available. Please recompile ImHex with zlib support.");
|
2023-12-31 11:39:24 +01:00
|
|
|
#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);
|
2023-12-24 13:14:51 +01:00
|
|
|
|
2023-12-31 11:39:24 +01:00
|
|
|
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());
|
2023-12-24 13:14:51 +01:00
|
|
|
|
2023-12-31 11:39:24 +01:00
|
|
|
ON_SCOPE_EXIT {
|
|
|
|
BZ2_bzDecompressEnd(&stream);
|
|
|
|
};
|
2023-12-24 13:14:51 +01:00
|
|
|
|
2023-12-31 11:39:24 +01:00
|
|
|
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;
|
2023-12-24 13:14:51 +01:00
|
|
|
|
2023-12-31 11:39:24 +01:00
|
|
|
if (stream.avail_out != 0)
|
|
|
|
break;
|
2023-12-24 13:14:51 +01:00
|
|
|
|
2024-03-22 17:34:49 +01:00
|
|
|
const auto prevSectionSize = section.size();
|
|
|
|
section.resize(prevSectionSize * 2);
|
|
|
|
stream.next_out = reinterpret_cast<char*>(section.data()) + prevSectionSize;
|
|
|
|
stream.avail_out = prevSectionSize;
|
2023-12-24 13:14:51 +01:00
|
|
|
}
|
|
|
|
|
2023-12-31 11:39:24 +01:00
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
hex::unused(evaluator, params);
|
2024-07-07 10:26:24 +02:00
|
|
|
err::E0012.throwError("hex::dec::bzlib_decompress is not available. Please recompile ImHex with bzip2 support.");
|
2023-12-31 11:39:24 +01:00
|
|
|
#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);
|
2024-07-21 20:33:46 +02:00
|
|
|
if (res == LZMA_STREAM_END) {
|
2023-12-31 11:39:24 +01:00
|
|
|
section.resize(section.size() - stream.avail_out);
|
|
|
|
break;
|
|
|
|
}
|
2024-07-21 20:33:46 +02:00
|
|
|
if (res != LZMA_OK)
|
2023-12-31 11:39:24 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
if (stream.avail_out != 0)
|
|
|
|
break;
|
|
|
|
|
2024-03-22 17:34:49 +01:00
|
|
|
const auto prevSectionSize = section.size();
|
|
|
|
section.resize(prevSectionSize * 2);
|
|
|
|
stream.next_out = section.data() + prevSectionSize;
|
|
|
|
stream.avail_out = prevSectionSize;
|
2023-12-31 11:39:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
hex::unused(evaluator, params);
|
2024-07-07 10:26:24 +02:00
|
|
|
err::E0012.throwError("hex::dec::lzma_decompress is not available. Please recompile ImHex with liblzma support.");
|
2023-12-31 11:39:24 +01:00
|
|
|
#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);
|
2024-07-07 10:26:24 +02:00
|
|
|
err::E0012.throwError("hex::dec::zstd_decompress is not available. Please recompile ImHex with zstd support.");
|
|
|
|
#endif
|
|
|
|
});
|
|
|
|
|
|
|
|
/* lz4_decompress(compressed_pattern, section_id) */
|
2024-07-07 15:23:12 +02:00
|
|
|
ContentRegistry::PatternLanguage::addFunction(nsHexDec, "lz4_decompress", FunctionParameterCount::exactly(3), [](Evaluator *evaluator, auto params) -> std::optional<Token::Literal> {
|
2024-07-07 10:26:24 +02:00
|
|
|
#if IMHEX_FEATURE_ENABLED(LZ4)
|
|
|
|
auto compressedData = getCompressedData(evaluator, params[0]);
|
|
|
|
auto §ion = evaluator->getSection(params[1].toUnsigned());
|
2024-07-07 15:23:12 +02:00
|
|
|
bool frame = params[2].toBoolean();
|
2024-07-07 10:26:24 +02:00
|
|
|
|
2024-07-07 15:23:12 +02:00
|
|
|
if (frame) {
|
|
|
|
LZ4F_decompressionContext_t dctx;
|
|
|
|
LZ4F_errorCode_t err = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
|
|
|
|
if (LZ4F_isError(err)) {
|
|
|
|
return false;
|
|
|
|
}
|
2024-07-07 10:26:24 +02:00
|
|
|
|
2024-07-07 15:23:12 +02:00
|
|
|
std::vector<u8> outBuffer(1024 * 1024);
|
2024-07-07 10:26:24 +02:00
|
|
|
|
2024-07-07 15:23:12 +02:00
|
|
|
const u8* sourcePointer = compressedData.data();
|
|
|
|
size_t srcSize = compressedData.size();
|
2024-07-07 10:26:24 +02:00
|
|
|
|
2024-07-07 15:23:12 +02:00
|
|
|
while (srcSize > 0) {
|
|
|
|
u8* dstPtr = outBuffer.data();
|
|
|
|
size_t dstCapacity = outBuffer.size();
|
2024-07-07 10:26:24 +02:00
|
|
|
|
2024-07-07 15:23:12 +02:00
|
|
|
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);
|
2024-07-07 10:26:24 +02:00
|
|
|
}
|
|
|
|
|
2024-07-07 15:23:12 +02:00
|
|
|
LZ4F_freeDecompressionContext(dctx);
|
|
|
|
} else {
|
|
|
|
section.resize(1024 * 1024);
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
auto decompressedSize = LZ4_decompress_safe(reinterpret_cast<const char*>(compressedData.data()), reinterpret_cast<char *>(section.data()), compressedData.size(), static_cast<int>(section.size()));
|
|
|
|
|
|
|
|
if (decompressedSize < 0) {
|
|
|
|
return false;
|
|
|
|
} else if (decompressedSize > 0) {
|
|
|
|
// Successful decompression
|
|
|
|
section.resize(decompressedSize);
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
// Buffer too small, resize and try again
|
|
|
|
section.resize(section.size() * 2);
|
|
|
|
}
|
|
|
|
}
|
2024-07-07 10:26:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
hex::unused(evaluator, params);
|
|
|
|
err::E0012.throwError("hex::dec::lz4_decompress is not available. Please recompile ImHex with liblz4 support.");
|
2023-12-31 11:39:24 +01:00
|
|
|
#endif
|
2023-12-24 13:14:51 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-07-21 20:33:46 +02:00
|
|
|
}
|