diff --git a/Makefile.common b/Makefile.common index 873d6ca837..057af9259d 100644 --- a/Makefile.common +++ b/Makefile.common @@ -801,7 +801,8 @@ endif ifeq ($(HAVE_ZLIB), 1) - OBJ += libretro-common/file/file_extract.o + OBJ += libretro-common/file/file_extract.o \ + tasks/task_decompress.o OBJ += $(ZLIB_OBJS) DEFINES += -DHAVE_ZLIB HAVE_RPNG = 1 diff --git a/tasks/task_decompress.c b/tasks/task_decompress.c new file mode 100644 index 0000000000..2780d063c8 --- /dev/null +++ b/tasks/task_decompress.c @@ -0,0 +1,157 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2015 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#include +#include +#include + +#include "tasks.h" +#include "../verbosity.h" + +typedef struct { + char *source_file; + char *target_dir; + char *valid_ext; + + char *callback_error; + + zlib_transfer_t zlib; +} decompress_state_t; + +static int file_decompressed(const char *name, const char *valid_exts, + const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, + uint32_t crc32, void *userdata) +{ + char path[PATH_MAX_LENGTH]; + decompress_state_t *dec = (decompress_state_t*)userdata; + + /* Ignore directories. */ + if (name[strlen(name) - 1] == '/' || name[strlen(name) - 1] == '\\') + goto next_file; + + /* Make directory */ + fill_pathname_join(path, dec->target_dir, name, sizeof(path)); + path_basedir(path); + + if (!path_mkdir(path)) + goto error; + + fill_pathname_join(path, dec->target_dir, name, sizeof(path)); + + if (!zlib_perform_mode(path, valid_exts, + cdata, cmode, csize, size, crc32, userdata)) + goto error; + + RARCH_LOG("[deflate] Path: %s, CRC32: 0x%x\n", name, crc32); + +next_file: + return 1; + +error: + dec->callback_error = (char*)malloc(PATH_MAX_LENGTH); + snprintf(dec->callback_error, PATH_MAX_LENGTH, "Failed to deflate %s.\n", path); + + return 0; + +} + +static void rarch_task_decompress_handler(rarch_task_t *task) +{ + decompress_state_t *dec = (decompress_state_t*)task->state; + decompress_task_data_t *data = NULL; + bool failed; + + zlib_parse_file_iterate(&dec->zlib, &failed, dec->source_file, + dec->valid_ext, file_decompressed, dec); + + if (failed) + { + task->error = dec->callback_error; + goto task_finished; + } + + if (task->cancelled) + dec->zlib.type = ZLIB_TRANSFER_DEINIT; + + /* run again to free resources */ + if (dec->zlib.type == ZLIB_TRANSFER_DEINIT) + { + zlib_parse_file_iterate(&dec->zlib, &failed, dec->source_file, + dec->valid_ext, file_decompressed, dec); + goto task_finished; + } + + return; + +task_finished: + task->finished = true; + + if (task->cancelled) + task->error = strdup("Task canceled"); + + if (!task->error) + { + data = (decompress_task_data_t*)calloc(1, sizeof(*data)); + data->source_file = dec->source_file; + task->task_data = data; + } + else + free(dec->source_file); + + if (dec->valid_ext) + free(dec->valid_ext); + free(dec->target_dir); + free(dec); +} + +bool rarch_task_push_decompress(const char *source_file, const char *target_dir, + const char *valid_ext, rarch_task_callback_t cb, void *user_data) +{ + decompress_state_t *s; + rarch_task_t *t; + + if (!target_dir || !target_dir[0] || !source_file || !source_file[0]) + return false; + + /* zip only */ + if (!path_file_exists(source_file) || strcmp("zip", path_get_extension(source_file)) != 0) + return false; + + if (!valid_ext || !valid_ext[0]) + valid_ext = NULL; + + s = (decompress_state_t*)calloc(1, sizeof(*s)); + + s->source_file = strdup(source_file); + s->target_dir = strdup(target_dir); + + s->valid_ext = valid_ext ? strdup(valid_ext) : NULL; + s->zlib.type = ZLIB_TRANSFER_INIT; + + t = (rarch_task_t*)calloc(1, sizeof(*t)); + t->handler = rarch_task_decompress_handler; + t->state = s; + + t->callback = cb; + t->user_data = user_data; + + rarch_task_push(t); + + return true; +} diff --git a/tasks/tasks.h b/tasks/tasks.h index c95df314e1..0c31243c52 100644 --- a/tasks/tasks.h +++ b/tasks/tasks.h @@ -144,6 +144,13 @@ int detect_ps1_game(const char *track_path, char *game_id); int detect_psp_game(const char *track_path, char *game_id); +typedef struct { + char *source_file; +} decompress_task_data_t; + +bool rarch_task_push_decompress(const char *source_file, const char *target_dir, + const char *valid_ext, rarch_task_callback_t cb, void *user_data); + #ifdef __cplusplus } #endif