Merge branch 'master' into deferred-push-refactor

This commit is contained in:
Twinaphex 2019-12-01 23:22:18 +01:00 committed by GitHub
commit e57e4dbf57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 2544 additions and 19 deletions

@ -163,6 +163,7 @@ OBJ += frontend/frontend_driver.o \
tasks/task_file_transfer.o \
tasks/task_image.o \
tasks/task_playlist_manager.o \
tasks/task_manual_content_scan.o \
$(LIBRETRO_COMM_DIR)/encodings/encoding_utf.o \
$(LIBRETRO_COMM_DIR)/encodings/encoding_crc32.o \
$(LIBRETRO_COMM_DIR)/encodings/encoding_base64.o \
@ -258,8 +259,8 @@ OBJ += \
performance_counters.o \
verbosity.o \
midi/drivers/null_midi.o \
$(LIBRETRO_COMM_DIR)/playlists/label_sanitization.o
$(LIBRETRO_COMM_DIR)/playlists/label_sanitization.o \
manual_content_scan.o
ifeq ($(HAVE_AUDIOMIXER), 1)
DEFINES += -DHAVE_AUDIOMIXER

@ -79,6 +79,7 @@ enum file_path_enum
FILE_PATH_LPL_EXTENSION,
FILE_PATH_LPL_EXTENSION_NO_DOT,
FILE_PATH_RDB_EXTENSION,
FILE_PATH_RDB_EXTENSION_NO_DOT,
FILE_PATH_BSV_EXTENSION,
FILE_PATH_AUTO_EXTENSION,
FILE_PATH_ZIP_EXTENSION,

@ -130,6 +130,9 @@ const char *file_path_str(enum file_path_enum enum_idx)
case FILE_PATH_RDB_EXTENSION:
str = ".rdb";
break;
case FILE_PATH_RDB_EXTENSION_NO_DOT:
str = "rdb";
break;
case FILE_PATH_ZIP_EXTENSION:
str = ".zip";
break;

@ -1205,6 +1205,7 @@ DATA RUNLOOP
#include "../tasks/task_image.c"
#include "../tasks/task_file_transfer.c"
#include "../tasks/task_playlist_manager.c"
#include "../tasks/task_manual_content_scan.c"
#ifdef HAVE_ZLIB
#include "../tasks/task_decompress.c"
#endif
@ -1634,3 +1635,8 @@ SSL
PLAYLIST NAME SANITIZATION
============================================================ */
#include "../libretro-common/playlists/label_sanitization.c"
/*============================================================
MANUAL CONTENT SCAN
============================================================ */
#include "../manual_content_scan.c"

@ -2105,3 +2105,25 @@ MSG_HASH(MENU_ENUM_LABEL_DRIVER_SWITCH_ENABLE,
"driver_switch_enable")
MSG_HASH(MENU_ENUM_LABEL_AI_SERVICE_PAUSE,
"ai_service_pause")
MSG_HASH(MENU_ENUM_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST,
"deferred_manual_content_scan_list")
MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST,
"manual_content_scan_list")
MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DIR,
"manual_content_scan_dir")
MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME,
"manual_content_scan_system_name")
MSG_HASH(MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME,
"deferred_dropdown_box_list_manual_content_scan_system_name")
MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM,
"manual_content_scan_system_name_custom")
MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME,
"manual_content_scan_core_name")
MSG_HASH(MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_CORE_NAME,
"deferred_dropdown_box_list_manual_content_scan_core_name")
MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_FILE_EXTS,
"manual_content_scan_file_exts")
MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_OVERWRITE,
"manual_content_scan_overwrite")
MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_START,
"manual_content_scan_start")

@ -10040,3 +10040,99 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_AI_SERVICE_PAUSE,
"Pauses core while screen is translated."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_LIST,
"Manual Scan"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_LIST,
"Configurable scan based on content file names. Does not require content to match the database."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_DIR,
"Content Directory"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_DIR,
"Selects a directory to scan for content."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME,
"System Name"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME,
"Specify a 'system name' with which to associate scanned content. Used to name to the generated playlist file and to identify playlist thumbnails."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM,
"Custom System Name"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM,
"Manually specify a 'system name' for scanned content. Only used when 'System Name' is set to '<Custom>'."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_CORE_NAME,
"Core"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_CORE_NAME,
"Select a default core to use when launching scanned content."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_FILE_EXTS,
"File Extensions"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_FILE_EXTS,
"Space-delimited list of file types to include in the scan. If empty, includes all files - or if a core is specified, all files supported by the core."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_OVERWRITE,
"Overwrite Existing Playlist"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_OVERWRITE,
"When enabled, any existing playlist will be deleted before scanning content. When disabled, existing playlist entries are preserved and only content currently missing from the playlist will be added."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_START,
"Start Scan"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_START,
"Scan selected content."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CONTENT_DIR,
"<Content Directory>"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CUSTOM,
"<Custom>"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_CORE_NAME_DETECT,
"<Unspecified>"
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_INVALID_CONFIG,
"Invalid manual scan configuration"
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_INVALID_CONTENT,
"No valid content detected"
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_START,
"Scanning content: "
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_IN_PROGRESS,
"Scanning: "
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_END,
"Scan complete: "
)

@ -133,6 +133,10 @@ char* string_tokenize(char **str, const char *delim);
/* Removes every instance of character 'c' from 'str' */
void string_remove_all_chars(char *str, char c);
/* Replaces every instance of character 'find' in 'str'
* with character 'replace' */
void string_replace_all_chars(char *str, char find, char replace);
/* Converts string to unsigned integer.
* Returns 0 if string is invalid */
unsigned string_to_unsigned(const char *str);

@ -319,6 +319,19 @@ void string_remove_all_chars(char *str, char c)
*write_ptr = '\0';
}
/* Replaces every instance of character 'find' in 'str'
* with character 'replace' */
void string_replace_all_chars(char *str, char find, char replace)
{
char *str_ptr = str;
if (string_is_empty(str))
return;
while((str_ptr = strchr(str_ptr, find)) != NULL)
*str_ptr++ = replace;
}
/* Converts string to unsigned integer.
* Returns 0 if string is invalid */
unsigned string_to_unsigned(const char *str)

875
manual_content_scan.c Normal file

