mirror of
https://github.com/libretro/RetroArch
synced 2024-12-27 15:29:23 +00:00
Refactor directory listing to greatly optimize sorting.
This commit is contained in:
parent
031e37db9c
commit
14f7f641f3
33
driver.c
33
driver.c
@ -484,28 +484,31 @@ static void deinit_filter(void)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XML
|
||||
static void deinit_shader_dir(void)
|
||||
{
|
||||
// It handles NULL, no worries :D
|
||||
dir_list_free(g_extern.shader_dir.list);
|
||||
g_extern.shader_dir.list = NULL;
|
||||
g_extern.shader_dir.ptr = 0;
|
||||
}
|
||||
|
||||
static void init_shader_dir(void)
|
||||
{
|
||||
if (!*g_settings.video.shader_dir)
|
||||
return;
|
||||
|
||||
g_extern.shader_dir.elems = dir_list_new(g_settings.video.shader_dir, "shader", false);
|
||||
g_extern.shader_dir.size = dir_list_size(g_extern.shader_dir.elems);
|
||||
g_extern.shader_dir.ptr = 0;
|
||||
g_extern.shader_dir.list = dir_list_new(g_settings.video.shader_dir, "shader", false);
|
||||
if (g_extern.shader_dir.list->size == 0)
|
||||
{
|
||||
deinit_shader_dir();
|
||||
return;
|
||||
}
|
||||
|
||||
dir_list_sort(g_extern.shader_dir.elems, false);
|
||||
g_extern.shader_dir.ptr = 0;
|
||||
dir_list_sort(g_extern.shader_dir.list, false);
|
||||
|
||||
for (unsigned i = 0; i < g_extern.shader_dir.size; i++)
|
||||
RARCH_LOG("Found shader \"%s\"\n", g_extern.shader_dir.elems[i]);
|
||||
}
|
||||
|
||||
static void deinit_shader_dir(void)
|
||||
{
|
||||
// It handles NULL, no worries :D
|
||||
dir_list_free(g_extern.shader_dir.elems);
|
||||
g_extern.shader_dir.elems = NULL;
|
||||
g_extern.shader_dir.size = 0;
|
||||
g_extern.shader_dir.ptr = 0;
|
||||
for (unsigned i = 0; i < g_extern.shader_dir.list->size; i++)
|
||||
RARCH_LOG("Found shader \"%s\"\n", g_extern.shader_dir.list->elems[i].data);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
31
file.h
31
file.h
@ -36,13 +36,30 @@ void save_ram_file(const char *path, int type);
|
||||
|
||||
bool init_rom_file(enum rarch_game_type type);
|
||||
|
||||
// Returns a NULL-terminated list of files in a directory with full paths.
|
||||
// If ext is NULL, any file will be picked.
|
||||
// If non-NULL, only files with extension ext are added.
|
||||
char **dir_list_new(const char *dir, const char *ext, bool include_dirs);
|
||||
size_t dir_list_size(char * const *dir_list);
|
||||
void dir_list_sort(char **dir_list, bool dir_first);
|
||||
void dir_list_free(char **dir_list);
|
||||
// Yep, this is C alright ;)
|
||||
union string_list_elem_attr
|
||||
{
|
||||
bool b;
|
||||
int i;
|
||||
void *p;
|
||||
};
|
||||
|
||||
struct string_list_elem
|
||||
{
|
||||
char *data;
|
||||
union string_list_elem_attr attr;
|
||||
};
|
||||
|
||||
struct string_list
|
||||
{
|
||||
struct string_list_elem *elems;
|
||||
size_t size;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
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);
|
||||
void dir_list_free(struct string_list *list);
|
||||
|
||||
bool path_is_directory(const char *path);
|
||||
bool path_file_exists(const char *path);
|
||||
|
174
file_path.c
174
file_path.c
@ -43,111 +43,105 @@
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
// Yep, this is C alright ;)
|
||||
struct string_list
|
||||
static void string_list_free(struct string_list *list)
|
||||
{
|
||||
char **data;
|
||||
size_t size;
|
||||
size_t cap;
|
||||
};
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < list->size; i++)
|
||||
free(list->elems[i].data);
|
||||
free(list->elems);
|
||||
free(list);
|
||||
}
|
||||
|
||||
static bool string_list_capacity(struct string_list *list, size_t cap)
|
||||
{
|
||||
rarch_assert(cap > list->size);
|
||||
|
||||
char **new_data = (char**)realloc(list->data, cap * sizeof(char*));
|
||||
struct string_list_elem *new_data = (struct string_list_elem*)realloc(list->elems, cap * sizeof(*new_data));
|
||||
if (!new_data)
|
||||
return false;
|
||||
|
||||
list->data = new_data;
|
||||
list->cap = cap;
|
||||
list->elems = new_data;
|
||||
list->cap = cap;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool string_list_init(struct string_list *list)
|
||||
static struct string_list *string_list_new(void)
|
||||
{
|
||||
memset(list, 0, sizeof(*list));
|
||||
return string_list_capacity(list, 32);
|
||||
struct string_list *list = (struct string_list*)calloc(1, sizeof(*list));
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
if (!string_list_capacity(list, 32))
|
||||
{
|
||||
string_list_free(list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static bool string_list_append(struct string_list *list, const char *elem)
|
||||
static bool string_list_append(struct string_list *list, const char *elem, union string_list_elem_attr attr)
|
||||
{
|
||||
if (list->size + 1 >= list->cap && !string_list_capacity(list, list->cap * 2))
|
||||
if (list->size >= list->cap &&
|
||||
!string_list_capacity(list, list->cap * 2))
|
||||
return false;
|
||||
|
||||
if (!(list->data[list->size] = strdup(elem)))
|
||||
char *dup = strdup(elem);
|
||||
if (!dup)
|
||||
return false;
|
||||
|
||||
list->elems[list->size].data = dup;
|
||||
list->elems[list->size].attr = attr;
|
||||
|
||||
list->size++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static char **string_list_finalize(struct string_list *list)
|
||||
{
|
||||
rarch_assert(list->cap > list->size);
|
||||
|
||||
list->data[list->size] = NULL;
|
||||
return list->data;
|
||||
}
|
||||
|
||||
static void string_list_cleanup(struct string_list *list)
|
||||
{
|
||||
for (size_t i = 0; i < list->size; i++)
|
||||
free(list->data[i]);
|
||||
free(list->data);
|
||||
memset(list, 0, sizeof(*list));
|
||||
}
|
||||
|
||||
static void string_list_free(char **list)
|
||||
{
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
char **orig = list;
|
||||
while (*list)
|
||||
free(*list++);
|
||||
free(orig);
|
||||
}
|
||||
|
||||
static char **string_split(const char *str, const char *delim)
|
||||
static struct string_list *string_split(const char *str, const char *delim)
|
||||
{
|
||||
char *copy = NULL;
|
||||
const char *tmp = NULL;
|
||||
struct string_list list;
|
||||
|
||||
if (!string_list_init(&list))
|
||||
struct string_list *list = string_list_new();
|
||||
if (!list)
|
||||
goto error;
|
||||
|
||||
copy = strdup(str);
|
||||
if (!copy)
|
||||
return NULL;
|
||||
goto error;
|
||||
|
||||
tmp = strtok(copy, delim);
|
||||
while (tmp)
|
||||
{
|
||||
if (!string_list_append(&list, tmp))
|
||||
union string_list_elem_attr attr;
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
|
||||
if (!string_list_append(list, tmp, attr))
|
||||
goto error;
|
||||
|
||||
tmp = strtok(NULL, delim);
|
||||
}
|
||||
|
||||
free(copy);
|
||||
return string_list_finalize(&list);
|
||||
return list;
|
||||
|
||||
error:
|
||||
string_list_cleanup(&list);
|
||||
string_list_free(list);
|
||||
free(copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool string_list_find_elem(char * const *list, const char *elem)
|
||||
static bool string_list_find_elem(const struct string_list *list, const char *elem)
|
||||
{
|
||||
if (!list)
|
||||
return false;
|
||||
|
||||
for (; *list; list++)
|
||||
if (strcmp(*list, elem) == 0)
|
||||
for (size_t i = 0; i < list->size; i++)
|
||||
{
|
||||
if (strcmp(list->elems[i].data, elem) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -161,50 +155,42 @@ static const char *path_get_extension(const char *path)
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t dir_list_size(char * const *dir_list)
|
||||
static int qstrcmp_plain(const void *a_, const void *b_)
|
||||
{
|
||||
if (!dir_list)
|
||||
return 0;
|
||||
const struct string_list_elem *a = (const struct string_list_elem*)a_;
|
||||
const struct string_list_elem *b = (const struct string_list_elem*)b_;
|
||||
|
||||
size_t size = 0;
|
||||
while (*dir_list++)
|
||||
size++;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int qstrcmp_plain(const void *a, const void *b)
|
||||
{
|
||||
return strcasecmp(*(const char * const*)a, *(const char * const*)b);
|
||||
return strcasecmp(a->data, b->data);
|
||||
}
|
||||
|
||||
static int qstrcmp_dir(const void *a_, const void *b_)
|
||||
{
|
||||
const char *a = *(const char * const*)a_;
|
||||
const char *b = *(const char * const*)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_;
|
||||
|
||||
// Sort directories before files.
|
||||
int a_dir = path_is_directory(a);
|
||||
int b_dir = path_is_directory(b);
|
||||
int a_dir = a->attr.b;
|
||||
int b_dir = b->attr.b;
|
||||
if (a_dir != b_dir)
|
||||
return b_dir - a_dir;
|
||||
|
||||
return strcasecmp(a, b);
|
||||
else
|
||||
return strcasecmp(a->data, b->data);
|
||||
}
|
||||
|
||||
void dir_list_sort(char **dir_list, bool dir_first)
|
||||
void dir_list_sort(struct string_list *list, bool dir_first)
|
||||
{
|
||||
if (!dir_list)
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
qsort(dir_list, dir_list_size(dir_list), sizeof(char*), dir_first ? qstrcmp_dir : qstrcmp_plain);
|
||||
qsort(list->elems, list->size, sizeof(struct string_list_elem),
|
||||
dir_first ? qstrcmp_dir : qstrcmp_plain);
|
||||
}
|
||||
|
||||
#ifdef _WIN32 // Because the API is just fucked up ...
|
||||
char **dir_list_new(const char *dir, const char *ext, bool include_dirs)
|
||||
struct string_list *dir_list_new(const char *dir, const char *ext, bool include_dirs)
|
||||
{
|
||||
struct string_list list;
|
||||
if (!string_list_init(&list))
|
||||
struct string_list *list = string_list_new();
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
@ -213,7 +199,7 @@ char **dir_list_new(const char *dir, const char *ext, bool include_dirs)
|
||||
char path_buf[PATH_MAX];
|
||||
snprintf(path_buf, sizeof(path_buf), "%s\\*", dir);
|
||||
|
||||
char **ext_list = NULL;
|
||||
struct string_list *ext_list = NULL;
|
||||
if (ext)
|
||||
ext_list = string_split(ext, "|");
|
||||
|
||||
@ -236,35 +222,38 @@ char **dir_list_new(const char *dir, const char *ext, bool include_dirs)
|
||||
char file_path[PATH_MAX];
|
||||
snprintf(file_path, sizeof(file_path), "%s\\%s", dir, name);
|
||||
|
||||
if (!string_list_append(&list, file_path))
|
||||
union string_list_elem_attr attr;
|
||||
attr.b = is_dir;
|
||||
|
||||
if (!string_list_append(list, file_path, attr))
|
||||
goto error;
|
||||
}
|
||||
while (FindNextFile(hFind, &ffd) != 0);
|
||||
|
||||
FindClose(hFind);
|
||||
string_list_free(ext_list);
|
||||
return string_list_finalize(&list);
|
||||
return list;
|
||||
|
||||
error:
|
||||
RARCH_ERR("Failed to open directory: \"%s\"\n", dir);
|
||||
if (hFind != INVALID_HANDLE_VALUE)
|
||||
FindClose(hFind);
|
||||
|
||||
string_list_cleanup(&list);
|
||||
string_list_free(list);
|
||||
string_list_free(ext_list);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
char **dir_list_new(const char *dir, const char *ext, bool include_dirs)
|
||||
struct string_list *dir_list_new(const char *dir, const char *ext, bool include_dirs)
|
||||
{
|
||||
struct string_list list;
|
||||
if (!string_list_init(&list))
|
||||
struct string_list *list = string_list_new();
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
DIR *directory = NULL;
|
||||
const struct dirent *entry = NULL;
|
||||
|
||||
char **ext_list = NULL;
|
||||
struct string_list *ext_list = NULL;
|
||||
if (ext)
|
||||
ext_list = string_split(ext, "|");
|
||||
|
||||
@ -287,14 +276,17 @@ char **dir_list_new(const char *dir, const char *ext, bool include_dirs)
|
||||
char file_path[PATH_MAX];
|
||||
snprintf(file_path, sizeof(file_path), "%s/%s", dir, name);
|
||||
|
||||
if (!string_list_append(&list, file_path))
|
||||
union string_list_elem_attr attr;
|
||||
attr.b = is_dir;
|
||||
|
||||
if (!string_list_append(list, file_path, attr))
|
||||
goto error;
|
||||
}
|
||||
|
||||
closedir(directory);
|
||||
|
||||
string_list_free(ext_list);
|
||||
return string_list_finalize(&list);
|
||||
return list;
|
||||
|
||||
error:
|
||||
RARCH_ERR("Failed to open directory: \"%s\"\n", dir);
|
||||
@ -302,15 +294,15 @@ error:
|
||||
if (directory)
|
||||
closedir(directory);
|
||||
|
||||
string_list_cleanup(&list);
|
||||
string_list_free(list);
|
||||
string_list_free(ext_list);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
void dir_list_free(char **dir_list)
|
||||
void dir_list_free(struct string_list *list)
|
||||
{
|
||||
string_list_free(dir_list);
|
||||
string_list_free(list);
|
||||
}
|
||||
|
||||
bool path_is_directory(const char *path)
|
||||
|
@ -449,8 +449,7 @@ struct global
|
||||
|
||||
struct
|
||||
{
|
||||
char **elems;
|
||||
size_t size;
|
||||
struct string_list *list;
|
||||
size_t ptr;
|
||||
} shader_dir;
|
||||
|
||||
|
24
retroarch.c
24
retroarch.c
@ -1513,6 +1513,9 @@ static void set_savestate_auto_index(void)
|
||||
if (!g_settings.savestate_auto_index)
|
||||
return;
|
||||
|
||||
// Find the file in the same directory as g_extern.savestate_name with the largest numeral suffix.
|
||||
// E.g. /foo/path/game.state, will try to find /foo/path/game.state%d, where %d is the largest number available.
|
||||
|
||||
char state_path[PATH_MAX];
|
||||
strlcpy(state_path, g_extern.savestate_name, sizeof(state_path));
|
||||
|
||||
@ -1527,22 +1530,23 @@ static void set_savestate_auto_index(void)
|
||||
*split = '\0';
|
||||
base = split + 1;
|
||||
}
|
||||
else
|
||||
dir = ".";
|
||||
|
||||
unsigned max_index = 0;
|
||||
|
||||
char **dir_list = dir_list_new(dir, NULL, false);
|
||||
struct string_list *dir_list = dir_list_new(dir, NULL, false);
|
||||
if (!dir_list)
|
||||
return;
|
||||
|
||||
unsigned index = 0;
|
||||
const char *dir_elem;
|
||||
while ((dir_elem = dir_list[index++]))
|
||||
for (size_t i = 0; i < dir_list->size; i++)
|
||||
{
|
||||
if (!strstr(dir_elem, base))
|
||||
const char *dir_elem = dir_list->elems[i].data;
|
||||
if (strstr(dir_elem, base) != dir_elem)
|
||||
continue;
|
||||
|
||||
const char *end = dir_elem + strlen(dir_elem);
|
||||
while ((end != dir_elem) && isdigit(end[-1])) end--;
|
||||
while ((end > dir_elem) && isdigit(end[-1])) end--;
|
||||
|
||||
unsigned index = strtoul(end, NULL, 0);
|
||||
if (index > max_index)
|
||||
@ -2128,7 +2132,7 @@ static void check_shader_dir(void)
|
||||
static bool old_pressed_next = false;
|
||||
static bool old_pressed_prev = false;
|
||||
|
||||
if (!g_extern.shader_dir.elems || !driver.video->xml_shader)
|
||||
if (!g_extern.shader_dir.list || !driver.video->xml_shader)
|
||||
return;
|
||||
|
||||
bool should_apply = false;
|
||||
@ -2137,20 +2141,20 @@ static void check_shader_dir(void)
|
||||
if (pressed_next && !old_pressed_next)
|
||||
{
|
||||
should_apply = true;
|
||||
g_extern.shader_dir.ptr = (g_extern.shader_dir.ptr + 1) % g_extern.shader_dir.size;
|
||||
g_extern.shader_dir.ptr = (g_extern.shader_dir.ptr + 1) % g_extern.shader_dir.list->size;
|
||||
}
|
||||
else if (pressed_prev && !old_pressed_prev)
|
||||
{
|
||||
should_apply = true;
|
||||
if (g_extern.shader_dir.ptr == 0)
|
||||
g_extern.shader_dir.ptr = g_extern.shader_dir.size - 1;
|
||||
g_extern.shader_dir.ptr = g_extern.shader_dir.list->size - 1;
|
||||
else
|
||||
g_extern.shader_dir.ptr--;
|
||||
}
|
||||
|
||||
if (should_apply)
|
||||
{
|
||||
const char *shader = g_extern.shader_dir.elems[g_extern.shader_dir.ptr];
|
||||
const char *shader = g_extern.shader_dir.list->elems[g_extern.shader_dir.ptr].data;
|
||||
|
||||
strlcpy(g_settings.video.bsnes_shader_path, shader, sizeof(g_settings.video.bsnes_shader_path));
|
||||
g_settings.video.shader_type = RARCH_SHADER_BSNES;
|
||||
|
Loading…
Reference in New Issue
Block a user