diff --git a/libretro-common/include/lists/dir_list.h b/libretro-common/include/lists/dir_list.h index 60fef108fa17..39a0f75217f2 100644 --- a/libretro-common/include/lists/dir_list.h +++ b/libretro-common/include/lists/dir_list.h @@ -85,6 +85,15 @@ bool dir_list_initialize(struct string_list *list, **/ void dir_list_sort(struct string_list *list, bool dir_first); +/** + * dir_list_sort_ignore_ext: + * @list : pointer to the directory listing. + * @dir_first : move the directories in the listing to the top? + * + * Sorts a directory listing. File extensions are ignored. + **/ +void dir_list_sort_ignore_ext(struct string_list *list, bool dir_first); + /** * dir_list_free: * @list : pointer to the directory listing diff --git a/libretro-common/lists/dir_list.c b/libretro-common/lists/dir_list.c index e5e818295452..bd54c7d2d213 100644 --- a/libretro-common/lists/dir_list.c +++ b/libretro-common/lists/dir_list.c @@ -46,6 +46,22 @@ static int qstrcmp_plain(const void *a_, const void *b_) return strcasecmp(a->data, b->data); } +static int qstrcmp_plain_noext(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_; + + const char *ext_a = path_get_extension(a->data); + size_t l_a = string_is_empty(ext_a) ? strlen(a->data) : (ext_a - a->data - 1); + const char *ext_b = path_get_extension(b->data); + size_t l_b = string_is_empty(ext_b) ? strlen(b->data) : (ext_b - b->data - 1); + + int rv = strncasecmp(a->data, b->data, MIN(l_a, l_b)); + if (rv == 0 && l_a != l_b) + return (int)(l_a - l_b); + return rv; +} + static int qstrcmp_dir(const void *a_, const void *b_) { const struct string_list_elem *a = (const struct string_list_elem*)a_; @@ -59,6 +75,19 @@ static int qstrcmp_dir(const void *a_, const void *b_) return strcasecmp(a->data, b->data); } +static int qstrcmp_dir_noext(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 qstrcmp_plain_noext(a, b); +} + /** * dir_list_sort: * @list : pointer to the directory listing. @@ -73,6 +102,20 @@ void dir_list_sort(struct string_list *list, bool dir_first) dir_first ? qstrcmp_dir : qstrcmp_plain); } +/** + * dir_list_sort_ignore_ext: + * @list : pointer to the directory listing. + * @dir_first : move the directories in the listing to the top? + * + * Sorts a directory listing. File extensions are ignored. + **/ +void dir_list_sort_ignore_ext(struct string_list *list, bool dir_first) +{ + if (list) + qsort(list->elems, list->size, sizeof(struct string_list_elem), + dir_first ? qstrcmp_dir_noext : qstrcmp_plain_noext); +} + /** * dir_list_free: * @list : pointer to the directory listing diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index db56e5f0eea0..b9ea8ec18de6 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -4152,7 +4152,7 @@ static unsigned menu_displaylist_parse_playlists( content_count = count; - dir_list_sort(&str_list, true); + dir_list_sort_ignore_ext(&str_list, true); list_size = str_list.size;