1 /* mz_strm_lzma.c -- Stream for lzma inflate/deflate
2    Version 2.8.1, December 1, 2018
3    part of the MiniZip project
4 
5    Copyright (C) 2010-2018 Nathan Moinvaziri
6       https://github.com/nmoinvaz/minizip
7 
8    This program is distributed under the terms of the same license as lzma.
9    See the accompanying LICENSE file for the full text of the license.
10 */
11 
12 
13 #include "mz.h"
14 #include "mz_strm.h"
15 #include "mz_strm_lzma.h"
16 
17 #include "lzma.h"
18 
19 /***************************************************************************/
20 
21 #define MZ_LZMA_HEADER_SIZE (4)
22 
23 /***************************************************************************/
24 
25 static mz_stream_vtbl mz_stream_lzma_vtbl = {
26     mz_stream_lzma_open,
27     mz_stream_lzma_is_open,
28     mz_stream_lzma_read,
29     mz_stream_lzma_write,
30     mz_stream_lzma_tell,
31     mz_stream_lzma_seek,
32     mz_stream_lzma_close,
33     mz_stream_lzma_error,
34     mz_stream_lzma_create,
35     mz_stream_lzma_delete,
36     mz_stream_lzma_get_prop_int64,
37     mz_stream_lzma_set_prop_int64
38 };
39 
40 /***************************************************************************/
41 
42 typedef struct mz_stream_lzma_s {
43     mz_stream   stream;
44     lzma_stream lstream;
45     int32_t     mode;
46     int32_t     error;
47     uint8_t     buffer[INT16_MAX];
48     int32_t     buffer_len;
49     int64_t     total_in;
50     int64_t     total_out;
51     int64_t     max_total_in;
52     int64_t     max_total_out;
53     int8_t      initialized;
54     uint32_t    preset;
55 } mz_stream_lzma;
56 
57 /***************************************************************************/
58 
mz_stream_lzma_open(void * stream,const char * path,int32_t mode)59 int32_t mz_stream_lzma_open(void *stream, const char *path, int32_t mode)
60 {
61     mz_stream_lzma *lzma = (mz_stream_lzma *)stream;
62     lzma_filter filters[LZMA_FILTERS_MAX + 1];
63     lzma_options_lzma opt_lzma;
64     uint32_t size = 0;
65     uint8_t major = 0;
66     uint8_t minor = 0;
67 
68     MZ_UNUSED(path);
69 
70     memset(&opt_lzma, 0, sizeof(opt_lzma));
71 
72     lzma->lstream.total_in = 0;
73     lzma->lstream.total_out = 0;
74 
75     lzma->total_in = 0;
76     lzma->total_out = 0;
77 
78     if (mode & MZ_OPEN_MODE_WRITE)
79     {
80 #ifdef MZ_ZIP_NO_COMPRESSION
81         MZ_UNUSED(filters);
82         MZ_UNUSED(major);
83         MZ_UNUSED(minor);
84         return MZ_SUPPORT_ERROR;
85 #else
86         lzma->lstream.next_out = lzma->buffer;
87         lzma->lstream.avail_out = sizeof(lzma->buffer);
88 
89         if (lzma_lzma_preset(&opt_lzma, lzma->preset))
90             return MZ_OPEN_ERROR;
91 
92         memset(&filters, 0, sizeof(filters));
93 
94         filters[0].id = LZMA_FILTER_LZMA1;
95         filters[0].options = &opt_lzma;
96         filters[1].id = LZMA_VLI_UNKNOWN;
97 
98         lzma_properties_size(&size, (lzma_filter *)&filters);
99 
100         mz_stream_write_uint8(lzma->stream.base, LZMA_VERSION_MAJOR);
101         mz_stream_write_uint8(lzma->stream.base, LZMA_VERSION_MINOR);
102         mz_stream_write_uint16(lzma->stream.base, (uint16_t)size);
103 
104         lzma->total_out += MZ_LZMA_HEADER_SIZE;
105 
106         lzma->error = lzma_alone_encoder(&lzma->lstream, &opt_lzma);
107 #endif
108     }
109     else if (mode & MZ_OPEN_MODE_READ)
110     {
111 #ifdef MZ_ZIP_NO_DECOMPRESSION
112         MZ_UNUSED(filters);
113         MZ_UNUSED(major);
114         MZ_UNUSED(minor);
115         return MZ_SUPPORT_ERROR;
116 #else
117         lzma->lstream.next_in = lzma->buffer;
118         lzma->lstream.avail_in = 0;
119 
120         mz_stream_read_uint8(lzma->stream.base, &major);
121         mz_stream_read_uint8(lzma->stream.base, &minor);
122         mz_stream_read_uint16(lzma->stream.base, (uint16_t *)&size);
123 
124         lzma->total_in += MZ_LZMA_HEADER_SIZE;
125 
126         lzma->error = lzma_alone_decoder(&lzma->lstream, UINT64_MAX);
127 #endif
128     }
129 
130     if (lzma->error != LZMA_OK)
131         return MZ_OPEN_ERROR;
132 
133     lzma->initialized = 1;
134     lzma->mode = mode;
135     return MZ_OK;
136 }
137 
mz_stream_lzma_is_open(void * stream)138 int32_t mz_stream_lzma_is_open(void *stream)
139 {
140     mz_stream_lzma *lzma = (mz_stream_lzma *)stream;
141     if (lzma->initialized != 1)
142         return MZ_OPEN_ERROR;
143     return MZ_OK;
144 }
145 
mz_stream_lzma_read(void * stream,void * buf,int32_t size)146 int32_t mz_stream_lzma_read(void *stream, void *buf, int32_t size)
147 {
148 #ifdef MZ_ZIP_NO_DECOMPRESSION
149     MZ_UNUSED(stream);
150     MZ_UNUSED(buf);
151     MZ_UNUSED(size);
152     return MZ_SUPPORT_ERROR;
153 #else
154     mz_stream_lzma *lzma = (mz_stream_lzma *)stream;
155     uint64_t total_in_before = 0;
156     uint64_t total_out_before = 0;
157     uint64_t total_in_after = 0;
158     uint64_t total_out_after = 0;
159     int32_t total_in = 0;
160     int32_t total_out = 0;
161     int32_t in_bytes = 0;
162     int32_t out_bytes = 0;
163     int32_t bytes_to_read = 0;
164     int32_t read = 0;
165     int32_t err = LZMA_OK;
166 
167 
168     lzma->lstream.next_out = (uint8_t*)buf;
169     lzma->lstream.avail_out = (size_t)size;
170 
171     do
172     {
173         if (lzma->lstream.avail_in == 0)
174         {
175             bytes_to_read = sizeof(lzma->buffer);
176             if (lzma->max_total_in > 0)
177             {
178                 if ((int64_t)bytes_to_read > (lzma->max_total_in - lzma->total_in))
179                     bytes_to_read = (int32_t)(lzma->max_total_in - lzma->total_in);
180             }
181 
182             read = mz_stream_read(lzma->stream.base, lzma->buffer, bytes_to_read);
183 
184             if (read < 0)
185                 return read;
186             if (read == 0)
187                 break;
188 
189             lzma->lstream.next_in = lzma->buffer;
190             lzma->lstream.avail_in = (size_t)read;
191         }
192 
193         total_in_before = lzma->lstream.avail_in;
194         total_out_before = lzma->lstream.total_out;
195 
196         err = lzma_code(&lzma->lstream, LZMA_RUN);
197 
198         total_in_after = lzma->lstream.avail_in;
199         total_out_after = lzma->lstream.total_out;
200         if ((lzma->max_total_out != -1) && (int64_t)total_out_after > lzma->max_total_out)
201             total_out_after = (uint64_t)lzma->max_total_out;
202 
203         in_bytes = (int32_t)(total_in_before - total_in_after);
204         out_bytes = (int32_t)(total_out_after - total_out_before);
205 
206         total_in += in_bytes;
207         total_out += out_bytes;
208 
209         lzma->total_in += in_bytes;
210         lzma->total_out += out_bytes;
211 
212         if (err == LZMA_STREAM_END)
213             break;
214         if (err != LZMA_OK)
215         {
216             lzma->error = err;
217             break;
218         }
219     }
220     while (lzma->lstream.avail_out > 0);
221 
222     if (lzma->error != 0)
223         return MZ_DATA_ERROR;
224 
225     return total_out;
226 #endif
227 }
228 
229 #ifndef MZ_ZIP_NO_COMPRESSION
mz_stream_lzma_flush(void * stream)230 static int32_t mz_stream_lzma_flush(void *stream)
231 {
232     mz_stream_lzma *lzma = (mz_stream_lzma *)stream;
233     if (mz_stream_write(lzma->stream.base, lzma->buffer, lzma->buffer_len) != lzma->buffer_len)
234         return MZ_WRITE_ERROR;
235     return MZ_OK;
236 }
237 
mz_stream_lzma_code(void * stream,int32_t flush)238 static int32_t mz_stream_lzma_code(void *stream, int32_t flush)
239 {
240     mz_stream_lzma *lzma = (mz_stream_lzma *)stream;
241     uint64_t total_out_before = 0;
242     uint64_t total_out_after = 0;
243     uint32_t out_bytes = 0;
244     int32_t err = LZMA_OK;
245 
246 
247     do
248     {
249         if (lzma->lstream.avail_out == 0)
250         {
251             err = mz_stream_lzma_flush(lzma);
252             if (err != MZ_OK)
253                 return err;
254 
255             lzma->lstream.avail_out = sizeof(lzma->buffer);
256             lzma->lstream.next_out = lzma->buffer;
257 
258             lzma->buffer_len = 0;
259         }
260 
261         total_out_before = lzma->lstream.total_out;
262         err = lzma_code(&lzma->lstream, (lzma_action)flush);
263         total_out_after = lzma->lstream.total_out;
264 
265         out_bytes = (uint32_t)(total_out_after - total_out_before);
266 
267         if (err != LZMA_OK && err != LZMA_STREAM_END)
268         {
269             lzma->error = err;
270             return MZ_DATA_ERROR;
271         }
272 
273         lzma->buffer_len += out_bytes;
274         lzma->total_out += out_bytes;
275     }
276     while ((lzma->lstream.avail_in > 0) || (flush == LZMA_FINISH && err == LZMA_OK));
277 
278     return MZ_OK;
279 }
280 #endif
281 
mz_stream_lzma_write(void * stream,const void * buf,int32_t size)282 int32_t mz_stream_lzma_write(void *stream, const void *buf, int32_t size)
283 {
284     mz_stream_lzma *lzma = (mz_stream_lzma *)stream;
285     int32_t err = size;
286 
287 #ifdef MZ_ZIP_NO_COMPRESSION
288     MZ_UNUSED(lzma);
289     MZ_UNUSED(buf);
290     err = MZ_SUPPORT_ERROR;
291 #else
292     lzma->lstream.next_in = (uint8_t*)(intptr_t)buf;
293     lzma->lstream.avail_in = (size_t)size;
294 
295     mz_stream_lzma_code(stream, LZMA_RUN);
296 
297     lzma->total_in += size;
298 #endif
299     return err;
300 }
301 
mz_stream_lzma_tell(void * stream)302 int64_t mz_stream_lzma_tell(void *stream)
303 {
304     MZ_UNUSED(stream);
305 
306     return MZ_TELL_ERROR;
307 }
308 
mz_stream_lzma_seek(void * stream,int64_t offset,int32_t origin)309 int32_t mz_stream_lzma_seek(void *stream, int64_t offset, int32_t origin)
310 {
311     MZ_UNUSED(stream);
312     MZ_UNUSED(offset);
313     MZ_UNUSED(origin);
314 
315     return MZ_SEEK_ERROR;
316 }
317 
mz_stream_lzma_close(void * stream)318 int32_t mz_stream_lzma_close(void *stream)
319 {
320     mz_stream_lzma *lzma = (mz_stream_lzma *)stream;
321 
322     if (lzma->mode & MZ_OPEN_MODE_WRITE)
323     {
324 #ifdef MZ_ZIP_NO_COMPRESSION
325         return MZ_SUPPORT_ERROR;
326 #else
327         mz_stream_lzma_code(stream, LZMA_FINISH);
328         mz_stream_lzma_flush(stream);
329 
330         lzma_end(&lzma->lstream);
331 #endif
332     }
333     else if (lzma->mode & MZ_OPEN_MODE_READ)
334     {
335 #ifdef MZ_ZIP_NO_DECOMPRESSION
336         return MZ_SUPPORT_ERROR;
337 #else
338         lzma_end(&lzma->lstream);
339 #endif
340     }
341 
342     lzma->initialized = 0;
343 
344     if (lzma->error != LZMA_OK)
345         return MZ_CLOSE_ERROR;
346     return MZ_OK;
347 }
348 
mz_stream_lzma_error(void * stream)349 int32_t mz_stream_lzma_error(void *stream)
350 {
351     mz_stream_lzma *lzma = (mz_stream_lzma *)stream;
352     return lzma->error;
353 }
354 
mz_stream_lzma_get_prop_int64(void * stream,int32_t prop,int64_t * value)355 int32_t mz_stream_lzma_get_prop_int64(void *stream, int32_t prop, int64_t *value)
356 {
357     mz_stream_lzma *lzma = (mz_stream_lzma *)stream;
358     switch (prop)
359     {
360     case MZ_STREAM_PROP_TOTAL_IN:
361         *value = lzma->total_in;
362         break;
363     case MZ_STREAM_PROP_TOTAL_IN_MAX:
364         *value = lzma->max_total_in;
365         break;
366     case MZ_STREAM_PROP_TOTAL_OUT:
367         *value = lzma->total_out;
368         break;
369     case MZ_STREAM_PROP_TOTAL_OUT_MAX:
370         *value = lzma->max_total_out;
371         break;
372     case MZ_STREAM_PROP_HEADER_SIZE:
373         *value = MZ_LZMA_HEADER_SIZE;
374         break;
375     default:
376         return MZ_EXIST_ERROR;
377     }
378     return MZ_OK;
379 }
380 
mz_stream_lzma_set_prop_int64(void * stream,int32_t prop,int64_t value)381 int32_t mz_stream_lzma_set_prop_int64(void *stream, int32_t prop, int64_t value)
382 {
383     mz_stream_lzma *lzma = (mz_stream_lzma *)stream;
384     switch (prop)
385     {
386     case MZ_STREAM_PROP_COMPRESS_LEVEL:
387         if (value >= 9)
388             lzma->preset = LZMA_PRESET_EXTREME;
389         else
390             lzma->preset = LZMA_PRESET_DEFAULT;
391         break;
392     case MZ_STREAM_PROP_TOTAL_IN_MAX:
393         lzma->max_total_in = value;
394         break;
395     case MZ_STREAM_PROP_TOTAL_OUT_MAX:
396         if (value < -1)
397             return MZ_PARAM_ERROR;
398         lzma->max_total_out = value;
399         break;
400     default:
401         return MZ_EXIST_ERROR;
402     }
403     return MZ_OK;
404 }
405 
mz_stream_lzma_create(void ** stream)406 void *mz_stream_lzma_create(void **stream)
407 {
408     mz_stream_lzma *lzma = NULL;
409 
410     lzma = (mz_stream_lzma *)MZ_ALLOC(sizeof(mz_stream_lzma));
411     if (lzma != NULL)
412     {
413         memset(lzma, 0, sizeof(mz_stream_lzma));
414         lzma->stream.vtbl = &mz_stream_lzma_vtbl;
415         lzma->preset = LZMA_PRESET_DEFAULT;
416         lzma->max_total_out = -1;
417     }
418     if (stream != NULL)
419         *stream = lzma;
420 
421     return lzma;
422 }
423 
mz_stream_lzma_delete(void ** stream)424 void mz_stream_lzma_delete(void **stream)
425 {
426     mz_stream_lzma *lzma = NULL;
427     if (stream == NULL)
428         return;
429     lzma = (mz_stream_lzma *)*stream;
430     if (lzma != NULL)
431         MZ_FREE(lzma);
432     *stream = NULL;
433 }
434 
mz_stream_lzma_get_interface(void)435 void *mz_stream_lzma_get_interface(void)
436 {
437     return (void *)&mz_stream_lzma_vtbl;
438 }
439