diff --git a/Makefile.common b/Makefile.common
index 48fec58b18..64c0fb81e3 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -175,6 +175,7 @@ OBJ += frontend/frontend.o \
tasks/task_file_transfer.o \
tasks/task_image.o \
tasks/task_audio_mixer.o \
+ tasks/task_playlist_manager.o \
$(LIBRETRO_COMM_DIR)/encodings/encoding_utf.o \
$(LIBRETRO_COMM_DIR)/encodings/encoding_crc32.o \
$(LIBRETRO_COMM_DIR)/encodings/encoding_base64.o \
diff --git a/griffin/griffin.c b/griffin/griffin.c
index 8c397179dc..d423f009a7 100644
--- a/griffin/griffin.c
+++ b/griffin/griffin.c
@@ -1198,6 +1198,7 @@ DATA RUNLOOP
#include "../tasks/task_save.c"
#include "../tasks/task_image.c"
#include "../tasks/task_file_transfer.c"
+#include "../tasks/task_playlist_manager.c"
#ifdef HAVE_ZLIB
#include "../tasks/task_decompress.c"
#endif
diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h
index 3b81c6471d..c6ed9c64f1 100644
--- a/intl/msg_hash_lbl.h
+++ b/intl/msg_hash_lbl.h
@@ -873,6 +873,8 @@ MSG_HASH(MENU_ENUM_LABEL_PLAYLIST_MANAGER_SETTINGS,
"playlist_manager_settings")
MSG_HASH(MENU_ENUM_LABEL_PLAYLIST_MANAGER_DEFAULT_CORE,
"playlist_manager_default_core")
+MSG_HASH(MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES,
+ "playlist_manager_reset_cores")
MSG_HASH(MENU_ENUM_LABEL_PLAYLIST_SETTINGS_BEGIN,
"playlist_settings_begin")
MSG_HASH(MENU_ENUM_LABEL_POINTER_ENABLE,
diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h
index d2a39a9f86..746595e72d 100644
--- a/intl/msg_hash_us.h
+++ b/intl/msg_hash_us.h
@@ -2164,6 +2164,22 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_DEFAULT_CORE,
"Specify core to use when launching content via a playlist entry that does not have an existing core association."
)
+MSG_HASH(
+ MENU_ENUM_LABEL_VALUE_PLAYLIST_MANAGER_RESET_CORES,
+ "Reset Core Associations"
+ )
+MSG_HASH(
+ MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_RESET_CORES,
+ "Remove existing core associations for all playlist entries."
+ )
+MSG_HASH(
+ MSG_PLAYLIST_MANAGER_RESETTING_CORES,
+ "Resetting cores: "
+ )
+MSG_HASH(
+ MSG_PLAYLIST_MANAGER_CORES_RESET,
+ "Cores reset: "
+ )
MSG_HASH(
MENU_ENUM_LABEL_VALUE_POINTER_ENABLE,
"Touch Support"
diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c
index d3c0f2b46a..b1e47fb7e6 100644
--- a/menu/cbs/menu_cbs_ok.c
+++ b/menu/cbs/menu_cbs_ok.c
@@ -5517,6 +5517,25 @@ static int action_ok_pl_entry_content_thumbnails(const char *path,
}
#endif
+static int action_ok_playlist_reset_cores(const char *path,
+ const char *label, unsigned type, size_t idx, size_t entry_idx)
+{
+ playlist_t *playlist = playlist_get_cached();
+ const char *playlist_path = NULL;
+
+ if (!playlist)
+ return -1;
+
+ playlist_path = playlist_get_conf_path(playlist);
+
+ if (string_is_empty(playlist_path))
+ return -1;
+
+ task_push_pl_manager_reset_cores(playlist_path);
+
+ return 0;
+}
+
static int is_rdb_entry(enum msg_hash_enums enum_idx)
{
switch (enum_idx)
@@ -5868,6 +5887,9 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_SETTINGS:
BIND_ACTION_OK(cbs, action_ok_push_playlist_manager_settings);
break;
+ case MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES:
+ BIND_ACTION_OK(cbs, action_ok_playlist_reset_cores);
+ break;
case MENU_ENUM_LABEL_RECORDING_SETTINGS:
BIND_ACTION_OK(cbs, action_ok_push_recording_settings_list);
break;
diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c
index 7c379bc7fa..db367ef5b3 100644
--- a/menu/cbs/menu_cbs_sublabel.c
+++ b/menu/cbs/menu_cbs_sublabel.c
@@ -130,6 +130,7 @@ default_sublabel_macro(action_bind_sublabel_directory_settings_list, MENU_
default_sublabel_macro(action_bind_sublabel_playlist_settings_list, MENU_ENUM_SUBLABEL_PLAYLIST_SETTINGS)
default_sublabel_macro(action_bind_sublabel_playlist_manager_list, MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_LIST)
default_sublabel_macro(action_bind_sublabel_playlist_manager_default_core, MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_DEFAULT_CORE)
+default_sublabel_macro(action_bind_sublabel_playlist_manager_reset_cores, MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_RESET_CORES)
default_sublabel_macro(action_bind_sublabel_network_settings_list, MENU_ENUM_SUBLABEL_NETWORK_SETTINGS)
default_sublabel_macro(action_bind_sublabel_network_on_demand_thumbnails, MENU_ENUM_SUBLABEL_NETWORK_ON_DEMAND_THUMBNAILS)
default_sublabel_macro(action_bind_sublabel_user_settings_list, MENU_ENUM_SUBLABEL_USER_SETTINGS)
@@ -2391,6 +2392,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_DEFAULT_CORE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_playlist_manager_default_core);
break;
+ case MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES:
+ BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_playlist_manager_reset_cores);
+ break;
case MENU_ENUM_LABEL_USER_INTERFACE_SETTINGS:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_user_interface_settings_list);
break;
diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c
index fbe6a0795d..f40dd80bec 100644
--- a/menu/drivers/materialui.c
+++ b/menu/drivers/materialui.c
@@ -2504,7 +2504,8 @@ static void materialui_list_insert(void *userdata,
node->texture_switch2_set = true;
}
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_RENAME_ENTRY)) ||
- string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_RESET_CORE_ASSOCIATION)))
+ string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_RESET_CORE_ASSOCIATION)) ||
+ string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES)))
{
node->texture_switch2_index = MUI_TEXTURE_RENAME;
node->texture_switch2_set = true;
diff --git a/menu/drivers/ozone/ozone_texture.c b/menu/drivers/ozone/ozone_texture.c
index d9b0194c00..bb1c7071da 100644
--- a/menu/drivers/ozone/ozone_texture.c
+++ b/menu/drivers/ozone/ozone_texture.c
@@ -40,6 +40,7 @@ menu_texture_item ozone_entries_icon_get_texture(ozone_handle_t *ozone,
case MENU_ENUM_LABEL_ADD_TO_FAVORITES_PLAYLIST:
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ADD_FAVORITE];
case MENU_ENUM_LABEL_RESET_CORE_ASSOCIATION:
+ case MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES:
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_UNDO];
case MENU_ENUM_LABEL_CORE_INPUT_REMAPPING_OPTIONS:
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_REMAPPING_OPTIONS];
diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c
index b318237c36..352fdd36da 100644
--- a/menu/drivers/stripes.c
+++ b/menu/drivers/stripes.c
@@ -2127,6 +2127,7 @@ static uintptr_t stripes_icon_get_id(stripes_handle_t *stripes,
case MENU_ENUM_LABEL_ADD_TO_FAVORITES_PLAYLIST:
return stripes->textures.list[STRIPES_TEXTURE_ADD_FAVORITE];
case MENU_ENUM_LABEL_RESET_CORE_ASSOCIATION:
+ case MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES:
return stripes->textures.list[STRIPES_TEXTURE_RENAME];
case MENU_ENUM_LABEL_CORE_INPUT_REMAPPING_OPTIONS:
return stripes->textures.list[STRIPES_TEXTURE_INPUT_REMAPPING_OPTIONS];
diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c
index b6ad6c7ee6..7111e99356 100644
--- a/menu/drivers/xmb.c
+++ b/menu/drivers/xmb.c
@@ -2323,6 +2323,7 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb,
case MENU_ENUM_LABEL_UNDO_LOAD_STATE:
case MENU_ENUM_LABEL_UNDO_SAVE_STATE:
case MENU_ENUM_LABEL_RESET_CORE_ASSOCIATION:
+ case MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES:
return xmb->textures.list[XMB_TEXTURE_UNDO];
case MENU_ENUM_LABEL_CORE_INPUT_REMAPPING_OPTIONS:
return xmb->textures.list[XMB_TEXTURE_INPUT_REMAPPING_OPTIONS];
diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c
index 92f168bc85..c5fe7f814c 100644
--- a/menu/menu_displaylist.c
+++ b/menu/menu_displaylist.c
@@ -2631,8 +2631,14 @@ static bool menu_displaylist_parse_playlist_manager_settings(
MENU_ENUM_LABEL_PLAYLIST_MANAGER_DEFAULT_CORE,
MENU_SETTING_PLAYLIST_MANAGER_DEFAULT_CORE, 0, 0);
+ /* Reset core associations */
+ menu_entries_append_enum(info->list,
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_MANAGER_RESET_CORES),
+ msg_hash_to_str(MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES),
+ MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES,
+ FILE_TYPE_PLAYLIST_ENTRY, 0, 0);
+
/* TODO: Add
- * - Reset core associations
* - Remove invalid entries */
return true;
diff --git a/msg_hash.h b/msg_hash.h
index ae32d0191f..b8eb112b90 100644
--- a/msg_hash.h
+++ b/msg_hash.h
@@ -1815,6 +1815,11 @@ enum msg_hash_enums
MENU_LABEL(PLAYLIST_MANAGER_LIST),
MENU_LABEL(PLAYLIST_MANAGER_SETTINGS),
MENU_LABEL(PLAYLIST_MANAGER_DEFAULT_CORE),
+ MENU_LABEL(PLAYLIST_MANAGER_RESET_CORES),
+
+ MSG_PLAYLIST_MANAGER_RESETTING_CORES,
+ MSG_PLAYLIST_MANAGER_CORES_RESET,
+
MENU_LABEL(CORE_UPDATER_SETTINGS),
MENU_LABEL(LAKKA_SERVICES),
MENU_LABEL(SHADER_APPLY_CHANGES),
diff --git a/tasks/task_playlist_manager.c b/tasks/task_playlist_manager.c
new file mode 100644
index 0000000000..0de83ef450
--- /dev/null
+++ b/tasks/task_playlist_manager.c
@@ -0,0 +1,315 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2011-2017 - Daniel De Matteis
+ * Copyright (C) 2014-2017 - Jean-André Santoni
+ * Copyright (C) 2016-2019 - Brad Parker
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "tasks_internal.h"
+
+#include "../msg_hash.h"
+#include "../file_path_special.h"
+#include "../playlist.h"
+
+#ifndef COLLECTION_SIZE
+#define COLLECTION_SIZE 99999
+#endif
+
+enum pl_manager_status
+{
+ PL_MANAGER_BEGIN = 0,
+ PL_MANAGER_ITERATE_ENTRY,
+ PL_MANAGER_END
+};
+
+typedef struct pl_manager_handle
+{
+ char *playlist_path;
+ char *playlist_name;
+ playlist_t *playlist;
+ size_t list_size;
+ size_t list_index;
+ enum pl_manager_status status;
+} pl_manager_handle_t;
+
+/**************************/
+/* Reset Associated Cores */
+/**************************/
+
+static void free_pl_manager_handle(pl_manager_handle_t *pl_manager)
+{
+ if (!pl_manager)
+ return;
+
+ if (!string_is_empty(pl_manager->playlist_path))
+ {
+ free(pl_manager->playlist_path);
+ pl_manager->playlist_path = NULL;
+ }
+
+ if (!string_is_empty(pl_manager->playlist_name))
+ {
+ free(pl_manager->playlist_name);
+ pl_manager->playlist_name = NULL;
+ }
+
+ if (pl_manager->playlist)
+ {
+ playlist_free(pl_manager->playlist);
+ pl_manager->playlist = NULL;
+ }
+
+ free(pl_manager);
+ pl_manager = NULL;
+}
+
+static void task_pl_manager_reset_cores_handler(retro_task_t *task)
+{
+ pl_manager_handle_t *pl_manager = NULL;
+
+ if (!task)
+ goto task_finished;
+
+ pl_manager = (pl_manager_handle_t*)task->state;
+
+ if (!pl_manager)
+ goto task_finished;
+
+ if (task_get_cancelled(task))
+ goto task_finished;
+
+ switch (pl_manager->status)
+ {
+ case PL_MANAGER_BEGIN:
+ {
+ /* Load playlist */
+ if (!path_is_valid(pl_manager->playlist_path))
+ goto task_finished;
+
+ pl_manager->playlist = playlist_init(pl_manager->playlist_path, COLLECTION_SIZE);
+
+ if (!pl_manager->playlist)
+ goto task_finished;
+
+ pl_manager->list_size = playlist_size(pl_manager->playlist);
+
+ if (pl_manager->list_size < 1)
+ goto task_finished;
+
+ /* All good - can start iterating */
+ pl_manager->status = PL_MANAGER_ITERATE_ENTRY;
+ }
+ break;
+ case PL_MANAGER_ITERATE_ENTRY:
+ {
+ const struct playlist_entry *entry = NULL;
+
+ /* Get current entry */
+ playlist_get_index(
+ pl_manager->playlist, pl_manager->list_index, &entry);
+
+ if (entry)
+ {
+ struct playlist_entry update_entry = {0};
+ char task_title[PATH_MAX_LENGTH];
+ char detect_string[PATH_MAX_LENGTH];
+
+ task_title[0] = '\0';
+ detect_string[0] = '\0';
+
+ /* Update progress display */
+ task_free_title(task);
+
+ strlcpy(
+ task_title, msg_hash_to_str(MSG_PLAYLIST_MANAGER_RESETTING_CORES),
+ sizeof(task_title));
+
+ if (!string_is_empty(entry->label))
+ strlcat(task_title, entry->label, sizeof(task_title));
+ else if (!string_is_empty(entry->path))
+ {
+ char entry_name[PATH_MAX_LENGTH];
+ entry_name[0] = '\0';
+
+ fill_pathname_base_noext(entry_name, entry->path, sizeof(entry_name));
+ strlcat(task_title, entry_name, sizeof(task_title));
+ }
+
+ task_set_title(task, strdup(task_title));
+ task_set_progress(task, (pl_manager->list_index * 100) / pl_manager->list_size);
+
+ /* Reset core association */
+ strlcpy(detect_string, file_path_str(FILE_PATH_DETECT), sizeof(detect_string));
+
+ update_entry.core_path = detect_string;
+ update_entry.core_name = detect_string;
+
+ playlist_update(
+ pl_manager->playlist, pl_manager->list_index, &update_entry);
+ }
+
+ /* Increment entry index */
+ pl_manager->list_index++;
+ if (pl_manager->list_index >= pl_manager->list_size)
+ pl_manager->status = PL_MANAGER_END;
+ }
+ break;
+ case PL_MANAGER_END:
+ {
+ playlist_t *cached_playlist = playlist_get_cached();
+ char task_title[PATH_MAX_LENGTH];
+
+ task_title[0] = '\0';
+
+ /* Save playlist changes to disk */
+ playlist_write_file(pl_manager->playlist);
+
+ /* If this is the currently cached playlist, then
+ * it must be re-cached (otherwise changes will be
+ * lost if the currently cached playlist is saved
+ * to disk for any reason...) */
+ if (cached_playlist)
+ {
+ if (string_is_equal(pl_manager->playlist_path, playlist_get_conf_path(cached_playlist)))
+ {
+ playlist_free_cached();
+ playlist_init_cached(pl_manager->playlist_path, COLLECTION_SIZE);
+ }
+ }
+
+ /* Update progress display */
+ task_free_title(task);
+
+ strlcpy(
+ task_title, msg_hash_to_str(MSG_PLAYLIST_MANAGER_CORES_RESET),
+ sizeof(task_title));
+ strlcat(task_title, pl_manager->playlist_name, sizeof(task_title));
+
+ task_set_title(task, strdup(task_title));
+ task_set_progress(task, 100);
+
+ goto task_finished;
+ }
+ break;
+ default:
+ task_set_progress(task, 100);
+ goto task_finished;
+ break;
+ }
+
+ return;
+
+task_finished:
+
+ if (task)
+ task_set_finished(task, true);
+
+ free_pl_manager_handle(pl_manager);
+}
+
+static bool task_pl_manager_reset_cores_finder(retro_task_t *task, void *user_data)
+{
+ pl_manager_handle_t *pl_manager = NULL;
+
+ if (!task || !user_data)
+ return false;
+
+ if (task->handler != task_pl_manager_reset_cores_handler)
+ return false;
+
+ pl_manager = (pl_manager_handle_t*)task->state;
+ if (!pl_manager)
+ return false;
+
+ return string_is_equal((const char*)user_data, pl_manager->playlist_path);
+}
+
+bool task_push_pl_manager_reset_cores(const char *playlist_path)
+{
+ task_finder_data_t find_data;
+ char playlist_name[PATH_MAX_LENGTH];
+ char task_title[PATH_MAX_LENGTH];
+ retro_task_t *task = task_init();
+ pl_manager_handle_t *pl_manager = (pl_manager_handle_t*)calloc(1, sizeof(pl_manager_handle_t));
+
+ playlist_name[0] = '\0';
+ task_title[0] = '\0';
+
+ /* Sanity check */
+ if (!task || !pl_manager)
+ goto error;
+
+ if (string_is_empty(playlist_path))
+ goto error;
+
+ fill_pathname_base_noext(playlist_name, playlist_path, sizeof(playlist_name));
+
+ if (string_is_empty(playlist_name))
+ goto error;
+
+ /* Concurrent management of the same playlist
+ * is not allowed */
+ find_data.func = task_pl_manager_reset_cores_finder;
+ find_data.userdata = (void*)playlist_path;
+
+ if (task_queue_find(&find_data))
+ goto error;
+
+ /* Configure task */
+ strlcpy(
+ task_title, msg_hash_to_str(MSG_PLAYLIST_MANAGER_RESETTING_CORES),
+ sizeof(task_title));
+ strlcat(task_title, playlist_name, sizeof(task_title));
+
+ task->handler = task_pl_manager_reset_cores_handler;
+ task->state = pl_manager;
+ task->title = strdup(task_title);
+ task->alternative_look = true;
+ task->progress = 0;
+
+ /* Configure handle */
+ pl_manager->playlist_path = strdup(playlist_path);
+ pl_manager->playlist_name = strdup(playlist_name);
+ pl_manager->playlist = NULL;
+ pl_manager->list_size = 0;
+ pl_manager->list_index = 0;
+ pl_manager->status = PL_MANAGER_BEGIN;
+
+ task_queue_push(task);
+
+ return true;
+
+error:
+
+ if (task)
+ {
+ free(task);
+ task = NULL;
+ }
+
+ if (pl_manager)
+ {
+ free(pl_manager);
+ pl_manager = NULL;
+ }
+
+ return false;
+}
diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h
index 4a1cd58fde..1de890b9c9 100644
--- a/tasks/tasks_internal.h
+++ b/tasks/tasks_internal.h
@@ -73,6 +73,8 @@ bool task_push_pl_entry_thumbnail_download(
#endif
+bool task_push_pl_manager_reset_cores(const char *playlist_path);
+
bool task_push_image_load(const char *fullpath,
bool supports_rgba, unsigned upscale_threshold,
retro_task_callback_t cb, void *userdata);