mirror of
https://github.com/libretro/RetroArch.git
synced 2025-12-28 05:24:00 +00:00
Rearrange scanning related functions (#18458)
This commit is contained in:
parent
90a52f1945
commit
1cf52a0dee
@ -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 \
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -22,15 +22,17 @@
|
||||
#include <retro_endianness.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <lists/dir_list.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <file/file_path.h>
|
||||
#include <formats/logiqx_dat.h>
|
||||
#include <formats/m3u_file.h>
|
||||
#include <encodings/crc32.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <streams/chd_stream.h>
|
||||
#include <streams/interface_stream.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <streams/interface_stream.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <streams/chd_stream.h>
|
||||
|
||||
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
|
||||
|
||||
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <boolean.h>
|
||||
|
||||
#include <string/stdstring.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <file/file_path.h>
|
||||
#include <formats/logiqx_dat.h>
|
||||
#include <formats/m3u_file.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user