@ -0,0 +1,875 @@
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (manual_content_scan.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/dir_list.h>
#include <retro_miscellaneous.h>
#include "configuration.h"
#include "msg_hash.h"
#include "list_special.h"
#include "core_info.h"
#include "file_path_special.h"
#include "manual_content_scan.h"
/* Holds all configuration parameters associated
* with a manual content scan */
typedef struct
{
char content_dir[PATH_MAX_LENGTH];
char system_name_content_dir[PATH_MAX_LENGTH];
char system_name_database[PATH_MAX_LENGTH];
char system_name_custom[PATH_MAX_LENGTH];
char core_name[PATH_MAX_LENGTH];
char core_path[PATH_MAX_LENGTH];
char file_exts_core[PATH_MAX_LENGTH];
char file_exts_custom[PATH_MAX_LENGTH];
enum manual_content_scan_system_name_type system_name_type;
enum manual_content_scan_core_type core_type;
bool overwrite_playlist;
} scan_settings_t;
/* Static settings object
* > Provides easy access to settings parameters
* when creating associated menu entries
* > We are handling this in almost exactly the same
* way as the regular global 'static settings_t *configuration_settings;'
* object in retroarch.c. This means it is not inherently thread safe,
* but this should not be an issue (i.e. regular configuration_settings
* are not thread safe, but we only access them when pushing a
* task, not in the task thread itself, so all is well) */
static scan_settings_t scan_settings = {
"", /* content_dir */
"", /* system_name_content_dir */
"", /* system_name_database */
"", /* system_name_custom */
"", /* core_name */
"", /* core_path */
"", /* file_exts_core */
"", /* file_exts_custom */
MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR, /* system_name_type */
MANUAL_CONTENT_SCAN_CORE_DETECT, /* core_type */
false /* overwrite_playlist */
};
/*****************/
/* Configuration */
/*****************/
/* Pointer access */
/* Returns a pointer to the internal
* 'system_name_custom' string */
char *manual_content_scan_get_system_name_custom_ptr(void)
{
return scan_settings.system_name_custom;
}
/* Returns size of the internal
* 'system_name_custom' string */
size_t manual_content_scan_get_system_name_custom_size(void)
{
return sizeof(scan_settings.system_name_custom);
}
/* Returns a pointer to the internal
* 'file_exts_custom' string */
char *manual_content_scan_get_file_exts_custom_ptr(void)
{
return scan_settings.file_exts_custom;
}
/* Returns size of the internal
* 'file_exts_custom' string */
size_t manual_content_scan_get_file_exts_custom_size(void)
{
return sizeof(scan_settings.file_exts_custom);
}
/* Returns a pointer to the internal
* 'overwrite_playlist' bool */
bool *manual_content_scan_get_overwrite_playlist_ptr(void)
{
return &scan_settings.overwrite_playlist;
}
/* Sanitisation */
/* Sanitises file extensions list string:
* > Removes period (full stop) characters
* > Converts to lower case
* > Trims leading/trailing whitespace */
static void manual_content_scan_scrub_file_exts(char *file_exts)
{
if (string_is_empty(file_exts))
return;
string_remove_all_chars(file_exts, '.');
string_to_lower(file_exts);
string_trim_whitespace(file_exts);
}
/* Removes invalid characters from
* 'system_name_custom' string */
void manual_content_scan_scrub_system_name_custom(void)
{
char *scrub_char_pointer = NULL;
if (string_is_empty(scan_settings.system_name_custom))
return;
/* Scrub characters that are not cross-platform
* and/or violate the No-Intro filename standard:
* http://datomatic.no-intro.org/stuff/The%20Official%20No-Intro%20Convention%20(20071030).zip
* Replace these characters with underscores */
while((scrub_char_pointer = strpbrk(scan_settings.system_name_custom, "&*/:`\"<>?\\|")))
*scrub_char_pointer = '_';
}
/* Removes period (full stop) characters from
* 'file_exts_custom' string and converts to
* lower case */
void manual_content_scan_scrub_file_exts_custom(void)
{
manual_content_scan_scrub_file_exts(scan_settings.file_exts_custom);
}
/* Menu setters */
/* Sets content directory for next manual scan
* operation.
* Returns true if content directory is valid. */
bool manual_content_scan_set_menu_content_dir(const char *content_dir)
{
const char *dir_name = NULL;
size_t len;
/* Sanity check */
if (string_is_empty(content_dir))
goto error;
if (!path_is_directory(content_dir))
goto error;
/* Copy directory path to settings struct */
strlcpy(
scan_settings.content_dir,
content_dir,
sizeof(scan_settings.content_dir));
/* Remove trailing slash, if required */
len = strlen(scan_settings.content_dir);
if (len > 0)
{
if (scan_settings.content_dir[len - 1] == path_default_slash_c())
scan_settings.content_dir[len - 1] = '\0';
}
else
goto error;
/* Handle case where path was a single slash... */
if (string_is_empty(scan_settings.content_dir))
goto error;
/* Get directory name (used as system name
* when scan_settings.system_name_type ==
* MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR) */
dir_name = path_basename(scan_settings.content_dir);
if (string_is_empty(dir_name))
goto error;
/* Copy directory name to settings struct */
strlcpy(
scan_settings.system_name_content_dir,
dir_name,
sizeof(scan_settings.system_name_content_dir));
return true;
error:
/* Directory is invalid - reset internal
* content directory and associated 'directory'
* system name */
scan_settings.content_dir[0] = '\0';
scan_settings.system_name_content_dir[0] = '\0';
return false;
}
/* Sets system name for the next manual scan
* operation.
* Returns true if system name is valid.
* NOTE:
* > Only sets 'system_name_type' and 'system_name_database'
* > 'system_name_content_dir' and 'system_name_custom' are
* (by necessity) handled elsewhere
* > This may look fishy, but it's not - it's a menu-specific
* function, and this is simply the cleanest way to handle
* the setting... */
bool manual_content_scan_set_menu_system_name(
enum manual_content_scan_system_name_type system_name_type,
const char *system_name)
{
/* Sanity check */
if (system_name_type > MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE)
goto error;
/* Cache system name 'type' */
scan_settings.system_name_type = system_name_type;
/* Check if we are using a non-database name */
if ((scan_settings.system_name_type == MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR) ||
(scan_settings.system_name_type == MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM))
scan_settings.system_name_database[0] = '\0';
else
{
/* We are using a database name... */
if (string_is_empty(system_name))
goto error;
/* Copy database name to settings struct */
strlcpy(
scan_settings.system_name_database,
system_name,
sizeof(scan_settings.system_name_database));
}
return true;
error:
/* Input parameters are invalid - reset internal
* 'system_name_type' and 'system_name_database' */
scan_settings.system_name_type = MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR;
scan_settings.system_name_database[0] = '\0';
return false;
}
/* Sets core name for the next manual scan
* operation (+ core path and other associated
* parameters).
* Returns true if core name is valid. */
bool manual_content_scan_set_menu_core_name(
enum manual_content_scan_core_type core_type,
const char *core_name)
{
/* Sanity check */
if (core_type > MANUAL_CONTENT_SCAN_CORE_SET)
goto error;
/* Cache core 'type' */
scan_settings.core_type = core_type;
/* Check if we are using core autodetection */
if (scan_settings.core_type == MANUAL_CONTENT_SCAN_CORE_DETECT)
{
scan_settings.core_name[0] = '\0';
scan_settings.core_path[0] = '\0';
scan_settings.file_exts_core[0] = '\0';
}
else
{
core_info_list_t *core_info_list = NULL;
core_info_t *core_info = NULL;
bool core_found = false;
size_t i;
/* We are using a manually set core... */
if (string_is_empty(core_name))
goto error;
/* Get core list */
core_info_get_list(&core_info_list);
if (!core_info_list)
goto error;
/* Search for the specified core name */
for (i = 0; i < core_info_list->count; i++)
{
core_info = NULL;
core_info = core_info_get(core_info_list, i);
if (core_info)
{
if (string_is_equal(core_info->display_name, core_name))
{
/* Core has been found */
core_found = true;
/* Copy core path to settings struct */
if (string_is_empty(core_info->path))
goto error;
strlcpy(
scan_settings.core_path,
core_info->path,
sizeof(scan_settings.core_path));
/* Copy core name to settings struct */
strlcpy(
scan_settings.core_name,
core_info->display_name,
sizeof(scan_settings.core_name));
/* Copy supported extensions to settings
* struct, if required */
if (!string_is_empty(core_info->supported_extensions))
{
strlcpy(
scan_settings.file_exts_core,
core_info->supported_extensions,
sizeof(scan_settings.file_exts_core));
/* Core info extensions are delimited by
* vertical bars. For internal consistency,
* replace them with spaces */
string_replace_all_chars(scan_settings.file_exts_core, '|', ' ');
/* Apply standard scrubbing/clean-up
* (should not be required, but must handle the
* case where a core info file is incorrectly
* formatted) */
manual_content_scan_scrub_file_exts(scan_settings.file_exts_core);
}
else
scan_settings.file_exts_core[0] = '\0';
break;
}
}
}
/* Sanity check */
if (!core_found)
goto error;
}
return true;
error:
/* Input parameters are invalid - reset internal
* core values */
scan_settings.core_type = MANUAL_CONTENT_SCAN_CORE_DETECT;
scan_settings.core_name[0] = '\0';
scan_settings.core_path[0] = '\0';
scan_settings.file_exts_core[0] = '\0';
return false;
}
/* Menu getters */
/* Fetches content directory for next manual scan
* operation.
* Returns true if content directory is valid. */
bool manual_content_scan_get_menu_content_dir(const char **content_dir)
{
if (!content_dir)
return false;
if (string_is_empty(scan_settings.content_dir))
return false;
*content_dir = scan_settings.content_dir;
return true;
}
/* Fetches system name for the next manual scan operation.
* Returns true if system name is valid.
* NOTE: This corresponds to the 'System Name' value
* displayed in menus - this is not identical to the
* actual system name used when generating the playlist */
bool manual_content_scan_get_menu_system_name(const char **system_name)
{
if (!system_name)
return false;
switch (scan_settings.system_name_type)
{
case MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR:
*system_name = msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CONTENT_DIR);
return true;
case MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM:
*system_name = msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CUSTOM);
return true;
case MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE:
if (string_is_empty(scan_settings.system_name_database))
return false;
else
{
*system_name = scan_settings.system_name_database;
return true;
}
default:
break;
}
return false;
}
/* Fetches core name for the next manual scan operation.
* Returns true if core name is valid. */
bool manual_content_scan_get_menu_core_name(const char **core_name)
{
if (!core_name)
return false;
switch (scan_settings.core_type)
{
case MANUAL_CONTENT_SCAN_CORE_DETECT:
*core_name = msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_CORE_NAME_DETECT);
return true;
case MANUAL_CONTENT_SCAN_CORE_SET:
if (string_is_empty(scan_settings.core_name))
return false;
else
{
*core_name = scan_settings.core_name;
return true;
}
default:
break;
}
return false;
}
/* Menu utility functions */
/* Creates a list of all possible 'system name' menu
* strings, for use in 'menu_displaylist' drop-down
* lists and 'menu_cbs_left/right'
* > Returns NULL in the event of failure
* > Returned string list must be free()'d */
struct string_list *manual_content_scan_get_menu_system_name_list(void)
{
settings_t *settings = config_get_ptr();
struct string_list *name_list = string_list_new();
union string_list_elem_attr attr;
/* Sanity check */
if (!name_list)
goto error;
attr.i = 0;
/* Add 'use content directory' entry */
if (!string_list_append(name_list, msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CONTENT_DIR), attr))
goto error;
/* Add 'use custom' entry */
if (!string_list_append(name_list, msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CUSTOM), attr))
goto error;
#ifdef HAVE_LIBRETRODB
/* If platform has database support, get names
* of all installed database files */
if (settings)
{
/* Note: dir_list_new_special() is well behaved - the
* returned string list will only include database
* files (i.e. don't have to check for directories,
* or verify file extensions) */
struct string_list *rdb_list = dir_list_new_special(
settings->paths.path_content_database,
DIR_LIST_DATABASES, NULL);
if (rdb_list && rdb_list->size)
{
unsigned i;
/* Ensure database list is in alphabetical order */
dir_list_sort(rdb_list, true);
/* Loop over database files */
for (i = 0; i < rdb_list->size; i++)
{
const char *rdb_path = rdb_list->elems[i].data;
const char *rdb_file = NULL;
char rdb_name[PATH_MAX_LENGTH];
rdb_name[0] = '\0';
/* Sanity check */
if (string_is_empty(rdb_path))
continue;
rdb_file = path_basename(rdb_path);
if (string_is_empty(rdb_file))
continue;
/* Remove file extension */
strlcpy(rdb_name, rdb_file, sizeof(rdb_name));
path_remove_extension(rdb_name);
if (string_is_empty(rdb_name))
continue;
/* Add database name to list */
if (!string_list_append(name_list, rdb_name, attr))
goto error;
}
}
/* Clean up */
string_list_free(rdb_list);
}
#endif
return name_list;
error:
if (name_list)
string_list_free(name_list);
return NULL;
}
/* Creates a list of all possible 'core name' menu
* strings, for use in 'menu_displaylist' drop-down
* lists and 'menu_cbs_left/right'
* > Returns NULL in the event of failure
* > Returned string list must be free()'d */
struct string_list *manual_content_scan_get_menu_core_name_list(void)
{
struct string_list *name_list = string_list_new();
core_info_list_t *core_info_list = NULL;
union string_list_elem_attr attr;
/* Sanity check */
if (!name_list)
goto error;
attr.i = 0;
/* Add 'DETECT' entry */
if (!string_list_append(name_list, msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_CORE_NAME_DETECT), attr))
goto error;
/* Get core list */
core_info_get_list(&core_info_list);
if (core_info_list)
{
core_info_t *core_info = NULL;
size_t i;
/* Sort cores alphabetically */
core_info_qsort(core_info_list, CORE_INFO_LIST_SORT_DISPLAY_NAME);
/* Loop through cores */
for (i = 0; i < core_info_list->count; i++)
{
core_info = NULL;
core_info = core_info_get(core_info_list, i);
if (core_info)
{
if (string_is_empty(core_info->display_name))
continue;
/* Add core name to list */
if (!string_list_append(name_list, core_info->display_name, attr))
goto error;
}
}
}
return name_list;
error:
if (name_list)
string_list_free(name_list);
return NULL;
}
/****************/
/* Task Helpers */
/****************/
/* Parses current manual content scan settings,
* and extracts all information required to configure
* a manual content scan task.
* Returns false if current settings are invalid. */
bool manual_content_scan_get_task_config(manual_content_scan_task_config_t *task_config)
{
settings_t *settings = config_get_ptr();
if (!task_config || !settings)
return false;
/* Ensure all 'task_config' strings are
* correctly initialised */
task_config->playlist_file[0] = '\0';
task_config->content_dir[0] = '\0';
task_config->system_name[0] = '\0';
task_config->database_name[0] = '\0';
task_config->core_name[0] = '\0';
task_config->core_path[0] = '\0';
task_config->file_exts[0] = '\0';
/* Get content directory */
if (string_is_empty(scan_settings.content_dir))
return false;
if (!path_is_directory(scan_settings.content_dir))
return false;
strlcpy(
task_config->content_dir,
scan_settings.content_dir,
sizeof(task_config->content_dir));
/* Get system name */
switch (scan_settings.system_name_type)
{
case MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR:
if (string_is_empty(scan_settings.system_name_content_dir))
return false;
strlcpy(
task_config->system_name,
scan_settings.system_name_content_dir,
sizeof(task_config->system_name));
break;
case MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM:
if (string_is_empty(scan_settings.system_name_custom))
return false;
strlcpy(
task_config->system_name,
scan_settings.system_name_custom,
sizeof(task_config->system_name));
break;
case MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE:
if (string_is_empty(scan_settings.system_name_database))
return false;
strlcpy(
task_config->system_name,
scan_settings.system_name_database,
sizeof(task_config->system_name));
break;
default:
return false;
}
/* Now we have a valid system name, can generate
* a 'database' name... */
strlcpy(
task_config->database_name,
task_config->system_name,
sizeof(task_config->database_name));
strlcat(
task_config->database_name,
file_path_str(FILE_PATH_LPL_EXTENSION),
sizeof(task_config->database_name));
/* ...which can in turn be used to generate the
* playlist path */
if (string_is_empty(settings->paths.directory_playlist))
return false;
fill_pathname_join(
task_config->playlist_file,
settings->paths.directory_playlist,
task_config->database_name,
sizeof(task_config->playlist_file));
if (string_is_empty(task_config->playlist_file))
return false;
/* Get core name and path */
switch (scan_settings.core_type)
{
case MANUAL_CONTENT_SCAN_CORE_DETECT:
task_config->core_set = false;
break;
case MANUAL_CONTENT_SCAN_CORE_SET:
task_config->core_set = true;
if (string_is_empty(scan_settings.core_name))
return false;
if (string_is_empty(scan_settings.core_path))
return false;
strlcpy(
task_config->core_name,
scan_settings.core_name,
sizeof(task_config->core_name));
strlcpy(
task_config->core_path,
scan_settings.core_path,
sizeof(task_config->core_path));
break;
default:
return false;
}
/* Get file extensions list
* > Note that compressed files are included by
* default, regardless of extension filter
* (since these can always be handled by the
* frontend) */
task_config->include_compressed_content = true;
if (!string_is_empty(scan_settings.file_exts_custom))
{
strlcpy(
task_config->file_exts,
scan_settings.file_exts_custom,
sizeof(task_config->file_exts));
/* User has explicitly specified which file
* types are allowed - have to exclude compressed
* content when calling dir_list_new() */
task_config->include_compressed_content = false;
}
else if (scan_settings.core_type == MANUAL_CONTENT_SCAN_CORE_SET)
{
if (!string_is_empty(scan_settings.file_exts_core))
strlcpy(
task_config->file_exts,
scan_settings.file_exts_core,
sizeof(task_config->file_exts));
}
/* Our extension lists are space delimited
* > dir_list_new() expects vertical bar
* delimiters, so find and replace */
if (!string_is_empty(task_config->file_exts))
string_replace_all_chars(task_config->file_exts, ' ', '|');
/* Copy 'overwrite playlist' setting */
task_config->overwrite_playlist = scan_settings.overwrite_playlist;
return true;
}
/* Creates a list of all valid content in the specified
* content directory
* > Returns NULL in the event of failure
* > Returned string list must be free()'d */
struct string_list *manual_content_scan_get_content_list(manual_content_scan_task_config_t *task_config)
{
struct string_list *dir_list = NULL;
bool filter_exts;
/* Sanity check */
if (!task_config)
goto error;
if (string_is_empty(task_config->content_dir))
goto error;
/* Get directory listing
* > Exclude directories and hidden files
* > Scan recursively */
dir_list = dir_list_new(
task_config->content_dir,
string_is_empty(task_config->file_exts) ? NULL : task_config->file_exts,
false, /* include_dirs */
false, /* include_hidden */
task_config->include_compressed_content,
true /* recursive */
);
/* Sanity check */
if (!dir_list)
goto error;
if (dir_list->size < 1)
goto error;
/* Ensure list is in alphabetical order
* > Not strictly required, but task status
* messages will be unintuitive if we leave
* the order 'random' */
dir_list_sort(dir_list, true);
return dir_list;
error:
if (dir_list)
string_list_free(dir_list);
return NULL;
}
/* Adds specified content to playlist, if not already
* present */
void manual_content_scan_add_content_to_playlist(
manual_content_scan_task_config_t *task_config,
playlist_t *playlist, const char *content_path)
{
/* Sanity check */
if (!task_config || !playlist || string_is_empty(content_path))
return;
if (!path_is_valid(content_path))
return;
/* Check whether content is already included
* in playlist */
if (!playlist_entry_exists(playlist, content_path))
{
struct playlist_entry entry = {0};
char label[PATH_MAX_LENGTH];
label[0] = '\0';
/* Get entry label */
fill_short_pathname_representation(
label, content_path, sizeof(label));
if (string_is_empty(label))
return;
/* Configure playlist entry
* > The push function reads our entry as const,
* so these casts are safe */
entry.path = (char*)content_path;
entry.label = label;
entry.core_path = (char*)"DETECT";
entry.core_name = (char*)"DETECT";
entry.crc32 = (char*)"00000000|crc";
entry.db_name = task_config->database_name;
/* Add entry to playlist */
playlist_push(playlist, &entry);
}
}

