diff --git a/menu/menu_explore.c b/menu/menu_explore.c
index 539fa977d0..0688d2a719 100644
--- a/menu/menu_explore.c
+++ b/menu/menu_explore.c
@@ -1,3 +1,19 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2011-2020 - Daniel De Matteis
+ * Copyright (C) 2020 - Psyraven
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
#include
#include "menu_driver.h"
#include "menu_cbs.h"
@@ -8,812 +24,1045 @@
#include
#include
-// Stretchy buffers, invented (?) by Sean Barrett
-#define ex_buf__hdr(b) (((struct ex_buf_hdr *)(b))-1)
+/* Stretchy buffers, invented (?) by Sean Barrett */
+#define EX_BUF__HDR(b) (((struct ex_buf_hdr *)(b))-1)
-#define ex_buf_len(b) ((b) ? ex_buf__hdr(b)->len : 0)
-#define ex_buf_cap(b) ((b) ? ex_buf__hdr(b)->cap : 0)
-#define ex_buf_end(b) ((b) + ex_buf_len(b))
-#define ex_buf_sizeof(b) ((b) ? ex_buf_len(b)*sizeof(*b) : 0)
+#define EX_BUF_LEN(b) ((b) ? EX_BUF__HDR(b)->len : 0)
+#define EX_BUF_CAP(b) ((b) ? EX_BUF__HDR(b)->cap : 0)
+#define EX_BUF_END(b) ((b) + EX_BUF_LEN(b))
+#define EX_BUF_SIZEOF(b) ((b) ? EX_BUF_LEN(b)*sizeof(*b) : 0)
-#define ex_buf_free(b) ((b) ? (free(ex_buf__hdr(b)), (b) = NULL) : 0)
-#define ex_buf_fit(b, n) ((size_t)(n) <= ex_buf_cap(b) ? 0 : (*(void**)(&(b)) = ex_buf__grow((b), (n), sizeof(*(b)))))
-#define ex_buf_push(b, ...) (ex_buf_fit((b), 1 + ex_buf_len(b)), (b)[ex_buf__hdr(b)->len++] = __VA_ARGS__)
-#define ex_buf_pop(b) (b)[--ex_buf__hdr(b)->len]
-#define ex_buf_resize(b, sz) (ex_buf_fit((b), (sz)), ((b) ? ex_buf__hdr(b)->len = (sz) : 0))
-#define ex_buf_clear(b) ((b) ? ex_buf__hdr(b)->len = 0 : 0)
-
-struct ex_buf_hdr { size_t len, cap; };
-static void *ex_buf__grow(const void *buf, size_t new_len, size_t elem_size)
-{
- size_t new_cap = MAX(2 * ex_buf_cap(buf), MAX(new_len, 16));
- size_t new_size = sizeof(struct ex_buf_hdr) + new_cap*elem_size;
- struct ex_buf_hdr *new_hdr;
- if (buf)
- new_hdr = (struct ex_buf_hdr *)realloc(ex_buf__hdr(buf), new_size);
- else
- (new_hdr = (struct ex_buf_hdr *)malloc(new_size))->len = 0;
- new_hdr->cap = new_cap;
- return new_hdr + 1;
-}
-
-// Arena allocator
-typedef struct ex_arena
-{
- char *ptr, *end, **blocks;
-} ex_arena;
+#define EX_BUF_FREE(b) ((b) ? (free(EX_BUF__HDR(b)), (b) = NULL) : 0)
+#define EX_BUF_FIT(b, n) ((size_t)(n) <= EX_BUF_CAP(b) ? 0 : (*(void**)(&(b)) = ex_buf__grow((b), (n), sizeof(*(b)))))
+#define EX_BUF_PUSH(b, val) (EX_BUF_FIT((b), 1 + EX_BUF_LEN(b)), (b)[EX_BUF__HDR(b)->len++] = (val))
+#define EX_BUF_POP(b) (b)[--EX_BUF__HDR(b)->len]
+#define EX_BUF_RESIZE(b, sz) (EX_BUF_FIT((b), (sz)), ((b) ? EX_BUF__HDR(b)->len = (sz) : 0))
+#define EX_BUF_CLEAR(b) ((b) ? EX_BUF__HDR(b)->len = 0 : 0)
#define EX_ARENA_ALIGNMENT 8
#define EX_ARENA_BLOCK_SIZE (64 * 1024)
#define EX_ARENA_ALIGN_UP(n, a) (((n) + (a) - 1) & ~((a) - 1))
+/* Explore */
+enum
+{
+ EXPLORE_BY_DEVELOPER = 0,
+ EXPLORE_BY_PUBLISHER,
+ EXPLORE_BY_RELEASEYEAR,
+ EXPLORE_BY_PLAYERCOUNT,
+ EXPLORE_BY_GENRE,
+ EXPLORE_BY_ORIGIN,
+ EXPLORE_BY_REGION,
+ EXPLORE_BY_FRANCHISE,
+ EXPLORE_BY_TAGS,
+ EXPLORE_BY_SYSTEM,
+ EXPLORE_CAT_COUNT,
+
+ EXPLORE_TYPE_ADDITIONALFILTER = FILE_TYPE_RDB, /* database icon */
+ EXPLORE_TYPE_FILTERNULL = MENU_SETTINGS_LAST,
+ EXPLORE_TYPE_SEARCH,
+ EXPLORE_TYPE_SHOWALL,
+ EXPLORE_TYPE_FIRSTCATEGORY,
+ EXPLORE_TYPE_FIRSTITEM = EXPLORE_TYPE_FIRSTCATEGORY + EXPLORE_CAT_COUNT
+};
+
+struct ex_buf_hdr
+{
+ size_t len;
+ size_t cap;
+};
+
+/* Arena allocator */
+typedef struct ex_arena
+{
+ char *ptr;
+ char *end;
+ char **blocks;
+} ex_arena;
+
+typedef struct ex_hashmap32
+{
+ uint32_t len;
+ uint32_t cap;
+ uint32_t *keys;
+ uintptr_t *vals;
+} ex_hashmap32;
+
+typedef struct
+{
+ uint32_t idx;
+ char str[1];
+} explore_string_t;
+
+typedef struct
+{
+ const struct playlist_entry* playlist_entry;
+ explore_string_t *by[EXPLORE_CAT_COUNT];
+ explore_string_t **split;
+ char* original_title;
+} explore_entry_t;
+
+typedef struct
+{
+ ex_arena arena;
+ explore_string_t **by[EXPLORE_CAT_COUNT];
+ bool has_unknown[EXPLORE_CAT_COUNT];
+
+ explore_entry_t* entries;
+ playlist_t **playlists;
+ const char* label_explore_item_str;
+ char title[1024];
+ char find_string[1024];
+ unsigned top_depth;
+} explore_state_t;
+
+static const struct { const char *name, *rdbkey; bool use_split, is_company, is_numeric; }
+explore_by_info[EXPLORE_CAT_COUNT] =
+{
+ { "Developer", "developer", true, true, false },
+ { "Publisher", "publisher", true, true, false },
+ { "Release Year", "releaseyear", false, false, true },
+ { "Player Count", "users", false, false, true },
+ { "Genre", "genre", true, false, false },
+ { "Origin", "origin", false, false, false },
+ { "Region", "region", false, false, false },
+ { "Franchise", "franchise", false, false, false },
+ { "Tags", "tags", true, false, false },
+ { "System", "system", false, false, false },
+};
+
+/* TODO/FIXME - static global */
+static explore_state_t* explore_state;
+
+static void *ex_buf__grow(const void *buf,
+ size_t new_len, size_t elem_size)
+{
+ struct ex_buf_hdr *new_hdr;
+ size_t new_cap = MAX(2 * EX_BUF_CAP(buf), MAX(new_len, 16));
+ size_t new_size = sizeof(struct ex_buf_hdr) + new_cap*elem_size;
+ if (buf)
+ new_hdr = (struct ex_buf_hdr *)realloc(EX_BUF__HDR(buf), new_size);
+ else
+ {
+ new_hdr = (struct ex_buf_hdr *)malloc(new_size);
+ new_hdr->len = 0;
+ }
+ new_hdr->cap = new_cap;
+ return new_hdr + 1;
+}
+
static void ex_arena_grow(ex_arena *arena, size_t min_size)
{
- size_t size = EX_ARENA_ALIGN_UP(MAX(min_size, EX_ARENA_BLOCK_SIZE), EX_ARENA_ALIGNMENT);
- arena->ptr = (char *)malloc(size);
- arena->end = arena->ptr + size;
- ex_buf_push(arena->blocks, arena->ptr);
+ size_t size = EX_ARENA_ALIGN_UP(
+ MAX(min_size, EX_ARENA_BLOCK_SIZE), EX_ARENA_ALIGNMENT);
+ arena->ptr = (char *)malloc(size);
+ arena->end = arena->ptr + size;
+ EX_BUF_PUSH(arena->blocks, arena->ptr);
}
static void *ex_arena_alloc(ex_arena *arena, size_t size)
{
- if (size > (size_t)(arena->end - arena->ptr))
- ex_arena_grow(arena, size);
- void *ptr = arena->ptr;
- arena->ptr = (char *)EX_ARENA_ALIGN_UP((uintptr_t)(arena->ptr + size), EX_ARENA_ALIGNMENT);
- return ptr;
+ void *ptr = NULL;
+
+ if (size > (size_t)(arena->end - arena->ptr))
+ ex_arena_grow(arena, size);
+
+ ptr = arena->ptr;
+ arena->ptr = (char *)
+ EX_ARENA_ALIGN_UP((uintptr_t)(arena->ptr + size), EX_ARENA_ALIGNMENT);
+ return ptr;
}
static void ex_arena_free(ex_arena *arena)
{
- for (char **it = arena->blocks; it != ex_buf_end(arena->blocks); it++)
- free(*it);
- ex_buf_free(arena->blocks);
- arena->ptr = arena->end = NULL;
- arena->blocks = NULL;
+ char **it;
+
+ for (it = arena->blocks; it != EX_BUF_END(arena->blocks); it++)
+ free(*it);
+
+ EX_BUF_FREE(arena->blocks);
+ arena->ptr = NULL;
+ arena->end = NULL;
+ arena->blocks = NULL;
}
-//Hash function
+/* Hash function */
static uint32_t ex_hash32(const char* str)
{
- uint32_t hash = (uint32_t)0x811c9dc5;
- for (unsigned char c; (c = *(str++)) != '\0';)
- hash = ((hash * (uint32_t)0x01000193) ^ (uint32_t)c);
- return (hash ? hash : 1);
+ unsigned char c;
+ uint32_t hash = (uint32_t)0x811c9dc5;
+ for (; (c = *(str++)) != '\0';)
+ hash = ((hash * (uint32_t)0x01000193) ^ (uint32_t)c);
+ if (hash)
+ return hash;
+ return 1;
}
-static uint32_t ex_hash32_nocase_filtered(const unsigned char* str, size_t len, unsigned char f_first, unsigned char f_last)
+static uint32_t ex_hash32_nocase_filtered(
+ const unsigned char* str, size_t len,
+ unsigned char f_first, unsigned char f_last)
{
- uint32_t hash = (uint32_t)0x811c9dc5;
- for (const unsigned char *end = str + len; str != end;)
- {
- unsigned char c = *(str++);
- if (c >= f_first && c <= f_last)
- hash = ((hash * (uint32_t)0x01000193) ^ (uint32_t)((c >= 'A' && c <= 'Z') ? (c | 0x20) : c));
- }
- return (hash ? hash : 1);
+ const unsigned char *end = NULL;
+ uint32_t hash = (uint32_t)0x811c9dc5;
+ for (end = str + len; str != end;)
+ {
+ unsigned char c = *(str++);
+ if (c >= f_first && c <= f_last)
+ hash = ((hash * (uint32_t)0x01000193)
+ ^ (uint32_t)((c >= 'A' && c <= 'Z')
+ ? (c | 0x20) : c));
+ }
+ if (hash)
+ return hash;
+ return 1;
}
-//Hashmap
-typedef struct ex_hashmap32
-{
- uint32_t len, cap, *keys;
- uintptr_t* vals;
-} ex_hashmap32;
-
+/* Hashmap */
static void ex_hashmap32__grow(ex_hashmap32* map, uint32_t new_cap)
{
- uint32_t oldCap = map->cap, *oldKeys = map->keys;
- uintptr_t *oldVals = map->vals;
- map->cap = (new_cap < 16 ? 16 : new_cap);
- map->keys = (uint32_t *)calloc(map->cap, sizeof(uint32_t));
- map->vals = (uintptr_t *)malloc(map->cap * sizeof(uintptr_t));
- for (size_t i = 0; i < oldCap; i++)
- {
- if (!oldKeys[i]) continue;
- for (uint32_t key = oldKeys[i], j = key;; j++)
- {
- if (!map->keys[j &= map->cap - 1]) { map->keys[j] = key; map->vals[j] = oldVals[i]; break; }
- }
- }
- free(oldKeys);
- free(oldVals);
+ size_t i, j;
+ uint32_t old_cap = map->cap;
+ uint32_t *old_keys = map->keys;
+ uintptr_t *old_vals = map->vals;
+
+ map->cap = (new_cap < 16) ? 16 : new_cap;
+ map->keys = (uint32_t *)calloc(map->cap, sizeof(uint32_t));
+ map->vals = (uintptr_t *)malloc(map->cap * sizeof(uintptr_t));
+
+ for (i = 0; i < old_cap; i++)
+ {
+ uint32_t key;
+ if (!old_keys[i])
+ continue;
+
+ for (key = old_keys[i], j = key;; j++)
+ {
+ if (!map->keys[j &= map->cap - 1])
+ {
+ map->keys[j] = key;
+ map->vals[j] = old_vals[i];
+ break;
+ }
+ }
+ }
+
+ free(old_keys);
+ free(old_vals);
}
-static void ex_hashmap32_free(ex_hashmap32* map) { free(map->keys); free(map->vals); }
+static void ex_hashmap32_free(ex_hashmap32* map)
+{
+ if (!map)
+ return;
+ free(map->keys);
+ free(map->vals);
+}
static uintptr_t ex_hashmap32_getnum(ex_hashmap32* map, uint32_t key)
{
- if (map->len == 0 || !key) return 0;
- for (uint32_t i = key;; i++)
- {
- if (map->keys[i &= map->cap - 1] == key) return map->vals[i];
- if (!map->keys[i]) return 0;
- }
+ uint32_t i;
+ if (!map || map->len == 0 || !key)
+ return 0;
+ for (i = key;; i++)
+ {
+ if (map->keys[i &= map->cap - 1] == key)
+ return map->vals[i];
+ if (!map->keys[i])
+ break;
+ }
+ return 0;
}
-static void ex_hashmap32_setnum(ex_hashmap32* map, uint32_t key, uintptr_t val)
+static void ex_hashmap32_setnum(
+ ex_hashmap32* map, uint32_t key, uintptr_t val)
{
- if (!key) return;
- if (2 * map->len >= map->cap) ex_hashmap32__grow(map, 2 * map->cap);
- for (uint32_t i = key;; i++)
- {
- if (!map->keys[i &= map->cap - 1]) { map->len++; map->keys[i] = key; map->vals[i] = val; return; }
- if (map->keys[i] == key) { map->vals[i] = val; return; }
- }
+ uint32_t i;
+ if (!key)
+ return;
+ if (2 * map->len >= map->cap)
+ ex_hashmap32__grow(map, 2 * map->cap);
+
+ for (i = key;; i++)
+ {
+ if (!map->keys[i &= map->cap - 1])
+ {
+ map->len++;
+ map->keys[i] = key;
+ map->vals[i] = val;
+ return;
+ }
+ if (map->keys[i] == key)
+ {
+ map->vals[i] = val;
+ return;
+ }
+ }
}
-static INLINE void* ex_hashmap32_getptr (ex_hashmap32* map, uint32_t key) { return (void*)ex_hashmap32_getnum(map, key); }
+static INLINE void * ex_hashmap32_getptr (ex_hashmap32* map, uint32_t key) { return (void*)ex_hashmap32_getnum(map, key); }
static INLINE void ex_hashmap32_setptr (ex_hashmap32* map, uint32_t key, void* ptr) { ex_hashmap32_setnum(map, key, (uintptr_t)ptr); }
static INLINE void* ex_hashmap32_strgetptr(ex_hashmap32* map, const char* str) { return (void*)ex_hashmap32_getnum(map, ex_hash32(str)); }
static INLINE void ex_hashmap32_strsetptr(ex_hashmap32* map, const char* str, void* ptr) { ex_hashmap32_setnum(map, ex_hash32(str), (uintptr_t)ptr); }
static INLINE uintptr_t ex_hashmap32_strgetnum(ex_hashmap32* map, const char* str) { return ex_hashmap32_getnum(map, ex_hash32(str)); }
static INLINE void ex_hashmap32_strsetnum(ex_hashmap32* map, const char* str, uintptr_t num) { ex_hashmap32_setnum(map, ex_hash32(str), num); }
-// Explore
-enum
-{
- EXPLORE_BY_DEVELOPER,
- EXPLORE_BY_PUBLISHER,
- EXPLORE_BY_RELEASEYEAR,
- EXPLORE_BY_PLAYERCOUNT,
- EXPLORE_BY_GENRE,
- EXPLORE_BY_ORIGIN,
- EXPLORE_BY_REGION,
- EXPLORE_BY_FRANCHISE,
- EXPLORE_BY_TAGS,
- EXPLORE_BY_SYSTEM,
- EXPLORE_CAT_COUNT,
-
- EXPLORE_TYPE_ADDITIONALFILTER = FILE_TYPE_RDB, //database icon
- EXPLORE_TYPE_FILTERNULL = MENU_SETTINGS_LAST,
- EXPLORE_TYPE_SEARCH,
- EXPLORE_TYPE_SHOWALL,
- EXPLORE_TYPE_FIRSTCATEGORY,
- EXPLORE_TYPE_FIRSTITEM = EXPLORE_TYPE_FIRSTCATEGORY + EXPLORE_CAT_COUNT,
-};
-
-static const struct { const char *name, *rdbkey; bool use_split, is_company, is_numeric; }
- explore_by_info[EXPLORE_CAT_COUNT] =
-{
- { "Developer", "developer", true, true, false },
- { "Publisher", "publisher", true, true, false },
- { "Release Year", "releaseyear", false, false, true },
- { "Player Count", "users", false, false, true },
- { "Genre", "genre", true, false, false },
- { "Origin", "origin", false, false, false },
- { "Region", "region", false, false, false },
- { "Franchise", "franchise", false, false, false },
- { "Tags", "tags", true, false, false },
- { "System", "system", false, false, false },
-};
-
-typedef struct
-{
- uint32_t idx;
- char str[1];
-} explore_string_t;
-
-typedef struct
-{
- const struct playlist_entry* playlist_entry;
- explore_string_t *by[EXPLORE_CAT_COUNT];
- explore_string_t **split;
- char* original_title;
-} explore_entry_t;
-
-typedef struct
-{
- ex_arena arena;
- explore_string_t **by[EXPLORE_CAT_COUNT];
- bool has_unknown[EXPLORE_CAT_COUNT];
-
- explore_entry_t* entries;
- playlist_t **playlists;
- const char* label_explore_item_str;
- char title[1024];
- char find_string[1024];
- unsigned top_depth;
-} explore_state_t;
-
-static explore_state_t* explore_state;
-
static int explore_qsort_func_strings(const void *a_, const void *b_)
{
- const explore_string_t **a = (const explore_string_t**)a_;
- const explore_string_t **b = (const explore_string_t**)b_;
- if ((*a)->str[0] != (*b)->str[0])
- return (unsigned char)(*a)->str[0] - (unsigned char)(*b)->str[0];
- return strcasecmp((*a)->str, (*b)->str);
+ const explore_string_t **a = (const explore_string_t**)a_;
+ const explore_string_t **b = (const explore_string_t**)b_;
+ if ((*a)->str[0] != (*b)->str[0])
+ return (unsigned char)(*a)->str[0] - (unsigned char)(*b)->str[0];
+ return strcasecmp((*a)->str, (*b)->str);
}
static int explore_qsort_func_entries(const void *a_, const void *b_)
{
- const explore_entry_t *a = (const explore_entry_t*)a_;
- const explore_entry_t *b = (const explore_entry_t*)b_;
- if (a->playlist_entry->label[0] != b->playlist_entry->label[0])
- return (unsigned char)a->playlist_entry->label[0] - (unsigned char)b->playlist_entry->label[0];
- return strcasecmp(a->playlist_entry->label, b->playlist_entry->label);
+ const explore_entry_t *a = (const explore_entry_t*)a_;
+ const explore_entry_t *b = (const explore_entry_t*)b_;
+ if (a->playlist_entry->label[0] != b->playlist_entry->label[0])
+ return (unsigned char)a->playlist_entry->label[0] - (unsigned char)b->playlist_entry->label[0];
+ return strcasecmp(a->playlist_entry->label, b->playlist_entry->label);
}
static int explore_qsort_func_menulist(const void *a_, const void *b_)
{
- const struct item_file *a = (const struct item_file*)a_;
- const struct item_file *b = (const struct item_file*)b_;
- if (a->path[0] != b->path[0])
- return (unsigned char)a->path[0] - (unsigned char)b->path[0];
- return strcasecmp(a->path, b->path);
+ const struct item_file *a = (const struct item_file*)a_;
+ const struct item_file *b = (const struct item_file*)b_;
+ if (a->path[0] != b->path[0])
+ return (unsigned char)a->path[0] - (unsigned char)b->path[0];
+ return strcasecmp(a->path, b->path);
}
static int explore_check_company_suffix(const char* p, bool search_reverse)
{
- if (search_reverse)
- {
- p -= (p[-1] == '.' ? 4 : 3);
- if (p[-1] != ' ') return 0;
- }
- if (tolower(p[0]) == 'i' && tolower(p[1]) == 'n' && tolower(p[2]) == 'c') return (p[3] == '.' ? 4 : 3); //, Inc
- if (tolower(p[0]) == 'l' && tolower(p[1] )== 't' && tolower(p[2]) == 'd') return (p[3] == '.' ? 4 : 3); //, Ltd
- if (tolower(p[0]) == 't' && tolower(p[1] )== 'h' && tolower(p[2]) == 'e') return (p[3] == '.' ? 4 : 3); //, The
- return 0;
+ if (search_reverse)
+ {
+ p -= (p[-1] == '.' ? 4 : 3);
+ if (p[-1] != ' ')
+ return 0;
+ }
+ if (tolower(p[0]) == 'i' && tolower(p[1]) == 'n' && tolower(p[2]) == 'c')
+ return (p[3] == '.' ? 4 : 3); /*, Inc */
+ if (tolower(p[0]) == 'l' && tolower(p[1] )== 't' && tolower(p[2]) == 'd')
+ return (p[3] == '.' ? 4 : 3); /*, Ltd */
+ if (tolower(p[0]) == 't' && tolower(p[1] )== 'h' && tolower(p[2]) == 'e')
+ return (p[3] == '.' ? 4 : 3); /*, The */
+ return 0;
}
static void explore_add_unique_string(ex_hashmap32 *maps, explore_entry_t *e, unsigned cat, const char *str, explore_string_t ***split_buf)
{
- if (!str || !*str)
- {
- explore_state->has_unknown[cat] = true;
- return;
- }
+ bool is_company;
+ const char *p;
+ const char *p_next;
+ if (!str || !*str)
+ {
+ explore_state->has_unknown[cat] = true;
+ return;
+ }
- if (!explore_by_info[cat].use_split) split_buf = NULL;
- bool is_company = explore_by_info[cat].is_company;
+ if (!explore_by_info[cat].use_split)
+ split_buf = NULL;
+ is_company = explore_by_info[cat].is_company;
- for (const char *p = str + 1, *p_next;; p++)
- {
- if (*p != '/' && *p != ',' && *p != '|' && *p != '\0')
- continue;
+ for (p = str + 1;; p++)
+ {
+ size_t len = 0;
+ uint32_t hash = 0;
+ explore_string_t* entry = NULL;
- if (!split_buf && *p != '\0') continue;
+ if (*p != '/' && *p != ',' && *p != '|' && *p != '\0')
+ continue;
- p_next = p;
- while (*str == ' ') str++;
- while (p[-1] == ' ') p--;
+ if (!split_buf && *p != '\0')
+ continue;
- if (p == str)
- {
- if (*p == '\0') return;
- continue;
- }
+ p_next = p;
+ while (*str == ' ')
+ str++;
+ while (p[-1] == ' ')
+ p--;
- if (is_company && p - str > 5)
- {
- p -= explore_check_company_suffix(p, true);
- while (p[-1] == ' ') p--;
- }
+ if (p == str)
+ {
+ if (*p == '\0')
+ return;
+ continue;
+ }
- size_t len = p - str;
- uint32_t hash = ex_hash32_nocase_filtered((unsigned char*)str, len, '0', 255);
- explore_string_t* entry = (explore_string_t*)ex_hashmap32_getptr(&maps[cat], hash);
- if (!entry)
- {
- entry = (explore_string_t*)ex_arena_alloc(&explore_state->arena, sizeof(explore_string_t) + len);
- memcpy(entry->str, str, len);
- entry->str[len] = '\0';
- ex_buf_push(explore_state->by[cat], entry);
- ex_hashmap32_setptr(&maps[cat], hash, entry);
- }
+ if (is_company && p - str > 5)
+ {
+ p -= explore_check_company_suffix(p, true);
+ while (p[-1] == ' ')
+ p--;
+ }
- if (!e->by[cat]) e->by[cat] = entry;
- else ex_buf_push(*split_buf, entry);
+ len = p - str;
+ hash = ex_hash32_nocase_filtered(
+ (unsigned char*)str, len, '0', 255);
+ entry =
+ (explore_string_t*)ex_hashmap32_getptr(&maps[cat], hash);
- if (*p_next == '\0') return;
- if (is_company && *p_next == ',')
- {
- p = p_next + 1;
- while (*p == ' ') p++;
- p += explore_check_company_suffix(p, false);
- while (*p == ' ') p++;
- if (*p == '\0') return;
- if (*p == '/' || *p == ',' || *p == '|') p_next = p;
- }
- p = p_next;
- str = p + 1;
- }
+ if (!entry)
+ {
+ entry = (explore_string_t*)
+ ex_arena_alloc(&explore_state->arena,
+ sizeof(explore_string_t) + len);
+ memcpy(entry->str, str, len);
+ entry->str[len] = '\0';
+ EX_BUF_PUSH(explore_state->by[cat], entry);
+ ex_hashmap32_setptr(&maps[cat], hash, entry);
+ }
+
+ if (!e->by[cat])
+ e->by[cat] = entry;
+ else
+ EX_BUF_PUSH(*split_buf, entry);
+
+ if (*p_next == '\0')
+ return;
+ if (is_company && *p_next == ',')
+ {
+ p = p_next + 1;
+ while (*p == ' ')
+ p++;
+ p += explore_check_company_suffix(p, false);
+ while (*p == ' ')
+ p++;
+ if (*p == '\0')
+ return;
+ if (*p == '/' || *p == ',' || *p == '|')
+ p_next = p;
+ }
+ p = p_next;
+ str = p + 1;
+ }
}
-static void explore_free()
+static void explore_free(void)
{
- for (int i = 0; i != EXPLORE_CAT_COUNT; i++)
- ex_buf_free(explore_state->by[i]);
+ unsigned i;
+ for (i = 0; i != EXPLORE_CAT_COUNT; i++)
+ EX_BUF_FREE(explore_state->by[i]);
- ex_buf_free(explore_state->entries);
+ EX_BUF_FREE(explore_state->entries);
- for (size_t i = 0; i != ex_buf_len(explore_state->playlists); i++)
- playlist_free(explore_state->playlists[i]);
- ex_buf_free(explore_state->playlists);
+ for (i = 0; i != EX_BUF_LEN(explore_state->playlists); i++)
+ playlist_free(explore_state->playlists[i]);
+ EX_BUF_FREE(explore_state->playlists);
- ex_arena_free(&explore_state->arena);
- free(explore_state);
- explore_state = NULL;
+ ex_arena_free(&explore_state->arena);
+ free(explore_state);
+ explore_state = NULL;
}
-static void explore_build_list()
+static void explore_build_list(void)
{
- char tmp[PATH_MAX_LENGTH];
+ unsigned i;
+ char tmp[PATH_MAX_LENGTH];
+ struct explore_rdb { libretrodb_t *handle; ex_hashmap32 playlist_entries; } *rdbs = NULL;
+ core_info_list_t *core_list = NULL;
+ ex_hashmap32 map_cores = {0};
+ ex_hashmap32 rdb_indices = {0};
+ explore_string_t **split_buf = NULL;
+ ex_hashmap32 cat_maps[EXPLORE_CAT_COUNT] = {{0}};
+ settings_t *settings = NULL;
+ const char *directory_playlist = NULL;
+ const char *directory_database = NULL;
+ libretro_vfs_implementation_dir *dir = NULL;
- if (explore_state)
- explore_free();
+ if (explore_state)
+ explore_free();
- explore_state = calloc(1, sizeof(explore_state_t));
- explore_state->label_explore_item_str = msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_ITEM);
+ explore_state = calloc(1, sizeof(explore_state_t));
+ explore_state->label_explore_item_str =
+ msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_ITEM);
- settings_t *settings = config_get_ptr();
- const char *directory_playlist = settings->paths.directory_playlist;
- const char *directory_database = settings->paths.path_content_database;
+ settings = config_get_ptr();
+ directory_playlist = settings->paths.directory_playlist;
+ directory_database = settings->paths.path_content_database;
- ex_hashmap32 rdb_indices = {0};
- struct explore_rdb { libretrodb_t *handle; ex_hashmap32 playlist_entries; } *rdbs = NULL;
+ /* Index all playlists */
+ for (dir = retro_vfs_opendir_impl(directory_playlist, false); dir;)
+ {
+ size_t j, used_entries = 0;
+ playlist_config_t playlist_config = {0};
+ playlist_t *playlist = NULL;
+ const char *fext = NULL;
+ const char *fname = NULL;
- // Index all playlists
- for (libretro_vfs_implementation_dir *dir = retro_vfs_opendir_impl(directory_playlist, false); dir;)
- {
- if (!retro_vfs_readdir_impl(dir)) { retro_vfs_closedir_impl(dir); break; }
+ if (!retro_vfs_readdir_impl(dir))
+ {
+ retro_vfs_closedir_impl(dir);
+ break;
+ }
- playlist_config_t playlist_config = {0};
- const char* fname = retro_vfs_dirent_get_name_impl(dir), *fext = (fname ? strrchr(fname, '.') : NULL);
- if (!fext || strcasecmp(fext, ".lpl"))
- continue;
+ fname = retro_vfs_dirent_get_name_impl(dir);
+ if (fname)
+ fext = strrchr(fname, '.');
- fill_pathname_join(playlist_config.path, directory_playlist, fname, sizeof(playlist_config.path));
- playlist_config.capacity = COLLECTION_SIZE;
- playlist_t *playlist = playlist_init(&playlist_config);
+ if (!fext || strcasecmp(fext, ".lpl"))
+ continue;
- size_t used_entries = 0;
- for (size_t j = 0; j < playlist_size(playlist); j++)
- {
- const struct playlist_entry *entry = NULL;
- playlist_get_index(playlist, j, &entry);
+ fill_pathname_join(playlist_config.path,
+ directory_playlist, fname, sizeof(playlist_config.path));
+ playlist_config.capacity = COLLECTION_SIZE;
+ playlist = playlist_init(&playlist_config);
- // Maybe remove this to also include playlist entries with no crc/db/label (build label from file name)
- if (!entry->crc32 || !*entry->crc32 || !entry->db_name || !*entry->db_name || !entry->label || !*entry->label) continue;
+ for (j = 0; j < playlist_size(playlist); j++)
+ {
+ uintptr_t rdb_num;
+ uint32_t entry_crc32;
+ struct explore_rdb* rdb = NULL;
+ const struct playlist_entry *entry = NULL;
+ playlist_get_index(playlist, j, &entry);
- uintptr_t rdb_num = ex_hashmap32_strgetnum(&rdb_indices, entry->db_name);
- if (!rdb_num)
- {
- struct explore_rdb rdb = { libretrodb_new() };
- fill_pathname_join_noext(tmp, directory_database, entry->db_name, sizeof(tmp));
- strlcat(tmp, ".rdb", sizeof(tmp));
- if (libretrodb_open(tmp, rdb.handle) != 0)
- {
- // invalid rdb file
- libretrodb_free(rdb.handle);
- ex_hashmap32_strsetnum(&rdb_indices, entry->db_name, (uintptr_t)-1);
- continue;
- }
+ /* Maybe remove this to also include playlist entries
+ * with no CRC/db/label (build label from file name) */
+ if (
+ !entry->crc32
+ || !*entry->crc32
+ || !entry->db_name
+ || !*entry->db_name
+ || !entry->label
+ || !*entry->label)
+ continue;
- ex_buf_push(rdbs, rdb);
- rdb_num = (uintptr_t)ex_buf_len(rdbs);
- ex_hashmap32_strsetnum(&rdb_indices, entry->db_name, rdb_num);
- }
+ rdb_num = ex_hashmap32_strgetnum(&rdb_indices, entry->db_name);
+ if (!rdb_num)
+ {
+ struct explore_rdb rdb;
- if (rdb_num == (uintptr_t)-1)
- continue;
+ rdb.handle = libretrodb_new();
+ rdb.playlist_entries.len = 0;
+ rdb.playlist_entries.cap = 0;
+ rdb.playlist_entries.keys = NULL;
+ rdb.playlist_entries.vals = NULL;
- struct explore_rdb* rdb = &rdbs[rdb_num - 1];
- uint32_t entry_crc32 = (uint32_t)strtoul(entry->crc32, NULL, 16);
- ex_hashmap32_setptr(&rdb->playlist_entries, entry_crc32, (void*)entry);
- used_entries++;
- }
+ fill_pathname_join_noext(
+ tmp, directory_database, entry->db_name, sizeof(tmp));
+ strlcat(tmp, ".rdb", sizeof(tmp));
- if (used_entries)
- ex_buf_push(explore_state->playlists, playlist);
- else
- playlist_free(playlist);
- }
+ if (libretrodb_open(tmp, rdb.handle) != 0)
+ {
+ /* invalid rdb file */
+ libretrodb_free(rdb.handle);
+ ex_hashmap32_strsetnum(&rdb_indices,
+ entry->db_name, (uintptr_t)-1);
+ continue;
+ }
- ex_hashmap32 map_cores = {0};
- core_info_list_t *core_list = NULL;
- if (core_info_get_list(&core_list) && core_list)
- for (size_t i = 0; i != core_list->count; i++)
- ex_hashmap32_strsetptr(&map_cores, core_list->list[i].display_name, &core_list->list[i]);
+ EX_BUF_PUSH(rdbs, rdb);
+ rdb_num = (uintptr_t)EX_BUF_LEN(rdbs);
+ ex_hashmap32_strsetnum(&rdb_indices, entry->db_name, rdb_num);
+ }
- // Loop through all RDBs referenced in the playlists and load meta data strings
- explore_string_t **split_buf = NULL;
- ex_hashmap32 cat_maps[EXPLORE_CAT_COUNT] = {{0}};
- for (size_t i = 0; i != ex_buf_len(rdbs); i++)
- {
- struct explore_rdb* rdb = &rdbs[i];
+ if (rdb_num == (uintptr_t)-1)
+ continue;
- struct rmsgpack_dom_value item;
- libretrodb_cursor_t *cur = libretrodb_cursor_new();
- bool more = (libretrodb_cursor_open(rdb->handle, cur, NULL) == 0
- && libretrodb_cursor_read_item(cur, &item) == 0);
+ rdb = &rdbs[rdb_num - 1];
+ entry_crc32 = (uint32_t)strtoul(entry->crc32, NULL, 16);
+ ex_hashmap32_setptr(&rdb->playlist_entries,
+ entry_crc32, (void*)entry);
+ used_entries++;
+ }
- for (; more; more = (rmsgpack_dom_value_free(&item), libretrodb_cursor_read_item(cur, &item) == 0))
- {
- uint32_t crc32;
- char numeric_buf[EXPLORE_CAT_COUNT][16];
- char* fields[EXPLORE_CAT_COUNT] = {NULL};
- char* original_title = NULL;
- bool found_crc32 = false;
- core_info_t* core_info = NULL;
+ if (used_entries)
+ EX_BUF_PUSH(explore_state->playlists, playlist);
+ else
+ playlist_free(playlist);
+ }
- if (item.type != RDT_MAP)
- continue;
+ if (core_info_get_list(&core_list) && core_list)
+ for (i = 0; i != core_list->count; i++)
+ ex_hashmap32_strsetptr(&map_cores, core_list->list[i].display_name, &core_list->list[i]);
- for (unsigned i = 0; i < item.val.map.len; i++)
- {
- struct rmsgpack_dom_value *key = &item.val.map.items[i].key;
- struct rmsgpack_dom_value *val = &item.val.map.items[i].value;
- if (!key || !val || key->type != RDT_STRING)
- continue;
+ /* Loop through all RDBs referenced in the playlists
+ * and load meta data strings */
+ for (i = 0; i != EX_BUF_LEN(rdbs); i++)
+ {
+ struct rmsgpack_dom_value item;
+ struct explore_rdb* rdb = &rdbs[i];
+ libretrodb_cursor_t *cur = libretrodb_cursor_new();
+ bool more =
+ (
+ libretrodb_cursor_open(rdb->handle, cur, NULL) == 0
+ && libretrodb_cursor_read_item(cur, &item) == 0);
- const char* key_str = key->val.string.buff;
- if (string_is_equal(key_str, "crc"))
- {
- crc32 = swap_if_little32(*(uint32_t*)val->val.binary.buff);
- found_crc32 = true;
- continue;
- }
- if (string_is_equal(key_str, "original_title"))
- {
- original_title = val->val.string.buff;
- continue;
- }
- for (unsigned cat = 0; cat != EXPLORE_CAT_COUNT; cat++)
- {
- if (!string_is_equal(key_str, explore_by_info[cat].rdbkey)) continue;
- if (explore_by_info[cat].is_numeric)
- {
- if (!val->val.int_) break;
- snprintf(numeric_buf[cat], sizeof(numeric_buf[cat]), "%d", (int)val->val.int_);
- fields[cat] = numeric_buf[cat];
- break;
- }
- if (val->type != RDT_STRING) break;
- fields[cat] = val->val.string.buff;
- break;
- }
- }
+ for (; more; more = (rmsgpack_dom_value_free(&item),
+ libretrodb_cursor_read_item(cur, &item) == 0))
+ {
+ unsigned k, l, cat;
+ uint32_t crc32;
+ explore_entry_t e;
+ char numeric_buf[EXPLORE_CAT_COUNT][16];
+ char* fields[EXPLORE_CAT_COUNT] = {NULL};
+ const struct playlist_entry *entry = NULL;
+ char* original_title = NULL;
+ bool found_crc32 = false;
+ core_info_t* core_info = NULL;
- if (!found_crc32)
- continue;
+ if (item.type != RDT_MAP)
+ continue;
- const struct playlist_entry *entry = (const struct playlist_entry *)ex_hashmap32_getptr(&rdb->playlist_entries, crc32);
- if (!entry)
- continue;
+ for (k = 0; k < item.val.map.len; k++)
+ {
+ const char *key_str = NULL;
+ struct rmsgpack_dom_value *key = &item.val.map.items[k].key;
+ struct rmsgpack_dom_value *val = &item.val.map.items[k].value;
+ if (!key || !val || key->type != RDT_STRING)
+ continue;
- explore_entry_t e = {entry};
- for (unsigned cat = 0; cat != EXPLORE_CAT_COUNT; cat++)
- explore_add_unique_string(cat_maps, &e, cat, fields[cat], &split_buf);
+ key_str = key->val.string.buff;
+ if (string_is_equal(key_str, "crc"))
+ {
+ crc32 = swap_if_little32(*(uint32_t*)val->val.binary.buff);
+ found_crc32 = true;
+ continue;
+ }
+ else if (string_is_equal(key_str, "original_title"))
+ {
+ original_title = val->val.string.buff;
+ continue;
+ }
+
+ for (cat = 0; cat != EXPLORE_CAT_COUNT; cat++)
+ {
+ if (!string_is_equal(key_str, explore_by_info[cat].rdbkey))
+ continue;
+
+ if (explore_by_info[cat].is_numeric)
+ {
+ if (!val->val.int_)
+ break;
+ snprintf(numeric_buf[cat],
+ sizeof(numeric_buf[cat]),
+ "%d", (int)val->val.int_);
+ fields[cat] = numeric_buf[cat];
+ break;
+ }
+ if (val->type != RDT_STRING)
+ break;
+ fields[cat] = val->val.string.buff;
+ break;
+ }
+ }
+
+ if (!found_crc32)
+ continue;
+
+ entry = (const struct playlist_entry *)ex_hashmap32_getptr(
+ &rdb->playlist_entries, crc32);
+ if (!entry)
+ continue;
+
+ e.playlist_entry = entry;
+ for (l = 0; l < EXPLORE_CAT_COUNT; l++)
+ e.by[l] = NULL;
+ e.split = NULL;
+ e.original_title = NULL;
+
+ for (cat = 0; cat != EXPLORE_CAT_COUNT; cat++)
+ explore_add_unique_string(cat_maps, &e, cat,
+ fields[cat], &split_buf);
if (!string_is_empty(entry->core_name))
- core_info = (core_info_t*)ex_hashmap32_strgetptr(&map_cores, entry->core_name);
- explore_add_unique_string(cat_maps, &e, EXPLORE_BY_SYSTEM, (core_info ? core_info->systemname : NULL), NULL);
+ core_info = (core_info_t*)
+ ex_hashmap32_strgetptr(&map_cores, entry->core_name);
+ explore_add_unique_string(cat_maps, &e,
+ EXPLORE_BY_SYSTEM,
+ (core_info ? core_info->systemname : NULL), NULL);
- if (original_title && *original_title)
- {
- size_t len = strlen(original_title) + 1;
- e.original_title = (char*)ex_arena_alloc(&explore_state->arena, len);
- memcpy(e.original_title, original_title, len);
- }
+ if (original_title && *original_title)
+ {
+ size_t len = strlen(original_title) + 1;
+ e.original_title = (char*)
+ ex_arena_alloc(&explore_state->arena, len);
+ memcpy(e.original_title, original_title, len);
+ }
- if (ex_buf_len(split_buf))
- {
- ex_buf_push(split_buf, NULL); //terminator
- size_t len = ex_buf_sizeof(split_buf);
- e.split = (explore_string_t **)ex_arena_alloc(&explore_state->arena, len);
- memcpy(e.split, split_buf, len);
- ex_buf_clear(split_buf);
- }
+ if (EX_BUF_LEN(split_buf))
+ {
+ size_t len;
- ex_buf_push(explore_state->entries, e);
- }
- libretrodb_cursor_close(cur);
- libretrodb_cursor_free(cur);
- libretrodb_close(rdb->handle);
- libretrodb_free(rdb->handle);
- ex_hashmap32_free(&rdb->playlist_entries);
- }
- ex_buf_free(split_buf);
- ex_hashmap32_free(&map_cores);
- ex_hashmap32_free(&rdb_indices);
- ex_buf_free(rdbs);
+ EX_BUF_PUSH(split_buf, NULL); /* terminator */
+ len = EX_BUF_SIZEOF(split_buf);
+ e.split = (explore_string_t **)
+ ex_arena_alloc(&explore_state->arena, len);
+ memcpy(e.split, split_buf, len);
+ EX_BUF_CLEAR(split_buf);
+ }
- for (int i = 0; i != EXPLORE_CAT_COUNT; i++)
- {
- size_t len = ex_buf_len(explore_state->by[i]);
- qsort(explore_state->by[i], len, sizeof(*explore_state->by[i]), explore_qsort_func_strings);
- for (uint32_t idx = 0; idx != len; idx++)
- explore_state->by[i][idx]->idx = idx;
+ EX_BUF_PUSH(explore_state->entries, e);
+ }
- ex_hashmap32_free(&cat_maps[i]);
- }
- qsort(explore_state->entries, ex_buf_len(explore_state->entries), sizeof(*explore_state->entries), explore_qsort_func_entries);
+ libretrodb_cursor_close(cur);
+ libretrodb_cursor_free(cur);
+ libretrodb_close(rdb->handle);
+ libretrodb_free(rdb->handle);
+ ex_hashmap32_free(&rdb->playlist_entries);
+ }
+ EX_BUF_FREE(split_buf);
+ ex_hashmap32_free(&map_cores);
+ ex_hashmap32_free(&rdb_indices);
+ EX_BUF_FREE(rdbs);
+
+ for (i = 0; i != EXPLORE_CAT_COUNT; i++)
+ {
+ uint32_t idx;
+ size_t len = EX_BUF_LEN(explore_state->by[i]);
+ qsort(explore_state->by[i], len, sizeof(*explore_state->by[i]), explore_qsort_func_strings);
+ for (idx = 0; idx != len; idx++)
+ explore_state->by[i][idx]->idx = idx;
+
+ ex_hashmap32_free(&cat_maps[i]);
+ }
+ qsort(explore_state->entries, EX_BUF_LEN(explore_state->entries), sizeof(*explore_state->entries), explore_qsort_func_entries);
}
static int explore_action_get_title(const char *path, const char *label, unsigned menu_type, char *s, size_t len)
{
- snprintf(s, len, "%s", explore_state->title);
- return 0;
+ snprintf(s, len, "%s", explore_state->title);
+ return 0;
}
static void explore_append_title(const char* fmt, ...)
{
- va_list ap;
- size_t len = strlen(explore_state->title);
- va_start(ap, fmt);
- vsnprintf(explore_state->title + len, sizeof(explore_state->title) - len, fmt, ap);
- va_end(ap);
+ va_list ap;
+ size_t len = strlen(explore_state->title);
+ va_start(ap, fmt);
+ vsnprintf(explore_state->title + len,
+ sizeof(explore_state->title) - len, fmt, ap);
+ va_end(ap);
}
static int explore_action_sublabel_spacer(file_list_t *list, unsigned type, unsigned i, const char *label, const char *path, char *s, size_t len)
{
- strlcpy(s, " ", len);
- return 1; // 1 means it'll never change and can be cached
+ strlcpy(s, " ", len);
+ return 1; /* 1 means it'll never change and can be cached */
}
-static int explore_action_ok(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx)
+static int explore_action_ok(const char *path, const char *label,
+ unsigned type, size_t idx, size_t entry_idx)
{
- const char* explore_tab = msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB);
- filebrowser_clear_type();
- return generic_action_ok_displaylist_push(explore_tab, NULL, explore_tab, type, idx, entry_idx, ACTION_OK_DL_PUSH_DEFAULT);
+ const char* explore_tab = msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB);
+ filebrowser_clear_type();
+ return generic_action_ok_displaylist_push(explore_tab, NULL, explore_tab, type, idx, entry_idx, ACTION_OK_DL_PUSH_DEFAULT);
}
static menu_file_list_cbs_t* explore_menu_entry(file_list_t *list, const char *path, unsigned type)
{
- menu_entries_append_enum(list, path, explore_state->label_explore_item_str, MENU_ENUM_LABEL_EXPLORE_ITEM, type, 0, 0);
- menu_file_list_cbs_t* cbs = ((menu_file_list_cbs_t*)list->list[list->size-1].actiondata);
- cbs->action_ok = explore_action_ok;
- return cbs;
+ menu_file_list_cbs_t* cbs = NULL;
+ menu_entries_append_enum(list, path, explore_state->label_explore_item_str, MENU_ENUM_LABEL_EXPLORE_ITEM, type, 0, 0);
+ cbs = ((menu_file_list_cbs_t*)list->list[list->size-1].actiondata);
+ if (!cbs)
+ return NULL;
+ cbs->action_ok = explore_action_ok;
+ return cbs;
}
static void explore_menu_add_spacer(file_list_t *list)
{
- if (!list->size) return;
- ((menu_file_list_cbs_t*)list->list[list->size-1].actiondata)->action_sublabel = explore_action_sublabel_spacer;
+ if (!list->size)
+ return;
+ ((menu_file_list_cbs_t*)list->list[list->size-1].actiondata)->action_sublabel = explore_action_sublabel_spacer;
}
static void explore_action_find_complete(void *userdata, const char *line)
{
- menu_input_dialog_end();
- if (line && *line)
- {
- strlcpy(explore_state->find_string, line, sizeof(explore_state->find_string));
- explore_action_ok(NULL, NULL, EXPLORE_TYPE_SEARCH, 0, 0);
- }
+ menu_input_dialog_end();
+ if (line && *line)
+ {
+ strlcpy(explore_state->find_string, line, sizeof(explore_state->find_string));
+ explore_action_ok(NULL, NULL, EXPLORE_TYPE_SEARCH, 0, 0);
+ }
}
static int explore_action_ok_find(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx)
{
- menu_input_ctx_line_t line = {0};
- line.label = "Search Text";
- line.cb = explore_action_find_complete;
- menu_input_dialog_start(&line);
- return 0;
+ menu_input_ctx_line_t line = {0};
+ line.label = "Search Text";
+ line.cb = explore_action_find_complete;
+ menu_input_dialog_start(&line);
+ return 0;
}
unsigned menu_displaylist_explore(file_list_t *list)
{
- char tmp[512];
- file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0);
+ unsigned i, cat;
+ char tmp[512];
+ unsigned depth, current_type, current_cat, previous_cat;
+ unsigned levels = 0;
+ struct item_file *stack_top = NULL;
+ file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0);
- if (!explore_state)
- {
- explore_build_list();
- explore_state->top_depth = (unsigned)menu_stack->size - 1;
- }
+ if (!explore_state)
+ {
+ explore_build_list();
+ explore_state->top_depth = (unsigned)menu_stack->size - 1;
+ }
- if (menu_stack->size > 1)
- {
- struct item_file *stack = &menu_stack->list[menu_stack->size - 1];
- menu_file_list_cbs_t* cbs = ((menu_file_list_cbs_t*)stack->actiondata);
- cbs->action_get_title = explore_action_get_title;
- }
+ if (menu_stack->size > 1)
+ {
+ struct item_file *stack = &menu_stack->list[menu_stack->size - 1];
+ menu_file_list_cbs_t* cbs = ((menu_file_list_cbs_t*)stack->actiondata);
+ cbs->action_get_title = explore_action_get_title;
+ }
- struct item_file *stack_top = menu_stack->list + explore_state->top_depth;
- unsigned depth = (unsigned)menu_stack->size - 1 - explore_state->top_depth;
- unsigned current_type = stack_top[depth].type;
- unsigned current_cat = current_type - EXPLORE_TYPE_FIRSTCATEGORY;
- unsigned previous_cat = stack_top[depth ? depth - 1 : 0].type - EXPLORE_TYPE_FIRSTCATEGORY;
+ stack_top = menu_stack->list + explore_state->top_depth;
+ depth = (unsigned)menu_stack->size - 1 - explore_state->top_depth;
+ current_type = stack_top[depth].type;
+ current_cat = current_type - EXPLORE_TYPE_FIRSTCATEGORY;
+ previous_cat = stack_top[depth ? depth - 1 : 0].type - EXPLORE_TYPE_FIRSTCATEGORY;
- if (depth)
- {
- ((menu_file_list_cbs_t*)stack_top[depth].actiondata)->action_get_title = explore_action_get_title;
+ if (depth)
+ {
+ bool clear_find_text = false;
+ ((menu_file_list_cbs_t*)stack_top[depth].actiondata)->action_get_title = explore_action_get_title;
- bool clear_find_text = (current_type != EXPLORE_TYPE_SEARCH);
- explore_state->title[0] = '\0';
- for (unsigned i = 1, levels = 0; i < depth; i++)
- {
- unsigned by_category = (stack_top[i].type - EXPLORE_TYPE_FIRSTCATEGORY);
- if (stack_top[i].type == EXPLORE_TYPE_SEARCH) clear_find_text = false;
- if (by_category >= EXPLORE_CAT_COUNT) continue;
+ clear_find_text = (current_type != EXPLORE_TYPE_SEARCH);
+ explore_state->title[0] = '\0';
- unsigned by_selected_type = stack_top[i + 1].type;
- const char* name = explore_by_info[by_category].name;
- explore_string_t **entries = explore_state->by[by_category];
- explore_append_title("%s%s: %s", (levels++ ? " / " : ""), name, (by_selected_type != EXPLORE_TYPE_FILTERNULL ? entries[by_selected_type - EXPLORE_TYPE_FIRSTITEM]->str : "Unknown"));
- }
+ for (i = 1; i < depth; i++)
+ {
+ unsigned by_selected_type;
+ explore_string_t **entries;
+ const char* name = NULL;
+ unsigned by_category = (stack_top[i].type -
+ EXPLORE_TYPE_FIRSTCATEGORY);
- if (clear_find_text)
- explore_state->find_string[0] = '\0';
+ if (stack_top[i].type == EXPLORE_TYPE_SEARCH)
+ clear_find_text = false;
+ if (by_category >= EXPLORE_CAT_COUNT)
+ continue;
- if (*explore_state->find_string)
- explore_append_title(" '%s'", explore_state->find_string);
- }
+ by_selected_type = stack_top[i + 1].type;
+ name = explore_by_info[by_category].name;
+ entries = explore_state->by[by_category];
+ explore_append_title("%s%s: %s", (levels++ ? " / " : ""), name, (by_selected_type != EXPLORE_TYPE_FILTERNULL ? entries[by_selected_type - EXPLORE_TYPE_FIRSTITEM]->str : "Unknown"));
+ }
- void playlist_set_cached(playlist_t* pl);
- playlist_set_cached(NULL);
+ if (clear_find_text)
+ explore_state->find_string[0] = '\0';
- if (current_type == MENU_EXPLORE_TAB || current_type == EXPLORE_TYPE_ADDITIONALFILTER)
- {
- // Explore top or selecting an additional filter
- bool is_top = (current_type == MENU_EXPLORE_TAB);
- if (is_top) strlcpy(explore_state->title, "Explore", sizeof(explore_state->title));
- else explore_append_title(" - Additional Filter");
+ if (*explore_state->find_string)
+ explore_append_title(" '%s'", explore_state->find_string);
+ }
- if (is_top || !*explore_state->find_string)
- {
- explore_menu_entry(list, "Search Name ...", EXPLORE_TYPE_SEARCH)->action_ok = explore_action_ok_find;
- explore_menu_add_spacer(list);
- }
+ playlist_set_cached(NULL);
- for (unsigned cat = 0; cat < EXPLORE_CAT_COUNT; cat++)
- {
- const char* name = explore_by_info[cat].name;
- explore_string_t **entries = explore_state->by[cat];
- if (!ex_buf_len(entries)) continue;
+ if ( current_type == MENU_EXPLORE_TAB
+ || current_type == EXPLORE_TYPE_ADDITIONALFILTER)
+ {
+ /* Explore top or selecting an additional filter */
+ bool is_top = (current_type == MENU_EXPLORE_TAB);
+ if (is_top)
+ strlcpy(explore_state->title, "Explore", sizeof(explore_state->title));
+ else
+ explore_append_title(" - Additional Filter");
- for (unsigned i = 1; i < depth; i++)
- if (stack_top[i].type == cat + EXPLORE_TYPE_FIRSTCATEGORY)
- goto SKIP_EXPLORE_BY_CATEGORY;
+ if (is_top || !*explore_state->find_string)
+ {
+ explore_menu_entry(list, "Search Name ...",
+ EXPLORE_TYPE_SEARCH)->action_ok = explore_action_ok_find;
+ explore_menu_add_spacer(list);
+ }
- if (!is_top)
- snprintf(tmp, sizeof(tmp), "By %s", name);
- else if (explore_by_info[cat].is_numeric)
- snprintf(tmp, sizeof(tmp), "By %s (%s - %s)", name, entries[0]->str, entries[ex_buf_len(entries) - 1]->str);
- else
- snprintf(tmp, sizeof(tmp), "By %s (%u entries)", name, (unsigned)ex_buf_len(entries));
- explore_menu_entry(list, tmp, cat + EXPLORE_TYPE_FIRSTCATEGORY);
+ for (cat = 0; cat < EXPLORE_CAT_COUNT; cat++)
+ {
+ const char *name = explore_by_info[cat].name;
+ explore_string_t **entries = explore_state->by[cat];
+ if (!EX_BUF_LEN(entries))
+ continue;
- SKIP_EXPLORE_BY_CATEGORY:;
- }
+ for (i = 1; i < depth; i++)
+ if (stack_top[i].type == cat + EXPLORE_TYPE_FIRSTCATEGORY)
+ goto SKIP_EXPLORE_BY_CATEGORY;
- if (is_top)
- {
- explore_menu_add_spacer(list);
- explore_menu_entry(list, "Show All", EXPLORE_TYPE_SHOWALL);
- }
- }
- else if (depth == 1 && current_type != EXPLORE_TYPE_SEARCH && current_type != EXPLORE_TYPE_SHOWALL)
- {
- // List all items in a selected explore by category
- explore_string_t **entries = explore_state->by[current_cat];
- for (int i = 0, i_last = ex_buf_len(entries) - 1; i <= i_last; i++)
- explore_menu_entry(list, entries[i]->str, EXPLORE_TYPE_FIRSTITEM + i);
- if (explore_state->has_unknown[current_cat])
- {
- explore_menu_add_spacer(list);
- explore_menu_entry(list, "Unknown", EXPLORE_TYPE_FILTERNULL);
- }
- explore_append_title("Select %s", explore_by_info[current_cat].name);
- }
- else if (previous_cat < EXPLORE_CAT_COUNT || current_type < EXPLORE_TYPE_FIRSTITEM)
- {
- unsigned levels = 0;
- unsigned cats[10];
- explore_string_t* filter[10];
- bool use_split[10];
- bool use_find = (*explore_state->find_string != '\0');
+ if (!is_top)
+ snprintf(tmp, sizeof(tmp), "By %s", name);
+ else if (explore_by_info[cat].is_numeric)
+ snprintf(tmp, sizeof(tmp), "By %s (%s - %s)", name, entries[0]->str, entries[EX_BUF_LEN(entries) - 1]->str);
+ else
+ snprintf(tmp, sizeof(tmp), "By %s (%u entries)", name,
+ (unsigned)EX_BUF_LEN(entries));
+ explore_menu_entry(list, tmp, cat + EXPLORE_TYPE_FIRSTCATEGORY);
- bool is_show_all = (depth == 1 && !use_find);
- bool is_filtered_category = (current_cat < EXPLORE_CAT_COUNT);
- bool filtered_category_have_unknown = false;
+SKIP_EXPLORE_BY_CATEGORY:;
+ }
- if (is_filtered_category)
- {
- // List filtered items in a selected explore by category
- explore_append_title(" - Select %s", explore_by_info[current_cat].name);
- }
- else
- {
- // Game list
- if (is_show_all)
- {
- explore_append_title("All");
- explore_menu_entry(list, "Search Name ...", EXPLORE_TYPE_SEARCH)->action_ok = explore_action_ok_find;
- }
- else
- explore_menu_entry(list, "Add Additional Filter", EXPLORE_TYPE_ADDITIONALFILTER);
- explore_menu_add_spacer(list);
- }
+ if (is_top)
+ {
+ explore_menu_add_spacer(list);
+ explore_menu_entry(list, "Show All", EXPLORE_TYPE_SHOWALL);
+ }
+ }
+ else if (
+ depth == 1
+ && current_type != EXPLORE_TYPE_SEARCH
+ && current_type != EXPLORE_TYPE_SHOWALL)
+ {
+ /* List all items in a selected explore by category */
+ explore_string_t **entries = explore_state->by[current_cat];
+ unsigned i_last = EX_BUF_LEN(entries) - 1;
+ for (i = 0; i <= i_last; i++)
+ explore_menu_entry(list, entries[i]->str, EXPLORE_TYPE_FIRSTITEM + i);
+ if (explore_state->has_unknown[current_cat])
+ {
+ explore_menu_add_spacer(list);
+ explore_menu_entry(list, "Unknown", EXPLORE_TYPE_FILTERNULL);
+ }
+ explore_append_title("Select %s", explore_by_info[current_cat].name);
+ }
+ else if (
+ previous_cat < EXPLORE_CAT_COUNT
+ || current_type < EXPLORE_TYPE_FIRSTITEM)
+ {
+ bool use_split[10];
+ unsigned cats[10];
+ explore_string_t* filter[10];
+ explore_entry_t *e = NULL;
+ explore_entry_t *e_end = NULL;
+ ex_hashmap32 map_filtered_category = {0};
+ unsigned levels = 0;
+ bool use_find = (
+ *explore_state->find_string != '\0');
- for (unsigned i = 1; i < depth; i++)
- {
- unsigned by_category = (stack_top[i].type - EXPLORE_TYPE_FIRSTCATEGORY);
- if (by_category >= EXPLORE_CAT_COUNT) continue;
+ bool is_show_all = (depth == 1 && !use_find);
+ bool is_filtered_category = (current_cat < EXPLORE_CAT_COUNT);
+ bool filtered_category_have_unknown = false;
- unsigned by_selected_type = stack_top[i + 1].type;
- explore_string_t **entries = explore_state->by[by_category];
- cats [levels] = by_category;
- use_split[levels] = explore_by_info[by_category].use_split;
- filter [levels] = (by_selected_type == EXPLORE_TYPE_FILTERNULL ? NULL : entries[by_selected_type - EXPLORE_TYPE_FIRSTITEM]);
- levels++;
- }
+ /* List filtered items in a selected explore by category */
+ if (is_filtered_category)
+ explore_append_title(" - Select %s",
+ explore_by_info[current_cat].name);
+ else
+ {
+ /* Game list */
+ if (is_show_all)
+ {
+ explore_append_title("All");
+ explore_menu_entry(list, "Search Name ...",
+ EXPLORE_TYPE_SEARCH)->action_ok = explore_action_ok_find;
+ }
+ else
+ explore_menu_entry(list, "Add Additional Filter",
+ EXPLORE_TYPE_ADDITIONALFILTER);
+ explore_menu_add_spacer(list);
+ }
- ex_hashmap32 map_filtered_category = {0};
- for (explore_entry_t *e = explore_state->entries, *e_end = ex_buf_end(explore_state->entries); e != e_end; e++)
- {
- for (unsigned lvl = 0; lvl != levels; lvl++)
- {
- if (filter[lvl] == e->by[cats[lvl]]) continue;
- if (use_split[lvl] && e->split)
- {
- explore_string_t** split = e->split;
- do { if (*split == filter[lvl]) break; } while (*(++split));
- if (*split) continue;
- }
- goto SKIP_ENTRY;
- }
+ for (i = 1; i < depth; i++)
+ {
+ explore_string_t **entries;
+ unsigned by_selected_type = 0;
+ unsigned by_category = (stack_top[i].type
+ - EXPLORE_TYPE_FIRSTCATEGORY);
- if (use_find && !strcasestr(e->playlist_entry->label, explore_state->find_string))
- goto SKIP_ENTRY;
+ if (by_category >= EXPLORE_CAT_COUNT)
+ continue;
- if (is_filtered_category)
- {
- explore_string_t* str = e->by[current_cat];
- if (!str) { filtered_category_have_unknown = true; continue; }
- if (ex_hashmap32_getnum(&map_filtered_category, str->idx + 1)) continue;
- ex_hashmap32_setnum(&map_filtered_category, str->idx + 1, 1);
- explore_menu_entry(list, str->str, EXPLORE_TYPE_FIRSTITEM + str->idx);
- }
- else
- {
- explore_menu_entry(list, (e->original_title ? e->original_title : e->playlist_entry->label), EXPLORE_TYPE_FIRSTITEM + (e - explore_state->entries));
- }
+ by_selected_type = stack_top[i + 1].type;
+ entries = explore_state->by[by_category];
+ cats [levels] = by_category;
+ use_split[levels] = explore_by_info[by_category].use_split;
+ filter [levels] =
+ (by_selected_type == EXPLORE_TYPE_FILTERNULL
+ ? NULL
+ : entries[by_selected_type - EXPLORE_TYPE_FIRSTITEM]);
+ levels++;
+ }
- SKIP_ENTRY:;
- }
+ e = explore_state->entries;
+ e_end = EX_BUF_END(explore_state->entries);
+ for (; e != e_end; e++)
+ {
+ unsigned lvl;
+ for (lvl = 0; lvl != levels; lvl++)
+ {
+ if (filter[lvl] == e->by[cats[lvl]])
+ continue;
+ if (use_split[lvl] && e->split)
+ {
+ explore_string_t** split = e->split;
+ do {
+ if (*split == filter[lvl])
+ break;
+ }while(*(++split));
+ if (*split)
+ continue;
+ }
+ goto SKIP_ENTRY;
+ }
- if (is_filtered_category)
- qsort(list->list, list->size, sizeof(*list->list), explore_qsort_func_menulist);
+ if (use_find && !strcasestr(e->playlist_entry->label, explore_state->find_string))
+ goto SKIP_ENTRY;
- if (is_filtered_category && filtered_category_have_unknown)
- {
- explore_menu_add_spacer(list);
- explore_menu_entry(list, "Unknown", EXPLORE_TYPE_FILTERNULL);
- }
+ if (is_filtered_category)
+ {
+ explore_string_t* str = e->by[current_cat];
+ if (!str)
+ {
+ filtered_category_have_unknown = true;
+ continue;
+ }
+ if (ex_hashmap32_getnum(&map_filtered_category, str->idx + 1))
+ continue;
+ ex_hashmap32_setnum(&map_filtered_category, str->idx + 1, 1);
+ explore_menu_entry(list, str->str, EXPLORE_TYPE_FIRSTITEM + str->idx);
+ }
+ else
+ {
+ explore_menu_entry(list, (e->original_title ? e->original_title : e->playlist_entry->label), EXPLORE_TYPE_FIRSTITEM + (e - explore_state->entries));
+ }
- explore_append_title(" (%u)", (unsigned) (list->size - 1));
- }
- else
- {
- // Content page of selected game
- const struct playlist_entry *pl_entry = explore_state->entries[current_type - EXPLORE_TYPE_FIRSTITEM].playlist_entry;
+SKIP_ENTRY:;
+ }
- snprintf(explore_state->title, sizeof(explore_state->title), "%s", pl_entry->label);
+ if (is_filtered_category)
+ qsort(list->list, list->size, sizeof(*list->list), explore_qsort_func_menulist);
- for (int pl_idx = 0; pl_idx != ex_buf_len(explore_state->playlists); pl_idx++)
- {
- playlist_t* pl = explore_state->playlists[pl_idx];
- const struct playlist_entry* pl_first;
- playlist_get_index(pl, 0, &pl_first);
- if (pl_entry < pl_first || pl_entry >= pl_first + playlist_size(pl))
- continue;
+ if (is_filtered_category && filtered_category_have_unknown)
+ {
+ explore_menu_add_spacer(list);
+ explore_menu_entry(list, "Unknown", EXPLORE_TYPE_FILTERNULL);
+ }
- // Fake all the state so the content screen and information screen think we're viewing via playlist
- playlist_set_cached(pl);
- menu_handle_t *menu = menu_driver_get_ptr();
- menu->rpl_entry_selection_ptr = (pl_entry - pl_first);
- strlcpy(menu->deferred_path, pl_entry->path, sizeof(menu->deferred_path));
+ explore_append_title(" (%u)", (unsigned) (list->size - 1));
+ }
+ else
+ {
+ /* Content page of selected game */
+ int pl_idx;
+ const struct playlist_entry *pl_entry = explore_state->entries[current_type - EXPLORE_TYPE_FIRSTITEM].playlist_entry;
- menu_displaylist_info_t info = {0};
- info.list = list;
- menu_displaylist_ctl(DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, &info);
- break;
- }
- }
+ snprintf(explore_state->title,
+ sizeof(explore_state->title), "%s", pl_entry->label);
- return list->size;
+ for (pl_idx = 0; pl_idx != EX_BUF_LEN(explore_state->playlists); pl_idx++)
+ {
+ const struct playlist_entry* pl_first = NULL;
+ playlist_t *pl =
+ explore_state->playlists[pl_idx];
+ menu_handle_t *menu = menu_driver_get_ptr();
+ menu_displaylist_info_t info = {0};
+
+ playlist_get_index(pl, 0, &pl_first);
+
+ if ( pl_entry < pl_first ||
+ pl_entry >= pl_first + playlist_size(pl))
+ continue;
+
+ /* Fake all the state so the content screen
+ * and information screen think we're viewing via playlist */
+ playlist_set_cached(pl);
+ menu->rpl_entry_selection_ptr = (pl_entry - pl_first);
+ strlcpy(menu->deferred_path,
+ pl_entry->path, sizeof(menu->deferred_path));
+ info.list = list;
+ menu_displaylist_ctl(DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, &info);
+ break;
+ }
+ }
+
+ return list->size;
}
diff --git a/playlist.c b/playlist.c
index c0a66e6552..294fb687b7 100644
--- a/playlist.c
+++ b/playlist.c
@@ -92,6 +92,13 @@ typedef int (playlist_sort_fun_t)(
const struct playlist_entry *a,
const struct playlist_entry *b);
+/* TODO/FIXME - hack for allowing the explore view to switch
+ * over to a playlist item */
+void playlist_set_cached(playlist_t* pl)
+{
+ playlist_cached = pl;
+}
+
/* Convenience function: copies specified playlist
* path to specified playlist configuration object */
void playlist_config_set_path(playlist_config_t *config, const char *path)
@@ -2994,8 +3001,3 @@ core_info_t *playlist_get_default_core_info(playlist_t* playlist)
return NULL;
}
-// hack for allowing the explore view to switch over to a playlist item
-void playlist_set_cached(playlist_t* pl)
-{
- playlist_cached = pl;
-}
diff --git a/playlist.h b/playlist.h
index 1a7a73d3a2..72388ad249 100644
--- a/playlist.h
+++ b/playlist.h
@@ -345,6 +345,8 @@ core_info_t *playlist_entry_get_core_info(const struct playlist_entry* entry);
* default core association */
core_info_t *playlist_get_default_core_info(playlist_t* playlist);
+void playlist_set_cached(playlist_t* pl);
+
RETRO_END_DECLS
#endif