From 2782b872679dcf333ed4c4d9379b06856066e8f5 Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Tue, 16 Jun 2020 11:11:09 +0100 Subject: [PATCH] Add option to automatically backup cores when updating --- config.def.h | 8 + configuration.c | 3 + configuration.h | 3 + core_backup.c | 28 +++- core_backup.h | 6 + intl/msg_hash_lbl.h | 8 + intl/msg_hash_us.h | 24 +++ menu/cbs/menu_cbs_ok.c | 36 ++-- menu/cbs/menu_cbs_sublabel.c | 8 + menu/menu_displaylist.c | 12 +- menu/menu_setting.c | 32 ++++ msg_hash.h | 4 + tasks/task_core_backup.c | 227 +++++++++++++++++++------ tasks/task_core_updater.c | 310 +++++++++++++++++++++++++---------- tasks/tasks_internal.h | 30 +++- 15 files changed, 580 insertions(+), 159 deletions(-) diff --git a/config.def.h b/config.def.h index 6de40d621d..3939f74548 100644 --- a/config.def.h +++ b/config.def.h @@ -965,6 +965,14 @@ static const uint16_t network_remote_base_port = 55400; #define DEFAULT_NETWORK_BUILDBOT_AUTO_EXTRACT_ARCHIVE true #define DEFAULT_NETWORK_BUILDBOT_SHOW_EXPERIMENTAL_CORES false +/* Automatically create a backup whenever a core is + * updated via the online updater */ +#define DEFAULT_CORE_UPDATER_AUTO_BACKUP false +/* Number of automatic core backups to retain + * (oldest backup will be deleted when creating + * a new one) */ +#define DEFAULT_CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE 1 + #if defined(ANDROID) || defined(IOS) #define DEFAULT_NETWORK_ON_DEMAND_THUMBNAILS true #else diff --git a/configuration.c b/configuration.c index f43fcf6b02..f28e885e62 100644 --- a/configuration.c +++ b/configuration.c @@ -1438,6 +1438,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("video_font_enable", &settings->bools.video_font_enable, true, DEFAULT_FONT_ENABLE, false); SETTING_BOOL("core_updater_auto_extract_archive", &settings->bools.network_buildbot_auto_extract_archive, true, DEFAULT_NETWORK_BUILDBOT_AUTO_EXTRACT_ARCHIVE, false); SETTING_BOOL("core_updater_show_experimental_cores", &settings->bools.network_buildbot_show_experimental_cores, true, DEFAULT_NETWORK_BUILDBOT_SHOW_EXPERIMENTAL_CORES, false); + SETTING_BOOL("core_updater_auto_backup", &settings->bools.core_updater_auto_backup, true, DEFAULT_CORE_UPDATER_AUTO_BACKUP, false); SETTING_BOOL("camera_allow", &settings->bools.camera_allow, true, false, false); SETTING_BOOL("discord_allow", &settings->bools.discord_enable, true, false, false); #if defined(VITA) @@ -1914,6 +1915,8 @@ static struct config_uint_setting *populate_settings_uint( SETTING_UINT("playlist_sublabel_last_played_style", &settings->uints.playlist_sublabel_last_played_style, true, DEFAULT_PLAYLIST_SUBLABEL_LAST_PLAYED_STYLE, false); #endif + SETTING_UINT("core_updater_auto_backup_history_size", &settings->uints.core_updater_auto_backup_history_size, true, DEFAULT_CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE, false); + *size = count; return tmp; diff --git a/configuration.h b/configuration.h index ff75c10806..c906a79b65 100644 --- a/configuration.h +++ b/configuration.h @@ -286,6 +286,7 @@ typedef struct settings bool network_buildbot_auto_extract_archive; bool network_buildbot_show_experimental_cores; bool network_on_demand_thumbnails; + bool core_updater_auto_backup; /* UI */ bool ui_menubar_enable; @@ -621,6 +622,8 @@ typedef struct settings unsigned ai_service_mode; unsigned ai_service_target_lang; unsigned ai_service_source_lang; + + unsigned core_updater_auto_backup_history_size; } uints; struct diff --git a/core_backup.c b/core_backup.c index 61f5dd28a7..71097ffb66 100644 --- a/core_backup.c +++ b/core_backup.c @@ -27,7 +27,6 @@ #include "frontend/frontend_driver.h" #include "file_path_special.h" -#include "core_info.h" #include "verbosity.h" #include "core_backup.h" @@ -532,7 +531,8 @@ core_backup_list_t *core_backup_list_init( if (dir_list->size < 1) goto error; - /* Ensure list is sorted in alphabetical order */ + /* Ensure list is sorted in alphabetical order + * > This corresponds to 'timestamp' order */ dir_list_sort(dir_list, true); /* Create core backup list */ @@ -622,6 +622,30 @@ size_t core_backup_list_size(core_backup_list_t *backup_list) return backup_list->size; } +/* Returns number of entries of specified 'backup mode' + * (manual or automatic) in core backup list */ +size_t core_backup_list_get_num_backups( + core_backup_list_t *backup_list, + enum core_backup_mode backup_mode) +{ + size_t i; + size_t num_backups = 0; + + if (!backup_list || !backup_list->entries) + return 0; + + for (i = 0; i < backup_list->size; i++) + { + core_backup_list_entry_t *current_entry = &backup_list->entries[i]; + + if (current_entry && + (current_entry->backup_mode == backup_mode)) + num_backups++; + } + + return num_backups; +} + /* Fetches core backup list entry corresponding * to the specified entry index. * Returns false if index is invalid. */ diff --git a/core_backup.h b/core_backup.h index d57ca89e32..4678295b1a 100644 --- a/core_backup.h +++ b/core_backup.h @@ -132,6 +132,12 @@ void core_backup_list_free(core_backup_list_t *backup_list); /* Returns number of entries in core backup list */ size_t core_backup_list_size(core_backup_list_t *backup_list); +/* Returns number of entries of specified 'backup mode' + * (manual or automatic) in core backup list */ +size_t core_backup_list_get_num_backups( + core_backup_list_t *backup_list, + enum core_backup_mode backup_mode); + /* Fetches core backup list entry corresponding * to the specified entry index. * Returns false if index is invalid. */ diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index c203627376..ff02eaa898 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -516,6 +516,14 @@ MSG_HASH( MENU_ENUM_LABEL_CORE_UPDATER_SHOW_EXPERIMENTAL_CORES, "core_updater_show_experimental_cores" ) +MSG_HASH( + MENU_ENUM_LABEL_CORE_UPDATER_AUTO_BACKUP, + "core_updater_auto_backup" + ) +MSG_HASH( + MENU_ENUM_LABEL_CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE, + "core_updater_auto_backup_history_size" + ) MSG_HASH( MENU_ENUM_LABEL_CORE_UPDATER_BUILDBOT_URL, "core_updater_buildbot_url" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index e0b2ebeeea..840456805a 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -481,6 +481,10 @@ MSG_HASH( MENU_ENUM_SUBLABEL_CORE_DELETE_BACKUP_LIST, "Remove a file from the list of archived backups." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CORE_BACKUP_MODE_AUTO, + "[Auto]" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CORE_BACKUP_CRC, "CRC32: " @@ -4598,6 +4602,22 @@ MSG_HASH( MENU_ENUM_SUBLABEL_CORE_UPDATER_SHOW_EXPERIMENTAL_CORES, "Include 'experimental' cores in the Core Downloader list. These are typically for development/testing purposes only, and are not recommended for general use." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CORE_UPDATER_AUTO_BACKUP, + "Backup Cores When Updating" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_CORE_UPDATER_AUTO_BACKUP, + "Automatically create a backup of any installed cores when performing an online update. Enables easy rollback to a working core if an update introduces a regression." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE, + "Core Backup History Size" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE, + "Specifies how many automatically generated backups to keep for each installed core. When this limit is reached, creating a new backup via an online update will delete the oldest backup. Note: Manual core backups are unaffected by this setting." + ) /* Settings > Playlists */ @@ -10862,6 +10882,10 @@ MSG_HASH( MSG_BACKING_UP_CORE, "Backing up core: " ) +MSG_HASH( + MSG_PRUNING_CORE_BACKUP_HISTORY, + "Removing obsolete backups: " + ) MSG_HASH( MSG_CORE_BACKUP_COMPLETE, "Core backup complete: " diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index de63d512a2..56a0ff0887 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -4338,16 +4338,20 @@ static int action_ok_core_updater_download(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { #ifdef HAVE_NETWORKING - core_updater_list_t *core_list = core_updater_list_get_cached(); - settings_t *settings = config_get_ptr(); - const char *path_dir_libretro = settings->paths.directory_libretro; + core_updater_list_t *core_list = core_updater_list_get_cached(); + settings_t *settings = config_get_ptr(); + bool auto_backup = settings->bools.core_updater_auto_backup; + unsigned auto_backup_history_size = settings->uints.core_updater_auto_backup_history_size; + const char *path_dir_libretro = settings->paths.directory_libretro; + const char *path_dir_core_assets = settings->paths.directory_core_assets; if (!core_list) return menu_cbs_exit(); task_push_core_updater_download( - core_list, path, false, true, - path_dir_libretro); + core_list, path, 0, false, + auto_backup, (size_t)auto_backup_history_size, + path_dir_libretro, path_dir_core_assets); #endif return 0; @@ -4357,14 +4361,19 @@ static int action_ok_core_updater_download(const char *path, static int action_ok_update_installed_cores(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; + settings_t *settings = config_get_ptr(); + bool auto_backup = settings->bools.core_updater_auto_backup; + unsigned auto_backup_history_size = settings->uints.core_updater_auto_backup_history_size; + const char *path_dir_libretro = settings->paths.directory_libretro; + const char *path_dir_core_assets = settings->paths.directory_core_assets; /* Ensure networking is initialised */ generic_action_ok_command(CMD_EVENT_NETWORK_INIT); /* Push update task */ - task_push_update_installed_cores(path_dir_libretro); + task_push_update_installed_cores( + auto_backup, auto_backup_history_size, + path_dir_libretro, path_dir_core_assets); return 0; } @@ -6253,15 +6262,16 @@ static int action_ok_netplay_disconnect(const char *path, static int action_ok_core_create_backup(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - const char *core_path = label; - settings_t *settings = config_get_ptr(); - const char *dir_core_assets = settings->paths.directory_core_assets; + const char *core_path = label; + settings_t *settings = config_get_ptr(); + unsigned auto_backup_history_size = settings->uints.core_updater_auto_backup_history_size; + const char *dir_core_assets = settings->paths.directory_core_assets; if (string_is_empty(core_path)) return -1; - task_push_core_backup(core_path, 0, CORE_BACKUP_MODE_MANUAL, - dir_core_assets, false); + task_push_core_backup(core_path, NULL, 0, CORE_BACKUP_MODE_MANUAL, + (size_t)auto_backup_history_size, dir_core_assets, false); return 0; } diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index bf1614e479..df3d106c06 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -525,6 +525,8 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_overlay_show_physical_inputs_p DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_updater_buildbot_assets_url, MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_updater_auto_extract_archive, MENU_ENUM_SUBLABEL_CORE_UPDATER_AUTO_EXTRACT_ARCHIVE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_updater_show_experimental_cores, MENU_ENUM_SUBLABEL_CORE_UPDATER_SHOW_EXPERIMENTAL_CORES) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_updater_auto_backup, MENU_ENUM_SUBLABEL_CORE_UPDATER_AUTO_BACKUP) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_updater_auto_backup_history_size, MENU_ENUM_SUBLABEL_CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_netplay_refresh_rooms, MENU_ENUM_SUBLABEL_NETPLAY_REFRESH_ROOMS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_rename_entry, MENU_ENUM_SUBLABEL_RENAME_ENTRY) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_delete_entry, MENU_ENUM_SUBLABEL_DELETE_ENTRY) @@ -2375,6 +2377,12 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_CORE_UPDATER_SHOW_EXPERIMENTAL_CORES: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_core_updater_show_experimental_cores); break; + case MENU_ENUM_LABEL_CORE_UPDATER_AUTO_BACKUP: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_core_updater_auto_backup); + break; + case MENU_ENUM_LABEL_CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_core_updater_auto_backup_history_size); + break; case MENU_ENUM_LABEL_CORE_UPDATER_BUILDBOT_URL: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_core_updater_buildbot_url); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index c7a092f3e5..3ae2508148 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -453,7 +453,7 @@ static unsigned menu_displaylist_parse_core_backup_list( if (core_backup_list_get_index(backup_list, i, &entry) && entry && !string_is_empty(entry->backup_path)) { - char timestamp[32]; + char timestamp[128]; char crc[16]; timestamp[0] = '\0'; @@ -465,6 +465,14 @@ static unsigned menu_displaylist_parse_core_backup_list( core_backup_list_get_entry_crc_str( entry, crc, sizeof(crc)); + /* Append 'auto backup' tag to timestamp, if required */ + if (entry->backup_mode == CORE_BACKUP_MODE_AUTO) + { + strlcat(timestamp, " ", sizeof(timestamp)); + strlcat(timestamp, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_BACKUP_MODE_AUTO), + sizeof(timestamp)); + } + /* Add menu entry */ if (menu_entries_append_enum(info->list, timestamp, @@ -7323,6 +7331,8 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_BUILDBOT_ASSETS_URL, PARSE_ONLY_STRING}, {MENU_ENUM_LABEL_CORE_UPDATER_AUTO_EXTRACT_ARCHIVE, PARSE_ONLY_BOOL}, {MENU_ENUM_LABEL_CORE_UPDATER_SHOW_EXPERIMENTAL_CORES, PARSE_ONLY_BOOL}, + {MENU_ENUM_LABEL_CORE_UPDATER_AUTO_BACKUP, PARSE_ONLY_BOOL}, + {MENU_ENUM_LABEL_CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE, PARSE_ONLY_UINT}, }; for (i = 0; i < ARRAY_SIZE(build_list); i++) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 533f946182..66689ee4b4 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -16162,6 +16162,38 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE ); + + CONFIG_BOOL( + list, list_info, + &settings->bools.core_updater_auto_backup, + MENU_ENUM_LABEL_CORE_UPDATER_AUTO_BACKUP, + MENU_ENUM_LABEL_VALUE_CORE_UPDATER_AUTO_BACKUP, + DEFAULT_CORE_UPDATER_AUTO_BACKUP, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + + CONFIG_UINT( + list, list_info, + &settings->uints.core_updater_auto_backup_history_size, + MENU_ENUM_LABEL_CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE, + MENU_ENUM_LABEL_VALUE_CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE, + DEFAULT_CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_COMBOBOX; + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + (*list)[list_info->index - 1].offset_by = 1; + menu_settings_list_current_add_range(list, list_info, (*list)[list_info->index - 1].offset_by, 500, 1, true, true); #endif END_SUB_GROUP(list, list_info, parent_group); END_GROUP(list, list_info, parent_group); diff --git a/msg_hash.h b/msg_hash.h index 050816e4fd..ae7df97325 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1955,6 +1955,8 @@ enum msg_hash_enums MENU_LABEL(CORE_MANAGER_LIST), MENU_LABEL(CORE_UPDATER_AUTO_EXTRACT_ARCHIVE), MENU_LABEL(CORE_UPDATER_SHOW_EXPERIMENTAL_CORES), + MENU_LABEL(CORE_UPDATER_AUTO_BACKUP), + MENU_LABEL(CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE), MENU_LABEL(CORE_UPDATER_BUILDBOT_URL), MENU_LABEL(BUILDBOT_ASSETS_URL), MENU_LABEL(CORE_SET_SUPPORTS_NO_CONTENT_ENABLE), @@ -2014,11 +2016,13 @@ enum msg_hash_enums MENU_ENUM_LABEL_CORE_RESTORE_BACKUP_ENTRY, MENU_ENUM_LABEL_CORE_DELETE_BACKUP_ENTRY, + MENU_ENUM_LABEL_VALUE_CORE_BACKUP_MODE_AUTO, MENU_ENUM_LABEL_VALUE_CORE_BACKUP_CRC, MSG_CORE_BACKUP_SCANNING_CORE, MSG_CORE_BACKUP_ALREADY_EXISTS, MSG_BACKING_UP_CORE, + MSG_PRUNING_CORE_BACKUP_HISTORY, MSG_CORE_BACKUP_COMPLETE, MSG_CORE_RESTORATION_ALREADY_INSTALLED, MSG_RESTORING_CORE, diff --git a/tasks/task_core_backup.c b/tasks/task_core_backup.c index aa16e15051..15bdbb772d 100644 --- a/tasks/task_core_backup.c +++ b/tasks/task_core_backup.c @@ -44,6 +44,8 @@ enum core_backup_status CORE_BACKUP_CHECK_CRC, CORE_BACKUP_PRE_ITERATE, CORE_BACKUP_ITERATE, + CORE_BACKUP_CHECK_HISTORY, + CORE_BACKUP_PRUNE_HISTORY, CORE_BACKUP_END, CORE_RESTORE_GET_CORE_CRC, CORE_RESTORE_GET_BACKUP_CRC, @@ -61,6 +63,9 @@ typedef struct core_backup_handle char *backup_path; enum core_backup_type backup_type; enum core_backup_mode backup_mode; + size_t auto_backup_history_size; + size_t num_auto_backups_to_remove; + size_t backup_index; int64_t core_file_size; int64_t backup_file_size; int64_t file_data_read; @@ -348,7 +353,13 @@ static void task_core_backup_handler(retro_task_t *task) backup_handle->backup_file = NULL; backup_handle->success = true; - backup_handle->status = CORE_BACKUP_END; + + /* If this is an automatic backup, check whether + * any old backup files should be deleted. + * In all other cases, backup is complete */ + backup_handle->status = (backup_handle->backup_mode == + CORE_BACKUP_MODE_AUTO) ? + CORE_BACKUP_CHECK_HISTORY : CORE_BACKUP_END; break; } @@ -368,6 +379,103 @@ static void task_core_backup_handler(retro_task_t *task) (backup_handle->file_data_read * 100) / backup_handle->core_file_size); } break; + case CORE_BACKUP_CHECK_HISTORY: + { + size_t num_backups; + + /* Sanity check: We can only reach this stage + * when performing automatic backups, but verify + * that this is true (i.e. don't inadvertently + * delete backups of the wrong type if someone + * edits the CORE_BACKUP_ITERATE case statement...) */ + if (backup_handle->backup_mode != CORE_BACKUP_MODE_AUTO) + { + backup_handle->status = CORE_BACKUP_END; + break; + } + + /* This is an automated backup + * > Fetch number of existing auto backups + * in the list + * > If we reach this stage then a new backup + * has been created successfully -> increment + * count by one */ + num_backups = core_backup_list_get_num_backups( + backup_handle->backup_list, + CORE_BACKUP_MODE_AUTO); + num_backups++; + + /* Check whether we have exceeded the backup + * history size limit */ + if (num_backups > backup_handle->auto_backup_history_size) + { + char task_title[PATH_MAX_LENGTH]; + + task_title[0] = '\0'; + + /* Get number of old backups to remove */ + backup_handle->num_auto_backups_to_remove = num_backups - + backup_handle->auto_backup_history_size; + + /* Update task title */ + task_free_title(task); + strlcpy(task_title, msg_hash_to_str(MSG_PRUNING_CORE_BACKUP_HISTORY), + sizeof(task_title)); + strlcat(task_title, backup_handle->core_name, sizeof(task_title)); + task_set_title(task, strdup(task_title)); + + /* Go to history clean-up phase */ + backup_handle->status = CORE_BACKUP_PRUNE_HISTORY; + } + /* No backups to remove - task is complete */ + else + backup_handle->status = CORE_BACKUP_END; + } + break; + case CORE_BACKUP_PRUNE_HISTORY: + { + size_t list_size = core_backup_list_size(backup_handle->backup_list); + + /* The core backup list is automatically sorted + * by timestamp - simply loop from start to end + * and delete automatic backups until the required + * number have been removed */ + while ((backup_handle->backup_index < list_size) && + (backup_handle->num_auto_backups_to_remove > 0)) + { + const core_backup_list_entry_t *entry = NULL; + + if (core_backup_list_get_index( + backup_handle->backup_list, + backup_handle->backup_index, + &entry) && + entry && + (entry->backup_mode == CORE_BACKUP_MODE_AUTO)) + { + /* Delete backup file (if it exists) */ + if (path_is_valid(entry->backup_path)) + filestream_delete(entry->backup_path); + + backup_handle->num_auto_backups_to_remove--; + backup_handle->backup_index++; + + /* Break here - only remove one file per + * iteration of the task */ + break; + } + + backup_handle->backup_index++; + } + + /* Check whether all old backups have been + * removed (also handle error conditions by + * ensuring we quit if end of backup list is + * reached...) */ + if ((backup_handle->num_auto_backups_to_remove < 1) || + (backup_handle->backup_index >= list_size)) + backup_handle->status = CORE_BACKUP_END; + } + break; case CORE_BACKUP_END: { char task_title[PATH_MAX_LENGTH]; @@ -409,14 +517,20 @@ task_finished: free_core_backup_handle(backup_handle); } -/* Note: If crc is set to 0, crc of core_path file will - * be calculated automatically */ -void *task_push_core_backup(const char *core_path, +/* Note 1: If crc is set to 0, crc of core_path file will + * be calculated automatically + * Note 2: If core_display_name is set to NULL, display + * name will be determined automatically + * > core_display_name *must* be set to a non-empty + * string if task_push_core_backup() is *not* called + * on the main thread */ +void *task_push_core_backup( + const char *core_path, const char *core_display_name, uint32_t crc, enum core_backup_mode backup_mode, + size_t auto_backup_history_size, const char *dir_core_assets, bool mute) { task_finder_data_t find_data; - core_info_ctx_find_t core_info; const char *core_name = NULL; retro_task_t *task = NULL; core_backup_handle_t *backup_handle = NULL; @@ -438,20 +552,27 @@ void *task_push_core_backup(const char *core_path, goto error; /* Get core name */ - core_info.inf = NULL; - core_info.path = core_path; - - /* If core is found, use display name */ - if (core_info_find(&core_info) && - core_info.inf->display_name) - core_name = core_info.inf->display_name; + if (!string_is_empty(core_display_name)) + core_name = core_display_name; else { - /* If not, use core file name */ - core_name = path_basename(core_path); + core_info_ctx_find_t core_info; - if (string_is_empty(core_name)) - goto error; + core_info.inf = NULL; + core_info.path = core_path; + + /* If core is found, use display name */ + if (core_info_find(&core_info) && + core_info.inf->display_name) + core_name = core_info.inf->display_name; + else + { + /* If not, use core file name */ + core_name = path_basename(core_path); + + if (string_is_empty(core_name)) + goto error; + } } /* Configure handle */ @@ -460,23 +581,26 @@ void *task_push_core_backup(const char *core_path, if (!backup_handle) goto error; - backup_handle->dir_core_assets = string_is_empty(dir_core_assets) ? NULL : strdup(dir_core_assets); - backup_handle->core_path = strdup(core_path); - backup_handle->core_name = strdup(core_name); - backup_handle->backup_path = NULL; - backup_handle->backup_type = CORE_BACKUP_TYPE_ARCHIVE; - backup_handle->backup_mode = backup_mode; - backup_handle->core_file_size = 0; - backup_handle->backup_file_size = 0; - backup_handle->file_data_read = 0; - backup_handle->core_crc = crc; - backup_handle->backup_crc = 0; - backup_handle->crc_match = false; - backup_handle->success = false; - backup_handle->core_file = NULL; - backup_handle->backup_file = NULL; - backup_handle->backup_list = NULL; - backup_handle->status = CORE_BACKUP_BEGIN; + backup_handle->dir_core_assets = string_is_empty(dir_core_assets) ? NULL : strdup(dir_core_assets); + backup_handle->core_path = strdup(core_path); + backup_handle->core_name = strdup(core_name); + backup_handle->backup_path = NULL; + backup_handle->backup_type = CORE_BACKUP_TYPE_ARCHIVE; + backup_handle->backup_mode = backup_mode; + backup_handle->auto_backup_history_size = auto_backup_history_size; + backup_handle->num_auto_backups_to_remove = 0; + backup_handle->backup_index = 0; + backup_handle->core_file_size = 0; + backup_handle->backup_file_size = 0; + backup_handle->file_data_read = 0; + backup_handle->core_crc = crc; + backup_handle->backup_crc = 0; + backup_handle->crc_match = false; + backup_handle->success = false; + backup_handle->core_file = NULL; + backup_handle->backup_file = NULL; + backup_handle->backup_list = NULL; + backup_handle->status = CORE_BACKUP_BEGIN; /* Create task */ task = task_init(); @@ -899,23 +1023,26 @@ bool task_push_core_restore(const char *backup_path, const char *dir_libretro, if (!backup_handle) goto error; - backup_handle->dir_core_assets = NULL; - backup_handle->core_path = strdup(core_path); - backup_handle->core_name = strdup(core_name); - backup_handle->backup_path = strdup(backup_path); - backup_handle->backup_type = backup_type; - backup_handle->backup_mode = CORE_BACKUP_MODE_MANUAL; - backup_handle->core_file_size = 0; - backup_handle->backup_file_size = 0; - backup_handle->file_data_read = 0; - backup_handle->core_crc = 0; - backup_handle->backup_crc = 0; - backup_handle->crc_match = false; - backup_handle->success = false; - backup_handle->core_file = NULL; - backup_handle->backup_file = NULL; - backup_handle->backup_list = NULL; - backup_handle->status = CORE_RESTORE_GET_CORE_CRC; + backup_handle->dir_core_assets = NULL; + backup_handle->core_path = strdup(core_path); + backup_handle->core_name = strdup(core_name); + backup_handle->backup_path = strdup(backup_path); + backup_handle->backup_type = backup_type; + backup_handle->backup_mode = CORE_BACKUP_MODE_MANUAL; + backup_handle->auto_backup_history_size = 0; + backup_handle->num_auto_backups_to_remove = 0; + backup_handle->backup_index = 0; + backup_handle->core_file_size = 0; + backup_handle->backup_file_size = 0; + backup_handle->file_data_read = 0; + backup_handle->core_crc = 0; + backup_handle->backup_crc = 0; + backup_handle->crc_match = false; + backup_handle->success = false; + backup_handle->core_file = NULL; + backup_handle->backup_file = NULL; + backup_handle->backup_list = NULL; + backup_handle->status = CORE_RESTORE_GET_CORE_CRC; /* Create task */ task = task_init(); diff --git a/tasks/task_core_updater.c b/tasks/task_core_updater.c index e0e5921ae1..8cfaa8ff47 100644 --- a/tasks/task_core_updater.c +++ b/tasks/task_core_updater.c @@ -66,6 +66,9 @@ typedef struct core_updater_list_handle enum core_updater_download_status { CORE_UPDATER_DOWNLOAD_BEGIN = 0, + CORE_UPDATER_DOWNLOAD_START_BACKUP, + CORE_UPDATER_DOWNLOAD_WAIT_BACKUP, + CORE_UPDATER_DOWNLOAD_START_TRANSFER, CORE_UPDATER_DOWNLOAD_WAIT_TRANSFER, CORE_UPDATER_DOWNLOAD_WAIT_DECOMPRESS, CORE_UPDATER_DOWNLOAD_END @@ -73,14 +76,17 @@ enum core_updater_download_status typedef struct core_updater_download_handle { + bool auto_backup; + size_t auto_backup_history_size; char *path_dir_libretro; + char *path_dir_core_assets; char *remote_filename; char *remote_core_path; char *local_download_path; char *local_core_path; char *display_name; + uint32_t local_crc; uint32_t remote_crc; - bool check_crc; bool crc_match; retro_task_t *http_task; bool http_task_finished; @@ -88,6 +94,8 @@ typedef struct core_updater_download_handle retro_task_t *decompress_task; bool decompress_task_finished; bool decompress_task_complete; + bool backup_enabled; + retro_task_t *backup_task; enum core_updater_download_status status; } core_updater_download_handle_t; @@ -104,7 +112,10 @@ enum update_installed_cores_status typedef struct update_installed_cores_handle { + bool auto_backup; + size_t auto_backup_history_size; char *path_dir_libretro; + char *path_dir_core_assets; core_updater_list_t* core_list; retro_task_t *list_task; retro_task_t *download_task; @@ -119,40 +130,37 @@ typedef struct update_installed_cores_handle /* Utility functions */ /*********************/ -/* Returns true if local core has the same crc - * value as core on buildbot */ -static bool local_core_matches_remote_crc( - const char *local_core_path, uint32_t remote_crc) +/* Returns CRC32 of specified core file */ +static uint32_t task_core_updater_get_core_crc(const char *core_path) { - /* Sanity check */ - if (string_is_empty(local_core_path) || (remote_crc == 0)) - return false; + if (string_is_empty(core_path)) + return 0; - if (path_is_valid(local_core_path)) + if (path_is_valid(core_path)) { /* Open core file */ - intfstream_t *local_core_file = intfstream_open_file( - local_core_path, RETRO_VFS_FILE_ACCESS_READ, + intfstream_t *core_file = intfstream_open_file( + core_path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); - if (local_core_file) + if (core_file) { uint32_t crc = 0; + /* Get crc value */ - bool success = intfstream_get_crc(local_core_file, &crc); + bool success = intfstream_get_crc(core_file, &crc); /* Close core file */ - intfstream_close(local_core_file); - free(local_core_file); - local_core_file = NULL; + intfstream_close(core_file); + free(core_file); + core_file = NULL; - /* Check whether crc matches remote file */ - if (success && (crc != 0) && (crc == remote_crc)) - return true; + if (success) + return crc; } } - return false; + return 0; } /*************************/ @@ -184,7 +192,7 @@ finish: /* Log any error messages */ if (!success) - RARCH_ERR("Download of core list '%s' failed: %s\n", + RARCH_ERR("[core updater] Download of core list '%s' failed: %s\n", (transf ? transf->path: "unknown"), (err ? err : "unknown")); @@ -482,7 +490,7 @@ static void cb_decompress_task_core_updater_download( /* Log any error messages */ if (!string_is_empty(err)) - RARCH_ERR("%s", err); + RARCH_ERR("[core updater] %s", err); } void cb_http_task_core_updater_download( @@ -576,7 +584,7 @@ finish: /* Log any error messages */ if (!string_is_empty(err)) - RARCH_ERR("Download of '%s' failed: %s\n", + RARCH_ERR("[core updater] Download of '%s' failed: %s\n", (transf ? transf->path: "unknown"), err); if (data) @@ -598,6 +606,9 @@ static void free_core_updater_download_handle(core_updater_download_handle_t *do if (download_handle->path_dir_libretro) free(download_handle->path_dir_libretro); + if (download_handle->path_dir_core_assets) + free(download_handle->path_dir_core_assets); + if (download_handle->remote_filename) free(download_handle->remote_filename); @@ -636,13 +647,15 @@ static void task_core_updater_download_handler(retro_task_t *task) { case CORE_UPDATER_DOWNLOAD_BEGIN: { - file_transfer_t *transf = NULL; + /* Get CRC of existing core, if required */ + if (download_handle->local_crc == 0) + download_handle->local_crc = task_core_updater_get_core_crc( + download_handle->local_core_path); - /* Check CRC of existing core, if required */ - if (download_handle->check_crc) - download_handle->crc_match = local_core_matches_remote_crc( - download_handle->local_core_path, - download_handle->remote_crc); + /* Check whether existing core and remote core + * have the same CRC */ + download_handle->crc_match = (download_handle->local_crc != 0) && + (download_handle->local_crc == download_handle->remote_crc); /* If CRC matches, end task immediately */ if (download_handle->crc_match) @@ -651,6 +664,99 @@ static void task_core_updater_download_handler(retro_task_t *task) break; } + /* If automatic backups are enabled and core is + * already installed, trigger a backup - otherwise, + * initialise download */ + download_handle->backup_enabled = download_handle->auto_backup && + path_is_valid(download_handle->local_core_path); + + download_handle->status = download_handle->backup_enabled ? + CORE_UPDATER_DOWNLOAD_START_BACKUP : + CORE_UPDATER_DOWNLOAD_START_TRANSFER; + } + break; + case CORE_UPDATER_DOWNLOAD_START_BACKUP: + { + /* Request core backup */ + download_handle->backup_task = (retro_task_t*)task_push_core_backup( + download_handle->local_core_path, + download_handle->display_name, + download_handle->local_crc, CORE_BACKUP_MODE_AUTO, + download_handle->auto_backup_history_size, + download_handle->path_dir_core_assets, true); + + if (download_handle->backup_task) + { + 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_BACKING_UP_CORE), + sizeof(task_title)); + strlcat(task_title, download_handle->display_name, sizeof(task_title)); + + task_set_title(task, strdup(task_title)); + + /* Start waiting for backup to complete */ + download_handle->status = CORE_UPDATER_DOWNLOAD_WAIT_BACKUP; + } + else + { + /* This cannot realistically happen... + * > If it does, just log an error and initialise + * download */ + RARCH_ERR("[core updater] Failed to backup core: %s\n", + download_handle->local_core_path); + download_handle->backup_enabled = false; + download_handle->status = CORE_UPDATER_DOWNLOAD_START_TRANSFER; + } + } + break; + case CORE_UPDATER_DOWNLOAD_WAIT_BACKUP: + { + bool backup_complete = false; + + /* > If task is running, check 'is finished' + * status + * > If task is NULL, then it is finished + * by definition */ + if (download_handle->backup_task) + { + backup_complete = task_get_finished(download_handle->backup_task); + + /* If backup task is running, copy current + * progress value to *this* task */ + if (!backup_complete) + { + /* Backup accounts for first third of + * task progress */ + int8_t progress = task_get_progress(download_handle->backup_task); + + task_set_progress(task, (int8_t)(((float)progress * (1.0f / 3.0f)) + 0.5f)); + } + } + else + backup_complete = true; + + /* If backup is complete, initialise download */ + if (backup_complete) + { + download_handle->backup_task = NULL; + download_handle->status = CORE_UPDATER_DOWNLOAD_START_TRANSFER; + } + } + break; + case CORE_UPDATER_DOWNLOAD_START_TRANSFER: + { + file_transfer_t *transf = NULL; + char task_title[PATH_MAX_LENGTH]; + + task_title[0] = '\0'; + /* Configure file transfer object */ transf = (file_transfer_t*)calloc(1, sizeof(file_transfer_t)); @@ -668,16 +774,6 @@ static void task_core_updater_download_handler(retro_task_t *task) download_handle->remote_core_path, true, NULL, cb_http_task_core_updater_download, transf); - /* Start waiting for HTTP transfer to complete */ - download_handle->status = CORE_UPDATER_DOWNLOAD_WAIT_TRANSFER; - } - break; - case CORE_UPDATER_DOWNLOAD_WAIT_TRANSFER: - { - char task_title[PATH_MAX_LENGTH]; - - task_title[0] = '\0'; - /* Update task title */ task_free_title(task); @@ -688,6 +784,12 @@ static void task_core_updater_download_handler(retro_task_t *task) task_set_title(task, strdup(task_title)); + /* Start waiting for HTTP transfer to complete */ + download_handle->status = CORE_UPDATER_DOWNLOAD_WAIT_TRANSFER; + } + break; + case CORE_UPDATER_DOWNLOAD_WAIT_TRANSFER: + { /* If HTTP task is NULL, then it either finished * or an error occurred - in either case, * just move on to the next state */ @@ -703,36 +805,46 @@ static void task_core_updater_download_handler(retro_task_t *task) * progress value to *this* task */ if (!download_handle->http_task_finished) { - /* Download accounts for first half of - * task progress */ + /* > If backups are enabled, download accounts + * for second third of task progress + * > Otherwise, download accounts for first half + * of task progress */ int8_t progress = task_get_progress(download_handle->http_task); - task_set_progress(task, progress >> 1); + if (download_handle->backup_enabled) + progress = (int8_t)(((float)progress * (1.0f / 3.0f)) + (100.0f / 3.0f) + 0.5f); + else + progress = progress >> 1; + + task_set_progress(task, progress); } } /* Wait for task_push_http_transfer_file() * callback to trigger */ if (download_handle->http_task_complete) + { + 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_EXTRACTING_CORE), + sizeof(task_title)); + strlcat(task_title, download_handle->display_name, sizeof(task_title)); + + task_set_title(task, strdup(task_title)); + + /* Start waiting for file to be extracted */ download_handle->status = CORE_UPDATER_DOWNLOAD_WAIT_DECOMPRESS; + } } break; case CORE_UPDATER_DOWNLOAD_WAIT_DECOMPRESS: { - 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_EXTRACTING_CORE), - sizeof(task_title)); - strlcat(task_title, download_handle->display_name, sizeof(task_title)); - - task_set_title(task, strdup(task_title)); - /* If decompression task is NULL, then it either * finished or an error occurred - in either case, * just move on to the next state */ @@ -749,11 +861,18 @@ static void task_core_updater_download_handler(retro_task_t *task) * current progress value to *this* task */ if (!download_handle->decompress_task_finished) { - /* Download accounts for second half - * of task progress */ + /* > If backups are enabled, decompression accounts + * for last third of task progress + * > Otherwise, decompression accounts for second + * half of task progress */ int8_t progress = task_get_progress(download_handle->decompress_task); - task_set_progress(task, 50 + (progress >> 1)); + if (download_handle->backup_enabled) + progress = (int8_t)(((float)progress * (1.0f / 3.0f)) + (200.0f / 3.0f) + 0.5f); + else + progress = 50 + (progress >> 1); + + task_set_progress(task, progress); } } @@ -816,8 +935,10 @@ static bool task_core_updater_download_finder(retro_task_t *task, void *user_dat void *task_push_core_updater_download( core_updater_list_t* core_list, - const char *filename, bool mute, bool check_crc, - const char *path_dir_libretro) + const char *filename, uint32_t crc, bool mute, + bool auto_backup, size_t auto_backup_history_size, + const char *path_dir_libretro, + const char *path_dir_core_assets) { task_finder_data_t find_data; char task_title[PATH_MAX_LENGTH]; @@ -861,14 +982,17 @@ void *task_push_core_updater_download( sizeof(local_download_path)); /* Configure handle */ + download_handle->auto_backup = auto_backup; + download_handle->auto_backup_history_size = auto_backup_history_size; download_handle->path_dir_libretro = strdup(path_dir_libretro); + download_handle->path_dir_core_assets = string_is_empty(path_dir_core_assets) ? NULL : strdup(path_dir_core_assets); download_handle->remote_filename = strdup(list_entry->remote_filename); download_handle->remote_core_path = strdup(list_entry->remote_core_path); download_handle->local_download_path = strdup(local_download_path); download_handle->local_core_path = strdup(list_entry->local_core_path); download_handle->display_name = strdup(list_entry->display_name); + download_handle->local_crc = crc; download_handle->remote_crc = list_entry->crc; - download_handle->check_crc = check_crc; download_handle->crc_match = false; download_handle->http_task = NULL; download_handle->http_task_finished = false; @@ -876,6 +1000,8 @@ void *task_push_core_updater_download( download_handle->decompress_task = NULL; download_handle->decompress_task_finished = false; download_handle->decompress_task_complete = false; + download_handle->backup_enabled = false; + download_handle->backup_task = NULL; download_handle->status = CORE_UPDATER_DOWNLOAD_BEGIN; /* Concurrent downloads of the same file are not allowed */ @@ -938,6 +1064,9 @@ static void free_update_installed_cores_handle( if (update_installed_handle->path_dir_libretro) free(update_installed_handle->path_dir_libretro); + if (update_installed_handle->path_dir_core_assets) + free(update_installed_handle->path_dir_core_assets); + core_updater_list_free(update_installed_handle->core_list); free(update_installed_handle); @@ -1063,7 +1192,7 @@ static void task_update_installed_cores_handler(retro_task_t *task) case UPDATE_INSTALLED_CORES_UPDATE_CORE: { const core_updater_list_entry_t *list_entry = NULL; - bool crc_match; + uint32_t local_crc; /* Get list entry * > In the event of an error, just return @@ -1077,15 +1206,16 @@ static void task_update_installed_cores_handler(retro_task_t *task) break; } - /* Check CRC of existing core */ - crc_match = local_core_matches_remote_crc( - list_entry->local_core_path, - list_entry->crc); + /* Get CRC of existing core */ + local_crc = task_core_updater_get_core_crc( + list_entry->local_core_path); - /* If CRC matches, then core is already the most - * recent version - just return to - * UPDATE_INSTALLED_CORES_ITERATE state */ - if (crc_match) + /* Check whether existing core and remote core + * have the same CRC + * > If CRC matches, then core is already the most + * recent version - just return to + * UPDATE_INSTALLED_CORES_ITERATE state */ + if ((local_crc != 0) && (local_crc == list_entry->crc)) { update_installed_handle->status = UPDATE_INSTALLED_CORES_ITERATE; break; @@ -1097,8 +1227,11 @@ static void task_update_installed_cores_handler(retro_task_t *task) task_push_core_updater_download( update_installed_handle->core_list, list_entry->remote_filename, - true, false, - update_installed_handle->path_dir_libretro); + local_crc, true, + update_installed_handle->auto_backup, + update_installed_handle->auto_backup_history_size, + update_installed_handle->path_dir_libretro, + update_installed_handle->path_dir_core_assets); /* Again, if an error occurred, just return to * UPDATE_INSTALLED_CORES_ITERATE state */ @@ -1208,7 +1341,10 @@ static bool task_update_installed_cores_finder(retro_task_t *task, void *user_da return false; } -void task_push_update_installed_cores(const char *path_dir_libretro) +void task_push_update_installed_cores( + bool auto_backup, size_t auto_backup_history_size, + const char *path_dir_libretro, + const char *path_dir_core_assets) { task_finder_data_t find_data; retro_task_t *task = NULL; @@ -1217,20 +1353,24 @@ void task_push_update_installed_cores(const char *path_dir_libretro) calloc(1, sizeof(update_installed_cores_handle_t)); /* Sanity check */ - if (!update_installed_handle) + if (!update_installed_handle || + string_is_empty(path_dir_libretro)) goto error; /* Configure handle */ - update_installed_handle->core_list = core_updater_list_init( - CORE_UPDATER_LIST_SIZE); - update_installed_handle->list_task = NULL; - update_installed_handle->download_task = NULL; - update_installed_handle->list_size = 0; - update_installed_handle->list_index = 0; - update_installed_handle->installed_index = 0; - update_installed_handle->num_updated = 0; - update_installed_handle->path_dir_libretro = strdup(path_dir_libretro); - update_installed_handle->status = UPDATE_INSTALLED_CORES_BEGIN; + update_installed_handle->auto_backup = auto_backup; + update_installed_handle->auto_backup_history_size = auto_backup_history_size; + update_installed_handle->path_dir_libretro = strdup(path_dir_libretro); + update_installed_handle->path_dir_core_assets = string_is_empty(path_dir_core_assets) ? + NULL : strdup(path_dir_core_assets); + update_installed_handle->core_list = core_updater_list_init(CORE_UPDATER_LIST_SIZE); + update_installed_handle->list_task = NULL; + update_installed_handle->download_task = NULL; + update_installed_handle->list_size = 0; + update_installed_handle->list_index = 0; + update_installed_handle->installed_index = 0; + update_installed_handle->num_updated = 0; + update_installed_handle->status = UPDATE_INSTALLED_CORES_BEGIN; if (!update_installed_handle->core_list) goto error; diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h index 96a992d5e1..5fd80e8b46 100644 --- a/tasks/tasks_internal.h +++ b/tasks/tasks_internal.h @@ -96,13 +96,21 @@ bool task_push_netplay_lan_scan_rooms(retro_task_callback_t cb); bool task_push_netplay_nat_traversal(void *nat_traversal_state, uint16_t port); /* Core updater tasks */ + void *task_push_get_core_updater_list( core_updater_list_t* core_list, bool mute, bool refresh_menu); +/* Note: If crc is set to 0, crc of local core file + * will be calculated automatically */ void *task_push_core_updater_download( - core_updater_list_t* core_list, const char *filename, - bool mute, bool check_crc, const char *path_dir_libretro); -void task_push_update_installed_cores(const char *path_dir_libretro); - + core_updater_list_t* core_list, + const char *filename, uint32_t crc, bool mute, + bool auto_backup, size_t auto_backup_history_size, + const char *path_dir_libretro, + const char *path_dir_core_assets); +void task_push_update_installed_cores( + bool auto_backup, size_t auto_backup_history_size, + const char *path_dir_libretro, + const char *path_dir_core_assets); bool task_push_pl_entry_thumbnail_download( const char *system, @@ -121,12 +129,18 @@ bool task_push_pl_thumbnail_download( /* Core backup/restore tasks */ -/* Note: If crc is set to 0, crc of core_path file will - * be calculated automatically */ -void *task_push_core_backup(const char *core_path, +/* Note 1: If crc is set to 0, crc of core_path file will + * be calculated automatically + * Note 2: If core_display_name is set to NULL, display + * name will be determined automatically + * > core_display_name *must* be set to a non-empty + * string if task_push_core_backup() is *not* called + * on the main thread */ +void *task_push_core_backup( + const char *core_path, const char *core_display_name, uint32_t crc, enum core_backup_mode backup_mode, + size_t auto_backup_history_size, const char *dir_core_assets, bool mute); - /* Note: If 'core_loaded' is true, menu stack should be * flushed if task_push_core_restore() returns true */ bool task_push_core_restore(const char *backup_path,