diff --git a/core_info.c b/core_info.c index 648f5678e8..01a160460a 100644 --- a/core_info.c +++ b/core_info.c @@ -207,6 +207,8 @@ static void core_info_list_free(core_info_list_t *core_info_list) free(info->firmware[j].desc); } free(info->firmware); + + free(info->core_file_id.str); } free(core_info_list->all_ext); @@ -504,11 +506,43 @@ static core_info_list_t *core_info_list_new(const char *path, } if (!string_is_empty(base_path)) + { + const char *core_filename = path_basename(base_path); + + /* Cache core path */ core_info[i].path = strdup(base_path); - if (!core_info[i].display_name) - core_info[i].display_name = - strdup(path_basename(core_info[i].path)); + /* Cache core file 'id' + * > Filename without extension or platform-specific suffix */ + if (!string_is_empty(core_filename)) + { + char *core_file_id = strdup(core_filename); + path_remove_extension(core_file_id); + + if (!string_is_empty(core_file_id)) + { +#if defined(RARCH_MOBILE) || (defined(RARCH_CONSOLE) && !defined(PSP) && !defined(_3DS) && !defined(VITA) && !defined(HW_WUP)) + char *last_underscore = strrchr(core_file_id, '_'); + if (last_underscore) + *last_underscore = '\0'; +#endif + core_info[i].core_file_id.str = core_file_id; + core_info[i].core_file_id.len = strlen(core_file_id); + + core_file_id = NULL; + } + + if (core_file_id) + { + free(core_file_id); + core_file_id = NULL; + } + + /* Get fallback display name, if required */ + if (!core_info[i].display_name) + core_info[i].display_name = strdup(core_filename); + } + } } if (core_info_list) @@ -529,7 +563,13 @@ bool core_info_list_get_info(core_info_list_t *core_info_list, core_info_t *out_info, const char *path) { size_t i; - if (!core_info_list || !out_info) + const char *core_filename = NULL; + + if (!core_info_list || !out_info || string_is_empty(path)) + return false; + + core_filename = path_basename(path); + if (string_is_empty(core_filename)) return false; memset(out_info, 0, sizeof(*out_info)); @@ -538,8 +578,10 @@ bool core_info_list_get_info(core_info_list_t *core_info_list, { const core_info_t *info = &core_info_list->list[i]; - if (string_is_equal(path_basename(info->path), - path_basename(path))) + if (!info || (info->core_file_id.len == 0)) + continue; + + if (!strncmp(info->core_file_id.str, core_filename, info->core_file_id.len)) { *out_info = *info; return true; @@ -605,15 +647,23 @@ static core_info_t *core_info_find_internal( const char *core) { size_t i; - const char *core_path_basename = path_basename(core); + const char *core_filename = NULL; + + if (!list || string_is_empty(core)) + return NULL; + + core_filename = path_basename(core); + if (string_is_empty(core_filename)) + return NULL; for (i = 0; i < list->count; i++) { core_info_t *info = core_info_get(list, i); - if (!info || !info->path) + if (!info || (info->core_file_id.len == 0)) continue; - if (string_is_equal(path_basename(info->path), core_path_basename)) + + if (!strncmp(info->core_file_id.str, core_filename, info->core_file_id.len)) return info; } @@ -753,14 +803,18 @@ bool core_info_load(core_info_ctx_find_t *info) return true; } -bool core_info_find(core_info_ctx_find_t *info, const char *core_path) +bool core_info_find(core_info_ctx_find_t *info) { core_info_state_t *p_coreinfo = coreinfo_get_ptr(); + if (!info || !p_coreinfo->curr_list) return false; - info->inf = core_info_find_internal(p_coreinfo->curr_list, core_path); + + info->inf = core_info_find_internal(p_coreinfo->curr_list, info->path); + if (!info->inf) return false; + return true; } @@ -978,22 +1032,30 @@ bool core_info_list_get_display_name(core_info_list_t *core_info_list, const char *path, char *s, size_t len) { size_t i; + const char *core_filename = NULL; - if (!core_info_list) + if (!core_info_list || string_is_empty(path)) + return false; + + core_filename = path_basename(path); + if (string_is_empty(core_filename)) return false; for (i = 0; i < core_info_list->count; i++) { const core_info_t *info = &core_info_list->list[i]; - if (!string_is_equal(path_basename(info->path), path_basename(path))) + if (!info || (info->core_file_id.len == 0)) continue; - if (!info->display_name) - continue; + if (!strncmp(info->core_file_id.str, core_filename, info->core_file_id.len)) + { + if (string_is_empty(info->display_name)) + break; - strlcpy(s, info->display_name, len); - return true; + strlcpy(s, info->display_name, len); + return true; + } } return false; diff --git a/core_info.h b/core_info.h index 2a7f9a5329..cb352483a4 100644 --- a/core_info.h +++ b/core_info.h @@ -35,6 +35,23 @@ typedef struct bool optional; } core_info_firmware_t; +/* Simple container/convenience struct for + * holding the 'id' of a core file + * > 'id' is the filename without extension or + * platform-specific suffix + * > 'id' is used for core info searches - enables + * matching regardless of core file base path, + * and is platform-independent (e.g. an Android + * core file will be correctly identified on Linux) + * > 'len' is used to cache the length of 'str', for + * improved performance when performing string + * comparisons */ +typedef struct +{ + char *str; + size_t len; +} core_file_id_t; + typedef struct { bool supports_no_game; @@ -67,6 +84,7 @@ typedef struct struct string_list *licenses_list; struct string_list *required_hw_api_list; core_info_firmware_t *firmware; + core_file_id_t core_file_id; void *userdata; } core_info_t; @@ -152,7 +170,7 @@ bool core_info_get_list(core_info_list_t **core); bool core_info_list_update_missing_firmware(core_info_ctx_firmware_t *info, bool *set_missing_bios); -bool core_info_find(core_info_ctx_find_t *info, const char *name); +bool core_info_find(core_info_ctx_find_t *info); bool core_info_load(core_info_ctx_find_t *info); diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 401ab085d0..6861dc850d 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -462,7 +462,7 @@ static void menu_action_setting_disp_set_label_core_updater_entry( core_info.inf = NULL; core_info.path = entry->local_core_path; - if (core_info_find(&core_info, entry->local_core_path)) + if (core_info_find(&core_info)) { strlcpy(s, "[#]", len); *w = (unsigned)STRLEN_CONST("[#]"); diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index f4619a09da..5f2aadb804 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -373,16 +373,18 @@ static int action_left_video_resolution(unsigned type, const char *label, static int playlist_association_left(unsigned type, const char *label, bool wraparound) { - char core_path[PATH_MAX_LENGTH]; + char core_filename[PATH_MAX_LENGTH]; size_t i, next, current = 0; settings_t *settings = config_get_ptr(); bool playlist_use_old_format = settings->bools.playlist_use_old_format; bool playlist_compression = settings->bools.playlist_compression; playlist_t *playlist = playlist_get_cached(); + const char *default_core_path = playlist_get_default_core_path(playlist); + bool default_core_set = false; core_info_list_t *core_info_list = NULL; core_info_t *core_info = NULL; - core_path[0] = '\0'; + core_filename[0] = '\0'; if (!playlist) return -1; @@ -392,52 +394,62 @@ static int playlist_association_left(unsigned type, const char *label, return menu_cbs_exit(); /* Get current core path association */ - if (string_is_empty(playlist_get_default_core_path(playlist))) + if (!string_is_empty(default_core_path) && + !string_is_equal(default_core_path, "DETECT")) { - core_path[0] = 'D'; - core_path[1] = 'E'; - core_path[2] = 'T'; - core_path[3] = 'E'; - core_path[4] = 'C'; - core_path[5] = 'T'; - core_path[6] = '\0'; + const char *default_core_filename = path_basename(default_core_path); + if (!string_is_empty(default_core_filename)) + { + strlcpy(core_filename, default_core_filename, sizeof(core_filename)); + default_core_set = true; + } } - else - strlcpy(core_path, playlist_get_default_core_path(playlist), sizeof(core_path)); /* Sort cores alphabetically */ core_info_qsort(core_info_list, CORE_INFO_LIST_SORT_DISPLAY_NAME); - /* Get the index of the currently associated core */ - for (i = 0; i < core_info_list->count; i++) + /* If a core is currently associated... */ + if (default_core_set) { - core_info = NULL; - core_info = core_info_get(core_info_list, i); - if (!core_info) - return -1; - if (string_is_equal(core_info->path, core_path)) - current = i; + /* ...get its index */ + for (i = 0; i < core_info_list->count; i++) + { + core_info = NULL; + core_info = core_info_get(core_info_list, i); + if (!core_info) + continue; + if (string_starts_with(core_filename, core_info->core_file_id.str)) + current = i; + } + + /* ...then decrement it */ + if (current == 0) + { + /* Unset core association (DETECT) */ + next = 0; + default_core_set = false; + } + else + next = current - 1; + } + /* If a core is *not* currently associated and + * wraparound is enabled, select last core in + * the list */ + else if (wraparound && (core_info_list->count > 1)) + { + next = core_info_list->count - 1; + default_core_set = true; } - /* Decrement core index */ - if (current > 0) - next = current - 1; - else if (wraparound && (core_info_list->count > 1)) - next = core_info_list->count - 1; - else - next = 0; /* Silence 'next' may be used uninitialized warning */ - - /* Get new core info */ + /* If a core is now associated, get new core info */ core_info = NULL; - core_info = core_info_get(core_info_list, next); - if (!core_info) - return -1; + if (default_core_set) + core_info = core_info_get(core_info_list, next); /* Update playlist */ - playlist_set_default_core_path(playlist, core_info->path); - playlist_set_default_core_name(playlist, core_info->display_name); - playlist_write_file( - playlist, playlist_use_old_format, playlist_compression); + playlist_set_default_core_path(playlist, core_info ? core_info->path : "DETECT"); + playlist_set_default_core_name(playlist, core_info ? core_info->display_name : "DETECT"); + playlist_write_file(playlist, playlist_use_old_format, playlist_compression); return 0; } diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index ca9e4f8ecf..73dd0a5a22 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -1308,60 +1308,6 @@ int generic_action_ok_displaylist_push(const char *path, return menu_cbs_exit(); } -/** - * menu_content_load_from_playlist: - * @playlist : Playlist handle. - * @idx : Index in playlist. - * - * Initializes core and loads content based on playlist entry. - **/ -static bool menu_content_playlist_load(playlist_t *playlist, size_t idx) -{ - char path[PATH_MAX_LENGTH]; - const struct playlist_entry *entry = NULL; - - path[0] = '\0'; - - playlist_get_index(playlist, idx, &entry); - - if (!entry || string_is_empty(entry->path)) - return false; - - strlcpy(path, entry->path, sizeof(path)); - playlist_resolve_path(PLAYLIST_LOAD, path, sizeof(path)); - - if (!string_is_empty(path)) - { - unsigned i; - bool valid_path = false; - char *path_check = NULL; - char *path_tolower = strdup(path); - - for (i = 0; i < strlen(path_tolower); ++i) - path_tolower[i] = tolower((unsigned char)path_tolower[i]); - - if (strstr(path_tolower, ".zip")) - strstr(path_tolower, ".zip")[4] = '\0'; - else if (strstr(path_tolower, ".7z")) - strstr(path_tolower, ".7z")[3] = '\0'; - - path_check = (char *) - calloc(strlen(path_tolower) + 1, sizeof(char)); - - strlcpy(path_check, path, strlen(path_tolower) + 1); - - valid_path = path_is_valid(path_check); - - free(path_tolower); - free(path_check); - - if (valid_path) - return true; - } - - return false; -} - /** * menu_content_find_first_core: * @core_info : Core info list handle. @@ -2034,17 +1980,66 @@ static int action_ok_file_load(const char *path, CORE_TYPE_PLAIN); } +static bool playlist_entry_path_is_valid(const char *entry_path) +{ + char *archive_delim = NULL; + char *file_path = NULL; + + if (string_is_empty(entry_path)) + goto error; + + file_path = strdup(entry_path); + + /* We need to check whether the file referenced by the + * entry path actually exists. If it is a normal file, + * we can do this directly. If the path contains an + * archive delimiter, then we have to trim everything + * after the archive extension + * > Note: Have to do a nasty cast here, since + * path_get_archive_delim() returns a const char * + * (this cast is safe, though, and is done in many + * places throughout the codebase...) */ + archive_delim = (char *)path_get_archive_delim(file_path); + + if (archive_delim) + { + *archive_delim = '\0'; + if (string_is_empty(file_path)) + goto error; + } + + /* Path is 'sanitised' - can now check if it exists */ + if (!path_is_valid(file_path)) + goto error; + + /* File is valid */ + free(file_path); + file_path = NULL; + + return true; + +error: + if (file_path) + { + free(file_path); + file_path = NULL; + } + + return false; +} + static int action_ok_playlist_entry_collection(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - char new_path[PATH_MAX_LENGTH]; - char new_core_path[PATH_MAX_LENGTH]; - size_t selection_ptr = 0; + char content_path[PATH_MAX_LENGTH]; + char content_label[PATH_MAX_LENGTH]; + char core_path[PATH_MAX_LENGTH]; + size_t selection_ptr = entry_idx; bool playlist_initialized = false; playlist_t *playlist = NULL; playlist_t *tmp_playlist = NULL; const struct playlist_entry *entry = NULL; - unsigned i = 0; + core_info_t* core_info = NULL; menu_handle_t *menu = menu_driver_get_ptr(); settings_t *settings = config_get_ptr(); bool playlist_use_old_format = settings->bools.playlist_use_old_format; @@ -2055,12 +2050,15 @@ static int action_ok_playlist_entry_collection(const char *path, const char *path_content_video_history = settings->paths.path_content_video_history; const char *path_content_image_history = settings->paths.path_content_image_history; - if (!menu) - return menu_cbs_exit(); + content_path[0] = '\0'; + content_label[0] = '\0'; + core_path[0] = '\0'; - new_path[0] = '\0'; - new_core_path[0] = '\0'; - tmp_playlist = playlist_get_cached(); + if (!menu) + goto error; + + /* Get playlist */ + tmp_playlist = playlist_get_cached(); if (!tmp_playlist) { @@ -2076,11 +2074,10 @@ static int action_ok_playlist_entry_collection(const char *path, string_is_equal(menu->db_playlist_file, path_content_image_history); enum playlist_sort_mode current_sort_mode; - tmp_playlist = playlist_init( - menu->db_playlist_file, COLLECTION_SIZE); + tmp_playlist = playlist_init(menu->db_playlist_file, COLLECTION_SIZE); if (!tmp_playlist) - return menu_cbs_exit(); + goto error; current_sort_mode = playlist_get_sort_mode(tmp_playlist); @@ -2092,83 +2089,57 @@ static int action_ok_playlist_entry_collection(const char *path, playlist_initialized = true; } - playlist = tmp_playlist; - selection_ptr = entry_idx; + playlist = tmp_playlist; + /* Get playlist entry */ playlist_get_index(playlist, selection_ptr, &entry); + if (!entry) + goto error; - /* Subsystem codepath */ - if (!string_is_empty(entry->subsystem_ident)) + /* Cache entry path */ + if (!string_is_empty(entry->path)) { - content_ctx_info_t content_info = {0}; - - task_push_load_new_core(entry->core_path, NULL, - &content_info, CORE_TYPE_PLAIN, NULL, NULL); - - content_clear_subsystem(); - - if (!content_set_subsystem_by_name(entry->subsystem_ident)) - { - RARCH_LOG("[playlist] subsystem not found in implementation\n"); - /* TODO: Add OSD message telling users that content can't be loaded */ - if (playlist_initialized) - playlist_free(tmp_playlist); - return 0; - } - - for (i = 0; i < entry->subsystem_roms->size; i++) - content_add_subsystem(entry->subsystem_roms->elems[i].data); - - task_push_load_subsystem_with_core_from_menu( - NULL, &content_info, - CORE_TYPE_PLAIN, NULL, NULL); - /* TODO: update playlist entry? move to first position I guess? */ - if (playlist_initialized) - playlist_free(tmp_playlist); - return 1; + strlcpy(content_path, entry->path, sizeof(content_path)); + playlist_resolve_path(PLAYLIST_LOAD, content_path, sizeof(content_path)); } - /* Check whether playlist already has core path/name - * assignments - * > Both core name and core path must be valid */ - if ( string_is_empty(entry->core_path) - || string_is_empty(entry->core_name) - || string_is_equal(entry->core_path, "DETECT") - || string_is_equal(entry->core_name, "DETECT")) + /* Cache entry label */ + if (!string_is_empty(entry->label)) + strlcpy(content_label, entry->label, sizeof(content_label)); + + /* Get core path */ + if (!playlist_entry_has_core(entry)) { - core_info_ctx_find_t core_info; - const char *entry_path = NULL; - const char *default_core_path = - playlist_get_default_core_path(playlist); - bool found_associated_core = false; - struct playlist_entry update_entry = {0}; + struct playlist_entry update_entry = {0}; - if (!string_is_empty(default_core_path)) + /* Entry core is not set - attempt to use + * playlist default */ + core_info = playlist_get_default_core_info(playlist); + + /* If default core is not set, prompt user + * to select one */ + if (!core_info) { - strlcpy(new_core_path, default_core_path, sizeof(new_core_path)); - playlist_resolve_path(PLAYLIST_LOAD, new_core_path, sizeof(new_core_path)); - found_associated_core = true; - } - - core_info.inf = NULL; - core_info.path = new_core_path; - - if (!core_info_find(&core_info, new_core_path)) - found_associated_core = false; - - if (!found_associated_core) - { - /* TODO: figure out if this should refer to the inner or outer entry_path */ - /* TODO: make sure there's only one entry_path in this function */ - int ret = action_ok_file_load_with_detect_core_collection(entry_path, + /* TODO: figure out if this should refer to the inner or outer content_path */ + int ret = action_ok_file_load_with_detect_core_collection(content_path, label, type, selection_ptr, entry_idx); - if (playlist_initialized) + + if (playlist_initialized && tmp_playlist) + { playlist_free(tmp_playlist); + tmp_playlist = NULL; + playlist = NULL; + } + return ret; } - update_entry.core_path = (char*)default_core_path; - update_entry.core_name = core_info.inf->display_name; + /* Cache core path */ + strlcpy(core_path, core_info->path, sizeof(core_path)); + + /* Update playlist entry */ + update_entry.core_path = core_info->path; + update_entry.core_name = core_info->display_name; command_playlist_update_write( playlist, @@ -2179,208 +2150,90 @@ static int action_ok_playlist_entry_collection(const char *path, } else { - strlcpy(new_core_path, entry->core_path, sizeof(new_core_path)); - playlist_resolve_path(PLAYLIST_LOAD, new_core_path, sizeof(new_core_path)); - } + /* Entry does have a core assignment - ensure + * it has corresponding core info entry */ + core_info = playlist_entry_get_core_info(entry); - if (!playlist || !menu_content_playlist_load(playlist, selection_ptr)) - { - runloop_msg_queue_push( - "File could not be loaded from playlist.\n", - 1, 100, true, - NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - if (playlist_initialized) - playlist_free(tmp_playlist); - return menu_cbs_exit(); - } - - playlist_get_index(playlist, selection_ptr, &entry); - - strlcpy(new_path, entry->path, sizeof(new_path)); - playlist_resolve_path(PLAYLIST_LOAD, new_path, sizeof(new_path)); - - if (playlist_initialized) - playlist_free(tmp_playlist); - - return default_action_ok_load_content_from_playlist_from_menu( - new_core_path, new_path, entry->label); -} - -static int action_ok_playlist_entry(const char *path, - const char *label, unsigned type, size_t idx, size_t entry_idx) -{ - char new_core_path[PATH_MAX_LENGTH]; - size_t selection_ptr = 0; - const struct playlist_entry *entry = NULL; - const char *entry_label = NULL; - settings_t *settings = config_get_ptr(); - playlist_t *playlist = playlist_get_cached(); - menu_handle_t *menu = menu_driver_get_ptr(); - bool playlist_use_old_format = settings->bools.playlist_use_old_format; - bool playlist_compression = settings->bools.playlist_compression; - - new_core_path[0] = '\0'; - - if (!playlist || !menu) - return menu_cbs_exit(); - - selection_ptr = entry_idx; - - playlist_get_index(playlist, selection_ptr, &entry); - - entry_label = entry->label; - - /* Check whether playlist already has core path/name - * assignments - * > Both core name and core path must be valid */ - if ( string_is_empty(entry->core_path) - || string_is_empty(entry->core_name) - || string_is_equal(entry->core_path, "DETECT") - || string_is_equal(entry->core_name, "DETECT")) - { - core_info_ctx_find_t core_info; - const char *default_core_path = - playlist_get_default_core_path(playlist); - bool found_associated_core = false; - - if (!string_is_empty(default_core_path)) + if (core_info && !string_is_empty(core_info->path)) + strlcpy(core_path, core_info->path, sizeof(core_path)); + else { - strlcpy(new_core_path, default_core_path, sizeof(new_core_path)); - playlist_resolve_path(PLAYLIST_LOAD, new_core_path, sizeof(new_core_path)); - found_associated_core = true; - } - - core_info.inf = NULL; - core_info.path = new_core_path; - - if (!core_info_find(&core_info, new_core_path)) - found_associated_core = false; - - if (!found_associated_core) - /* TODO: figure out if this should refer to the inner or outer entry_path */ - /* TODO: make sure there's only one entry_path in this function */ - return action_ok_file_load_with_detect_core(entry->path, - label, type, selection_ptr, entry_idx); - - { - struct playlist_entry entry = {0}; - - entry.core_path = (char*)default_core_path; - entry.core_name = core_info.inf->display_name; - - command_playlist_update_write(NULL, - selection_ptr, - &entry, - playlist_use_old_format, - playlist_compression); - } - } - else - { - strlcpy(new_core_path, entry->core_path, sizeof(new_core_path)); - playlist_resolve_path(PLAYLIST_LOAD, new_core_path, sizeof(new_core_path)); - } - - if (!playlist || !menu_content_playlist_load(playlist, selection_ptr)) - { - runloop_msg_queue_push( - "File could not be loaded from playlist.\n", - 1, 100, true, - NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - return menu_cbs_exit(); - } - - playlist_get_index(playlist, - selection_ptr, &entry); - - return default_action_ok_load_content_from_playlist_from_menu( - new_core_path, entry->path, entry_label); -} - -static int action_ok_playlist_entry_start_content(const char *path, - const char *label, unsigned type, size_t idx, size_t entry_idx) -{ - size_t selection_ptr = 0; - const struct playlist_entry *entry = NULL; - settings_t *settings = config_get_ptr(); - playlist_t *playlist = playlist_get_cached(); - menu_handle_t *menu = menu_driver_get_ptr(); - bool playlist_use_old_format = settings->bools.playlist_use_old_format; - bool playlist_compression = settings->bools.playlist_compression; - - if (!playlist || !menu) - return menu_cbs_exit(); - - selection_ptr = menu->scratchpad.unsigned_var; - - playlist_get_index(playlist, selection_ptr, &entry); - - /* Check whether playlist already has core path/name - * assignments - * > Both core name and core path must be valid */ - if ( string_is_empty(entry->core_path) - || string_is_empty(entry->core_name) - || string_is_equal(entry->core_path, "DETECT") - || string_is_equal(entry->core_name, "DETECT")) - { - core_info_ctx_find_t core_info; - char new_core_path[PATH_MAX_LENGTH]; - const char *entry_path = NULL; - const char *default_core_path = - playlist_get_default_core_path(playlist); - bool found_associated_core = false; - - new_core_path[0] = '\0'; - - if (!string_is_empty(default_core_path)) - { - strlcpy(new_core_path, default_core_path, sizeof(new_core_path)); - found_associated_core = true; - } - - core_info.inf = NULL; - core_info.path = new_core_path; - - if (!core_info_find(&core_info, new_core_path)) - found_associated_core = false; - - /* TODO: figure out if this should refer to - * the inner or outer entry_path. */ - /* TODO: make sure there's only one entry_path - * in this function. */ - if (!found_associated_core) - return action_ok_file_load_with_detect_core(entry_path, - label, type, selection_ptr, entry_idx); - - { - struct playlist_entry entry = {0}; - - entry.core_path = new_core_path; - entry.core_name = core_info.inf->display_name; - - command_playlist_update_write( - playlist, - selection_ptr, - &entry, - playlist_use_old_format, - playlist_compression); + /* Core path is invalid - just copy what we have + * and hope for the best... */ + strlcpy(core_path, entry->core_path, sizeof(core_path)); + playlist_resolve_path(PLAYLIST_LOAD, core_path, sizeof(core_path)); } } - if (!menu_content_playlist_load(playlist, selection_ptr)) - { - runloop_msg_queue_push("File could not be loaded from playlist.\n", - 1, 100, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, - MESSAGE_QUEUE_CATEGORY_INFO); + /* Ensure core path is valid */ + if (string_is_empty(core_path) || !path_is_valid(core_path)) goto error; + + /* Subsystem codepath */ + if (!string_is_empty(entry->subsystem_ident)) + { + content_ctx_info_t content_info = {0}; + size_t i; + + task_push_load_new_core(core_path, NULL, + &content_info, CORE_TYPE_PLAIN, NULL, NULL); + + content_clear_subsystem(); + + if (!content_set_subsystem_by_name(entry->subsystem_ident)) + { + RARCH_LOG("[playlist] subsystem not found in implementation\n"); + goto error; + } + + for (i = 0; i < entry->subsystem_roms->size; i++) + content_add_subsystem(entry->subsystem_roms->elems[i].data); + + task_push_load_subsystem_with_core_from_menu( + NULL, &content_info, + CORE_TYPE_PLAIN, NULL, NULL); + + /* TODO: update playlist entry? move to first position I guess? */ + if (playlist_initialized && tmp_playlist) + { + playlist_free(tmp_playlist); + tmp_playlist = NULL; + playlist = NULL; + } + return 1; } - playlist_get_index(playlist, selection_ptr, &entry); + /* Ensure entry path is valid */ + if (!playlist_entry_path_is_valid(content_path)) + goto error; + /* Free temporary playlist, if required */ + if (playlist_initialized && tmp_playlist) + { + playlist_free(tmp_playlist); + tmp_playlist = NULL; + playlist = NULL; + } + + /* Note: Have to use cached entry label, since entry + * may be free()'d by above playlist_free() - but need + * to pass NULL explicitly if label is empty */ return default_action_ok_load_content_from_playlist_from_menu( - entry->core_path, entry->path, entry->label); + core_path, content_path, string_is_empty(content_label) ? NULL : content_label); error: + runloop_msg_queue_push( + "File could not be loaded from playlist.\n", + 1, 100, true, + NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + + if (playlist_initialized && tmp_playlist) + { + playlist_free(tmp_playlist); + tmp_playlist = NULL; + playlist = NULL; + } + return menu_cbs_exit(); } @@ -4674,7 +4527,7 @@ static int action_ok_add_to_favorites(const char *path, core_info.inf = NULL; core_info.path = core_path; - if (core_info_find(&core_info, core_path)) + if (core_info_find(&core_info)) if (!string_is_empty(core_info.inf->display_name)) strlcpy(core_name, core_info.inf->display_name, sizeof(core_name)); } @@ -4803,7 +4656,7 @@ static int action_ok_add_to_favorites_playlist(const char *path, core_info.inf = NULL; core_info.path = entry->core_path; - if (core_info_find(&core_info, entry->core_path)) + if (core_info_find(&core_info)) if (!string_is_empty(core_info.inf->display_name)) strlcpy(core_display_name, core_info.inf->display_name, sizeof(core_display_name)); @@ -7121,18 +6974,7 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs, BIND_ACTION_OK(cbs, action_ok_push_default); break; case FILE_TYPE_PLAYLIST_ENTRY: - if (string_is_equal(label, "collection")) - { - BIND_ACTION_OK(cbs, action_ok_playlist_entry_collection); - } - else if (string_is_equal(label, "rdb_entry_start_content")) - { - BIND_ACTION_OK(cbs, action_ok_playlist_entry_start_content); - } - else - { - BIND_ACTION_OK(cbs, action_ok_playlist_entry); - } + BIND_ACTION_OK(cbs, action_ok_playlist_entry_collection); break; #ifdef HAVE_LAKKA_SWITCH case MENU_SET_SWITCH_GPU_PROFILE: diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 64663cdcd1..fa90b6db34 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -474,16 +474,18 @@ static int action_right_video_resolution(unsigned type, const char *label, static int playlist_association_right(unsigned type, const char *label, bool wraparound) { - char core_path[PATH_MAX_LENGTH]; + char core_filename[PATH_MAX_LENGTH]; size_t i, next, current = 0; core_info_list_t *core_info_list = NULL; core_info_t *core_info = NULL; settings_t *settings = config_get_ptr(); playlist_t *playlist = playlist_get_cached(); + const char *default_core_path = playlist_get_default_core_path(playlist); + bool default_core_set = false; bool playlist_use_old_format = settings->bools.playlist_use_old_format; bool playlist_compression = settings->bools.playlist_compression; - core_path[0] = '\0'; + core_filename[0] = '\0'; if (!playlist) return -1; @@ -493,59 +495,65 @@ static int playlist_association_right(unsigned type, const char *label, return menu_cbs_exit(); /* Get current core path association */ - if (string_is_empty(playlist_get_default_core_path(playlist))) + if (!string_is_empty(default_core_path) && + !string_is_equal(default_core_path, "DETECT")) { - core_path[0] = 'D'; - core_path[1] = 'E'; - core_path[2] = 'T'; - core_path[3] = 'E'; - core_path[4] = 'C'; - core_path[5] = 'T'; - core_path[6] = '\0'; + const char *default_core_filename = path_basename(default_core_path); + if (!string_is_empty(default_core_filename)) + { + strlcpy(core_filename, default_core_filename, sizeof(core_filename)); + default_core_set = true; + } } - else - strlcpy(core_path, playlist_get_default_core_path(playlist), sizeof(core_path)); /* Sort cores alphabetically */ core_info_qsort(core_info_list, CORE_INFO_LIST_SORT_DISPLAY_NAME); - /* Get the index of the currently associated core */ - for (i = 0; i < core_info_list->count; i++) + /* If a core is currently associated... */ + if (default_core_set) { - core_info = NULL; - core_info = core_info_get(core_info_list, i); - if (!core_info) - return -1; - if (string_is_equal(core_info->path, core_path)) - current = i; - } - - /* Increment core index */ - next = current + 1; - if (next >= core_info_list->count) - { - if (wraparound) - next = 0; - else + /* ...get its index */ + for (i = 0; i < core_info_list->count; i++) { - if (core_info_list->count > 0) - next = core_info_list->count - 1; + core_info = NULL; + core_info = core_info_get(core_info_list, i); + if (!core_info) + continue; + if (string_starts_with(core_filename, core_info->core_file_id.str)) + current = i; + } + + /* ...then increment it */ + next = current + 1; + if (next >= core_info_list->count) + { + if (wraparound || (core_info_list->count < 1)) + { + /* Unset core association (DETECT) */ + next = 0; + default_core_set = false; + } else - next = 0; + next = core_info_list->count - 1; } } + /* If a core is *not* currently associated, + * select first core in the list */ + else + { + next = 0; + default_core_set = true; + } - /* Get new core info */ + /* If a core is now associated, get new core info */ core_info = NULL; - core_info = core_info_get(core_info_list, next); - if (!core_info) - return -1; + if (default_core_set) + core_info = core_info_get(core_info_list, next); /* Update playlist */ - playlist_set_default_core_path(playlist, core_info->path); - playlist_set_default_core_name(playlist, core_info->display_name); - playlist_write_file( - playlist, playlist_use_old_format, playlist_compression); + playlist_set_default_core_path(playlist, core_info ? core_info->path : "DETECT"); + playlist_set_default_core_name(playlist, core_info ? core_info->display_name : "DETECT"); + playlist_write_file(playlist, playlist_use_old_format, playlist_compression); return 0; } diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index b9fdaec101..66daa943d7 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -72,7 +72,7 @@ static int menu_action_sublabel_file_browser_core(file_list_t *list, unsigned ty core_info.inf = NULL; core_info.path = path; - if (core_info_find(&core_info, path) && + if (core_info_find(&core_info) && core_info.inf->licenses_list) { char tmp[MENU_SUBLABEL_MAX_LENGTH]; diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c index fd5f00832e..ae9f195fd0 100644 --- a/menu/cbs/menu_cbs_title.c +++ b/menu/cbs/menu_cbs_title.c @@ -347,7 +347,7 @@ static int action_get_title_deferred_core_backup_list( core_info.path = core_path; /* If core is found, add display name */ - if (core_info_find(&core_info, core_path) && + if (core_info_find(&core_info) && core_info.inf->display_name) strlcat(s, core_info.inf->display_name, len); else diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 900149e9d5..7438a0c0df 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -157,7 +157,7 @@ static int menu_displaylist_parse_core_info(menu_displaylist_info_t *info) core_info_finder.inf = NULL; core_info_finder.path = core_path; - if (core_info_find(&core_info_finder, core_path)) + if (core_info_find(&core_info_finder)) core_info = core_info_finder.inf; } else if (core_info_get_current_core(&core_info) && core_info) @@ -3282,7 +3282,7 @@ static unsigned menu_displaylist_parse_content_information( core_info.inf = NULL; core_info.path = core_path; - if (core_info_find(&core_info, core_path)) + if (core_info_find(&core_info)) if (!string_is_empty(core_info.inf->display_name)) strlcpy(core_name, core_info.inf->display_name, sizeof(core_name)); } diff --git a/playlist.c b/playlist.c index 597c61ba4e..2883bbe5c1 100644 --- a/playlist.c +++ b/playlist.c @@ -2898,3 +2898,66 @@ void playlist_set_sort_mode(playlist_t *playlist, enum playlist_sort_mode sort_m playlist->modified = true; } } + +/* Returns true if specified entry has a valid + * core association (i.e. a non-empty string + * other than DETECT) */ +bool playlist_entry_has_core(const struct playlist_entry *entry) +{ + if (!entry || + string_is_empty(entry->core_path) || + string_is_empty(entry->core_name) || + string_is_equal(entry->core_path, "DETECT") || + string_is_equal(entry->core_name, "DETECT")) + return false; + + return true; +} + +/* Fetches core info object corresponding to the + * currently associated core of the specified + * playlist entry. + * Returns NULL if entry does not have a valid + * core association */ +core_info_t *playlist_entry_get_core_info(const struct playlist_entry* entry) +{ + core_info_ctx_find_t core_info; + + if (!playlist_entry_has_core(entry)) + return NULL; + + /* Search for associated core */ + core_info.inf = NULL; + core_info.path = entry->core_path; + + if (core_info_find(&core_info)) + return core_info.inf; + + return NULL; +} + +/* Fetches core info object corresponding to the + * currently associated default core of the + * specified playlist. + * Returns NULL if playlist does not have a valid + * default core association */ +core_info_t *playlist_get_default_core_info(playlist_t* playlist) +{ + core_info_ctx_find_t core_info; + + if (!playlist || + string_is_empty(playlist->default_core_path) || + string_is_empty(playlist->default_core_name) || + string_is_equal(playlist->default_core_path, "DETECT") || + string_is_equal(playlist->default_core_name, "DETECT")) + return NULL; + + /* Search for associated core */ + core_info.inf = NULL; + core_info.path = playlist->default_core_path; + + if (core_info_find(&core_info)) + return core_info.inf; + + return NULL; +} diff --git a/playlist.h b/playlist.h index 0ec5fb4b81..e3642c1033 100644 --- a/playlist.h +++ b/playlist.h @@ -24,6 +24,8 @@ #include #include +#include "core_info.h" + RETRO_BEGIN_DECLS /* Default maximum playlist size */ @@ -317,6 +319,25 @@ void playlist_set_thumbnail_mode( playlist_t *playlist, enum playlist_thumbnail_id thumbnail_id, enum playlist_thumbnail_mode thumbnail_mode); void playlist_set_sort_mode(playlist_t *playlist, enum playlist_sort_mode sort_mode); +/* Returns true if specified entry has a valid + * core association (i.e. a non-empty string + * other than DETECT) */ +bool playlist_entry_has_core(const struct playlist_entry *entry); + +/* Fetches core info object corresponding to the + * currently associated core of the specified + * playlist entry. + * Returns NULL if entry does not have a valid + * core association */ +core_info_t *playlist_entry_get_core_info(const struct playlist_entry* entry); + +/* Fetches core info object corresponding to the + * currently associated default core of the + * specified playlist. + * Returns NULL if playlist does not have a valid + * default core association */ +core_info_t *playlist_get_default_core_info(playlist_t* playlist); + RETRO_END_DECLS #endif diff --git a/runtime_file.c b/runtime_file.c index 51a30d0fbc..94cf934261 100644 --- a/runtime_file.c +++ b/runtime_file.c @@ -281,15 +281,14 @@ runtime_log_t *runtime_log_init( char log_file_dir[PATH_MAX_LENGTH]; char log_file_path[PATH_MAX_LENGTH]; char tmp_buf[PATH_MAX_LENGTH]; - core_info_list_t *core_info = NULL; - runtime_log_t *runtime_log = NULL; - const char *core_path_basename = NULL; + core_info_ctx_find_t core_info; + runtime_log_t *runtime_log = NULL; - content_name[0] = '\0'; - core_name[0] = '\0'; - log_file_dir[0] = '\0'; - log_file_path[0] = '\0'; - tmp_buf[0] = '\0'; + content_name[0] = '\0'; + core_name[0] = '\0'; + log_file_dir[0] = '\0'; + log_file_path[0] = '\0'; + tmp_buf[0] = '\0'; if ( string_is_empty(dir_runtime_log) && string_is_empty(dir_playlist)) @@ -301,13 +300,8 @@ runtime_log_t *runtime_log_init( if ( string_is_empty(core_path) || string_is_equal(core_path, "builtin") || - string_is_equal(core_path, "DETECT")) - return NULL; - - core_path_basename = path_basename(core_path); - - if ( string_is_empty(content_path) || - string_is_empty(core_path_basename)) + string_is_equal(core_path, "DETECT") || + string_is_empty(content_path)) return NULL; /* Get core name @@ -315,24 +309,12 @@ runtime_log_t *runtime_log_init( * we are performing aggregate (not per core) logging, * since content name is sometimes dependent upon core * (e.g. see TyrQuake below) */ - core_info_get_list(&core_info); + core_info.inf = NULL; + core_info.path = core_path; - if (!core_info) - return NULL; - - for (i = 0; i < core_info->count; i++) - { - const char *entry_core_name = core_info->list[i].core_name; - if (!string_is_equal( - path_basename(core_info->list[i].path), core_path_basename)) - continue; - - if (string_is_empty(entry_core_name)) - return NULL; - - strlcpy(core_name, entry_core_name, sizeof(core_name)); - break; - } + if (core_info_find(&core_info) && + core_info.inf->core_name) + strlcpy(core_name, core_info.inf->core_name, sizeof(core_name)); if (string_is_empty(core_name)) return NULL; diff --git a/tasks/task_content.c b/tasks/task_content.c index 67c703aa18..265e0699ea 100644 --- a/tasks/task_content.c +++ b/tasks/task_content.c @@ -1992,8 +1992,6 @@ static bool task_load_content_callback(content_ctx_info_t *content_info, content_ctx.set_supports_no_game_enable = set_supports_no_game_enable; - if (!string_is_empty(path_dir_system)) - content_ctx.directory_system = strdup(path_dir_system); if (!string_is_empty(path_dir_cache)) content_ctx.directory_cache = strdup(path_dir_cache); if (!string_is_empty(system->valid_extensions)) diff --git a/tasks/task_core_backup.c b/tasks/task_core_backup.c index 6cf1921120..aa16e15051 100644 --- a/tasks/task_core_backup.c +++ b/tasks/task_core_backup.c @@ -442,7 +442,7 @@ void *task_push_core_backup(const char *core_path, core_info.path = core_path; /* If core is found, use display name */ - if (core_info_find(&core_info, core_path) && + if (core_info_find(&core_info) && core_info.inf->display_name) core_name = core_info.inf->display_name; else @@ -881,7 +881,7 @@ bool task_push_core_restore(const char *backup_path, const char *dir_libretro, core_info.path = core_path; /* If core is found, use display name */ - if (core_info_find(&core_info, core_path) && + if (core_info_find(&core_info) && core_info.inf->display_name) core_name = core_info.inf->display_name; else diff --git a/tasks/task_playlist_manager.c b/tasks/task_playlist_manager.c index 05d59c2484..e5e47bc89b 100644 --- a/tasks/task_playlist_manager.c +++ b/tasks/task_playlist_manager.c @@ -463,35 +463,19 @@ static void pl_manager_validate_core_association( goto reset_core; else { - const char *core_path_basename = path_basename(core_path); - core_info_list_t *core_info = NULL; char core_display_name[PATH_MAX_LENGTH]; - size_t i; + core_info_ctx_find_t core_info; core_display_name[0] = '\0'; - if (string_is_empty(core_path_basename)) - goto reset_core; + /* Search core info */ + core_info.inf = NULL; + core_info.path = core_path; - /* Final check - search core info */ - core_info_get_list(&core_info); - - if (core_info) - { - for (i = 0; i < core_info->count; i++) - { - const char *info_display_name = core_info->list[i].display_name; - - if (!string_is_equal( - path_basename(core_info->list[i].path), core_path_basename)) - continue; - - if (!string_is_empty(info_display_name)) - strlcpy(core_display_name, info_display_name, sizeof(core_display_name)); - - break; - } - } + if (core_info_find(&core_info) && + !string_is_empty(core_info.inf->display_name)) + strlcpy(core_display_name, core_info.inf->display_name, + sizeof(core_display_name)); /* If core_display_name string is empty, it means the * core wasn't found -> reset association */ diff --git a/ui/drivers/qt/qt_playlist.cpp b/ui/drivers/qt/qt_playlist.cpp index b614cf03ef..12aab55b3c 100644 --- a/ui/drivers/qt/qt_playlist.cpp +++ b/ui/drivers/qt/qt_playlist.cpp @@ -988,7 +988,7 @@ void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&) coreInfo.inf = NULL; coreInfo.path = corePath; - if (core_info_find(&coreInfo, corePath)) + if (core_info_find(&coreInfo)) { /* Set new core association */ playlist_set_default_core_path(playlist, coreInfo.inf->path); diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 17b41aa2a8..42616c2616 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -1395,25 +1395,21 @@ QVector > MainWindow::getCoreInfo() { unsigned i; QVector > infoList; + QByteArray currentCorePathArray; + core_info_ctx_find_t core_info_finder; QHash currentCore = getSelectedCore(); - core_info_list_t *core_info_list = NULL; + const char *current_core_path_data = NULL; const core_info_t *core_info = NULL; - core_info_get_list(&core_info_list); + currentCorePathArray = currentCore["core_path"].toUtf8(); + current_core_path_data = currentCorePathArray.constData(); - if (!core_info_list || core_info_list->count == 0) - return infoList; + /* Search for current core */ + core_info_finder.inf = NULL; + core_info_finder.path = current_core_path_data; - for (i = 0; i < core_info_list->count; i++) - { - const core_info_t *core = &core_info_list->list[i]; - - if (currentCore["core_path"] == core->path) - { - core_info = core; - break; - } - } + if (core_info_find(&core_info_finder)) + core_info = core_info_finder.inf; if (currentCore["core_path"].isEmpty() || !core_info || !core_info->config_data) { @@ -1865,6 +1861,7 @@ void MainWindow::loadContent(const QHash &contentHash) const char *contentCrc32 = NULL; QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); core_selection coreSelection = static_cast(coreMap.value("core_selection").toInt()); + core_info_ctx_find_t core_info; contentDbNameFull[0] = '\0'; @@ -1958,6 +1955,15 @@ void MainWindow::loadContent(const QHash &contentHash) contentDbName = contentDbNameArray.constData(); contentCrc32 = contentCrc32Array.constData(); + /* Search for specified core - ensures path + * is 'sanitised' */ + core_info.inf = NULL; + core_info.path = corePath; + + if (core_info_find(&core_info) && + !string_is_empty(core_info.inf->path)) + corePath = core_info.inf->path; + /* Add lpl extension to db_name, if required */ if (!string_is_empty(contentDbName)) { @@ -2127,11 +2133,15 @@ void MainWindow::setCoreActions() if (!defaultCorePath.isEmpty()) { + QByteArray defaultCorePathArray; QString currentPlaylistItemDataString; - bool allPlaylists = false; - int row = 0; - core_info_list_t *coreInfoList = NULL; - unsigned j = 0; + core_info_ctx_find_t core_info_finder; + const char *default_core_path_data = NULL; + bool allPlaylists = false; + int row = 0; + + defaultCorePathArray = defaultCorePath.toUtf8(); + default_core_path_data = defaultCorePathArray.constData(); if (currentPlaylistItem) { @@ -2153,42 +2163,45 @@ void MainWindow::setCoreActions() continue; } - core_info_get_list(&coreInfoList); + /* Search for default core */ + core_info_finder.inf = NULL; + core_info_finder.path = default_core_path_data; - if (coreInfoList) + if (core_info_find(&core_info_finder)) { - for (j = 0; j < coreInfoList->count; j++) + const core_info_t *info = core_info_finder.inf; + + if (m_launchWithComboBox->findText(info->core_name) == -1) { - const core_info_t *info = &coreInfoList->list[j]; + int i = 0; + bool found_existing = false; - if (defaultCorePath == info->path) + for (i = 0; i < m_launchWithComboBox->count(); i++) { - if (m_launchWithComboBox->findText(info->core_name) == -1) + QByteArray CorePathArray; + const char *core_path_data = NULL; + QVariantMap map = m_launchWithComboBox->itemData(i, Qt::UserRole).toMap(); + + CorePathArray = map.value("core_path").toString().toUtf8(); + core_path_data = CorePathArray.constData(); + + if (string_starts_with(path_basename(core_path_data), info->core_file_id.str) || + map.value("core_name").toString() == info->core_name || + map.value("core_name").toString() == info->display_name) { - int i = 0; - bool found_existing = false; - - for (i = 0; i < m_launchWithComboBox->count(); i++) - { - QVariantMap map = m_launchWithComboBox->itemData(i, Qt::UserRole).toMap(); - - if (map.value("core_path").toString() == info->path || map.value("core_name").toString() == info->core_name) - { - found_existing = true; - break; - } - } - - if (!found_existing) - { - QVariantMap comboBoxMap; - comboBoxMap["core_name"] = info->core_name; - comboBoxMap["core_path"] = info->path; - comboBoxMap["core_selection"] = CORE_SELECTION_PLAYLIST_DEFAULT; - m_launchWithComboBox->addItem(info->core_name, QVariant::fromValue(comboBoxMap)); - } + found_existing = true; + break; } } + + if (!found_existing) + { + QVariantMap comboBoxMap; + comboBoxMap["core_name"] = info->core_name; + comboBoxMap["core_path"] = info->path; + comboBoxMap["core_selection"] = CORE_SELECTION_PLAYLIST_DEFAULT; + m_launchWithComboBox->addItem(info->core_name, QVariant::fromValue(comboBoxMap)); + } } }