diff --git a/libretro-common/file/archive_file.c b/libretro-common/file/archive_file.c index fd176275cf..5f58ab8f13 100644 --- a/libretro-common/file/archive_file.c +++ b/libretro-common/file/archive_file.c @@ -32,6 +32,14 @@ #include #include +#ifdef HAVE_MMAP +#include +#include +#include +#include +#include +#endif + static int file_archive_get_file_list_cb( const char *path, const char *valid_exts, @@ -159,6 +167,25 @@ static int file_archive_parse_file_init(file_archive_transfer_t *state, state->archive_size = filestream_get_size(state->archive_file); +#ifdef HAVE_MMAP + if (state->archive_size <= (256*1024*1024)) + { + state->archive_mmap_fd = open(path, O_RDONLY); + if (state->archive_mmap_fd) + { + state->archive_mmap_data = (uint8_t*)mmap(NULL, (size_t)state->archive_size, + PROT_READ, MAP_SHARED, state->archive_mmap_fd, 0); + + if (state->archive_mmap_data == (uint8_t*)MAP_FAILED) + { + close(state->archive_mmap_fd); + state->archive_mmap_fd = 0; + state->archive_mmap_data = NULL; + } + } + } +#endif + state->step_current = 0; state->step_total = 0; @@ -273,6 +300,16 @@ int file_archive_parse_file_iterate( state->archive_file = NULL; } +#ifdef HAVE_MMAP + if (state->archive_mmap_data) + { + munmap(state->archive_mmap_data, (size_t)state->archive_size); + close(state->archive_mmap_fd); + state->archive_mmap_fd = 0; + state->archive_mmap_data = NULL; + } +#endif + if (userdata) userdata->transfer = NULL; break; @@ -302,15 +339,19 @@ static bool file_archive_walk(const char *file, const char *valid_exts, file_archive_file_cb file_cb, struct archive_extract_userdata *userdata) { file_archive_transfer_t state; - bool returnerr = true; + bool returnerr = true; - state.type = ARCHIVE_TRANSFER_INIT; - state.archive_file = NULL; - state.archive_size = 0; - state.context = NULL; - state.step_total = 0; - state.step_current = 0; - state.backend = NULL; + state.type = ARCHIVE_TRANSFER_INIT; + state.archive_file = NULL; +#ifdef HAVE_MMAP + state.archive_mmap_fd = 0; + state.archive_mmap_data = NULL; +#endif + state.archive_size = 0; + state.context = NULL; + state.step_total = 0; + state.step_current = 0; + state.backend = NULL; for (;;) { @@ -640,13 +681,17 @@ uint32_t file_archive_get_file_crc32(const char *path) archive_path += 1; } - state.type = ARCHIVE_TRANSFER_INIT; - state.archive_file = NULL; - state.archive_size = 0; - state.context = NULL; - state.step_total = 0; - state.step_current = 0; - state.backend = NULL; + state.type = ARCHIVE_TRANSFER_INIT; + state.archive_file = NULL; +#ifdef HAVE_MMAP + state.archive_mmap_fd = 0; + state.archive_mmap_data = NULL; +#endif + state.archive_size = 0; + state.context = NULL; + state.step_total = 0; + state.step_current = 0; + state.backend = NULL; /* Initialize and open archive first. Sets next state type to ITERATE. */ diff --git a/libretro-common/file/archive_file_zlib.c b/libretro-common/file/archive_file_zlib.c index 39103fd2dc..47ddbfb11e 100644 --- a/libretro-common/file/archive_file_zlib.c +++ b/libretro-common/file/archive_file_zlib.c @@ -49,7 +49,7 @@ enum file_archive_compression_mode typedef struct { - RFILE *file; + struct file_archive_transfer *state; uint8_t *directory; uint8_t *directory_entry; uint8_t *directory_end; @@ -80,8 +80,13 @@ static void zip_context_free_stream( } if (zip_context->compressed_data) { - free(zip_context->compressed_data); - zip_context->compressed_data = NULL; +#ifdef HAVE_MMAP + if (!zip_context->state->archive_mmap_data) +#endif + { + free(zip_context->compressed_data); + zip_context->compressed_data = NULL; + } } if (zip_context->decompressed_data && !keep_decompressed) { @@ -95,38 +100,57 @@ static bool zlib_stream_decompress_data_to_file_init( const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size) { zip_context_t *zip_context = (zip_context_t *)context; + struct file_archive_transfer *state = zip_context->state; uint8_t local_header_buf[4]; + uint8_t *local_header; uint32_t offsetNL, offsetEL; int64_t offsetData; - /* free previous stream if left unfinished */ + /* free previous data and stream if left unfinished */ zip_context_free_stream(zip_context, false); - /* allocate memory for the compressed data */ - zip_context->compressed_data = (uint8_t*)malloc(csize); - if (!zip_context->compressed_data) - goto error; - /* seek past most of the local directory header */ - filestream_seek(zip_context->file, (int64_t)(size_t)cdata + 26, RETRO_VFS_SEEK_POSITION_START); - if (filestream_read(zip_context->file, local_header_buf, 4) != 4) - goto error; +#ifdef HAVE_MMAP + if (state->archive_mmap_data) + { + local_header = state->archive_mmap_data + (size_t)cdata + 26; + } + else +#endif + { + filestream_seek(state->archive_file, (int64_t)(size_t)cdata + 26, RETRO_VFS_SEEK_POSITION_START); + if (filestream_read(state->archive_file, local_header_buf, 4) != 4) + goto error; + local_header = local_header_buf; + } - offsetNL = read_le(local_header_buf, 2); /* file name length */ - offsetEL = read_le(local_header_buf + 2, 2); /* extra field length */ + offsetNL = read_le(local_header, 2); /* file name length */ + offsetEL = read_le(local_header + 2, 2); /* extra field length */ offsetData = (int64_t)(size_t)cdata + 26 + 4 + offsetNL + offsetEL; - /* skip over name and extra data */ - filestream_seek(zip_context->file, offsetData, RETRO_VFS_SEEK_POSITION_START); - if (filestream_read(zip_context->file, zip_context->compressed_data, csize) != csize) - goto error; +#ifdef HAVE_MMAP + if (state->archive_mmap_data) + { + zip_context->compressed_data = state->archive_mmap_data + (size_t)offsetData; + } + else +#endif + { + /* allocate memory for the compressed data */ + zip_context->compressed_data = (uint8_t*)malloc(csize); + if (!zip_context->compressed_data) + goto error; + + /* skip over name and extra data */ + filestream_seek(state->archive_file, offsetData, RETRO_VFS_SEEK_POSITION_START); + if (filestream_read(state->archive_file, zip_context->compressed_data, csize) != csize) + goto error; + } switch (cmode) { case ZIP_MODE_STORED: - zip_context->decompressed_data = zip_context->compressed_data; - zip_context->compressed_data = NULL; - handle->data = zip_context->decompressed_data; + handle->data = zip_context->compressed_data; return true; case ZIP_MODE_DEFLATED: @@ -397,7 +421,7 @@ static int zip_parse_file_init(file_archive_transfer_t *state, * context and the entire directory, then read the directory. */ zip_context = (zip_context_t*)malloc(sizeof(zip_context_t) + (size_t)directory_size); - zip_context->file = state->archive_file; + zip_context->state = state; zip_context->directory = (uint8_t*)(zip_context + 1); zip_context->directory_entry = zip_context->directory; zip_context->directory_end = zip_context->directory + (size_t)directory_size; diff --git a/libretro-common/include/file/archive_file.h b/libretro-common/include/file/archive_file.h index 849ac2c90f..8d8cf008fa 100644 --- a/libretro-common/include/file/archive_file.h +++ b/libretro-common/include/file/archive_file.h @@ -58,6 +58,10 @@ typedef struct file_archive_transfer { enum file_archive_transfer_type type; struct RFILE *archive_file; +#ifdef HAVE_MMAP + int archive_mmap_fd; + uint8_t *archive_mmap_data; +#endif int64_t archive_size; void *context; unsigned step_total, step_current;