450 lines
11 KiB
C++
450 lines
11 KiB
C++
#include "extra2d/asset/data_processor.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
|
|
#ifdef E2D_USE_ZSTD
|
|
#include <zstd.h>
|
|
#endif
|
|
|
|
#ifdef E2D_USE_LZ4
|
|
#include <lz4.h>
|
|
#endif
|
|
|
|
#ifdef E2D_USE_ZLIB
|
|
#include <zlib.h>
|
|
#endif
|
|
|
|
namespace extra2d {
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Decryptor 实现
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Decryptor::Decryptor(const std::string& key, Type type)
|
|
: key_(key), type_(type) {
|
|
}
|
|
|
|
std::vector<u8> Decryptor::process(const std::vector<u8>& input) {
|
|
if (input.empty() || type_ == Type::None) {
|
|
return processNext(input);
|
|
}
|
|
|
|
std::vector<u8> result;
|
|
switch (type_) {
|
|
case Type::XOR:
|
|
result = decryptXOR(input);
|
|
break;
|
|
case Type::AES256:
|
|
result = decryptAES256(input);
|
|
break;
|
|
default:
|
|
result = input;
|
|
break;
|
|
}
|
|
|
|
return processNext(result);
|
|
}
|
|
|
|
std::vector<u8> Decryptor::decryptXOR(const std::vector<u8>& input) {
|
|
if (key_.empty()) {
|
|
return input;
|
|
}
|
|
|
|
std::vector<u8> result(input.size());
|
|
const size_t keyLen = key_.size();
|
|
|
|
for (size_t i = 0; i < input.size(); ++i) {
|
|
result[i] = input[i] ^ static_cast<u8>(key_[i % keyLen]);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::vector<u8> Decryptor::decryptAES256(const std::vector<u8>& input) {
|
|
return decryptXOR(input);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Decompressor 实现
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Decompressor::Decompressor(Compression algo)
|
|
: algo_(algo) {
|
|
}
|
|
|
|
std::vector<u8> Decompressor::process(const std::vector<u8>& input) {
|
|
if (input.empty() || algo_ == Compression::None) {
|
|
return processNext(input);
|
|
}
|
|
|
|
std::vector<u8> result;
|
|
switch (algo_) {
|
|
case Compression::Zstd:
|
|
result = decompressZstd(input);
|
|
break;
|
|
case Compression::LZ4:
|
|
result = decompressLZ4(input);
|
|
break;
|
|
case Compression::Zlib:
|
|
result = decompressZlib(input);
|
|
break;
|
|
default:
|
|
result = input;
|
|
break;
|
|
}
|
|
|
|
return processNext(result);
|
|
}
|
|
|
|
std::vector<u8> Decompressor::decompressZstd(const std::vector<u8>& input) {
|
|
#ifdef E2D_USE_ZSTD
|
|
unsigned long long const decompressedSize = ZSTD_getFrameContentSize(input.data(), input.size());
|
|
|
|
if (decompressedSize == ZSTD_CONTENTSIZE_ERROR ||
|
|
decompressedSize == ZSTD_CONTENTSIZE_UNKNOWN) {
|
|
return {};
|
|
}
|
|
|
|
std::vector<u8> result(static_cast<size_t>(decompressedSize));
|
|
|
|
size_t const actualSize = ZSTD_decompress(
|
|
result.data(), result.size(),
|
|
input.data(), input.size()
|
|
);
|
|
|
|
if (ZSTD_isError(actualSize)) {
|
|
return {};
|
|
}
|
|
|
|
result.resize(actualSize);
|
|
return result;
|
|
#else
|
|
return input;
|
|
#endif
|
|
}
|
|
|
|
std::vector<u8> Decompressor::decompressLZ4(const std::vector<u8>& input) {
|
|
#ifdef E2D_USE_LZ4
|
|
if (input.size() < sizeof(u32)) {
|
|
return {};
|
|
}
|
|
|
|
u32 originalSize;
|
|
std::memcpy(&originalSize, input.data(), sizeof(u32));
|
|
|
|
std::vector<u8> result(originalSize);
|
|
|
|
int const decompressedSize = LZ4_decompress_safe(
|
|
reinterpret_cast<const char*>(input.data() + sizeof(u32)),
|
|
reinterpret_cast<char*>(result.data()),
|
|
static_cast<int>(input.size() - sizeof(u32)),
|
|
static_cast<int>(originalSize)
|
|
);
|
|
|
|
if (decompressedSize < 0) {
|
|
return {};
|
|
}
|
|
|
|
return result;
|
|
#else
|
|
return input;
|
|
#endif
|
|
}
|
|
|
|
std::vector<u8> Decompressor::decompressZlib(const std::vector<u8>& input) {
|
|
#ifdef E2D_USE_ZLIB
|
|
std::vector<u8> result;
|
|
result.reserve(input.size() * 4);
|
|
|
|
const size_t CHUNK = 16384;
|
|
u8 out[CHUNK];
|
|
|
|
z_stream strm;
|
|
strm.zalloc = Z_NULL;
|
|
strm.zfree = Z_NULL;
|
|
strm.opaque = Z_NULL;
|
|
strm.avail_in = static_cast<uInt>(input.size());
|
|
strm.next_in = const_cast<Bytef*>(input.data());
|
|
|
|
if (inflateInit(&strm) != Z_OK) {
|
|
return {};
|
|
}
|
|
|
|
int ret;
|
|
do {
|
|
strm.avail_out = CHUNK;
|
|
strm.next_out = out;
|
|
|
|
ret = inflate(&strm, Z_NO_FLUSH);
|
|
if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
|
|
inflateEnd(&strm);
|
|
return {};
|
|
}
|
|
|
|
result.insert(result.end(), out, out + CHUNK - strm.avail_out);
|
|
} while (strm.avail_out == 0);
|
|
|
|
inflateEnd(&strm);
|
|
return result;
|
|
#else
|
|
return input;
|
|
#endif
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Encryptor 实现
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Encryptor::Encryptor(const std::string& key, Decryptor::Type type)
|
|
: key_(key), type_(type) {
|
|
}
|
|
|
|
std::vector<u8> Encryptor::process(const std::vector<u8>& input) {
|
|
if (input.empty() || type_ == Decryptor::Type::None) {
|
|
return processNext(input);
|
|
}
|
|
|
|
std::vector<u8> result;
|
|
switch (type_) {
|
|
case Decryptor::Type::XOR:
|
|
result = encryptXOR(input);
|
|
break;
|
|
case Decryptor::Type::AES256:
|
|
result = encryptAES256(input);
|
|
break;
|
|
default:
|
|
result = input;
|
|
break;
|
|
}
|
|
|
|
return processNext(result);
|
|
}
|
|
|
|
std::vector<u8> Encryptor::encryptXOR(const std::vector<u8>& input) {
|
|
if (key_.empty()) {
|
|
return input;
|
|
}
|
|
|
|
std::vector<u8> result(input.size());
|
|
const size_t keyLen = key_.size();
|
|
|
|
for (size_t i = 0; i < input.size(); ++i) {
|
|
result[i] = input[i] ^ static_cast<u8>(key_[i % keyLen]);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::vector<u8> Encryptor::encryptAES256(const std::vector<u8>& input) {
|
|
return encryptXOR(input);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Compressor 实现
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Compressor::Compressor(Compression algo, int level)
|
|
: algo_(algo), level_(level) {
|
|
}
|
|
|
|
std::vector<u8> Compressor::process(const std::vector<u8>& input) {
|
|
if (input.empty() || algo_ == Compression::None) {
|
|
return processNext(input);
|
|
}
|
|
|
|
std::vector<u8> result;
|
|
switch (algo_) {
|
|
case Compression::Zstd:
|
|
result = compressZstd(input);
|
|
break;
|
|
case Compression::LZ4:
|
|
result = compressLZ4(input);
|
|
break;
|
|
case Compression::Zlib:
|
|
result = compressZlib(input);
|
|
break;
|
|
default:
|
|
result = input;
|
|
break;
|
|
}
|
|
|
|
return processNext(result);
|
|
}
|
|
|
|
std::vector<u8> Compressor::compressZstd(const std::vector<u8>& input) {
|
|
#ifdef E2D_USE_ZSTD
|
|
size_t const bound = ZSTD_compressBound(input.size());
|
|
std::vector<u8> result(bound);
|
|
|
|
size_t const compressedSize = ZSTD_compress(
|
|
result.data(), result.size(),
|
|
input.data(), input.size(),
|
|
level_
|
|
);
|
|
|
|
if (ZSTD_isError(compressedSize)) {
|
|
return {};
|
|
}
|
|
|
|
result.resize(compressedSize);
|
|
return result;
|
|
#else
|
|
return input;
|
|
#endif
|
|
}
|
|
|
|
std::vector<u8> Compressor::compressLZ4(const std::vector<u8>& input) {
|
|
#ifdef E2D_USE_LZ4
|
|
int const bound = LZ4_compressBound(static_cast<int>(input.size()));
|
|
std::vector<u8> result(sizeof(u32) + bound);
|
|
|
|
u32 originalSize = static_cast<u32>(input.size());
|
|
std::memcpy(result.data(), &originalSize, sizeof(u32));
|
|
|
|
int const compressedSize = LZ4_compress_default(
|
|
reinterpret_cast<const char*>(input.data()),
|
|
reinterpret_cast<char*>(result.data() + sizeof(u32)),
|
|
static_cast<int>(input.size()),
|
|
bound
|
|
);
|
|
|
|
if (compressedSize <= 0) {
|
|
return {};
|
|
}
|
|
|
|
result.resize(sizeof(u32) + compressedSize);
|
|
return result;
|
|
#else
|
|
return input;
|
|
#endif
|
|
}
|
|
|
|
std::vector<u8> Compressor::compressZlib(const std::vector<u8>& input) {
|
|
#ifdef E2D_USE_ZLIB
|
|
std::vector<u8> result;
|
|
result.reserve(input.size() / 2);
|
|
|
|
const size_t CHUNK = 16384;
|
|
u8 out[CHUNK];
|
|
|
|
z_stream strm;
|
|
strm.zalloc = Z_NULL;
|
|
strm.zfree = Z_NULL;
|
|
strm.opaque = Z_NULL;
|
|
|
|
if (deflateInit(&strm, level_) != Z_OK) {
|
|
return {};
|
|
}
|
|
|
|
strm.avail_in = static_cast<uInt>(input.size());
|
|
strm.next_in = const_cast<Bytef*>(input.data());
|
|
|
|
int ret;
|
|
do {
|
|
strm.avail_out = CHUNK;
|
|
strm.next_out = out;
|
|
|
|
ret = deflate(&strm, Z_FINISH);
|
|
if (ret == Z_STREAM_ERROR) {
|
|
deflateEnd(&strm);
|
|
return {};
|
|
}
|
|
|
|
result.insert(result.end(), out, out + CHUNK - strm.avail_out);
|
|
} while (strm.avail_out == 0);
|
|
|
|
deflateEnd(&strm);
|
|
return result;
|
|
#else
|
|
return input;
|
|
#endif
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// DataPipe 实现
|
|
// ---------------------------------------------------------------------------
|
|
|
|
DataPipe& DataPipe::decrypt(const std::string& key, Decryptor::Type type) {
|
|
processors_.push_back(std::make_unique<Decryptor>(key, type));
|
|
return *this;
|
|
}
|
|
|
|
DataPipe& DataPipe::decompress(Compression algo) {
|
|
processors_.push_back(std::make_unique<Decompressor>(algo));
|
|
return *this;
|
|
}
|
|
|
|
DataPipe& DataPipe::encrypt(const std::string& key, Decryptor::Type type) {
|
|
processors_.push_back(std::make_unique<Encryptor>(key, type));
|
|
return *this;
|
|
}
|
|
|
|
DataPipe& DataPipe::compress(Compression algo, int level) {
|
|
processors_.push_back(std::make_unique<Compressor>(algo, level));
|
|
return *this;
|
|
}
|
|
|
|
DataPipe& DataPipe::add(Unique<DataProcessor> processor) {
|
|
processors_.push_back(std::move(processor));
|
|
return *this;
|
|
}
|
|
|
|
std::vector<u8> DataPipe::process(const std::vector<u8>& input) {
|
|
if (processors_.empty()) {
|
|
return input;
|
|
}
|
|
|
|
std::vector<u8> result = input;
|
|
for (auto& processor : processors_) {
|
|
result = processor->process(result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void DataPipe::clear() {
|
|
processors_.clear();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 工具函数实现
|
|
// ---------------------------------------------------------------------------
|
|
|
|
std::vector<u8> computeChecksum(const std::vector<u8>& data) {
|
|
std::vector<u8> result(32, 0);
|
|
|
|
u64 hash1 = 14695981039346656037ULL;
|
|
u64 hash2 = 14695981039346656037ULL;
|
|
|
|
for (size_t i = 0; i < data.size(); ++i) {
|
|
hash1 ^= static_cast<u64>(data[i]);
|
|
hash1 *= 1099511628211ULL;
|
|
|
|
hash2 ^= static_cast<u64>(data[i]) << ((i % 8) * 8);
|
|
hash2 *= 1099511628211ULL;
|
|
}
|
|
|
|
for (size_t i = 0; i < 8; ++i) {
|
|
result[i] = static_cast<u8>((hash1 >> (i * 8)) & 0xFF);
|
|
result[i + 8] = static_cast<u8>((hash2 >> (i * 8)) & 0xFF);
|
|
}
|
|
|
|
for (size_t i = 16; i < 32; ++i) {
|
|
result[i] = static_cast<u8>((hash1 ^ hash2) >> ((i - 16) * 8) & 0xFF);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool verifyChecksum(const std::vector<u8>& data, const std::vector<u8>& checksum) {
|
|
if (checksum.size() != 32) {
|
|
return false;
|
|
}
|
|
|
|
auto computed = computeChecksum(data);
|
|
return std::equal(computed.begin(), computed.end(), checksum.begin());
|
|
}
|
|
|
|
}
|