1 /* Copyright (C) 2010-2020 The RetroArch team
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (archive_file_zlib.c).
5 * ---------------------------------------------------------------------------------------
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 #include <file/archive_file.h>
27 #include <streams/file_stream.h>
28 #include <retro_inline.h>
29 #include <retro_miscellaneous.h>
30 #include <encodings/crc32.h>
34 #ifndef CENTRAL_FILE_HEADER_SIGNATURE
35 #define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50
38 #ifndef END_OF_CENTRAL_DIR_SIGNATURE
39 #define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50
42 #define _READ_CHUNK_SIZE (128*1024) /* Read 128KiB compressed chunks */
44 enum file_archive_compression_mode
52 struct file_archive_transfer *state;
54 uint8_t *directory_entry;
55 uint8_t *directory_end;
57 uint32_t boffset, csize, usize;
61 uint8_t *decompressed_data;
64 static INLINE uint32_t read_le(const uint8_t *data, unsigned size)
70 for (i = 0; i < size; i += 8)
71 val |= (uint32_t)*data++ << i;
76 static void zip_context_free_stream(
77 zip_context_t *zip_context, bool keep_decompressed)
79 if (zip_context->zstream)
81 inflateEnd(zip_context->zstream);
82 free(zip_context->zstream);
83 zip_context->fdoffset = 0;
84 zip_context->csize = 0;
85 zip_context->usize = 0;
86 zip_context->zstream = NULL;
88 if (zip_context->tmpbuf)
90 free(zip_context->tmpbuf);
91 zip_context->tmpbuf = NULL;
93 if (zip_context->decompressed_data && !keep_decompressed)
95 free(zip_context->decompressed_data);
96 zip_context->decompressed_data = NULL;
100 static bool zlib_stream_decompress_data_to_file_init(
101 void *context, file_archive_file_handle_t *handle,
102 const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size)
104 zip_context_t *zip_context = (zip_context_t *)context;
105 struct file_archive_transfer *state = zip_context->state;
106 uint8_t local_header_buf[4];
107 uint8_t *local_header;
108 uint32_t offsetNL, offsetEL;
111 /* free previous data and stream if left unfinished */
112 zip_context_free_stream(zip_context, false);
114 /* seek past most of the local directory header */
116 if (state->archive_mmap_data)
118 local_header = state->archive_mmap_data + (size_t)cdata + 26;
123 filestream_seek(state->archive_file, (int64_t)(size_t)cdata + 26, RETRO_VFS_SEEK_POSITION_START);
124 if (filestream_read(state->archive_file, local_header_buf, 4) != 4)
126 local_header = local_header_buf;
129 offsetNL = read_le(local_header, 2); /* file name length */
130 offsetEL = read_le(local_header + 2, 2); /* extra field length */
131 offsetData = (int64_t)(size_t)cdata + 26 + 4 + offsetNL + offsetEL;
133 zip_context->fdoffset = offsetData;
134 zip_context->usize = size;
135 zip_context->csize = csize;
136 zip_context->boffset = 0;
137 zip_context->cmode = cmode;
138 zip_context->decompressed_data = (uint8_t*)malloc(size);
139 zip_context->zstream = NULL;
140 zip_context->tmpbuf = NULL;
142 if (cmode == ZIP_MODE_DEFLATED)
144 /* Initialize the zlib inflate machinery */
145 zip_context->zstream = (z_stream*)malloc(sizeof(z_stream));
146 zip_context->tmpbuf = malloc(_READ_CHUNK_SIZE);
148 zip_context->zstream->next_in = NULL;
149 zip_context->zstream->avail_in = 0;
150 zip_context->zstream->total_in = 0;
151 zip_context->zstream->next_out = zip_context->decompressed_data;
152 zip_context->zstream->avail_out = size;
153 zip_context->zstream->total_out = 0;
155 zip_context->zstream->zalloc = NULL;
156 zip_context->zstream->zfree = NULL;
157 zip_context->zstream->opaque = NULL;
159 if (inflateInit2(zip_context->zstream, -MAX_WBITS) != Z_OK) {
160 free(zip_context->zstream);
161 zip_context->zstream = NULL;
169 zip_context_free_stream(zip_context, false);
173 static int zlib_stream_decompress_data_to_file_iterate(
174 void *context, file_archive_file_handle_t *handle)
176 zip_context_t *zip_context = (zip_context_t *)context;
177 struct file_archive_transfer *state = zip_context->state;
180 if (zip_context->cmode == ZIP_MODE_STORED)
183 if (zip_context->state->archive_mmap_data)
185 /* Simply copy the data to the output buffer */
186 memcpy(zip_context->decompressed_data,
187 zip_context->state->archive_mmap_data + (size_t)zip_context->fdoffset,
193 /* Read the entire file to memory */
194 filestream_seek(state->archive_file, zip_context->fdoffset, RETRO_VFS_SEEK_POSITION_START);
195 if (filestream_read(state->archive_file,
196 zip_context->decompressed_data,
197 zip_context->usize) < 0)
201 handle->data = zip_context->decompressed_data;
204 else if (zip_context->cmode == ZIP_MODE_DEFLATED)
206 int to_read = MIN(zip_context->csize - zip_context->boffset, _READ_CHUNK_SIZE);
208 if (!zip_context->zstream)
210 /* file was uncompressed or decompression finished before */
215 if (state->archive_mmap_data)
217 /* Decompress from the mapped file */
218 dptr = state->archive_mmap_data + (size_t)zip_context->fdoffset + zip_context->boffset;
224 /* Read some compressed data from file to the temp buffer */
225 filestream_seek(state->archive_file, zip_context->fdoffset + zip_context->boffset,
226 RETRO_VFS_SEEK_POSITION_START);
227 rd = filestream_read(state->archive_file, zip_context->tmpbuf, to_read);
230 dptr = zip_context->tmpbuf;
233 zip_context->boffset += rd;
234 zip_context->zstream->next_in = dptr;
235 zip_context->zstream->avail_in = (uInt)rd;
237 if (inflate(zip_context->zstream, 0) < 0)
240 if (zip_context->boffset >= zip_context->csize)
242 inflateEnd(zip_context->zstream);
243 free(zip_context->zstream);
244 zip_context->zstream = NULL;
246 handle->data = zip_context->decompressed_data;
250 return 0; /* still more data to process */
253 /* No idea what kind of compression this is */
257 static uint32_t zlib_stream_crc32_calculate(uint32_t crc,
258 const uint8_t *data, size_t length)
260 return encoding_crc32(crc, data, length);
263 static bool zip_file_decompressed_handle(
264 file_archive_transfer_t *transfer,
265 file_archive_file_handle_t* handle,
266 const uint8_t *cdata, unsigned cmode, uint32_t csize,
267 uint32_t size, uint32_t crc32)
271 transfer->backend = &zlib_backend;
273 if (!transfer->backend->stream_decompress_data_to_file_init(
274 transfer->context, handle, cdata, cmode, csize, size))
279 ret = transfer->backend->stream_decompress_data_to_file_iterate(
280 transfer->context, handle);
297 /* Extract the relative path (needle) from a
298 * ZIP archive (path) and allocate a buffer for it to write it in.
300 * optional_outfile if not NULL will be used to extract the file to.
301 * buf will be 0 then.
304 static int zip_file_decompressed(
305 const char *name, const char *valid_exts,
306 const uint8_t *cdata, unsigned cmode,
307 uint32_t csize, uint32_t size,
308 uint32_t crc32, struct archive_extract_userdata *userdata)
310 decomp_state_t* decomp_state = (decomp_state_t*)userdata->cb_data;
311 char last_char = name[strlen(name) - 1];
312 /* Ignore directories. */
313 if (last_char == '/' || last_char == '\\')
316 if (strstr(name, decomp_state->needle))
318 file_archive_file_handle_t handle = {0};
320 if (zip_file_decompressed_handle(userdata->transfer,
321 &handle, cdata, cmode, csize, size, crc32))
323 if (decomp_state->opt_file != 0)
325 /* Called in case core has need_fullpath enabled. */
326 bool success = filestream_write_file(decomp_state->opt_file, handle.data, size);
328 /* Note: Do not free handle.data here - this
329 * will be done when stream is deinitialised */
332 decomp_state->size = 0;
339 /* Called in case core has need_fullpath disabled.
340 * Will move decompressed content directly into
341 * RetroArch's ROM buffer. */
342 zip_context_t *zip_context = (zip_context_t *)userdata->transfer->context;
344 decomp_state->size = 0;
345 *decomp_state->buf = handle.data;
346 decomp_state->size = size;
347 /* We keep the data, prevent its deallocation during free */
348 zip_context->decompressed_data = NULL;
353 decomp_state->found = true;
359 static int64_t zip_file_read(
361 const char *needle, void **buf,
362 const char *optional_outfile)
364 file_archive_transfer_t state = {0};
365 decomp_state_t decomp = {0};
366 struct archive_extract_userdata userdata = {0};
367 bool returnerr = true;
371 decomp.needle = strdup(needle);
372 if (optional_outfile)
373 decomp.opt_file = strdup(optional_outfile);
375 state.type = ARCHIVE_TRANSFER_INIT;
376 userdata.transfer = &state;
377 userdata.cb_data = &decomp;
382 ret = file_archive_parse_file_iterate(&state, &returnerr, path,
383 "", zip_file_decompressed, &userdata);
386 }while (ret == 0 && !decomp.found);
388 file_archive_parse_file_iterate_stop(&state);
391 free(decomp.opt_file);
398 return (int64_t)decomp.size;
401 static int zip_parse_file_init(file_archive_transfer_t *state,
404 uint8_t footer_buf[1024];
405 uint8_t *footer = footer_buf;
406 int64_t read_pos = state->archive_size;
407 int64_t read_block = MIN(read_pos, (ssize_t)sizeof(footer_buf));
408 int64_t directory_size, directory_offset;
409 zip_context_t *zip_context = NULL;
411 /* Minimal ZIP file size is 22 bytes */
415 /* Find the end of central directory record by scanning
416 * the file from the end towards the beginning.
420 if (--footer < footer_buf)
423 return -1; /* reached beginning of file */
425 /* Read 21 bytes of overlaps except on the first block. */
426 if (read_pos == state->archive_size)
427 read_pos = read_pos - read_block;
429 read_pos = MAX(read_pos - read_block + 21, 0);
431 /* Seek to read_pos and read read_block bytes. */
432 filestream_seek(state->archive_file, read_pos, RETRO_VFS_SEEK_POSITION_START);
433 if (filestream_read(state->archive_file, footer_buf, read_block) != read_block)
436 footer = footer_buf + read_block - 22;
438 if (read_le(footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE)
440 unsigned comment_len = read_le(footer + 20, 2);
441 if (read_pos + (footer - footer_buf) + 22 + comment_len == state->archive_size)
442 break; /* found it! */
446 /* Read directory info and do basic sanity checks. */
447 directory_size = read_le(footer + 12, 4);
448 directory_offset = read_le(footer + 16, 4);
449 if (directory_size > state->archive_size
450 || directory_offset > state->archive_size)
453 /* This is a ZIP file, allocate one block of memory for both the
454 * context and the entire directory, then read the directory.
456 zip_context = (zip_context_t*)malloc(sizeof(zip_context_t) + (size_t)directory_size);
457 zip_context->state = state;
458 zip_context->directory = (uint8_t*)(zip_context + 1);
459 zip_context->directory_entry = zip_context->directory;
460 zip_context->directory_end = zip_context->directory + (size_t)directory_size;
461 zip_context->zstream = NULL;
462 zip_context->tmpbuf = NULL;
463 zip_context->decompressed_data = NULL;
465 filestream_seek(state->archive_file, directory_offset, RETRO_VFS_SEEK_POSITION_START);
466 if (filestream_read(state->archive_file, zip_context->directory, directory_size) != directory_size)
472 state->context = zip_context;
473 state->step_total = read_le(footer + 10, 2); /* total entries */;
478 static int zip_parse_file_iterate_step_internal(
479 zip_context_t * zip_context, char *filename,
480 const uint8_t **cdata,
481 unsigned *cmode, uint32_t *size, uint32_t *csize,
482 uint32_t *checksum, unsigned *payback)
484 uint8_t *entry = zip_context->directory_entry;
485 uint32_t signature, namelength, extralength, commentlength, offset;
487 if (entry < zip_context->directory || entry >= zip_context->directory_end)
490 signature = read_le(zip_context->directory_entry + 0, 4);
492 if (signature != CENTRAL_FILE_HEADER_SIGNATURE)
495 *cmode = read_le(zip_context->directory_entry + 10, 2); /* compression mode, 0 = store, 8 = deflate */
496 *checksum = read_le(zip_context->directory_entry + 16, 4); /* CRC32 */
497 *csize = read_le(zip_context->directory_entry + 20, 4); /* compressed size */
498 *size = read_le(zip_context->directory_entry + 24, 4); /* uncompressed size */
500 namelength = read_le(zip_context->directory_entry + 28, 2); /* file name length */
501 extralength = read_le(zip_context->directory_entry + 30, 2); /* extra field length */
502 commentlength = read_le(zip_context->directory_entry + 32, 2); /* file comment length */
504 if (namelength >= PATH_MAX_LENGTH)
507 memcpy(filename, zip_context->directory_entry + 46, namelength); /* file name */
508 filename[namelength] = '\0';
510 offset = read_le(zip_context->directory_entry + 42, 4); /* relative offset of local file header */
512 *cdata = (uint8_t*)(size_t)offset; /* store file offset in data pointer */
514 *payback = 46 + namelength + extralength + commentlength;
519 static int zip_parse_file_iterate_step(void *context,
520 const char *valid_exts, struct archive_extract_userdata *userdata,
521 file_archive_file_cb file_cb)
523 zip_context_t *zip_context = (zip_context_t *)context;
524 const uint8_t *cdata = NULL;
525 uint32_t checksum = 0;
529 unsigned payload = 0;
530 int ret = zip_parse_file_iterate_step_internal(zip_context,
531 userdata->current_file_path, &cdata, &cmode, &size, &csize, &checksum, &payload);
536 userdata->crc = checksum;
538 if (file_cb && !file_cb(userdata->current_file_path, valid_exts, cdata, cmode,
539 csize, size, checksum, userdata))
542 zip_context->directory_entry += payload;
547 static void zip_parse_file_free(void *context)
549 zip_context_t *zip_context = (zip_context_t *)context;
550 zip_context_free_stream(zip_context, false);
554 const struct file_archive_file_backend zlib_backend = {
556 zip_parse_file_iterate_step,
558 zlib_stream_decompress_data_to_file_init,
559 zlib_stream_decompress_data_to_file_iterate,
560 zlib_stream_crc32_calculate,