201
manual_content_scan.h Normal file

@ -0,0 +1,201 @@
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (manual_content_scan.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.
*/
#ifndef __MANUAL_CONTENT_SCAN_H
#define __MANUAL_CONTENT_SCAN_H
#include <retro_common_api.h>
#include <libretro.h>
#include <boolean.h>
#include <lists/string_list.h>
#include "playlist.h"
RETRO_BEGIN_DECLS
/* Defines all possible system name types
* > Use content directory name
* > Use custom name
* > Use database name */
enum manual_content_scan_system_name_type
{
MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR = 0,
MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM,
MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE
};
/* Defines all possible core name types
* > Autodetect core (DETECT)
* > Use manually set core */
enum manual_content_scan_core_type
{
MANUAL_CONTENT_SCAN_CORE_DETECT = 0,
MANUAL_CONTENT_SCAN_CORE_SET
};
/* Holds all configuration parameters required
* for a manual content scan task */
typedef struct
{
char playlist_file[PATH_MAX_LENGTH];
char content_dir[PATH_MAX_LENGTH];
char system_name[PATH_MAX_LENGTH];
char database_name[PATH_MAX_LENGTH];
char core_name[PATH_MAX_LENGTH];
char core_path[PATH_MAX_LENGTH];
char file_exts[PATH_MAX_LENGTH];
bool core_set;
bool overwrite_playlist;
bool include_compressed_content;
} manual_content_scan_task_config_t;
/*****************/
/* Configuration */
/*****************/
/* Pointer access
* > This is a little ugly, but it allows us to
* make use of standard 'menu_settings' code
* for several config parameters (rather than
* implementing unnecessary custom menu entries) */
/* Returns a pointer to the internal
* 'system_name_custom' string */
char *manual_content_scan_get_system_name_custom_ptr(void);
/* Returns size of the internal
* 'system_name_custom' string */
size_t manual_content_scan_get_system_name_custom_size(void);
/* Returns a pointer to the internal
* 'file_exts_custom' string */
char *manual_content_scan_get_file_exts_custom_ptr(void);
/* Returns size of the internal
* 'file_exts_custom' string */
size_t manual_content_scan_get_file_exts_custom_size(void);
/* Returns a pointer to the internal
* 'overwrite_playlist' bool */
bool *manual_content_scan_get_overwrite_playlist_ptr(void);
/* Sanitisation */
/* Removes invalid characters from
* 'system_name_custom' string */
void manual_content_scan_scrub_system_name_custom(void);
/* Removes period (full stop) characters from
* 'file_exts_custom' string and converts to
* lower case */
void manual_content_scan_scrub_file_exts_custom(void);
/* Menu setters */
/* Sets content directory for next manual scan
* operation.
* Returns true if content directory is valid. */
bool manual_content_scan_set_menu_content_dir(const char *content_dir);
/* Sets system name for the next manual scan
* operation.
* Returns true if system name is valid.
* NOTE:
* > Only sets 'system_name_type' and 'system_name_database'
* > 'system_name_content_dir' and 'system_name_custom' are
* (by necessity) handled elsewhere
* > This may look fishy, but it's not - it's a menu-specific
* function, and this is simply the cleanest way to handle
* the setting... */
bool manual_content_scan_set_menu_system_name(
enum manual_content_scan_system_name_type system_name_type,
const char *system_name);
/* Sets core name for the next manual scan
* operation (+ core path and other associated
* parameters).
* Returns true if core name is valid. */
bool manual_content_scan_set_menu_core_name(
enum manual_content_scan_core_type core_type,
const char *core_name);
/* Menu getters */
/* Fetches content directory for next manual scan
* operation.
* Returns true if content directory is valid. */
bool manual_content_scan_get_menu_content_dir(const char **content_dir);
/* Fetches system name for the next manual scan operation.
* Returns true if system name is valid.
* NOTE: This corresponds to the 'System Name' value
* displayed in menus - this is not identical to the
* actual system name used when generating the playlist */
bool manual_content_scan_get_menu_system_name(const char **system_name);
/* Fetches core name for the next manual scan operation.
* Returns true if core name is valid. */
bool manual_content_scan_get_menu_core_name(const char **core_name);
/* Menu utility functions */
/* Creates a list of all possible 'system name' menu
* strings, for use in 'menu_displaylist' drop-down
* lists and 'menu_cbs_left/right'
* > Returns NULL in the event of failure
* > Returned string list must be free()'d */
struct string_list *manual_content_scan_get_menu_system_name_list(void);
/* Creates a list of all possible 'core name' menu
* strings, for use in 'menu_displaylist' drop-down
* lists and 'menu_cbs_left/right'
* > Returns NULL in the event of failure
* > Returned string list must be free()'d */
struct string_list *manual_content_scan_get_menu_core_name_list(void);
/****************/
/* Task Helpers */
/****************/
/* Parses current manual content scan settings,
* and extracts all information required to configure
* a manual content scan task.
* Returns false if current settings are invalid. */
bool manual_content_scan_get_task_config(manual_content_scan_task_config_t *task_config);
/* Creates a list of all valid content in the specified
* content directory
* > Returns NULL in the event of failure
* > Returned string list must be free()'d */
struct string_list *manual_content_scan_get_content_list(manual_content_scan_task_config_t *task_config);
/* Adds specified content to playlist, if not already
* present */
void manual_content_scan_add_content_to_playlist(
manual_content_scan_task_config_t *task_config,
playlist_t *playlist, const char *content_path);
RETRO_END_DECLS
#endif

@ -217,6 +217,8 @@ generic_deferred_push(deferred_push_switch_gpu_profile, DISPLAYLIST_
generic_deferred_push(deferred_push_switch_backlight_control, DISPLAYLIST_SWITCH_BACKLIGHT_CONTROL)
#endif
generic_deferred_push(deferred_push_manual_content_scan_list, DISPLAYLIST_MANUAL_CONTENT_SCAN_LIST)
static int deferred_push_cursor_manager_list_deferred(
menu_displaylist_info_t *info)
{
@ -636,6 +638,8 @@ generic_deferred_push_clear_general(deferred_push_dropdown_box_list_playlist_def
generic_deferred_push_clear_general(deferred_push_dropdown_box_list_playlist_label_display_mode, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_LABEL_DISPLAY_MODE)
generic_deferred_push_clear_general(deferred_push_dropdown_box_list_playlist_right_thumbnail_mode, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_RIGHT_THUMBNAIL_MODE)
generic_deferred_push_clear_general(deferred_push_dropdown_box_list_playlist_left_thumbnail_mode, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_LEFT_THUMBNAIL_MODE)
generic_deferred_push_clear_general(deferred_push_dropdown_box_list_manual_content_scan_system_name, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME)
generic_deferred_push_clear_general(deferred_push_dropdown_box_list_manual_content_scan_core_name, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_MANUAL_CONTENT_SCAN_CORE_NAME)
static int menu_cbs_init_bind_deferred_push_compare_label(
menu_file_list_cbs_t *cbs,
@ -745,6 +749,8 @@ static int menu_cbs_init_bind_deferred_push_compare_label(
{MENU_ENUM_LABEL_DEFERRED_ACCOUNTS_TWITCH_LIST, deferred_push_accounts_twitch_list},
{MENU_ENUM_LABEL_DEFERRED_VIDEO_SHADER_PRESET_SAVE_LIST, deferred_push_video_shader_preset_save},
{MENU_ENUM_LABEL_DEFERRED_VIDEO_SHADER_PRESET_REMOVE_LIST, deferred_push_video_shader_preset_remove},
{MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME, deferred_push_dropdown_box_list_manual_content_scan_system_name},
{MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_CORE_NAME, deferred_push_dropdown_box_list_manual_content_scan_core_name},
};
for (i = 0; i < ARRAY_SIZE(info_list); i++)
@ -1088,6 +1094,9 @@ static int menu_cbs_init_bind_deferred_push_compare_label(
case MENU_ENUM_LABEL_FAVORITES:
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_detect_core_list);
break;
case MENU_ENUM_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST:
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_manual_content_scan_list);
break;
default:
return -1;
}
@ -1304,6 +1313,9 @@ static int menu_cbs_init_bind_deferred_push_compare_label(
case MENU_LABEL_FAVORITES:
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_detect_core_list);
break;
case MENU_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST:
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_manual_content_scan_list);
break;
default:
return -1;
}
@ -1352,4 +1364,4 @@ int menu_cbs_init_bind_deferred_push(menu_file_list_cbs_t *cbs,
return 0;
return -1;
}
}

