mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-27 10:10:57 +00:00
(Core Updater) Only download when new core is available + add option to update all installed cores
This commit is contained in:
parent
305aee2c33
commit
695749f155
@ -1760,6 +1760,7 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
$(LIBRETRO_COMM_DIR)/net/net_http_parse.o \
|
||||
$(LIBRETRO_COMM_DIR)/net/net_socket.o \
|
||||
$(LIBRETRO_COMM_DIR)/net/net_natt.o \
|
||||
core_updater_list.o \
|
||||
network/net_http_special.o \
|
||||
tasks/task_http.o \
|
||||
tasks/task_netplay_lan_scan.o \
|
||||
@ -1769,7 +1770,8 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
|
||||
ifeq ($(HAVE_MENU_COMMON), 1)
|
||||
OBJ += tasks/task_pl_thumbnail_download.o \
|
||||
menu/menu_networking.o
|
||||
tasks/task_core_updater.o \
|
||||
menu/menu_networking.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SSL), 1)
|
||||
|
778
core_updater_list.c
Normal file
778
core_updater_list.c
Normal file
@ -0,0 +1,778 @@
|
||||
/* Copyright (C) 2010-2019 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (core_updater_list.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <file/file_path.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <net/net_http.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
#include "configuration.h"
|
||||
#include "file_path_special.h"
|
||||
#include "core_info.h"
|
||||
|
||||
#include "core_updater_list.h"
|
||||
|
||||
/* Holds all entries in a core updater list */
|
||||
struct core_updater_list
|
||||
{
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
core_updater_list_entry_t *entries;
|
||||
};
|
||||
|
||||
/* Cached ('global') core updater list */
|
||||
static core_updater_list_t *core_list_cached = NULL;
|
||||
|
||||
/**************************************/
|
||||
/* Initialisation / De-Initialisation */
|
||||
/**************************************/
|
||||
|
||||
/* Frees contents of specified core updater
|
||||
* list entry */
|
||||
static void core_updater_list_free_entry(core_updater_list_entry_t *entry)
|
||||
{
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
if (entry->remote_filename)
|
||||
{
|
||||
free(entry->remote_filename);
|
||||
entry->remote_filename = NULL;
|
||||
}
|
||||
|
||||
if (entry->remote_core_path)
|
||||
{
|
||||
free(entry->remote_core_path);
|
||||
entry->remote_core_path = NULL;
|
||||
}
|
||||
|
||||
if (entry->local_core_path)
|
||||
{
|
||||
free(entry->local_core_path);
|
||||
entry->local_core_path = NULL;
|
||||
}
|
||||
|
||||
if (entry->local_info_path)
|
||||
{
|
||||
free(entry->local_info_path);
|
||||
entry->local_info_path = NULL;
|
||||
}
|
||||
|
||||
if (entry->display_name)
|
||||
{
|
||||
free(entry->display_name);
|
||||
entry->display_name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Creates a new, empty core updater list.
|
||||
* Returns a handle to a new core_updater_list_t object
|
||||
* on success, otherwise returns NULL. */
|
||||
core_updater_list_t *core_updater_list_init(size_t max_size)
|
||||
{
|
||||
core_updater_list_t *core_list = NULL;
|
||||
core_updater_list_entry_t *entries = NULL;
|
||||
|
||||
/* Sanity check */
|
||||
if (max_size < 1)
|
||||
return NULL;
|
||||
|
||||
/* Create core updater list */
|
||||
core_list = (core_updater_list_t*)calloc(1, sizeof(*core_list));
|
||||
|
||||
if (!core_list)
|
||||
return NULL;
|
||||
|
||||
/* Create entries array */
|
||||
entries = (core_updater_list_entry_t*)calloc(max_size, sizeof(*entries));
|
||||
|
||||
if (!entries)
|
||||
{
|
||||
free(core_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initial configuration */
|
||||
core_list->size = 0;
|
||||
core_list->capacity = max_size;
|
||||
core_list->entries = entries;
|
||||
|
||||
return core_list;
|
||||
}
|
||||
|
||||
/* Resets (removes all entries of) specified core
|
||||
* updater list */
|
||||
void core_updater_list_reset(core_updater_list_t *core_list)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!core_list)
|
||||
return;
|
||||
|
||||
for (i = 0; i < core_list->size; i++)
|
||||
{
|
||||
core_updater_list_entry_t *entry = &core_list->entries[i];
|
||||
|
||||
if (entry)
|
||||
core_updater_list_free_entry(entry);
|
||||
}
|
||||
|
||||
core_list->size = 0;
|
||||
}
|
||||
|
||||
/* Frees specified core updater list */
|
||||
void core_updater_list_free(core_updater_list_t *core_list)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!core_list)
|
||||
return;
|
||||
|
||||
for (i = 0; i < core_list->size; i++)
|
||||
{
|
||||
core_updater_list_entry_t *entry = &core_list->entries[i];
|
||||
|
||||
if (entry)
|
||||
core_updater_list_free_entry(entry);
|
||||
}
|
||||
|
||||
free(core_list->entries);
|
||||
core_list->entries = NULL;
|
||||
|
||||
free(core_list);
|
||||
}
|
||||
|
||||
/***************/
|
||||
/* Cached List */
|
||||
/***************/
|
||||
|
||||
/* Creates a new, empty cached core updater list
|
||||
* (i.e. 'global' list).
|
||||
* Returns false in the event of an error. */
|
||||
bool core_updater_list_init_cached(size_t max_size)
|
||||
{
|
||||
/* Free any existing cached core updater list */
|
||||
if (core_list_cached)
|
||||
{
|
||||
core_updater_list_free(core_list_cached);
|
||||
core_list_cached = NULL;
|
||||
}
|
||||
|
||||
core_list_cached = core_updater_list_init(max_size);
|
||||
|
||||
if (!core_list_cached)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Fetches cached core updater list */
|
||||
core_updater_list_t *core_updater_list_get_cached(void)
|
||||
{
|
||||
if (core_list_cached)
|
||||
return core_list_cached;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Frees cached core updater list */
|
||||
void core_updater_list_free_cached(void)
|
||||
{
|
||||
core_updater_list_free(core_list_cached);
|
||||
core_list_cached = NULL;
|
||||
}
|
||||
|
||||
/***********/
|
||||
/* Getters */
|
||||
/***********/
|
||||
|
||||
/* Returns number of entries in core updater list */
|
||||
size_t core_updater_list_size(core_updater_list_t *core_list)
|
||||
{
|
||||
if (!core_list)
|
||||
return 0;
|
||||
|
||||
return core_list->size;
|
||||
}
|
||||
|
||||
/* Returns maximum allowed number of entries in core
|
||||
* updater list */
|
||||
size_t core_updater_list_capacity(core_updater_list_t *core_list)
|
||||
{
|
||||
if (!core_list)
|
||||
return 0;
|
||||
|
||||
return core_list->capacity;
|
||||
}
|
||||
|
||||
/* Fetches core updater list entry corresponding
|
||||
* to the specified entry index.
|
||||
* Returns false if index is invalid. */
|
||||
bool core_updater_list_get_index(
|
||||
core_updater_list_t *core_list,
|
||||
size_t idx,
|
||||
const core_updater_list_entry_t **entry)
|
||||
{
|
||||
if (!core_list || !entry)
|
||||
return false;
|
||||
|
||||
if (idx >= core_list->size)
|
||||
return false;
|
||||
|
||||
if (entry)
|
||||
*entry = &core_list->entries[idx];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Fetches core updater list entry corresponding
|
||||
* to the specified remote core filename.
|
||||
* Returns false if core is not found. */
|
||||
bool core_updater_list_get_filename(
|
||||
core_updater_list_t *core_list,
|
||||
const char *remote_filename,
|
||||
const core_updater_list_entry_t **entry)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!core_list || string_is_empty(remote_filename))
|
||||
return false;
|
||||
|
||||
if (core_list->size < 1)
|
||||
return false;
|
||||
|
||||
/* Search for specified filename */
|
||||
for (i = 0; i < core_list->size; i++)
|
||||
{
|
||||
core_updater_list_entry_t *current_entry = &core_list->entries[i];
|
||||
|
||||
if (string_is_empty(current_entry->remote_filename))
|
||||
continue;
|
||||
|
||||
if (string_is_equal(remote_filename, current_entry->remote_filename))
|
||||
{
|
||||
if (entry)
|
||||
*entry = current_entry;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Fetches core updater list entry corresponding
|
||||
* to the specified core.
|
||||
* Returns false if core is not found. */
|
||||
bool core_updater_list_get_core(
|
||||
core_updater_list_t *core_list,
|
||||
const char *local_core_path,
|
||||
const core_updater_list_entry_t **entry)
|
||||
{
|
||||
size_t i;
|
||||
char real_core_path[PATH_MAX_LENGTH];
|
||||
|
||||
real_core_path[0] = '\0';
|
||||
|
||||
if (!core_list || !entry || string_is_empty(local_core_path))
|
||||
return false;
|
||||
|
||||
if (core_list->size < 1)
|
||||
return false;
|
||||
|
||||
/* Resolve absolute pathname of local_core_path */
|
||||
strlcpy(real_core_path, local_core_path, sizeof(real_core_path));
|
||||
path_resolve_realpath(real_core_path, sizeof(real_core_path), true);
|
||||
|
||||
if (string_is_empty(real_core_path))
|
||||
return false;
|
||||
|
||||
/* Search for specified core */
|
||||
for (i = 0; i < core_list->size; i++)
|
||||
{
|
||||
core_updater_list_entry_t *current_entry = &core_list->entries[i];
|
||||
|
||||
if (string_is_empty(current_entry->local_core_path))
|
||||
continue;
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Handle case-insensitive operating systems*/
|
||||
if (string_is_equal_noncase(real_core_path, current_entry->local_core_path))
|
||||
{
|
||||
#else
|
||||
if (string_is_equal(real_core_path, current_entry->local_core_path))
|
||||
{
|
||||
#endif
|
||||
if (entry)
|
||||
*entry = current_entry;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/***********/
|
||||
/* Setters */
|
||||
/***********/
|
||||
|
||||
/* Parses date string and adds contents to
|
||||
* specified core updater list entry */
|
||||
static bool core_updater_list_set_date(
|
||||
core_updater_list_entry_t *entry, const char *date_str)
|
||||
{
|
||||
struct string_list *date_list = NULL;
|
||||
|
||||
if (!entry || string_is_empty(date_str))
|
||||
goto error;
|
||||
|
||||
/* Split date string into component values */
|
||||
date_list = string_split(date_str, "-");
|
||||
|
||||
if (!date_list)
|
||||
goto error;
|
||||
|
||||
/* Date string must have 3 values:
|
||||
* [year] [month] [day] */
|
||||
if (date_list->size < 3)
|
||||
goto error;
|
||||
|
||||
/* Convert date string values */
|
||||
entry->date.year = string_to_unsigned(date_list->elems[0].data);
|
||||
entry->date.month = string_to_unsigned(date_list->elems[1].data);
|
||||
entry->date.day = string_to_unsigned(date_list->elems[2].data);
|
||||
|
||||
/* Clean up */
|
||||
string_list_free(date_list);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
|
||||
if (date_list)
|
||||
string_list_free(date_list);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Parses crc string and adds value to
|
||||
* specified core updater list entry */
|
||||
static bool core_updater_list_set_crc(
|
||||
core_updater_list_entry_t *entry, const char *crc_str)
|
||||
{
|
||||
uint32_t crc;
|
||||
|
||||
if (!entry || string_is_empty(crc_str))
|
||||
return false;
|
||||
|
||||
crc = (uint32_t)string_hex_to_unsigned(crc_str);
|
||||
|
||||
if (crc == 0)
|
||||
return false;
|
||||
|
||||
entry->crc = crc;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Parses core filename string and adds all
|
||||
* associated paths to the specified core
|
||||
* updater list entry */
|
||||
static bool core_updater_list_set_paths(
|
||||
core_updater_list_entry_t *entry, const char *filename_str)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
char *last_underscore = NULL;
|
||||
char *tmp_url = NULL;
|
||||
char remote_core_path[PATH_MAX_LENGTH];
|
||||
char local_core_path[PATH_MAX_LENGTH];
|
||||
char local_info_path[PATH_MAX_LENGTH];
|
||||
char display_name[255];
|
||||
bool is_archive;
|
||||
|
||||
remote_core_path[0] = '\0';
|
||||
local_core_path[0] = '\0';
|
||||
local_info_path[0] = '\0';
|
||||
display_name[0] = '\0';
|
||||
|
||||
if (!entry || string_is_empty(filename_str) || !settings)
|
||||
return false;
|
||||
|
||||
if (string_is_empty(settings->paths.directory_libretro) ||
|
||||
string_is_empty(settings->paths.path_libretro_info) ||
|
||||
string_is_empty(settings->paths.network_buildbot_url))
|
||||
return false;
|
||||
|
||||
/* Check whether remote file is an archive */
|
||||
is_archive = path_is_compressed_file(filename_str);
|
||||
|
||||
/* remote_filename */
|
||||
if (entry->remote_filename)
|
||||
{
|
||||
free(entry->remote_filename);
|
||||
entry->remote_filename = NULL;
|
||||
}
|
||||
|
||||
entry->remote_filename = strdup(filename_str);
|
||||
|
||||
/* remote_core_path */
|
||||
fill_pathname_join(
|
||||
remote_core_path,
|
||||
settings->paths.network_buildbot_url,
|
||||
filename_str,
|
||||
sizeof(remote_core_path));
|
||||
|
||||
/* > Apply proper URL encoding (messy...) */
|
||||
tmp_url = strdup(remote_core_path);
|
||||
remote_core_path[0] = '\0';
|
||||
net_http_urlencode_full(
|
||||
remote_core_path, tmp_url, sizeof(remote_core_path));
|
||||
if (tmp_url)
|
||||
free(tmp_url);
|
||||
|
||||
if (entry->remote_core_path)
|
||||
{
|
||||
free(entry->remote_core_path);
|
||||
entry->remote_core_path = NULL;
|
||||
}
|
||||
|
||||
entry->remote_core_path = strdup(remote_core_path);
|
||||
|
||||
/* local_core_path */
|
||||
fill_pathname_join(
|
||||
local_core_path,
|
||||
settings->paths.directory_libretro,
|
||||
filename_str,
|
||||
sizeof(local_core_path));
|
||||
|
||||
if (is_archive)
|
||||
path_remove_extension(local_core_path);
|
||||
|
||||
path_resolve_realpath(local_core_path, sizeof(local_core_path), true);
|
||||
|
||||
if (entry->local_core_path)
|
||||
{
|
||||
free(entry->local_core_path);
|
||||
entry->local_core_path = NULL;
|
||||
}
|
||||
|
||||
entry->local_core_path = strdup(local_core_path);
|
||||
|
||||
/* local_info_path */
|
||||
fill_pathname_join_noext(
|
||||
local_info_path,
|
||||
settings->paths.path_libretro_info,
|
||||
filename_str,
|
||||
sizeof(local_info_path));
|
||||
|
||||
if (is_archive)
|
||||
path_remove_extension(local_info_path);
|
||||
|
||||
/* > Remove any non-standard core filename
|
||||
* additions (i.e. info files end with
|
||||
* '_libretro' but core files may have
|
||||
* a platform specific addendum,
|
||||
* e.g. '_android')*/
|
||||
last_underscore = (char*)strrchr(local_info_path, '_');
|
||||
|
||||
if (!string_is_empty(last_underscore))
|
||||
{
|
||||
if (string_is_not_equal_fast(last_underscore, "_libretro", 9))
|
||||
*last_underscore = '\0';
|
||||
}
|
||||
|
||||
/* > Add proper file extension */
|
||||
strlcat(
|
||||
local_info_path,
|
||||
file_path_str(FILE_PATH_CORE_INFO_EXTENSION),
|
||||
sizeof(local_info_path));
|
||||
|
||||
if (entry->local_info_path)
|
||||
{
|
||||
free(entry->local_info_path);
|
||||
entry->local_info_path = NULL;
|
||||
}
|
||||
|
||||
entry->local_info_path = strdup(local_info_path);
|
||||
|
||||
/* display_name
|
||||
* > Note: It's a bit rubbish that we have to
|
||||
* read the actual core info files here...
|
||||
* Would be better to cache this globally
|
||||
* (at present, we only cache info for
|
||||
* *installed* cores...) */
|
||||
if (path_is_valid(local_info_path))
|
||||
if (!core_info_get_display_name(
|
||||
local_info_path, display_name, sizeof(display_name)))
|
||||
display_name[0] = '\0';
|
||||
|
||||
/* > If info file does not exist, just use
|
||||
* core filename */
|
||||
if (string_is_empty(display_name))
|
||||
strlcpy(display_name, filename_str, sizeof(display_name));
|
||||
|
||||
if (entry->display_name)
|
||||
{
|
||||
free(entry->display_name);
|
||||
entry->display_name = NULL;
|
||||
}
|
||||
|
||||
entry->display_name = strdup(display_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Adds entry to the end of the specified core
|
||||
* updater list
|
||||
* NOTE: Entry string values are passed by
|
||||
* reference - *do not free the entry passed
|
||||
* to this function* */
|
||||
static bool core_updater_list_push_entry(
|
||||
core_updater_list_t *core_list, core_updater_list_entry_t *entry)
|
||||
{
|
||||
core_updater_list_entry_t *list_entry = NULL;
|
||||
|
||||
if (!core_list || !entry)
|
||||
return false;
|
||||
|
||||
/* Ensure there is enough space for the new entry */
|
||||
if (core_list->capacity <= core_list->size)
|
||||
return false;
|
||||
|
||||
/* Get handle of new entry inside list */
|
||||
list_entry = &core_list->entries[core_list->size];
|
||||
|
||||
if (!list_entry)
|
||||
return false;
|
||||
|
||||
/* Ensure list entry is empty */
|
||||
core_updater_list_free_entry(list_entry);
|
||||
|
||||
/* Assign paths */
|
||||
list_entry->remote_filename = entry->remote_filename;
|
||||
list_entry->remote_core_path = entry->remote_core_path;
|
||||
list_entry->local_core_path = entry->local_core_path;
|
||||
list_entry->local_info_path = entry->local_info_path;
|
||||
list_entry->display_name = entry->display_name;
|
||||
|
||||
/* Copy crc */
|
||||
list_entry->crc = entry->crc;
|
||||
|
||||
/* Copy date */
|
||||
memcpy(&list_entry->date, &entry->date, sizeof(core_updater_list_date_t));
|
||||
|
||||
/* Increment list size */
|
||||
core_list->size++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Parses the contents of a single buildbot
|
||||
* core listing and adds it to the specified
|
||||
* core updater list */
|
||||
static void core_updater_list_add_entry(
|
||||
core_updater_list_t *core_list,
|
||||
struct string_list *network_core_entry_list)
|
||||
{
|
||||
const char *date_str = NULL;
|
||||
const char *crc_str = NULL;
|
||||
const char *filename_str = NULL;
|
||||
core_updater_list_entry_t entry = {0};
|
||||
|
||||
if (!core_list || !network_core_entry_list)
|
||||
goto error;
|
||||
|
||||
/* > Listings must have 3 entries:
|
||||
* [date] [crc] [filename] */
|
||||
if (network_core_entry_list->size < 3)
|
||||
goto error;
|
||||
|
||||
/* Get handles of the individual listing strings */
|
||||
date_str = network_core_entry_list->elems[0].data;
|
||||
crc_str = network_core_entry_list->elems[1].data;
|
||||
filename_str = network_core_entry_list->elems[2].data;
|
||||
|
||||
if (string_is_empty(date_str) ||
|
||||
string_is_empty(crc_str) ||
|
||||
string_is_empty(filename_str))
|
||||
goto error;
|
||||
|
||||
/* Check whether core file is already included
|
||||
* in the list (this is *not* an error condition,
|
||||
* it just means we can skip the current listing) */
|
||||
if (core_updater_list_get_filename(core_list, filename_str, NULL))
|
||||
goto error;
|
||||
|
||||
/* Parse individual listing strings */
|
||||
if (!core_updater_list_set_date(&entry, date_str))
|
||||
goto error;
|
||||
|
||||
if (!core_updater_list_set_crc(&entry, crc_str))
|
||||
goto error;
|
||||
|
||||
if (!core_updater_list_set_paths(&entry, filename_str))
|
||||
goto error;
|
||||
|
||||
/* Add entry to list */
|
||||
if (!core_updater_list_push_entry(core_list, &entry))
|
||||
goto error;
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
/* This is not a *fatal* error - it just
|
||||
* means one of the following:
|
||||
* - The current line of entry text received
|
||||
* from the buildbot is broken somehow
|
||||
* (could be the case that the network buffer
|
||||
* wasn't large enough to cache the entire
|
||||
* string, so the last line was truncated)
|
||||
* - The core updater list struct is at capacity
|
||||
* In either case, the current entry is discarded
|
||||
* and we move on to the next one
|
||||
* (network transfers are fishy business, so we
|
||||
* choose to ignore this sort of error - don't
|
||||
* want the whole fetch to fail because of a
|
||||
* trivial glitch...) */
|
||||
core_updater_list_free_entry(&entry);
|
||||
}
|
||||
|
||||
/* Core updater list qsort helper function */
|
||||
static int core_updater_list_qsort_func(
|
||||
const core_updater_list_entry_t *a, const core_updater_list_entry_t *b)
|
||||
{
|
||||
if (!a || !b)
|
||||
return 0;
|
||||
|
||||
if (string_is_empty(a->display_name) || string_is_empty(b->display_name))
|
||||
return 0;
|
||||
|
||||
return strcasecmp(a->display_name, b->display_name);
|
||||
}
|
||||
|
||||
/* Sorts core updater list into alphabetical order */
|
||||
static void core_updater_list_qsort(core_updater_list_t *core_list)
|
||||
{
|
||||
if (!core_list)
|
||||
return;
|
||||
|
||||
if (core_list->size < 2)
|
||||
return;
|
||||
|
||||
qsort(
|
||||
core_list->entries,
|
||||
core_list->size,
|
||||
sizeof(core_updater_list_entry_t),
|
||||
(int (*)(const void *, const void *))
|
||||
core_updater_list_qsort_func);
|
||||
}
|
||||
|
||||
/* Reads the contents of a buildbot core list
|
||||
* network request into the specified
|
||||
* core_updater_list_t object.
|
||||
* Returns false in the event of an error. */
|
||||
bool core_updater_list_parse_network_data(
|
||||
core_updater_list_t *core_list, const char *data, size_t len)
|
||||
{
|
||||
struct string_list *network_core_list = NULL;
|
||||
struct string_list *network_core_entry_list = NULL;
|
||||
char *data_buf = NULL;
|
||||
size_t i;
|
||||
|
||||
/* Sanity check */
|
||||
if (!core_list || string_is_empty(data) || (len < 1))
|
||||
goto error;
|
||||
|
||||
/* We're populating a list 'from scratch' - remove
|
||||
* any existing entries */
|
||||
core_updater_list_reset(core_list);
|
||||
|
||||
/* Input data string is not terminated - have
|
||||
* to copy it to a temporary buffer... */
|
||||
data_buf = (char*)malloc((len + 1) * sizeof(char));
|
||||
|
||||
if (!data_buf)
|
||||
goto error;
|
||||
|
||||
memcpy(data_buf, data, len * sizeof(char));
|
||||
data_buf[len] = '\0';
|
||||
|
||||
/* Split network listing request into lines */
|
||||
network_core_list = string_split(data_buf, "\n");
|
||||
|
||||
if (!network_core_list)
|
||||
goto error;
|
||||
|
||||
if (network_core_list->size < 1)
|
||||
goto error;
|
||||
|
||||
/* Temporary data buffer is no longer required */
|
||||
free(data_buf);
|
||||
|
||||
/* Loop over lines */
|
||||
for (i = 0; i < network_core_list->size; i++)
|
||||
{
|
||||
const char *line = network_core_list->elems[i].data;
|
||||
|
||||
if (string_is_empty(line))
|
||||
continue;
|
||||
|
||||
/* Split line into listings info components */
|
||||
network_core_entry_list = string_split(line, " ");
|
||||
|
||||
/* Parse listings info and add to core updater
|
||||
* list */
|
||||
core_updater_list_add_entry(
|
||||
core_list, network_core_entry_list);
|
||||
|
||||
/* Clean up */
|
||||
string_list_free(network_core_entry_list);
|
||||
network_core_entry_list = NULL;
|
||||
}
|
||||
|
||||
/* Sanity check */
|
||||
if (core_list->size < 1)
|
||||
goto error;
|
||||
|
||||
/* Clean up */
|
||||
string_list_free(network_core_list);
|
||||
|
||||
/* Sort completed list */
|
||||
core_updater_list_qsort(core_list);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
|
||||
if (network_core_list)
|
||||
string_list_free(network_core_list);
|
||||
|
||||
if (network_core_entry_list)
|
||||
string_list_free(network_core_entry_list);
|
||||
|
||||
if (data_buf)
|
||||
free(data_buf);
|
||||
|
||||
return false;
|
||||
}
|
143
core_updater_list.h
Normal file
143
core_updater_list.h
Normal file
@ -0,0 +1,143 @@
|
||||
/* Copyright (C) 2010-2019 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (core_updater_list.h).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __CORE_UPDATER_LIST_H
|
||||
#define __CORE_UPDATER_LIST_H
|
||||
|
||||
#include <retro_common_api.h>
|
||||
#include <libretro.h>
|
||||
|
||||
#include <boolean.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
/* Default maximum number of entries in
|
||||
* a core updater list */
|
||||
#define CORE_UPDATER_LIST_SIZE 9999
|
||||
|
||||
/* Holds all date info for a core file
|
||||
* on the buildbot */
|
||||
typedef struct
|
||||
{
|
||||
unsigned year;
|
||||
unsigned month;
|
||||
unsigned day;
|
||||
} core_updater_list_date_t;
|
||||
|
||||
/* Holds all info related to a core
|
||||
* file on the buildbot */
|
||||
typedef struct
|
||||
{
|
||||
char *remote_filename;
|
||||
char *remote_core_path;
|
||||
char *local_core_path;
|
||||
char *local_info_path;
|
||||
char *display_name;
|
||||
uint32_t crc;
|
||||
core_updater_list_date_t date;
|
||||
} core_updater_list_entry_t;
|
||||
|
||||
/* Prevent direct access to core_updater_list_t
|
||||
* members */
|
||||
typedef struct core_updater_list core_updater_list_t;
|
||||
|
||||
/**************************************/
|
||||
/* Initialisation / De-Initialisation */
|
||||
/**************************************/
|
||||
|
||||
/* Creates a new, empty core updater list with a
|
||||
* maximum number of 'max_size' entries.
|
||||
* Returns a handle to a new core_updater_list_t object
|
||||
* on success, otherwise returns NULL. */
|
||||
core_updater_list_t *core_updater_list_init(size_t max_size);
|
||||
|
||||
/* Resets (removes all entries of) specified core
|
||||
* updater list */
|
||||
void core_updater_list_reset(core_updater_list_t *core_list);
|
||||
|
||||
/* Frees specified core updater list */
|
||||
void core_updater_list_free(core_updater_list_t *core_list);
|
||||
|
||||
/***************/
|
||||
/* Cached List */
|
||||
/***************/
|
||||
|
||||
/* Creates a new, empty cached core updater list
|
||||
* (i.e. 'global' list).
|
||||
* Returns false in the event of an error. */
|
||||
bool core_updater_list_init_cached(size_t max_size);
|
||||
|
||||
/* Fetches cached core updater list */
|
||||
core_updater_list_t *core_updater_list_get_cached(void);
|
||||
|
||||
/* Frees cached core updater list */
|
||||
void core_updater_list_free_cached(void);
|
||||
|
||||
/***********/
|
||||
/* Getters */
|
||||
/***********/
|
||||
|
||||
/* Returns number of entries in core updater list */
|
||||
size_t core_updater_list_size(core_updater_list_t *core_list);
|
||||
|
||||
/* Returns maximum allowed number of entries in core
|
||||
* updater list */
|
||||
size_t core_updater_list_capacity(core_updater_list_t *core_list);
|
||||
|
||||
/* Fetches core updater list entry corresponding
|
||||
* to the specified entry index.
|
||||
* Returns false if index is invalid. */
|
||||
bool core_updater_list_get_index(
|
||||
core_updater_list_t *core_list,
|
||||
size_t idx,
|
||||
const core_updater_list_entry_t **entry);
|
||||
|
||||
/* Fetches core updater list entry corresponding
|
||||
* to the specified remote core filename.
|
||||
* Returns false if core is not found. */
|
||||
bool core_updater_list_get_filename(
|
||||
core_updater_list_t *core_list,
|
||||
const char *remote_filename,
|
||||
const core_updater_list_entry_t **entry);
|
||||
|
||||
/* Fetches core updater list entry corresponding
|
||||
* to the specified core.
|
||||
* Returns false if core is not found. */
|
||||
bool core_updater_list_get_core(
|
||||
core_updater_list_t *core_list,
|
||||
const char *local_core_path,
|
||||
const core_updater_list_entry_t **entry);
|
||||
|
||||
/***********/
|
||||
/* Setters */
|
||||
/***********/
|
||||
|
||||
/* Reads the contents of a buildbot core list
|
||||
* network request into the specified
|
||||
* core_updater_list_t object.
|
||||
* Returns false in the event of an error. */
|
||||
bool core_updater_list_parse_network_data(
|
||||
core_updater_list_t *core_list, const char *data, size_t len);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
@ -1084,6 +1084,10 @@ FRONTEND
|
||||
|
||||
#include "../core_info.c"
|
||||
|
||||
#if defined(HAVE_NETWORKING)
|
||||
#include "../core_updater_list.c"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
UI
|
||||
============================================================ */
|
||||
@ -1235,6 +1239,7 @@ DATA RUNLOOP
|
||||
#endif
|
||||
#if defined(HAVE_NETWORKING) && defined(HAVE_MENU)
|
||||
#include "../tasks/task_pl_thumbnail_download.c"
|
||||
#include "../tasks/task_core_updater.c"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
|
@ -487,6 +487,8 @@ MSG_HASH(MENU_ENUM_LABEL_DOWNLOAD_CORE_CONTENT,
|
||||
"download_core_content")
|
||||
MSG_HASH(MENU_ENUM_LABEL_DOWNLOAD_CORE_CONTENT_DIRS,
|
||||
"download_core_content_dirs")
|
||||
MSG_HASH(MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES,
|
||||
"update_installed_cores")
|
||||
MSG_HASH(MENU_ENUM_LABEL_CONTENT_DIR,
|
||||
"content_directory")
|
||||
MSG_HASH(MENU_ENUM_LABEL_MENU_SCALE_FACTOR,
|
||||
|
@ -832,6 +832,54 @@ MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_DOWNLOAD_CORE_CONTENT,
|
||||
"Content Downloader"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_UPDATE_INSTALLED_CORES,
|
||||
"Update Installed Cores"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_UPDATE_INSTALLED_CORES,
|
||||
"Update all installed cores to the latest version available."
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_FETCHING_CORE_LIST,
|
||||
"Fetching core list..."
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_CORE_LIST_FAILED,
|
||||
"Failed to retrieve core list!"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_LATEST_CORE_INSTALLED,
|
||||
"Latest version already installed: "
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_UPDATING_CORE,
|
||||
"Updating core: "
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_DOWNLOADING_CORE,
|
||||
"Downloading core: "
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_EXTRACTING_CORE,
|
||||
"Extracting core: "
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_CORE_INSTALLED,
|
||||
"Core installed: "
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_SCANNING_CORES,
|
||||
"Scanning cores..."
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_CHECKING_CORE,
|
||||
"Checking core: "
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_ALL_CORES_UPDATED,
|
||||
"All installed cores at latest version"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_MENU_SCALE_FACTOR,
|
||||
"Menu Scale Factor"
|
||||
|
@ -141,6 +141,11 @@ void string_replace_all_chars(char *str, char find, char replace);
|
||||
* Returns 0 if string is invalid */
|
||||
unsigned string_to_unsigned(const char *str);
|
||||
|
||||
/* Converts hexadecimal string to unsigned integer.
|
||||
* Handles optional leading '0x'.
|
||||
* Returns 0 if string is invalid */
|
||||
unsigned string_hex_to_unsigned(const char *str);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -349,3 +349,36 @@ unsigned string_to_unsigned(const char *str)
|
||||
|
||||
return (unsigned)strtoul(str, NULL, 10);
|
||||
}
|
||||
|
||||
/* Converts hexadecimal string to unsigned integer.
|
||||
* Handles optional leading '0x'.
|
||||
* Returns 0 if string is invalid */
|
||||
unsigned string_hex_to_unsigned(const char *str)
|
||||
{
|
||||
const char *hex_str = str;
|
||||
const char *ptr = NULL;
|
||||
size_t len;
|
||||
|
||||
if (string_is_empty(str))
|
||||
return 0;
|
||||
|
||||
/* Remove leading '0x', if required */
|
||||
len = strlen(str);
|
||||
|
||||
if (len >= 2)
|
||||
if ((str[0] == '0') &&
|
||||
((str[1] == 'x') || (str[1] == 'X')))
|
||||
hex_str = str + 2;
|
||||
|
||||
if (string_is_empty(hex_str))
|
||||
return 0;
|
||||
|
||||
/* Check for valid characters */
|
||||
for (ptr = hex_str; *ptr != '\0'; ptr++)
|
||||
{
|
||||
if (!isxdigit(*ptr))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (unsigned)strtoul(hex_str, NULL, 16);
|
||||
}
|
||||
|
@ -3797,12 +3797,11 @@ static void cb_decompressed(retro_task_t *task,
|
||||
|
||||
switch (type_hash)
|
||||
{
|
||||
case CB_CORE_UPDATER_DOWNLOAD:
|
||||
generic_action_ok_command(CMD_EVENT_CORE_INFO_INIT);
|
||||
break;
|
||||
case CB_UPDATE_ASSETS:
|
||||
generic_action_ok_command(CMD_EVENT_REINIT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3820,6 +3819,37 @@ static void cb_decompressed(retro_task_t *task,
|
||||
}
|
||||
#endif
|
||||
|
||||
static int action_ok_core_updater_list(const char *path,
|
||||
const char *label, unsigned type, size_t idx, size_t entry_idx)
|
||||
{
|
||||
core_updater_list_t *core_list = NULL;
|
||||
bool refresh = true;
|
||||
|
||||
/* Get cached core updater list, initialising
|
||||
* it if required */
|
||||
core_list = core_updater_list_get_cached();
|
||||
|
||||
if (!core_list)
|
||||
{
|
||||
core_updater_list_init_cached(CORE_UPDATER_LIST_SIZE);
|
||||
core_list = core_updater_list_get_cached();
|
||||
|
||||
if (!core_list)
|
||||
return menu_cbs_exit();
|
||||
}
|
||||
|
||||
/* Initial setup... */
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
|
||||
generic_action_ok_command(CMD_EVENT_NETWORK_INIT);
|
||||
|
||||
/* Push core list update task */
|
||||
task_push_get_core_updater_list(core_list, false, true);
|
||||
|
||||
return generic_action_ok_displaylist_push(
|
||||
path, NULL, label, type, idx, entry_idx,
|
||||
ACTION_OK_DL_CORE_UPDATER_LIST);
|
||||
}
|
||||
|
||||
static int generic_action_ok_network(const char *path,
|
||||
const char *label, unsigned type, size_t idx, size_t entry_idx,
|
||||
enum msg_hash_enums enum_idx)
|
||||
@ -3860,17 +3890,6 @@ static int generic_action_ok_network(const char *path,
|
||||
callback = cb_net_generic;
|
||||
suppress_msg = true;
|
||||
break;
|
||||
case MENU_ENUM_LABEL_CB_CORE_UPDATER_LIST:
|
||||
|
||||
if (string_is_empty(settings->paths.network_buildbot_url))
|
||||
return menu_cbs_exit();
|
||||
|
||||
fill_pathname_join(url_path, settings->paths.network_buildbot_url,
|
||||
".index-extended", sizeof(url_path));
|
||||
url_label = msg_hash_to_str(enum_idx);
|
||||
type_id2 = ACTION_OK_DL_CORE_UPDATER_LIST;
|
||||
callback = cb_net_generic;
|
||||
break;
|
||||
case MENU_ENUM_LABEL_CB_THUMBNAILS_UPDATER_LIST:
|
||||
fill_pathname_join(url_path,
|
||||
"http://thumbnailpacks.libretro.com",
|
||||
@ -3919,7 +3938,6 @@ static int (funcname)(const char *path, const char *label, unsigned type, size_t
|
||||
|
||||
default_action_ok_list(action_ok_core_content_list, MENU_ENUM_LABEL_CB_CORE_CONTENT_LIST)
|
||||
default_action_ok_list(action_ok_core_content_dirs_list, MENU_ENUM_LABEL_CB_CORE_CONTENT_DIRS_LIST)
|
||||
default_action_ok_list(action_ok_core_updater_list, MENU_ENUM_LABEL_CB_CORE_UPDATER_LIST)
|
||||
default_action_ok_list(action_ok_thumbnails_updater_list, MENU_ENUM_LABEL_CB_THUMBNAILS_UPDATER_LIST)
|
||||
default_action_ok_list(action_ok_lakka_list, MENU_ENUM_LABEL_CB_LAKKA_LIST)
|
||||
|
||||
@ -3952,7 +3970,7 @@ void cb_generic_download(retro_task_t *task,
|
||||
settings_t *settings = config_get_ptr();
|
||||
http_transfer_data_t *data = (http_transfer_data_t*)task_data;
|
||||
|
||||
if (!data || !data->data | !transf)
|
||||
if (!data || !data->data || !transf)
|
||||
goto finish;
|
||||
|
||||
output_path[0] = '\0';
|
||||
@ -3965,9 +3983,6 @@ void cb_generic_download(retro_task_t *task,
|
||||
case MENU_ENUM_LABEL_CB_CORE_THUMBNAILS_DOWNLOAD:
|
||||
dir_path = settings->paths.directory_thumbnails;
|
||||
break;
|
||||
case MENU_ENUM_LABEL_CB_CORE_UPDATER_DOWNLOAD:
|
||||
dir_path = settings->paths.directory_libretro;
|
||||
break;
|
||||
case MENU_ENUM_LABEL_CB_CORE_CONTENT_DOWNLOAD:
|
||||
dir_path = settings->paths.directory_core_assets;
|
||||
#if defined(HAVE_COMPRESSION) && defined(HAVE_ZLIB)
|
||||
@ -4094,28 +4109,23 @@ void cb_generic_download(retro_task_t *task,
|
||||
|
||||
if (path_is_compressed_file(output_path))
|
||||
{
|
||||
void *frontend_userdata = task->frontend_userdata;
|
||||
task->frontend_userdata = NULL;
|
||||
retro_task_t *decompress_task = NULL;
|
||||
void *frontend_userdata = task->frontend_userdata;
|
||||
task->frontend_userdata = NULL;
|
||||
|
||||
if (!task_push_decompress(output_path, dir_path,
|
||||
NULL, NULL, NULL,
|
||||
cb_decompressed, (void*)(uintptr_t)
|
||||
msg_hash_calculate(msg_hash_to_str(transf->enum_idx)),
|
||||
frontend_userdata))
|
||||
decompress_task = (retro_task_t*)task_push_decompress(
|
||||
output_path, dir_path,
|
||||
NULL, NULL, NULL,
|
||||
cb_decompressed, (void*)(uintptr_t)
|
||||
msg_hash_calculate(msg_hash_to_str(transf->enum_idx)),
|
||||
frontend_userdata, false);
|
||||
|
||||
if (!decompress_task)
|
||||
{
|
||||
err = msg_hash_to_str(MSG_DECOMPRESSION_FAILED);
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
#else
|
||||
switch (transf->enum_idx)
|
||||
{
|
||||
case MENU_ENUM_LABEL_CB_CORE_UPDATER_DOWNLOAD:
|
||||
generic_action_ok_command(CMD_EVENT_CORE_INFO_INIT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
finish:
|
||||
@ -4249,6 +4259,36 @@ static int action_ok_core_content_download(const char *path,
|
||||
MENU_ENUM_LABEL_CB_CORE_CONTENT_DOWNLOAD);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if (!core_list)
|
||||
return menu_cbs_exit();
|
||||
|
||||
task_push_core_updater_download(
|
||||
core_list, path, false, true);
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int action_ok_update_installed_cores(const char *path,
|
||||
const char *label, unsigned type, size_t idx, size_t entry_idx)
|
||||
{
|
||||
#ifdef HAVE_NETWORKING
|
||||
/* Ensure networking is initialised */
|
||||
generic_action_ok_command(CMD_EVENT_NETWORK_INIT);
|
||||
|
||||
/* Push update task */
|
||||
task_push_update_installed_cores();
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define default_action_ok_download(funcname, _id) \
|
||||
static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \
|
||||
{ \
|
||||
@ -4258,7 +4298,6 @@ static int (funcname)(const char *path, const char *label, unsigned type, size_t
|
||||
default_action_ok_download(action_ok_core_content_thumbnails, MENU_ENUM_LABEL_CB_CORE_THUMBNAILS_DOWNLOAD)
|
||||
default_action_ok_download(action_ok_thumbnails_updater_download, MENU_ENUM_LABEL_CB_THUMBNAILS_UPDATER_DOWNLOAD)
|
||||
default_action_ok_download(action_ok_download_url, MENU_ENUM_LABEL_CB_DOWNLOAD_URL)
|
||||
default_action_ok_download(action_ok_core_updater_download, MENU_ENUM_LABEL_CB_CORE_UPDATER_DOWNLOAD)
|
||||
default_action_ok_download(action_ok_lakka_download, MENU_ENUM_LABEL_CB_LAKKA_DOWNLOAD)
|
||||
default_action_ok_download(action_ok_update_assets, MENU_ENUM_LABEL_CB_UPDATE_ASSETS)
|
||||
default_action_ok_download(action_ok_update_core_info_files, MENU_ENUM_LABEL_CB_UPDATE_CORE_INFO_FILES)
|
||||
@ -6506,6 +6545,9 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
|
||||
case MENU_ENUM_LABEL_CORE_UPDATER_LIST:
|
||||
BIND_ACTION_OK(cbs, action_ok_core_updater_list);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES:
|
||||
BIND_ACTION_OK(cbs, action_ok_update_installed_cores);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_THUMBNAILS_UPDATER_LIST:
|
||||
BIND_ACTION_OK(cbs, action_ok_thumbnails_updater_list);
|
||||
break;
|
||||
|
@ -290,6 +290,7 @@ default_sublabel_macro(action_bind_sublabel_video_post_filter_record, MENU_
|
||||
default_sublabel_macro(action_bind_sublabel_start_core, MENU_ENUM_SUBLABEL_START_CORE)
|
||||
default_sublabel_macro(action_bind_sublabel_core_list, MENU_ENUM_SUBLABEL_CORE_LIST)
|
||||
default_sublabel_macro(action_bind_sublabel_download_core, MENU_ENUM_SUBLABEL_DOWNLOAD_CORE)
|
||||
default_sublabel_macro(action_bind_sublabel_update_installed_cores, MENU_ENUM_SUBLABEL_UPDATE_INSTALLED_CORES)
|
||||
default_sublabel_macro(action_bind_sublabel_sideload_core_list, MENU_ENUM_SUBLABEL_SIDELOAD_CORE_LIST)
|
||||
default_sublabel_macro(action_bind_sublabel_load_disc, MENU_ENUM_SUBLABEL_LOAD_DISC)
|
||||
default_sublabel_macro(action_bind_sublabel_dump_disc, MENU_ENUM_SUBLABEL_DUMP_DISC)
|
||||
@ -2423,6 +2424,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
||||
case MENU_ENUM_LABEL_CORE_UPDATER_LIST:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_download_core);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_update_installed_cores);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_VIDEO_POST_FILTER_RECORD:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_post_filter_record);
|
||||
break;
|
||||
|
@ -7615,6 +7615,7 @@ static void materialui_list_insert(
|
||||
else if (
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_ONLINE_UPDATER)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_CORE_INFO_FILES)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_AUTOCONFIG_PROFILES)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_ASSETS)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_CHEATS)) ||
|
||||
|
@ -112,6 +112,7 @@ menu_texture_item ozone_entries_icon_get_texture(ozone_handle_t *ozone,
|
||||
case MENU_ENUM_LABEL_SIDELOAD_CORE_LIST:
|
||||
case MENU_ENUM_LABEL_CORE_SETTINGS:
|
||||
case MENU_ENUM_LABEL_CORE_UPDATER_LIST:
|
||||
case MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES:
|
||||
case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_CORE:
|
||||
case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CORE:
|
||||
case MENU_ENUM_LABEL_REMAP_FILE_SAVE_CORE:
|
||||
|
@ -2388,6 +2388,7 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb,
|
||||
case MENU_ENUM_LABEL_SIDELOAD_CORE_LIST:
|
||||
case MENU_ENUM_LABEL_CORE_SETTINGS:
|
||||
case MENU_ENUM_LABEL_CORE_UPDATER_LIST:
|
||||
case MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES:
|
||||
case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_CORE:
|
||||
case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CORE:
|
||||
case MENU_ENUM_LABEL_REMAP_FILE_SAVE_CORE:
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include <net/net_http_parse.h>
|
||||
#include "../../network/netplay/netplay.h"
|
||||
#include "../network/netplay/netplay_discovery.h"
|
||||
#include "../core_updater_list.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LAKKA_SWITCH
|
||||
@ -7922,9 +7923,35 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
case DISPLAYLIST_CORES_UPDATER:
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
#ifdef HAVE_NETWORKING
|
||||
count = print_buf_lines(info->list, menu->core_buf, "",
|
||||
(int)menu->core_len, FILE_TYPE_DOWNLOAD_CORE, true, true);
|
||||
{
|
||||
core_updater_list_t *core_list = core_updater_list_get_cached();
|
||||
|
||||
if (core_list)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < core_updater_list_size(core_list); i++)
|
||||
{
|
||||
const core_updater_list_entry_t *entry = NULL;
|
||||
|
||||
if (core_updater_list_get_index(core_list, i, &entry))
|
||||
{
|
||||
if (menu_entries_append_enum(info->list,
|
||||
entry->remote_filename,
|
||||
"",
|
||||
MENU_ENUM_LABEL_URL_ENTRY,
|
||||
FILE_TYPE_DOWNLOAD_CORE, 0, 0))
|
||||
{
|
||||
file_list_set_alt_at_offset(
|
||||
info->list, i, entry->display_name);
|
||||
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (count == 0)
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY),
|
||||
@ -7935,7 +7962,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
info->need_push = true;
|
||||
info->need_refresh = true;
|
||||
info->need_clear = true;
|
||||
#endif
|
||||
|
||||
break;
|
||||
case DISPLAYLIST_THUMBNAILS_UPDATER:
|
||||
#ifdef HAVE_NETWORKING
|
||||
@ -8806,6 +8833,13 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
MENU_ENUM_LABEL_CORE_UPDATER_LIST,
|
||||
MENU_SETTING_ACTION, 0, 0))
|
||||
count++;
|
||||
|
||||
if (menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_UPDATE_INSTALLED_CORES),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES),
|
||||
MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES,
|
||||
MENU_SETTING_ACTION, 0, 0))
|
||||
count++;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
@ -67,6 +67,7 @@
|
||||
#include "../ui/ui_companion_driver.h"
|
||||
#include "../verbosity.h"
|
||||
#include "../tasks/task_powerstate.h"
|
||||
#include "../core_updater_list.h"
|
||||
|
||||
#define SCROLL_INDEX_SIZE (2 * (26 + 2) + 1)
|
||||
|
||||
@ -3277,7 +3278,7 @@ static bool menu_init(menu_handle_t *menu_data)
|
||||
task_push_decompress(settings->arrays.bundle_assets_src,
|
||||
settings->arrays.bundle_assets_dst,
|
||||
NULL, settings->arrays.bundle_assets_dst_subdir,
|
||||
NULL, bundle_decompressed, NULL, NULL);
|
||||
NULL, bundle_decompressed, NULL, NULL, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -3596,6 +3597,7 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data)
|
||||
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
|
||||
menu_shader_manager_free();
|
||||
#endif
|
||||
core_updater_list_free_cached();
|
||||
|
||||
if (menu_driver_data)
|
||||
{
|
||||
|
@ -47,7 +47,7 @@ unsigned print_buf_lines(file_list_t *list, char *buf,
|
||||
{
|
||||
char c;
|
||||
unsigned count = 0;
|
||||
int i, j = 0;
|
||||
int i = 0;
|
||||
char *line_start = buf;
|
||||
|
||||
if (!buf || !buf_size)
|
||||
@ -123,55 +123,6 @@ unsigned print_buf_lines(file_list_t *list, char *buf,
|
||||
}
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case FILE_TYPE_DOWNLOAD_CORE:
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
if (settings)
|
||||
{
|
||||
char display_name[255];
|
||||
char core_path[PATH_MAX_LENGTH];
|
||||
char *last = NULL;
|
||||
|
||||
display_name[0] = core_path[0] = '\0';
|
||||
|
||||
fill_pathname_join_noext(
|
||||
core_path,
|
||||
settings->paths.path_libretro_info,
|
||||
(extended && !string_is_empty(core_pathname))
|
||||
? core_pathname : line_start,
|
||||
sizeof(core_path));
|
||||
path_remove_extension(core_path);
|
||||
|
||||
last = (char*)strrchr(core_path, '_');
|
||||
|
||||
if (!string_is_empty(last))
|
||||
{
|
||||
if (string_is_not_equal_fast(last, "_libretro", 9))
|
||||
*last = '\0';
|
||||
}
|
||||
|
||||
strlcat(core_path,
|
||||
file_path_str(FILE_PATH_CORE_INFO_EXTENSION),
|
||||
sizeof(core_path));
|
||||
|
||||
if (
|
||||
path_is_valid(core_path)
|
||||
&& core_info_get_display_name(
|
||||
core_path, display_name, sizeof(display_name)))
|
||||
file_list_set_alt_at_offset(list, j, display_name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case FILE_TYPE_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
j++;
|
||||
|
||||
string_list_free(str_list);
|
||||
|
||||
/* Restore the saved char */
|
||||
|
@ -384,6 +384,7 @@ void menu_widgets_msg_queue_push(
|
||||
if (task)
|
||||
{
|
||||
msg_widget->msg = strdup(title);
|
||||
msg_widget->msg_new = strdup(title);
|
||||
msg_widget->msg_len = (unsigned)strlen(title);
|
||||
|
||||
msg_widget->task_error = task->error;
|
||||
@ -449,7 +450,7 @@ void menu_widgets_msg_queue_push(
|
||||
msg_widget->expiration_timer_started = false;
|
||||
}
|
||||
|
||||
if (task->title != msg_widget->task_title_ptr)
|
||||
if (!string_is_equal(task->title, msg_widget->msg_new))
|
||||
{
|
||||
unsigned len = (unsigned)strlen(task->title);
|
||||
unsigned new_width = font_driver_get_message_width(font_regular, task->title, len, msg_queue_text_scale_factor);
|
||||
@ -609,6 +610,9 @@ static void menu_widgets_msg_queue_free(menu_widget_msg_t *msg, bool touch_list)
|
||||
if (msg->msg)
|
||||
free(msg->msg);
|
||||
|
||||
if (msg->msg_new)
|
||||
free(msg->msg_new);
|
||||
|
||||
/* Remove it from the list */
|
||||
if (touch_list)
|
||||
{
|
||||
|
15
msg_hash.h
15
msg_hash.h
@ -1882,6 +1882,21 @@ enum msg_hash_enums
|
||||
MENU_LABEL(DISC_INFORMATION),
|
||||
MENU_LABEL(CORE_DELETE),
|
||||
|
||||
/* Core updater */
|
||||
MENU_LABEL(UPDATE_INSTALLED_CORES),
|
||||
|
||||
MSG_FETCHING_CORE_LIST,
|
||||
MSG_CORE_LIST_FAILED,
|
||||
MSG_LATEST_CORE_INSTALLED,
|
||||
MSG_UPDATING_CORE,
|
||||
MSG_DOWNLOADING_CORE,
|
||||
MSG_EXTRACTING_CORE,
|
||||
MSG_CORE_INSTALLED,
|
||||
MSG_SCANNING_CORES,
|
||||
MSG_CHECKING_CORE,
|
||||
MSG_ALL_CORES_UPDATED,
|
||||
|
||||
|
||||
MENU_LABEL(VIDEO_SHADER_PARAMETERS),
|
||||
MENU_LABEL(VIDEO_SHADER_PRESET_PARAMETERS),
|
||||
MENU_LABEL(DISK_OPTIONS),
|
||||
|
1199
tasks/task_core_updater.c
Normal file
1199
tasks/task_core_updater.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -261,7 +261,7 @@ bool task_check_decompress(const char *source_file)
|
||||
return task_queue_find(&find_data);
|
||||
}
|
||||
|
||||
bool task_push_decompress(
|
||||
void *task_push_decompress(
|
||||
const char *source_file,
|
||||
const char *target_dir,
|
||||
const char *target_file,
|
||||
@ -269,7 +269,8 @@ bool task_push_decompress(
|
||||
const char *valid_ext,
|
||||
retro_task_callback_t cb,
|
||||
void *user_data,
|
||||
void *frontend_userdata)
|
||||
void *frontend_userdata,
|
||||
bool mute)
|
||||
{
|
||||
char tmp[PATH_MAX_LENGTH];
|
||||
const char *ext = NULL;
|
||||
@ -283,7 +284,7 @@ bool task_push_decompress(
|
||||
RARCH_WARN(
|
||||
"[decompress] Empty or null source file or"
|
||||
" target directory arguments.\n");
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ext = path_get_extension(source_file);
|
||||
@ -304,7 +305,7 @@ bool task_push_decompress(
|
||||
"[decompress] File '%s' does not exist"
|
||||
" or is not a compressed file.\n",
|
||||
source_file);
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!valid_ext || !valid_ext[0])
|
||||
@ -315,7 +316,7 @@ bool task_push_decompress(
|
||||
RARCH_LOG(
|
||||
"[decompress] File '%s' already being decompressed.\n",
|
||||
source_file);
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RARCH_LOG("[decompress] File '%s.\n", source_file);
|
||||
@ -323,14 +324,14 @@ bool task_push_decompress(
|
||||
s = (decompress_state_t*)calloc(1, sizeof(*s));
|
||||
|
||||
if (!s)
|
||||
return false;
|
||||
return NULL;
|
||||
|
||||
t = (retro_task_t*)calloc(1, sizeof(*t));
|
||||
|
||||
if (!t)
|
||||
{
|
||||
free(s);
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->source_file = strdup(source_file);
|
||||
@ -365,8 +366,9 @@ bool task_push_decompress(
|
||||
path_basename(source_file));
|
||||
|
||||
t->title = strdup(tmp);
|
||||
t->mute = mute;
|
||||
|
||||
task_queue_push(t);
|
||||
|
||||
return true;
|
||||
return t;
|
||||
}
|
||||
|
@ -74,6 +74,7 @@ typedef struct
|
||||
{
|
||||
enum msg_hash_enums enum_idx;
|
||||
char path[PATH_MAX_LENGTH];
|
||||
void *user_data;
|
||||
} file_transfer_t;
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
@ -28,6 +28,10 @@
|
||||
#include "../config.h"
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_NETWORKING)
|
||||
#include "../core_updater_list.h"
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_NETWORKING) && defined(HAVE_MENU)
|
||||
/* Required for task_push_pl_entry_thumbnail_download() */
|
||||
#include "../playlist.h"
|
||||
@ -71,6 +75,13 @@ 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);
|
||||
void *task_push_core_updater_download(
|
||||
core_updater_list_t* core_list, const char *filename, bool mute, bool check_crc);
|
||||
void task_push_update_installed_cores(void);
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
bool task_push_pl_thumbnail_download(const char *system, const char *playlist_path);
|
||||
bool task_push_pl_entry_thumbnail_download(
|
||||
@ -113,7 +124,7 @@ bool task_push_overlay_load_default(
|
||||
|
||||
bool task_check_decompress(const char *source_file);
|
||||
|
||||
bool task_push_decompress(
|
||||
void *task_push_decompress(
|
||||
const char *source_file,
|
||||
const char *target_dir,
|
||||
const char *target_file,
|
||||
@ -121,7 +132,8 @@ bool task_push_decompress(
|
||||
const char *valid_ext,
|
||||
retro_task_callback_t cb,
|
||||
void *user_data,
|
||||
void *frontend_userdata);
|
||||
void *frontend_userdata,
|
||||
bool mute);
|
||||
|
||||
void task_file_load_handler(retro_task_t *task);
|
||||
|
||||
|
@ -3023,6 +3023,7 @@ int MainWindow::onExtractArchive(QString path, QString extractionDir, QString te
|
||||
const char *dir = dirArray.constData();
|
||||
struct string_list *file_list = file_archive_get_file_list(file, NULL);
|
||||
bool returnerr = true;
|
||||
retro_task_t *decompress_task = NULL;
|
||||
|
||||
if (!file_list || file_list->size == 0)
|
||||
{
|
||||
@ -3079,9 +3080,12 @@ int MainWindow::onExtractArchive(QString path, QString extractionDir, QString te
|
||||
m_updateProgressDialog->setCancelButtonText(QString());
|
||||
m_updateProgressDialog->show();
|
||||
|
||||
if (!task_push_decompress(file, dir,
|
||||
NULL, NULL, NULL,
|
||||
cb, this, NULL))
|
||||
decompress_task = (retro_task_t*)task_push_decompress(
|
||||
file, dir,
|
||||
NULL, NULL, NULL,
|
||||
cb, this, NULL, false);
|
||||
|
||||
if (!decompress_task)
|
||||
{
|
||||
m_updateProgressDialog->cancel();
|
||||
return -1;
|
||||
|
Loading…
Reference in New Issue
Block a user