From 3d212c1d7c882b9d55f68b9091a4cb9236b461fa Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 5 Oct 2013 18:07:38 +0200 Subject: [PATCH] Start refactoring file_extract. Add support for low-level parsing of zip contents. --- file.h | 2 + file_extract.c | 165 +++++++++++++++++++++++++++++++++++-------------- file_extract.h | 12 ++++ file_path.c | 4 +- 4 files changed, 135 insertions(+), 48 deletions(-) diff --git a/file.h b/file.h index 24e1cd9519..880734f715 100644 --- a/file.h +++ b/file.h @@ -68,6 +68,8 @@ void dir_list_free(struct string_list *list); bool string_list_find_elem(const struct string_list *list, const char *elem); bool string_list_find_elem_prefix(const struct string_list *list, const char *prefix, const char *elem); struct string_list *string_split(const char *str, const char *delim); +struct string_list *string_list_new(void); +bool string_list_append(struct string_list *list, const char *elem, union string_list_elem_attr attr); void string_list_free(struct string_list *list); bool path_is_directory(const char *path); diff --git a/file_extract.c b/file_extract.c index 74a9b5768d..3bb8dfbd47 100644 --- a/file_extract.c +++ b/file_extract.c @@ -18,6 +18,7 @@ #include "compat/strl.h" #include #include +#include #ifdef WANT_MINIZ #include "deps/miniz/zlib.h" @@ -46,7 +47,7 @@ static uint32_t read_le(const uint8_t *data, unsigned size) return val; } -static bool inflate_data_to_file(const char *path, uint8_t *cdata, +static bool inflate_data_to_file(const char *path, const uint8_t *cdata, uint32_t csize, uint32_t size, uint32_t crc32) { bool ret = true; @@ -60,7 +61,7 @@ static bool inflate_data_to_file(const char *path, uint8_t *cdata, if (inflateInit2(&stream, -MAX_WBITS) != Z_OK) GOTO_END_ERROR(); - stream.next_in = cdata; + stream.next_in = (uint8_t*)cdata; stream.avail_in = csize; stream.next_out = out_data; stream.avail_out = size; @@ -85,24 +86,14 @@ end: return ret; } -bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *valid_exts) +bool zlib_parse_file(const char *file, zlib_file_cb file_cb, void *userdata) { const uint8_t *footer = NULL; const uint8_t *directory = NULL; bool ret = true; - if (!valid_exts) - { - RARCH_ERR("Libretro implementation does not have any valid extensions. Cannot unzip without knowing this.\n"); - return false; - } - - struct string_list *list = string_split(valid_exts, "|"); - if (!list) - return false; - uint8_t *data = NULL; - ssize_t zip_size = read_file(zip_path, (void**)&data); + ssize_t zip_size = read_file(file, (void**)&data); if (zip_size < 22) GOTO_END_ERROR(); @@ -150,44 +141,126 @@ bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *va RARCH_LOG("OFFSET: %u, CSIZE: %u, SIZE: %u.\n", offset + 30 + offsetNL + offsetEL, csize, size); - // Extract first ROM that matches our list. - const char *ext = path_get_extension(filename); - if (ext && string_list_find_elem(list, ext)) - { - char new_path[PATH_MAX]; - fill_pathname_resolve_relative(new_path, zip_path, - path_basename(filename), sizeof(new_path)); - - switch (cmode) - { - case 0: // Uncompressed - if (!write_file(new_path, cdata, size)) - GOTO_END_ERROR(); - goto end; - - case 8: // Deflate - if (inflate_data_to_file(new_path, cdata, csize, size, crc32)) - { - strlcpy(zip_path, new_path, zip_path_size); - goto end; - } - else - GOTO_END_ERROR(); - - default: - GOTO_END_ERROR(); - } - } + if (!file_cb(filename, cdata, cmode, csize, size, crc32, userdata)) + break; directory += 46 + namelength + extralength + commentlength; } - RARCH_ERR("Didn't find any ROMS that matched valid extensions for libretro implementation.\n"); - GOTO_END_ERROR(); - end: free(data); - string_list_free(list); return ret; } +struct zip_extract_userdata +{ + char *zip_path; + size_t zip_path_size; + struct string_list *ext; + bool found_rom; +}; + +static bool zip_extract_cb(const char *name, const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, + uint32_t crc32, void *userdata) +{ + struct zip_extract_userdata *data = (struct zip_extract_userdata*)userdata; + + // Extract first ROM that matches our list. + const char *ext = path_get_extension(name); + if (ext && string_list_find_elem(data->ext, ext)) + { + char new_path[PATH_MAX]; + fill_pathname_resolve_relative(new_path, data->zip_path, + path_basename(name), sizeof(new_path)); + + switch (cmode) + { + case 0: // Uncompressed + data->found_rom = write_file(new_path, cdata, size); + return false; + + case 8: // Deflate + if (inflate_data_to_file(new_path, cdata, csize, size, crc32)) + { + strlcpy(data->zip_path, new_path, data->zip_path_size); + data->found_rom = true; + return false; + } + else + return false; + + default: + return false; + } + } + + return true; +} + +bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *valid_exts) +{ + if (!valid_exts) + { + RARCH_ERR("Libretro implementation does not have any valid extensions. Cannot unzip without knowing this.\n"); + return false; + } + + bool ret = true; + struct string_list *list = string_split(valid_exts, "|"); + if (!list) + GOTO_END_ERROR(); + + struct zip_extract_userdata userdata = {0}; + userdata.zip_path = zip_path; + userdata.zip_path_size = zip_path_size; + userdata.ext = list; + + if (!zlib_parse_file(zip_path, zip_extract_cb, &userdata)) + { + RARCH_ERR("Parsing ZIP failed.\n"); + GOTO_END_ERROR(); + } + + if (!userdata.found_rom) + { + RARCH_ERR("Didn't find any ROMS that matched valid extensions for libretro implementation.\n"); + GOTO_END_ERROR(); + } + +end: + if (list) + string_list_free(list); + return ret; +} + +static bool zlib_get_file_list_cb(const char *path, const uint8_t *cdata, unsigned cmode, + uint32_t csize, uint32_t size, + uint32_t crc32, void *userdata) +{ + (void)cdata; + (void)cmode; + (void)csize; + (void)size; + (void)crc32; + struct string_list *list = (struct string_list*)userdata; + union string_list_elem_attr attr; + memset(&attr, 0, sizeof(attr)); + return string_list_append(list, path, attr); +} + +struct string_list *zlib_get_file_list(const char *path) +{ + struct string_list *list = string_list_new(); + if (!list) + return NULL; + + if (!zlib_parse_file(path, zlib_get_file_list_cb, list)) + { + RARCH_ERR("Parsing ZIP failed.\n"); + string_list_free(list); + return NULL; + } + else + return list; +} + diff --git a/file_extract.h b/file_extract.h index 8c07e14604..460d23a14d 100644 --- a/file_extract.h +++ b/file_extract.h @@ -17,9 +17,21 @@ #define FILE_EXTRACT_H__ #include "boolean.h" +#include "file.h" #include +#include +// Returns true when parsing should continue. False to stop. +typedef bool (*zlib_file_cb)(const char *name, + const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, + uint32_t crc32, void *userdata); + +// Low-level file parsing. Enumerates over all files and calls file_cb with userdata. +bool zlib_parse_file(const char *file, zlib_file_cb file_cb, void *userdata); + +// Built with zlib_parse_file. bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *valid_exts); +struct string_list *zlib_get_file_list(const char *path); #endif diff --git a/file_path.c b/file_path.c index 35c1e5d142..c782585970 100644 --- a/file_path.c +++ b/file_path.c @@ -79,7 +79,7 @@ static bool string_list_capacity(struct string_list *list, size_t cap) return true; } -static struct string_list *string_list_new(void) +struct string_list *string_list_new(void) { struct string_list *list = (struct string_list*)calloc(1, sizeof(*list)); if (!list) @@ -94,7 +94,7 @@ static struct string_list *string_list_new(void) return list; } -static bool string_list_append(struct string_list *list, const char *elem, union string_list_elem_attr attr) +bool string_list_append(struct string_list *list, const char *elem, union string_list_elem_attr attr) { if (list->size >= list->cap && !string_list_capacity(list, list->cap * 2))