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