From 1cf52a0dee837588a2de3c023a35b9b02be154fd Mon Sep 17 00:00:00 2001 From: zoltanvb <101990835+zoltanvb@users.noreply.github.com> Date: Sat, 29 Nov 2025 18:15:13 +0100 Subject: [PATCH] Rearrange scanning related functions (#18458) --- Makefile.common | 1 - griffin/griffin.c | 3 +- tasks/task_database.c | 949 +++++++++++++++++++------------ tasks/task_database_cue.c | 349 ++++++++++++ tasks/task_database_cue.h | 22 + tasks/task_manual_content_scan.c | 629 -------------------- 6 files changed, 971 insertions(+), 982 deletions(-) delete mode 100644 tasks/task_manual_content_scan.c diff --git a/Makefile.common b/Makefile.common index 57dabf5eac..90c760effe 100644 --- a/Makefile.common +++ b/Makefile.common @@ -278,7 +278,6 @@ OBJ += \ tasks/task_file_transfer.o \ tasks/task_image.o \ tasks/task_playlist_manager.o \ - tasks/task_manual_content_scan.o \ tasks/task_core_backup.o \ $(LIBRETRO_COMM_DIR)/encodings/encoding_utf.o \ $(LIBRETRO_COMM_DIR)/encodings/encoding_crc32.o \ diff --git a/griffin/griffin.c b/griffin/griffin.c index b4942b34ca..6bfa52a801 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1310,7 +1310,6 @@ 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" #include "../tasks/task_core_backup.c" #ifdef HAVE_TRANSLATE #include "../tasks/task_translation.c" @@ -1318,8 +1317,8 @@ DATA RUNLOOP #ifdef HAVE_ZLIB #include "../tasks/task_decompress.c" #endif -#ifdef HAVE_LIBRETRODB #include "../tasks/task_database.c" +#ifdef HAVE_LIBRETRODB #include "../tasks/task_database_cue.c" #endif #if defined(HAVE_NETWORKING) && defined(HAVE_MENU) diff --git a/tasks/task_database.c b/tasks/task_database.c index e05023d758..2dba3965d8 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -22,15 +22,17 @@ #include #include #include +#include #include +#include +#include #include -#include -#include #include #include "tasks_internal.h" #include "../core_info.h" #include "../database_info.h" +#include "../manual_content_scan.h" #include "../file_path_special.h" #include "../msg_hash.h" @@ -39,6 +41,10 @@ #include "../configuration.h" #include "../ui/ui_companion_driver.h" #include "../gfx/video_display_server.h" +#ifdef HAVE_MENU +#include "../menu/menu_driver.h" +#endif +#include "../runloop.h" #endif #include "../retroarch.h" #include "../verbosity.h" @@ -93,6 +99,34 @@ typedef struct db_handle uint8_t flags; } db_handle_t; +enum manual_scan_status +{ + MANUAL_SCAN_BEGIN = 0, + MANUAL_SCAN_ITERATE_CLEAN, + MANUAL_SCAN_ITERATE_CONTENT, + MANUAL_SCAN_ITERATE_M3U, + MANUAL_SCAN_END +}; + +typedef struct manual_scan_handle +{ + manual_content_scan_task_config_t *task_config; + playlist_t *playlist; + struct string_list *file_exts_list; + struct string_list *content_list; + logiqx_dat_t *dat_file; + struct string_list *m3u_list; + playlist_config_t playlist_config; /* size_t alignment */ + size_t playlist_size; + size_t playlist_index; + size_t content_list_size; + size_t content_list_index; + size_t m3u_index; + enum manual_scan_status status; +} manual_scan_handle_t; + +#ifdef HAVE_LIBRETRODB + static const char *database_info_get_current_name( database_state_handle_t *handle) { @@ -217,354 +251,6 @@ static int task_database_iterate_start(retro_task_t *task, return 0; } -static int intfstream_get_serial(intfstream_t *fd, char *s, size_t len, const char *filename) -{ - const char *system_name = NULL; - if (detect_system(fd, &system_name, filename) >= 1) - { - size_t system_len = strlen(system_name); - if (string_starts_with_size(system_name, "Sony", STRLEN_CONST("Sony"))) - { - if ( STRLEN_CONST("Sony - PlayStation Portable") == system_len - && memcmp(system_name, "Sony - PlayStation Portable", system_len) == 0) - { - if (detect_psp_game(fd, s, len, filename) != 0) - return 1; - } - else if ( STRLEN_CONST("Sony - PlayStation") == system_len - && memcmp(system_name, "Sony - PlayStation", system_len) == 0) - { - if (detect_ps1_game(fd, s, len, filename) != 0) - return 1; - } - else if ( STRLEN_CONST("Sony - PlayStation 2") == system_len - && memcmp(system_name, "Sony - PlayStation 2", system_len) == 0) - { - if (detect_ps2_game(fd, s, len, filename) != 0) - return 1; - } - } - else if (string_starts_with_size(system_name, "Nintendo", STRLEN_CONST("Nintendo"))) - { - if ( STRLEN_CONST("Nintendo - GameCube") == system_len - && memcmp(system_name, "Nintendo - GameCube", system_len) == 0) - { - if (detect_gc_game(fd, s, len, filename) != 0) - return 1; - } - else if ( STRLEN_CONST("Nintendo - Wii") == system_len - && memcmp(system_name, "Nintendo - Wii", system_len) == 0) - { - if (detect_wii_game(fd, s, len, filename) != 0) - return 1; - } - } - else if (string_starts_with_size(system_name, "Sega", STRLEN_CONST("Sega"))) - { - if ( STRLEN_CONST("Sega - Mega-CD - Sega CD") == system_len - && memcmp(system_name, "Sega - Mega-CD - Sega CD", system_len) == 0) - { - if (detect_scd_game(fd, s, len, filename) != 0) - return 1; - } - else if ( STRLEN_CONST("Sega - Saturn") == system_len - && memcmp(system_name, "Sega - Saturn", system_len) == 0) - { - if (detect_sat_game(fd, s, len, filename) != 0) - return 1; - } - else if ( STRLEN_CONST("Sega - Dreamcast") == system_len - && memcmp(system_name, "Sega - Dreamcast", system_len) == 0) - { - if (detect_dc_game(fd, s, len, filename) != 0) - return 1; - } - } - /* Philips CD-i has no serial entry on disc, use default fallback to CRC */ - } - return 0; -} - -static bool intfstream_file_get_serial(const char *name, - uint64_t offset, int64_t size, char *s, size_t len, uint64_t *fsize) -{ - int rv; - uint8_t *data = NULL; - int64_t file_size = -1; - intfstream_t *fd = intfstream_open_file(name, - RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); - - if (!fd) - return 0; - - if (intfstream_seek(fd, 0, SEEK_END) == -1) - goto error; - - file_size = intfstream_tell(fd); - *fsize = file_size; - - if (intfstream_seek(fd, 0, SEEK_SET) == -1) - goto error; - - if (file_size < 0) - goto error; - - if (offset != 0 || size < file_size) - { - if (intfstream_seek(fd, (int64_t)offset, SEEK_SET) == -1) - goto error; - - data = (uint8_t*)malloc(size); - - if (intfstream_read(fd, data, size) != (int64_t) size) - { - free(data); - goto error; - } - - intfstream_close(fd); - free(fd); - if (!(fd = intfstream_open_memory(data, RETRO_VFS_FILE_ACCESS_READ, - RETRO_VFS_FILE_ACCESS_HINT_NONE, - size))) - { - free(data); - return 0; - } - } - - rv = intfstream_get_serial(fd, s, len, name); - intfstream_close(fd); - free(fd); - free(data); - return rv; - -error: - intfstream_close(fd); - free(fd); - return 0; -} - -static int task_database_cue_get_serial(const char *name, char *s, size_t len, uint64_t *filesize) -{ - char track_path[PATH_MAX_LENGTH]; - uint64_t offset = 0; - size_t _len = 0; - - track_path[0] = '\0'; - - if (cue_find_track(name, true, &offset, &_len, - track_path, sizeof(track_path)) < 0) - { -#ifdef DEBUG - RARCH_LOG("[Scanner] %s\n", - msg_hash_to_str(MSG_COULD_NOT_FIND_VALID_DATA_TRACK)); -#endif - return 0; - } - - return intfstream_file_get_serial(track_path, offset, _len, s, len, filesize); -} - -static int task_database_gdi_get_serial(const char *name, char *s, size_t len, uint64_t *filesize) -{ - char track_path[PATH_MAX_LENGTH]; - - track_path[0] = '\0'; - - if (gdi_find_track(name, true, - track_path, sizeof(track_path)) < 0) - { -#ifdef DEBUG - RARCH_LOG("[Scanner] %s\n", - msg_hash_to_str(MSG_COULD_NOT_FIND_VALID_DATA_TRACK)); -#endif - return 0; - } - - return intfstream_file_get_serial(track_path, 0, INT64_MAX, s, len, filesize); -} - -/* Helper function to detect if a CHD file is a CD-i disc - * CD-i discs store data in AUDIO-labeled tracks, so we need - * to explicitly open track 1 for scanning */ -static bool is_chd_file_cdi(const char *path) -{ - intfstream_t *fd; - uint8_t magic[12]; - bool is_cdi = false; - const uint8_t cdi_magic[] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}; - - /* Try to open track 1 explicitly */ - fd = intfstream_open_chd_track(path, - RETRO_VFS_FILE_ACCESS_READ, - RETRO_VFS_FILE_ACCESS_HINT_NONE, - 1); /* Explicit track number, not CHDSTREAM_TRACK_FIRST_DATA */ - - if (!fd) - return false; - - /* Read and check CD-i magic bytes at offset 0 */ - if (intfstream_read(fd, magic, sizeof(magic)) == sizeof(magic)) - is_cdi = (memcmp(magic, cdi_magic, sizeof(cdi_magic)) == 0); - - intfstream_close(fd); - free(fd); - return is_cdi; -} - -static int task_database_chd_get_serial(const char *name, char *serial, size_t len, uint64_t *filesize) -{ - int result; - int32_t track; - intfstream_t *fd; - - /* CD-i discs store data in AUDIO-labeled tracks, so we must - * explicitly open track 1 instead of using CHDSTREAM_TRACK_FIRST_DATA */ - track = is_chd_file_cdi(name) ? 1 : CHDSTREAM_TRACK_FIRST_DATA; - - fd = intfstream_open_chd_track( - name, - RETRO_VFS_FILE_ACCESS_READ, - RETRO_VFS_FILE_ACCESS_HINT_NONE, - track); - if (!fd) - return 0; - - *filesize = intfstream_get_size(fd); /* TODO: get the full CHD size instead */ - result = intfstream_get_serial(fd, serial, len, name); - intfstream_close(fd); - free(fd); - return result; -} - -static bool intfstream_file_get_crc_and_size(const char *name, - uint64_t offset, int64_t len, uint32_t *crc, uint64_t *size) -{ - bool rv; - intfstream_t *fd = intfstream_open_file(name, - RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); - uint8_t *data = NULL; - int64_t file_size = -1; - - if (!fd) - return 0; - - if (intfstream_seek(fd, 0, SEEK_END) == -1) - goto error; - - file_size = intfstream_tell(fd); - *size = file_size; - - if (intfstream_seek(fd, 0, SEEK_SET) == -1) - goto error; - - if (file_size < 0) - goto error; - - if (offset != 0 || len < file_size) - { - if (intfstream_seek(fd, (int64_t)offset, SEEK_SET) == -1) - goto error; - - data = (uint8_t*)malloc(len); - - if (intfstream_read(fd, data, len) != (int64_t)len) - goto error; - - intfstream_close(fd); - free(fd); - fd = intfstream_open_memory(data, RETRO_VFS_FILE_ACCESS_READ, - RETRO_VFS_FILE_ACCESS_HINT_NONE, len); - - if (!fd) - goto error; - } - - rv = intfstream_get_crc(fd, crc); - intfstream_close(fd); - free(fd); - free(data); - return rv; - -error: - if (fd) - { - intfstream_close(fd); - free(fd); - } - if (data) - free(data); - return 0; -} - -static int task_database_cue_get_crc_and_size(const char *name, uint32_t *crc, uint64_t *size) -{ - char track_path[PATH_MAX_LENGTH]; - uint64_t offset = 0; - size_t _len = 0; - - track_path[0] = '\0'; - - if (cue_find_track(name, false, &offset, &_len, - track_path, sizeof(track_path)) < 0) - { -#ifdef DEBUG - RARCH_LOG("[Scanner] %s\n", - msg_hash_to_str(MSG_COULD_NOT_FIND_VALID_DATA_TRACK)); -#endif - return 0; - } - return intfstream_file_get_crc_and_size(track_path, offset, _len, crc, size); -} - -static int task_database_gdi_get_crc_and_size(const char *name, uint32_t *crc, uint64_t *size) -{ - char track_path[PATH_MAX_LENGTH]; - - track_path[0] = '\0'; - - if (gdi_find_track(name, true, - track_path, sizeof(track_path)) < 0) - { -#ifdef DEBUG - RARCH_LOG("[Scanner] %s\n", - msg_hash_to_str(MSG_COULD_NOT_FIND_VALID_DATA_TRACK)); -#endif - return 0; - } - return intfstream_file_get_crc_and_size(track_path, 0, INT64_MAX, crc, size); -} - -static bool task_database_chd_get_crc_and_size(const char *name, uint32_t *crc, uint64_t *size) -{ - bool found_crc = false; - int32_t track; - intfstream_t *fd; - - /* CD-i discs store data in AUDIO-labeled tracks, so we must - * explicitly open track 1 instead of using CHDSTREAM_TRACK_PRIMARY */ - track = is_chd_file_cdi(name) ? 1 : CHDSTREAM_TRACK_PRIMARY; - - fd = intfstream_open_chd_track( - name, - RETRO_VFS_FILE_ACCESS_READ, - RETRO_VFS_FILE_ACCESS_HINT_NONE, - track); - if (!fd) - return 0; - - *size = intfstream_get_size(fd); - found_crc = intfstream_get_crc(fd, crc); - if (fd) - { - intfstream_close(fd); - free(fd); - } - return found_crc; -} - static void task_database_cue_prune(database_info_handle_t *db, const char *name) { @@ -1708,3 +1394,566 @@ error: free(db); return false; } + +#endif + +/* 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->file_exts_list) + { + string_list_free(manual_scan->file_exts_list); + manual_scan->file_exts_list = NULL; + } + + if (manual_scan->content_list) + { + string_list_free(manual_scan->content_list); + manual_scan->content_list = NULL; + } + + if (manual_scan->m3u_list) + { + string_list_free(manual_scan->m3u_list); + manual_scan->m3u_list = NULL; + } + + if (manual_scan->dat_file) + { + logiqx_dat_free(manual_scan->dat_file); + manual_scan->dat_file = NULL; + } + + free(manual_scan); + manual_scan = NULL; +} + +static void cb_task_manual_content_scan( + retro_task_t *task, void *task_data, + void *user_data, const char *err) +{ + manual_scan_handle_t *manual_scan = NULL; + playlist_t *cached_playlist = playlist_get_cached(); +#if defined(RARCH_INTERNAL) && defined(HAVE_MENU) + struct menu_state *menu_st = menu_state_get_ptr(); + if (!task) + goto end; +#else + if (!task) + return; +#endif + + if (!(manual_scan = (manual_scan_handle_t*)task->state)) + { +#if defined(RARCH_INTERNAL) && defined(HAVE_MENU) + goto end; +#else + return; +#endif + } + + /* If the manual content scan task has modified 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->playlist_config.path, + playlist_get_conf_path(cached_playlist))) + { + playlist_config_t playlist_config; + + /* Copy configuration of cached playlist + * (could use manual_scan->playlist_config, + * but doing it this way guarantees that + * the cached playlist is preserved in + * its original state) */ + if (playlist_config_copy( + playlist_get_config(cached_playlist), + &playlist_config)) + { + playlist_free_cached(); + playlist_init_cached(&playlist_config); + } + } + } + +#if defined(RARCH_INTERNAL) && defined(HAVE_MENU) +end: + /* When creating playlists, the playlist tabs of + * any active menu driver must be refreshed */ + if (menu_st->driver_ctx->environ_cb) + menu_st->driver_ctx->environ_cb(MENU_ENVIRON_RESET_HORIZONTAL_LIST, + NULL, menu_st->userdata); +#endif +} + +static void task_manual_content_scan_free(retro_task_t *task) +{ + manual_scan_handle_t *manual_scan = NULL; + + if (!task) + return; + + manual_scan = (manual_scan_handle_t*)task->state; + + free_manual_content_scan_handle(manual_scan); +} + +static void task_manual_content_scan_handler(retro_task_t *task) +{ + uint8_t flg; + manual_scan_handle_t *manual_scan = NULL; + + if (!task) + goto task_finished; + + if (!(manual_scan = (manual_scan_handle_t*)task->state)) + goto task_finished; + + flg = task_get_flags(task); + + if ((flg & RETRO_TASK_FLG_CANCELLED) > 0) + goto task_finished; + + switch (manual_scan->status) + { + case MANUAL_SCAN_BEGIN: + { + /* Get allowed file extensions list */ + if (!string_is_empty(manual_scan->task_config->file_exts)) + manual_scan->file_exts_list = string_split( + manual_scan->task_config->file_exts, "|"); + + /* Get content list */ + if (!(manual_scan->content_list + = manual_content_scan_get_content_list( + manual_scan->task_config))) + { + const char *_msg = msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_INVALID_CONTENT); + runloop_msg_queue_push(_msg, strlen(_msg), 1, 100, true, NULL,\ + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + goto task_finished; + } + + manual_scan->content_list_size = manual_scan->content_list->size; + + /* Load DAT file, if required */ + if (!string_is_empty(manual_scan->task_config->dat_file_path)) + { + if (!(manual_scan->dat_file = + logiqx_dat_init( + manual_scan->task_config->dat_file_path))) + { + const char *_msg = msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_DAT_FILE_LOAD_ERROR); + runloop_msg_queue_push(_msg, strlen(_msg), 1, 100, true, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + goto task_finished; + } + } + + /* Open playlist */ + if (!(manual_scan->playlist = + playlist_init(&manual_scan->playlist_config))) + goto task_finished; + + /* Reset playlist, if required */ + if (manual_scan->task_config->overwrite_playlist) + playlist_clear(manual_scan->playlist); + + /* Get initial playlist size */ + manual_scan->playlist_size = + playlist_size(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); + } + + /* Record remaining scan parameters to enable + * subsequent 'refresh playlist' operations */ + playlist_set_scan_content_dir(manual_scan->playlist, + manual_scan->task_config->content_dir); + playlist_set_scan_file_exts(manual_scan->playlist, + manual_scan->task_config->file_exts_custom_set ? + manual_scan->task_config->file_exts : NULL); + playlist_set_scan_dat_file_path(manual_scan->playlist, + manual_scan->task_config->dat_file_path); + playlist_set_scan_search_recursively(manual_scan->playlist, + manual_scan->task_config->search_recursively); + playlist_set_scan_search_archives(manual_scan->playlist, + manual_scan->task_config->search_archives); + playlist_set_scan_filter_dat_content(manual_scan->playlist, + manual_scan->task_config->filter_dat_content); + playlist_set_scan_overwrite_playlist(manual_scan->playlist, + manual_scan->task_config->overwrite_playlist); + + /* All good - can start iterating + * > If playlist has content and 'validate + * entries' is enabled, go to clean-up phase + * > Otherwise go straight to content scan phase */ + if (manual_scan->task_config->validate_entries && + (manual_scan->playlist_size > 0)) + manual_scan->status = MANUAL_SCAN_ITERATE_CLEAN; + else + manual_scan->status = MANUAL_SCAN_ITERATE_CONTENT; + } + break; + case MANUAL_SCAN_ITERATE_CLEAN: + { + const struct playlist_entry *entry = NULL; + bool delete_entry = false; + + /* Get current entry */ + playlist_get_index(manual_scan->playlist, + manual_scan->playlist_index, &entry); + + if (entry) + { + size_t _len; + const char *entry_file = NULL; + const char *entry_file_ext = NULL; + char task_title[128]; + + /* Update progress display */ + task_free_title(task); + + _len = strlcpy(task_title, + msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_PLAYLIST_CLEANUP), + sizeof(task_title)); + + if ( !string_is_empty(entry->path) + && (entry_file = path_basename(entry->path))) + strlcpy(task_title + _len, + entry_file, + sizeof(task_title) - _len); + + task_set_title(task, strdup(task_title)); + task_set_progress(task, (manual_scan->playlist_index * 100) / + manual_scan->playlist_size); + + /* Check whether playlist content exists on + * the filesystem */ + if (!playlist_content_path_is_valid(entry->path)) + delete_entry = true; + /* If file exists, check whether it has a + * permitted file extension */ + else if ( manual_scan->file_exts_list + && (entry_file_ext = path_get_extension(entry->path)) + && !string_list_find_elem_prefix( + manual_scan->file_exts_list, + ".", entry_file_ext)) + delete_entry = true; + + if (delete_entry) + { + /* Invalid content - delete entry */ + playlist_delete_index(manual_scan->playlist, + manual_scan->playlist_index); + + /* Update playlist_size */ + manual_scan->playlist_size = playlist_size(manual_scan->playlist); + } + } + + /* Increment entry index *if* current entry still + * exists (i.e. if entry was deleted, current index + * will already point to the *next* entry) */ + if (!delete_entry) + manual_scan->playlist_index++; + + if (manual_scan->playlist_index >= + manual_scan->playlist_size) + manual_scan->status = MANUAL_SCAN_ITERATE_CONTENT; + } + break; + case MANUAL_SCAN_ITERATE_CONTENT: + { + const char *content_path = manual_scan->content_list->elems[ + manual_scan->content_list_index].data; + int content_type = manual_scan->content_list->elems[ + manual_scan->content_list_index].attr.i; + + if (!string_is_empty(content_path)) + { + size_t _len; + char task_title[128]; + const char *content_file = path_basename(content_path); + + /* Update progress display */ + task_free_title(task); + + _len = strlcpy(task_title, + msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_IN_PROGRESS), + sizeof(task_title)); + + if (!string_is_empty(content_file)) + strlcpy(task_title + _len, + content_file, + sizeof(task_title) - _len); + + task_set_title(task, strdup(task_title)); + task_set_progress(task, + (manual_scan->content_list_index * 100) / + manual_scan->content_list_size); + + /* Add content to playlist */ + manual_content_scan_add_content_to_playlist( + manual_scan->task_config, manual_scan->playlist, + content_path, content_type, manual_scan->dat_file); + + /* If this is an M3U file, add it to the + * M3U list for later processing */ + if (m3u_file_is_m3u(content_path)) + { + union string_list_elem_attr attr; + attr.i = 0; + /* Note: If string_list_append() fails, there is + * really nothing we can do. The M3U file will + * just be ignored... */ + string_list_append( + manual_scan->m3u_list, content_path, attr); + } + } + + /* Increment content index */ + manual_scan->content_list_index++; + if (manual_scan->content_list_index >= + manual_scan->content_list_size) + { + /* Check whether we have any M3U files + * to process */ + if (manual_scan->m3u_list->size > 0) + manual_scan->status = MANUAL_SCAN_ITERATE_M3U; + else + manual_scan->status = MANUAL_SCAN_END; + } + } + break; + case MANUAL_SCAN_ITERATE_M3U: + { + const char *m3u_path = manual_scan->m3u_list->elems[ + manual_scan->m3u_index].data; + + if (!string_is_empty(m3u_path)) + { + size_t _len; + char task_title[128]; + const char *m3u_name = path_basename_nocompression(m3u_path); + m3u_file_t *m3u_file = NULL; + + /* Update progress display */ + task_free_title(task); + + _len = strlcpy(task_title, + msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_M3U_CLEANUP), + sizeof(task_title)); + + if (!string_is_empty(m3u_name)) + strlcpy(task_title + _len, + m3u_name, + sizeof(task_title) - _len); + + task_set_title(task, strdup(task_title)); + task_set_progress(task, (manual_scan->m3u_index * 100) / + manual_scan->m3u_list->size); + + /* Load M3U file */ + if ((m3u_file = m3u_file_init(m3u_path))) + { + size_t i; + + /* Loop over M3U entries */ + for (i = 0; i < m3u_file_get_size(m3u_file); i++) + { + m3u_file_entry_t *m3u_entry = NULL; + + /* Delete any playlist items matching the + * content path of the M3U entry */ + if (m3u_file_get_entry(m3u_file, i, &m3u_entry)) + playlist_delete_by_path( + manual_scan->playlist, m3u_entry->full_path); + } + + m3u_file_free(m3u_file); + } + } + + /* Increment M3U file index */ + manual_scan->m3u_index++; + if (manual_scan->m3u_index >= manual_scan->m3u_list->size) + manual_scan->status = MANUAL_SCAN_END; + } + break; + case MANUAL_SCAN_END: + { + size_t _len; + char task_title[128]; + + /* Ensure playlist is alphabetically sorted + * > Override user settings here */ + playlist_set_sort_mode(manual_scan->playlist, PLAYLIST_SORT_MODE_DEFAULT); + playlist_qsort(manual_scan->playlist); + + /* Save playlist changes to disk */ + playlist_write_file(manual_scan->playlist); + + /* Update progress display */ + task_free_title(task); + + _len = strlcpy( + task_title, msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_END), + sizeof(task_title)); + strlcpy(task_title + _len, + manual_scan->task_config->system_name, + sizeof(task_title) - _len); + + 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_flags(task, RETRO_TASK_FLG_FINISHED, true); +} + +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; + if (!(manual_scan = (manual_scan_handle_t*)task->state)) + return false; + return string_is_equal( + (const char*)user_data, manual_scan->playlist_config.path); +} + +bool task_push_manual_content_scan( + const playlist_config_t *playlist_config, + const char *playlist_directory) +{ + size_t _len; + task_finder_data_t find_data; + char task_title[128]; + retro_task_t *task = NULL; + manual_scan_handle_t *manual_scan = NULL; + + /* Sanity check */ + if ( !playlist_config + || string_is_empty(playlist_directory)) + return false; + + if (!(manual_scan = (manual_scan_handle_t*) + calloc(1, sizeof(manual_scan_handle_t)))) + return false; + + /* Configure handle */ + manual_scan->task_config = NULL; + manual_scan->playlist = NULL; + manual_scan->file_exts_list = NULL; + manual_scan->content_list = NULL; + manual_scan->dat_file = NULL; + manual_scan->playlist_size = 0; + manual_scan->playlist_index = 0; + manual_scan->content_list_size = 0; + manual_scan->content_list_index = 0; + manual_scan->status = MANUAL_SCAN_BEGIN; + manual_scan->m3u_index = 0; + manual_scan->m3u_list = string_list_new(); + + if (!manual_scan->m3u_list) + goto error; + + /* > Get current manual content scan configuration */ + if (!(manual_scan->task_config = (manual_content_scan_task_config_t*) + calloc(1, sizeof(manual_content_scan_task_config_t)))) + goto error; + + if (!manual_content_scan_get_task_config( + manual_scan->task_config, playlist_directory)) + { + const char *_msg = msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_INVALID_CONFIG); + runloop_msg_queue_push(_msg, strlen(_msg), 1, 100, true, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + goto error; + } + + /* > Cache playlist configuration */ + if (!playlist_config_copy(playlist_config, + &manual_scan->playlist_config)) + goto error; + + playlist_config_set_path( + &manual_scan->playlist_config, + manual_scan->task_config->playlist_file); + + /* 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->playlist_config.path; + + if (task_queue_find(&find_data)) + goto error; + + /* Create task */ + if (!(task = task_init())) + goto error; + + /* > Get task title */ + _len = strlcpy( + task_title, msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_START), + sizeof(task_title)); + strlcpy(task_title + _len, + manual_scan->task_config->system_name, + sizeof(task_title) - _len); + + /* > Configure task */ + task->handler = task_manual_content_scan_handler; + task->state = manual_scan; + task->title = strdup(task_title); + task->progress = 0; + task->callback = cb_task_manual_content_scan; + task->cleanup = task_manual_content_scan_free; + task->flags |= RETRO_TASK_FLG_ALTERNATIVE_LOOK; + + /* > Push task */ + task_queue_push(task); + + return true; + +error: + /* Clean up handle */ + free_manual_content_scan_handle(manual_scan); + manual_scan = NULL; + + return false; +} diff --git a/tasks/task_database_cue.c b/tasks/task_database_cue.c index 76c544d50a..e3c6d5fbaf 100644 --- a/tasks/task_database_cue.c +++ b/tasks/task_database_cue.c @@ -1414,3 +1414,352 @@ size_t gdi_next_file(intfstream_t *fd, const char *gdi_path, return 0; } + +int intfstream_get_serial(intfstream_t *fd, char *s, size_t len, const char *filename) +{ + const char *system_name = NULL; + if (detect_system(fd, &system_name, filename) >= 1) + { + size_t system_len = strlen(system_name); + if (string_starts_with_size(system_name, "Sony", STRLEN_CONST("Sony"))) + { + if ( STRLEN_CONST("Sony - PlayStation Portable") == system_len + && memcmp(system_name, "Sony - PlayStation Portable", system_len) == 0) + { + if (detect_psp_game(fd, s, len, filename) != 0) + return 1; + } + else if ( STRLEN_CONST("Sony - PlayStation") == system_len + && memcmp(system_name, "Sony - PlayStation", system_len) == 0) + { + if (detect_ps1_game(fd, s, len, filename) != 0) + return 1; + } + else if ( STRLEN_CONST("Sony - PlayStation 2") == system_len + && memcmp(system_name, "Sony - PlayStation 2", system_len) == 0) + { + if (detect_ps2_game(fd, s, len, filename) != 0) + return 1; + } + } + else if (string_starts_with_size(system_name, "Nintendo", STRLEN_CONST("Nintendo"))) + { + if ( STRLEN_CONST("Nintendo - GameCube") == system_len + && memcmp(system_name, "Nintendo - GameCube", system_len) == 0) + { + if (detect_gc_game(fd, s, len, filename) != 0) + return 1; + } + else if ( STRLEN_CONST("Nintendo - Wii") == system_len + && memcmp(system_name, "Nintendo - Wii", system_len) == 0) + { + if (detect_wii_game(fd, s, len, filename) != 0) + return 1; + } + } + else if (string_starts_with_size(system_name, "Sega", STRLEN_CONST("Sega"))) + { + if ( STRLEN_CONST("Sega - Mega-CD - Sega CD") == system_len + && memcmp(system_name, "Sega - Mega-CD - Sega CD", system_len) == 0) + { + if (detect_scd_game(fd, s, len, filename) != 0) + return 1; + } + else if ( STRLEN_CONST("Sega - Saturn") == system_len + && memcmp(system_name, "Sega - Saturn", system_len) == 0) + { + if (detect_sat_game(fd, s, len, filename) != 0) + return 1; + } + else if ( STRLEN_CONST("Sega - Dreamcast") == system_len + && memcmp(system_name, "Sega - Dreamcast", system_len) == 0) + { + if (detect_dc_game(fd, s, len, filename) != 0) + return 1; + } + } + /* Philips CD-i has no serial entry on disc, use default fallback to CRC */ + } + return 0; +} + +bool intfstream_file_get_serial(const char *name, + uint64_t offset, int64_t size, char *s, size_t len, uint64_t *fsize) +{ + int rv; + uint8_t *data = NULL; + int64_t file_size = -1; + intfstream_t *fd = intfstream_open_file(name, + RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); + + if (!fd) + return 0; + + if (intfstream_seek(fd, 0, SEEK_END) == -1) + goto error; + + file_size = intfstream_tell(fd); + *fsize = file_size; + + if (intfstream_seek(fd, 0, SEEK_SET) == -1) + goto error; + + if (file_size < 0) + goto error; + + if (offset != 0 || size < file_size) + { + if (intfstream_seek(fd, (int64_t)offset, SEEK_SET) == -1) + goto error; + + data = (uint8_t*)malloc(size); + + if (intfstream_read(fd, data, size) != (int64_t) size) + { + free(data); + goto error; + } + + intfstream_close(fd); + free(fd); + if (!(fd = intfstream_open_memory(data, RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE, + size))) + { + free(data); + return 0; + } + } + + rv = intfstream_get_serial(fd, s, len, name); + intfstream_close(fd); + free(fd); + free(data); + return rv; + +error: + intfstream_close(fd); + free(fd); + return 0; +} + +int task_database_cue_get_serial(const char *name, char *s, size_t len, uint64_t *filesize) +{ + char track_path[PATH_MAX_LENGTH]; + uint64_t offset = 0; + size_t _len = 0; + + track_path[0] = '\0'; + + if (cue_find_track(name, true, &offset, &_len, + track_path, sizeof(track_path)) < 0) + { +#ifdef DEBUG + RARCH_LOG("[Scanner] %s\n", + msg_hash_to_str(MSG_COULD_NOT_FIND_VALID_DATA_TRACK)); +#endif + return 0; + } + + return intfstream_file_get_serial(track_path, offset, _len, s, len, filesize); +} + +int task_database_gdi_get_serial(const char *name, char *s, size_t len, uint64_t *filesize) +{ + char track_path[PATH_MAX_LENGTH]; + + track_path[0] = '\0'; + + if (gdi_find_track(name, true, + track_path, sizeof(track_path)) < 0) + { +#ifdef DEBUG + RARCH_LOG("[Scanner] %s\n", + msg_hash_to_str(MSG_COULD_NOT_FIND_VALID_DATA_TRACK)); +#endif + return 0; + } + + return intfstream_file_get_serial(track_path, 0, INT64_MAX, s, len, filesize); +} + +/* Helper function to detect if a CHD file is a CD-i disc + * CD-i discs store data in AUDIO-labeled tracks, so we need + * to explicitly open track 1 for scanning */ +bool is_chd_file_cdi(const char *path) +{ + intfstream_t *fd; + uint8_t magic[12]; + bool is_cdi = false; + const uint8_t cdi_magic[] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}; + + /* Try to open track 1 explicitly */ + fd = intfstream_open_chd_track(path, + RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE, + 1); /* Explicit track number, not CHDSTREAM_TRACK_FIRST_DATA */ + + if (!fd) + return false; + + /* Read and check CD-i magic bytes at offset 0 */ + if (intfstream_read(fd, magic, sizeof(magic)) == sizeof(magic)) + is_cdi = (memcmp(magic, cdi_magic, sizeof(cdi_magic)) == 0); + + intfstream_close(fd); + free(fd); + return is_cdi; +} + +int task_database_chd_get_serial(const char *name, char *serial, size_t len, uint64_t *filesize) +{ + int result; + int32_t track; + intfstream_t *fd; + + /* CD-i discs store data in AUDIO-labeled tracks, so we must + * explicitly open track 1 instead of using CHDSTREAM_TRACK_FIRST_DATA */ + track = is_chd_file_cdi(name) ? 1 : CHDSTREAM_TRACK_FIRST_DATA; + + fd = intfstream_open_chd_track( + name, + RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE, + track); + if (!fd) + return 0; + + *filesize = intfstream_get_size(fd); /* TODO: get the full CHD size instead */ + result = intfstream_get_serial(fd, serial, len, name); + intfstream_close(fd); + free(fd); + return result; +} + +bool intfstream_file_get_crc_and_size(const char *name, + uint64_t offset, int64_t len, uint32_t *crc, uint64_t *size) +{ + bool rv; + intfstream_t *fd = intfstream_open_file(name, + RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); + uint8_t *data = NULL; + int64_t file_size = -1; + + if (!fd) + return 0; + + if (intfstream_seek(fd, 0, SEEK_END) == -1) + goto error; + + file_size = intfstream_tell(fd); + *size = file_size; + + if (intfstream_seek(fd, 0, SEEK_SET) == -1) + goto error; + + if (file_size < 0) + goto error; + + if (offset != 0 || len < file_size) + { + if (intfstream_seek(fd, (int64_t)offset, SEEK_SET) == -1) + goto error; + + data = (uint8_t*)malloc(len); + + if (intfstream_read(fd, data, len) != (int64_t)len) + goto error; + + intfstream_close(fd); + free(fd); + fd = intfstream_open_memory(data, RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE, len); + + if (!fd) + goto error; + } + + rv = intfstream_get_crc(fd, crc); + intfstream_close(fd); + free(fd); + free(data); + return rv; + +error: + if (fd) + { + intfstream_close(fd); + free(fd); + } + if (data) + free(data); + return 0; +} + +int task_database_cue_get_crc_and_size(const char *name, uint32_t *crc, uint64_t *size) +{ + char track_path[PATH_MAX_LENGTH]; + uint64_t offset = 0; + size_t _len = 0; + + track_path[0] = '\0'; + + if (cue_find_track(name, false, &offset, &_len, + track_path, sizeof(track_path)) < 0) + { +#ifdef DEBUG + RARCH_LOG("[Scanner] %s\n", + msg_hash_to_str(MSG_COULD_NOT_FIND_VALID_DATA_TRACK)); +#endif + return 0; + } + return intfstream_file_get_crc_and_size(track_path, offset, _len, crc, size); +} + +int task_database_gdi_get_crc_and_size(const char *name, uint32_t *crc, uint64_t *size) +{ + char track_path[PATH_MAX_LENGTH]; + + track_path[0] = '\0'; + + if (gdi_find_track(name, true, + track_path, sizeof(track_path)) < 0) + { +#ifdef DEBUG + RARCH_LOG("[Scanner] %s\n", + msg_hash_to_str(MSG_COULD_NOT_FIND_VALID_DATA_TRACK)); +#endif + return 0; + } + return intfstream_file_get_crc_and_size(track_path, 0, INT64_MAX, crc, size); +} + +bool task_database_chd_get_crc_and_size(const char *name, uint32_t *crc, uint64_t *size) +{ + bool found_crc = false; + int32_t track; + intfstream_t *fd; + + /* CD-i discs store data in AUDIO-labeled tracks, so we must + * explicitly open track 1 instead of using CHDSTREAM_TRACK_PRIMARY */ + track = is_chd_file_cdi(name) ? 1 : CHDSTREAM_TRACK_PRIMARY; + + fd = intfstream_open_chd_track( + name, + RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE, + track); + if (!fd) + return 0; + + *size = intfstream_get_size(fd); + found_crc = intfstream_get_crc(fd, crc); + if (fd) + { + intfstream_close(fd); + free(fd); + } + return found_crc; +} + diff --git a/tasks/task_database_cue.h b/tasks/task_database_cue.h index c83559147c..7d4fbbeb68 100644 --- a/tasks/task_database_cue.h +++ b/tasks/task_database_cue.h @@ -16,6 +16,8 @@ #include #include +#include +#include RETRO_BEGIN_DECLS @@ -53,6 +55,26 @@ int gdi_find_track(const char *gdi_path, bool first, char *s, size_t gdi_next_file(intfstream_t *fd, const char *gdi_path, char *s, size_t len); +int intfstream_get_serial(intfstream_t *fd, char *s, size_t len, + const char *filename); +bool intfstream_file_get_serial(const char *name, + uint64_t offset, int64_t size, char *s, size_t len, uint64_t *fsize); +int task_database_cue_get_serial(const char *name, char *s, size_t len, + uint64_t *filesize); +int task_database_gdi_get_serial(const char *name, char *s, size_t len, + uint64_t *filesize); +bool is_chd_file_cdi(const char *path); +int task_database_chd_get_serial(const char *name, char *serial, size_t len, + uint64_t *filesize); +bool intfstream_file_get_crc_and_size(const char *name, + uint64_t offset, int64_t len, uint32_t *crc, uint64_t *size); +int task_database_cue_get_crc_and_size(const char *name, uint32_t *crc, + uint64_t *size); +int task_database_gdi_get_crc_and_size(const char *name, uint32_t *crc, + uint64_t *size); +bool task_database_chd_get_crc_and_size(const char *name, uint32_t *crc, + uint64_t *size); + RETRO_END_DECLS #endif diff --git a/tasks/task_manual_content_scan.c b/tasks/task_manual_content_scan.c deleted file mode 100644 index ad2e4d575b..0000000000 --- a/tasks/task_manual_content_scan.c +++ /dev/null @@ -1,629 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2011-2017 - Daniel De Matteis - * Copyright (C) 2014-2017 - Jean-André Santoni - * Copyright (C) 2016-2019 - Brad Parker - * Copyright (C) 2019 - James Leaver - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "tasks_internal.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 -#include "../runloop.h" -#endif - -enum manual_scan_status -{ - MANUAL_SCAN_BEGIN = 0, - MANUAL_SCAN_ITERATE_CLEAN, - MANUAL_SCAN_ITERATE_CONTENT, - MANUAL_SCAN_ITERATE_M3U, - MANUAL_SCAN_END -}; - -typedef struct manual_scan_handle -{ - manual_content_scan_task_config_t *task_config; - playlist_t *playlist; - struct string_list *file_exts_list; - struct string_list *content_list; - logiqx_dat_t *dat_file; - struct string_list *m3u_list; - playlist_config_t playlist_config; /* size_t alignment */ - size_t playlist_size; - size_t playlist_index; - size_t content_list_size; - size_t content_list_index; - size_t m3u_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->file_exts_list) - { - string_list_free(manual_scan->file_exts_list); - manual_scan->file_exts_list = NULL; - } - - if (manual_scan->content_list) - { - string_list_free(manual_scan->content_list); - manual_scan->content_list = NULL; - } - - if (manual_scan->m3u_list) - { - string_list_free(manual_scan->m3u_list); - manual_scan->m3u_list = NULL; - } - - if (manual_scan->dat_file) - { - logiqx_dat_free(manual_scan->dat_file); - manual_scan->dat_file = NULL; - } - - free(manual_scan); - manual_scan = NULL; -} - -static void cb_task_manual_content_scan( - retro_task_t *task, void *task_data, - void *user_data, const char *err) -{ - manual_scan_handle_t *manual_scan = NULL; - playlist_t *cached_playlist = playlist_get_cached(); -#if defined(RARCH_INTERNAL) && defined(HAVE_MENU) - struct menu_state *menu_st = menu_state_get_ptr(); - if (!task) - goto end; -#else - if (!task) - return; -#endif - - if (!(manual_scan = (manual_scan_handle_t*)task->state)) - { -#if defined(RARCH_INTERNAL) && defined(HAVE_MENU) - goto end; -#else - return; -#endif - } - - /* If the manual content scan task has modified 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->playlist_config.path, - playlist_get_conf_path(cached_playlist))) - { - playlist_config_t playlist_config; - - /* Copy configuration of cached playlist - * (could use manual_scan->playlist_config, - * but doing it this way guarantees that - * the cached playlist is preserved in - * its original state) */ - if (playlist_config_copy( - playlist_get_config(cached_playlist), - &playlist_config)) - { - playlist_free_cached(); - playlist_init_cached(&playlist_config); - } - } - } - -#if defined(RARCH_INTERNAL) && defined(HAVE_MENU) -end: - /* When creating playlists, the playlist tabs of - * any active menu driver must be refreshed */ - if (menu_st->driver_ctx->environ_cb) - menu_st->driver_ctx->environ_cb(MENU_ENVIRON_RESET_HORIZONTAL_LIST, - NULL, menu_st->userdata); -#endif -} - -static void task_manual_content_scan_free(retro_task_t *task) -{ - manual_scan_handle_t *manual_scan = NULL; - - if (!task) - return; - - manual_scan = (manual_scan_handle_t*)task->state; - - free_manual_content_scan_handle(manual_scan); -} - -static void task_manual_content_scan_handler(retro_task_t *task) -{ - uint8_t flg; - manual_scan_handle_t *manual_scan = NULL; - - if (!task) - goto task_finished; - - if (!(manual_scan = (manual_scan_handle_t*)task->state)) - goto task_finished; - - flg = task_get_flags(task); - - if ((flg & RETRO_TASK_FLG_CANCELLED) > 0) - goto task_finished; - - switch (manual_scan->status) - { - case MANUAL_SCAN_BEGIN: - { - /* Get allowed file extensions list */ - if (!string_is_empty(manual_scan->task_config->file_exts)) - manual_scan->file_exts_list = string_split( - manual_scan->task_config->file_exts, "|"); - - /* Get content list */ - if (!(manual_scan->content_list - = manual_content_scan_get_content_list( - manual_scan->task_config))) - { - const char *_msg = msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_INVALID_CONTENT); - runloop_msg_queue_push(_msg, strlen(_msg), 1, 100, true, NULL,\ - MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - goto task_finished; - } - - manual_scan->content_list_size = manual_scan->content_list->size; - - /* Load DAT file, if required */ - if (!string_is_empty(manual_scan->task_config->dat_file_path)) - { - if (!(manual_scan->dat_file = - logiqx_dat_init( - manual_scan->task_config->dat_file_path))) - { - const char *_msg = msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_DAT_FILE_LOAD_ERROR); - runloop_msg_queue_push(_msg, strlen(_msg), 1, 100, true, NULL, - MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - goto task_finished; - } - } - - /* Open playlist */ - if (!(manual_scan->playlist = - playlist_init(&manual_scan->playlist_config))) - goto task_finished; - - /* Reset playlist, if required */ - if (manual_scan->task_config->overwrite_playlist) - playlist_clear(manual_scan->playlist); - - /* Get initial playlist size */ - manual_scan->playlist_size = - playlist_size(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); - } - - /* Record remaining scan parameters to enable - * subsequent 'refresh playlist' operations */ - playlist_set_scan_content_dir(manual_scan->playlist, - manual_scan->task_config->content_dir); - playlist_set_scan_file_exts(manual_scan->playlist, - manual_scan->task_config->file_exts_custom_set ? - manual_scan->task_config->file_exts : NULL); - playlist_set_scan_dat_file_path(manual_scan->playlist, - manual_scan->task_config->dat_file_path); - playlist_set_scan_search_recursively(manual_scan->playlist, - manual_scan->task_config->search_recursively); - playlist_set_scan_search_archives(manual_scan->playlist, - manual_scan->task_config->search_archives); - playlist_set_scan_filter_dat_content(manual_scan->playlist, - manual_scan->task_config->filter_dat_content); - playlist_set_scan_overwrite_playlist(manual_scan->playlist, - manual_scan->task_config->overwrite_playlist); - - /* All good - can start iterating - * > If playlist has content and 'validate - * entries' is enabled, go to clean-up phase - * > Otherwise go straight to content scan phase */ - if (manual_scan->task_config->validate_entries && - (manual_scan->playlist_size > 0)) - manual_scan->status = MANUAL_SCAN_ITERATE_CLEAN; - else - manual_scan->status = MANUAL_SCAN_ITERATE_CONTENT; - } - break; - case MANUAL_SCAN_ITERATE_CLEAN: - { - const struct playlist_entry *entry = NULL; - bool delete_entry = false; - - /* Get current entry */ - playlist_get_index(manual_scan->playlist, - manual_scan->playlist_index, &entry); - - if (entry) - { - size_t _len; - const char *entry_file = NULL; - const char *entry_file_ext = NULL; - char task_title[128]; - - /* Update progress display */ - task_free_title(task); - - _len = strlcpy(task_title, - msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_PLAYLIST_CLEANUP), - sizeof(task_title)); - - if ( !string_is_empty(entry->path) - && (entry_file = path_basename(entry->path))) - strlcpy(task_title + _len, - entry_file, - sizeof(task_title) - _len); - - task_set_title(task, strdup(task_title)); - task_set_progress(task, (manual_scan->playlist_index * 100) / - manual_scan->playlist_size); - - /* Check whether playlist content exists on - * the filesystem */ - if (!playlist_content_path_is_valid(entry->path)) - delete_entry = true; - /* If file exists, check whether it has a - * permitted file extension */ - else if ( manual_scan->file_exts_list - && (entry_file_ext = path_get_extension(entry->path)) - && !string_list_find_elem_prefix( - manual_scan->file_exts_list, - ".", entry_file_ext)) - delete_entry = true; - - if (delete_entry) - { - /* Invalid content - delete entry */ - playlist_delete_index(manual_scan->playlist, - manual_scan->playlist_index); - - /* Update playlist_size */ - manual_scan->playlist_size = playlist_size(manual_scan->playlist); - } - } - - /* Increment entry index *if* current entry still - * exists (i.e. if entry was deleted, current index - * will already point to the *next* entry) */ - if (!delete_entry) - manual_scan->playlist_index++; - - if (manual_scan->playlist_index >= - manual_scan->playlist_size) - manual_scan->status = MANUAL_SCAN_ITERATE_CONTENT; - } - break; - case MANUAL_SCAN_ITERATE_CONTENT: - { - const char *content_path = manual_scan->content_list->elems[ - manual_scan->content_list_index].data; - int content_type = manual_scan->content_list->elems[ - manual_scan->content_list_index].attr.i; - - if (!string_is_empty(content_path)) - { - size_t _len; - char task_title[128]; - const char *content_file = path_basename(content_path); - - /* Update progress display */ - task_free_title(task); - - _len = strlcpy(task_title, - msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_IN_PROGRESS), - sizeof(task_title)); - - if (!string_is_empty(content_file)) - strlcpy(task_title + _len, - content_file, - sizeof(task_title) - _len); - - task_set_title(task, strdup(task_title)); - task_set_progress(task, - (manual_scan->content_list_index * 100) / - manual_scan->content_list_size); - - /* Add content to playlist */ - manual_content_scan_add_content_to_playlist( - manual_scan->task_config, manual_scan->playlist, - content_path, content_type, manual_scan->dat_file); - - /* If this is an M3U file, add it to the - * M3U list for later processing */ - if (m3u_file_is_m3u(content_path)) - { - union string_list_elem_attr attr; - attr.i = 0; - /* Note: If string_list_append() fails, there is - * really nothing we can do. The M3U file will - * just be ignored... */ - string_list_append( - manual_scan->m3u_list, content_path, attr); - } - } - - /* Increment content index */ - manual_scan->content_list_index++; - if (manual_scan->content_list_index >= - manual_scan->content_list_size) - { - /* Check whether we have any M3U files - * to process */ - if (manual_scan->m3u_list->size > 0) - manual_scan->status = MANUAL_SCAN_ITERATE_M3U; - else - manual_scan->status = MANUAL_SCAN_END; - } - } - break; - case MANUAL_SCAN_ITERATE_M3U: - { - const char *m3u_path = manual_scan->m3u_list->elems[ - manual_scan->m3u_index].data; - - if (!string_is_empty(m3u_path)) - { - size_t _len; - char task_title[128]; - const char *m3u_name = path_basename_nocompression(m3u_path); - m3u_file_t *m3u_file = NULL; - - /* Update progress display */ - task_free_title(task); - - _len = strlcpy(task_title, - msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_M3U_CLEANUP), - sizeof(task_title)); - - if (!string_is_empty(m3u_name)) - strlcpy(task_title + _len, - m3u_name, - sizeof(task_title) - _len); - - task_set_title(task, strdup(task_title)); - task_set_progress(task, (manual_scan->m3u_index * 100) / - manual_scan->m3u_list->size); - - /* Load M3U file */ - if ((m3u_file = m3u_file_init(m3u_path))) - { - size_t i; - - /* Loop over M3U entries */ - for (i = 0; i < m3u_file_get_size(m3u_file); i++) - { - m3u_file_entry_t *m3u_entry = NULL; - - /* Delete any playlist items matching the - * content path of the M3U entry */ - if (m3u_file_get_entry(m3u_file, i, &m3u_entry)) - playlist_delete_by_path( - manual_scan->playlist, m3u_entry->full_path); - } - - m3u_file_free(m3u_file); - } - } - - /* Increment M3U file index */ - manual_scan->m3u_index++; - if (manual_scan->m3u_index >= manual_scan->m3u_list->size) - manual_scan->status = MANUAL_SCAN_END; - } - break; - case MANUAL_SCAN_END: - { - size_t _len; - char task_title[128]; - - /* Ensure playlist is alphabetically sorted - * > Override user settings here */ - playlist_set_sort_mode(manual_scan->playlist, PLAYLIST_SORT_MODE_DEFAULT); - playlist_qsort(manual_scan->playlist); - - /* Save playlist changes to disk */ - playlist_write_file(manual_scan->playlist); - - /* Update progress display */ - task_free_title(task); - - _len = strlcpy( - task_title, msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_END), - sizeof(task_title)); - strlcpy(task_title + _len, - manual_scan->task_config->system_name, - sizeof(task_title) - _len); - - 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_flags(task, RETRO_TASK_FLG_FINISHED, true); -} - -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; - if (!(manual_scan = (manual_scan_handle_t*)task->state)) - return false; - return string_is_equal( - (const char*)user_data, manual_scan->playlist_config.path); -} - -bool task_push_manual_content_scan( - const playlist_config_t *playlist_config, - const char *playlist_directory) -{ - size_t _len; - task_finder_data_t find_data; - char task_title[128]; - retro_task_t *task = NULL; - manual_scan_handle_t *manual_scan = NULL; - - /* Sanity check */ - if ( !playlist_config - || string_is_empty(playlist_directory)) - return false; - - if (!(manual_scan = (manual_scan_handle_t*) - calloc(1, sizeof(manual_scan_handle_t)))) - return false; - - /* Configure handle */ - manual_scan->task_config = NULL; - manual_scan->playlist = NULL; - manual_scan->file_exts_list = NULL; - manual_scan->content_list = NULL; - manual_scan->dat_file = NULL; - manual_scan->playlist_size = 0; - manual_scan->playlist_index = 0; - manual_scan->content_list_size = 0; - manual_scan->content_list_index = 0; - manual_scan->status = MANUAL_SCAN_BEGIN; - manual_scan->m3u_index = 0; - manual_scan->m3u_list = string_list_new(); - - if (!manual_scan->m3u_list) - goto error; - - /* > Get current manual content scan configuration */ - if (!(manual_scan->task_config = (manual_content_scan_task_config_t*) - calloc(1, sizeof(manual_content_scan_task_config_t)))) - goto error; - - if (!manual_content_scan_get_task_config( - manual_scan->task_config, playlist_directory)) - { - const char *_msg = msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_INVALID_CONFIG); - runloop_msg_queue_push(_msg, strlen(_msg), 1, 100, true, NULL, - MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - goto error; - } - - /* > Cache playlist configuration */ - if (!playlist_config_copy(playlist_config, - &manual_scan->playlist_config)) - goto error; - - playlist_config_set_path( - &manual_scan->playlist_config, - manual_scan->task_config->playlist_file); - - /* 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->playlist_config.path; - - if (task_queue_find(&find_data)) - goto error; - - /* Create task */ - if (!(task = task_init())) - goto error; - - /* > Get task title */ - _len = strlcpy( - task_title, msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_START), - sizeof(task_title)); - strlcpy(task_title + _len, - manual_scan->task_config->system_name, - sizeof(task_title) - _len); - - /* > Configure task */ - task->handler = task_manual_content_scan_handler; - task->state = manual_scan; - task->title = strdup(task_title); - task->progress = 0; - task->callback = cb_task_manual_content_scan; - task->cleanup = task_manual_content_scan_free; - task->flags |= RETRO_TASK_FLG_ALTERNATIVE_LOOK; - - /* > Push task */ - task_queue_push(task); - - return true; - -error: - /* Clean up handle */ - free_manual_content_scan_handle(manual_scan); - manual_scan = NULL; - - return false; -}