@ -43,6 +43,7 @@
#include "../../verbosity.h"
#include "../../wifi/wifi_driver.h"
#include "../../playlist.h"
#include "../../manual_content_scan.h"
#ifdef HAVE_NETWORKING
#include "../../network/netplay/netplay.h"
@ -1292,6 +1293,66 @@ static void menu_action_setting_disp_set_label_achievement_information(
strlcpy(s2, path, len2);
}
static void menu_action_setting_disp_set_label_manual_content_scan_dir(file_list_t* list,
unsigned *w, unsigned type, unsigned i,
const char *label,
char *s, size_t len,
const char *path,
char *s2, size_t len2)
{
const char *content_dir = NULL;
*s = '\0';
*w = 19;
strlcpy(s2, path, len2);
if (!manual_content_scan_get_menu_content_dir(&content_dir))
return;
strlcpy(s, content_dir, len);
}
static void menu_action_setting_disp_set_label_manual_content_scan_system_name(file_list_t* list,
unsigned *w, unsigned type, unsigned i,
const char *label,
char *s, size_t len,
const char *path,
char *s2, size_t len2)
{
const char *system_name = NULL;
*s = '\0';
*w = 19;
strlcpy(s2, path, len2);
if (!manual_content_scan_get_menu_system_name(&system_name))
return;
strlcpy(s, system_name, len);
}
static void menu_action_setting_disp_set_label_manual_content_scan_core_name(file_list_t* list,
unsigned *w, unsigned type, unsigned i,
const char *label,
char *s, size_t len,
const char *path,
char *s2, size_t len2)
{
const char *core_name = NULL;
*s = '\0';
*w = 19;
strlcpy(s2, path, len2);
if (!manual_content_scan_get_menu_core_name(&core_name))
return;
strlcpy(s, core_name, len);
}
static void menu_action_setting_disp_set_label_no_items(
file_list_t* list,
unsigned *w, unsigned type, unsigned i,
@ -1502,6 +1563,18 @@ static int menu_cbs_init_bind_get_string_representation_compare_label(
BIND_ACTION_GET_VALUE(cbs,
menu_action_setting_disp_set_label_playlist_left_thumbnail_mode);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DIR:
BIND_ACTION_GET_VALUE(cbs,
menu_action_setting_disp_set_label_manual_content_scan_dir);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
BIND_ACTION_GET_VALUE(cbs,
menu_action_setting_disp_set_label_manual_content_scan_system_name);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME:
BIND_ACTION_GET_VALUE(cbs,
menu_action_setting_disp_set_label_manual_content_scan_core_name);
break;
default:
return - 1;
}

@ -41,6 +41,7 @@
#include "../../retroarch.h"
#include "../../network/netplay/netplay.h"
#include "../../playlist.h"
#include "../../manual_content_scan.h"
#ifndef BIND_ACTION_LEFT
#define BIND_ACTION_LEFT(cbs, name) \
@ -515,6 +516,116 @@ static int playlist_left_thumbnail_mode_left(unsigned type, const char *label,
return 0;
}
static int manual_content_scan_system_name_left(unsigned type, const char *label,
bool wraparound)
{
struct string_list *system_name_list =
manual_content_scan_get_menu_system_name_list();
const char *current_system_name = NULL;
enum manual_content_scan_system_name_type next_system_name_type =
MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE;
const char *next_system_name = NULL;
unsigned current_index = 0;
unsigned next_index = 0;
unsigned i;
if (!system_name_list)
return -1;
/* Get currently selected system name */
if (manual_content_scan_get_menu_system_name(&current_system_name))
{
/* Get index of currently selected system name */
for (i = 0; i < system_name_list->size; i++)
{
const char *system_name = system_name_list->elems[i].data;
if (string_is_equal(current_system_name, system_name))
{
current_index = i;
break;
}
}
/* Decrement index */
if (current_index > 0)
next_index = current_index - 1;
else if (wraparound && (system_name_list->size > 1))
next_index = system_name_list->size - 1;
}
/* Get new system name parameters */
if (next_index == (unsigned)MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR)
next_system_name_type = MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR;
else if (next_index == (unsigned)MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM)
next_system_name_type = MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM;
next_system_name = system_name_list->elems[next_index].data;
/* Set system name */
manual_content_scan_set_menu_system_name(
next_system_name_type, next_system_name);
/* Clean up */
string_list_free(system_name_list);
return 0;
}
static int manual_content_scan_core_name_left(unsigned type, const char *label,
bool wraparound)
{
struct string_list *core_name_list =
manual_content_scan_get_menu_core_name_list();
const char *current_core_name = NULL;
enum manual_content_scan_core_type next_core_type =
MANUAL_CONTENT_SCAN_CORE_SET;
const char *next_core_name = NULL;
unsigned current_index = 0;
unsigned next_index = 0;
unsigned i;
if (!core_name_list)
return -1;
/* Get currently selected core name */
if (manual_content_scan_get_menu_core_name(&current_core_name))
{
/* Get index of currently selected core name */
for (i = 0; i < core_name_list->size; i++)
{
const char *core_name = core_name_list->elems[i].data;
if (string_is_equal(current_core_name, core_name))
{
current_index = i;
break;
}
}
/* Decrement index */
if (current_index > 0)
next_index = current_index - 1;
else if (wraparound && (core_name_list->size > 1))
next_index = core_name_list->size - 1;
}
/* Get new core name parameters */
if (next_index == (unsigned)MANUAL_CONTENT_SCAN_CORE_DETECT)
next_core_type = MANUAL_CONTENT_SCAN_CORE_DETECT;
next_core_name = core_name_list->elems[next_index].data;
/* Set core name */
manual_content_scan_set_menu_core_name(
next_core_type, next_core_name);
/* Clean up */
string_list_free(core_name_list);
return 0;
}
static int core_setting_left(unsigned type, const char *label,
bool wraparound)
{
@ -766,6 +877,12 @@ static int menu_cbs_init_bind_left_compare_label(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_LEFT_THUMBNAIL_MODE:
BIND_ACTION_LEFT(cbs, playlist_left_thumbnail_mode_left);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
BIND_ACTION_LEFT(cbs, manual_content_scan_system_name_left);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME:
BIND_ACTION_LEFT(cbs, manual_content_scan_core_name_left);
break;
default:
return -1;
}
@ -861,6 +978,7 @@ static int menu_cbs_init_bind_left_compare_type(menu_file_list_cbs_t *cbs,
case FILE_TYPE_DOWNLOAD_THUMBNAIL_CONTENT:
case FILE_TYPE_DOWNLOAD_URL:
case FILE_TYPE_SCAN_DIRECTORY:
case FILE_TYPE_MANUAL_SCAN_DIRECTORY:
case FILE_TYPE_FONT:
case MENU_SETTING_GROUP:
case MENU_SETTINGS_CORE_INFO_NONE:

@ -75,6 +75,7 @@
#include "../../lakka.h"
#include "../../wifi/wifi_driver.h"
#include "../../gfx/video_display_server.h"
#include "../../manual_content_scan.h"
#include <net/net_http.h>
@ -182,6 +183,10 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl)
return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_PLAYLIST_RIGHT_THUMBNAIL_MODE;
case ACTION_OK_DL_DROPDOWN_BOX_LIST_PLAYLIST_LEFT_THUMBNAIL_MODE:
return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_PLAYLIST_LEFT_THUMBNAIL_MODE;
case ACTION_OK_DL_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME;
case ACTION_OK_DL_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_CORE_NAME:
return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_CORE_NAME;
case ACTION_OK_DL_MIXER_STREAM_SETTINGS_LIST:
return MENU_ENUM_LABEL_DEFERRED_MIXER_STREAM_SETTINGS_LIST;
case ACTION_OK_DL_ACCOUNTS_LIST:
@ -306,6 +311,8 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl)
return MENU_ENUM_LABEL_DEFERRED_VIDEO_SHADER_PRESET_SAVE_LIST;
case ACTION_OK_DL_SHADER_PRESET_REMOVE:
return MENU_ENUM_LABEL_DEFERRED_VIDEO_SHADER_PRESET_REMOVE_LIST;
case ACTION_OK_DL_MANUAL_CONTENT_SCAN_LIST:
return MENU_ENUM_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST;
default:
break;
}
@ -440,6 +447,24 @@ int generic_action_ok_displaylist_push(const char *path,
info.enum_idx = MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_PLAYLIST_LEFT_THUMBNAIL_MODE;
dl_type = DISPLAYLIST_GENERIC;
break;
case ACTION_OK_DL_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
info.type = type;
info.directory_ptr = idx;
info_path = path;
info_label = msg_hash_to_str(
MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME);
info.enum_idx = MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME;
dl_type = DISPLAYLIST_GENERIC;
break;
case ACTION_OK_DL_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_CORE_NAME:
info.type = type;
info.directory_ptr = idx;
info_path = path;
info_label = msg_hash_to_str(
MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_CORE_NAME);
info.enum_idx = MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_CORE_NAME;
dl_type = DISPLAYLIST_GENERIC;
break;
case ACTION_OK_DL_USER_BINDS_LIST:
info.type = type;
info.directory_ptr = idx;
@ -600,6 +625,14 @@ int generic_action_ok_displaylist_push(const char *path,
info_label = label;
dl_type = DISPLAYLIST_FILE_BROWSER_SCAN_DIR;
break;
case ACTION_OK_DL_MANUAL_SCAN_DIR_LIST:
filebrowser_set_type(FILEBROWSER_MANUAL_SCAN_DIR);
info.type = FILE_TYPE_DIRECTORY;
info.directory_ptr = idx;
info_path = new_path;
info_label = label;
dl_type = DISPLAYLIST_FILE_BROWSER_SELECT_DIR;
break;
case ACTION_OK_DL_REMAP_FILE:
filebrowser_clear_type();
info.type = type;
@ -1019,6 +1052,7 @@ int generic_action_ok_displaylist_push(const char *path,
case ACTION_OK_DL_SHADER_PRESET_REMOVE:
case ACTION_OK_DL_SHADER_PRESET_SAVE:
case ACTION_OK_DL_CDROM_INFO_LIST:
case ACTION_OK_DL_MANUAL_CONTENT_SCAN_LIST:
action_ok_dl_lbl(action_ok_dl_to_enum(action_type), DISPLAYLIST_GENERIC);
break;
case ACTION_OK_DL_CDROM_INFO_DETAIL_LIST:
@ -2989,6 +3023,47 @@ static int action_ok_path_scan_directory(const char *path,
}
#endif
static int action_ok_path_manual_scan_directory(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
const char *flush_char = msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST);
unsigned flush_type = 0;
const char *menu_path = NULL;
char content_dir[PATH_MAX_LENGTH];
content_dir[0] = '\0';
/* 'Reset' file browser */
filebrowser_clear_type();
/* Get user-selected scan directory */
menu_entries_get_last_stack(&menu_path,
NULL, NULL, NULL, NULL);
if (!string_is_empty(menu_path))
strlcpy(content_dir, menu_path, sizeof(content_dir));
#ifdef HAVE_COCOATOUCH
{
/* For iOS, set the path using realpath because the path name
* can start with /private and this ensures the path starts with it.
* This will allow the path to be properly substituted when
* fill_pathname_expand_special() is called. */
char real_content_dir[PATH_MAX_LENGTH] = {0};
realpath(content_dir, real_content_dir);
strlcpy(content_dir, real_content_dir, sizeof(content_dir));
}
#endif
/* Update manual content scan settings */
manual_content_scan_set_menu_content_dir(content_dir);
/* Return to 'manual content scan' menu */
menu_entries_flush_stack(flush_char, flush_type);
return 0;
}
static int action_ok_core_deferred_set(const char *new_core_path,
const char *content_label, unsigned type, size_t idx, size_t entry_idx)
{
@ -4671,6 +4746,7 @@ default_action_ok_func(action_ok_push_load_disc_list, ACTION_OK_DL_LOAD_DISC_LIS
default_action_ok_func(action_ok_open_archive, ACTION_OK_DL_OPEN_ARCHIVE)
default_action_ok_func(action_ok_rgui_menu_theme_preset, ACTION_OK_DL_RGUI_MENU_THEME_PRESET)
default_action_ok_func(action_ok_pl_thumbnails_updater_list, ACTION_OK_DL_PL_THUMBNAILS_UPDATER_LIST)
default_action_ok_func(action_ok_push_manual_content_scan_list, ACTION_OK_DL_MANUAL_CONTENT_SCAN_LIST)
static int action_ok_open_uwp_permission_settings(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
@ -5161,6 +5237,17 @@ int action_ok_push_filebrowser_list_file_select(const char *path,
entry_idx, ACTION_OK_DL_FILE_BROWSER_SELECT_DIR);
}
int action_ok_push_manual_content_scan_dir_select(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
settings_t *settings = config_get_ptr();
filebrowser_clear_type();
return generic_action_ok_displaylist_push(path,
settings->paths.directory_menu_content, label, type, idx,
entry_idx, ACTION_OK_DL_MANUAL_SCAN_DIR_LIST);
}
/* TODO/FIXME */
static int action_ok_push_dropdown_setting_core_options_item_special(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
@ -5532,6 +5619,54 @@ static int action_ok_push_dropdown_item_playlist_left_thumbnail_mode(const char
return action_cancel_pop_default(NULL, NULL, 0, 0);
}
static int action_ok_push_dropdown_item_manual_content_scan_system_name(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
const char* system_name = path;
enum manual_content_scan_system_name_type system_name_type =
MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE;
(void)label;
(void)type;
(void)entry_idx;
/* Get system name type (i.e. check if setting is
* 'use content directory' or 'use custom') */
if (idx == (size_t)MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR)
system_name_type = MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR;
else if (idx == (size_t)MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM)
system_name_type = MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM;
/* Set system name */
manual_content_scan_set_menu_system_name(
system_name_type, system_name);
return action_cancel_pop_default(NULL, NULL, 0, 0);
}
static int action_ok_push_dropdown_item_manual_content_scan_core_name(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
const char* core_name = path;
enum manual_content_scan_core_type core_type =
MANUAL_CONTENT_SCAN_CORE_SET;
(void)label;
(void)type;
(void)entry_idx;
/* Get core type (i.e. check if setting is
* DETECT/Unspecified) */
if (idx == (size_t)MANUAL_CONTENT_SCAN_CORE_DETECT)
core_type = MANUAL_CONTENT_SCAN_CORE_DETECT;
/* Set core name */
manual_content_scan_set_menu_core_name(
core_type, core_name);
return action_cancel_pop_default(NULL, NULL, 0, 0);
}
static int action_ok_push_default(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
@ -5750,6 +5885,33 @@ static int action_ok_playlist_left_thumbnail_mode(const char *path,
return 0;
}
static int action_ok_manual_content_scan_system_name(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
generic_action_ok_displaylist_push(
NULL,
NULL, NULL, 0, idx, 0,
ACTION_OK_DL_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME);
return 0;
}
static int action_ok_manual_content_scan_core_name(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
generic_action_ok_displaylist_push(
NULL,
NULL, NULL, 0, idx, 0,
ACTION_OK_DL_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_CORE_NAME);
return 0;
}
static int action_ok_manual_content_scan_start(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
task_push_manual_content_scan();
return 0;
}
static int action_ok_netplay_enable_host(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
@ -6698,6 +6860,21 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_ACHIEVEMENT_RESUME:
BIND_ACTION_OK(cbs, action_ok_cheevos_toggle_hardcore_mode);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST:
BIND_ACTION_OK(cbs, action_ok_push_manual_content_scan_list);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DIR:
BIND_ACTION_OK(cbs, action_ok_push_manual_content_scan_dir_select);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
BIND_ACTION_OK(cbs, action_ok_manual_content_scan_system_name);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME:
BIND_ACTION_OK(cbs, action_ok_manual_content_scan_core_name);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_START:
BIND_ACTION_OK(cbs, action_ok_manual_content_scan_start);
break;
default:
return -1;
}
@ -6826,6 +7003,12 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
case MENU_LABEL_PLAYLIST_MANAGER_LEFT_THUMBNAIL_MODE:
BIND_ACTION_OK(cbs, action_ok_playlist_left_thumbnail_mode);
break;
case MENU_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
BIND_ACTION_OK(cbs, action_ok_manual_content_scan_system_name);
break;
case MENU_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME:
BIND_ACTION_OK(cbs, action_ok_manual_content_scan_core_name);
break;
default:
return -1;
}
@ -6958,6 +7141,12 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs,
case MENU_SETTING_DROPDOWN_ITEM_PLAYLIST_LEFT_THUMBNAIL_MODE:
BIND_ACTION_OK(cbs, action_ok_push_dropdown_item_playlist_left_thumbnail_mode);
break;
case MENU_SETTING_DROPDOWN_ITEM_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
BIND_ACTION_OK(cbs, action_ok_push_dropdown_item_manual_content_scan_system_name);
break;
case MENU_SETTING_DROPDOWN_ITEM_MANUAL_CONTENT_SCAN_CORE_NAME:
BIND_ACTION_OK(cbs, action_ok_push_dropdown_item_manual_content_scan_core_name);
break;
case MENU_SETTING_ACTION_CORE_DISK_OPTIONS:
BIND_ACTION_OK(cbs, action_ok_push_default);
break;
@ -7043,6 +7232,9 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs,
BIND_ACTION_OK(cbs, action_ok_path_scan_directory);
break;
#endif
case FILE_TYPE_MANUAL_SCAN_DIRECTORY:
BIND_ACTION_OK(cbs, action_ok_path_manual_scan_directory);
break;
case FILE_TYPE_CONFIG:
BIND_ACTION_OK(cbs, action_ok_config_load);
break;

@ -42,6 +42,7 @@
#include "../../ui/ui_companion_driver.h"
#include "../../network/netplay/netplay.h"
#include "../../playlist.h"
#include "../../manual_content_scan.h"
#ifndef BIND_ACTION_RIGHT
#define BIND_ACTION_RIGHT(cbs, name) \
@ -628,6 +629,134 @@ static int playlist_left_thumbnail_mode_right(unsigned type, const char *label,
return 0;
}
static int manual_content_scan_system_name_right(unsigned type, const char *label,
bool wraparound)
{
struct string_list *system_name_list =
manual_content_scan_get_menu_system_name_list();
const char *current_system_name = NULL;
enum manual_content_scan_system_name_type next_system_name_type =
MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE;
const char *next_system_name = NULL;
unsigned current_index = 0;
unsigned next_index = 0;
unsigned i;
if (!system_name_list)
return -1;
/* Get currently selected system name */
if (manual_content_scan_get_menu_system_name(&current_system_name))
{
/* Get index of currently selected system name */
for (i = 0; i < system_name_list->size; i++)
{
const char *system_name = system_name_list->elems[i].data;
if (string_is_equal(current_system_name, system_name))
{
current_index = i;
break;
}
}
/* Increment index */
next_index = current_index + 1;
if (next_index >= system_name_list->size)
{
if (wraparound)
next_index = 0;
else
{
if (system_name_list->size > 0)
next_index = system_name_list->size - 1;
else
next_index = 0;
}
}
}
/* Get new system name parameters */
if (next_index == (unsigned)MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR)
next_system_name_type = MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR;
else if (next_index == (unsigned)MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM)
next_system_name_type = MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM;
next_system_name = system_name_list->elems[next_index].data;
/* Set system name */
manual_content_scan_set_menu_system_name(
next_system_name_type, next_system_name);
/* Clean up */
string_list_free(system_name_list);
return 0;
}
static int manual_content_scan_core_name_right(unsigned type, const char *label,
bool wraparound)
{
struct string_list *core_name_list =
manual_content_scan_get_menu_core_name_list();
const char *current_core_name = NULL;
enum manual_content_scan_core_type next_core_type =
MANUAL_CONTENT_SCAN_CORE_SET;
const char *next_core_name = NULL;
unsigned current_index = 0;
unsigned next_index = 0;
unsigned i;
if (!core_name_list)
return -1;
/* Get currently selected core name */
if (manual_content_scan_get_menu_core_name(&current_core_name))
{
/* Get index of currently selected core name */
for (i = 0; i < core_name_list->size; i++)
{
const char *core_name = core_name_list->elems[i].data;
if (string_is_equal(current_core_name, core_name))
{
current_index = i;
break;
}
}
/* Increment index */
next_index = current_index + 1;
if (next_index >= core_name_list->size)
{
if (wraparound)
next_index = 0;
else
{
if (core_name_list->size > 0)
next_index = core_name_list->size - 1;
else
next_index = 0;
}
}
}
/* Get new core name parameters */
if (next_index == (unsigned)MANUAL_CONTENT_SCAN_CORE_DETECT)
next_core_type = MANUAL_CONTENT_SCAN_CORE_DETECT;
next_core_name = core_name_list->elems[next_index].data;
/* Set core name */
manual_content_scan_set_menu_core_name(
next_core_type, next_core_name);
/* Clean up */
string_list_free(core_name_list);
return 0;
}
int core_setting_right(unsigned type, const char *label,
bool wraparound)
{
@ -735,6 +864,7 @@ static int menu_cbs_init_bind_right_compare_type(menu_file_list_cbs_t *cbs,
case FILE_TYPE_DOWNLOAD_THUMBNAIL_CONTENT:
case FILE_TYPE_DOWNLOAD_URL:
case FILE_TYPE_SCAN_DIRECTORY:
case FILE_TYPE_MANUAL_SCAN_DIRECTORY:
case FILE_TYPE_FONT:
case MENU_SETTING_GROUP:
case MENU_SETTINGS_CORE_INFO_NONE:
@ -915,6 +1045,12 @@ static int menu_cbs_init_bind_right_compare_label(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_LEFT_THUMBNAIL_MODE:
BIND_ACTION_RIGHT(cbs, playlist_left_thumbnail_mode_right);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
BIND_ACTION_RIGHT(cbs, manual_content_scan_system_name_right);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME:
BIND_ACTION_RIGHT(cbs, manual_content_scan_core_name_right);
break;
default:
return -1;
}

@ -38,6 +38,7 @@
#include "../../retroarch.h"
#include "../../performance_counters.h"
#include "../../playlist.h"
#include "../../manual_content_scan.h"
#include "../../input/input_remapping.h"
@ -302,6 +303,29 @@ static int action_start_playlist_left_thumbnail_mode(unsigned type, const char *
return 0;
}
static int action_start_manual_content_scan_dir(unsigned type, const char *label)
{
/* Reset content directory */
manual_content_scan_set_menu_content_dir("");
return 0;
}
static int action_start_manual_content_scan_system_name(unsigned type, const char *label)
{
/* Reset system name */
manual_content_scan_set_menu_system_name(
MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR, "");
return 0;
}
static int action_start_manual_content_scan_core_name(unsigned type, const char *label)
{
/* Reset core name */
manual_content_scan_set_menu_core_name(
MANUAL_CONTENT_SCAN_CORE_DETECT, "");
return 0;
}
static int action_start_video_resolution(unsigned type, const char *label)
{
unsigned width = 0, height = 0;
@ -405,6 +429,15 @@ static int menu_cbs_init_bind_start_compare_label(menu_file_list_cbs_t *cbs)
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_LEFT_THUMBNAIL_MODE:
BIND_ACTION_START(cbs, action_start_playlist_left_thumbnail_mode);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DIR:
BIND_ACTION_START(cbs, action_start_manual_content_scan_dir);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
BIND_ACTION_START(cbs, action_start_manual_content_scan_system_name);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME:
BIND_ACTION_START(cbs, action_start_manual_content_scan_core_name);
break;
default:
return -1;
}

@ -714,6 +714,14 @@ default_sublabel_macro(action_bind_sublabel_thumbnails_updater_list,
default_sublabel_macro(action_bind_sublabel_pl_thumbnails_updater_list, MENU_ENUM_SUBLABEL_PL_THUMBNAILS_UPDATER_LIST)
default_sublabel_macro(action_bind_sublabel_help_send_debug_info, MENU_ENUM_SUBLABEL_HELP_SEND_DEBUG_INFO)
default_sublabel_macro(action_bind_sublabel_rdb_entry_detail, MENU_ENUM_SUBLABEL_RDB_ENTRY_DETAIL)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_list, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_LIST)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_dir, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_DIR)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_system_name, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_system_name_custom, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_core_name, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_CORE_NAME)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_file_exts, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_FILE_EXTS)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_overwrite, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_OVERWRITE)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_start, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_START)
static int action_bind_sublabel_systeminfo_controller_entry(
file_list_t *list,
@ -3049,6 +3057,30 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_RDB_ENTRY_DETAIL:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_rdb_entry_detail);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_list);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DIR:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_dir);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_system_name);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_system_name_custom);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_core_name);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_FILE_EXTS:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_file_exts);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_OVERWRITE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_overwrite);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_START:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_start);
break;
default:
case MSG_UNKNOWN:
return -1;

