diff --git a/config.def.h b/config.def.h
index fcecd91b55..49f9035842 100644
--- a/config.def.h
+++ b/config.def.h
@@ -243,6 +243,15 @@
#endif
#define DEFAULT_CHECK_FIRMWARE_BEFORE_LOADING false
+/* Specifies whether to cache core info
+ * into a single (compressed) file for improved
+ * load times on platforms with slow IO */
+#if defined(_3DS)
+#define DEFAULT_CORE_INFO_CACHE_ENABLE true
+#else
+#define DEFAULT_CORE_INFO_CACHE_ENABLE false
+#endif
+
/* Specifies whether to 'reload' (fork and quit)
* RetroArch when launching content with the
* currently loaded core
diff --git a/configuration.c b/configuration.c
index 0ef261d057..ef622c9c9c 100644
--- a/configuration.c
+++ b/configuration.c
@@ -1485,6 +1485,7 @@ static struct config_bool_setting *populate_settings_bool(
SETTING_BOOL("input_descriptor_hide_unbound", &settings->bools.input_descriptor_hide_unbound, true, input_descriptor_hide_unbound, false);
SETTING_BOOL("load_dummy_on_core_shutdown", &settings->bools.load_dummy_on_core_shutdown, true, DEFAULT_LOAD_DUMMY_ON_CORE_SHUTDOWN, false);
SETTING_BOOL("check_firmware_before_loading", &settings->bools.check_firmware_before_loading, true, DEFAULT_CHECK_FIRMWARE_BEFORE_LOADING, false);
+ SETTING_BOOL("core_info_cache_enable", &settings->bools.core_info_cache_enable, true, DEFAULT_CORE_INFO_CACHE_ENABLE, false);
#ifndef HAVE_DYNAMIC
SETTING_BOOL("always_reload_core_on_run_content", &settings->bools.always_reload_core_on_run_content, true, DEFAULT_ALWAYS_RELOAD_CORE_ON_RUN_CONTENT, false);
#endif
diff --git a/configuration.h b/configuration.h
index c5b715b705..3dcdf9a8ee 100644
--- a/configuration.h
+++ b/configuration.h
@@ -777,6 +777,7 @@ typedef struct settings
bool network_remote_enable_user[MAX_USERS];
bool load_dummy_on_core_shutdown;
bool check_firmware_before_loading;
+ bool core_info_cache_enable;
#ifndef HAVE_DYNAMIC
bool always_reload_core_on_run_content;
#endif
diff --git a/core_info.c b/core_info.c
index f40fd037fa..721c759aa8 100644
--- a/core_info.c
+++ b/core_info.c
@@ -15,11 +15,14 @@
* If not, see .
*/
+#include
#include
#include
#include
#include
#include
+#include
+#include
#include
#include
@@ -28,6 +31,7 @@
#endif
#include "retroarch.h"
+#include "verbosity.h"
#include "core_info.h"
#include "file_path_special.h"
@@ -40,23 +44,1108 @@
#include "play_feature_delivery/play_feature_delivery.h"
#endif
-#define CORE_INFO_CACHE_FILE_NAME ".cache"
+/*************************/
+/* Core Info Cache START */
+/*************************/
+
+#define CORE_INFO_CACHE_DEFAULT_CAPACITY 8
typedef struct
{
+ core_info_t *items;
+ size_t length;
+ size_t capacity;
+ bool refresh;
+} core_info_cache_list_t;
+
+typedef struct
+{
+ core_info_t *core_info;
+ core_info_cache_list_t *core_info_cache_list;
char **current_string_val;
struct string_list **current_string_list_val;
uint32_t *current_entry_uint_val;
bool *current_entry_bool_val;
- bool to_core_file_id;
- bool to_firmware;
- core_info_t *core_info;
- core_info_cache_list_t *core_info_cache_list;
-
unsigned array_depth;
unsigned object_depth;
+ bool to_core_file_id;
+ bool to_firmware;
} CCJSONContext;
+/* Forward declarations */
+static void core_info_free(core_info_t* info);
+static uint32_t core_info_hash_string(const char *str);
+static core_info_cache_list_t *core_info_cache_list_new(void);
+static void core_info_cache_add(core_info_cache_list_t *list, core_info_t *info,
+ bool transfer);
+
+/* JSON Handlers START */
+
+static bool CCJSONObjectMemberHandler(void *context, const char *pValue, size_t length)
+{
+ CCJSONContext *pCtx = (CCJSONContext *)context;
+
+ if ((pCtx->object_depth == 2) && (pCtx->array_depth == 1) && length)
+ {
+ pCtx->current_string_val = NULL;
+ pCtx->current_string_list_val = NULL;
+ pCtx->current_entry_uint_val = NULL;
+ pCtx->current_entry_bool_val = NULL;
+ pCtx->to_core_file_id = false;
+ pCtx->to_firmware = false;
+
+ switch (pValue[0])
+ {
+ case 'a':
+ if (string_is_equal(pValue, "authors"))
+ {
+ pCtx->current_string_val = &pCtx->core_info->authors;
+ pCtx->current_string_list_val = &pCtx->core_info->authors_list;
+ }
+ break;
+ case 'c':
+ if (string_is_equal(pValue, "categories"))
+ {
+ pCtx->current_string_val = &pCtx->core_info->categories;
+ pCtx->current_string_list_val = &pCtx->core_info->categories_list;
+ }
+ else if (string_is_equal(pValue, "core_name"))
+ pCtx->current_string_val = &pCtx->core_info->core_name;
+ else if (string_is_equal(pValue, "core_file_id"))
+ pCtx->to_core_file_id = true;
+ break;
+ case 'd':
+ if (string_is_equal(pValue, "display_name"))
+ pCtx->current_string_val = &pCtx->core_info->display_name;
+ else if (string_is_equal(pValue, "display_version"))
+ pCtx->current_string_val = &pCtx->core_info->display_version;
+ else if (string_is_equal(pValue, "databases"))
+ {
+ pCtx->current_string_val = &pCtx->core_info->databases;
+ pCtx->current_string_list_val = &pCtx->core_info->databases_list;
+ }
+ else if (string_is_equal(pValue, "description"))
+ pCtx->current_string_val = &pCtx->core_info->description;
+ else if (string_is_equal(pValue, "database_match_archive_member"))
+ pCtx->current_entry_bool_val = &pCtx->core_info->database_match_archive_member;
+ break;
+ case 'f':
+ if (string_is_equal(pValue, "firmware"))
+ pCtx->to_firmware = true;
+ break;
+ case 'h':
+ if (string_is_equal(pValue, "has_info"))
+ pCtx->current_entry_bool_val = &pCtx->core_info->has_info;
+ break;
+ case 'l':
+ if (string_is_equal(pValue, "licenses"))
+ {
+ pCtx->current_string_val = &pCtx->core_info->licenses;
+ pCtx->current_string_list_val = &pCtx->core_info->licenses_list;
+ }
+ else if (string_is_equal(pValue, "is_experimental"))
+ pCtx->current_entry_bool_val = &pCtx->core_info->is_experimental;
+ break;
+ case 'n':
+ if (string_is_equal(pValue, "notes"))
+ {
+ pCtx->current_string_val = &pCtx->core_info->notes;
+ pCtx->current_string_list_val = &pCtx->core_info->note_list;
+ }
+ break;
+ case 'p':
+ if (string_is_equal(pValue, "path"))
+ pCtx->current_string_val = &pCtx->core_info->path;
+ else if (string_is_equal(pValue, "permissions"))
+ {
+ pCtx->current_string_val = &pCtx->core_info->permissions;
+ pCtx->current_string_list_val = &pCtx->core_info->permissions_list;
+ }
+ break;
+ case 'r':
+ if (string_is_equal(pValue, "required_hw_api"))
+ {
+ pCtx->current_string_val = &pCtx->core_info->required_hw_api;
+ pCtx->current_string_list_val = &pCtx->core_info->required_hw_api_list;
+ }
+ break;
+ case 's':
+ if (string_is_equal(pValue, "system_manufacturer"))
+ pCtx->current_string_val = &pCtx->core_info->system_manufacturer;
+ else if (string_is_equal(pValue, "systemname"))
+ pCtx->current_string_val = &pCtx->core_info->systemname;
+ else if (string_is_equal(pValue, "system_id"))
+ pCtx->current_string_val = &pCtx->core_info->system_id;
+ else if (string_is_equal(pValue, "supported_extensions"))
+ {
+ pCtx->current_string_val = &pCtx->core_info->supported_extensions;
+ pCtx->current_string_list_val = &pCtx->core_info->supported_extensions_list;
+ }
+ else if (string_is_equal(pValue, "supports_no_game"))
+ pCtx->current_entry_bool_val = &pCtx->core_info->supports_no_game;
+ break;
+ }
+ }
+ else if ((pCtx->object_depth == 3) && (pCtx->array_depth == 1) && length)
+ {
+ pCtx->current_string_val = NULL;
+ pCtx->current_entry_uint_val = NULL;
+
+ if (pCtx->to_core_file_id)
+ {
+ if (string_is_equal(pValue, "str"))
+ pCtx->current_string_val = &pCtx->core_info->core_file_id.str;
+ else if (string_is_equal(pValue, "hash"))
+ pCtx->current_entry_uint_val = &pCtx->core_info->core_file_id.hash;
+ }
+ }
+ else if ((pCtx->object_depth == 3) && (pCtx->array_depth == 2) && length)
+ {
+ pCtx->current_string_val = NULL;
+ pCtx->current_entry_bool_val = NULL;
+
+ if (pCtx->to_firmware && (pCtx->core_info->firmware_count > 0))
+ {
+ size_t firmware_idx = pCtx->core_info->firmware_count - 1;
+
+ if (string_is_equal(pValue, "path"))
+ pCtx->current_string_val = &pCtx->core_info->firmware[firmware_idx].path;
+ else if (string_is_equal(pValue, "desc"))
+ pCtx->current_string_val = &pCtx->core_info->firmware[firmware_idx].desc;
+ else if (string_is_equal(pValue, "optional"))
+ pCtx->current_entry_bool_val = &pCtx->core_info->firmware[firmware_idx].optional;
+ }
+ }
+
+ return true;
+}
+
+static bool CCJSONStringHandler(void *context, const char *pValue, size_t length)
+{
+ CCJSONContext *pCtx = (CCJSONContext*)context;
+
+ if (pCtx->current_string_val && length && !string_is_empty(pValue))
+ {
+ if (*pCtx->current_string_val)
+ free(*pCtx->current_string_val);
+ *pCtx->current_string_val = strdup(pValue);
+
+ if (pCtx->current_string_list_val)
+ {
+ if (*pCtx->current_string_list_val)
+ string_list_free(*pCtx->current_string_list_val);
+ *pCtx->current_string_list_val = string_split(*pCtx->current_string_val, "|");
+ }
+ }
+
+ pCtx->current_string_val = NULL;
+ pCtx->current_string_list_val = NULL;
+
+ return true;
+}
+
+static bool CCJSONNumberHandler(void *context, const char *pValue, size_t length)
+{
+ CCJSONContext *pCtx = (CCJSONContext*)context;
+
+ if (pCtx->current_entry_uint_val)
+ *pCtx->current_entry_uint_val = string_to_unsigned(pValue);
+
+ pCtx->current_entry_uint_val = NULL;
+
+ return true;
+}
+
+static bool CCJSONBoolHandler(void *context, bool value)
+{
+ CCJSONContext *pCtx = (CCJSONContext *)context;
+
+ if (pCtx->current_entry_bool_val)
+ *pCtx->current_entry_bool_val = value;
+
+ pCtx->current_entry_bool_val = NULL;
+
+ return true;
+}
+
+static bool CCJSONStartObjectHandler(void *context)
+{
+ CCJSONContext *pCtx = (CCJSONContext*)context;
+
+ pCtx->object_depth++;
+
+ if ((pCtx->object_depth == 1) && (pCtx->array_depth == 0))
+ {
+ if (pCtx->core_info_cache_list)
+ return false;
+
+ pCtx->core_info_cache_list = core_info_cache_list_new();
+ if (!pCtx->core_info_cache_list)
+ return false;
+ }
+ else if ((pCtx->object_depth == 2) && (pCtx->array_depth == 1))
+ {
+ if (pCtx->core_info)
+ {
+ core_info_free(pCtx->core_info);
+ free(pCtx->core_info);
+ pCtx->core_info = NULL;
+ }
+
+ pCtx->core_info = (core_info_t*)calloc(1, sizeof(core_info_t));
+ if (!pCtx->core_info)
+ return false;
+ }
+ else if ((pCtx->object_depth == 3) && (pCtx->array_depth == 2))
+ {
+ if (pCtx->to_firmware)
+ {
+ size_t new_idx = pCtx->core_info->firmware_count;
+ core_info_firmware_t *firmware_tmp = (core_info_firmware_t*)
+ realloc(pCtx->core_info->firmware,
+ (pCtx->core_info->firmware_count + 1) * sizeof(core_info_firmware_t));
+
+ if (!firmware_tmp)
+ return false;
+
+ firmware_tmp[new_idx].path = NULL;
+ firmware_tmp[new_idx].desc = NULL;
+ firmware_tmp[new_idx].missing = false;
+ firmware_tmp[new_idx].optional = false;
+
+ pCtx->core_info->firmware = firmware_tmp;
+ pCtx->core_info->firmware_count++;
+ }
+ }
+
+ return true;
+}
+
+static bool CCJSONEndObjectHandler(void *context)
+{
+ CCJSONContext *pCtx = (CCJSONContext*)context;
+
+ if ((pCtx->object_depth == 2) && (pCtx->array_depth == 1) && pCtx->core_info)
+ {
+ core_info_cache_add(pCtx->core_info_cache_list, pCtx->core_info, true);
+ free(pCtx->core_info);
+ pCtx->core_info = NULL;
+ }
+ else if ((pCtx->object_depth == 3) && (pCtx->array_depth == 1))
+ pCtx->to_core_file_id = false;
+
+ retro_assert(pCtx->object_depth > 0);
+ pCtx->object_depth--;
+
+ return true;
+}
+
+static bool CCJSONStartArrayHandler(void *context)
+{
+ CCJSONContext *pCtx = (CCJSONContext*)context;
+ pCtx->array_depth++;
+ return true;
+}
+
+static bool CCJSONEndArrayHandler(void *context)
+{
+ CCJSONContext *pCtx = (CCJSONContext*)context;
+
+ if ((pCtx->object_depth == 2) && (pCtx->array_depth == 2))
+ pCtx->to_firmware = false;
+
+ retro_assert(pCtx->array_depth > 0);
+ pCtx->array_depth--;
+
+ return true;
+}
+
+/* JSON Handlers END */
+
+/* Note: 'dst' must be zero initialised, or memory
+ * leaks will occur */
+static void core_info_copy(core_info_t *src, core_info_t *dst)
+{
+ dst->path = src->path ? strdup(src->path) : NULL;
+ dst->display_name = src->display_name ? strdup(src->display_name) : NULL;
+ dst->display_version = src->display_version ? strdup(src->display_version) : NULL;
+ dst->core_name = src->core_name ? strdup(src->core_name) : NULL;
+ dst->system_manufacturer = src->system_manufacturer ? strdup(src->system_manufacturer) : NULL;
+ dst->systemname = src->systemname ? strdup(src->systemname) : NULL;
+ dst->system_id = src->system_id ? strdup(src->system_id) : NULL;
+ dst->supported_extensions = src->supported_extensions ? strdup(src->supported_extensions) : NULL;
+ dst->authors = src->authors ? strdup(src->authors) : NULL;
+ dst->permissions = src->permissions ? strdup(src->permissions) : NULL;
+ dst->licenses = src->licenses ? strdup(src->licenses) : NULL;
+ dst->categories = src->categories ? strdup(src->categories) : NULL;
+ dst->databases = src->databases ? strdup(src->databases) : NULL;
+ dst->notes = src->notes ? strdup(src->notes) : NULL;
+ dst->required_hw_api = src->required_hw_api ? strdup(src->required_hw_api) : NULL;
+ dst->description = src->description ? strdup(src->description) : NULL;
+
+ dst->categories_list = src->categories_list ? string_list_clone(src->categories_list) : NULL;
+ dst->databases_list = src->databases_list ? string_list_clone(src->databases_list) : NULL;
+ dst->note_list = src->note_list ? string_list_clone(src->note_list) : NULL;
+ dst->supported_extensions_list = src->supported_extensions_list ? string_list_clone(src->supported_extensions_list) : NULL;
+ dst->authors_list = src->authors_list ? string_list_clone(src->authors_list) : NULL;
+ dst->permissions_list = src->permissions_list ? string_list_clone(src->permissions_list) : NULL;
+ dst->licenses_list = src->licenses_list ? string_list_clone(src->licenses_list) : NULL;
+ dst->required_hw_api_list = src->required_hw_api_list ? string_list_clone(src->required_hw_api_list) : NULL;
+
+ if (src->firmware_count > 0)
+ {
+ dst->firmware = (core_info_firmware_t*)calloc(src->firmware_count,
+ sizeof(core_info_firmware_t));
+
+ if (dst->firmware)
+ {
+ size_t i;
+
+ dst->firmware_count = src->firmware_count;
+
+ for (i = 0; i < src->firmware_count; i++)
+ {
+ dst->firmware[i].path = src->firmware[i].path ? strdup(src->firmware[i].path) : NULL;
+ dst->firmware[i].desc = src->firmware[i].desc ? strdup(src->firmware[i].desc) : NULL;
+ dst->firmware[i].missing = src->firmware[i].missing;
+ dst->firmware[i].optional = src->firmware[i].optional;
+ }
+ }
+ else
+ dst->firmware_count = 0;
+ }
+
+ dst->core_file_id.str = src->core_file_id.str ? strdup(src->core_file_id.str) : NULL;
+ dst->core_file_id.hash = src->core_file_id.hash;
+
+ dst->has_info = src->has_info;
+ dst->supports_no_game = src->supports_no_game;
+ dst->database_match_archive_member = src->database_match_archive_member;
+ dst->is_experimental = src->is_experimental;
+ dst->is_locked = src->is_locked;
+ dst->is_installed = src->is_installed;
+}
+
+/* Like core_info_copy, but transfers 'ownership'
+ * of internal objects/data structures from 'src'
+ * to 'dst' */
+static void core_info_transfer(core_info_t *src, core_info_t *dst)
+{
+ dst->path = src->path;
+ src->path = NULL;
+
+ dst->display_name = src->display_name;
+ src->display_name = NULL;
+
+ dst->display_version = src->display_version;
+ src->display_version = NULL;
+
+ dst->core_name = src->core_name;
+ src->core_name = NULL;
+
+ dst->system_manufacturer = src->system_manufacturer;
+ src->system_manufacturer = NULL;
+
+ dst->systemname = src->systemname;
+ src->systemname = NULL;
+
+ dst->system_id = src->system_id;
+ src->system_id = NULL;
+
+ dst->supported_extensions = src->supported_extensions;
+ src->supported_extensions = NULL;
+
+ dst->authors = src->authors;
+ src->authors = NULL;
+
+ dst->permissions = src->permissions;
+ src->permissions = NULL;
+
+ dst->licenses = src->licenses;
+ src->licenses = NULL;
+
+ dst->categories = src->categories;
+ src->categories = NULL;
+
+ dst->databases = src->databases;
+ src->databases = NULL;
+
+ dst->notes = src->notes;
+ src->notes = NULL;
+
+ dst->required_hw_api = src->required_hw_api;
+ src->required_hw_api = NULL;
+
+ dst->description = src->description;
+ src->description = NULL;
+
+ dst->categories_list = src->categories_list;
+ src->categories_list = NULL;
+
+ dst->databases_list = src->databases_list;
+ src->databases_list = NULL;
+
+ dst->note_list = src->note_list;
+ src->note_list = NULL;
+
+ dst->supported_extensions_list = src->supported_extensions_list;
+ src->supported_extensions_list = NULL;
+
+ dst->authors_list = src->authors_list;
+ src->authors_list = NULL;
+
+ dst->permissions_list = src->permissions_list;
+ src->permissions_list = NULL;
+
+ dst->licenses_list = src->licenses_list;
+ src->licenses_list = NULL;
+
+ dst->required_hw_api_list = src->required_hw_api_list;
+ src->required_hw_api_list = NULL;
+
+ dst->firmware = src->firmware;
+ dst->firmware_count = src->firmware_count;
+ src->firmware = NULL;
+ src->firmware_count = 0;
+
+ dst->core_file_id.str = src->core_file_id.str;
+ src->core_file_id.str = NULL;
+ dst->core_file_id.hash = src->core_file_id.hash;
+
+ dst->has_info = src->has_info;
+ dst->supports_no_game = src->supports_no_game;
+ dst->database_match_archive_member = src->database_match_archive_member;
+ dst->is_experimental = src->is_experimental;
+ dst->is_locked = src->is_locked;
+ dst->is_installed = src->is_installed;
+}
+
+static void core_info_cache_list_free(core_info_cache_list_t *core_info_cache_list)
+{
+ size_t i;
+
+ if (!core_info_cache_list)
+ return;
+
+ for (i = 0; i < core_info_cache_list->length; i++)
+ {
+ core_info_t* info = (core_info_t*)&core_info_cache_list->items[i];
+ core_info_free(info);
+ }
+
+ free(core_info_cache_list->items);
+ free(core_info_cache_list);
+}
+
+static core_info_cache_list_t *core_info_cache_list_new(void)
+{
+ core_info_cache_list_t *core_info_cache_list = NULL;
+
+ core_info_cache_list = (core_info_cache_list_t *)malloc(sizeof(*core_info_cache_list));
+ if (!core_info_cache_list)
+ return NULL;
+
+ core_info_cache_list->length = 0;
+ core_info_cache_list->items = (core_info_t *)calloc(CORE_INFO_CACHE_DEFAULT_CAPACITY,
+ sizeof(core_info_t));
+
+ if (!core_info_cache_list->items)
+ {
+ core_info_cache_list_free(core_info_cache_list);
+ return NULL;
+ }
+
+ core_info_cache_list->capacity = CORE_INFO_CACHE_DEFAULT_CAPACITY;
+ core_info_cache_list->refresh = false;
+
+ return core_info_cache_list;
+}
+
+static core_info_t *core_info_cache_find(core_info_cache_list_t *list, char *core_file_id)
+{
+ uint32_t hash;
+ size_t i;
+
+ if (!list ||
+ string_is_empty(core_file_id))
+ return NULL;
+
+ hash = core_info_hash_string(core_file_id);
+
+ for (i = 0; i < list->length; i++)
+ {
+ core_info_t *info = (core_info_t*)&list->items[i];
+
+ if (!info)
+ continue;
+
+ if ((info->core_file_id.hash == hash) &&
+ string_is_equal(info->core_file_id.str, core_file_id))
+ {
+ info->is_installed = true;
+ return info;
+ }
+ }
+
+ return NULL;
+}
+
+static void core_info_cache_add(core_info_cache_list_t *list, core_info_t *info,
+ bool transfer)
+{
+ core_info_t *info_cache = NULL;
+
+ if (!list ||
+ !info ||
+ (info->core_file_id.hash == 0) ||
+ string_is_empty(info->core_file_id.str))
+ return;
+
+ if (list->length >= list->capacity)
+ {
+ size_t prev_capacity = list->capacity;
+ core_info_t *items_tmp = (core_info_t*)realloc(list->items,
+ (list->capacity << 1) * sizeof(core_info_t));
+
+ if (!items_tmp)
+ return;
+
+ list->capacity = list->capacity << 1;
+ list->items = items_tmp;
+
+ memset(&list->items[prev_capacity], 0,
+ (list->capacity - prev_capacity) * sizeof(core_info_t));
+ }
+
+ info_cache = (core_info_t*)&list->items[list->length];
+
+ if (transfer)
+ core_info_transfer(info, info_cache);
+ else
+ core_info_copy(info, info_cache);
+
+ list->length++;
+}
+
+static core_info_cache_list_t *core_info_cache_read(const char *info_dir)
+{
+ intfstream_t *file = NULL;
+ rjson_t *parser = NULL;
+ CCJSONContext context = {0};
+ core_info_cache_list_t *core_info_cache_list = NULL;
+ char file_path[PATH_MAX_LENGTH];
+
+ /* Check whether a 'force refresh' file
+ * is present */
+ file_path[0] = '\0';
+
+ if (string_is_empty(info_dir))
+ strlcpy(file_path, FILE_PATH_CORE_INFO_CACHE_REFRESH, sizeof(file_path));
+ else
+ fill_pathname_join(file_path, info_dir, FILE_PATH_CORE_INFO_CACHE_REFRESH,
+ sizeof(file_path));
+
+ if (path_is_valid(file_path))
+ return core_info_cache_list_new();
+
+ /* Open info cache file */
+ file_path[0] = '\0';
+
+ if (string_is_empty(info_dir))
+ strlcpy(file_path, FILE_PATH_CORE_INFO_CACHE, sizeof(file_path));
+ else
+ fill_pathname_join(file_path, info_dir, FILE_PATH_CORE_INFO_CACHE,
+ sizeof(file_path));
+
+#if defined(HAVE_ZLIB)
+ file = intfstream_open_rzip_file(file_path,
+ RETRO_VFS_FILE_ACCESS_READ);
+#else
+ file = intfstream_open_file(file_path,
+ RETRO_VFS_FILE_ACCESS_READ,
+ RETRO_VFS_FILE_ACCESS_HINT_NONE);
+#endif
+
+ if (!file)
+ return core_info_cache_list_new();
+
+ /* Parse info cache file */
+ parser = rjson_open_stream(file);
+ if (!parser)
+ {
+ RARCH_ERR("[Core Info] Failed to create JSON parser\n");
+ goto end;
+ }
+
+ rjson_set_options(parser,
+ RJSON_OPTION_ALLOW_UTF8BOM
+ | RJSON_OPTION_ALLOW_COMMENTS
+ | RJSON_OPTION_ALLOW_UNESCAPED_CONTROL_CHARACTERS
+ | RJSON_OPTION_REPLACE_INVALID_ENCODING);
+
+ if (rjson_parse(parser, &context,
+ CCJSONObjectMemberHandler,
+ CCJSONStringHandler,
+ CCJSONNumberHandler,
+ CCJSONStartObjectHandler,
+ CCJSONEndObjectHandler,
+ CCJSONStartArrayHandler,
+ CCJSONEndArrayHandler,
+ CCJSONBoolHandler,
+ NULL) /* Unused null handler */
+ != RJSON_DONE)
+ {
+ RARCH_WARN("[Core Info] Error parsing chunk:\n---snip---\n%.*s\n---snip---\n",
+ rjson_get_source_context_len(parser),
+ rjson_get_source_context_buf(parser));
+ RARCH_WARN("[Core Info] Error: Invalid JSON at line %d, column %d - %s.\n",
+ (int)rjson_get_source_line(parser),
+ (int)rjson_get_source_column(parser),
+ (*rjson_get_error(parser) ? rjson_get_error(parser) : "format error"));
+
+ /* Info cache is corrupt - discard it */
+ core_info_cache_list_free(context.core_info_cache_list);
+ core_info_cache_list = core_info_cache_list_new();
+ }
+ else
+ core_info_cache_list = context.core_info_cache_list;
+
+ rjson_free(parser);
+
+ /* Clean up leftovers in the event of
+ * a parsing error */
+ if (context.core_info)
+ {
+ core_info_free(context.core_info);
+ free(context.core_info);
+ }
+
+end:
+ intfstream_close(file);
+ free(file);
+
+ return core_info_cache_list;
+}
+
+static void core_info_cache_write(core_info_cache_list_t *list, const char *info_dir)
+{
+ intfstream_t *file = NULL;
+ rjsonwriter_t *writer = NULL;
+ char file_path[PATH_MAX_LENGTH];
+ size_t i, j;
+
+ file_path[0] = '\0';
+
+ if (!list)
+ return;
+
+ /* Open info cache file */
+ if (string_is_empty(info_dir))
+ strlcpy(file_path, FILE_PATH_CORE_INFO_CACHE, sizeof(file_path));
+ else
+ fill_pathname_join(file_path, info_dir, FILE_PATH_CORE_INFO_CACHE,
+ sizeof(file_path));
+
+ /* TODO/FIXME: Apparently rzip compression is an issue on UWP */
+#if defined(HAVE_ZLIB) && !(defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
+ file = intfstream_open_rzip_file(file_path,
+ RETRO_VFS_FILE_ACCESS_WRITE);
+#else
+ file = intfstream_open_file(file_path,
+ RETRO_VFS_FILE_ACCESS_WRITE,
+ RETRO_VFS_FILE_ACCESS_HINT_NONE);
+#endif
+
+ if (!file)
+ {
+ RARCH_ERR("[Core Info] Failed to write to core info cache file: %s\n", file_path);
+ return;
+ }
+
+ /* Write info cache */
+ writer = rjsonwriter_open_stream(file);
+ if (!writer)
+ {
+ RARCH_ERR("[Core Info] Failed to create JSON writer\n");
+ goto end;
+ }
+
+ rjsonwriter_add_start_object(writer);
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_add_spaces(writer, 2);
+ rjsonwriter_add_string(writer, "version");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, "1.0");
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_add_spaces(writer, 2);
+ rjsonwriter_add_string(writer, "items");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_start_array(writer);
+ rjsonwriter_add_newline(writer);
+
+ for (i = 0; i < list->length; i++)
+ {
+ core_info_t* info = &list->items[i];
+
+ if (!info || !info->is_installed)
+ continue;
+
+ if (i > 0)
+ {
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+ }
+
+ rjsonwriter_add_spaces(writer, 4);
+ rjsonwriter_add_start_object(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "path");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->path);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "display_name");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->display_name);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "display_version");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->display_version);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "core_name");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->core_name);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "system_manufacturer");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->system_manufacturer);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "systemname");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->systemname);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "system_id");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->system_id);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "supported_extensions");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->supported_extensions);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "authors");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->authors);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "permissions");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->permissions);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "licenses");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->licenses);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "categories");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->categories);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "databases");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->databases);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "notes");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->notes);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "required_hw_api");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->required_hw_api);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "description");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->description);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ if (info->firmware_count > 0)
+ {
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "firmware");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_start_array(writer);
+ rjsonwriter_add_newline(writer);
+
+ for (j = 0; j < info->firmware_count; j++)
+ {
+ rjsonwriter_add_spaces(writer, 8);
+ rjsonwriter_add_start_object(writer);
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_add_spaces(writer, 10);
+ rjsonwriter_add_string(writer, "path");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->firmware[j].path);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_add_spaces(writer, 10);
+ rjsonwriter_add_string(writer, "desc");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->firmware[j].desc);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_add_spaces(writer, 10);
+ rjsonwriter_add_string(writer, "optional");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_bool(writer, info->firmware[j].optional);
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_add_spaces(writer, 8);
+ rjsonwriter_add_end_object(writer);
+
+ if (j < info->firmware_count - 1)
+ rjsonwriter_add_comma(writer);
+
+ rjsonwriter_add_newline(writer);
+ }
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_end_array(writer);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+ }
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "core_file_id");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_start_object(writer);
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_add_spaces(writer, 8);
+ rjsonwriter_add_string(writer, "str");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_string(writer, info->core_file_id.str);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_add_spaces(writer, 8);
+ rjsonwriter_add_string(writer, "hash");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_unsigned(writer, info->core_file_id.hash);
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_end_object(writer);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "firmware_count");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_unsigned(writer, info->firmware_count);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "has_info");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_bool(writer, info->has_info);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "supports_no_game");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_bool(writer, info->supports_no_game);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "database_match_archive_member");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_bool(writer, info->database_match_archive_member);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "is_experimental");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_bool(writer, info->is_experimental);
+ rjsonwriter_add_newline(writer);
+
+ rjsonwriter_add_spaces(writer, 4);
+ rjsonwriter_add_end_object(writer);
+ }
+
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_add_spaces(writer, 2);
+ rjsonwriter_add_end_array(writer);
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_add_end_object(writer);
+ rjsonwriter_add_newline(writer);
+ rjsonwriter_free(writer);
+
+ RARCH_LOG("[Core Info] Wrote to cache file: %s\n", file_path);
+
+ /* Remove 'force refresh' file, if required */
+ file_path[0] = '\0';
+
+ if (string_is_empty(info_dir))
+ strlcpy(file_path, FILE_PATH_CORE_INFO_CACHE_REFRESH, sizeof(file_path));
+ else
+ fill_pathname_join(file_path, info_dir, FILE_PATH_CORE_INFO_CACHE_REFRESH,
+ sizeof(file_path));
+
+ if (path_is_valid(file_path))
+ filestream_delete(file_path);
+
+end:
+ intfstream_close(file);
+ free(file);
+
+ list->refresh = false;
+}
+
+static void core_info_check_uninstalled(core_info_cache_list_t *list)
+{
+ size_t i;
+
+ if (!list)
+ return;
+
+ for (i = 0; i < list->length; i++)
+ {
+ core_info_t *info = (core_info_t *)&list->items[i];
+
+ if (!info)
+ continue;
+
+ if (!info->is_installed)
+ {
+ list->refresh = true;
+ return;
+ }
+ }
+}
+
+/* When called, generates a temporary file
+ * that will force an info cache refresh the
+ * next time that core info is initialised with
+ * caching enabled */
+bool core_info_cache_force_refresh(const char *path_info)
+{
+ char file_path[PATH_MAX_LENGTH];
+
+ file_path[0] = '\0';
+
+ /* Get 'force refresh' file path */
+ if (string_is_empty(path_info))
+ strlcpy(file_path, FILE_PATH_CORE_INFO_CACHE_REFRESH, sizeof(file_path));
+ else
+ fill_pathname_join(file_path, path_info, FILE_PATH_CORE_INFO_CACHE_REFRESH,
+ sizeof(file_path));
+
+ /* Generate a new, empty 'force refresh' file,
+ * if required */
+ if (!path_is_valid(file_path))
+ {
+ RFILE *refresh_file = filestream_open(
+ file_path,
+ RETRO_VFS_FILE_ACCESS_WRITE,
+ RETRO_VFS_FILE_ACCESS_HINT_NONE);
+
+ if (!refresh_file)
+ return false;
+
+ /* We have to write something - just output
+ * a single character */
+ if (filestream_putc(refresh_file, 0) != 0)
+ {
+ filestream_close(refresh_file);
+ return false;
+ }
+
+ filestream_close(refresh_file);
+ }
+
+ return true;
+}
+
+/***********************/
+/* Core Info Cache END */
+/***********************/
+
enum compare_op
{
COMPARE_OP_EQUAL = 0,
@@ -67,6 +1156,37 @@ enum compare_op
COMPARE_OP_GREATER_EQUAL
};
+typedef struct
+{
+ const char *path;
+ const char *filename;
+} core_file_path_t;
+
+typedef struct
+{
+ core_file_path_t *list;
+ size_t size;
+} core_file_path_list_t;
+
+typedef struct
+{
+ const char *filename;
+ uint32_t hash;
+} core_lock_file_path_t;
+
+typedef struct
+{
+ core_lock_file_path_t *list;
+ size_t size;
+} core_lock_file_path_list_t;
+
+typedef struct
+{
+ struct string_list *dir_list;
+ core_file_path_list_t *core_list;
+ core_lock_file_path_list_t *lock_list;
+} core_path_list_t;
+
static uint32_t core_info_hash_string(const char *str)
{
unsigned char c;
@@ -76,6 +1196,165 @@ static uint32_t core_info_hash_string(const char *str)
return (hash ? hash : 1);
}
+static void core_info_path_list_free(core_path_list_t *path_list)
+{
+ if (!path_list)
+ return;
+
+ if (path_list->core_list)
+ {
+ if (path_list->core_list->list)
+ free(path_list->core_list->list);
+ free(path_list->core_list);
+ }
+
+ if (path_list->lock_list)
+ {
+ if (path_list->lock_list->list)
+ free(path_list->lock_list->list);
+ free(path_list->lock_list);
+ }
+
+ if (path_list->dir_list)
+ string_list_free(path_list->dir_list);
+
+ free(path_list);
+}
+
+static core_path_list_t *core_info_path_list_new(const char *core_dir,
+ const char *core_ext, bool show_hidden_files)
+{
+ core_path_list_t *path_list = (core_path_list_t*)calloc(1, sizeof(*path_list));
+ bool dir_list_ok = false;
+ char exts[32];
+ size_t i;
+
+ exts[0] = '\0';
+
+ if (string_is_empty(core_ext) ||
+ !path_list)
+ goto error;
+
+ /* Allocate list containers */
+ path_list->dir_list = string_list_new();
+ path_list->core_list = (core_file_path_list_t*)calloc(1,
+ sizeof(*path_list->core_list));
+ path_list->lock_list = (core_lock_file_path_list_t*)calloc(1,
+ sizeof(*path_list->lock_list));
+
+ if (!path_list->dir_list ||
+ !path_list->core_list ||
+ !path_list->lock_list)
+ goto error;
+
+ /* Get list of file extensions to include
+ * (core + lock file) */
+ fill_pathname_join_delim(exts, core_ext, FILE_PATH_LOCK_EXTENSION_NO_DOT,
+ '|', sizeof(exts));
+
+ /* Fetch core directory listing */
+ dir_list_ok = dir_list_append(path_list->dir_list,
+ core_dir, exts, false, show_hidden_files,
+ false, false);
+
+#if defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
+ {
+ /* UWP: browse the optional packages for additional cores */
+ struct string_list core_packages = {0};
+
+ if (string_list_initialize(&core_packages))
+ {
+ uwp_fill_installed_core_packages(&core_packages);
+ for (i = 0; i < core_packages.size; i++)
+ dir_list_append(path_list->dir_list,
+ core_packages.elems[i].data, exts, false,
+ show_hidden_files, false, false);
+ string_list_deinitialize(&core_packages);
+ }
+ }
+#else
+ /* Keep the old 'directory not found' behaviour */
+ if (!dir_list_ok)
+ goto error;
+#endif
+
+ /* Allocate sub lists */
+ path_list->core_list->list = (core_file_path_t*)
+ malloc(path_list->dir_list->size *
+ sizeof(*path_list->core_list->list));
+ path_list->lock_list->list = (core_lock_file_path_t*)
+ malloc(path_list->dir_list->size *
+ sizeof(*path_list->lock_list->list));
+
+ if (!path_list->core_list->list ||
+ !path_list->lock_list->list)
+ goto error;
+
+ /* Parse directory listing */
+ for (i = 0; i < path_list->dir_list->size; i++)
+ {
+ const char *file_path = path_list->dir_list->elems[i].data;
+ const char *filename = NULL;
+ const char *file_ext = NULL;
+
+ if (string_is_empty(file_path) ||
+ !(filename = path_basename_nocompression(file_path)) ||
+ !(file_ext = path_get_extension(filename)))
+ continue;
+
+ /* Check whether this is a core or lock file */
+ if (string_is_equal(file_ext, core_ext))
+ {
+ path_list->core_list->list[
+ path_list->core_list->size].path = file_path;
+ path_list->core_list->list[
+ path_list->core_list->size].filename = filename;
+ path_list->core_list->size++;
+ }
+ else if (string_is_equal(file_ext, FILE_PATH_LOCK_EXTENSION_NO_DOT))
+ {
+ path_list->lock_list->list[
+ path_list->lock_list->size].filename = filename;
+ path_list->lock_list->list[
+ path_list->lock_list->size].hash = core_info_hash_string(filename);
+ path_list->lock_list->size++;
+ }
+ }
+
+ return path_list;
+
+error:
+ core_info_path_list_free(path_list);
+ return NULL;
+}
+
+static bool core_info_path_is_locked(core_lock_file_path_list_t *lock_list,
+ const char *core_file_name)
+{
+ size_t i;
+ uint32_t hash;
+ char lock_filename[256];
+
+ if (lock_list->size < 1)
+ return false;
+
+ strlcpy(lock_filename, core_file_name, sizeof(lock_filename));
+ strlcat(lock_filename, FILE_PATH_LOCK_EXTENSION, sizeof(lock_filename));
+
+ hash = core_info_hash_string(lock_filename);
+
+ for (i = 0; i < lock_list->size; i++)
+ {
+ core_lock_file_path_t *lock_file = &lock_list->list[i];
+
+ if ((lock_file->hash == hash) &&
+ string_is_equal(lock_file->filename, lock_filename))
+ return true;
+ }
+
+ return false;
+}
+
static bool core_info_get_file_id(const char *core_filename,
char *core_file_id, size_t len)
{
@@ -471,60 +1750,23 @@ static void core_info_list_free(core_info_list_t *core_info_list)
free(core_info_list);
}
-static void core_info_cache_list_free(core_info_cache_list_t *core_info_cache_list)
-{
- size_t i;
-
- if (!core_info_cache_list)
- return;
-
- for (i = 0; i < core_info_cache_list->length; i++)
- {
- core_info_t* info = (core_info_t*)&core_info_cache_list->items[i];
- core_info_free(info);
- }
-
- free(core_info_cache_list->items);
- free(core_info_cache_list);
-}
-
static core_info_list_t *core_info_list_new(const char *path,
const char *libretro_info_dir,
const char *exts,
- bool dir_show_hidden_files)
+ bool dir_show_hidden_files,
+ bool enable_cache)
{
size_t i;
- struct string_list contents = {0};
+ core_path_list_t *path_list = NULL;
core_info_t *core_info = NULL;
core_info_list_t *core_info_list = NULL;
core_info_cache_list_t *core_info_cache_list = NULL;
const char *info_dir = libretro_info_dir;
- bool ok = false;
- string_list_initialize(&contents);
-
- ok = dir_list_append(&contents, path, exts,
- false, dir_show_hidden_files, false, false);
-
-#if defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
- {
- /* UWP: browse the optional packages for additional cores */
- struct string_list core_packages = {0};
-
- if (string_list_initialize(&core_packages))
- {
- uwp_fill_installed_core_packages(&core_packages);
- for (i = 0; i < core_packages.size; i++)
- dir_list_append(&contents, core_packages.elems[i].data, exts,
- false, dir_show_hidden_files, false, false);
- string_list_deinitialize(&core_packages);
- }
- }
-#else
- /* Keep the old 'directory not found' behaviour */
- if (!ok)
+ path_list = core_info_path_list_new(path, exts,
+ dir_show_hidden_files);
+ if (!path_list)
goto error;
-#endif
core_info_list = (core_info_list_t*)malloc(sizeof(*core_info_list));
if (!core_info_list)
@@ -535,7 +1777,8 @@ static core_info_list_t *core_info_list_new(const char *path,
core_info_list->info_count = 0;
core_info_list->all_ext = NULL;
- core_info = (core_info_t*)calloc(contents.size, sizeof(*core_info));
+ core_info = (core_info_t*)calloc(path_list->core_list->size,
+ sizeof(*core_info));
if (!core_info)
{
@@ -544,43 +1787,55 @@ static core_info_list_t *core_info_list_new(const char *path,
}
core_info_list->list = core_info;
- core_info_list->count = contents.size;
+ core_info_list->count = path_list->core_list->size;
-#ifdef _3DS
- core_info_cache_list = core_info_read_cache_file(libretro_info_dir);
- if (!core_info_cache_list)
- goto error;
-#endif
-
- for (i = 0; i < contents.size; i++)
+ /* Read core info cache, if enabled */
+ if (enable_cache)
{
- core_info_t *info = &core_info[i];
- core_info_t *info_cache = NULL;
- const char *base_path = contents.elems[i].data;
- const char *core_filename = NULL;
- config_file_t *conf = NULL;
+ core_info_cache_list = core_info_cache_read(info_dir);
+ if (!core_info_cache_list)
+ goto error;
+ }
+
+ for (i = 0; i < path_list->core_list->size; i++)
+ {
+ core_info_t *info = &core_info[i];
+ core_file_path_t *core_file = &path_list->core_list->list[i];
+ const char *base_path = core_file->path;
+ const char *core_filename = core_file->filename;
+ config_file_t *conf = NULL;
char core_file_id[256];
core_file_id[0] = '\0';
- if (string_is_empty(base_path) ||
- !(core_filename = path_basename_nocompression(base_path)) ||
- !core_info_get_file_id(core_filename, core_file_id,
+ if (!core_info_get_file_id(core_filename, core_file_id,
sizeof(core_file_id)))
continue;
- info_cache = core_info_get_cache(core_info_cache_list, core_file_id);
- if (info_cache)
+ /* If info cache is available, search for
+ * current core */
+ if (core_info_cache_list)
{
- core_info_copy(info_cache, info);
- continue;
+ core_info_t *info_cache = core_info_cache_find(core_info_cache_list,
+ core_file_id);
+
+ if (info_cache)
+ {
+ core_info_copy(info_cache, info);
+ /* Core lock status is 'dynamic', and
+ * cannot be cached */
+ info->is_locked = core_info_path_is_locked(path_list->lock_list,
+ core_filename);
+ continue;
+ }
}
/* Cache core path */
info->path = strdup(base_path);
/* Get core lock status */
- info->is_locked = core_info_get_core_lock(info->path, false);
+ info->is_locked = core_info_path_is_locked(path_list->lock_list,
+ core_filename);
/* Cache core file 'id' */
info->core_file_id.str = strdup(core_file_id);
@@ -600,27 +1855,41 @@ static core_info_list_t *core_info_list_new(const char *path,
info->display_name = strdup(core_filename);
info->is_installed = true;
-#ifdef _3DS
- core_info_add_cache(core_info_cache_list, info);
- core_info_cache_list->refresh = true;
-#endif
+
+ /* If info cache is enabled and we reach this
+ * point, current core is uncached
+ * > Add it to the list, and trigger a cache
+ * refresh */
+ if (core_info_cache_list)
+ {
+ core_info_cache_add(core_info_cache_list, info, false);
+ core_info_cache_list->refresh = true;
+ }
}
-#ifdef _3DS
- core_info_check_uninstalled(core_info_cache_list);
- if (core_info_cache_list->refresh) {
- core_info_write_cache_file(core_info_cache_list, libretro_info_dir);
- }
-
- core_info_cache_list_free(core_info_cache_list);
-#endif
core_info_list_resolve_all_extensions(core_info_list);
- string_list_deinitialize(&contents);
+ /* If info cache is enabled
+ * > Check whether any cached cores have been
+ * uninstalled since the last run (triggers
+ * a refresh)
+ * > Write new cache to disk if updates are
+ * required */
+ if (core_info_cache_list)
+ {
+ core_info_check_uninstalled(core_info_cache_list);
+
+ if (core_info_cache_list->refresh)
+ core_info_cache_write(core_info_cache_list, info_dir);
+
+ core_info_cache_list_free(core_info_cache_list);
+ }
+
+ core_info_path_list_free(path_list);
return core_info_list;
error:
- string_list_deinitialize(&contents);
+ core_info_path_list_free(path_list);
return NULL;
}
@@ -804,13 +2073,14 @@ void core_info_deinit_list(void)
}
bool core_info_init_list(const char *path_info, const char *dir_cores,
- const char *exts, bool dir_show_hidden_files)
+ const char *exts, bool dir_show_hidden_files, bool enable_cache)
{
core_info_state_t *p_coreinfo = coreinfo_get_ptr();
if (!(p_coreinfo->curr_list = core_info_list_new(dir_cores,
!string_is_empty(path_info) ? path_info : dir_cores,
exts,
- dir_show_hidden_files)))
+ dir_show_hidden_files,
+ enable_cache)))
return false;
return true;
}
@@ -1649,805 +2919,3 @@ bool core_info_get_core_lock(const char *core_path, bool validate_path)
return is_locked;
}
-
-core_info_t *core_info_get_cache(core_info_cache_list_t *list, char *core_file_id)
-{
- size_t i;
- core_info_t *info = NULL;
-
- if (!list | !core_file_id)
- return NULL;
-
- for (i = 0; i < list->length; i++)
- {
- core_info_t *temp = (core_info_t*)&list->items[i];
- if (!temp)
- continue;
-
- if (string_is_equal(temp->core_file_id.str, core_file_id))
- {
- temp->is_installed = true;
- info = temp;
- break;
- }
- }
-
- return info;
-}
-
-void core_info_add_cache(core_info_cache_list_t *list, core_info_t *info)
-{
- if (!info->core_file_id.str)
- return;
-
- if (list->length >= list->capacity)
- {
- size_t prev_capacity = list->capacity;
- list->capacity = list->capacity * 2;
- list->items = (core_info_t*)realloc(list->items, list->capacity * sizeof(core_info_t));
- memset(&list->items[prev_capacity], 0, (list->capacity - prev_capacity) * sizeof(core_info_t));
- }
-
- core_info_t *cache = &list->items[list->length];
- core_info_copy(info, cache);
- list->length++;
-}
-
-static bool CCJSONObjectMemberHandler(void *context, const char *pValue, size_t length)
-{
- CCJSONContext *pCtx = (CCJSONContext *)context;
-
- if ((pCtx->object_depth == 2) && (pCtx->array_depth == 1) && length)
- {
- pCtx->current_string_val = NULL;
- pCtx->current_string_list_val = NULL;
- pCtx->current_entry_uint_val = NULL;
- pCtx->current_entry_bool_val = NULL;
- pCtx->to_core_file_id = false;
- pCtx->to_firmware = false;
-
- switch (pValue[0])
- {
- case 'a':
- if (string_is_equal(pValue, "authors"))
- {
- pCtx->current_string_val = &pCtx->core_info->authors;
- pCtx->current_string_list_val = &pCtx->core_info->authors_list;
- }
- break;
- case 'c':
- if (string_is_equal(pValue, "categories"))
- {
- pCtx->current_string_val = &pCtx->core_info->categories;
- pCtx->current_string_list_val = &pCtx->core_info->categories_list;
- }
- else if (string_is_equal(pValue, "core_name"))
- pCtx->current_string_val = &pCtx->core_info->core_name;
- else if (string_is_equal(pValue, "core_file_id"))
- pCtx->to_core_file_id = true;
- break;
- case 'd':
- if (string_is_equal(pValue, "display_name"))
- pCtx->current_string_val = &pCtx->core_info->display_name;
- else if (string_is_equal(pValue, "display_version"))
- pCtx->current_string_val = &pCtx->core_info->display_version;
- else if (string_is_equal(pValue, "databases"))
- {
- pCtx->current_string_val = &pCtx->core_info->databases;
- pCtx->current_string_list_val = &pCtx->core_info->databases_list;
- }
- else if (string_is_equal(pValue, "description"))
- pCtx->current_string_val = &pCtx->core_info->description;
- else if (string_is_equal(pValue, "database_match_archive_member"))
- pCtx->current_entry_bool_val = &pCtx->core_info->database_match_archive_member;
- break;
- case 'f':
- if (string_is_equal(pValue, "firmware"))
- pCtx->to_firmware = true;
- break;
- case 'h':
- if (string_is_equal(pValue, "has_info"))
- pCtx->current_entry_bool_val = &pCtx->core_info->has_info;
- break;
- case 'l':
- if (string_is_equal(pValue, "licenses"))
- {
- pCtx->current_string_val = &pCtx->core_info->licenses;
- pCtx->current_string_list_val = &pCtx->core_info->licenses_list;
- }
- else if (string_is_equal(pValue, "is_experimental"))
- pCtx->current_entry_bool_val = &pCtx->core_info->is_experimental;
- else if (string_is_equal(pValue, "is_locked"))
- pCtx->current_entry_bool_val = &pCtx->core_info->is_locked;
- break;
- case 'n':
- if (string_is_equal(pValue, "notes"))
- {
- pCtx->current_string_val = &pCtx->core_info->notes;
- pCtx->current_string_list_val = &pCtx->core_info->note_list;
- }
- break;
- case 'p':
- if (string_is_equal(pValue, "path"))
- pCtx->current_string_val = &pCtx->core_info->path;
- else if (string_is_equal(pValue, "permissions"))
- {
- pCtx->current_string_val = &pCtx->core_info->permissions;
- pCtx->current_string_list_val = &pCtx->core_info->permissions_list;
- }
- break;
- case 'r':
- if (string_is_equal(pValue, "required_hw_api"))
- {
- pCtx->current_string_val = &pCtx->core_info->required_hw_api;
- pCtx->current_string_list_val = &pCtx->core_info->required_hw_api_list;
- }
- break;
- case 's':
- if (string_is_equal(pValue, "system_manufacturer"))
- pCtx->current_string_val = &pCtx->core_info->system_manufacturer;
- else if (string_is_equal(pValue, "systemname"))
- pCtx->current_string_val = &pCtx->core_info->systemname;
- else if (string_is_equal(pValue, "system_id"))
- pCtx->current_string_val = &pCtx->core_info->system_id;
- else if (string_is_equal(pValue, "supported_extensions"))
- {
- pCtx->current_string_val = &pCtx->core_info->supported_extensions;
- pCtx->current_string_list_val = &pCtx->core_info->supported_extensions_list;
- }
- else if (string_is_equal(pValue, "supports_no_game"))
- pCtx->current_entry_bool_val = &pCtx->core_info->supports_no_game;
- break;
- }
- }
- else if ((pCtx->object_depth == 3) && (pCtx->array_depth == 1) && length)
- {
- if (pCtx->to_core_file_id)
- {
- if (string_is_equal(pValue, "str"))
- pCtx->current_string_val = &pCtx->core_info->core_file_id.str;
- else if (string_is_equal(pValue, "hash"))
- pCtx->current_entry_uint_val = &pCtx->core_info->core_file_id.hash;
- }
- }
- else if ((pCtx->object_depth == 3) && (pCtx->array_depth == 2) && length)
- {
- if (pCtx->to_firmware)
- {
- if (string_is_equal(pValue, "path"))
- pCtx->current_string_val = &pCtx->core_info->firmware[pCtx->core_info->firmware_count - 1].path;
- else if (string_is_equal(pValue, "desc"))
- pCtx->current_string_val = &pCtx->core_info->firmware[pCtx->core_info->firmware_count - 1].desc;
- else if (string_is_equal(pValue, "missing"))
- pCtx->current_entry_bool_val = &pCtx->core_info->firmware[pCtx->core_info->firmware_count - 1].missing;
- else if (string_is_equal(pValue, "optional"))
- pCtx->current_entry_bool_val = &pCtx->core_info->firmware[pCtx->core_info->firmware_count - 1].optional;
- }
- }
-
- return true;
-}
-
-static bool CCJSONStringHandler(void *context, const char *pValue, size_t length)
-{
- CCJSONContext *pCtx = (CCJSONContext*)context;
-
- if (pCtx->current_string_val)
- {
- *pCtx->current_string_val = strdup(pValue);
-
- if (pCtx->current_string_list_val)
- {
- *pCtx->current_string_list_val = string_split(*pCtx->current_string_val, "|");
- }
- }
-
- return true;
-}
-
-static bool CCJSONNumberHandler(void *context, const char *pValue, size_t length)
-{
- CCJSONContext *pCtx = (CCJSONContext*)context;
-
- if (pCtx->current_entry_uint_val)
- *pCtx->current_entry_uint_val = (unsigned)strtoul(pValue, NULL, 10);
-
- return true;
-}
-
-static bool CCJSONBoolHandler(void *context, bool value)
-{
- CCJSONContext *pCtx = (CCJSONContext *)context;
-
- if (pCtx->current_entry_bool_val)
- *pCtx->current_entry_bool_val = value;
-
- return true;
-}
-
-static bool CCJSONStartObjectHandler(void *context)
-{
- CCJSONContext *pCtx = (CCJSONContext*)context;
-
- pCtx->object_depth++;
-
- if ((pCtx->object_depth == 1) && (pCtx->array_depth == 0))
- {
- pCtx->core_info_cache_list = new_core_info_cache_list();
- if (!pCtx->core_info_cache_list)
- return false;
- }
- else if ((pCtx->object_depth == 2) && (pCtx->array_depth == 1))
- {
- pCtx->core_info = (core_info_t *)calloc(1, sizeof(core_info_t));
- if (!pCtx->core_info)
- return false;
- }
- else if ((pCtx->object_depth == 3) && (pCtx->array_depth == 2))
- {
- if (pCtx->to_firmware)
- {
- pCtx->core_info->firmware_count++;
- pCtx->core_info->firmware = (core_info_firmware_t *)realloc(pCtx->core_info->firmware, pCtx->core_info->firmware_count * sizeof(core_info_firmware_t));
- }
- }
-
- return true;
-}
-
-static bool CCJSONEndObjectHandler(void *context)
-{
- CCJSONContext *pCtx = (CCJSONContext*)context;
-
- if ((pCtx->object_depth == 2) && (pCtx->array_depth == 1))
- {
- core_info_add_cache(pCtx->core_info_cache_list, pCtx->core_info);
- core_info_free(pCtx->core_info);
- }
- else if ((pCtx->object_depth == 3) && (pCtx->array_depth == 1))
- {
- if (pCtx->to_core_file_id)
- pCtx->to_core_file_id = false;
- }
-
- retro_assert(pCtx->object_depth > 0);
- pCtx->object_depth--;
-
- return true;
-}
-
-static bool CCJSONStartArrayHandler(void *context)
-{
- CCJSONContext *pCtx = (CCJSONContext*)context;
-
- pCtx->array_depth++;
-
- return true;
-}
-
-static bool CCJSONEndArrayHandler(void *context)
-{
- CCJSONContext *pCtx = (CCJSONContext*)context;
-
- if ((pCtx->object_depth == 2) && (pCtx->array_depth == 2))
- {
- if (pCtx->to_firmware)
- pCtx->to_firmware = false;
- }
-
- retro_assert(pCtx->array_depth > 0);
- pCtx->array_depth--;
-
- return true;
-}
-
-core_info_cache_list_t *core_info_read_cache_file(const char *info_dir)
-{
- core_info_cache_list_t *core_info_cache_list;
- char file_path[PATH_MAX_LENGTH];
-
- core_info_cache_list = NULL;
-
- file_path[0] = '\0';
- fill_pathname_join(file_path, info_dir, CORE_INFO_CACHE_FILE_NAME, sizeof(file_path));
-
-#if defined(HAVE_ZLIB)
- /* Always use RZIP interface when reading playlists
- * > this will automatically handle uncompressed
- * data */
- intfstream_t *file = intfstream_open_rzip_file(
- file_path,
- RETRO_VFS_FILE_ACCESS_READ);
-#else
- intfstream_t *file = intfstream_open_file(
- playlist->config.path,
- RETRO_VFS_FILE_ACCESS_READ,
- RETRO_VFS_FILE_ACCESS_HINT_NONE);
-#endif
-
- if (file) {
- rjson_t *parser;
- CCJSONContext context = { 0 };
-
- parser = rjson_open_stream(file);
- if (!parser)
- {
- RARCH_ERR("Failed to create JSON parser\n");
- goto end;
- }
-
- rjson_set_options(parser,
- RJSON_OPTION_ALLOW_UTF8BOM
- | RJSON_OPTION_ALLOW_COMMENTS
- | RJSON_OPTION_ALLOW_UNESCAPED_CONTROL_CHARACTERS
- | RJSON_OPTION_REPLACE_INVALID_ENCODING);
-
- if (rjson_parse(parser, &context,
- CCJSONObjectMemberHandler,
- CCJSONStringHandler,
- CCJSONNumberHandler,
- CCJSONStartObjectHandler,
- CCJSONEndObjectHandler,
- CCJSONStartArrayHandler,
- CCJSONEndArrayHandler,
- CCJSONBoolHandler,
- NULL) // null
- != RJSON_DONE)
- {
- RARCH_WARN("Error parsing chunk:\n---snip---\n%.*s\n---snip---\n",
- rjson_get_source_context_len(parser),
- rjson_get_source_context_buf(parser));
- RARCH_WARN("Error: Invalid JSON at line %d, column %d - %s.\n",
- (int)rjson_get_source_line(parser),
- (int)rjson_get_source_column(parser),
- (*rjson_get_error(parser) ? rjson_get_error(parser) : "format error"));
- }
- core_info_cache_list = context.core_info_cache_list;
- rjson_free(parser);
- }
- else
- {
- core_info_cache_list = new_core_info_cache_list();
- }
-
-end:
- intfstream_close(file);
- free(file);
-
- return core_info_cache_list;
-}
-
-core_info_cache_list_t *new_core_info_cache_list(void)
-{
- const int default_cache_capacity = 8;
-
- core_info_cache_list_t *core_info_cache_list;
-
- core_info_cache_list = (core_info_cache_list_t *)malloc(sizeof(*core_info_cache_list));
- if (!core_info_cache_list)
- return NULL;
-
- core_info_cache_list->items = (core_info_t *)calloc(default_cache_capacity, sizeof(core_info_t));
- if (!core_info_cache_list->items)
- {
- core_info_cache_list_free(core_info_cache_list);
- return NULL;
- }
-
- core_info_cache_list->length = 0;
- core_info_cache_list->capacity = default_cache_capacity;
- core_info_cache_list->refresh = false;
-
- return core_info_cache_list;
-}
-
-void core_info_write_cache_file(core_info_cache_list_t *list, const char *info_dir)
-{
- size_t i, j;
-
- intfstream_t *file = NULL;
- rjsonwriter_t *writer;
- char file_path[PATH_MAX_LENGTH];
-
- file_path[0] = '\0';
-
- fill_pathname_join(file_path, info_dir, CORE_INFO_CACHE_FILE_NAME, sizeof(file_path));
-
- file = intfstream_open_file(file_path, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
- if (!file)
- {
- RARCH_ERR("Failed to write to core info cache file: %s\n", file_path);
- return;
- }
-
- writer = rjsonwriter_open_stream(file);
- if (!writer)
- {
- RARCH_ERR("Failed to create JSON writer\n");
- goto end;
- }
-
- rjsonwriter_add_start_object(writer);
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_spaces(writer, 2);
- rjsonwriter_add_string(writer, "version");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, "1.0");
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_spaces(writer, 2);
- rjsonwriter_add_string(writer, "items");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_start_array(writer);
- rjsonwriter_add_newline(writer);
-
- for (i = 0; i < list->length; i++)
- {
- core_info_t* info = &list->items[i];
- if (!info->is_installed)
- continue;
-
- if (i > 0)
- {
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
- }
-
- rjsonwriter_add_spaces(writer, 4);
- rjsonwriter_add_start_object(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "path");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->path);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "display_name");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->display_name);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "display_version");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->display_version);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "core_name");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->core_name);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "system_manufacturer");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->system_manufacturer);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "systemname");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->systemname);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "system_id");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->system_id);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "supported_extensions");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->supported_extensions);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "authors");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->authors);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "permissions");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->permissions);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "licenses");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->licenses);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "categories");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->categories);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "databases");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->databases);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "notes");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->notes);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "required_hw_api");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->required_hw_api);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "description");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->description);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- if (info->firmware_count)
- {
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "firmware");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_start_array(writer);
- rjsonwriter_add_newline(writer);
-
- for (j = 0; j < info->firmware_count; j++)
- {
- rjsonwriter_add_spaces(writer, 8);
- rjsonwriter_add_start_object(writer);
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_spaces(writer, 10);
- rjsonwriter_add_string(writer, "path");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->firmware[j].path);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_spaces(writer, 10);
- rjsonwriter_add_string(writer, "desc");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->firmware[j].desc);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_spaces(writer, 10);
- rjsonwriter_add_string(writer, "missing");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_bool(writer, info->firmware[j].missing);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_spaces(writer, 10);
- rjsonwriter_add_string(writer, "optional");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_bool(writer, info->firmware[j].optional);
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_spaces(writer, 8);
- rjsonwriter_add_end_object(writer);
-
- if (j < info->firmware_count - 1)
- rjsonwriter_add_comma(writer);
-
- rjsonwriter_add_newline(writer);
- }
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_end_array(writer);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
- }
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "core_file_id");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_start_object(writer);
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_spaces(writer, 8);
- rjsonwriter_add_string(writer, "str");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_string(writer, info->core_file_id.str);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_spaces(writer, 8);
- rjsonwriter_add_string(writer, "hash");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_unsigned(writer, info->core_file_id.hash);
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_end_object(writer);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "firmware_count");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_unsigned(writer, info->firmware_count);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "has_info");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_bool(writer, info->has_info);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "supports_no_game");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_bool(writer, info->supports_no_game);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "database_match_archive_member");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_bool(writer, info->database_match_archive_member);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "is_experimental");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_bool(writer, info->is_experimental);
- rjsonwriter_add_comma(writer);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 6);
- rjsonwriter_add_string(writer, "is_locked");
- rjsonwriter_add_colon(writer);
- rjsonwriter_add_space(writer);
- rjsonwriter_add_bool(writer, info->is_locked);
- rjsonwriter_add_newline(writer);
-
- rjsonwriter_add_spaces(writer, 4);
- rjsonwriter_add_end_object(writer);
- }
-
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_spaces(writer, 2);
- rjsonwriter_add_end_array(writer);
- rjsonwriter_add_newline(writer);
- rjsonwriter_add_end_object(writer);
- rjsonwriter_add_newline(writer);
- rjsonwriter_free(writer);
-
- RARCH_LOG("[CoreInfo]: Written to cache file: %s\n", file_path);
-end:
- intfstream_close(file);
- free(file);
-
- list->refresh = false;
-}
-
-void core_info_copy(core_info_t *src, core_info_t *dst)
-{
- size_t i;
-
- dst->path = src->path ? strdup(src->path) : NULL;
- dst->display_name = src->display_name ? strdup(src->display_name) : NULL;
- dst->display_version = src->display_version ? strdup(src->display_version) : NULL;
- dst->core_name = src->core_name ? strdup(src->core_name) : NULL;
- dst->system_manufacturer = src->system_manufacturer ? strdup(src->system_manufacturer) : NULL;
- dst->systemname = src->systemname ? strdup(src->systemname) : NULL;
- dst->system_id = src->system_id ? strdup(src->system_id) : NULL;
- dst->supported_extensions = src->supported_extensions ? strdup(src->supported_extensions) : NULL;
- dst->authors = src->authors ? strdup(src->authors) : NULL;
- dst->permissions = src->permissions ? strdup(src->permissions) : NULL;
- dst->licenses = src->licenses ? strdup(src->licenses) : NULL;
- dst->categories = src->categories ? strdup(src->categories) : NULL;
- dst->databases = src->databases ? strdup(src->databases) : NULL;
- dst->notes = src->notes ? strdup(src->notes) : NULL;
- dst->required_hw_api = src->required_hw_api ? strdup(src->required_hw_api) : NULL;
- dst->description = src->description ? strdup(src->description) : NULL;
- dst->categories_list = src->categories_list ? string_list_clone(src->categories_list) : NULL;
- dst->databases_list = src->databases_list ? string_list_clone(src->databases_list) : NULL;
- dst->note_list = src->note_list ? string_list_clone(src->note_list) : NULL;
- dst->supported_extensions_list = src->supported_extensions_list ? string_list_clone(src->supported_extensions_list) : NULL;
- dst->authors_list = src->authors_list ? string_list_clone(src->authors_list) : NULL;
- dst->permissions_list = src->permissions_list ? string_list_clone(src->permissions_list) : NULL;
- dst->licenses_list = src->licenses_list ? string_list_clone(src->licenses_list) : NULL;
- dst->required_hw_api_list = src->required_hw_api_list ? string_list_clone(src->required_hw_api_list) : NULL;
-
- if (src->firmware_count)
- {
- dst->firmware = (core_info_firmware_t*)calloc(src->firmware_count, sizeof(core_info_firmware_t));
- if (!dst->firmware)
- return;
-
- for (i = 0; i < src->firmware_count; i++)
- {
- dst->firmware[i].path = src->firmware[i].path ? strdup(src->firmware[i].path) : NULL;
- dst->firmware[i].desc = src->firmware[i].desc ? strdup(src->firmware[i].desc) : NULL;
- dst->firmware[i].missing = src->firmware[i].missing;
- dst->firmware[i].optional = src->firmware[i].optional;
- }
- }
-
- dst->core_file_id.str = src->core_file_id.str ? strdup(src->core_file_id.str) : NULL;
- dst->core_file_id.hash = src->core_file_id.hash;
-
- dst->firmware_count = src->firmware_count;
- dst->has_info = src->has_info;
- dst->supports_no_game = src->supports_no_game;
- dst->database_match_archive_member = src->database_match_archive_member;
- dst->is_experimental = src->is_experimental;
- dst->is_locked = src->is_locked;
- dst->is_installed = src->is_installed;
-}
-
-void core_info_check_uninstalled(core_info_cache_list_t *list)
-{
- size_t i;
-
- if (!list)
- return;
-
- for (i = 0; i < list->length; i++)
- {
- core_info_t *info = (core_info_t *)&list->items[i];
- if (!info)
- continue;
-
- if (!info->is_installed)
- {
- list->refresh = true;
- return;
- }
- }
-}
diff --git a/core_info.h b/core_info.h
index f264e6b2fb..7cb286abff 100644
--- a/core_info.h
+++ b/core_info.h
@@ -110,14 +110,6 @@ typedef struct
size_t info_count;
} core_info_list_t;
-typedef struct
-{
- core_info_t *items;
- size_t length;
- size_t capacity;
- bool refresh;
-} core_info_cache_list_t;
-
typedef struct core_info_ctx_firmware
{
const char *path;
@@ -165,7 +157,7 @@ bool core_info_get_current_core(core_info_t **core);
void core_info_deinit_list(void);
bool core_info_init_list(const char *path_info, const char *dir_cores,
- const char *exts, bool show_hidden_files);
+ const char *exts, bool show_hidden_files, bool enable_cache);
bool core_info_get_list(core_info_list_t **core);
@@ -212,13 +204,11 @@ core_info_state_t *coreinfo_get_ptr(void);
bool core_info_core_file_id_is_equal(const char *core_path_a, const char *core_path_b);
-core_info_t *core_info_get_cache(core_info_cache_list_t *list, char *core_file_id);
-void core_info_add_cache(core_info_cache_list_t *list, core_info_t *info);
-void core_info_copy(core_info_t *src, core_info_t *dst);
-void core_info_write_cache_file(core_info_cache_list_t *list, const char *info_dir);
-core_info_cache_list_t *core_info_read_cache_file(const char *info_dir);
-core_info_cache_list_t *new_core_info_cache_list(void);
-void core_info_check_uninstalled(core_info_cache_list_t *list);
+/* When called, generates a temporary file
+ * that will force an info cache refresh the
+ * next time that core info is initialised with
+ * caching enabled */
+bool core_info_cache_force_refresh(const char *path_info);
RETRO_END_DECLS
diff --git a/file_path_special.h b/file_path_special.h
index 8ce54e96dd..cb72bba45c 100644
--- a/file_path_special.h
+++ b/file_path_special.h
@@ -106,10 +106,13 @@ RETRO_BEGIN_DECLS
#define FILE_PATH_CORE_BACKUP_EXTENSION ".lcbk"
#define FILE_PATH_CORE_BACKUP_EXTENSION_NO_DOT "lcbk"
#define FILE_PATH_LOCK_EXTENSION ".lck"
+#define FILE_PATH_LOCK_EXTENSION_NO_DOT "lck"
#define FILE_PATH_BACKUP_EXTENSION ".bak"
#if defined(RARCH_MOBILE)
#define FILE_PATH_DEFAULT_OVERLAY "gamepads/neo-retropad/neo-retropad.cfg"
#endif
+#define FILE_PATH_CORE_INFO_CACHE "core_info.cache"
+#define FILE_PATH_CORE_INFO_CACHE_REFRESH "core_info.refresh"
enum application_special_type
{
diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h
index abe0714846..1812b4c699 100644
--- a/intl/msg_hash_lbl.h
+++ b/intl/msg_hash_lbl.h
@@ -1172,6 +1172,10 @@ MSG_HASH(
MENU_ENUM_LABEL_CHECK_FOR_MISSING_FIRMWARE,
"check_for_missing_firmware"
)
+MSG_HASH(
+ MENU_ENUM_LABEL_CORE_INFO_CACHE_ENABLE,
+ "core_info_cache_enable"
+ )
MSG_HASH(
MENU_ENUM_LABEL_DUMMY_ON_CORE_SHUTDOWN,
"dummy_on_core_shutdown"
diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h
index 57eabb8bff..92381cb3ef 100644
--- a/intl/msg_hash_us.h
+++ b/intl/msg_hash_us.h
@@ -2886,6 +2886,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_CHECK_FOR_MISSING_FIRMWARE,
"Check if all the required firmware is present before attempting to load content."
)
+MSG_HASH(
+ MENU_ENUM_LABEL_VALUE_CORE_INFO_CACHE_ENABLE,
+ "Cache Core Info Files"
+ )
+MSG_HASH(
+ MENU_ENUM_SUBLABEL_CORE_INFO_CACHE_ENABLE,
+ "Maintain a persistent local cache of installed core information. Greatly reduces loading times on platforms with slow disk access."
+ )
#ifndef HAVE_DYNAMIC
MSG_HASH(
MENU_ENUM_LABEL_VALUE_ALWAYS_RELOAD_CORE_ON_RUN_CONTENT,
diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c
index d97c5611f6..cb0404e252 100644
--- a/menu/cbs/menu_cbs_sublabel.c
+++ b/menu/cbs/menu_cbs_sublabel.c
@@ -357,6 +357,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_adaptive_vsync, MENU_
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_allow_rotate, MENU_ENUM_SUBLABEL_VIDEO_ALLOW_ROTATE)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_dummy_on_core_shutdown, MENU_ENUM_SUBLABEL_DUMMY_ON_CORE_SHUTDOWN)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_dummy_check_missing_firmware, MENU_ENUM_SUBLABEL_CHECK_FOR_MISSING_FIRMWARE)
+DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_info_cache_enable, MENU_ENUM_SUBLABEL_CORE_INFO_CACHE_ENABLE)
#ifndef HAVE_DYNAMIC
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_always_reload_core_on_run_content, MENU_ENUM_SUBLABEL_ALWAYS_RELOAD_CORE_ON_RUN_CONTENT)
#endif
@@ -3403,6 +3404,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_CHECK_FOR_MISSING_FIRMWARE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_dummy_check_missing_firmware);
break;
+ case MENU_ENUM_LABEL_CORE_INFO_CACHE_ENABLE:
+ BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_core_info_cache_enable);
+ break;
#ifndef HAVE_DYNAMIC
case MENU_ENUM_LABEL_ALWAYS_RELOAD_CORE_ON_RUN_CONTENT:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_always_reload_core_on_run_content);
diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c
index cc14b36a23..02df4ca3cf 100644
--- a/menu/menu_displaylist.c
+++ b/menu/menu_displaylist.c
@@ -8618,11 +8618,12 @@ unsigned menu_displaylist_build_list(
case DISPLAYLIST_CORE_SETTINGS_LIST:
{
menu_displaylist_build_info_t build_list[] = {
- {MENU_ENUM_LABEL_VIDEO_SHARED_CONTEXT, PARSE_ONLY_BOOL},
- {MENU_ENUM_LABEL_DRIVER_SWITCH_ENABLE, PARSE_ONLY_BOOL},
- {MENU_ENUM_LABEL_DUMMY_ON_CORE_SHUTDOWN, PARSE_ONLY_BOOL},
- {MENU_ENUM_LABEL_CHECK_FOR_MISSING_FIRMWARE, PARSE_ONLY_BOOL},
- {MENU_ENUM_LABEL_VIDEO_ALLOW_ROTATE, PARSE_ONLY_BOOL},
+ {MENU_ENUM_LABEL_VIDEO_SHARED_CONTEXT, PARSE_ONLY_BOOL},
+ {MENU_ENUM_LABEL_DRIVER_SWITCH_ENABLE, PARSE_ONLY_BOOL},
+ {MENU_ENUM_LABEL_DUMMY_ON_CORE_SHUTDOWN, PARSE_ONLY_BOOL},
+ {MENU_ENUM_LABEL_CHECK_FOR_MISSING_FIRMWARE, PARSE_ONLY_BOOL},
+ {MENU_ENUM_LABEL_VIDEO_ALLOW_ROTATE, PARSE_ONLY_BOOL},
+ {MENU_ENUM_LABEL_CORE_INFO_CACHE_ENABLE, PARSE_ONLY_BOOL},
#ifndef HAVE_DYNAMIC
{MENU_ENUM_LABEL_ALWAYS_RELOAD_CORE_ON_RUN_CONTENT, PARSE_ONLY_BOOL},
#endif
diff --git a/menu/menu_setting.c b/menu/menu_setting.c
index b6fd84cab3..ad305b9490 100644
--- a/menu/menu_setting.c
+++ b/menu/menu_setting.c
@@ -7809,6 +7809,20 @@ static void general_write_handler(rarch_setting_t *setting)
task_push_wifi_disable(NULL);
#endif
break;
+ case MENU_ENUM_LABEL_CORE_INFO_CACHE_ENABLE:
+ {
+ settings_t *settings = config_get_ptr();
+ const char *dir_libretro = settings->paths.directory_libretro;
+ const char *path_libretro_info = settings->paths.path_libretro_info;
+
+ /* When enabling the core info cache,
+ * force a cache refresh on the next
+ * core info initialisation */
+ if (*setting->value.target.boolean)
+ core_info_cache_force_refresh(!string_is_empty(path_libretro_info) ?
+ path_libretro_info : dir_libretro);
+ }
+ break;
default:
break;
}
@@ -9327,9 +9341,9 @@ static bool setting_append_list(
{
unsigned i, listing = 0;
#ifndef HAVE_DYNAMIC
- struct bool_entry bool_entries[7];
+ struct bool_entry bool_entries[8];
#else
- struct bool_entry bool_entries[6];
+ struct bool_entry bool_entries[7];
#endif
START_GROUP(list, list_info, &group_info,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_SETTINGS), parent_group);
@@ -9384,6 +9398,13 @@ static bool setting_append_list(
bool_entries[listing].flags = SD_FLAG_ADVANCED;
listing++;
+ bool_entries[listing].target = &settings->bools.core_info_cache_enable;
+ bool_entries[listing].name_enum_idx = MENU_ENUM_LABEL_CORE_INFO_CACHE_ENABLE;
+ bool_entries[listing].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_CORE_INFO_CACHE_ENABLE;
+ bool_entries[listing].default_value = DEFAULT_CORE_INFO_CACHE_ENABLE;
+ bool_entries[listing].flags = SD_FLAG_NONE;
+ listing++;
+
#ifndef HAVE_DYNAMIC
bool_entries[listing].target = &settings->bools.always_reload_core_on_run_content;
bool_entries[listing].name_enum_idx = MENU_ENUM_LABEL_ALWAYS_RELOAD_CORE_ON_RUN_CONTENT;
diff --git a/msg_hash.h b/msg_hash.h
index f04332229f..a0433b8cf5 100644
--- a/msg_hash.h
+++ b/msg_hash.h
@@ -2121,6 +2121,7 @@ enum msg_hash_enums
MENU_LABEL(DUMMY_ON_CORE_SHUTDOWN),
MENU_LABEL(CHECK_FOR_MISSING_FIRMWARE),
+ MENU_LABEL(CORE_INFO_CACHE_ENABLE),
#ifndef HAVE_DYNAMIC
MENU_LABEL(ALWAYS_RELOAD_CORE_ON_RUN_CONTENT),
#endif
diff --git a/retroarch.c b/retroarch.c
index ad9faf0c36..b82cc1c540 100644
--- a/retroarch.c
+++ b/retroarch.c
@@ -14131,6 +14131,7 @@ bool command_event(enum event_command cmd, void *data)
const char *dir_libretro = settings->paths.directory_libretro;
const char *path_libretro_info = settings->paths.path_libretro_info;
bool show_hidden_files = settings->bools.show_hidden_files;
+ bool core_info_cache_enable = settings->bools.core_info_cache_enable;
ext_name[0] = '\0';
@@ -14143,7 +14144,8 @@ bool command_event(enum event_command cmd, void *data)
core_info_init_list(path_libretro_info,
dir_libretro,
ext_name,
- show_hidden_files
+ show_hidden_files,
+ core_info_cache_enable
);
}
break;
diff --git a/samples/tasks/database/main.c b/samples/tasks/database/main.c
index a26f05236c..3756a5db6f 100644
--- a/samples/tasks/database/main.c
+++ b/samples/tasks/database/main.c
@@ -66,7 +66,7 @@ int main(int argc, char *argv[])
#else
task_queue_init(false /* threaded enable */, main_msg_queue_push);
#endif
- core_info_init_list(core_info_dir, core_dir, exts, true);
+ core_info_init_list(core_info_dir, core_dir, exts, true, false);
task_push_dbscan(playlist_dir, db_dir, input_dir, true,
true, main_db_cb);