diff --git a/core_info.c b/core_info.c index 76cd77b5aa..0ea6910a0c 100644 --- a/core_info.c +++ b/core_info.c @@ -723,6 +723,15 @@ bool core_info_get_list(core_info_list_t **core) return true; } +/* Returns number of installed cores */ +size_t core_info_count(void) +{ + core_info_state_t *p_coreinfo = coreinfo_get_ptr(); + if (!p_coreinfo || !p_coreinfo->curr_list) + return 0; + return p_coreinfo->curr_list->count; +} + bool core_info_list_update_missing_firmware(core_info_ctx_firmware_t *info, bool *set_missing_bios) { diff --git a/core_info.h b/core_info.h index e61949f08b..4a38dd4892 100644 --- a/core_info.h +++ b/core_info.h @@ -180,6 +180,9 @@ bool core_info_init_list(const char *path_info, const char *dir_cores, bool core_info_get_list(core_info_list_t **core); +/* Returns number of installed cores */ +size_t core_info_count(void); + bool core_info_list_update_missing_firmware(core_info_ctx_firmware_t *info, bool *set_missing_bios); diff --git a/file_path_special.h b/file_path_special.h index bf63782dca..f0c777e403 100644 --- a/file_path_special.h +++ b/file_path_special.h @@ -105,6 +105,7 @@ 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_BACKUP_EXTENSION ".bak" enum application_special_type { diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index a23d1e5c6e..dc180e4397 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1084,6 +1084,10 @@ MSG_HASH( MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES, "update_installed_cores" ) +MSG_HASH( + MENU_ENUM_LABEL_SWITCH_INSTALLED_CORES_PFD, + "switch_installed_cores_pfd" + ) MSG_HASH( MENU_ENUM_LABEL_CONTENT_DIR, "content_directory" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 5a2119d1bc..63d8f808c1 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -291,6 +291,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_UPDATE_INSTALLED_CORES, "Update all installed cores to the latest version available." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_SWITCH_INSTALLED_CORES_PFD, + "Switch Cores to Play Store Versions" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_SWITCH_INSTALLED_CORES_PFD, + "Replace all legacy and manually installed cores with the latest versions from the Play Store, where available." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_THUMBNAILS_UPDATER_LIST, "Thumbnails Updater" @@ -10100,6 +10108,10 @@ MSG_HASH( MSG_ALL_CORES_UPDATED, "All installed cores at latest version" ) +MSG_HASH( + MSG_ALL_CORES_SWITCHED_PFD, + "All supported cores switched to Play Store versions" + ) MSG_HASH( MSG_NUM_CORES_UPDATED, "cores updated: " diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 08f0ef875b..bed19a583e 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -90,6 +90,7 @@ #endif #if defined(ANDROID) +#include "../../file_path_special.h" #include "../../play_feature_delivery/play_feature_delivery.h" #endif @@ -4512,7 +4513,7 @@ static int action_ok_core_updater_download(const char *path, * the play feature delivery interface */ if (play_feature_delivery_enabled()) task_push_play_feature_delivery_core_install( - core_list, path); + core_list, path, false); else #endif task_push_core_updater_download( @@ -4544,6 +4545,25 @@ static int action_ok_update_installed_cores(const char *path, return 0; } + +#if defined(ANDROID) +static int action_ok_switch_installed_cores_pfd(const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx) +{ + settings_t *settings = config_get_ptr(); + const char *path_dir_libretro = settings->paths.directory_libretro; + const char *path_libretro_info = settings->paths.path_libretro_info; + + /* Ensure networking is initialised */ + generic_action_ok_command(CMD_EVENT_NETWORK_INIT); + + /* Push core switch/update task */ + task_push_play_feature_delivery_switch_installed_cores( + path_dir_libretro, path_libretro_info); + + return 0; +} +#endif #endif static int action_ok_sideload_core(const char *path, @@ -6735,11 +6755,32 @@ static int action_ok_core_delete(const char *path, if (play_feature_delivery_enabled()) { const char *core_filename = path_basename(core_path); + char backup_core_path[PATH_MAX_LENGTH]; + + backup_core_path[0] = '\0'; if (play_feature_delivery_core_installed(core_filename)) play_feature_delivery_delete(core_filename); else filestream_delete(core_path); + + /* When installing cores via play feature + * delivery, there is a low probability of + * backup core files being left behind if + * something interrupts the install process + * (i.e. a crash or user exit while the + * install task is running). To prevent the + * accumulation of mess, additionally check + * for and remove any such backups when deleting + * a core */ + strlcpy(backup_core_path, core_path, + sizeof(backup_core_path)); + strlcat(backup_core_path, FILE_PATH_BACKUP_EXTENSION, + sizeof(backup_core_path)); + + if (!string_is_empty(backup_core_path) && + path_is_valid(backup_core_path)) + filestream_delete(backup_core_path); } else #endif @@ -7008,6 +7049,9 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_DOWNLOAD_CORE_CONTENT_DIRS, action_ok_core_content_dirs_list}, {MENU_ENUM_LABEL_CORE_UPDATER_LIST, action_ok_core_updater_list}, {MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES, action_ok_update_installed_cores}, +#if defined(ANDROID) + {MENU_ENUM_LABEL_SWITCH_INSTALLED_CORES_PFD, action_ok_switch_installed_cores_pfd}, +#endif {MENU_ENUM_LABEL_THUMBNAILS_UPDATER_LIST, action_ok_thumbnails_updater_list}, {MENU_ENUM_LABEL_PL_THUMBNAILS_UPDATER_LIST, action_ok_pl_thumbnails_updater_list}, {MENU_ENUM_LABEL_DOWNLOAD_PL_ENTRY_THUMBNAILS, action_ok_pl_entry_content_thumbnails}, diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 2f44c809d5..a531962166 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -365,6 +365,9 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_start_core, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_list, MENU_ENUM_SUBLABEL_CORE_LIST) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_download_core, MENU_ENUM_SUBLABEL_DOWNLOAD_CORE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_update_installed_cores, MENU_ENUM_SUBLABEL_UPDATE_INSTALLED_CORES) +#if defined(ANDROID) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_switch_installed_cores_pfd, MENU_ENUM_SUBLABEL_SWITCH_INSTALLED_CORES_PFD) +#endif DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_sideload_core_list, MENU_ENUM_SUBLABEL_SIDELOAD_CORE_LIST) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_manager_list, MENU_ENUM_SUBLABEL_CORE_MANAGER_LIST) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_load_disc, MENU_ENUM_SUBLABEL_LOAD_DISC) @@ -3122,6 +3125,11 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_update_installed_cores); break; +#if defined(ANDROID) + case MENU_ENUM_LABEL_SWITCH_INSTALLED_CORES_PFD: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_switch_installed_cores_pfd); + break; +#endif case MENU_ENUM_LABEL_CORE_MANAGER_LIST: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_core_manager_list); break; diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index ae6618b58e..bfa473afa7 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -9903,7 +9903,8 @@ static void materialui_list_insert( node->icon_texture_index = MUI_TEXTURE_START_CORE; node->icon_type = MUI_ICON_TYPE_INTERNAL; } - else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_LOAD_STATE))) + else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_LOAD_STATE)) || + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SWITCH_INSTALLED_CORES_PFD))) { node->icon_texture_index = MUI_TEXTURE_LOAD_STATE; node->icon_type = MUI_ICON_TYPE_INTERNAL; diff --git a/menu/drivers/ozone/ozone_texture.c b/menu/drivers/ozone/ozone_texture.c index 0b96f48ddb..d7fcdb023c 100644 --- a/menu/drivers/ozone/ozone_texture.c +++ b/menu/drivers/ozone/ozone_texture.c @@ -131,6 +131,7 @@ uintptr_t ozone_entries_icon_get_texture(ozone_handle_t *ozone, case MENU_ENUM_LABEL_CORE_SETTINGS: case MENU_ENUM_LABEL_CORE_UPDATER_LIST: case MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES: + case MENU_ENUM_LABEL_SWITCH_INSTALLED_CORES_PFD: case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_CORE: case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CORE: case MENU_ENUM_LABEL_REMAP_FILE_SAVE_CORE: diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 6c18f210b3..86bbbc6146 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -2577,6 +2577,7 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, case MENU_ENUM_LABEL_CORE_SETTINGS: case MENU_ENUM_LABEL_CORE_UPDATER_LIST: case MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES: + case MENU_ENUM_LABEL_SWITCH_INSTALLED_CORES_PFD: case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_CORE: case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CORE: case MENU_ENUM_LABEL_REMAP_FILE_SAVE_CORE: diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index fb3da9dee2..32f5777f11 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -11065,18 +11065,36 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, MENU_SETTING_ACTION, 0, 0)) count++; + /* Only show 'update installed cores' if + * one or more cores are installed */ + if (core_info_count() > 0) + { #if defined(ANDROID) - /* Play Store builds auto-update installed - * cores, rendering the 'update installed - * cores' option irrelevant/useless */ - if (!play_feature_delivery_enabled()) + /* When using Play Store builds, cores are + * updated automatically - the 'update + * installed cores' option is therefore + * irrelevant/useless, so we instead present + * an option for switching any existing buildbot + * or sideloaded cores to the latest Play Store + * version */ + if (play_feature_delivery_enabled()) + { + if (menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SWITCH_INSTALLED_CORES_PFD), + msg_hash_to_str(MENU_ENUM_LABEL_SWITCH_INSTALLED_CORES_PFD), + MENU_ENUM_LABEL_SWITCH_INSTALLED_CORES_PFD, + MENU_SETTING_ACTION, 0, 0)) + count++; + } + else #endif - if (menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_UPDATE_INSTALLED_CORES), - msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES), - MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES, - MENU_SETTING_ACTION, 0, 0)) - count++; + if (menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_UPDATE_INSTALLED_CORES), + msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES), + MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES, + MENU_SETTING_ACTION, 0, 0)) + count++; + } } #endif #endif diff --git a/msg_hash.h b/msg_hash.h index 9cec4652b1..1fc1822a7a 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -2065,6 +2065,7 @@ enum msg_hash_enums /* Core updater */ MENU_LABEL(UPDATE_INSTALLED_CORES), + MENU_LABEL(SWITCH_INSTALLED_CORES_PFD), MSG_FETCHING_CORE_LIST, MSG_CORE_LIST_FAILED, @@ -2077,6 +2078,7 @@ enum msg_hash_enums MSG_SCANNING_CORES, MSG_CHECKING_CORE, MSG_ALL_CORES_UPDATED, + MSG_ALL_CORES_SWITCHED_PFD, MSG_NUM_CORES_UPDATED, MSG_NUM_CORES_LOCKED, MSG_CORE_UPDATE_DISABLED, diff --git a/play_feature_delivery/play_feature_delivery.c b/play_feature_delivery/play_feature_delivery.c index ac25b844aa..1bcd3462a8 100644 --- a/play_feature_delivery/play_feature_delivery.c +++ b/play_feature_delivery/play_feature_delivery.c @@ -87,7 +87,6 @@ JNIEXPORT void JNICALL Java_com_retroarch_browser_retroactivity_RetroActivityCom (JNIEnv *env, jobject this_obj, jstring core_name, jboolean successful) { play_feature_delivery_state_t* state = play_feature_delivery_get_state(); - const char *core_name_c = NULL; /* Lock mutex */ #ifdef HAVE_THREADS @@ -523,7 +522,14 @@ bool play_feature_delivery_download(const char *core_file) /* We only support one download at a time */ if (!state->active) { - /* Convert to a Java-style string */ + /* Update status */ + state->download_progress = 0; + state->last_status = PLAY_FEATURE_DELIVERY_PENDING; + state->active = true; + strlcpy(state->last_core_name, core_name, + sizeof(state->last_core_name)); + + /* Convert core name to a Java-style string */ core_name_jni = (*env)->NewStringUTF(env, core_name); /* Request download */ @@ -533,13 +539,6 @@ bool play_feature_delivery_download(const char *core_file) /* Free core_name_jni reference */ (*env)->DeleteLocalRef(env, core_name_jni); - /* Update status */ - state->download_progress = 0; - state->last_status = PLAY_FEATURE_DELIVERY_PENDING; - state->active = true; - strlcpy(state->last_core_name, core_name, - sizeof(state->last_core_name)); - success = true; } diff --git a/playlist.c b/playlist.c index abfa09cad1..c9a3add3c0 100644 --- a/playlist.c +++ b/playlist.c @@ -37,6 +37,10 @@ #include "file_path_special.h" #include "core_info.h" +#if defined(ANDROID) +#include "play_feature_delivery/play_feature_delivery.h" +#endif + #ifndef PLAYLIST_ENTRIES #define PLAYLIST_ENTRIES 6 #endif diff --git a/tasks/task_core_updater.c b/tasks/task_core_updater.c index a0171d5f55..1dae3c27bf 100644 --- a/tasks/task_core_updater.c +++ b/tasks/task_core_updater.c @@ -39,6 +39,7 @@ #include "../core_updater_list.h" #if defined(ANDROID) +#include "../file_path_special.h" #include "../play_feature_delivery/play_feature_delivery.h" #endif @@ -131,8 +132,8 @@ typedef struct update_installed_cores_handle bool auto_backup; } update_installed_cores_handle_t; -/* Play feature delivery core install */ #if defined(ANDROID) +/* Play feature delivery core install */ enum play_feature_delivery_install_task_status { PLAY_FEATURE_DELIVERY_INSTALL_BEGIN = 0, @@ -144,11 +145,35 @@ typedef struct play_feature_delivery_install_handle { char *core_filename; char *local_core_path; + char *backup_core_path; char *display_name; enum play_feature_delivery_install_task_status status; bool success; bool core_already_installed; } play_feature_delivery_install_handle_t; + +/* Play feature delivery switch installed cores */ +enum play_feature_delivery_switch_cores_task_status +{ + PLAY_FEATURE_DELIVERY_SWITCH_CORES_BEGIN = 0, + PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE, + PLAY_FEATURE_DELIVERY_SWITCH_CORES_INSTALL_CORE, + PLAY_FEATURE_DELIVERY_SWITCH_CORES_WAIT_INSTALL, + PLAY_FEATURE_DELIVERY_SWITCH_CORES_END +}; + +typedef struct play_feature_delivery_switch_cores_handle +{ + char *path_dir_libretro; + char *path_libretro_info; + char *error_msg; + core_updater_list_t* core_list; + retro_task_t *install_task; + size_t list_size; + size_t list_index; + size_t installed_index; + enum play_feature_delivery_switch_cores_task_status status; +} play_feature_delivery_switch_cores_handle_t; #endif /*********************/ @@ -1517,12 +1542,11 @@ error: free_update_installed_cores_handle(update_installed_handle); } +#if defined(ANDROID) /**************************************/ /* Play feature delivery core install */ /**************************************/ -#if defined(ANDROID) - static void free_play_feature_delivery_install_handle( play_feature_delivery_install_handle_t *pfd_install_handle) { @@ -1535,6 +1559,9 @@ static void free_play_feature_delivery_install_handle( if (pfd_install_handle->local_core_path) free(pfd_install_handle->local_core_path); + if (pfd_install_handle->backup_core_path) + free(pfd_install_handle->backup_core_path); + if (pfd_install_handle->display_name) free(pfd_install_handle->display_name); @@ -1574,10 +1601,54 @@ static void task_play_feature_delivery_core_install_handler(retro_task_t *task) } /* If core is already installed via other - * means, must delete it before attempting + * means, must remove it before attempting * play feature delivery transaction */ if (path_is_valid(pfd_install_handle->local_core_path)) - filestream_delete(pfd_install_handle->local_core_path); + { + char backup_core_path[PATH_MAX_LENGTH]; + bool backup_successful = false; + + backup_core_path[0] = '\0'; + + /* Have to create a backup, in case install + * process fails + * > Note: since only one install task can + * run at a time, a UID is not required */ + + /* Generate backup file name */ + strlcpy(backup_core_path, pfd_install_handle->local_core_path, + sizeof(backup_core_path)); + strlcat(backup_core_path, FILE_PATH_BACKUP_EXTENSION, + sizeof(backup_core_path)); + + if (!string_is_empty(backup_core_path)) + { + int ret; + + /* If an old backup file exists (i.e. leftovers + * from a mid-task crash/user exit), delete it */ + if (path_is_valid(backup_core_path)) + filestream_delete(backup_core_path); + + /* Attempt to rename core file */ + ret = filestream_rename( + pfd_install_handle->local_core_path, + backup_core_path); + + if (!ret) + { + /* Success - cache backup file name */ + pfd_install_handle->backup_core_path = strdup(backup_core_path); + backup_successful = true; + } + } + + /* If backup failed, all we can do is delete + * the existing core file... */ + if (!backup_successful && + path_is_valid(pfd_install_handle->local_core_path)) + filestream_delete(pfd_install_handle->local_core_path); + } /* Start download */ if (play_feature_delivery_download( @@ -1659,6 +1730,34 @@ static void task_play_feature_delivery_core_install_handler(retro_task_t *task) sizeof(task_title)); task_set_title(task, strdup(task_title)); + + /* Check whether a core backup file was created */ + if (!string_is_empty(pfd_install_handle->backup_core_path) && + path_is_valid(pfd_install_handle->backup_core_path)) + { + /* If install was successful, delete backup */ + if (pfd_install_handle->success) + filestream_delete(pfd_install_handle->backup_core_path); + else + { + /* Otherwise, attempt to restore backup */ + int ret = filestream_rename( + pfd_install_handle->backup_core_path, + pfd_install_handle->local_core_path); + + /* If restore failed, all we can do is attempt + * to delete the backup... */ + if (ret && path_is_valid(pfd_install_handle->backup_core_path)) + filestream_delete(pfd_install_handle->backup_core_path); + } + } + + /* If task is muted and install failed, set + * error string (allows status to be checked + * externally) */ + if (!pfd_install_handle->success && + task_get_mute(task)) + task_set_error(task, strdup(task_title)); } /* fall-through */ default: @@ -1688,9 +1787,10 @@ static bool task_play_feature_delivery_core_install_finder( return false; } -void task_push_play_feature_delivery_core_install( +void *task_push_play_feature_delivery_core_install( core_updater_list_t* core_list, - const char *filename) + const char *filename, + bool mute) { task_finder_data_t find_data; char task_title[PATH_MAX_LENGTH]; @@ -1727,6 +1827,7 @@ void task_push_play_feature_delivery_core_install( /* Configure handle */ pfd_install_handle->core_filename = strdup(list_entry->remote_filename); pfd_install_handle->local_core_path = strdup(list_entry->local_core_path); + pfd_install_handle->backup_core_path = NULL; pfd_install_handle->display_name = strdup(list_entry->display_name); pfd_install_handle->success = false; pfd_install_handle->core_already_installed = false; @@ -1746,7 +1847,7 @@ void task_push_play_feature_delivery_core_install( task->handler = task_play_feature_delivery_core_install_handler; task->state = pfd_install_handle; - task->mute = false; + task->mute = mute; task->title = strdup(task_title); task->alternative_look = true; task->progress = 0; @@ -1762,6 +1863,372 @@ void task_push_play_feature_delivery_core_install( /* Push task */ task_queue_push(task); + return task; + +error: + + /* Clean up task */ + if (task) + { + free(task); + task = NULL; + } + + /* Clean up handle */ + free_play_feature_delivery_install_handle(pfd_install_handle); + + return NULL; +} + +/************************************************/ +/* Play feature delivery switch installed cores */ +/************************************************/ + +static void free_play_feature_delivery_switch_cores_handle( + play_feature_delivery_switch_cores_handle_t *pfd_switch_cores_handle) +{ + if (!pfd_switch_cores_handle) + return; + + if (pfd_switch_cores_handle->path_dir_libretro) + free(pfd_switch_cores_handle->path_dir_libretro); + + if (pfd_switch_cores_handle->path_libretro_info) + free(pfd_switch_cores_handle->path_libretro_info); + + if (pfd_switch_cores_handle->error_msg) + free(pfd_switch_cores_handle->error_msg); + + core_updater_list_free(pfd_switch_cores_handle->core_list); + + free(pfd_switch_cores_handle); + pfd_switch_cores_handle = NULL; +} + +static void task_play_feature_delivery_switch_cores_handler(retro_task_t *task) +{ + play_feature_delivery_switch_cores_handle_t *pfd_switch_cores_handle = NULL; + + if (!task) + goto task_finished; + + pfd_switch_cores_handle = (play_feature_delivery_switch_cores_handle_t*)task->state; + + if (!pfd_switch_cores_handle) + goto task_finished; + + if (task_get_cancelled(task)) + goto task_finished; + + switch (pfd_switch_cores_handle->status) + { + case PLAY_FEATURE_DELIVERY_SWITCH_CORES_BEGIN: + { + /* Query available cores + * Note: It should never be possible for this + * function (or the subsequent parsing of its + * output) to fail. We handle error conditions + * regardless, but there is no need to perform + * detailed checking - just report any problems + * to the user as a generic 'failed to retrieve + * core list' error */ + struct string_list *available_cores = + play_feature_delivery_available_cores(); + bool success = false; + + if (!available_cores) + { + pfd_switch_cores_handle->status = + PLAY_FEATURE_DELIVERY_SWITCH_CORES_END; + break; + } + + /* Populate core updater list */ + success = core_updater_list_parse_pfd_data( + pfd_switch_cores_handle->core_list, + pfd_switch_cores_handle->path_dir_libretro, + pfd_switch_cores_handle->path_libretro_info, + available_cores); + + string_list_free(available_cores); + + /* Cache list size */ + if (success) + pfd_switch_cores_handle->list_size = + core_updater_list_size(pfd_switch_cores_handle->core_list); + + if (pfd_switch_cores_handle->list_size < 1) + pfd_switch_cores_handle->status = + PLAY_FEATURE_DELIVERY_SWITCH_CORES_END; + else + pfd_switch_cores_handle->status = + PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE; + } + break; + case PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE: + { + const core_updater_list_entry_t *list_entry = NULL; + bool core_installed = false; + + /* Check whether we have reached the end + * of the list */ + if (pfd_switch_cores_handle->list_index >= + pfd_switch_cores_handle->list_size) + { + pfd_switch_cores_handle->status = + PLAY_FEATURE_DELIVERY_SWITCH_CORES_END; + break; + } + + /* Check whether current core is installed */ + if (core_updater_list_get_index( + pfd_switch_cores_handle->core_list, + pfd_switch_cores_handle->list_index, + &list_entry) && + path_is_valid(list_entry->local_core_path)) + { + core_installed = true; + pfd_switch_cores_handle->installed_index = + pfd_switch_cores_handle->list_index; + pfd_switch_cores_handle->status = + PLAY_FEATURE_DELIVERY_SWITCH_CORES_INSTALL_CORE; + } + + /* Update progress display */ + task_free_title(task); + + if (core_installed) + { + char task_title[PATH_MAX_LENGTH]; + + task_title[0] = '\0'; + + strlcpy(task_title, msg_hash_to_str(MSG_CHECKING_CORE), + sizeof(task_title)); + strlcat(task_title, list_entry->display_name, + sizeof(task_title)); + + task_set_title(task, strdup(task_title)); + } + else + task_set_title(task, strdup(msg_hash_to_str(MSG_SCANNING_CORES))); + + task_set_progress(task, + (pfd_switch_cores_handle->list_index * 100) / + pfd_switch_cores_handle->list_size); + + /* Increment list index */ + pfd_switch_cores_handle->list_index++; + } + break; + case PLAY_FEATURE_DELIVERY_SWITCH_CORES_INSTALL_CORE: + { + const core_updater_list_entry_t *list_entry = NULL; + + /* Get list entry + * > In the event of an error, just return + * to PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE + * state */ + if (!core_updater_list_get_index( + pfd_switch_cores_handle->core_list, + pfd_switch_cores_handle->installed_index, + &list_entry)) + { + pfd_switch_cores_handle->status = + PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE; + break; + } + + /* Check whether core is already installed via + * play feature delivery */ + if (play_feature_delivery_core_installed( + list_entry->remote_filename)) + { + pfd_switch_cores_handle->status = + PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE; + break; + } + + /* Existing core is not installed via + * play feature delivery + * > Request installation/replacement */ + pfd_switch_cores_handle->install_task = (retro_task_t*) + task_push_play_feature_delivery_core_install( + pfd_switch_cores_handle->core_list, + list_entry->remote_filename, + true); + + /* Again, if an error occurred, just return to + * PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE + * state */ + if (!pfd_switch_cores_handle->install_task) + pfd_switch_cores_handle->status = + PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE; + else + { + char task_title[PATH_MAX_LENGTH]; + + task_title[0] = '\0'; + + /* Update task title */ + task_free_title(task); + + strlcpy(task_title, msg_hash_to_str(MSG_UPDATING_CORE), + sizeof(task_title)); + strlcat(task_title, list_entry->display_name, + sizeof(task_title)); + + task_set_title(task, strdup(task_title)); + + /* Wait for installation to complete */ + pfd_switch_cores_handle->status = + PLAY_FEATURE_DELIVERY_SWITCH_CORES_WAIT_INSTALL; + } + } + break; + case PLAY_FEATURE_DELIVERY_SWITCH_CORES_WAIT_INSTALL: + { + bool install_complete = false; + const char* error_msg = NULL; + + /* > If task is running, check 'is finished' status + * > If task is NULL, then it is finished by + * definition */ + if (pfd_switch_cores_handle->install_task) + { + error_msg = task_get_error( + pfd_switch_cores_handle->install_task); + install_complete = task_get_finished( + pfd_switch_cores_handle->install_task); + } + else + install_complete = true; + + /* Check for installation errors + * > These should be considered 'serious', and + * will trigger the task to end early */ + if (!string_is_empty(error_msg)) + { + pfd_switch_cores_handle->error_msg = strdup(error_msg); + pfd_switch_cores_handle->install_task = NULL; + pfd_switch_cores_handle->status = + PLAY_FEATURE_DELIVERY_SWITCH_CORES_END; + break; + } + + /* If installation is complete, return to + * PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE + * state */ + if (install_complete) + { + pfd_switch_cores_handle->install_task = NULL; + pfd_switch_cores_handle->status = + PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE; + } + } + break; + case PLAY_FEATURE_DELIVERY_SWITCH_CORES_END: + { + const char *task_title = msg_hash_to_str(MSG_CORE_LIST_FAILED); + + /* Set final task title */ + task_free_title(task); + + /* > Check whether core list was generated + * successfully */ + if (pfd_switch_cores_handle->list_size > 0) + { + /* Check whether any installation errors occurred */ + if (!string_is_empty(pfd_switch_cores_handle->error_msg)) + task_title = pfd_switch_cores_handle->error_msg; + else + task_title = msg_hash_to_str(MSG_ALL_CORES_SWITCHED_PFD); + } + + task_set_title(task, strdup(task_title)); + } + /* fall-through */ + default: + task_set_progress(task, 100); + goto task_finished; + } + + return; + +task_finished: + + if (task) + task_set_finished(task, true); + + free_play_feature_delivery_switch_cores_handle(pfd_switch_cores_handle); +} + +static bool task_play_feature_delivery_switch_cores_finder( + retro_task_t *task, void *user_data) +{ + if (!task) + return false; + + if (task->handler == task_play_feature_delivery_switch_cores_handler) + return true; + + return false; +} + +void task_push_play_feature_delivery_switch_installed_cores( + const char *path_dir_libretro, + const char *path_libretro_info) +{ + task_finder_data_t find_data; + retro_task_t *task = NULL; + play_feature_delivery_switch_cores_handle_t *pfd_switch_cores_handle = + (play_feature_delivery_switch_cores_handle_t*) + calloc(1, sizeof(play_feature_delivery_switch_cores_handle_t)); + + /* Sanity check */ + if (string_is_empty(path_dir_libretro) || + string_is_empty(path_libretro_info) || + !pfd_switch_cores_handle || + !play_feature_delivery_enabled()) + goto error; + + /* Only one instance of this task my run at a time */ + find_data.func = task_play_feature_delivery_switch_cores_finder; + find_data.userdata = NULL; + + if (task_queue_find(&find_data)) + goto error; + + /* Configure handle */ + pfd_switch_cores_handle->path_dir_libretro = strdup(path_dir_libretro); + pfd_switch_cores_handle->path_libretro_info = strdup(path_libretro_info); + pfd_switch_cores_handle->error_msg = NULL; + pfd_switch_cores_handle->core_list = core_updater_list_init(); + pfd_switch_cores_handle->install_task = NULL; + pfd_switch_cores_handle->list_size = 0; + pfd_switch_cores_handle->list_index = 0; + pfd_switch_cores_handle->installed_index = 0; + pfd_switch_cores_handle->status = PLAY_FEATURE_DELIVERY_SWITCH_CORES_BEGIN; + + if (!pfd_switch_cores_handle->core_list) + goto error; + + /* Create task */ + task = task_init(); + + if (!task) + goto error; + + /* Configure task */ + task->handler = task_play_feature_delivery_switch_cores_handler; + task->state = pfd_switch_cores_handle; + task->title = strdup(msg_hash_to_str(MSG_SCANNING_CORES)); + task->alternative_look = true; + task->progress = 0; + + /* Push task */ + task_queue_push(task); + return; error: @@ -1774,7 +2241,7 @@ error: } /* Clean up handle */ - free_play_feature_delivery_install_handle(pfd_install_handle); + free_play_feature_delivery_switch_cores_handle(pfd_switch_cores_handle); } #endif diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h index bc44eacaf4..ab50669b6d 100644 --- a/tasks/tasks_internal.h +++ b/tasks/tasks_internal.h @@ -97,9 +97,13 @@ void task_push_update_installed_cores( const char *path_dir_libretro, const char *path_dir_core_assets); #if defined(ANDROID) -void task_push_play_feature_delivery_core_install( +void *task_push_play_feature_delivery_core_install( core_updater_list_t* core_list, - const char *filename); + const char *filename, + bool mute); +void task_push_play_feature_delivery_switch_installed_cores( + const char *path_dir_libretro, + const char *path_libretro_info); #endif bool task_push_pl_entry_thumbnail_download(