@ -65,6 +65,8 @@ static int action_get_title_action_generic(const char *path, const char *label,
const char *title = msg_hash_to_str(lbl); \
if (!string_is_empty(path) && !string_is_empty(title)) \
fill_pathname_join_delim(s, title, path, ' ', len); \
else if (!string_is_empty(title)) \
strlcpy(s, title, len); \
return 1; \
}
@ -396,10 +398,12 @@ default_title_macro(action_get_title_goto_music, MENU_ENUM_LABEL_
default_title_macro(action_get_title_goto_video, MENU_ENUM_LABEL_VALUE_GOTO_VIDEO)
default_title_macro(action_get_title_collection, MENU_ENUM_LABEL_VALUE_PLAYLISTS_TAB)
default_title_macro(action_get_title_deferred_core_list, MENU_ENUM_LABEL_VALUE_SUPPORTED_CORES)
default_title_macro(action_get_title_dropdown_resolution_item, MENU_ENUM_LABEL_VALUE_SCREEN_RESOLUTION)
default_title_macro(action_get_title_dropdown_playlist_default_core_item, MENU_ENUM_LABEL_VALUE_PLAYLIST_MANAGER_DEFAULT_CORE)
default_title_macro(action_get_title_dropdown_playlist_label_display_mode_item, MENU_ENUM_LABEL_VALUE_PLAYLIST_MANAGER_LABEL_DISPLAY_MODE)
default_title_macro(action_get_title_manual_content_scan_list, MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_LIST)
default_title_macro(action_get_title_dropdown_manual_content_scan_system_name_item, MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME)
default_title_macro(action_get_title_dropdown_manual_content_scan_core_name_item, MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_CORE_NAME)
default_fill_title_macro(action_get_title_disk_image_append, MENU_ENUM_LABEL_VALUE_DISK_IMAGE_APPEND)
default_fill_title_macro(action_get_title_cheat_file_load, MENU_ENUM_LABEL_VALUE_CHEAT_FILE)
@ -444,6 +448,7 @@ default_fill_title_macro(action_get_title_extraction_directory, MENU_ENUM_LABE
default_fill_title_macro(action_get_title_menu, MENU_ENUM_LABEL_VALUE_MENU_SETTINGS)
default_fill_title_macro(action_get_title_font_path, MENU_ENUM_LABEL_VALUE_XMB_FONT)
default_fill_title_macro(action_get_title_log_dir, MENU_ENUM_LABEL_VALUE_LOG_DIR)
default_fill_title_macro(action_get_title_manual_content_scan_dir, MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_DIR)
default_title_copy_macro(action_get_title_help, MENU_ENUM_LABEL_VALUE_HELP_LIST)
default_title_copy_macro(action_get_title_input_settings, MENU_ENUM_LABEL_VALUE_INPUT_SETTINGS)
@ -1226,6 +1231,12 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs,
BIND_ACTION_GET_TITLE(cbs, action_get_title_switch_backlight_control);
break;
#endif
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST:
BIND_ACTION_GET_TITLE(cbs, action_get_title_manual_content_scan_list);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DIR:
BIND_ACTION_GET_TITLE(cbs, action_get_title_manual_content_scan_dir);
break;
default:
return -1;
}
@ -1363,6 +1374,9 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs,
case MENU_LABEL_CORE_ASSETS_DIRECTORY:
BIND_ACTION_GET_TITLE(cbs, action_get_title_core_assets_directory);
break;
case MENU_LABEL_THUMBNAILS_DIRECTORY:
BIND_ACTION_GET_TITLE(cbs, action_get_title_thumbnail_directory);
break;
case MENU_LABEL_RGUI_CONFIG_DIRECTORY:
BIND_ACTION_GET_TITLE(cbs, action_get_title_config_directory);
break;
@ -1543,6 +1557,12 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs,
BIND_ACTION_GET_TITLE(cbs, action_get_title_switch_backlight_control);
break;
#endif
case MENU_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST:
BIND_ACTION_GET_TITLE(cbs, action_get_title_manual_content_scan_list);
break;
case MENU_LABEL_MANUAL_CONTENT_SCAN_DIR:
BIND_ACTION_GET_TITLE(cbs, action_get_title_manual_content_scan_dir);
break;
default:
return -1;
}
@ -1651,6 +1671,18 @@ int menu_cbs_init_bind_title(menu_file_list_cbs_t *cbs,
BIND_ACTION_GET_TITLE(cbs, action_get_title_dropdown_playlist_left_thumbnail_mode_item);
return 0;
}
if (string_is_equal(label,
msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME)))
{
BIND_ACTION_GET_TITLE(cbs, action_get_title_dropdown_manual_content_scan_system_name_item);
return 0;
}
if (string_is_equal(label,
msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_CORE_NAME)))
{
BIND_ACTION_GET_TITLE(cbs, action_get_title_dropdown_manual_content_scan_core_name_item);
return 0;
}
if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_RPL_ENTRY_ACTIONS)))
{
BIND_ACTION_GET_TITLE(cbs, action_get_quick_menu_views_settings_list);
@ -1676,6 +1708,11 @@ int menu_cbs_init_bind_title(menu_file_list_cbs_t *cbs,
BIND_ACTION_GET_TITLE(cbs, action_get_title_collection);
return 0;
}
if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST)))
{
BIND_ACTION_GET_TITLE(cbs, action_get_title_manual_content_scan_list);
return 0;
}
return -1;
}

