Add option to automatically backup cores when updating

This commit is contained in:
jdgleaver 2020-06-16 11:11:09 +01:00
parent 4b364fa1e1
commit 2782b87267
15 changed files with 580 additions and 159 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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. */

View File

@ -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. */

View File

@ -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"

View File

@ -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: "

View File

@ -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;
}

View File

@ -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;

View File

@ -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++)

View File

@ -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);

View File

@ -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,

View File

@ -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();

View File

@ -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;

View File

@ -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,