mirror of
https://github.com/InoriRus/Kyty.git
synced 2024-06-23 15:02:05 -04:00
1382 lines
27 KiB
C++
1382 lines
27 KiB
C++
#include "Kyty/Core/Compression.h"
|
|
|
|
#include "Kyty/Core/Common.h"
|
|
#include "Kyty/Core/DbgAssert.h"
|
|
#include "Kyty/Core/File.h"
|
|
#include "Kyty/Core/MemoryAlloc.h"
|
|
#include "Kyty/Core/SafeDelete.h"
|
|
#include "Kyty/Sys/SysTimer.h"
|
|
|
|
extern "C" {
|
|
#include "LzmaDec.h"
|
|
#include "LzmaEnc.h"
|
|
}
|
|
|
|
// IWYU pragma: no_include "7zTypes.h"
|
|
|
|
#include "miniz.h"
|
|
|
|
//#if KYTY_COMPILER == KYTY_COMPILER_MSVC
|
|
//#define MINIZ_NO_STDIO
|
|
//#define MINIZ_NO_MALLOC
|
|
//#define MZ_ASSERT(x) ASSERT(x)
|
|
////#pragma GCC diagnostic push
|
|
////#pragma GCC diagnostic ignored "-Wunused-value"
|
|
//#include "miniz.c.h"
|
|
////#pragma GCC diagnostic pop
|
|
//#else
|
|
//#define MINIZ_NO_STDIO
|
|
//#define MINIZ_NO_MALLOC
|
|
//#define MZ_ASSERT(x) ASSERT(x)
|
|
//#pragma GCC diagnostic push
|
|
//#pragma GCC diagnostic ignored "-Wunused-value"
|
|
//#if KYTY_COMPILER == KYTY_COMPILER_MINGW
|
|
//#pragma GCC diagnostic ignored "-Wmisleading-indentation"
|
|
//#endif
|
|
////#pragma GCC diagnostic ignored "-Wenum-compare"
|
|
//#include "miniz.c.h"
|
|
//#pragma GCC diagnostic pop
|
|
//#endif
|
|
|
|
#include "zstd.h"
|
|
|
|
namespace Kyty::Core {
|
|
|
|
namespace LzmaImpl {
|
|
|
|
struct InStream
|
|
{
|
|
ISeqInStream t {};
|
|
Core::File mem_file;
|
|
|
|
void Init(const uint8_t* buf, uint32_t size);
|
|
void Close();
|
|
};
|
|
|
|
struct OutStream
|
|
{
|
|
ISeqOutStream t {};
|
|
Core::ByteBuffer buf;
|
|
|
|
void Init(uint32_t initial_size);
|
|
};
|
|
|
|
SRes Read(void* p, void* buf, size_t* size)
|
|
{
|
|
SRes result = SZ_OK;
|
|
auto* s = static_cast<InStream*>(p);
|
|
|
|
EXIT_IF(!p);
|
|
EXIT_IF(!buf);
|
|
EXIT_IF(!size);
|
|
|
|
uint64_t s64 = *size;
|
|
|
|
EXIT_IF((s64 >> 32u) != 0);
|
|
|
|
uint32_t br = 0;
|
|
s->mem_file.Read(buf, s64, &br);
|
|
|
|
// if (br != s64)
|
|
// {
|
|
// result = SZ_ERROR_READ;
|
|
// }
|
|
|
|
*size = br;
|
|
|
|
return result;
|
|
}
|
|
|
|
size_t Write(void* p, const void* buf, size_t size)
|
|
{
|
|
auto* s = static_cast<OutStream*>(p);
|
|
|
|
EXIT_IF(!p);
|
|
EXIT_IF(!buf);
|
|
EXIT_IF(size == 0);
|
|
|
|
uint64_t s64 = size;
|
|
|
|
EXIT_IF((s64 >> 32u) != 0);
|
|
|
|
s->buf.Add(static_cast<const Byte*>(buf), s64);
|
|
|
|
return s64;
|
|
}
|
|
|
|
SRes Progress(void* /*p*/, UInt64 /*inSize*/, UInt64 /*outSize*/)
|
|
{
|
|
return SZ_OK;
|
|
}
|
|
|
|
void* Alloc(void* /*p*/, size_t size)
|
|
{
|
|
return Core::mem_alloc(size);
|
|
}
|
|
|
|
void Free(void* /*p*/, void* address)
|
|
{
|
|
Core::mem_free(address);
|
|
}
|
|
|
|
void InStream::Init(const uint8_t* buf, uint32_t size)
|
|
{
|
|
this->t.Read = Read;
|
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
|
|
this->mem_file.OpenInMem(const_cast<uint8_t*>(buf), size);
|
|
}
|
|
|
|
void InStream::Close()
|
|
{
|
|
this->mem_file.Close();
|
|
}
|
|
|
|
void OutStream::Init(uint32_t initial_size)
|
|
{
|
|
this->t.Write = Write;
|
|
this->buf.Expand(initial_size);
|
|
}
|
|
|
|
static ICompressProgress g_progress_callback = {&LzmaImpl::Progress};
|
|
static ISzAlloc g_alloc_lzma = {&LzmaImpl::Alloc, &LzmaImpl::Free};
|
|
|
|
} // namespace LzmaImpl
|
|
|
|
namespace ZipImpl {
|
|
void* Alloc(void* /*opaque*/, size_t items, size_t size)
|
|
{
|
|
return Core::mem_alloc(items * size);
|
|
}
|
|
|
|
void Free(void* /*opaque*/, void* address)
|
|
{
|
|
Core::mem_free(address);
|
|
}
|
|
|
|
void* Realloc(void* /*opaque*/, void* address, size_t items, size_t size)
|
|
{
|
|
return Core::mem_realloc(address, items * size);
|
|
}
|
|
|
|
size_t Read(void* opaque, mz_uint64 file_ofs, void* buf, size_t n)
|
|
{
|
|
File* f = static_cast<File*>(opaque);
|
|
f->Seek(file_ofs);
|
|
uint32_t b = 0;
|
|
EXIT_IF(sizeof(size_t) > 4 && (uint64_t(n) >> 32u) > 0);
|
|
auto nn = static_cast<uint32_t>(n);
|
|
f->Read(buf, nn, &b);
|
|
return b;
|
|
}
|
|
|
|
size_t Write(void* opaque, mz_uint64 file_ofs, const void* buf, size_t n)
|
|
{
|
|
File* f = static_cast<File*>(opaque);
|
|
f->Seek(file_ofs);
|
|
uint32_t b = 0;
|
|
EXIT_IF(sizeof(size_t) > 4 && (uint64_t(n) >> 32u) > 0);
|
|
auto nn = static_cast<uint32_t>(n);
|
|
f->Write(buf, nn, &b);
|
|
return b;
|
|
}
|
|
|
|
} // namespace ZipImpl
|
|
|
|
constexpr uint32_t HASH_LOG = 12u;
|
|
constexpr uint32_t HASH_SIZE = (1u << HASH_LOG);
|
|
constexpr uint32_t HASH_MASK = (HASH_SIZE - 1u);
|
|
|
|
static void UPDATE_HASH(uint32_t* v, const uint8_t* p)
|
|
{
|
|
(*v) = *(reinterpret_cast<const uint16_t*>(p));
|
|
(*v) ^= *(reinterpret_cast<const uint16_t*>((p) + 1u)) ^ ((*v) >> (16u - HASH_LOG));
|
|
}
|
|
|
|
constexpr int32_t MAX_COPY = 32;
|
|
constexpr int32_t MAX_LEN = 264; /* 256 + 8 */
|
|
constexpr int32_t MAX_DISTANCE = 8192;
|
|
|
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
|
static uint32_t lzf_calc_compressed_size(const void* input, uint32_t length)
|
|
{
|
|
const auto* ip = static_cast<const uint8_t*>(input);
|
|
const uint8_t* ip_limit = ip + length - MAX_COPY - 4;
|
|
uint32_t opl = 0;
|
|
|
|
const uint8_t* htab[HASH_SIZE];
|
|
const uint8_t** hslot = nullptr;
|
|
uint32_t hval = 0;
|
|
|
|
const uint8_t* ref = nullptr;
|
|
int32_t copy = 0;
|
|
int32_t len = 0;
|
|
// int32_t distance;
|
|
const uint8_t* anchor = nullptr;
|
|
|
|
for (hslot = htab; hslot < htab + HASH_SIZE; hslot++)
|
|
{
|
|
*hslot = ip;
|
|
}
|
|
|
|
copy = 0;
|
|
opl++;
|
|
|
|
while (ip < ip_limit)
|
|
{
|
|
UPDATE_HASH(&hval, ip);
|
|
hslot = htab + (hval & HASH_MASK);
|
|
ref = /*(uint8_t*)*/ *hslot;
|
|
|
|
*hslot = ip;
|
|
|
|
if ((ip == ref) || (*(reinterpret_cast<const uint16_t*>(ref)) != *(reinterpret_cast<const uint16_t*>(ip))) || (ref[2] != ip[2]) ||
|
|
((ip - ref) >= MAX_DISTANCE))
|
|
{
|
|
ip++;
|
|
opl++;
|
|
copy++;
|
|
if (copy >= MAX_COPY)
|
|
{
|
|
copy = 0;
|
|
opl++;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
anchor = /*(uint8_t*)*/ ip;
|
|
len = 3;
|
|
ref += 3;
|
|
ip += 3;
|
|
|
|
if (ip < ip_limit - MAX_LEN)
|
|
{
|
|
while (len < MAX_LEN - 8)
|
|
{
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
len += 8;
|
|
}
|
|
ip--;
|
|
}
|
|
len = static_cast<int32_t>(ip - anchor);
|
|
|
|
ip = anchor + len;
|
|
|
|
if (copy != 0)
|
|
{
|
|
// anchor = anchor - copy - 1;
|
|
copy = 0;
|
|
} else
|
|
{
|
|
opl--;
|
|
}
|
|
|
|
len -= 2;
|
|
|
|
// distance--;
|
|
|
|
if (len < 7)
|
|
{
|
|
opl++;
|
|
} else
|
|
{
|
|
opl += 2;
|
|
}
|
|
|
|
opl += 2;
|
|
|
|
--ip;
|
|
UPDATE_HASH(&hval, ip);
|
|
htab[hval & HASH_MASK] = ip;
|
|
ip++;
|
|
}
|
|
|
|
ip_limit = static_cast<const uint8_t*>(input) + length;
|
|
while (ip < ip_limit)
|
|
{
|
|
ip++;
|
|
opl++;
|
|
copy++;
|
|
if (copy == MAX_COPY)
|
|
{
|
|
copy = 0;
|
|
opl++;
|
|
}
|
|
}
|
|
|
|
if (copy == 0)
|
|
{
|
|
opl--;
|
|
}
|
|
|
|
return opl;
|
|
}
|
|
|
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
|
static uint32_t lzf_compress(const void* input, uint32_t length, void* output)
|
|
{
|
|
const auto* ip = static_cast<const uint8_t*>(input);
|
|
const uint8_t* ip_limit = ip + length - MAX_COPY - 4;
|
|
auto* op = static_cast<uint8_t*>(output);
|
|
|
|
const uint8_t* htab[HASH_SIZE];
|
|
const uint8_t** hslot = nullptr;
|
|
uint32_t hval = 0;
|
|
|
|
const uint8_t* ref = nullptr;
|
|
int32_t copy = 0;
|
|
int32_t len = 0;
|
|
int32_t distance = 0;
|
|
const uint8_t* anchor = nullptr;
|
|
|
|
for (hslot = htab; hslot < htab + HASH_SIZE; hslot++)
|
|
{
|
|
*hslot = ip;
|
|
}
|
|
|
|
copy = 0;
|
|
*op++ = MAX_COPY - 1;
|
|
|
|
while (ip < ip_limit)
|
|
{
|
|
UPDATE_HASH(&hval, ip);
|
|
hslot = htab + (hval & HASH_MASK);
|
|
ref = /*(uint8_t*)*/ *hslot;
|
|
|
|
*hslot = ip;
|
|
|
|
distance = static_cast<int32_t>(ip - ref);
|
|
|
|
if ((ip == ref) || (*(reinterpret_cast<const uint16_t*>(ref)) != *(reinterpret_cast<const uint16_t*>(ip))) || (ref[2] != ip[2]) ||
|
|
(distance >= MAX_DISTANCE))
|
|
{
|
|
*op++ = *ip++;
|
|
copy++;
|
|
if (copy >= MAX_COPY)
|
|
{
|
|
copy = 0;
|
|
*op++ = MAX_COPY - 1;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
anchor = /*(uint8_t*)*/ ip;
|
|
len = 3;
|
|
ref += 3;
|
|
ip += 3;
|
|
|
|
if (ip < ip_limit - MAX_LEN)
|
|
{
|
|
while (len < MAX_LEN - 8)
|
|
{
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
if (*ref++ != *ip++)
|
|
{
|
|
break;
|
|
}
|
|
len += 8;
|
|
}
|
|
ip--;
|
|
}
|
|
len = static_cast<int32_t>(ip - anchor);
|
|
|
|
ip = anchor + len;
|
|
|
|
if (copy != 0)
|
|
{
|
|
// anchor = anchor - copy - 1;
|
|
*(op - copy - 1) = copy - 1;
|
|
copy = 0;
|
|
} else
|
|
{
|
|
op--;
|
|
}
|
|
|
|
len -= 2;
|
|
|
|
distance--;
|
|
|
|
if (len < 7)
|
|
{
|
|
*op++ = (static_cast<uint32_t>(len) << 5u) + (static_cast<uint32_t>(distance) >> 8u);
|
|
} else
|
|
{
|
|
*op++ = (7u << 5u) + (static_cast<uint32_t>(distance) >> 8u);
|
|
*op++ = len - 7;
|
|
}
|
|
|
|
*op++ = (static_cast<uint32_t>(distance) & 255u);
|
|
|
|
*op++ = MAX_COPY - 1;
|
|
|
|
--ip;
|
|
UPDATE_HASH(&hval, ip);
|
|
htab[hval & HASH_MASK] = ip;
|
|
ip++;
|
|
}
|
|
|
|
ip_limit = static_cast<const uint8_t*>(input) + length;
|
|
while (ip < ip_limit)
|
|
{
|
|
*op++ = *ip++;
|
|
copy++;
|
|
if (copy == MAX_COPY)
|
|
{
|
|
copy = 0;
|
|
*op++ = MAX_COPY - 1;
|
|
}
|
|
}
|
|
|
|
if (copy != 0)
|
|
{
|
|
*(op - copy - 1) = copy - 1;
|
|
} else
|
|
{
|
|
op--;
|
|
}
|
|
|
|
return op - static_cast<uint8_t*>(output);
|
|
}
|
|
|
|
static uint32_t lzf_calc_decompressed_size(const void* input, uint32_t length)
|
|
{
|
|
const auto* ip = static_cast<const uint8_t*>(input);
|
|
const uint8_t* ip_limit = ip + length - 1;
|
|
uint32_t opl = 0;
|
|
|
|
while (ip < ip_limit)
|
|
{
|
|
uint32_t ctrl = (*ip) + 1;
|
|
uint32_t len = (*ip++) >> 5u;
|
|
|
|
if (ctrl < 33)
|
|
{
|
|
if (ctrl != 0u)
|
|
{
|
|
ip++;
|
|
opl++;
|
|
ctrl--;
|
|
|
|
if (ctrl != 0u)
|
|
{
|
|
ip++;
|
|
opl++;
|
|
ctrl--;
|
|
|
|
if (ctrl != 0u)
|
|
{
|
|
ip++;
|
|
opl++;
|
|
ctrl--;
|
|
|
|
for (; ctrl != 0u; ctrl--)
|
|
{
|
|
ip++;
|
|
opl++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
len--;
|
|
|
|
if (len == 7 - 1)
|
|
{
|
|
len += *ip++;
|
|
}
|
|
|
|
ip++;
|
|
|
|
opl += 3;
|
|
if (len != 0u)
|
|
{
|
|
for (; len != 0u; --len)
|
|
{
|
|
opl++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return opl;
|
|
}
|
|
|
|
static uint32_t lzf_decompress(const void* input, uint32_t length, void* output, uint32_t maxout)
|
|
{
|
|
const auto* ip = static_cast<const uint8_t*>(input);
|
|
const uint8_t* ip_limit = ip + length - 1;
|
|
auto* op = static_cast<uint8_t*>(output);
|
|
uint8_t* op_limit = op + maxout;
|
|
uint8_t* ref = nullptr;
|
|
|
|
while (ip < ip_limit)
|
|
{
|
|
uint32_t ctrl = (*ip) + 1;
|
|
uint32_t ofs = ((*ip) & 31u) << 8u;
|
|
uint32_t len = (*ip++) >> 5u;
|
|
|
|
if (ctrl < 33)
|
|
{
|
|
if (op + ctrl > op_limit)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (ctrl != 0u)
|
|
{
|
|
*op++ = *ip++;
|
|
ctrl--;
|
|
|
|
if (ctrl != 0u)
|
|
{
|
|
*op++ = *ip++;
|
|
ctrl--;
|
|
|
|
if (ctrl != 0u)
|
|
{
|
|
*op++ = *ip++;
|
|
ctrl--;
|
|
|
|
for (; ctrl != 0u; ctrl--)
|
|
{
|
|
*op++ = *ip++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
len--;
|
|
ref = op - ofs;
|
|
ref--;
|
|
|
|
if (len == 7 - 1)
|
|
{
|
|
len += *ip++;
|
|
}
|
|
|
|
ref -= *ip++;
|
|
|
|
if (op + len + 3 > op_limit)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (ref < static_cast<uint8_t*>(output))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
*op++ = *ref++;
|
|
*op++ = *ref++;
|
|
*op++ = *ref++;
|
|
if (len != 0u)
|
|
{
|
|
for (; len != 0u; --len)
|
|
{
|
|
*op++ = *ref++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return op - static_cast<uint8_t*>(output);
|
|
}
|
|
|
|
ByteBuffer CompressLzma(const uint8_t* buf, uint32_t length)
|
|
{
|
|
CLzmaEncHandle enc = LzmaEnc_Create(&LzmaImpl::g_alloc_lzma);
|
|
|
|
EXIT_IF(!enc);
|
|
EXIT_IF(!buf);
|
|
EXIT_IF(length == 0);
|
|
|
|
CLzmaEncProps props;
|
|
LzmaEncProps_Init(&props);
|
|
|
|
[[maybe_unused]] SRes res = LzmaEnc_SetProps(enc, &props);
|
|
|
|
EXIT_IF(res != SZ_OK);
|
|
|
|
LzmaImpl::InStream in_stream {};
|
|
LzmaImpl::OutStream out_stream {};
|
|
|
|
EXIT_IF(reinterpret_cast<ISeqOutStream*>(&out_stream) != &out_stream.t);
|
|
EXIT_IF(reinterpret_cast<ISeqInStream*>(&in_stream) != &in_stream.t);
|
|
|
|
uint8_t header[LZMA_PROPS_SIZE + 8];
|
|
|
|
SizeT size = LZMA_PROPS_SIZE;
|
|
res = LzmaEnc_WriteProperties(enc, header, &size);
|
|
|
|
EXIT_IF(res != SZ_OK || size != LZMA_PROPS_SIZE);
|
|
|
|
uint64_t length64 = length;
|
|
|
|
std::memcpy(header + LZMA_PROPS_SIZE, &length64, 8);
|
|
|
|
in_stream.Init(buf, length);
|
|
out_stream.Init(0);
|
|
|
|
out_stream.t.Write(&out_stream.t, header, LZMA_PROPS_SIZE + 8);
|
|
|
|
res =
|
|
LzmaEnc_Encode(enc, &out_stream.t, &in_stream.t, &LzmaImpl::g_progress_callback, &LzmaImpl::g_alloc_lzma, &LzmaImpl::g_alloc_lzma);
|
|
|
|
EXIT_IF(res != SZ_OK);
|
|
|
|
LzmaEnc_Destroy(enc, &LzmaImpl::g_alloc_lzma, &LzmaImpl::g_alloc_lzma);
|
|
|
|
in_stream.Close();
|
|
|
|
return out_stream.buf;
|
|
}
|
|
|
|
ByteBuffer CompressLzma(const ByteBuffer& buf)
|
|
{
|
|
return CompressLzma(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size());
|
|
}
|
|
|
|
ByteBuffer CompressLzma(const String& str)
|
|
{
|
|
String::Utf8 utf8 = str.utf8_str();
|
|
return CompressLzma(reinterpret_cast<const uint8_t*>(utf8.GetDataConst()), utf8.Size());
|
|
}
|
|
|
|
constexpr size_t LZMA_IN_BUF_SIZE = (1u << 16u);
|
|
constexpr size_t LZMA_OUT_BUF_SIZE = (1u << 16u);
|
|
|
|
static SRes Decode2(CLzmaDec* state, ISeqOutStream* out_stream, ISeqInStream* in_stream, UInt64 unpack_size)
|
|
{
|
|
uint8_t in_buf[LZMA_IN_BUF_SIZE];
|
|
uint8_t out_buf[LZMA_OUT_BUF_SIZE];
|
|
size_t in_pos = 0;
|
|
size_t in_size = 0;
|
|
size_t out_pos = 0;
|
|
LzmaDec_Init(state);
|
|
for (;;)
|
|
{
|
|
if (in_pos == in_size)
|
|
{
|
|
in_size = LZMA_IN_BUF_SIZE;
|
|
RINOK(in_stream->Read(in_stream, in_buf, &in_size));
|
|
in_pos = 0;
|
|
}
|
|
{
|
|
SRes res = 0;
|
|
SizeT in_processed = in_size - in_pos;
|
|
SizeT out_processed = LZMA_OUT_BUF_SIZE - out_pos;
|
|
ELzmaFinishMode finish_mode = LZMA_FINISH_ANY;
|
|
ELzmaStatus status = LZMA_STATUS_NOT_SPECIFIED;
|
|
if (out_processed > unpack_size)
|
|
{
|
|
out_processed = static_cast<SizeT>(unpack_size);
|
|
finish_mode = LZMA_FINISH_END;
|
|
}
|
|
|
|
res = LzmaDec_DecodeToBuf(state, out_buf + out_pos, &out_processed, in_buf + in_pos, &in_processed, finish_mode, &status);
|
|
in_pos += in_processed;
|
|
out_pos += out_processed;
|
|
unpack_size -= out_processed;
|
|
|
|
if (out_stream != nullptr)
|
|
{
|
|
if (out_stream->Write(out_stream, out_buf, out_pos) != out_pos)
|
|
{
|
|
return SZ_ERROR_WRITE;
|
|
}
|
|
}
|
|
|
|
out_pos = 0;
|
|
|
|
if (res != SZ_OK || (unpack_size == 0))
|
|
{
|
|
return res;
|
|
}
|
|
|
|
if (in_processed == 0 && out_processed == 0)
|
|
{
|
|
return SZ_ERROR_DATA;
|
|
}
|
|
}
|
|
}
|
|
|
|
return SZ_OK;
|
|
}
|
|
|
|
ByteBuffer DecompressLzma(const uint8_t* buf, uint32_t length)
|
|
{
|
|
EXIT_IF(!buf);
|
|
EXIT_IF(length == 0);
|
|
|
|
CLzmaDec dec;
|
|
LzmaDec_Construct(&dec);
|
|
|
|
LzmaImpl::InStream in_stream {};
|
|
LzmaImpl::OutStream out_stream {};
|
|
|
|
in_stream.Init(buf, length);
|
|
|
|
EXIT_IF(reinterpret_cast<ISeqOutStream*>(&out_stream) != &out_stream.t);
|
|
EXIT_IF(reinterpret_cast<ISeqInStream*>(&in_stream) != &in_stream.t);
|
|
|
|
uint8_t header[LZMA_PROPS_SIZE + 8];
|
|
|
|
size_t size = LZMA_PROPS_SIZE + 8;
|
|
in_stream.t.Read(&in_stream.t, header, &size);
|
|
|
|
EXIT_IF(size != LZMA_PROPS_SIZE + 8);
|
|
|
|
uint64_t length64 = 0;
|
|
|
|
std::memcpy(&length64, header + LZMA_PROPS_SIZE, 8);
|
|
|
|
EXIT_IF((length64 >> 32u) != 0);
|
|
|
|
out_stream.Init(length64);
|
|
|
|
[[maybe_unused]] SRes res = LzmaDec_Allocate(&dec, header, LZMA_PROPS_SIZE, &LzmaImpl::g_alloc_lzma);
|
|
|
|
EXIT_IF(res != SZ_OK);
|
|
|
|
res = Decode2(&dec, &out_stream.t, &in_stream.t, length64);
|
|
|
|
EXIT_IF(res != SZ_OK);
|
|
|
|
LzmaDec_Free(&dec, &LzmaImpl::g_alloc_lzma);
|
|
|
|
in_stream.Close();
|
|
|
|
return out_stream.buf;
|
|
}
|
|
|
|
ByteBuffer DecompressLzma(const ByteBuffer& buf)
|
|
{
|
|
return DecompressLzma(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size());
|
|
}
|
|
|
|
String DecompressLzmaStr(const uint8_t* buf, uint32_t length)
|
|
{
|
|
ByteBuffer utf8 = DecompressLzma(buf, length);
|
|
EXIT_IF(utf8.At(utf8.Size() - 1) != (Byte)0);
|
|
return String::FromUtf8(reinterpret_cast<const char*>(utf8.GetDataConst()));
|
|
}
|
|
|
|
String DecompressLzmaStr(const ByteBuffer& buf)
|
|
{
|
|
return DecompressLzmaStr(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size());
|
|
}
|
|
|
|
constexpr uint32_t ZIP_OUT_BUF_SIZE = (16 * 1024);
|
|
|
|
ByteBuffer CompressZip(const uint8_t* buf, uint32_t length, ZipCompressLevel level)
|
|
{
|
|
int status = 0;
|
|
mz_stream stream;
|
|
memset(&stream, 0, sizeof(stream));
|
|
|
|
ByteBuffer out;
|
|
uint8_t temp_buf[ZIP_OUT_BUF_SIZE];
|
|
|
|
stream.next_in = buf;
|
|
stream.avail_in = length;
|
|
stream.zalloc = ZipImpl::Alloc;
|
|
stream.zfree = ZipImpl::Free;
|
|
|
|
status = mz_deflateInit(&stream, level);
|
|
|
|
EXIT_IF(status != MZ_OK);
|
|
|
|
if (status == MZ_OK)
|
|
{
|
|
for (;;)
|
|
{
|
|
stream.next_out = temp_buf;
|
|
stream.avail_out = ZIP_OUT_BUF_SIZE;
|
|
|
|
status = mz_deflate(&stream, MZ_FINISH);
|
|
|
|
EXIT_IF(status != MZ_OK && status != MZ_STREAM_END);
|
|
|
|
out.Add(reinterpret_cast<Byte*>(temp_buf), ZIP_OUT_BUF_SIZE - stream.avail_out);
|
|
|
|
if (status != MZ_OK)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
EXIT_IF(stream.total_out != out.Size());
|
|
|
|
mz_deflateEnd(&stream);
|
|
|
|
return out;
|
|
}
|
|
|
|
ByteBuffer CompressZip(const ByteBuffer& buf, ZipCompressLevel level)
|
|
{
|
|
return CompressZip(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size(), level);
|
|
}
|
|
|
|
ByteBuffer CompressZip(const String& str, ZipCompressLevel level)
|
|
{
|
|
String::Utf8 utf8 = str.utf8_str();
|
|
return CompressZip(reinterpret_cast<const uint8_t*>(utf8.GetDataConst()), utf8.Size(), level);
|
|
}
|
|
|
|
ByteBuffer DecompressZip(const uint8_t* buf, uint32_t length)
|
|
{
|
|
int status = 0;
|
|
mz_stream stream;
|
|
memset(&stream, 0, sizeof(stream));
|
|
|
|
ByteBuffer out;
|
|
uint8_t temp_buf[ZIP_OUT_BUF_SIZE];
|
|
|
|
stream.next_in = buf;
|
|
stream.avail_in = length;
|
|
stream.zalloc = ZipImpl::Alloc;
|
|
stream.zfree = ZipImpl::Free;
|
|
|
|
status = mz_inflateInit(&stream);
|
|
|
|
EXIT_IF(status != MZ_OK);
|
|
|
|
if (status == MZ_OK)
|
|
{
|
|
for (;;)
|
|
{
|
|
stream.next_out = temp_buf;
|
|
stream.avail_out = ZIP_OUT_BUF_SIZE;
|
|
|
|
status = mz_inflate(&stream, MZ_NO_FLUSH);
|
|
|
|
EXIT_IF(status != MZ_OK && status != MZ_STREAM_END);
|
|
|
|
out.Add(reinterpret_cast<Byte*>(temp_buf), ZIP_OUT_BUF_SIZE - stream.avail_out);
|
|
|
|
if (status != MZ_OK)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
EXIT_IF(stream.total_out != out.Size());
|
|
|
|
mz_deflateEnd(&stream);
|
|
|
|
return out;
|
|
}
|
|
|
|
ByteBuffer DecompressZip(const ByteBuffer& buf)
|
|
{
|
|
return DecompressZip(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size());
|
|
}
|
|
|
|
String DecompressZipStr(const uint8_t* buf, uint32_t length)
|
|
{
|
|
ByteBuffer utf8 = DecompressZip(buf, length);
|
|
EXIT_IF(utf8.At(utf8.Size() - 1) != Byte(0));
|
|
return String::FromUtf8(reinterpret_cast<const char*>(utf8.GetDataConst()));
|
|
}
|
|
|
|
String DecompressZipStr(const ByteBuffer& buf)
|
|
{
|
|
return DecompressZipStr(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size());
|
|
}
|
|
|
|
ByteBuffer CompressLzf(const uint8_t* buf, uint32_t length)
|
|
{
|
|
uint32_t size = lzf_calc_compressed_size(buf, length);
|
|
ByteBuffer b(size * 2);
|
|
size = lzf_compress(buf, length, b.GetData());
|
|
|
|
KYTY_MEM_CHECK(b.GetDataConst());
|
|
|
|
EXIT_IF(size > b.Size());
|
|
|
|
if (size != b.Size())
|
|
{
|
|
b.RemoveAt(size, b.Size() - size);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
ByteBuffer CompressLzf(const ByteBuffer& buf)
|
|
{
|
|
return CompressLzf(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size());
|
|
}
|
|
|
|
ByteBuffer CompressLzf(const String& str)
|
|
{
|
|
String::Utf8 utf8 = str.utf8_str();
|
|
return CompressLzf(reinterpret_cast<const uint8_t*>(utf8.GetDataConst()), utf8.Size());
|
|
}
|
|
|
|
ByteBuffer DecompressLzf(const uint8_t* buf, uint32_t length)
|
|
{
|
|
[[maybe_unused]] uint32_t size = lzf_calc_decompressed_size(buf, length);
|
|
ByteBuffer b(size);
|
|
size = lzf_decompress(buf, length, b.GetData(), size);
|
|
|
|
EXIT_IF(size != b.Size());
|
|
|
|
return b;
|
|
}
|
|
|
|
ByteBuffer DecompressLzf(const ByteBuffer& buf)
|
|
{
|
|
return DecompressLzf(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size());
|
|
}
|
|
|
|
String DecompressLzfStr(const uint8_t* buf, uint32_t length)
|
|
{
|
|
ByteBuffer utf8 = DecompressLzf(buf, length);
|
|
EXIT_IF(utf8.At(utf8.Size() - 1) != (Byte)0);
|
|
return String::FromUtf8(reinterpret_cast<const char*>(utf8.GetDataConst()));
|
|
}
|
|
|
|
String DecompressLzfStr(const ByteBuffer& buf)
|
|
{
|
|
return DecompressLzfStr(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size());
|
|
}
|
|
|
|
ByteBuffer CompressZstd(const uint8_t* buf, uint32_t length, int level)
|
|
{
|
|
size_t dst_size = ZSTD_compressBound(length) * 2;
|
|
auto* dst = new uint8_t[dst_size];
|
|
dst_size = ZSTD_compress(dst, dst_size, buf, length, level);
|
|
if (ZSTD_isError(dst_size) != 0u)
|
|
{
|
|
EXIT("ZSTD: %s\n", ZSTD_getErrorName(dst_size));
|
|
}
|
|
ByteBuffer ret(dst, static_cast<uint32_t>(dst_size));
|
|
DeleteArray(dst);
|
|
return ret;
|
|
}
|
|
|
|
ByteBuffer CompressZstd(const ByteBuffer& buf, int level)
|
|
{
|
|
return CompressZstd(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size(), level);
|
|
}
|
|
|
|
ByteBuffer CompressZstd(const String& str, int level)
|
|
{
|
|
String::Utf8 utf8 = str.utf8_str();
|
|
return CompressZstd(reinterpret_cast<const uint8_t*>(utf8.GetDataConst()), utf8.Size(), level);
|
|
}
|
|
|
|
ByteBuffer DecompressZstd(const uint8_t* buf, uint32_t length)
|
|
{
|
|
ByteBuffer r;
|
|
size_t buff_out_size = ZSTD_DStreamOutSize();
|
|
Byte* buff_out = new Byte[buff_out_size];
|
|
ZSTD_DCtx* const dctx = ZSTD_createDCtx();
|
|
ZSTD_inBuffer input = {buf, length, 0};
|
|
while (input.pos < input.size)
|
|
{
|
|
ZSTD_outBuffer output = {buff_out, buff_out_size, 0};
|
|
auto ret = ZSTD_decompressStream(dctx, &output, &input);
|
|
if (ZSTD_isError(ret) != 0u)
|
|
{
|
|
EXIT("ZSTD: %s\n", ZSTD_getErrorName(ret));
|
|
}
|
|
r.Add(buff_out, static_cast<uint32_t>(output.pos));
|
|
}
|
|
ZSTD_freeDCtx(dctx);
|
|
DeleteArray(buff_out);
|
|
return r;
|
|
}
|
|
|
|
ByteBuffer DecompressZstd(const ByteBuffer& buf)
|
|
{
|
|
return DecompressZstd(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size());
|
|
}
|
|
|
|
String DecompressZstdStr(const uint8_t* buf, uint32_t length)
|
|
{
|
|
ByteBuffer utf8 = DecompressZstd(buf, length);
|
|
EXIT_IF(utf8.At(utf8.Size() - 1) != (Byte)0);
|
|
return String::FromUtf8(reinterpret_cast<const char*>(utf8.GetDataConst()));
|
|
}
|
|
|
|
String DecompressZstdStr(const ByteBuffer& buf)
|
|
{
|
|
return DecompressZstdStr(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size());
|
|
}
|
|
|
|
struct ZipPrivate
|
|
{
|
|
mz_zip_archive zip;
|
|
};
|
|
|
|
ZipReader::~ZipReader()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
bool ZipReader::Open(const String& file_name)
|
|
{
|
|
Close();
|
|
|
|
m_p = new ZipPrivate;
|
|
memset(&m_p->zip, 0, sizeof(m_p->zip));
|
|
m_p->zip.m_pRead = ZipImpl::Read;
|
|
m_p->zip.m_pWrite = ZipImpl::Write;
|
|
m_p->zip.m_pAlloc = ZipImpl::Alloc;
|
|
m_p->zip.m_pFree = ZipImpl::Free;
|
|
m_p->zip.m_pRealloc = ZipImpl::Realloc;
|
|
|
|
File* f = new File;
|
|
f->Open(file_name, File::Mode::Read);
|
|
|
|
m_p->zip.m_pIO_opaque = f;
|
|
|
|
if (f->IsInvalid() || (mz_zip_reader_init(&m_p->zip, f->Size(), 0) == 0))
|
|
{
|
|
Close();
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZipReader::Open(const ByteBuffer& buf)
|
|
{
|
|
Close();
|
|
|
|
m_p = new ZipPrivate;
|
|
memset(&m_p->zip, 0, sizeof(m_p->zip));
|
|
m_p->zip.m_pRead = ZipImpl::Read;
|
|
m_p->zip.m_pWrite = ZipImpl::Write;
|
|
m_p->zip.m_pAlloc = ZipImpl::Alloc;
|
|
m_p->zip.m_pFree = ZipImpl::Free;
|
|
m_p->zip.m_pRealloc = ZipImpl::Realloc;
|
|
|
|
File* f = new File;
|
|
f->OpenInMem(const_cast<ByteBuffer&>(buf)); // NOLINT(cppcoreguidelines-pro-type-const-cast)
|
|
|
|
m_p->zip.m_pIO_opaque = f;
|
|
|
|
if (mz_zip_reader_init(&m_p->zip, f->Size(), 0) == 0)
|
|
{
|
|
Close();
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZipReader::Open(uint8_t* mem, uint32_t size)
|
|
{
|
|
Close();
|
|
|
|
m_p = new ZipPrivate;
|
|
memset(&m_p->zip, 0, sizeof(m_p->zip));
|
|
m_p->zip.m_pRead = ZipImpl::Read;
|
|
m_p->zip.m_pWrite = ZipImpl::Write;
|
|
m_p->zip.m_pAlloc = ZipImpl::Alloc;
|
|
m_p->zip.m_pFree = ZipImpl::Free;
|
|
m_p->zip.m_pRealloc = ZipImpl::Realloc;
|
|
|
|
File* f = new File;
|
|
f->OpenInMem(mem, size);
|
|
|
|
m_p->zip.m_pIO_opaque = f;
|
|
|
|
if (mz_zip_reader_init(&m_p->zip, f->Size(), 0) == 0)
|
|
{
|
|
Close();
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ZipReader::Close()
|
|
{
|
|
if (m_p != nullptr)
|
|
{
|
|
mz_zip_reader_end(&m_p->zip);
|
|
File* f = static_cast<File*>(m_p->zip.m_pIO_opaque);
|
|
f->Close();
|
|
Delete(f);
|
|
Delete(m_p);
|
|
m_p = nullptr;
|
|
}
|
|
}
|
|
|
|
int ZipReader::GetNumFiles()
|
|
{
|
|
EXIT_IF(!m_p);
|
|
|
|
return static_cast<int>(mz_zip_reader_get_num_files(&m_p->zip));
|
|
}
|
|
|
|
bool ZipReader::GetFileStat(int file_index, ZipFileStat* o)
|
|
{
|
|
EXIT_IF(!m_p);
|
|
|
|
mz_zip_archive_file_stat s;
|
|
|
|
if (mz_zip_reader_file_stat(&m_p->zip, file_index, &s) != 0)
|
|
{
|
|
o->m_file_index = s.m_file_index;
|
|
|
|
SysTimeStruct at = {0};
|
|
sys_time_t_to_system(s.m_time, at);
|
|
o->m_time = DateTime(Date(at.Year, at.Month, at.Day), Time(at.Hour, at.Minute, at.Second, at.Milliseconds));
|
|
|
|
o->m_crc32 = s.m_crc32;
|
|
o->m_comp_size = s.m_comp_size;
|
|
o->m_uncomp_size = s.m_uncomp_size;
|
|
o->m_filename = String::FromUtf8(s.m_filename);
|
|
o->m_comment = String::FromUtf8(s.m_comment);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ZipReader::IsFileDirectory(int file_index)
|
|
{
|
|
EXIT_IF(!m_p);
|
|
|
|
return mz_zip_reader_is_file_a_directory(&m_p->zip, file_index) != 0;
|
|
}
|
|
|
|
bool ZipReader::IsFileEncrypted(int file_index)
|
|
{
|
|
EXIT_IF(!m_p);
|
|
|
|
return mz_zip_reader_is_file_encrypted(&m_p->zip, file_index) != 0;
|
|
}
|
|
|
|
String ZipReader::GetFileName(int file_index)
|
|
{
|
|
uint32_t len = mz_zip_reader_get_filename(&m_p->zip, file_index, nullptr, 0);
|
|
ByteBuffer buf(len);
|
|
mz_zip_reader_get_filename(&m_p->zip, file_index, reinterpret_cast<char*>(buf.GetData()), len);
|
|
EXIT_IF(buf.At(buf.Size() - 1) != (Byte)0);
|
|
return String::FromUtf8(reinterpret_cast<const char*>(buf.GetDataConst()));
|
|
}
|
|
|
|
int ZipReader::FindFile(const String& name, const String& comment)
|
|
{
|
|
return mz_zip_reader_locate_file(&m_p->zip, name.C_Str(), comment.C_Str(), 0);
|
|
}
|
|
|
|
ByteBuffer ZipReader::ExtractFile(int file_index)
|
|
{
|
|
size_t size = 0;
|
|
|
|
if (file_index < 0 || IsFileDirectory(file_index))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
ZipFileStat s {};
|
|
GetFileStat(file_index, &s);
|
|
|
|
if (s.m_uncomp_size == 0)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
void* ptr = mz_zip_reader_extract_to_heap(&m_p->zip, file_index, &size, 0);
|
|
|
|
EXIT_IF(!ptr || !size);
|
|
|
|
EXIT_IF(sizeof(size_t) > 4 && (uint64_t(size) >> 32u) > 0);
|
|
auto nn = static_cast<uint32_t>(size);
|
|
|
|
ByteBuffer buf(nn);
|
|
std::memcpy(buf.GetData(), ptr, size);
|
|
|
|
m_p->zip.m_pFree(nullptr, ptr);
|
|
|
|
return buf;
|
|
}
|
|
|
|
ByteBuffer ZipReader::ExtractFile(const String& name)
|
|
{
|
|
return ExtractFile(FindFile(name));
|
|
}
|
|
|
|
// ZipWriter::ZipWriter()
|
|
//{
|
|
// m_p = nullptr;
|
|
//}
|
|
|
|
ZipWriter::~ZipWriter()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
bool ZipWriter::Create(const String& file_name)
|
|
{
|
|
Close();
|
|
|
|
m_p = new ZipPrivate;
|
|
memset(&m_p->zip, 0, sizeof(m_p->zip));
|
|
m_p->zip.m_pRead = ZipImpl::Read;
|
|
m_p->zip.m_pWrite = ZipImpl::Write;
|
|
m_p->zip.m_pAlloc = ZipImpl::Alloc;
|
|
m_p->zip.m_pFree = ZipImpl::Free;
|
|
m_p->zip.m_pRealloc = ZipImpl::Realloc;
|
|
|
|
File* f = new File;
|
|
f->Create(file_name);
|
|
|
|
m_p->zip.m_pIO_opaque = f;
|
|
|
|
if (f->IsInvalid() || (mz_zip_writer_init(&m_p->zip, 0) == 0))
|
|
{
|
|
Close();
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ZipWriter::Close()
|
|
{
|
|
if (m_p != nullptr)
|
|
{
|
|
mz_zip_writer_finalize_archive(&m_p->zip);
|
|
mz_zip_writer_end(&m_p->zip);
|
|
File* f = static_cast<File*>(m_p->zip.m_pIO_opaque);
|
|
f->Close();
|
|
Delete(f);
|
|
Delete(m_p);
|
|
m_p = nullptr;
|
|
}
|
|
}
|
|
|
|
bool ZipWriter::AddFile(const String& file_name, const ByteBuffer& buf, ZipCompressLevel level)
|
|
{
|
|
EXIT_IF(!m_p);
|
|
|
|
if (m_p != nullptr)
|
|
{
|
|
return AddFile(file_name, reinterpret_cast<const uint8_t*>(buf.GetData()), buf.Size(), level);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ZipWriter::AddFile(const String& file_name, const uint8_t* buf, uint32_t size, ZipCompressLevel level)
|
|
{
|
|
EXIT_IF(!m_p);
|
|
EXIT_IF(!buf && size > 0);
|
|
|
|
if (m_p != nullptr)
|
|
{
|
|
return mz_zip_writer_add_mem(&m_p->zip, file_name.C_Str(), buf, size, level) != 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ZipWriter::AddFileFromFile(const String& file_name, const String& from_file, ZipCompressLevel level)
|
|
{
|
|
EXIT_IF(!m_p);
|
|
|
|
if (m_p != nullptr)
|
|
{
|
|
File f;
|
|
f.Open(from_file, File::Mode::Read);
|
|
if (f.IsInvalid())
|
|
{
|
|
return false;
|
|
}
|
|
ByteBuffer buf = f.ReadWholeBuffer();
|
|
f.Close();
|
|
return AddFile(file_name, buf, level);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ZipWriter::AddFileFromReader(const String& /*file_name*/, ZipReader* from_reader, int file_index)
|
|
{
|
|
EXIT_IF(!m_p);
|
|
EXIT_IF(!from_reader);
|
|
|
|
return mz_zip_writer_add_from_zip_reader(&m_p->zip, &from_reader->m_p->zip, file_index) != 0;
|
|
}
|
|
|
|
bool ZipWriter::AddDir(const String& dir_name)
|
|
{
|
|
EXIT_IF(!m_p);
|
|
|
|
String dir = dir_name.EndsWith(U"/") ? dir_name : dir_name + U"/";
|
|
return AddFile(dir, nullptr, 0);
|
|
}
|
|
|
|
} // namespace Kyty::Core
|