@ -2552,7 +2552,6 @@ enum materialui_entry_value_type materialui_get_entry_value_type(
switch (entry_file_type)
{
case FILE_TYPE_IN_CARCHIVE:
case FILE_TYPE_COMPRESSED:
case FILE_TYPE_MORE:
case FILE_TYPE_CORE:
case FILE_TYPE_DIRECT_LOAD:
@ -2564,6 +2563,15 @@ enum materialui_entry_value_type materialui_get_entry_value_type(
case FILE_TYPE_IMAGE:
case FILE_TYPE_MOVIE:
break;
case FILE_TYPE_COMPRESSED:
/* Note that we have to perform a backup check here,
* since the 'manual content scan - file extensions'
* setting may have a value of 'zip' or '7z' etc, which
* means it would otherwise get incorreclty identified as
* an achive file... */
if (entry_type != FILE_TYPE_CARCHIVE)
value_type = MUI_ENTRY_VALUE_TEXT;
break;
default:
value_type = MUI_ENTRY_VALUE_TEXT;
break;
@ -2693,7 +2701,13 @@ static void materialui_render_menu_entry_default(
switch (entry_file_type)
{
case FILE_TYPE_COMPRESSED:
icon_texture = mui->textures.list[MUI_TEXTURE_ARCHIVE];
/* Note that we have to perform a backup check here,
* since the 'manual content scan - file extensions'
* setting may have a value of 'zip' or '7z' etc, which
* means it would otherwise get incorreclty identified as
* an achive file... */
if (entry_type == FILE_TYPE_CARCHIVE)
icon_texture = mui->textures.list[MUI_TEXTURE_ARCHIVE];
break;
case FILE_TYPE_IMAGE:
icon_texture = mui->textures.list[MUI_TEXTURE_IMAGE];
@ -7613,7 +7627,8 @@ static void materialui_list_insert(
node->has_icon = true;
}
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SCAN_DIRECTORY)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SCAN_FILE))
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SCAN_FILE)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST))
)
{
node->icon_texture_index = MUI_TEXTURE_ADD;

@ -250,6 +250,7 @@ menu_texture_item ozone_entries_icon_get_texture(ozone_handle_t *ozone,
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_USER];
case MENU_ENUM_LABEL_DIRECTORY_SETTINGS:
case MENU_ENUM_LABEL_SCAN_DIRECTORY:
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST:
case MENU_ENUM_LABEL_REMAP_FILE_SAVE_CONTENT_DIR:
case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CONTENT_DIR:
case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_PARENT:

@ -2379,6 +2379,7 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb,
return xmb->textures.list[XMB_TEXTURE_RESUME];
case MENU_ENUM_LABEL_DIRECTORY_SETTINGS:
case MENU_ENUM_LABEL_SCAN_DIRECTORY:
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST:
case MENU_ENUM_LABEL_REMAP_FILE_SAVE_CONTENT_DIR:
case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CONTENT_DIR:
case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_PARENT:

@ -52,11 +52,14 @@ enum
ACTION_OK_DL_DROPDOWN_BOX_LIST_PLAYLIST_LABEL_DISPLAY_MODE,
ACTION_OK_DL_DROPDOWN_BOX_LIST_PLAYLIST_RIGHT_THUMBNAIL_MODE,
ACTION_OK_DL_DROPDOWN_BOX_LIST_PLAYLIST_LEFT_THUMBNAIL_MODE,
ACTION_OK_DL_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME,
ACTION_OK_DL_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_CORE_NAME,
ACTION_OK_DL_OPEN_ARCHIVE,
ACTION_OK_DL_OPEN_ARCHIVE_DETECT_CORE,
ACTION_OK_DL_MUSIC,
ACTION_OK_DL_NETPLAY,
ACTION_OK_DL_SCAN_DIR_LIST,
ACTION_OK_DL_MANUAL_SCAN_DIR_LIST,
ACTION_OK_DL_HELP,
ACTION_OK_DL_RPL_ENTRY,
ACTION_OK_DL_RDB_ENTRY,
@ -166,7 +169,8 @@ enum
ACTION_OK_DL_BROWSE_URL_START,
ACTION_OK_DL_CONTENT_SETTINGS,
ACTION_OK_DL_CDROM_INFO_DETAIL_LIST,
ACTION_OK_DL_RGUI_MENU_THEME_PRESET
ACTION_OK_DL_RGUI_MENU_THEME_PRESET,
ACTION_OK_DL_MANUAL_CONTENT_SCAN_LIST
};
/* Function callbacks */

