mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-28 02:30:35 +00:00
1474 lines
47 KiB
C
1474 lines
47 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
* Copyright (C) 2014-2017 - Jean-André Santoni
|
|
* Copyright (C) 2016-2019 - Brad Parker
|
|
* Copyright (C) 2019 - James Leaver
|
|
*
|
|
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with RetroArch.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <boolean.h>
|
|
|
|
#include <string/stdstring.h>
|
|
#include <file/file_path.h>
|
|
#include <net/net_http.h>
|
|
#include <streams/interface_stream.h>
|
|
#include <streams/file_stream.h>
|
|
|
|
#include "task_file_transfer.h"
|
|
#include "tasks_internal.h"
|
|
|
|
#include "../configuration.h"
|
|
#include "../retroarch.h"
|
|
#include "../command.h"
|
|
#include "../msg_hash.h"
|
|
#include "../verbosity.h"
|
|
#include "../core_updater_list.h"
|
|
|
|
#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
|
|
#include "../menu/menu_entries.h"
|
|
#endif
|
|
|
|
/* Get core updater list */
|
|
enum core_updater_list_status
|
|
{
|
|
CORE_UPDATER_LIST_BEGIN = 0,
|
|
CORE_UPDATER_LIST_WAIT,
|
|
CORE_UPDATER_LIST_END
|
|
};
|
|
|
|
typedef struct core_updater_list_handle
|
|
{
|
|
core_updater_list_t* core_list;
|
|
bool refresh_menu;
|
|
retro_task_t *http_task;
|
|
bool http_task_finished;
|
|
bool http_task_complete;
|
|
bool http_task_success;
|
|
http_transfer_data_t *http_data;
|
|
enum core_updater_list_status status;
|
|
} core_updater_list_handle_t;
|
|
|
|
/* Download core */
|
|
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
|
|
};
|
|
|
|
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 crc_match;
|
|
retro_task_t *http_task;
|
|
bool http_task_finished;
|
|
bool http_task_complete;
|
|
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;
|
|
|
|
/* Update installed cores */
|
|
enum update_installed_cores_status
|
|
{
|
|
UPDATE_INSTALLED_CORES_BEGIN = 0,
|
|
UPDATE_INSTALLED_CORES_WAIT_LIST,
|
|
UPDATE_INSTALLED_CORES_ITERATE,
|
|
UPDATE_INSTALLED_CORES_UPDATE_CORE,
|
|
UPDATE_INSTALLED_CORES_WAIT_DOWNLOAD,
|
|
UPDATE_INSTALLED_CORES_END
|
|
};
|
|
|
|
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;
|
|
size_t list_size;
|
|
size_t list_index;
|
|
size_t installed_index;
|
|
unsigned num_updated;
|
|
unsigned num_locked;
|
|
enum update_installed_cores_status status;
|
|
} update_installed_cores_handle_t;
|
|
|
|
/*********************/
|
|
/* Utility functions */
|
|
/*********************/
|
|
|
|
/* Returns CRC32 of specified core file */
|
|
static uint32_t task_core_updater_get_core_crc(const char *core_path)
|
|
{
|
|
if (string_is_empty(core_path))
|
|
return 0;
|
|
|
|
if (path_is_valid(core_path))
|
|
{
|
|
/* Open core file */
|
|
intfstream_t *core_file = intfstream_open_file(
|
|
core_path, RETRO_VFS_FILE_ACCESS_READ,
|
|
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
|
|
|
if (core_file)
|
|
{
|
|
uint32_t crc = 0;
|
|
|
|
/* Get crc value */
|
|
bool success = intfstream_get_crc(core_file, &crc);
|
|
|
|
/* Close core file */
|
|
intfstream_close(core_file);
|
|
free(core_file);
|
|
core_file = NULL;
|
|
|
|
if (success)
|
|
return crc;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*************************/
|
|
/* Get core updater list */
|
|
/*************************/
|
|
|
|
static void cb_http_task_core_updater_get_list(
|
|
retro_task_t *task, void *task_data,
|
|
void *user_data, const char *err)
|
|
{
|
|
http_transfer_data_t *data = (http_transfer_data_t*)task_data;
|
|
file_transfer_t *transf = (file_transfer_t*)user_data;
|
|
core_updater_list_handle_t *list_handle = NULL;
|
|
bool success = data && string_is_empty(err);
|
|
|
|
if (!transf)
|
|
goto finish;
|
|
|
|
list_handle = (core_updater_list_handle_t*)transf->user_data;
|
|
|
|
if (!list_handle)
|
|
goto finish;
|
|
|
|
list_handle->http_data = data;
|
|
list_handle->http_task_complete = true;
|
|
list_handle->http_task_success = success;
|
|
|
|
finish:
|
|
|
|
/* Log any error messages */
|
|
if (!success)
|
|
RARCH_ERR("[core updater] Download of core list '%s' failed: %s\n",
|
|
(transf ? transf->path: "unknown"),
|
|
(err ? err : "unknown"));
|
|
|
|
if (transf)
|
|
free(transf);
|
|
}
|
|
|
|
static void free_core_updater_list_handle(
|
|
core_updater_list_handle_t *list_handle)
|
|
{
|
|
if (!list_handle)
|
|
return;
|
|
|
|
if (list_handle->http_data)
|
|
{
|
|
if (list_handle->http_data->data)
|
|
free(list_handle->http_data->data);
|
|
|
|
free(list_handle->http_data);
|
|
}
|
|
|
|
free(list_handle);
|
|
list_handle = NULL;
|
|
}
|
|
|
|
static void task_core_updater_get_list_handler(retro_task_t *task)
|
|
{
|
|
core_updater_list_handle_t *list_handle = NULL;
|
|
|
|
if (!task)
|
|
goto task_finished;
|
|
|
|
list_handle = (core_updater_list_handle_t*)task->state;
|
|
|
|
if (!list_handle)
|
|
goto task_finished;
|
|
|
|
if (task_get_cancelled(task))
|
|
goto task_finished;
|
|
|
|
switch (list_handle->status)
|
|
{
|
|
case CORE_UPDATER_LIST_BEGIN:
|
|
{
|
|
settings_t *settings = config_get_ptr();
|
|
file_transfer_t *transf = NULL;
|
|
char *tmp_url = NULL;
|
|
char buildbot_url[PATH_MAX_LENGTH];
|
|
const char *net_buildbot_url =
|
|
settings->paths.network_buildbot_url;
|
|
|
|
buildbot_url[0] = '\0';
|
|
|
|
/* Reset core updater list */
|
|
core_updater_list_reset(list_handle->core_list);
|
|
|
|
/* Get core listing URL */
|
|
if (!settings)
|
|
goto task_finished;
|
|
|
|
if (string_is_empty(net_buildbot_url))
|
|
goto task_finished;
|
|
|
|
fill_pathname_join(
|
|
buildbot_url,
|
|
net_buildbot_url,
|
|
".index-extended",
|
|
sizeof(buildbot_url));
|
|
|
|
tmp_url = strdup(buildbot_url);
|
|
buildbot_url[0] = '\0';
|
|
net_http_urlencode_full(
|
|
buildbot_url, tmp_url, sizeof(buildbot_url));
|
|
if (tmp_url)
|
|
free(tmp_url);
|
|
|
|
if (string_is_empty(buildbot_url))
|
|
goto task_finished;
|
|
|
|
/* Configure file transfer object */
|
|
transf = (file_transfer_t*)calloc(1, sizeof(file_transfer_t));
|
|
|
|
if (!transf)
|
|
goto task_finished;
|
|
|
|
/* > Seems to be required - not sure why the
|
|
* underlying code is implemented like this... */
|
|
strlcpy(transf->path, buildbot_url, sizeof(transf->path));
|
|
|
|
transf->user_data = (void*)list_handle;
|
|
|
|
/* Push HTTP transfer task */
|
|
list_handle->http_task = (retro_task_t*)task_push_http_transfer_file(
|
|
buildbot_url, true, NULL,
|
|
cb_http_task_core_updater_get_list, transf);
|
|
|
|
/* Start waiting for HTTP transfer to complete */
|
|
list_handle->status = CORE_UPDATER_LIST_WAIT;
|
|
}
|
|
break;
|
|
case CORE_UPDATER_LIST_WAIT:
|
|
{
|
|
/* If HTTP task is NULL, then it either finished
|
|
* or an error occurred - in either case,
|
|
* just move on to the next state */
|
|
if (!list_handle->http_task)
|
|
list_handle->http_task_complete = true;
|
|
/* Otherwise, check if HTTP task is still running */
|
|
else if (!list_handle->http_task_finished)
|
|
{
|
|
list_handle->http_task_finished =
|
|
task_get_finished(list_handle->http_task);
|
|
|
|
/* If HTTP task is running, copy current
|
|
* progress value to *this* task */
|
|
if (!list_handle->http_task_finished)
|
|
task_set_progress(
|
|
task, task_get_progress(list_handle->http_task));
|
|
}
|
|
|
|
/* Wait for task_push_http_transfer_file()
|
|
* callback to trigger */
|
|
if (list_handle->http_task_complete)
|
|
list_handle->status = CORE_UPDATER_LIST_END;
|
|
}
|
|
break;
|
|
case CORE_UPDATER_LIST_END:
|
|
{
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
/* Check whether HTTP task was successful */
|
|
if (list_handle->http_task_success)
|
|
{
|
|
/* Parse HTTP transfer data */
|
|
if (list_handle->http_data)
|
|
core_updater_list_parse_network_data(
|
|
list_handle->core_list,
|
|
settings->paths.directory_libretro,
|
|
settings->paths.path_libretro_info,
|
|
settings->paths.network_buildbot_url,
|
|
list_handle->http_data->data,
|
|
list_handle->http_data->len);
|
|
}
|
|
else
|
|
{
|
|
/* Notify user of error via task title */
|
|
task_free_title(task);
|
|
task_set_title(task, strdup(msg_hash_to_str(MSG_CORE_LIST_FAILED)));
|
|
}
|
|
|
|
/* Enable menu refresh, if required */
|
|
#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
|
|
if (list_handle->refresh_menu)
|
|
menu_entries_ctl(
|
|
MENU_ENTRIES_CTL_UNSET_REFRESH,
|
|
&list_handle->refresh_menu);
|
|
#endif
|
|
}
|
|
/* fall-through */
|
|
default:
|
|
task_set_progress(task, 100);
|
|
goto task_finished;
|
|
}
|
|
|
|
return;
|
|
|
|
task_finished:
|
|
|
|
if (task)
|
|
task_set_finished(task, true);
|
|
|
|
free_core_updater_list_handle(list_handle);
|
|
}
|
|
|
|
static bool task_core_updater_get_list_finder(retro_task_t *task, void *user_data)
|
|
{
|
|
core_updater_list_handle_t *list_handle = NULL;
|
|
|
|
if (!task || !user_data)
|
|
return false;
|
|
|
|
if (task->handler != task_core_updater_get_list_handler)
|
|
return false;
|
|
|
|
list_handle = (core_updater_list_handle_t*)task->state;
|
|
if (!list_handle)
|
|
return false;
|
|
|
|
return ((uintptr_t)user_data == (uintptr_t)list_handle->core_list);
|
|
}
|
|
|
|
void *task_push_get_core_updater_list(
|
|
core_updater_list_t* core_list, bool mute, bool refresh_menu)
|
|
{
|
|
task_finder_data_t find_data;
|
|
retro_task_t *task = NULL;
|
|
core_updater_list_handle_t *list_handle = (core_updater_list_handle_t*)
|
|
calloc(1, sizeof(core_updater_list_handle_t));
|
|
|
|
/* Sanity check */
|
|
if (!core_list || !list_handle)
|
|
goto error;
|
|
|
|
/* Configure handle */
|
|
list_handle->core_list = core_list;
|
|
list_handle->refresh_menu = refresh_menu;
|
|
list_handle->http_task = NULL;
|
|
list_handle->http_task_finished = false;
|
|
list_handle->http_task_complete = false;
|
|
list_handle->http_task_success = false;
|
|
list_handle->http_data = NULL;
|
|
list_handle->status = CORE_UPDATER_LIST_BEGIN;
|
|
|
|
/* Concurrent downloads of the buildbot core listing
|
|
* to the same core_updater_list_t object are not
|
|
* allowed */
|
|
find_data.func = task_core_updater_get_list_finder;
|
|
find_data.userdata = (void*)core_list;
|
|
|
|
if (task_queue_find(&find_data))
|
|
goto error;
|
|
|
|
/* Create task */
|
|
task = task_init();
|
|
|
|
if (!task)
|
|
goto error;
|
|
|
|
/* Configure task */
|
|
task->handler = task_core_updater_get_list_handler;
|
|
task->state = list_handle;
|
|
task->mute = mute;
|
|
task->title = strdup(msg_hash_to_str(MSG_FETCHING_CORE_LIST));
|
|
task->alternative_look = true;
|
|
task->progress = 0;
|
|
|
|
/* Push task */
|
|
task_queue_push(task);
|
|
|
|
return task;
|
|
|
|
error:
|
|
|
|
/* Clean up task */
|
|
if (task)
|
|
{
|
|
free(task);
|
|
task = NULL;
|
|
}
|
|
|
|
/* Clean up handle */
|
|
free_core_updater_list_handle(list_handle);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*****************/
|
|
/* Download core */
|
|
/*****************/
|
|
|
|
static void cb_task_core_updater_download(
|
|
retro_task_t *task, void *task_data,
|
|
void *user_data, const char *err)
|
|
{
|
|
/* Reload core info files
|
|
* > This must be done on the main thread */
|
|
command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
|
|
}
|
|
|
|
static void cb_decompress_task_core_updater_download(
|
|
retro_task_t *task, void *task_data,
|
|
void *user_data, const char *err)
|
|
{
|
|
decompress_task_data_t *decompress_data =
|
|
(decompress_task_data_t*)task_data;
|
|
core_updater_download_handle_t *download_handle =
|
|
(core_updater_download_handle_t*)user_data;
|
|
|
|
/* Signal that decompression task is complete */
|
|
if (download_handle)
|
|
download_handle->decompress_task_complete = true;
|
|
|
|
/* Remove original archive file */
|
|
if (decompress_data)
|
|
{
|
|
if (!string_is_empty(decompress_data->source_file))
|
|
if (path_is_valid(decompress_data->source_file))
|
|
filestream_delete(decompress_data->source_file);
|
|
|
|
if (decompress_data->source_file)
|
|
free(decompress_data->source_file);
|
|
|
|
free(decompress_data);
|
|
}
|
|
|
|
/* Log any error messages */
|
|
if (!string_is_empty(err))
|
|
RARCH_ERR("[core updater] %s", err);
|
|
}
|
|
|
|
void cb_http_task_core_updater_download(
|
|
retro_task_t *task, void *task_data,
|
|
void *user_data, const char *err)
|
|
{
|
|
http_transfer_data_t *data = (http_transfer_data_t*)task_data;
|
|
file_transfer_t *transf = (file_transfer_t*)user_data;
|
|
core_updater_download_handle_t *download_handle = NULL;
|
|
char output_dir[PATH_MAX_LENGTH];
|
|
|
|
output_dir[0] = '\0';
|
|
|
|
if (!data || !transf)
|
|
goto finish;
|
|
|
|
if (!data->data || string_is_empty(transf->path))
|
|
goto finish;
|
|
|
|
download_handle = (core_updater_download_handle_t*)transf->user_data;
|
|
|
|
if (!download_handle)
|
|
goto finish;
|
|
|
|
/* Update download_handle task status
|
|
* NOTE: We set decompress_task_complete = true
|
|
* here to prevent any lock-ups in the event
|
|
* of errors (or lack of decompression support).
|
|
* decompress_task_complete will be set false
|
|
* if/when we actually call task_push_decompress() */
|
|
download_handle->http_task_complete = true;
|
|
download_handle->decompress_task_complete = true;
|
|
|
|
/* Create output directory, if required */
|
|
strlcpy(output_dir, transf->path, sizeof(output_dir));
|
|
path_basedir_wrapper(output_dir);
|
|
|
|
if (!path_mkdir(output_dir))
|
|
{
|
|
err = msg_hash_to_str(MSG_FAILED_TO_CREATE_THE_DIRECTORY);
|
|
goto finish;
|
|
}
|
|
|
|
#ifdef HAVE_COMPRESSION
|
|
/* If core file is an archive, make sure it is
|
|
* not being decompressed already (by another task) */
|
|
if (path_is_compressed_file(transf->path))
|
|
{
|
|
if (task_check_decompress(transf->path))
|
|
{
|
|
err = msg_hash_to_str(MSG_DECOMPRESSION_ALREADY_IN_PROGRESS);
|
|
goto finish;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Write core file to disk */
|
|
if (!filestream_write_file(transf->path, data->data, data->len))
|
|
{
|
|
err = "Write failed.";
|
|
goto finish;
|
|
}
|
|
|
|
#if defined(HAVE_COMPRESSION) && defined(HAVE_ZLIB)
|
|
/* Decompress core file, if required
|
|
* NOTE: If core is compressed and platform
|
|
* doesn't have compression support, then this
|
|
* whole thing falls apart...
|
|
* We assume that the build process is configured
|
|
* in such a way that this cannot happen... */
|
|
if (path_is_compressed_file(transf->path))
|
|
{
|
|
download_handle->decompress_task = (retro_task_t*)task_push_decompress(
|
|
transf->path, output_dir,
|
|
NULL, NULL, NULL,
|
|
cb_decompress_task_core_updater_download,
|
|
(void*)download_handle,
|
|
NULL, true);
|
|
|
|
if (!download_handle->decompress_task)
|
|
{
|
|
err = msg_hash_to_str(MSG_DECOMPRESSION_FAILED);
|
|
goto finish;
|
|
}
|
|
|
|
download_handle->decompress_task_complete = false;
|
|
}
|
|
#endif
|
|
|
|
finish:
|
|
|
|
/* Log any error messages */
|
|
if (!string_is_empty(err))
|
|
RARCH_ERR("[core updater] Download of '%s' failed: %s\n",
|
|
(transf ? transf->path: "unknown"), err);
|
|
|
|
if (data)
|
|
{
|
|
if (data->data)
|
|
free(data->data);
|
|
free(data);
|
|
}
|
|
|
|
if (transf)
|
|
free(transf);
|
|
}
|
|
|
|
static void free_core_updater_download_handle(core_updater_download_handle_t *download_handle)
|
|
{
|
|
if (!download_handle)
|
|
return;
|
|
|
|
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);
|
|
|
|
if (download_handle->remote_core_path)
|
|
free(download_handle->remote_core_path);
|
|
|
|
if (download_handle->local_download_path)
|
|
free(download_handle->local_download_path);
|
|
|
|
if (download_handle->local_core_path)
|
|
free(download_handle->local_core_path);
|
|
|
|
if (download_handle->display_name)
|
|
free(download_handle->display_name);
|
|
|
|
free(download_handle);
|
|
download_handle = NULL;
|
|
}
|
|
|
|
static void task_core_updater_download_handler(retro_task_t *task)
|
|
{
|
|
core_updater_download_handle_t *download_handle = NULL;
|
|
|
|
if (!task)
|
|
goto task_finished;
|
|
|
|
download_handle = (core_updater_download_handle_t*)task->state;
|
|
|
|
if (!download_handle)
|
|
goto task_finished;
|
|
|
|
if (task_get_cancelled(task))
|
|
goto task_finished;
|
|
|
|
switch (download_handle->status)
|
|
{
|
|
case CORE_UPDATER_DOWNLOAD_BEGIN:
|
|
{
|
|
/* 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 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)
|
|
{
|
|
download_handle->status = CORE_UPDATER_DOWNLOAD_END;
|
|
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));
|
|
|
|
if (!transf)
|
|
goto task_finished;
|
|
|
|
strlcpy(
|
|
transf->path, download_handle->local_download_path,
|
|
sizeof(transf->path));
|
|
|
|
transf->user_data = (void*)download_handle;
|
|
|
|
/* Push HTTP transfer task */
|
|
download_handle->http_task = (retro_task_t*)task_push_http_transfer_file(
|
|
download_handle->remote_core_path, true, NULL,
|
|
cb_http_task_core_updater_download, transf);
|
|
|
|
/* Update task title */
|
|
task_free_title(task);
|
|
|
|
strlcpy(
|
|
task_title, msg_hash_to_str(MSG_DOWNLOADING_CORE),
|
|
sizeof(task_title));
|
|
strlcat(task_title, download_handle->display_name, sizeof(task_title));
|
|
|
|
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 */
|
|
if (!download_handle->http_task)
|
|
download_handle->http_task_complete = true;
|
|
/* Otherwise, check if HTTP task is still running */
|
|
else if (!download_handle->http_task_finished)
|
|
{
|
|
download_handle->http_task_finished =
|
|
task_get_finished(download_handle->http_task);
|
|
|
|
/* If HTTP task is running, copy current
|
|
* progress value to *this* task */
|
|
if (!download_handle->http_task_finished)
|
|
{
|
|
/* > 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);
|
|
|
|
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:
|
|
{
|
|
/* If decompression task is NULL, then it either
|
|
* finished or an error occurred - in either case,
|
|
* just move on to the next state */
|
|
if (!download_handle->decompress_task)
|
|
download_handle->decompress_task_complete = true;
|
|
/* Otherwise, check if decompression task is still
|
|
* running */
|
|
else if (!download_handle->decompress_task_finished)
|
|
{
|
|
download_handle->decompress_task_finished =
|
|
task_get_finished(download_handle->decompress_task);
|
|
|
|
/* If decompression task is running, copy
|
|
* current progress value to *this* task */
|
|
if (!download_handle->decompress_task_finished)
|
|
{
|
|
/* > 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);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* Wait for task_push_decompress()
|
|
* callback to trigger */
|
|
if (download_handle->decompress_task_complete)
|
|
download_handle->status = CORE_UPDATER_DOWNLOAD_END;
|
|
}
|
|
break;
|
|
case CORE_UPDATER_DOWNLOAD_END:
|
|
{
|
|
char task_title[PATH_MAX_LENGTH];
|
|
|
|
task_title[0] = '\0';
|
|
|
|
/* Set final task title */
|
|
task_free_title(task);
|
|
|
|
strlcpy(
|
|
task_title,
|
|
download_handle->crc_match ?
|
|
msg_hash_to_str(MSG_LATEST_CORE_INSTALLED) : msg_hash_to_str(MSG_CORE_INSTALLED),
|
|
sizeof(task_title));
|
|
strlcat(task_title, download_handle->display_name, sizeof(task_title));
|
|
|
|
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_core_updater_download_handle(download_handle);
|
|
}
|
|
|
|
static bool task_core_updater_download_finder(retro_task_t *task, void *user_data)
|
|
{
|
|
core_updater_download_handle_t *download_handle = NULL;
|
|
|
|
if (!task || !user_data)
|
|
return false;
|
|
|
|
if (task->handler != task_core_updater_download_handler)
|
|
return false;
|
|
|
|
download_handle = (core_updater_download_handle_t*)task->state;
|
|
if (!download_handle)
|
|
return false;
|
|
|
|
return string_is_equal((const char*)user_data, download_handle->remote_filename);
|
|
}
|
|
|
|
void *task_push_core_updater_download(
|
|
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)
|
|
{
|
|
task_finder_data_t find_data;
|
|
char task_title[PATH_MAX_LENGTH];
|
|
char local_download_path[PATH_MAX_LENGTH];
|
|
const core_updater_list_entry_t *list_entry = NULL;
|
|
retro_task_t *task = NULL;
|
|
core_updater_download_handle_t *download_handle = (core_updater_download_handle_t*)
|
|
calloc(1, sizeof(core_updater_download_handle_t));
|
|
|
|
task_title[0] = '\0';
|
|
local_download_path[0] = '\0';
|
|
|
|
/* Sanity check */
|
|
if (!core_list ||
|
|
string_is_empty(filename) ||
|
|
!download_handle)
|
|
goto error;
|
|
|
|
/* Get core updater list entry */
|
|
if (!core_updater_list_get_filename(
|
|
core_list, filename, &list_entry))
|
|
goto error;
|
|
|
|
if (string_is_empty(list_entry->remote_core_path) ||
|
|
string_is_empty(list_entry->local_core_path) ||
|
|
string_is_empty(list_entry->display_name))
|
|
goto error;
|
|
|
|
/* Check whether core is locked
|
|
* > Have to set validate_path to 'false' here,
|
|
* since this may not run on the main thread
|
|
* > Validation is not required anyway, since core
|
|
* updater list provides 'sane' core paths */
|
|
if (core_info_get_core_lock(list_entry->local_core_path, false))
|
|
{
|
|
RARCH_ERR("[core updater] Update disabled - core is locked: %s\n",
|
|
list_entry->local_core_path);
|
|
|
|
/* If task is not muted, generate notification */
|
|
if (!mute)
|
|
{
|
|
char msg[PATH_MAX_LENGTH];
|
|
|
|
msg[0] = '\0';
|
|
|
|
strlcpy(msg, msg_hash_to_str(MSG_CORE_UPDATE_DISABLED), sizeof(msg));
|
|
strlcat(msg, list_entry->display_name, sizeof(msg));
|
|
|
|
runloop_msg_queue_push(msg, 1, 100, true,
|
|
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
|
}
|
|
|
|
goto error;
|
|
}
|
|
|
|
/* Get local file download path */
|
|
if (string_is_empty(path_dir_libretro))
|
|
goto error;
|
|
|
|
fill_pathname_join(
|
|
local_download_path,
|
|
path_dir_libretro,
|
|
list_entry->remote_filename,
|
|
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->crc_match = false;
|
|
download_handle->http_task = NULL;
|
|
download_handle->http_task_finished = false;
|
|
download_handle->http_task_complete = false;
|
|
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 */
|
|
find_data.func = task_core_updater_download_finder;
|
|
find_data.userdata = (void*)download_handle->remote_filename;
|
|
|
|
if (task_queue_find(&find_data))
|
|
goto error;
|
|
|
|
/* Create task */
|
|
task = task_init();
|
|
|
|
if (!task)
|
|
goto error;
|
|
|
|
/* Configure task */
|
|
strlcpy(
|
|
task_title, msg_hash_to_str(MSG_UPDATING_CORE),
|
|
sizeof(task_title));
|
|
strlcat(task_title, download_handle->display_name, sizeof(task_title));
|
|
|
|
task->handler = task_core_updater_download_handler;
|
|
task->state = download_handle;
|
|
task->mute = mute;
|
|
task->title = strdup(task_title);
|
|
task->alternative_look = true;
|
|
task->progress = 0;
|
|
task->callback = cb_task_core_updater_download;
|
|
|
|
/* Push task */
|
|
task_queue_push(task);
|
|
|
|
return task;
|
|
|
|
error:
|
|
|
|
/* Clean up task */
|
|
if (task)
|
|
{
|
|
free(task);
|
|
task = NULL;
|
|
}
|
|
|
|
/* Clean up handle */
|
|
free_core_updater_download_handle(download_handle);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**************************/
|
|
/* Update installed cores */
|
|
/**************************/
|
|
|
|
static void free_update_installed_cores_handle(
|
|
update_installed_cores_handle_t *update_installed_handle)
|
|
{
|
|
if (!update_installed_handle)
|
|
return;
|
|
|
|
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);
|
|
update_installed_handle = NULL;
|
|
}
|
|
|
|
static void task_update_installed_cores_handler(retro_task_t *task)
|
|
{
|
|
update_installed_cores_handle_t *update_installed_handle = NULL;
|
|
|
|
if (!task)
|
|
goto task_finished;
|
|
|
|
update_installed_handle = (update_installed_cores_handle_t*)task->state;
|
|
|
|
if (!update_installed_handle)
|
|
goto task_finished;
|
|
|
|
if (task_get_cancelled(task))
|
|
goto task_finished;
|
|
|
|
switch (update_installed_handle->status)
|
|
{
|
|
case UPDATE_INSTALLED_CORES_BEGIN:
|
|
/* Request buildbot core list */
|
|
update_installed_handle->list_task = (retro_task_t*)
|
|
task_push_get_core_updater_list(
|
|
update_installed_handle->core_list,
|
|
true, false);
|
|
|
|
/* If push failed, go to end
|
|
* (error will message will be displayed when
|
|
* final task title is set) */
|
|
if (!update_installed_handle->list_task)
|
|
update_installed_handle->status = UPDATE_INSTALLED_CORES_END;
|
|
else
|
|
update_installed_handle->status = UPDATE_INSTALLED_CORES_WAIT_LIST;
|
|
break;
|
|
case UPDATE_INSTALLED_CORES_WAIT_LIST:
|
|
{
|
|
bool list_available = false;
|
|
|
|
/* > If task is running, check 'is finished'
|
|
* status
|
|
* > If task is NULL, then it is finished
|
|
* by definition */
|
|
if (update_installed_handle->list_task)
|
|
list_available = task_get_finished(update_installed_handle->list_task);
|
|
else
|
|
list_available = true;
|
|
|
|
/* If list is available, make sure it isn't empty
|
|
* (error will message will be displayed when
|
|
* final task title is set) */
|
|
if (list_available)
|
|
{
|
|
update_installed_handle->list_size =
|
|
core_updater_list_size(update_installed_handle->core_list);
|
|
|
|
if (update_installed_handle->list_size < 1)
|
|
update_installed_handle->status = UPDATE_INSTALLED_CORES_END;
|
|
else
|
|
update_installed_handle->status = UPDATE_INSTALLED_CORES_ITERATE;
|
|
}
|
|
}
|
|
break;
|
|
case UPDATE_INSTALLED_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 (update_installed_handle->list_index >= update_installed_handle->list_size)
|
|
{
|
|
update_installed_handle->status = UPDATE_INSTALLED_CORES_END;
|
|
break;
|
|
}
|
|
|
|
/* Check whether current core is installed */
|
|
if (core_updater_list_get_index(
|
|
update_installed_handle->core_list,
|
|
update_installed_handle->list_index,
|
|
&list_entry))
|
|
{
|
|
if (path_is_valid(list_entry->local_core_path))
|
|
{
|
|
core_installed = true;
|
|
update_installed_handle->installed_index =
|
|
update_installed_handle->list_index;
|
|
update_installed_handle->status =
|
|
UPDATE_INSTALLED_CORES_UPDATE_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,
|
|
(update_installed_handle->list_index * 100) /
|
|
update_installed_handle->list_size);
|
|
|
|
/* Increment list index */
|
|
update_installed_handle->list_index++;
|
|
}
|
|
break;
|
|
case UPDATE_INSTALLED_CORES_UPDATE_CORE:
|
|
{
|
|
const core_updater_list_entry_t *list_entry = NULL;
|
|
uint32_t local_crc;
|
|
|
|
/* Get list entry
|
|
* > In the event of an error, just return
|
|
* to UPDATE_INSTALLED_CORES_ITERATE state */
|
|
if (!core_updater_list_get_index(
|
|
update_installed_handle->core_list,
|
|
update_installed_handle->installed_index,
|
|
&list_entry))
|
|
{
|
|
update_installed_handle->status = UPDATE_INSTALLED_CORES_ITERATE;
|
|
break;
|
|
}
|
|
|
|
/* Check whether core is locked
|
|
* > Have to set validate_path to 'false' here,
|
|
* since this does not run on the main thread
|
|
* > Validation is not required anyway, since core
|
|
* updater list provides 'sane' core paths */
|
|
if (core_info_get_core_lock(list_entry->local_core_path, false))
|
|
{
|
|
RARCH_LOG("[core updater] Skipping locked core: %s\n",
|
|
list_entry->display_name);
|
|
|
|
/* Core update is disabled
|
|
* > Just increment 'locked cores' counter and
|
|
* return to UPDATE_INSTALLED_CORES_ITERATE state */
|
|
update_installed_handle->num_locked++;
|
|
update_installed_handle->status = UPDATE_INSTALLED_CORES_ITERATE;
|
|
break;
|
|
}
|
|
|
|
/* Get CRC of existing core */
|
|
local_crc = task_core_updater_get_core_crc(
|
|
list_entry->local_core_path);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* Existing core is not the most recent version
|
|
* > Request download */
|
|
update_installed_handle->download_task = (retro_task_t*)
|
|
task_push_core_updater_download(
|
|
update_installed_handle->core_list,
|
|
list_entry->remote_filename,
|
|
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 */
|
|
if (!update_installed_handle->download_task)
|
|
update_installed_handle->status = UPDATE_INSTALLED_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));
|
|
|
|
/* Increment 'updated cores' counter */
|
|
update_installed_handle->num_updated++;
|
|
|
|
/* Wait for download to complete */
|
|
update_installed_handle->status = UPDATE_INSTALLED_CORES_WAIT_DOWNLOAD;
|
|
}
|
|
}
|
|
break;
|
|
case UPDATE_INSTALLED_CORES_WAIT_DOWNLOAD:
|
|
{
|
|
bool download_complete = false;
|
|
|
|
/* > If task is running, check 'is finished'
|
|
* status
|
|
* > If task is NULL, then it is finished
|
|
* by definition */
|
|
if (update_installed_handle->download_task)
|
|
download_complete = task_get_finished(update_installed_handle->download_task);
|
|
else
|
|
download_complete = true;
|
|
|
|
/* If download is complete, return to
|
|
* UPDATE_INSTALLED_CORES_ITERATE state */
|
|
if (download_complete)
|
|
{
|
|
update_installed_handle->download_task = NULL;
|
|
update_installed_handle->status = UPDATE_INSTALLED_CORES_ITERATE;
|
|
}
|
|
}
|
|
break;
|
|
case UPDATE_INSTALLED_CORES_END:
|
|
{
|
|
/* Set final task title */
|
|
task_free_title(task);
|
|
|
|
/* > Check whether core list was fetched
|
|
* successfully */
|
|
if (update_installed_handle->list_size > 0)
|
|
{
|
|
char task_title[PATH_MAX_LENGTH];
|
|
|
|
task_title[0] = '\0';
|
|
|
|
/* > Generate final status message based on number
|
|
* of cores that were updated/locked */
|
|
if (update_installed_handle->num_updated > 0)
|
|
{
|
|
if (update_installed_handle->num_locked > 0)
|
|
snprintf(
|
|
task_title, sizeof(task_title), "%s [%s%u, %s%u]",
|
|
msg_hash_to_str(MSG_ALL_CORES_UPDATED),
|
|
msg_hash_to_str(MSG_NUM_CORES_UPDATED),
|
|
update_installed_handle->num_updated,
|
|
msg_hash_to_str(MSG_NUM_CORES_LOCKED),
|
|
update_installed_handle->num_locked);
|
|
else
|
|
snprintf(
|
|
task_title, sizeof(task_title), "%s [%s%u]",
|
|
msg_hash_to_str(MSG_ALL_CORES_UPDATED),
|
|
msg_hash_to_str(MSG_NUM_CORES_UPDATED),
|
|
update_installed_handle->num_updated);
|
|
}
|
|
else if (update_installed_handle->num_locked > 0)
|
|
snprintf(
|
|
task_title, sizeof(task_title), "%s [%s%u]",
|
|
msg_hash_to_str(MSG_ALL_CORES_UPDATED),
|
|
msg_hash_to_str(MSG_NUM_CORES_LOCKED),
|
|
update_installed_handle->num_locked);
|
|
else
|
|
strlcpy(task_title, msg_hash_to_str(MSG_ALL_CORES_UPDATED),
|
|
sizeof(task_title));
|
|
|
|
task_set_title(task, strdup(task_title));
|
|
}
|
|
else
|
|
task_set_title(task, strdup(msg_hash_to_str(MSG_CORE_LIST_FAILED)));
|
|
}
|
|
/* fall-through */
|
|
default:
|
|
task_set_progress(task, 100);
|
|
goto task_finished;
|
|
}
|
|
|
|
return;
|
|
|
|
task_finished:
|
|
|
|
if (task)
|
|
task_set_finished(task, true);
|
|
|
|
free_update_installed_cores_handle(update_installed_handle);
|
|
}
|
|
|
|
static bool task_update_installed_cores_finder(retro_task_t *task, void *user_data)
|
|
{
|
|
if (!task)
|
|
return false;
|
|
|
|
if (task->handler == task_update_installed_cores_handler)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
update_installed_cores_handle_t *update_installed_handle =
|
|
(update_installed_cores_handle_t*)
|
|
calloc(1, sizeof(update_installed_cores_handle_t));
|
|
|
|
/* Sanity check */
|
|
if (!update_installed_handle ||
|
|
string_is_empty(path_dir_libretro))
|
|
goto error;
|
|
|
|
/* Configure handle */
|
|
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->num_locked = 0;
|
|
update_installed_handle->status = UPDATE_INSTALLED_CORES_BEGIN;
|
|
|
|
if (!update_installed_handle->core_list)
|
|
goto error;
|
|
|
|
/* Only one instance of this task may run at a time */
|
|
find_data.func = task_update_installed_cores_finder;
|
|
find_data.userdata = NULL;
|
|
|
|
if (task_queue_find(&find_data))
|
|
goto error;
|
|
|
|
/* Create task */
|
|
task = task_init();
|
|
|
|
if (!task)
|
|
goto error;
|
|
|
|
/* Configure task */
|
|
task->handler = task_update_installed_cores_handler;
|
|
task->state = update_installed_handle;
|
|
task->title = strdup(msg_hash_to_str(MSG_FETCHING_CORE_LIST));
|
|
task->alternative_look = true;
|
|
task->progress = 0;
|
|
|
|
/* Push task */
|
|
task_queue_push(task);
|
|
|
|
return;
|
|
|
|
error:
|
|
|
|
/* Clean up task */
|
|
if (task)
|
|
{
|
|
free(task);
|
|
task = NULL;
|
|
}
|
|
|
|
/* Clean up handle */
|
|
free_update_installed_cores_handle(update_installed_handle);
|
|
}
|