libclod
C library for interacting with NBTs, region files, LOD data and other things.
Loading...
Searching...
No Matches
decompress.c
1#include <clod/compression.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include <libdeflate.h>
6#include <lz4frame.h>
7#include <lzma.h>
8#include <zstd.h>
9
11 void *(*malloc_func)(size_t);
12 void (*free_func)(void *);
13
14 struct libdeflate_decompressor *libdeflate_decompressor;
15 LZ4F_dctx *lz4_ctx;
16 ZSTD_DCtx *zstd_dctx;
17};
18
20 struct clod_decompressor *ctx = malloc(sizeof(struct clod_decompressor));
21 memset(ctx, 0,sizeof(*ctx));
22 // LZ4 and ZSTD are missing custom memory allocation methods.
23 ctx->malloc_func = malloc;
24 ctx->free_func = free;
25 return ctx;
26}
27
29 if (ctx->libdeflate_decompressor)
30 libdeflate_free_decompressor(ctx->libdeflate_decompressor);
31
32 if (ctx->lz4_ctx)
33 LZ4F_freeDecompressionContext(ctx->lz4_ctx);
34
35 if (ctx->zstd_dctx)
36 ZSTD_freeDCtx(ctx->zstd_dctx);
37
38 free(ctx);
39}
40
41void *decompressor_lzma_malloc(void *user, size_t n, size_t size) {
42 auto const ctx = (struct clod_decompressor *)user;
43 return ctx->malloc_func(n * size);
44}
45void decompressor_lzma_free(void *user, void *address) {
46 auto const ctx = (struct clod_decompressor *)user;
47 ctx->free_func(address);
48}
49
52 void *const dst, const size_t dst_size,
53 const void *const src, const size_t src_size,
54 size_t *const actual_size,
55 const enum clod_compression_method method
56) {
57 switch (method) {
58 case CLOD_UNCOMPRESSED: {
59 if (dst_size < src_size) {
60 if (actual_size) *actual_size = src_size;
62 }
63 if (!actual_size && dst_size != src_size) return CLOD_COMPRESSION_SHORT_OUTPUT;
64 memcpy(dst, src, src_size);
65 if (actual_size) *actual_size = src_size;
67 }
68 case CLOD_GZIP: {
69 if (!ctx->libdeflate_decompressor) {
70 struct libdeflate_options opts = {0};
71 opts.sizeof_options = sizeof(opts);
72 opts.malloc_func = ctx->malloc_func;
73 opts.free_func = ctx->free_func;
74
75 ctx->libdeflate_decompressor = libdeflate_alloc_decompressor_ex(&opts);
76 if (!ctx->libdeflate_decompressor) {
78 }
79 }
80
81 const enum libdeflate_result res = libdeflate_gzip_decompress(ctx->libdeflate_decompressor,
82 src, src_size,
83 dst, dst_size,
84 actual_size
85 );
86
87 if (res == LIBDEFLATE_INSUFFICIENT_SPACE) {
88 if (actual_size) *actual_size = 0;
90 }
91
92 switch (res) {
93 case LIBDEFLATE_SUCCESS: return CLOD_COMPRESSION_SUCCESS;
94 case LIBDEFLATE_BAD_DATA: return CLOD_COMPRESSION_MALFORMED;
95 case LIBDEFLATE_SHORT_OUTPUT: return CLOD_COMPRESSION_SHORT_OUTPUT;
96 default: return CLOD_COMPRESSION_INVALID;
97 }
98 }
99 case CLOD_ZLIB: {
100 if (!ctx->libdeflate_decompressor) {
101 struct libdeflate_options opts = {0};
102 opts.sizeof_options = sizeof(opts);
103 opts.malloc_func = ctx->malloc_func;
104 opts.free_func = ctx->free_func;
105
106 ctx->libdeflate_decompressor = libdeflate_alloc_decompressor_ex(&opts);
107 if (!ctx->libdeflate_decompressor) {
109 }
110 }
111
112 const enum libdeflate_result res = libdeflate_zlib_decompress(ctx->libdeflate_decompressor,
113 src, src_size,
114 dst, dst_size,
115 actual_size
116 );
117
118 if (res == LIBDEFLATE_INSUFFICIENT_SPACE) {
119 if (actual_size) *actual_size = 0;
121 }
122
123 switch (res) {
124 case LIBDEFLATE_SUCCESS: return CLOD_COMPRESSION_SUCCESS;
125 case LIBDEFLATE_BAD_DATA: return CLOD_COMPRESSION_MALFORMED;
126 case LIBDEFLATE_SHORT_OUTPUT: return CLOD_COMPRESSION_SHORT_OUTPUT;
127 default: return CLOD_COMPRESSION_INVALID;
128 }
129 }
130 case CLOD_DEFLATE: {
131 if (!ctx->libdeflate_decompressor) {
132 struct libdeflate_options opts = {0};
133 opts.sizeof_options = sizeof(opts);
134 opts.malloc_func = ctx->malloc_func;
135 opts.free_func = ctx->free_func;
136
137 ctx->libdeflate_decompressor = libdeflate_alloc_decompressor_ex(&opts);
138 if (!ctx->libdeflate_decompressor) {
140 }
141 }
142
143 const enum libdeflate_result res = libdeflate_deflate_decompress(ctx->libdeflate_decompressor,
144 src, src_size,
145 dst, dst_size,
146 actual_size
147 );
148
149 if (res == LIBDEFLATE_INSUFFICIENT_SPACE) {
150 if (actual_size) *actual_size = 0;
152 }
153
154 switch (res) {
155 case LIBDEFLATE_SUCCESS: return CLOD_COMPRESSION_SUCCESS;
156 case LIBDEFLATE_BAD_DATA: return CLOD_COMPRESSION_MALFORMED;
157 case LIBDEFLATE_SHORT_OUTPUT: return CLOD_COMPRESSION_SHORT_OUTPUT;
158 default: return CLOD_COMPRESSION_INVALID;
159 }
160 }
161 case CLOD_LZ4F: {
162 if (src_size < LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH) {
164 }
165
166 const size_t header_size = LZ4F_headerSize(src, src_size);
167 if (LZ4F_isError(header_size) || header_size > src_size) {
169 }
170
171 if (!ctx->lz4_ctx) {
172 const LZ4F_errorCode_t err = LZ4F_createDecompressionContext(&ctx->lz4_ctx, LZ4F_VERSION);
173 if (LZ4F_isError(err) || !ctx->lz4_ctx) {
175 }
176 }
177
178 size_t src_offset = 0, dst_offset = 0;
179
180 LZ4F_frameInfo_t info;
181 src_offset = src_size;
182 size_t res = LZ4F_getFrameInfo(ctx->lz4_ctx, &info, src, &src_offset);
183
184 if (LZ4F_isError(res)) {
185 LZ4F_resetDecompressionContext(ctx->lz4_ctx);
187 }
188
189 if (info.contentSize > dst_size) {
190 LZ4F_resetDecompressionContext(ctx->lz4_ctx);
191 if (actual_size) *actual_size = info.contentSize;
193 }
194
195 if (!actual_size && info.contentSize > 0 && info.contentSize < dst_size) {
196 LZ4F_resetDecompressionContext(ctx->lz4_ctx);
198 }
199
200 while (true) {
201 size_t dst_chunk_size = dst_size - dst_offset;
202 size_t src_chunk_size = src_size - src_offset;
203
204 res = LZ4F_decompress(ctx->lz4_ctx,
205 (char*)dst + dst_offset, &dst_chunk_size,
206 (const char*)src + src_offset, &src_chunk_size,
207 nullptr);
208
209 if (LZ4F_isError(res)) {
210 LZ4F_resetDecompressionContext(ctx->lz4_ctx);
212 }
213
214 dst_offset += dst_chunk_size;
215 src_offset += src_chunk_size;
216
217 if (res == 0) {
218 LZ4F_resetDecompressionContext(ctx->lz4_ctx);
219 if (!actual_size && dst_offset < dst_size) return CLOD_COMPRESSION_SHORT_OUTPUT;
220 if (actual_size) *actual_size = dst_offset;
222 }
223
224 if (src_offset >= src_size) {
225 LZ4F_resetDecompressionContext(ctx->lz4_ctx);
227 }
228
229 if (dst_offset >= dst_size) {
230 LZ4F_resetDecompressionContext(ctx->lz4_ctx);
231 if (actual_size) *actual_size = info.contentSize > 0 ? info.contentSize : 0;
233 }
234 }
235 }
236 case CLOD_XZ: {
237 const lzma_allocator allocator = {
238 .alloc = decompressor_lzma_malloc,
239 .free = decompressor_lzma_free,
240 .opaque = ctx,
241 };
242
243 lzma_stream stream = LZMA_STREAM_INIT;
244 stream.allocator = &allocator;
245
246 lzma_ret ret = lzma_stream_decoder(&stream, UINT64_MAX, 0);
247 if (ret != LZMA_OK) {
248 return ret == LZMA_MEM_ERROR ? CLOD_COMPRESSION_ALLOC_FAILED : CLOD_COMPRESSION_INVALID;
249 }
250
251 stream.next_in = (uint8_t *)src;
252 stream.avail_in = src_size;
253 stream.next_out = (uint8_t *)dst;
254 stream.avail_out = dst_size;
255
256 do {
257 ret = lzma_code(&stream, LZMA_FINISH);
258 } while (ret == LZMA_OK);
259
260 lzma_end(&stream);
261
262 switch (ret) {
263 case LZMA_STREAM_END:
264 if (!actual_size && stream.avail_out != 0) return CLOD_COMPRESSION_SHORT_OUTPUT;
265 if (actual_size) *actual_size = stream.total_out;
267 case LZMA_BUF_ERROR:
268 if (stream.avail_out == 0) {
269 if (actual_size) *actual_size = 0;
271 }
273 case LZMA_MEM_ERROR: case LZMA_MEMLIMIT_ERROR:
275 case LZMA_FORMAT_ERROR: case LZMA_OPTIONS_ERROR: case LZMA_DATA_ERROR:
277 default:
279 }
280 }
281 case CLOD_ZSTD: {
282 if (!ctx->zstd_dctx) {
283 ctx->zstd_dctx = ZSTD_createDCtx();
284 if (!ctx->zstd_dctx) return CLOD_COMPRESSION_ALLOC_FAILED;
285 }
286
287 ZSTD_inBuffer in = {src, src_size, 0};
288 ZSTD_outBuffer out = {dst, dst_size, 0};
289
290 size_t ret = 1;
291 while (in.pos < in.size && ret != 0) {
292 ret = ZSTD_decompressStream(ctx->zstd_dctx, &out, &in);
293 if (ZSTD_isError(ret)) {
294 ZSTD_DCtx_reset(ctx->zstd_dctx, ZSTD_reset_session_only);
295 if (ZSTD_getErrorCode(ret) == ZSTD_error_noForwardProgress_destFull) {
296 if (actual_size) {
297 auto const frame_size = ZSTD_getFrameContentSize(src, src_size);
298 if (frame_size == ZSTD_CONTENTSIZE_UNKNOWN || frame_size == ZSTD_CONTENTSIZE_ERROR)
299 *actual_size = 0;
300 else
301 *actual_size = frame_size;
302 }
304 }
306 }
307 }
308
309 if (ret == 0) {
310 if (!actual_size && out.pos != out.size) return CLOD_COMPRESSION_SHORT_OUTPUT;
311 if (actual_size) *actual_size = out.pos;
313 }
314
316 }
317 default: {
319 }
320 }
321}
clod_compression_result
Definition compression.h:92
void clod_decompressor_free(struct clod_decompressor *ctx)
Definition decompress.c:28
struct clod_decompressor * clod_decompressor_init()
Definition decompress.c:19
clod_compression_method
Definition compression.h:36
enum clod_compression_result clod_decompress(struct clod_decompressor *ctx, void *const dst, const size_t dst_size, const void *const src, const size_t src_size, size_t *const actual_size, const enum clod_compression_method method)
Definition decompress.c:51
@ CLOD_COMPRESSION_ALLOC_FAILED
@ CLOD_COMPRESSION_SHORT_OUTPUT
@ CLOD_COMPRESSION_MALFORMED
Definition compression.h:98
@ CLOD_COMPRESSION_SUCCESS
Definition compression.h:94
@ CLOD_COMPRESSION_INVALID
Definition compression.h:96
@ CLOD_COMPRESSION_SHORT_BUFFER
@ CLOD_GZIP
Definition compression.h:63
@ CLOD_UNCOMPRESSED
Definition compression.h:38
@ CLOD_ZSTD
Definition compression.h:43
@ CLOD_LZ4F
Definition compression.h:47
@ CLOD_XZ
Definition compression.h:51
@ CLOD_DEFLATE
Definition compression.h:55
@ CLOD_ZLIB
Definition compression.h:59
Sized string helpers.