From c53bb64611b401d4a83897fbf0ee81276fbef7e3 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Sat, 28 Oct 2023 10:46:46 -0400 Subject: [PATCH] Sort playlists ignoring extension. "Sony - PlayStation 2" sorts before "Sony - PlayStation" because when the menu sorts, it includes the `.lpl` extension, and space comes before period. --- libretro-common/include/lists/dir_list.h | 9 +++++ libretro-common/lists/dir_list.c | 43 ++++++++++++++++++++++++ menu/menu_displaylist.c | 2 +- 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/libretro-common/include/lists/dir_list.h b/libretro-common/include/lists/dir_list.h index 60fef108fa1..39a0f75217f 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 e5e81829545..bd54c7d2d21 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 db56e5f0eea..b9ea8ec18de 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;