@ -98,6 +98,7 @@
#include "../tasks/tasks_internal.h"
#include "../dynamic.h"
#include "../runtime_file.h"
#include "../manual_content_scan.h"
static char new_path_entry[4096] = {0};
static char new_lbl_entry[4096] = {0};
@ -2334,9 +2335,9 @@ static unsigned menu_displaylist_parse_playlists(
if (!horizontal)
{
#ifdef HAVE_LIBRETRODB
if (settings->bools.menu_content_show_add)
{
#ifdef HAVE_LIBRETRODB
if (menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCAN_DIRECTORY),
msg_hash_to_str(MENU_ENUM_LABEL_SCAN_DIRECTORY),
@ -2349,8 +2350,15 @@ static unsigned menu_displaylist_parse_playlists(
MENU_ENUM_LABEL_SCAN_FILE,
MENU_SETTING_ACTION, 0, 0))
count++;
}
#endif
if (menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_LIST),
msg_hash_to_str(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST),
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST,
MENU_SETTING_ACTION, 0, 0))
count++;
}
if (settings->bools.menu_content_show_favorites)
if (menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_FAVORITES),
@ -3667,6 +3675,64 @@ static unsigned populate_playlist_thumbnail_mode_dropdown_list(
return count;
}
static bool menu_displaylist_parse_manual_content_scan_list(
menu_displaylist_info_t *info)
{
unsigned count = 0;
/* Content directory */
if (menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_DIR),
msg_hash_to_str(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DIR),
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DIR,
MENU_SETTING_MANUAL_CONTENT_SCAN_DIR, 0, 0))
count++;
/* System name */
if (menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME),
msg_hash_to_str(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME),
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME,
MENU_SETTING_MANUAL_CONTENT_SCAN_SYSTEM_NAME, 0, 0))
count++;
/* Custom system name */
if (menu_displaylist_parse_settings_enum(info->list,
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM, PARSE_ONLY_STRING,
false) == 0)
count++;
/* Core name */
if (menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_CORE_NAME),
msg_hash_to_str(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME),
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME,
MENU_SETTING_MANUAL_CONTENT_SCAN_CORE_NAME, 0, 0))
count++;
/* File extensions */
if (menu_displaylist_parse_settings_enum(info->list,
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_FILE_EXTS, PARSE_ONLY_STRING,
false) == 0)
count++;
/* Overwrite playlist */
if (menu_displaylist_parse_settings_enum(info->list,
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_OVERWRITE, PARSE_ONLY_BOOL,
false) == 0)
count++;
/* Start scan */
if (menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_START),
msg_hash_to_str(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_START),
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_START,
MENU_SETTING_ACTION_MANUAL_CONTENT_SCAN_START, 0, 0))
count++;
return (count > 0);
}
unsigned menu_displaylist_build_list(file_list_t *list, enum menu_displaylist_ctl_state type)
{
unsigned i;
@ -3863,6 +3929,12 @@ unsigned menu_displaylist_build_list(file_list_t *list, enum menu_displaylist_ct
MENU_SETTING_ACTION, 0, 0))
count++;
#endif
if (menu_entries_append_enum(list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_LIST),
msg_hash_to_str(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST),
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST,
MENU_SETTING_ACTION, 0, 0))
count++;
break;
case DISPLAYLIST_NETWORK_INFO:
#if defined(HAVE_NETWORKING) && !defined(HAVE_SOCKET_LEGACY) && (!defined(SWITCH) || defined(SWITCH) && defined(HAVE_LIBNX))
@ -4125,6 +4197,84 @@ unsigned menu_displaylist_build_list(file_list_t *list, enum menu_displaylist_ct
case DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_LEFT_THUMBNAIL_MODE:
count = populate_playlist_thumbnail_mode_dropdown_list(list, PLAYLIST_THUMBNAIL_LEFT);
break;
case DISPLAYLIST_DROPDOWN_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
{
/* Get system name list */
struct string_list *system_name_list = manual_content_scan_get_menu_system_name_list();
if (system_name_list)
{
const char *current_system_name = NULL;
unsigned i;
/* Get currently selected system name */
manual_content_scan_get_menu_system_name(&current_system_name);
/* Loop through names */
for (i = 0; i < system_name_list->size; i++)
{
/* Note: manual_content_scan_get_system_name_list()
* ensures that system_name cannot be empty here */
const char *system_name = system_name_list->elems[i].data;
/* Add menu entry */
if (menu_entries_append_enum(list,
system_name,
"",
MENU_ENUM_LABEL_NO_ITEMS,
MENU_SETTING_DROPDOWN_ITEM_MANUAL_CONTENT_SCAN_SYSTEM_NAME,
i, 0))
count++;
/* Check whether current entry is checked */
if (string_is_equal(current_system_name, system_name))
menu_entries_set_checked(list, i, true);
}
/* Clean up */
string_list_free(system_name_list);
}
}
break;
case DISPLAYLIST_DROPDOWN_LIST_MANUAL_CONTENT_SCAN_CORE_NAME:
{
/* Get core name list */
struct string_list *core_name_list = manual_content_scan_get_menu_core_name_list();
if (core_name_list)
{
const char *current_core_name = NULL;
unsigned i;
/* Get currently selected core name */
manual_content_scan_get_menu_core_name(&current_core_name);
/* Loop through names */
for (i = 0; i < core_name_list->size; i++)
{
/* Note: manual_content_scan_get_core_name_list()
* ensures that core_name cannot be empty here */
const char *core_name = core_name_list->elems[i].data;
/* Add menu entry */
if (menu_entries_append_enum(list,
core_name,
"",
MENU_ENUM_LABEL_NO_ITEMS,
MENU_SETTING_DROPDOWN_ITEM_MANUAL_CONTENT_SCAN_CORE_NAME,
i, 0))
count++;
/* Check whether current entry is checked */
if (string_is_equal(current_core_name, core_name))
menu_entries_set_checked(list, i, true);
}
/* Clean up */
string_list_free(core_name_list);
}
}
break;
case DISPLAYLIST_PERFCOUNTERS_CORE:
case DISPLAYLIST_PERFCOUNTERS_FRONTEND:
{
@ -7213,6 +7363,8 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
case DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_LABEL_DISPLAY_MODE:
case DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_RIGHT_THUMBNAIL_MODE:
case DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_LEFT_THUMBNAIL_MODE:
case DISPLAYLIST_DROPDOWN_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
case DISPLAYLIST_DROPDOWN_LIST_MANUAL_CONTENT_SCAN_CORE_NAME:
case DISPLAYLIST_PERFCOUNTERS_CORE:
case DISPLAYLIST_PERFCOUNTERS_FRONTEND:
case DISPLAYLIST_MENU_SETTINGS_LIST:
@ -7238,6 +7390,8 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
case DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_LABEL_DISPLAY_MODE:
case DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_RIGHT_THUMBNAIL_MODE:
case DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_LEFT_THUMBNAIL_MODE:
case DISPLAYLIST_DROPDOWN_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME:
case DISPLAYLIST_DROPDOWN_LIST_MANUAL_CONTENT_SCAN_CORE_NAME:
menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY),
msg_hash_to_str(MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY),
@ -7701,6 +7855,12 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
MENU_SETTING_ACTION, 0, 0))
count++;
#endif
if (menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_LIST),
msg_hash_to_str(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST),
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST,
MENU_SETTING_ACTION, 0, 0))
count++;
if (count == 0)
menu_entries_append_enum(info->list,
@ -8967,6 +9127,18 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
use_filebrowser = true;
break;
case DISPLAYLIST_MANUAL_CONTENT_SCAN_LIST:
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
if (!menu_displaylist_parse_manual_content_scan_list(info))
menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY),
msg_hash_to_str(MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY),
MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY,
FILE_TYPE_NONE, 0, 0);
info->need_push = true;
break;
case DISPLAYLIST_DROPDOWN_LIST:
{
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);

@ -60,6 +60,8 @@ enum menu_displaylist_ctl_state
DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_LABEL_DISPLAY_MODE,
DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_RIGHT_THUMBNAIL_MODE,
DISPLAYLIST_DROPDOWN_LIST_PLAYLIST_LEFT_THUMBNAIL_MODE,
DISPLAYLIST_DROPDOWN_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME,
DISPLAYLIST_DROPDOWN_LIST_MANUAL_CONTENT_SCAN_CORE_NAME,
DISPLAYLIST_CDROM_DETAIL_INFO,
DISPLAYLIST_INFO,
DISPLAYLIST_HELP,
@ -212,6 +214,7 @@ enum menu_displaylist_ctl_state
#if defined(HAVE_LAKKA_SWITCH) || defined(HAVE_LIBNX)
DISPLAYLIST_SWITCH_CPU_PROFILE,
#endif
DISPLAYLIST_MANUAL_CONTENT_SCAN_LIST,
DISPLAYLIST_PENDING_CLEAR
};

@ -91,6 +91,8 @@ enum menu_settings_type
MENU_SETTING_DROPDOWN_ITEM_PLAYLIST_LABEL_DISPLAY_MODE,
MENU_SETTING_DROPDOWN_ITEM_PLAYLIST_RIGHT_THUMBNAIL_MODE,
MENU_SETTING_DROPDOWN_ITEM_PLAYLIST_LEFT_THUMBNAIL_MODE,
MENU_SETTING_DROPDOWN_ITEM_MANUAL_CONTENT_SCAN_SYSTEM_NAME,
MENU_SETTING_DROPDOWN_ITEM_MANUAL_CONTENT_SCAN_CORE_NAME,
MENU_SETTING_DROPDOWN_SETTING_CORE_OPTIONS_ITEM,
MENU_SETTING_DROPDOWN_SETTING_STRING_OPTIONS_ITEM,
MENU_SETTING_DROPDOWN_SETTING_FLOAT_ITEM,
@ -206,6 +208,11 @@ enum menu_settings_type
MENU_SET_CDROM_INFO,
MENU_SETTING_ACTION_DELETE_PLAYLIST,
MENU_SETTING_MANUAL_CONTENT_SCAN_DIR,
MENU_SETTING_MANUAL_CONTENT_SCAN_SYSTEM_NAME,
MENU_SETTING_MANUAL_CONTENT_SCAN_CORE_NAME,
MENU_SETTING_ACTION_MANUAL_CONTENT_SCAN_START,
MENU_SETTINGS_LAST
};

@ -95,6 +95,7 @@
#include "../managers/cheat_manager.h"
#include "../verbosity.h"
#include "../playlist.h"
#include "../manual_content_scan.h"
#include "../tasks/tasks_internal.h"
@ -157,7 +158,8 @@ enum settings_list_type
SETTINGS_LIST_USER_ACCOUNTS_TWITCH,
SETTINGS_LIST_DIRECTORY,
SETTINGS_LIST_PRIVACY,
SETTINGS_LIST_MIDI
SETTINGS_LIST_MIDI,
SETTINGS_LIST_MANUAL_CONTENT_SCAN
};
struct bool_entry
@ -6679,6 +6681,17 @@ void general_write_handler(rarch_setting_t *setting)
}
}
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM:
/* Ensure that custom system name includes no
* invalid characters */
manual_content_scan_scrub_system_name_custom();
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_FILE_EXTS:
/* Ensure that custom file extension list includes
* no period (full stop) characters, and converts
* string to lower case */
manual_content_scan_scrub_file_exts_custom();
break;
default:
break;
}
@ -16430,6 +16443,63 @@ static bool setting_append_list(
(*list)[list_info->index - 1].action_ok = &setting_action_ok_uint;
menu_settings_list_current_add_range(list, list_info, 0.0f, 100.0f, 1.0f, true, true);
END_SUB_GROUP(list, list_info, parent_group);
END_GROUP(list, list_info, parent_group);
break;
case SETTINGS_LIST_MANUAL_CONTENT_SCAN:
START_GROUP(list, list_info, &group_info,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_LIST), parent_group);
parent_group = msg_hash_to_str(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST);
START_SUB_GROUP(list, list_info, "State",
&group_info, &subgroup_info, parent_group);
CONFIG_STRING(
list, list_info,
manual_content_scan_get_system_name_custom_ptr(),
manual_content_scan_get_system_name_custom_size(),
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM,
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM,
"",
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler);
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT);
(*list)[list_info->index - 1].ui_type = ST_UI_TYPE_STRING_LINE_EDIT;
CONFIG_STRING(
list, list_info,
manual_content_scan_get_file_exts_custom_ptr(),
manual_content_scan_get_file_exts_custom_size(),
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_FILE_EXTS,
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_FILE_EXTS,
"",
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler);
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT);
(*list)[list_info->index - 1].ui_type = ST_UI_TYPE_STRING_LINE_EDIT;
CONFIG_BOOL(
list, list_info,
manual_content_scan_get_overwrite_playlist_ptr(),
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_OVERWRITE,
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_OVERWRITE,
false,
MENU_ENUM_LABEL_VALUE_OFF,
MENU_ENUM_LABEL_VALUE_ON,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler,
SD_FLAG_NONE);
END_SUB_GROUP(list, list_info, parent_group);
END_GROUP(list, list_info, parent_group);
break;
@ -16566,7 +16636,8 @@ static rarch_setting_t *menu_setting_new_internal(rarch_setting_info_t *list_inf
SETTINGS_LIST_USER_ACCOUNTS_TWITCH,
SETTINGS_LIST_DIRECTORY,
SETTINGS_LIST_PRIVACY,
SETTINGS_LIST_MIDI
SETTINGS_LIST_MIDI,
SETTINGS_LIST_MANUAL_CONTENT_SCAN
};
const char *root = msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU);
rarch_setting_t *list = (rarch_setting_t*)calloc(

@ -127,6 +127,14 @@ void filebrowser_parse(menu_displaylist_info_t *info, unsigned type_data)
FILE_TYPE_SCAN_DIRECTORY, 0 ,0);
#endif
break;
case FILEBROWSER_MANUAL_SCAN_DIR:
if (info)
menu_entries_prepend(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCAN_THIS_DIRECTORY),
msg_hash_to_str(MENU_ENUM_LABEL_SCAN_THIS_DIRECTORY),
MENU_ENUM_LABEL_SCAN_THIS_DIRECTORY,
FILE_TYPE_MANUAL_SCAN_DIRECTORY, 0 ,0);
break;
case FILEBROWSER_SELECT_DIR:
if (info)
menu_entries_prepend(info->list,
@ -212,6 +220,8 @@ void filebrowser_parse(menu_displaylist_info_t *info, unsigned type_data)
continue;
if (filebrowser_types == FILEBROWSER_SCAN_DIR)
continue;
if (filebrowser_types == FILEBROWSER_MANUAL_SCAN_DIR)
continue;
}
/* Need to preserve slash first time. */

@ -32,6 +32,7 @@ enum filebrowser_enums
FILEBROWSER_SELECT_DIR,
FILEBROWSER_SCAN_DIR,
FILEBROWSER_SCAN_FILE,
FILEBROWSER_MANUAL_SCAN_DIR,
FILEBROWSER_SELECT_FILE,
FILEBROWSER_SELECT_FILE_SUBSYSTEM,
FILEBROWSER_SELECT_IMAGE,

