diff --git a/apple/iOS/browser.m b/apple/iOS/browser.m index 31dc193fa6..94c3034e61 100644 --- a/apple/iOS/browser.m +++ b/apple/iOS/browser.m @@ -115,7 +115,7 @@ static void file_action(enum file_action action, NSString* source, NSString* tar { RADirectoryItem* item = [RADirectoryItem new]; item.path = BOXSTRING(element->data); - item.isDirectory = element->attr.b; + item.isDirectory = (element->attr.i == RARCH_DIRECTORY); return item; } @@ -256,7 +256,7 @@ static void file_action(enum file_action action, NSString* source, NSString* tar const char* basename = path_basename(contents->elems[i].data); uint32_t section = isalpha(basename[0]) ? (toupper(basename[0]) - 'A') + 2 : 1; - section = contents->elems[i].attr.b ? 0 : section; + section = (contents->elems[i].attr.i == RARCH_DIRECTORY) ? 0 : section; [self.sections[section] addObject:[RADirectoryItem directoryItemFromElement:&contents->elems[i]]]; } @@ -432,7 +432,7 @@ static void file_action(enum file_action action, NSString* source, NSString* tar for (i = 0; i < contents->size; i ++) { - if (contents->elems[i].attr.b) + if (contents->elems[i].attr.i == RARCH_DIRECTORY) { const char* basename = path_basename(contents->elems[i].data); diff --git a/decompress/7zip_support.c b/decompress/7zip_support.c index cdec20c973..4b223d8daa 100644 --- a/decompress/7zip_support.c +++ b/decompress/7zip_support.c @@ -21,6 +21,7 @@ #include #include "../miscellaneous.h" +#include "../file_path.h" #include "../deps/7zip/7z.h" #include "../deps/7zip/7zAlloc.h" @@ -28,6 +29,8 @@ #include "../deps/7zip/7zFile.h" #include "../deps/7zip/7zVersion.h" + + static ISzAlloc g_Alloc = { SzAlloc, SzFree }; static int Buf_EnsureSize(CBuf *dest, size_t size) @@ -225,7 +228,6 @@ int read_7zip_file(const char * archive_path, const char *relative_path, void ** res = ConvertUtf16toCharString(temp,infile); UInt64 filesize = f->Size; - (void)filesize; if (strcmp(infile,relative_path) == 0) { @@ -267,3 +269,149 @@ int read_7zip_file(const char * archive_path, const char *relative_path, void ** RARCH_ERR("\nUnspecified error in 7-ZIP archive, error number was: #%d\n", res); return -1; } + +struct string_list *compressed_7zip_file_list_new(const char *path, + const char* ext) +{ + + struct string_list *ext_list = NULL; + struct string_list *list = (struct string_list*)string_list_new(); + if (!list) + { + RARCH_ERR("Could not allocate list memory in compressed_7zip_file_list_new\n."); + return NULL; + } + + if (ext) + ext_list = string_split(ext, "|"); + + /* 7Zip part begin */ + CFileInStream archiveStream; + CLookToRead lookStream; + CSzArEx db; + SRes res; + ISzAlloc allocImp; + ISzAlloc allocTempImp; + UInt16 *temp = NULL; + size_t tempSize = 0; + long outsize = -1; + + //These are the allocation routines - currently using the non-standard 7zip choices. + allocImp.Alloc = SzAlloc; + allocImp.Free = SzFree; + allocTempImp.Alloc = SzAllocTemp; + allocTempImp.Free = SzFreeTemp; + + if (InFile_Open(&archiveStream.file, path)) + { + RARCH_ERR("Could not open %s as 7z archive\n.",path); + goto error; + } + FileInStream_CreateVTable(&archiveStream); + LookToRead_CreateVTable(&lookStream, False); + lookStream.realStream = &archiveStream.s; + LookToRead_Init(&lookStream); + CrcGenerateTable(); + SzArEx_Init(&db); + res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp); + if (res == SZ_OK) + { + UInt32 i; + UInt32 blockIndex = 0xFFFFFFFF; + Byte *outBuffer = 0; + size_t outBufferSize = 0; + for (i = 0; i < db.db.NumFiles; i++) + { + size_t offset = 0; + size_t outSizeProcessed = 0; + const CSzFileItem *f = db.db.Files + i; + size_t len; + if (f->IsDir) + { + /* we skip over everything, which is a directory. */ + continue; + } + len = SzArEx_GetFileNameUtf16(&db, i, NULL); + if (len > tempSize) + { + SzFree(NULL, temp); + tempSize = len; + temp = (UInt16 *) SzAlloc(NULL, tempSize * sizeof(temp[0])); + if (temp == 0) + { + res = SZ_ERROR_MEM; + break; + } + } + SzArEx_GetFileNameUtf16(&db, i, temp); + char infile[PATH_MAX]; + res = ConvertUtf16toCharString(temp, infile); + + const char *file_ext = path_get_extension(infile); + bool supported_by_core = false; + + union string_list_elem_attr attr; + + if (string_list_find_elem_prefix(ext_list, ".", file_ext)) + supported_by_core = true; + + /* + * Currently we only support files without subdirs in the archives. + * Folders are not supported (differences between win and lin. + * Archives within archives should imho never be supported. + */ + + if (!supported_by_core) + continue; + + attr.i = RARCH_COMPRESSED_FILE_IN_ARCHIVE; + + if (!string_list_append(list, infile, attr)) + goto error; + + } + } + SzArEx_Free(&db, &allocImp); + SzFree(NULL, temp); + File_Close(&archiveStream.file); + + if (res != SZ_OK) + { + //Error handling: + if (res == SZ_ERROR_UNSUPPORTED) + { + RARCH_ERR("7Zip decoder doesn't support this archive\n"); + goto error; + } + else if (res == SZ_ERROR_MEM) + { + RARCH_ERR("7Zip decoder could not allocate memory\n"); + goto error; + } + else if (res == SZ_ERROR_CRC) + { + RARCH_ERR("7Zip decoder encountered a CRC error in the archive\n"); + goto error; + } + else + { + RARCH_ERR( + "\nUnspecified error in 7-ZIP archive, error number was: #%d\n", + res); + goto error; + } + } + + string_list_free(ext_list); + return list; + +error: + RARCH_ERR("Failed to open compressed_file: \"%s\"\n", path); + SzArEx_Free(&db, &allocImp); + SzFree(NULL, temp); + File_Close(&archiveStream.file); + string_list_free(list); + string_list_free(ext_list); + return NULL; + +} diff --git a/decompress/7zip_support.h b/decompress/7zip_support.h index d08d5031cc..2c425b21ea 100644 --- a/decompress/7zip_support.h +++ b/decompress/7zip_support.h @@ -23,6 +23,9 @@ extern "C" { int read_7zip_file(const char * archive_path, const char *relative_path, void **buf); +struct string_list *compressed_7zip_file_list_new(const char *path, + const char* ext); + #ifdef __cplusplus } #endif diff --git a/file.c b/file.c index 325fb0498d..ec997a2e32 100644 --- a/file.c +++ b/file.c @@ -151,16 +151,29 @@ static ssize_t read_content_file(const char *path, void **buf) if (g_extern.is_carchive) { - if(archive_found) - { - /* FIXME - should use fill_pathname_relative helper function here - * to avoid errors. */ - char rel_path[PATH_MAX]; - snprintf(rel_path, sizeof(rel_path), - "%s", archive_found + strlen(g_extern.carchive_path) + 1); - ret = read_compressed_file(g_extern.carchive_path, - rel_path, (void**)&ret_buf); - } + if(archive_found) + { + if (strlen(path) < strlen(g_extern.carchive_path)+2) + { + /* + * This error condition happens for example, when + * carchive_path == path, or + * carchive_path + '/' == path. + */ + RARCH_ERR("Could not extract image path %s from carchive path %s.\n", + path, g_extern.carchive_path); + return -1; + } + ret = read_compressed_file(g_extern.carchive_path, + archive_found + strlen(g_extern.carchive_path) + 1, (void**)&ret_buf); + } + else + { + /* If we didn't actually find the archivename in the filename + * the given path is not inside the archive. Then we proceed to just load the file. + */ + ret = read_file(path, (void**)&ret_buf); + } } else #endif diff --git a/file_path.c b/file_path.c index 8fe81e687f..17f4740ad9 100644 --- a/file_path.c +++ b/file_path.c @@ -357,12 +357,13 @@ static int qstrcmp_dir(const void *a_, const void *b_) { const struct string_list_elem *a = (const struct string_list_elem*)a_; const struct string_list_elem *b = (const struct string_list_elem*)b_; - int a_dir = a->attr.b; - int b_dir = b->attr.b; + int a_type = a->attr.i; + int b_type = b->attr.i; + /* Sort directories before files. */ - if (a_dir != b_dir) - return b_dir - a_dir; + if (a_type != b_type) + return b_type - a_type; return strcasecmp(a->data, b->data); } @@ -375,6 +376,22 @@ void dir_list_sort(struct string_list *list, bool dir_first) dir_first ? qstrcmp_dir : qstrcmp_plain); } +struct string_list *compressed_file_list_new(const char *path, + const char* ext) +{ +#ifdef HAVE_COMPRESSION + const char* file_ext = path_get_extension(path); +#ifdef HAVE_7ZIP + if (strcmp(file_ext,"7z") == 0) + { + return compressed_7zip_file_list_new(path,ext); + } +#endif + +#endif + return NULL; +} + #ifdef _WIN32 struct string_list *dir_list_new(const char *dir, @@ -402,9 +419,21 @@ struct string_list *dir_list_new(const char *dir, { union string_list_elem_attr attr; char file_path[PATH_MAX]; - const char *name = ffd.cFileName; - const char *file_ext = path_get_extension(name); - bool is_dir = ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + const char *name = ffd.cFileName; + const char *file_ext = path_get_extension(name); + bool is_dir = ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + bool is_compressed_file = false; + bool supported_by_core = false; + attr.i = RARCH_FILETYPE_UNSET; + + fill_pathname_join(file_path, dir, name, sizeof(file_path)); + + if (!is_dir) + { + is_compressed_file = path_is_compressed_file(file_path); + if (string_list_find_elem_prefix(ext_list, ".", file_ext)) + supported_by_core = true; + } if (!include_dirs && is_dir) continue; @@ -412,12 +441,23 @@ struct string_list *dir_list_new(const char *dir, if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; - if (!is_dir && ext_list && !string_list_find_elem_prefix(ext_list, ".", file_ext)) + if (!is_compressed_file && !is_dir && ext_list && !supported_by_core) continue; - fill_pathname_join(file_path, dir, name, sizeof(file_path)); - - attr.b = is_dir; + if (is_dir) + attr.i = RARCH_DIRECTORY; + if (is_compressed_file) + attr.i = RARCH_COMPRESSED_ARCHIVE; + /* The order of these ifs is important. + * If the file format is explicitly supported by the libretro-core, we + * need to immediately load it and not designate it as a compressed file. + * + * Example: .zip could be supported as a image by the core and as a + * compressed_file. In that case, we have to interpret it as a image. + * + * */ + if (supported_by_core) + attr.i = RARCH_PLAIN_FILE; if (!string_list_append(list, file_path, attr)) goto error; @@ -480,21 +520,44 @@ struct string_list *dir_list_new(const char *dir, union string_list_elem_attr attr; const char *name = entry->d_name; const char *file_ext = path_get_extension(name); + bool is_compressed_file = false; + bool supported_by_core = false; + attr.i = RARCH_FILETYPE_UNSET; fill_pathname_join(file_path, dir, name, sizeof(file_path)); is_dir = dirent_is_directory(file_path, entry); + + if (!is_dir) + { + is_compressed_file = path_is_compressed_file(file_path); + if (string_list_find_elem_prefix(ext_list, ".", file_ext)) + supported_by_core = true; + } + if (!include_dirs && is_dir) continue; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; - if (!is_dir && ext_list - && !string_list_find_elem_prefix(ext_list, ".", file_ext)) + if (!is_dir && ext_list && !is_compressed_file && !supported_by_core) continue; - attr.b = is_dir; + if (is_dir) + attr.i = RARCH_DIRECTORY; + if (is_compressed_file) + attr.i = RARCH_COMPRESSED_ARCHIVE; + /* The order of these ifs is important. + * If the file format is explicitly supported by the libretro-core, we + * need to immediately load it and not designate it as a compressed file. + * + * Example: .zip could be supported as a image by the core and as a + * compressed_file. In that case, we have to interpret it as a image. + * + * */ + if (supported_by_core) + attr.i = RARCH_PLAIN_FILE; if (!string_list_append(list, file_path, attr)) goto error; @@ -540,6 +603,20 @@ static const char *path_default_slash(void) #endif } +bool path_is_compressed_file(const char* path) +{ +#ifdef HAVE_COMPRESSION + const char* file_ext = path_get_extension(path); +#ifdef HAVE_7ZIP + if (strcmp(file_ext,"7z") == 0) + { + return true; + } +#endif + +#endif + return false; +} bool path_is_directory(const char *path) { diff --git a/file_path.h b/file_path.h index c7beb2d366..d01358955d 100644 --- a/file_path.h +++ b/file_path.h @@ -26,6 +26,19 @@ extern "C" { #endif +/* Order in this enum is equivalent to negative sort order in filelist + * (i.e. DIRECTORY is on top of PLAIN_FILE) */ +enum +{ + RARCH_FILETYPE_UNSET, + RARCH_PLAIN_FILE, + RARCH_COMPRESSED_FILE_IN_ARCHIVE, + RARCH_COMPRESSED_ARCHIVE, + RARCH_DIRECTORY, + RARCH_FILE_UNSUPPORTED, +} FILE_TYPES; + + #ifdef HAVE_COMPRESSION long read_compressed_file(const char * archive_path, const char *relative_path, void **buf); #endif @@ -54,6 +67,8 @@ struct string_list size_t cap; }; +struct string_list *compressed_file_list_new(const char *filename, + const char* ext); struct string_list *dir_list_new(const char *dir, const char *ext, bool include_dirs); void dir_list_sort(struct string_list *list, bool dir_first); @@ -71,6 +86,8 @@ void string_list_join_concat(char *buffer, size_t size, void string_list_set(struct string_list *list, unsigned index, const char *str); +/* path_is_compressed_file also means: The compressed file is supported */ +bool path_is_compressed_file(const char *path); bool path_is_directory(const char *path); bool path_file_exists(const char *path); diff --git a/frontend/menu/backend/menu_common_backend.c b/frontend/menu/backend/menu_common_backend.c index 6ee140e6c3..ade99042e1 100644 --- a/frontend/menu/backend/menu_common_backend.c +++ b/frontend/menu/backend/menu_common_backend.c @@ -1753,6 +1753,30 @@ static int menu_action_ok(const char *dir, menu_entries_push(driver.menu->menu_stack, cat_path, menu_label, type, driver.menu->selection_ptr); } + else if (type == MENU_FILE_CARCHIVE) + { + char cat_path[PATH_MAX]; + fill_pathname_join(cat_path, dir, path, sizeof(cat_path)); + + menu_entries_push(driver.menu->menu_stack, + cat_path, menu_label, type, driver.menu->selection_ptr); + return 0; + } +#ifdef HAVE_COMPRESSION + else if (type == MENU_FILE_IN_CARCHIVE) + { + fill_pathname_join(g_extern.fullpath, dir, path, + sizeof(g_extern.fullpath)); + + g_extern.is_carchive = true; + strncpy(g_extern.carchive_path,dir,sizeof(g_extern.carchive_path)); + + rarch_main_set_state(RARCH_ACTION_STATE_LOAD_CONTENT); + menu_flush_stack_type(driver.menu->menu_stack,MENU_SETTINGS); + driver.menu->msg_force = true; + return -1; + } +#endif else { fill_pathname_join(g_extern.fullpath, dir, path, diff --git a/frontend/menu/disp/lakka.c b/frontend/menu/disp/lakka.c index bb8c639139..f0cb794a6d 100644 --- a/frontend/menu/disp/lakka.c +++ b/frontend/menu/disp/lakka.c @@ -959,7 +959,7 @@ static void lakka_init_items(int i, menu_category_t *category, for (j = 0; j < num_items; j++) { - if (list->elems[j].attr.b) // is a directory + if (list->elems[j].attr.i == RARCH_DIRECTORY) // is a directory lakka_init_items(i, category, info, list->elems[j].data); else { diff --git a/frontend/menu/disp/rgui.c b/frontend/menu/disp/rgui.c index 3dc5db8416..3fb7354b1a 100644 --- a/frontend/menu/disp/rgui.c +++ b/frontend/menu/disp/rgui.c @@ -380,6 +380,16 @@ static void rgui_render(void) type = MENU_FILE_DIRECTORY; w = 5; } + else if (type == MENU_FILE_CARCHIVE) + { + strlcpy(type_str, "(COMP)", sizeof(type_str)); + w = 6; + } + else if (type == MENU_FILE_IN_CARCHIVE) + { + strlcpy(type_str, "(CFILE)", sizeof(type_str)); + w = 7; + } else if (type >= MENU_SETTINGS_CORE_OPTION_START) strlcpy( type_str, diff --git a/frontend/menu/menu_common.h b/frontend/menu/menu_common.h index b5444b8321..623ce026ce 100644 --- a/frontend/menu/menu_common.h +++ b/frontend/menu/menu_common.h @@ -66,6 +66,8 @@ typedef enum MENU_FILE_PLAYLIST_ENTRY, MENU_FILE_USE_DIRECTORY, MENU_FILE_SWITCH, + MENU_FILE_CARCHIVE, + MENU_FILE_IN_CARCHIVE, MENU_SETTINGS, } menu_file_type_t; diff --git a/frontend/menu/menu_entries.c b/frontend/menu/menu_entries.c index a9059247ae..03f6c51a57 100644 --- a/frontend/menu/menu_entries.c +++ b/frontend/menu/menu_entries.c @@ -629,6 +629,7 @@ int menu_parse_check(const char *label, unsigned menu_type) RARCH_LOG("label is menu_parse_check: %s\n", label); #endif if (!((menu_type == MENU_FILE_DIRECTORY || + menu_type == MENU_FILE_CARCHIVE || menu_common_type_is(label, menu_type) == MENU_SETTINGS_SHADER_OPTIONS || menu_common_type_is(label, menu_type) == MENU_FILE_DIRECTORY || !strcmp(label, "input_overlay") || @@ -793,7 +794,16 @@ int menu_parse_and_resolve(file_list_t *list, file_list_t *menu_list) else exts = g_extern.system.valid_extensions; - struct string_list *str_list = dir_list_new(dir, exts, true); + struct string_list *str_list = NULL; + + if (path_is_compressed_file(dir)) + { + str_list = compressed_file_list_new(dir,exts); + } + else + { + str_list = dir_list_new(dir, exts, true); + } if (!str_list) return -1; @@ -806,7 +816,24 @@ int menu_parse_and_resolve(file_list_t *list, file_list_t *menu_list) list_size = str_list->size; for (i = 0; i < str_list->size; i++) { - bool is_dir = str_list->elems[i].attr.b; + menu_file_type_t file_type = 0; + switch (str_list->elems[i].attr.i) + { + case RARCH_DIRECTORY: + file_type = MENU_FILE_DIRECTORY; + break; + case RARCH_COMPRESSED_ARCHIVE: + file_type = MENU_FILE_CARCHIVE; + break; + case RARCH_COMPRESSED_FILE_IN_ARCHIVE: + file_type = MENU_FILE_IN_CARCHIVE; + break; + case RARCH_PLAIN_FILE: + default: + file_type = MENU_FILE_PLAIN; + break; + } + bool is_dir = (file_type == MENU_FILE_DIRECTORY); if ((menu_common_type_is(label, menu_type) == MENU_FILE_DIRECTORY) && !is_dir) continue; @@ -827,11 +854,17 @@ int menu_parse_and_resolve(file_list_t *list, file_list_t *menu_list) /* Push menu_type further down in the chain. * Needed for shader manager currently. */ if (!strcmp(label, "core_list")) + { + /* Compressed cores are unsupported */ + if (file_type == MENU_FILE_CARCHIVE) + continue; + file_list_push(list, path, "", is_dir ? MENU_FILE_DIRECTORY : MENU_FILE_CORE, 0); + } else file_list_push(list, path, "", - is_dir ? MENU_FILE_DIRECTORY : MENU_FILE_PLAIN, 0); + file_type, 0); } menu_entries_push_list(driver.menu, list,