diff --git a/griffin/griffin.c b/griffin/griffin.c index 46848fdbd1..a6c3aa3c4c 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -575,7 +575,9 @@ FILE #include "../content.c" #include "../libretro-common/file/file_path.c" #include "../file_path_special.c" +#ifndef IOS #include "../libretro-common/file/dir_list.c" +#endif #include "../dir_list_special.c" #include "../libretro-common/string/string_list.c" #include "../libretro-common/string/stdstring.c" diff --git a/griffin/griffin_objc.m b/griffin/griffin_objc.m index 3629921122..6385a12a97 100644 --- a/griffin/griffin_objc.m +++ b/griffin/griffin_objc.m @@ -34,6 +34,7 @@ #if defined(HAVE_COCOATOUCH) #if TARGET_OS_IPHONE +#include "../libretro-common/file/dir_list_obj.m" #include "../ui/drivers/cocoa/cocoatouch_menu.m" #include "../ui/drivers/cocoa/cocoatouch_browser.m" diff --git a/libretro-common/file/dir_list_obj.m b/libretro-common/file/dir_list_obj.m new file mode 100644 index 0000000000..b1325acee1 --- /dev/null +++ b/libretro-common/file/dir_list_obj.m @@ -0,0 +1,226 @@ +/* Copyright (C) 2010-2015 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (dir_list.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +static int qstrcmp_plain(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_; + + return strcasecmp(a->data, b->data); +} + +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_type = a->attr.i; + int b_type = b->attr.i; + + + /* Sort directories before files. */ + if (a_type != b_type) + return b_type - a_type; + return strcasecmp(a->data, b->data); +} + +/** + * dir_list_sort: + * @list : pointer to the directory listing. + * @dir_first : move the directories in the listing to the top? + * + * Sorts a directory listing. + * + **/ +void dir_list_sort(struct string_list *list, bool dir_first) +{ + if (list) + qsort(list->elems, list->size, sizeof(struct string_list_elem), + dir_first ? qstrcmp_dir : qstrcmp_plain); +} + +/** + * dir_list_free: + * @list : pointer to the directory listing + * + * Frees a directory listing. + * + **/ +void dir_list_free(struct string_list *list) +{ + string_list_free(list); +} + +/** + * + * dirent_is_directory: + * @path : path to the directory entry. + * @entry : pointer to the directory entry. + * + * Is the directory listing entry a directory? + * + * Returns: true if directory listing entry is + * a directory, false if not. + */ + +static bool dirent_is_directory(const char *path) +{ + BOOL is_directory; + BOOL file_exists_at_path = [[NSFileManager defaultManager] fileExistsAtPath:@(path) isDirectory:&is_directory]; + + (void)file_exists_at_path; + + return is_directory; +} + +/** + * parse_dir_entry: + * @name : name of the directory listing entry. + * @file_path : file path of the directory listing entry. + * @is_dir : is the directory listing a directory? + * @include_dirs : include directories as part of the finished directory listing? + * @list : pointer to directory listing. + * @ext_list : pointer to allowed file extensions listing. + * @file_ext : file extension of the directory listing entry. + * + * Parses a directory listing. + * + * Returns: zero on success, -1 on error, 1 if we should + * continue to the next entry in the directory listing. + **/ +static int parse_dir_entry(const char *name, char *file_path, + bool is_dir, bool include_dirs, + struct string_list *list, struct string_list *ext_list, + const char *file_ext) +{ + union string_list_elem_attr attr; + bool is_compressed_file = false; + bool supported_by_core = false; + + attr.i = RARCH_FILETYPE_UNSET; + + 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) + return 1; + + if (!strcmp(name, ".") || !strcmp(name, "..")) + return 1; + + if (!is_compressed_file && !is_dir && ext_list && !supported_by_core) + return 1; + + 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)) + return -1; + + return 0; +} + +/** + * dir_list_new: + * @dir : directory path. + * @ext : allowed extensions of file directory entries to include. + * @include_dirs : include directories as part of the finished directory listing? + * + * Create a directory listing. + * + * Returns: pointer to a directory listing of type 'struct string_list *' on success, + * NULL in case of error. Has to be freed manually. + **/ +struct string_list *dir_list_new(const char *dir, + const char *ext, bool include_dirs) +{ + NSArray *entries = NULL; + char path_buf[PATH_MAX_LENGTH] = {0}; + struct string_list *ext_list = NULL; + struct string_list *list = NULL; + + (void)path_buf; + + if (!(list = string_list_new())) + return NULL; + + if (ext) + ext_list = string_split(ext, "|"); + + entries = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@(dir) error:nil]; + + for (NSString *name in entries) + { + int ret = 0; + char file_path[PATH_MAX_LENGTH] = {0}; + const char *file_ext = path_get_extension([name UTF8String]); + bool is_dir = false; + + fill_pathname_join(file_path, dir, [name UTF8String], sizeof(file_path)); + + is_dir = dirent_is_directory(file_path); + + ret = parse_dir_entry([name UTF8String], file_path, is_dir, + include_dirs, list, ext_list, file_ext); + + if (ret == -1) + goto error; + + if (ret == 1) + continue; + } + + string_list_free(ext_list); + return list; + +error: + string_list_free(list); + string_list_free(ext_list); + return NULL; +}