@ -151,6 +151,8 @@ enum msg_file_type
* menu_cbs_init_bind_get_string_representation_compare_type() breaks... */
FILE_TYPE_DOWNLOAD_PL_THUMBNAIL_CONTENT,
FILE_TYPE_MANUAL_SCAN_DIRECTORY,
FILE_TYPE_LAST
};
@ -1245,6 +1247,8 @@ enum msg_hash_enums
MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_PLAYLIST_LABEL_DISPLAY_MODE,
MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_PLAYLIST_RIGHT_THUMBNAIL_MODE,
MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_PLAYLIST_LEFT_THUMBNAIL_MODE,
MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_SYSTEM_NAME,
MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MANUAL_CONTENT_SCAN_CORE_NAME,
MENU_ENUM_LABEL_DEFERRED_MIXER_STREAM_SETTINGS_LIST,
MENU_ENUM_LABEL_DEFERRED_CONFIGURATIONS_LIST,
MENU_ENUM_LABEL_DEFERRED_FAVORITES_LIST,
@ -1343,6 +1347,7 @@ enum msg_hash_enums
MENU_ENUM_LABEL_DEFERRED_ACCOUNTS_YOUTUBE_LIST,
MENU_ENUM_LABEL_DEFERRED_ACCOUNTS_LIST,
MENU_ENUM_LABEL_DEFERRED_INFORMATION,
MENU_ENUM_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST,
MENU_LABEL(FILE_DETECT_CORE_LIST_PUSH_DIR),
MENU_LABEL(DOWNLOADED_FILE_DETECT_CORE_LIST),
@ -2658,6 +2663,27 @@ enum msg_hash_enums
MSG_NO_DISC_INSERTED,
MENU_LABEL(DELETE_PLAYLIST),
/* Manual content scan */
MENU_LABEL(MANUAL_CONTENT_SCAN_LIST),
MENU_LABEL(MANUAL_CONTENT_SCAN_DIR),
MENU_LABEL(MANUAL_CONTENT_SCAN_SYSTEM_NAME),
MENU_LABEL(MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM),
MENU_LABEL(MANUAL_CONTENT_SCAN_CORE_NAME),
MENU_LABEL(MANUAL_CONTENT_SCAN_FILE_EXTS),
MENU_LABEL(MANUAL_CONTENT_SCAN_OVERWRITE),
MENU_LABEL(MANUAL_CONTENT_SCAN_START),
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CONTENT_DIR,
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CUSTOM,
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_CORE_NAME_DETECT,
MSG_MANUAL_CONTENT_SCAN_INVALID_CONFIG,
MSG_MANUAL_CONTENT_SCAN_INVALID_CONTENT,
MSG_MANUAL_CONTENT_SCAN_START,
MSG_MANUAL_CONTENT_SCAN_IN_PROGRESS,
MSG_MANUAL_CONTENT_SCAN_END,
MSG_LAST
};
@ -2715,6 +2741,7 @@ enum msg_hash_enums
#define MENU_LABEL_DEFERRED_CONFIGURATIONS_LIST 0x679a1b0bU
#define MENU_LABEL_DEFERRED_BROWSE_URL_START 0xcef58296U
#define MENU_LABEL_DEFERRED_INFORMATION 0x3FCC9F2BU
#define MENU_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST 0x479546DCU
/* Cheevos settings */
@ -2910,6 +2937,11 @@ enum msg_hash_enums
#define MENU_LABEL_HELP_CHANGE_VIRTUAL_GAMEPAD 0x6e66ef07U
#define MENU_LABEL_HELP_AUDIO_VIDEO_TROUBLESHOOTING 0xd44d395cU
/* Manual content scan */
#define MENU_LABEL_MANUAL_CONTENT_SCAN_DIR 0x6674149FU
#define MENU_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME 0xA3EC34C5U
#define MENU_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME 0xD13B7849U
/* Main menu */
#define MENU_LABEL_LOAD_CONTENT_LIST 0x5745de1fU
#define MENU_LABEL_LOAD_CONTENT_HISTORY 0xfe1d79e5U

@ -368,8 +368,7 @@ void playlist_get_index_by_path(playlist_t *playlist,
}
bool playlist_entry_exists(playlist_t *playlist,
const char *path,
const char *crc32)
const char *path)
{
size_t i;
char real_search_path[PATH_MAX_LENGTH];

@ -214,8 +214,7 @@ void playlist_get_index_by_path(playlist_t *playlist,
const struct playlist_entry **entry);
bool playlist_entry_exists(playlist_t *playlist,
const char *path,
const char *crc32);
const char *path);
char *playlist_get_conf_path(playlist_t *playlist);

@ -850,7 +850,7 @@ static int database_info_list_iterate_found_match(
fprintf(stderr, "entry path str: %s\n", entry_path_str);
#endif
if (!playlist_entry_exists(playlist, entry_path_str, db_crc))
if (!playlist_entry_exists(playlist, entry_path_str))
{
struct playlist_entry entry;
@ -1052,8 +1052,7 @@ static int task_database_iterate_playlist_lutro(
free(db_playlist_path);
if (!playlist_entry_exists(playlist,
path, "DETECT"))
if (!playlist_entry_exists(playlist, path))
{
struct playlist_entry entry;
char *game_title = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));

@ -0,0 +1,354 @@
/* 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
*
* 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 <lists/string_list.h>
#include <file/file_path.h>
#include "tasks_internal.h"
#include "../retroarch.h"
#include "../msg_hash.h"
#include "../playlist.h"
#include "../manual_content_scan.h"
#ifdef RARCH_INTERNAL
#ifdef HAVE_MENU
#include "../menu/menu_driver.h"
#endif
#endif
enum manual_scan_status
{
MANUAL_SCAN_BEGIN = 0,
MANUAL_SCAN_ITERATE_CONTENT,
MANUAL_SCAN_END
};
typedef struct manual_scan_handle
{
manual_content_scan_task_config_t *task_config;
playlist_t *playlist;
struct string_list *content_list;
size_t list_size;
size_t list_index;
enum manual_scan_status status;
} manual_scan_handle_t;
/* Frees task handle + all constituent objects */
static void free_manual_content_scan_handle(manual_scan_handle_t *manual_scan)
{
if (!manual_scan)
return;
if (manual_scan->task_config)
{
free(manual_scan->task_config);
manual_scan->task_config = NULL;
}
if (manual_scan->playlist)
{
playlist_free(manual_scan->playlist);
manual_scan->playlist = NULL;
}
if (manual_scan->content_list)
{
string_list_free(manual_scan->content_list);
manual_scan->content_list = NULL;
}
free(manual_scan);
manual_scan = NULL;
}
static void task_manual_content_scan_handler(retro_task_t *task)
{
manual_scan_handle_t *manual_scan = NULL;
if (!task)
goto task_finished;
manual_scan = (manual_scan_handle_t*)task->state;
if (!manual_scan)
goto task_finished;
if (task_get_cancelled(task))
goto task_finished;
switch (manual_scan->status)
{
case MANUAL_SCAN_BEGIN:
{
/* Get content list */
manual_scan->content_list = manual_content_scan_get_content_list(
manual_scan->task_config);
if (!manual_scan->content_list)
{
runloop_msg_queue_push(
msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_INVALID_CONTENT),
1, 100, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
goto task_finished;
}
manual_scan->list_size = manual_scan->content_list->size;
/* Open playlist */
manual_scan->playlist = playlist_init(
manual_scan->task_config->playlist_file, COLLECTION_SIZE);
if (!manual_scan->playlist)
goto task_finished;
/* Reset playlist, if required */
if (manual_scan->task_config->overwrite_playlist)
playlist_clear(manual_scan->playlist);
/* Set default core, if required */
if (manual_scan->task_config->core_set)
{
playlist_set_default_core_path(
manual_scan->playlist, manual_scan->task_config->core_path);
playlist_set_default_core_name(
manual_scan->playlist, manual_scan->task_config->core_name);
}
/* All good - can start iterating */
manual_scan->status = MANUAL_SCAN_ITERATE_CONTENT;
}
break;
case MANUAL_SCAN_ITERATE_CONTENT:
{
const char *content_path =
manual_scan->content_list->elems[manual_scan->list_index].data;
if (!string_is_empty(content_path))
{
const char *content_file = path_basename(content_path);
char task_title[PATH_MAX_LENGTH];
task_title[0] = '\0';
/* Update progress display */
task_free_title(task);
strlcpy(
task_title, msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_IN_PROGRESS),
sizeof(task_title));
if (!string_is_empty(content_file))
strlcat(task_title, content_file, sizeof(task_title));
task_set_title(task, strdup(task_title));
task_set_progress(task, (manual_scan->list_index * 100) / manual_scan->list_size);
/* Add content to playlist */
manual_content_scan_add_content_to_playlist(
manual_scan->task_config, manual_scan->playlist,
content_path);
}
/* Increment content index */
manual_scan->list_index++;
if (manual_scan->list_index >= manual_scan->list_size)
manual_scan->status = MANUAL_SCAN_END;
}
break;
case MANUAL_SCAN_END:
{
playlist_t *cached_playlist = playlist_get_cached();
char task_title[PATH_MAX_LENGTH];
task_title[0] = '\0';
/* Ensure playlist is alphabetically sorted */
playlist_qsort(manual_scan->playlist);
/* Save playlist changes to disk */
playlist_write_file(manual_scan->playlist);
/* If this is the currently cached playlist, then
* it must be re-cached (otherwise changes will be
* lost if the currently cached playlist is saved
* to disk for any reason...) */
if (cached_playlist)
{
if (string_is_equal(
manual_scan->task_config->playlist_file,
playlist_get_conf_path(cached_playlist)))
{
playlist_free_cached();
playlist_init_cached(
manual_scan->task_config->playlist_file, COLLECTION_SIZE);
}
}
/* Update progress display */
task_free_title(task);
strlcpy(
task_title, msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_END),
sizeof(task_title));
strlcat(task_title, manual_scan->task_config->system_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_manual_content_scan_handle(manual_scan);
}
static bool task_manual_content_scan_finder(retro_task_t *task, void *user_data)
{
manual_scan_handle_t *manual_scan = NULL;
if (!task || !user_data)
return false;
if (task->handler != task_manual_content_scan_handler)
return false;
manual_scan = (manual_scan_handle_t*)task->state;
if (!manual_scan)
return false;
return string_is_equal(
(const char*)user_data, manual_scan->task_config->playlist_file);
}
static void cb_task_manual_content_scan_refresh_menu(
retro_task_t *task, void *task_data,
void *user_data, const char *err)
{
#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
menu_ctx_environment_t menu_environ;
menu_environ.type = MENU_ENVIRON_RESET_HORIZONTAL_LIST;
menu_environ.data = NULL;
menu_driver_ctl(RARCH_MENU_CTL_ENVIRONMENT, &menu_environ);
#endif
}
bool task_push_manual_content_scan(void)
{
task_finder_data_t find_data;
char task_title[PATH_MAX_LENGTH];
retro_task_t *task = NULL;
manual_scan_handle_t *manual_scan = (manual_scan_handle_t*)
calloc(1, sizeof(manual_scan_handle_t));
task_title[0] = '\0';
/* Sanity check */
if (!manual_scan)
goto error;
/* Configure handle */
manual_scan->task_config = NULL;
manual_scan->playlist = NULL;
manual_scan->content_list = NULL;
manual_scan->list_size = 0;
manual_scan->list_index = 0;
manual_scan->status = MANUAL_SCAN_BEGIN;
/* > Get current manual content scan configuration */
manual_scan->task_config = (manual_content_scan_task_config_t*)
calloc(1, sizeof(manual_content_scan_task_config_t));
if (!manual_scan->task_config)
goto error;
if (!manual_content_scan_get_task_config(manual_scan->task_config))
{
runloop_msg_queue_push(
msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_INVALID_CONFIG),
1, 100, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
goto error;
}
/* Concurrent scanning of content to the same
* playlist is not allowed */
find_data.func = task_manual_content_scan_finder;
find_data.userdata = (void*)manual_scan->task_config->playlist_file;
if (task_queue_find(&find_data))
goto error;
/* Create task */
task = task_init();
if (!task)
goto error;
/* > Get task title */
strlcpy(
task_title, msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_START),
sizeof(task_title));
strlcat(task_title, manual_scan->task_config->system_name,
sizeof(task_title));
/* > Configure task */
task->handler = task_manual_content_scan_handler;
task->state = manual_scan;
task->title = strdup(task_title);
task->alternative_look = true;
task->progress = 0;
task->callback = cb_task_manual_content_scan_refresh_menu;
/* > Push task */
task_queue_push(task);
return true;
error:
/* Clean up task */
if (task)
{
free(task);
task = NULL;
}
/* Clean up handle */
free_manual_content_scan_handle(manual_scan);
manual_scan = NULL;
return false;
}

@ -98,6 +98,8 @@ bool task_push_dbscan(
retro_task_callback_t cb);
#endif
bool task_push_manual_content_scan(void);
#ifdef HAVE_OVERLAY
bool task_push_overlay_load_default(
retro_task_callback_t cb,