mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
* file_path: stop at the end of the path after '~'
fill_pathname_expand_special() skipped two characters after a leading
'~' or ':', so a bare "~" read past its NUL and "~x" lost the x. It
now skips the prefix and a slash after it, if there is one.
* scan: hand the callback user data and what the scan did
task_push_dbscan() and task_push_manual_content_scan() take user data
for their callback, and the callback's task_data is now a
task_dbscan_result_t: how many entries the scan added, how many its
playlists already held, and how many files matched nothing. A file is
counted by what became of it, so an archive that is expanded is not
counted as unmatched, and a loose scan's unmatched files count as
added. Callers pass NULL for now.
* scan: say in the result when a scan failed
A scan that ends before it runs (bad setup, no valid content, a DAT
that will not load) or ends with a task error now sets the result's
failed flag. A database folder that is missing or unset is treated as
an empty one, so it reports the same "no database" error instead of
finishing as if it had scanned.
* scan: count the files a scan could not read
A file whose scan fails (an unreadable file, a failed lookup) was in
no count. The result now carries an errors count for them. They do
not fail the scan.
* scan: print --scan errors on stderr and totals on stdout
A command-line scan now ends with a totals line on stdout, or with the
reason it failed on stderr. A file it could not read is listed on
stderr. The ++/==/?? marks are coloured only when their stream is a
terminal, so pipes and redirected output get plain text; on Windows
redirected output keeps its marks instead of losing them to
WriteConsole.
* scan: the manual scan finder skips a finished task
A finished scan stays in the task queue while its callback runs, and
the finder that refuses a second scan for the same playlist still
matched it, so a scan pushed from that callback was refused.
* retroarch: run every --scan, then exit with what the scans did
Each --scan is collected while the command line is parsed and run in
turn once it is done, so later options and further --scan flags are
no longer dropped. A path is expanded and made absolute first; one
that does not exist, or a scan that cannot start, is reported on
stderr and the next path still runs. --scan with --cloudsync or
--command is refused.
Without --menu the exit status is 1 if any scan failed, else 0 if any
added an entry, else 2, and a content path given with the scans is
ignored, with a line on stderr. With --menu each finished scan starts
the next, as a second scan is refused while one runs.
* manual_content_scan: clear the database name a refresh did not save
A playlist with no saved database name left the name buffer
uninitialised, and the refresh compared it against every installed
database, so whatever was on the stack could pick a database the
playlist never named.
* scan: Skip Database References names the scan's one playlist
The option was meant to put the playlist's name in each entry's
db_name, even for a database match (#18641), for users who build
their own thumbnail sets. When the scan picks the playlists, db_name
also picks the playlist an entry goes to, so with the option on every
match went to one playlist named after the content folder.
It now applies only when the scan writes one playlist, and gives
that playlist's name, a loose scan's unmatched entries included. With
the target on <Automatic>, or Fully Automatic with the option left on
from a Custom scan, matches go to their databases' playlists as with
it off; the entries of a custom playlist look their thumbnails up
under its name, not the folder's.
* scan: name the scanned folder in the start line
The "Scanning content" line, in the log and in --scan's output, has
named the database folder since the manual scan and the database scan
were merged (2fdf736d51); before, it named the path being scanned. It
names the content folder again.
* manual_content_scan: reset, scan overrides and their apply
The command line's scan options need each scan to start from the
defaults and to lay their values over them, or over a playlist's saved
settings. Add a reset, a record of those options and a function that
applies it through the existing setters: a core named by path, file
name or display name, a database, a playlist, the database check or a
DAT, extensions and the switches, stopping with a reason at the first
that does not resolve.
A core path is normalised before it is compared. A core's file name
matches in the case given first, then ignoring case, on every
platform; a display name matches exactly. Given another database, a
playlist named after its database keeps that name as a custom one, so
a refresh still writes the same playlist.
The core setter's copy step becomes its own function, so a core found
by path or file name can be set. Nothing calls the new functions yet.
* scan: run --scan items through one chain
The --scan paths ran through a loop in retroarch.c, and with --menu
through a second chain of their own. The scan options and playlist
refreshes to come need one runner for every item, from the command
line and from the menu.
task_push_dbscan_chain() runs a list of items one task at a time: a
prep task lists the databases, then each step is pushed from the last
task's callback on the main thread, so nothing waits and nothing
locks. An item that fails is reported and the run goes on; a
cancelled task ends it. retroarch.c builds the items in the first
argument pass and hands them to the chain; without --menu it waits
for the chain's end and exits as before.
Running out of memory prints "out of memory" on stderr: for an item's
path the item fails, and for the chain itself nothing runs, which
without --menu exits 1.
The chain also prints a scan's override errors, and the warnings for
an option that needs one playlist or does nothing for a single file,
which the scan options of the next commit can trigger.
* retroarch: Manual Scan options for --scan
--scan-playlist, --scan-database, --scan-match, --scan-dat, --scan-core,
--scan-exts and the five switches set the Manual Scan settings of the
--scan before them; a --scan without any stays Fully Automatic.
Values are checked in the first pass, so an option before any --scan,
an empty or bad value, or one too long for its setting ends the run
with its reason on stderr and status 1, before the config loads and
before anything is scanned. Cores, databases and DAT files are
resolved when their scan runs, and one that does not resolve fails
only that scan.
* scan: refresh playlists in the chain
A refresh item rescans one playlist with its saved scan settings, as
the menu's Refresh Playlist does, with the item's scan options laid
over them and written back by the scan. A refresh-all item does the
same for every playlist in the playlist folder that has saved
settings, in the order Manage Playlists lists them, histories and
favorites left out.
A refresh item names its playlist by name or by a path in the playlist
folder. A bare "name.lpl" is the playlist folder's file, and an
absolute path is normalised before it is checked against the folder.
The name is looked up in the folder's listing on every platform, the
exact name first, then ignoring case, since macOS and FAT/exFAT cards
ignore case; the refresh then takes the file's own name, so a
database's playlist stays one. Two or more playlists that match only
in other cases fail the item.
The playlist folder walk and each playlist load run in prep tasks: on
the worker with a threaded queue, under the shared I/O window without
one, each pushed from the previous step's callback. A playlist whose
saved content folder is gone is skipped rather than failed. A listing
that runs out of memory fails the refresh-all; a playlist folder that
is missing or cannot be read lists nothing, and the refresh-all ends
with "no playlists with saved scan settings". A refresh's start line
names its content folder; its totals and failure lines name the
playlist.
* retroarch: --scan-refresh and --scan-refresh-all
--scan-refresh=NAME|PATH rescans a playlist with its saved scan
settings, as the menu's Refresh Playlist does, and --scan-refresh-all
rescans every playlist that has them. Both are items of the same run
as --scan: the scan options after one are laid over the saved
settings and written back, and the exit status follows the same rule.
--scan-playlist after a refresh is refused, so a refresh always
updates its own playlist, and an empty --scan-refresh is refused as
the other empty values are. Like --scan, the refresh options cannot be
combined with --cloudsync or --command; the error names whichever of
the three comes first on the command line.
* menu: Refresh All Playlists
Manage Playlists gets Refresh All Playlists as its first entry when it
lists any playlist, so an empty list still says there are no entries.
It rescans every playlist that has saved scan settings, with those
settings, through the same chain as --scan-refresh-all, and ends with
one notification saying how many playlists were refreshed, skipped
(content folder gone) and failed.
While a content scan runs it is refused with a new message; a refused
scan had none. Other languages come through Crowdin.
While a scan chain runs (Refresh All, or a command-line scan with
--menu), Start Scan, a playlist's Refresh, Scan Directory and Scan
File are refused with the same message, as they would run with the
settings the chain last loaded. task_push_dbscan() returns false then
too, which covers the companion and the iOS caller.
* docs: --scan, refresh and the Manual Scan options in the man page
--help lists the options but not what a script needs: the order the
scans run in, the exit status and what the options set. The man page
now has them, next to --cloudsync, and CHANGES.md has the entry.
* scan chain: writes from workers reach the playlist cache by note
A playlist written by a task - the chain's prep parse, a scan, a
playlist-manager task, whatever thread it runs on - leaves a note on a
lock-free stack instead of reading the cached playlist; the main
thread applies the notes when it next decides whether the cache may
stand in, so no worker reads playlist_cached while the main thread
frees or installs it. Without threads or pointer atomics a write
applies at once.
The chain's prep tasks set their handlers by name, and
thread_read_audit.py fails on a handler set from a
retro_task_handler_t parameter, which would hide every handler passed
in. The chain puts the Manual Scan settings it found back when it
ends, and is no longer running when its done callback runs, so that
callback may start another. task_push_dbscan() says why it refuses,
so the menu and the companions show the same reason, and the
companions' status line takes it from companion_core. --scan and
--scan-refresh set the verbosity override, so a config with logging
off cannot detach the console the totals go to, and refuse a path too
long for the chain's buffers. The Refresh All notification passes the
length written, and fill_pathname_expand_special() stops at the end of
a buffer shorter than the home or application directory.
Lanes: playlist_parity_test writes the cached file from a worker,
once at the same size and then while the main thread reuses and frees
the cache; database_scan_test checks the refusal message, the restored
settings and a done callback that starts the next chain;
fill_pathname_test expands into a buffer shorter than the home
directory.
* playlist: header-only parse for the scan chain's prep
The prep task needs a playlist's saved scan settings and nothing else,
yet parsed every entry and, through autofix, could write the file back
from a worker. playlist_parse_begin_header() stops at "items", rebases
the scan record in memory when the base content directory moved, and
marks the playlist so playlist_write_file() refuses it.
Lane: playlist_parity_test header_only (entries skipped, scan dir
rebased, file bytes untouched, old format).
* scan chain: ask the file system nothing on the main thread
The chain's steps ran on the main thread between its tasks and stat'd
and realpath'd there: a --scan path's resolve and stat, a
--scan-refresh path's two realpaths, the content folder's stat, the
saved and override DATs' stat and size, an override core path's
realpath. On a network share each of those can stall a frame.
- manual_content_scan_fs_read() reads them on any thread, touching
nothing but its arguments; the _fs variants of the content-folder,
playlist and override setters take its answers instead of asking.
The plain setters pass NULL and behave as before, so the menu is
unchanged.
- A --scan path gets its own prep task (dbscan_path_handler); a
--scan-refresh resolves its path inside the load task, and the load
reads the folder, the saved DAT and the overrides' paths once the
header is parsed. The worker gets copies of the item's strings.
- The prep's state is freed in one place.
Lane: database_scan_test "fs off main": six items that each stop
before their scan, on a threaded queue, with path_stat, path_is_valid,
path_is_directory, path_get_size and path_resolve_realpath wrapped:
no call on the main thread.
Not moved: the scan task's own push still stats the content folder
and the DAT (manual_content_scan_get_task_config), shared with the
menu.
* samples: the scan chain's lanes hold on Windows and without realpath
From a native Windows run of both tests:
- database_scan_test "fs off main" refreshed f_gone.lpl through
"pl/../pl/", which only a realpath folds; the sample builds without
RARCH_INTERNAL, where path_resolve_realpath() folds nothing, so the
item was rightly not in the playlist folder. It names the playlist
by its plain path now, which the check still realpaths on the
worker.
- "another case, a database's playlist" read the system name type
after the chain ended, and the chain puts the Manual Scan settings
back then. The item callback records each scan's type instead.
- playlist_parity_test's header lane expected "/moved/snes"; autofix
converts the suffix to the local form, "/moved\snes" on Windows.
- The worker lane's stress read required a core name of every read.
Windows replaces the file with MoveFileExW, and an open that meets
the replace fails: that read is an empty playlist, which has none.
---------
Co-authored-by: LibretroAdmin <[email protected]>
495 lines
18 KiB
C
495 lines
18 KiB
C
/* Copyright (C) 2010-2019 The RetroArch team
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
* The following license statement only applies to this file (manual_content_scan.c).
|
|
* ---------------------------------------------------------------------------------------
|
|
*
|
|
* Permission is hereby granted, free of charge,
|
|
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation the rights to
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
|
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#ifndef __MANUAL_CONTENT_SCAN_H
|
|
#define __MANUAL_CONTENT_SCAN_H
|
|
|
|
#include <retro_common_api.h>
|
|
#include <libretro.h>
|
|
|
|
#include <boolean.h>
|
|
|
|
#include <lists/string_list.h>
|
|
#include <lists/dir_list.h>
|
|
#include <formats/logiqx_dat.h>
|
|
|
|
#include "playlist.h"
|
|
|
|
RETRO_BEGIN_DECLS
|
|
|
|
enum manual_content_scan_method
|
|
{
|
|
MANUAL_CONTENT_SCAN_METHOD_AUTOMATIC = 0,
|
|
MANUAL_CONTENT_SCAN_METHOD_CUSTOM
|
|
};
|
|
|
|
/* Defines all possible system name types
|
|
* > Use content directory name
|
|
* > Use custom name
|
|
* > Use database name */
|
|
enum manual_content_scan_system_name_type
|
|
{
|
|
MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR = 0,
|
|
MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM,
|
|
MANUAL_CONTENT_SCAN_SYSTEM_NAME_AUTO,
|
|
MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE
|
|
};
|
|
|
|
/* Defines types of matching content to databases */
|
|
enum manual_content_scan_db_usage
|
|
{
|
|
MANUAL_CONTENT_SCAN_USE_DB_STRICT = 0,
|
|
MANUAL_CONTENT_SCAN_USE_DB_LOOSE,
|
|
MANUAL_CONTENT_SCAN_USE_DB_DAT_STRICT,
|
|
MANUAL_CONTENT_SCAN_USE_DB_DAT_LOOSE,
|
|
MANUAL_CONTENT_SCAN_USE_DB_NONE
|
|
};
|
|
|
|
/* Defines database selection types */
|
|
enum manual_content_scan_db_selection
|
|
{
|
|
MANUAL_CONTENT_SCAN_SELECT_DB_AUTO = 0,
|
|
MANUAL_CONTENT_SCAN_SELECT_DB_AUTO_FIRST_MATCH,
|
|
MANUAL_CONTENT_SCAN_SELECT_DB_SPECIFIC
|
|
};
|
|
|
|
/* Defines all possible core name types
|
|
* > Autodetect core (DETECT)
|
|
* > Use manually set core */
|
|
enum manual_content_scan_core_type
|
|
{
|
|
MANUAL_CONTENT_SCAN_CORE_DETECT = 0,
|
|
MANUAL_CONTENT_SCAN_CORE_SET
|
|
};
|
|
|
|
/* Defines all possible return values for
|
|
* manual_content_scan_validate_dat_file_path() */
|
|
enum manual_content_scan_dat_file_path_status
|
|
{
|
|
MANUAL_CONTENT_SCAN_DAT_FILE_UNSET = 0,
|
|
MANUAL_CONTENT_SCAN_DAT_FILE_OK,
|
|
MANUAL_CONTENT_SCAN_DAT_FILE_INVALID,
|
|
MANUAL_CONTENT_SCAN_DAT_FILE_TOO_LARGE
|
|
};
|
|
|
|
/* Defines all possible return values for
|
|
* manual_content_scan_set_menu_from_playlist() */
|
|
enum manual_content_scan_playlist_refresh_status
|
|
{
|
|
MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_OK = 0,
|
|
MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_MISSING_CONFIG,
|
|
MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CONTENT_DIR,
|
|
MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_SYSTEM_NAME,
|
|
MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CORE,
|
|
MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_DAT_FILE,
|
|
MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_DAT_FILE_TOO_LARGE
|
|
};
|
|
|
|
/* Defines all possible return values for
|
|
* manual_content_scan_apply_overrides() */
|
|
enum manual_content_scan_override_status
|
|
{
|
|
MANUAL_CONTENT_SCAN_OVR_OK = 0,
|
|
MANUAL_CONTENT_SCAN_OVR_NO_SUCH_CORE,
|
|
MANUAL_CONTENT_SCAN_OVR_AMBIGUOUS_CORE,
|
|
MANUAL_CONTENT_SCAN_OVR_NO_SUCH_DATABASE,
|
|
MANUAL_CONTENT_SCAN_OVR_NOT_A_DAT,
|
|
MANUAL_CONTENT_SCAN_OVR_DAT_TOO_LARGE,
|
|
MANUAL_CONTENT_SCAN_OVR_DAT_WITHOUT_MATCH
|
|
};
|
|
|
|
enum manual_content_scan_override_flags
|
|
{
|
|
MANUAL_CONTENT_SCAN_OVR_MATCH = (1 << 0), /* 'match' is set */
|
|
MANUAL_CONTENT_SCAN_OVR_FIRST_DB = (1 << 1),
|
|
MANUAL_CONTENT_SCAN_OVR_NO_RECURSIVE = (1 << 2),
|
|
MANUAL_CONTENT_SCAN_OVR_NO_ARCHIVES = (1 << 3),
|
|
MANUAL_CONTENT_SCAN_OVR_OVERWRITE = (1 << 4),
|
|
MANUAL_CONTENT_SCAN_OVR_VALIDATE = (1 << 5),
|
|
MANUAL_CONTENT_SCAN_OVR_OMIT_DB_REF = (1 << 6)
|
|
};
|
|
|
|
/* Settings laid over a scan's own; a NULL string or a clear
|
|
* flag leaves its setting as it is */
|
|
typedef struct manual_content_scan_overrides
|
|
{
|
|
char *playlist; /* --scan-playlist */
|
|
char *database; /* --scan-database=NAME */
|
|
char *dat; /* --scan-dat, as typed */
|
|
char *core; /* --scan-core, as typed */
|
|
char *exts; /* --scan-exts, space separated */
|
|
enum manual_content_scan_db_usage match; /* STRICT, LOOSE or NONE */
|
|
uint16_t flags; /* MANUAL_CONTENT_SCAN_OVR_* */
|
|
} manual_content_scan_overrides_t;
|
|
|
|
/* Holds all configuration parameters required
|
|
* for a manual content scan task */
|
|
typedef struct
|
|
{
|
|
char core_name[NAME_MAX_LENGTH];
|
|
char system_name[NAME_MAX_LENGTH];
|
|
char database_name[NAME_MAX_LENGTH];
|
|
char content_dir[PATH_MAX_LENGTH];
|
|
char playlist_file[PATH_MAX_LENGTH];
|
|
char core_path[PATH_MAX_LENGTH];
|
|
char file_exts[PATH_MAX_LENGTH];
|
|
char dat_file_path[PATH_MAX_LENGTH];
|
|
bool core_set;
|
|
bool file_exts_custom_set;
|
|
bool search_recursively;
|
|
bool search_archives;
|
|
bool filter_dat_content;
|
|
bool overwrite_playlist;
|
|
bool validate_entries;
|
|
bool omit_db_reference;
|
|
bool target_is_single_determined_playlist;
|
|
bool content_is_file;
|
|
enum manual_content_scan_db_usage db_usage;
|
|
enum manual_content_scan_db_selection db_selection;
|
|
/* Captured when the config is built, on the main thread: the scan
|
|
* handler runs on the threaded task queue's worker and reads these
|
|
* from its config, never from the live settings or runloop. */
|
|
bool cli_scan_output;
|
|
bool scan_serial_and_crc;
|
|
} manual_content_scan_task_config_t;
|
|
|
|
/*****************/
|
|
/* Configuration */
|
|
/*****************/
|
|
|
|
/* Pointer access
|
|
* > This is a little ugly, but it allows us to
|
|
* make use of standard 'menu_settings' code
|
|
* for several config parameters (rather than
|
|
* implementing unnecessary custom menu entries) */
|
|
|
|
/* Returns a pointer to the internal
|
|
* 'content_dir' string */
|
|
char *manual_content_scan_get_content_dir_ptr(void);
|
|
|
|
unsigned manual_content_scan_get_scan_method_enum(void);
|
|
unsigned manual_content_scan_get_scan_use_db_enum(void);
|
|
unsigned manual_content_scan_get_scan_db_select_enum(void);
|
|
|
|
char *manual_content_scan_get_scan_db_select_custom_ptr(void);
|
|
size_t *manual_content_scan_get_scan_db_select_custom_size(void);
|
|
|
|
/* Returns a pointer to the internal
|
|
* 'system_name_custom' string */
|
|
char *manual_content_scan_get_system_name_custom_ptr(void);
|
|
|
|
/* Returns size of the internal
|
|
* 'system_name_custom' string */
|
|
size_t manual_content_scan_get_system_name_custom_size(void);
|
|
|
|
/* Returns a pointer to the internal
|
|
* 'file_exts_custom' string */
|
|
char *manual_content_scan_get_file_exts_custom_ptr(void);
|
|
|
|
/* Returns size of the internal
|
|
* 'file_exts_custom' string */
|
|
size_t manual_content_scan_get_file_exts_custom_size(void);
|
|
|
|
/* Returns custom extensions,
|
|
* falling back to core-supported extensions */
|
|
const char *manual_content_scan_get_file_exts(void);
|
|
|
|
/* Returns a pointer to the internal
|
|
* 'dat_file_path' string */
|
|
char *manual_content_scan_get_dat_file_path_ptr(void);
|
|
|
|
/* Returns size of the internal
|
|
* 'dat_file_path' string */
|
|
size_t manual_content_scan_get_dat_file_path_size(void);
|
|
|
|
/* Returns a pointer to the internal
|
|
* 'search_recursively' bool */
|
|
bool *manual_content_scan_get_search_recursively_ptr(void);
|
|
|
|
bool *manual_content_scan_get_omit_db_ref_ptr(void);
|
|
/* Returns a pointer to the internal
|
|
* 'search_archives' bool */
|
|
bool *manual_content_scan_get_search_archives_ptr(void);
|
|
|
|
/* Returns a pointer to the internal
|
|
* 'filter_dat_content' bool */
|
|
bool *manual_content_scan_get_filter_dat_content_ptr(void);
|
|
|
|
/* Returns a pointer to the internal
|
|
* 'overwrite_playlist' bool */
|
|
bool *manual_content_scan_get_overwrite_playlist_ptr(void);
|
|
|
|
/* Returns a pointer to the internal
|
|
* 'validate_entries' bool */
|
|
bool *manual_content_scan_get_validate_entries_ptr(void);
|
|
|
|
/* Sanitisation */
|
|
|
|
/* Removes invalid characters from
|
|
* 'system_name_custom' string */
|
|
void manual_content_scan_scrub_system_name_custom(void);
|
|
|
|
/* Removes period (full stop) characters from
|
|
* 'file_exts_custom' string and converts to
|
|
* lower case */
|
|
void manual_content_scan_scrub_file_exts_custom(void);
|
|
|
|
/* Checks 'dat_file_path' string and resets it
|
|
* if invalid */
|
|
enum manual_content_scan_dat_file_path_status
|
|
manual_content_scan_validate_dat_file_path(void);
|
|
|
|
/* Menu setters */
|
|
|
|
/* Sets every setting back to its default */
|
|
void manual_content_scan_reset_settings(void);
|
|
|
|
/* The Manual Scan settings as they are, for a scan chain to put back
|
|
* once it has run its own: NULL out of memory. Restoring frees it. */
|
|
typedef struct manual_content_scan_saved manual_content_scan_saved_t;
|
|
manual_content_scan_saved_t *manual_content_scan_save_settings(void);
|
|
void manual_content_scan_restore_settings(manual_content_scan_saved_t *saved);
|
|
|
|
/* Sets content directory for next manual scan
|
|
* operation.
|
|
* Returns true if content directory is valid. */
|
|
bool manual_content_scan_set_menu_content_dir(const char *content_dir);
|
|
|
|
/* What the file system says of one scan's paths, read off the main
|
|
* thread by manual_content_scan_fs_read(), so that the _fs setters
|
|
* below ask it nothing. Only the paths it was read for: it holds no
|
|
* reference to the settings. */
|
|
typedef struct manual_content_scan_fs
|
|
{
|
|
char *ov_dat; /* @ov_dat expanded and resolved, or NULL */
|
|
char *ov_core; /* @ov_core resolved when it is a path, or NULL */
|
|
int64_t dat_size; /* the saved DAT's size; <= 0: not there */
|
|
int64_t ov_dat_size; /* likewise, @ov_dat's */
|
|
int content_stat; /* path_stat() of the content folder */
|
|
} manual_content_scan_fs_t;
|
|
|
|
/* Any thread: reads nothing but its arguments and the file system.
|
|
* @content_dir and @dat as the settings hold them, before expansion;
|
|
* @ov_core and @ov_dat an item's overrides, each NULL when unset.
|
|
* NULL out of memory. Free with manual_content_scan_fs_free(). */
|
|
manual_content_scan_fs_t *manual_content_scan_fs_read(
|
|
const char *content_dir, const char *dat,
|
|
const char *ov_core, const char *ov_dat);
|
|
void manual_content_scan_fs_free(manual_content_scan_fs_t *fs);
|
|
|
|
/* As manual_content_scan_set_menu_content_dir(), the folder's stat
|
|
* taken from @fs (NULL: asked now) */
|
|
bool manual_content_scan_set_menu_content_dir_fs(const char *content_dir,
|
|
const manual_content_scan_fs_t *fs);
|
|
|
|
bool manual_content_scan_set_menu_scan_method(
|
|
enum manual_content_scan_method method);
|
|
|
|
bool manual_content_scan_set_menu_scan_use_db(
|
|
enum manual_content_scan_db_usage usage);
|
|
|
|
bool manual_content_scan_set_menu_scan_db_select(
|
|
enum manual_content_scan_db_selection select,
|
|
const char *db_name);
|
|
|
|
/* Sets system name for the next manual scan
|
|
* operation.
|
|
* Returns true if system name is valid.
|
|
* NOTE:
|
|
* > Only sets 'system_name_type' and 'system_name_database'
|
|
* > 'system_name_content_dir' and 'system_name_custom' are
|
|
* (by necessity) handled elsewhere
|
|
* > This may look fishy, but it's not - it's a menu-specific
|
|
* function, and this is simply the cleanest way to handle
|
|
* the setting... */
|
|
bool manual_content_scan_set_menu_system_name(
|
|
enum manual_content_scan_system_name_type system_name_type,
|
|
const char *system_name);
|
|
|
|
/* Sets core name for the next manual scan
|
|
* operation (+ core path and other associated
|
|
* parameters).
|
|
* Returns true if core name is valid. */
|
|
bool manual_content_scan_set_menu_core_name(
|
|
enum manual_content_scan_core_type core_type,
|
|
const char *core_name);
|
|
|
|
/* Sets all parameters for the next manual scan
|
|
* operation according the to recorded values in
|
|
* the specified playlist.
|
|
* Returns MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_OK
|
|
* if playlist contains a valid scan record. */
|
|
enum manual_content_scan_playlist_refresh_status
|
|
manual_content_scan_set_menu_from_playlist(playlist_t *playlist,
|
|
const char *path_content_database, bool show_hidden_files);
|
|
|
|
/* As manual_content_scan_set_menu_from_playlist(), with the
|
|
* paths of the installed databases in @rdb_list (NULL: none). */
|
|
enum manual_content_scan_playlist_refresh_status
|
|
manual_content_scan_set_menu_from_playlist_list(playlist_t *playlist,
|
|
const struct string_list *rdb_list);
|
|
|
|
/* As manual_content_scan_set_menu_from_playlist_list(), the folder
|
|
* and the DAT as @fs read them (NULL: asked now) */
|
|
enum manual_content_scan_playlist_refresh_status
|
|
manual_content_scan_set_menu_from_playlist_list_fs(
|
|
playlist_t *playlist, const struct string_list *rdb_list,
|
|
const manual_content_scan_fs_t *fs);
|
|
|
|
manual_content_scan_overrides_t *manual_content_scan_overrides_new(void);
|
|
void manual_content_scan_overrides_free(manual_content_scan_overrides_t *ov);
|
|
|
|
/* Lays @ov over the current settings and sets the Custom
|
|
* method, stopping at the first override that does not
|
|
* resolve; NULL @ov changes nothing.
|
|
* > @rdb_list: paths of the installed databases, or NULL
|
|
* > @count: the cores @ov->core matched, when not NULL */
|
|
enum manual_content_scan_override_status
|
|
manual_content_scan_apply_overrides(
|
|
const manual_content_scan_overrides_t *ov,
|
|
const struct string_list *rdb_list, unsigned *count);
|
|
|
|
/* As manual_content_scan_apply_overrides(), the core path and the
|
|
* DAT as @fs read them for @ov (NULL: asked now) */
|
|
enum manual_content_scan_override_status
|
|
manual_content_scan_apply_overrides_fs(
|
|
const manual_content_scan_overrides_t *ov,
|
|
const struct string_list *rdb_list, unsigned *count,
|
|
const manual_content_scan_fs_t *fs);
|
|
|
|
/* Menu getters */
|
|
|
|
/* Fetches content directory for next manual scan
|
|
* operation.
|
|
* Returns true if content directory is valid. */
|
|
bool manual_content_scan_get_menu_content_dir(const char **content_dir);
|
|
|
|
bool manual_content_scan_get_menu_scan_method(const char **scan_method);
|
|
bool manual_content_scan_get_menu_scan_use_db(const char **scan_use_db);
|
|
bool manual_content_scan_get_menu_scan_db_select(const char **scan_db_select);
|
|
|
|
/* Fetches system name for the next manual scan operation.
|
|
* Returns true if system name is valid.
|
|
* NOTE: This corresponds to the 'System Name' value
|
|
* displayed in menus - this is not identical to the
|
|
* actual system name used when generating the playlist */
|
|
bool manual_content_scan_get_menu_system_name(const char **system_name);
|
|
unsigned manual_content_scan_get_menu_system_name_type(void);
|
|
|
|
/* Fetches core name for the next manual scan operation.
|
|
* Returns true if core name is valid. */
|
|
bool manual_content_scan_get_menu_core_name(const char **core_name);
|
|
|
|
/* Menu utility functions */
|
|
|
|
struct string_list *manual_content_scan_get_menu_scan_method_list(void);
|
|
struct string_list *manual_content_scan_get_menu_scan_use_db_list(void);
|
|
struct string_list *manual_content_scan_get_menu_scan_db_select_list(
|
|
const char *path_content_database, bool show_hidden_files);
|
|
|
|
/* Creates a list of all possible 'system name' menu
|
|
* strings, for use in 'menu_displaylist' drop-down
|
|
* lists and 'menu_cbs_left/right'
|
|
* > Returns NULL in the event of failure
|
|
* > Returned string list must be free()'d */
|
|
struct string_list *manual_content_scan_get_menu_system_name_list(
|
|
const char *path_content_database, bool show_hidden_files);
|
|
|
|
/* Creates a list of all possible 'core name' menu
|
|
* strings, for use in 'menu_displaylist' drop-down
|
|
* lists and 'menu_cbs_left/right'
|
|
* > Returns NULL in the event of failure
|
|
* > Returned string list must be free()'d */
|
|
struct string_list *manual_content_scan_get_menu_core_name_list(void);
|
|
|
|
/****************/
|
|
/* Task Helpers */
|
|
/****************/
|
|
|
|
/* True if a scan of the current settings writes one playlist
|
|
* (target_is_single_determined_playlist). */
|
|
bool manual_content_scan_is_single_playlist(void);
|
|
|
|
/* Parses current manual content scan settings,
|
|
* and extracts all information required to configure
|
|
* a manual content scan task.
|
|
* Returns false if current settings are invalid. */
|
|
bool manual_content_scan_get_task_config(
|
|
manual_content_scan_task_config_t *task_config,
|
|
const char *path_dir_playlist
|
|
);
|
|
|
|
/* Performs rudimentary validation of a Logiqx XML DAT
|
|
* file path: extension whitelist (.dat / .xml,
|
|
* case-insensitive), existence, size > 0, with the
|
|
* size optionally returned for free-memory checks.
|
|
* The parser itself (logiqx_dat.c) is path-agnostic
|
|
* and performs no validation or I/O; every consumer
|
|
* of a DAT path goes through this one predicate. */
|
|
bool manual_content_scan_dat_path_is_valid(const char *path,
|
|
uint64_t *file_size);
|
|
|
|
/* Creates a list of all valid content in the specified
|
|
* content directory
|
|
* > Returns NULL in the event of failure
|
|
* > Returned string list must be free()'d */
|
|
struct string_list *manual_content_scan_get_content_list(
|
|
manual_content_scan_task_config_t *task_config);
|
|
|
|
/* Resumable counterpart of
|
|
* manual_content_scan_get_content_list(): creates
|
|
* @*list and, when the content dir is a directory, a
|
|
* dir_list_iter_t in @*iter that fills it under the
|
|
* caller's budget via dir_list_iter_step(). The same
|
|
* extension / compressed-file policy is applied. A
|
|
* content dir that is a plain file fills the list here
|
|
* and leaves *iter NULL. When the walk completes the
|
|
* caller owes the list the same treatment the blocking
|
|
* getter applied: reject it when empty, then
|
|
* dir_list_sort(list, true).
|
|
* > Returns false (leaving *list / *iter NULL) if
|
|
* config is invalid or the directory cannot be
|
|
* opened */
|
|
bool manual_content_scan_content_list_iter_new(
|
|
manual_content_scan_task_config_t *task_config,
|
|
struct string_list **list, dir_list_iter_t **iter);
|
|
|
|
/* Adds specified content to playlist, if not already
|
|
* present */
|
|
void manual_content_scan_add_content_to_playlist(
|
|
manual_content_scan_task_config_t *task_config,
|
|
playlist_t *playlist, const char *content_path,
|
|
int content_type, logiqx_dat_t *dat_file);
|
|
|
|
bool manual_content_scan_get_playlist_content_label(
|
|
const char *content_path, logiqx_dat_t *dat_file,
|
|
bool filter_dat_content,
|
|
char *s, size_t len);
|
|
bool manual_content_scan_get_playlist_content_path(
|
|
manual_content_scan_task_config_t *task_config,
|
|
const char *content_path, int content_type,
|
|
char *s, size_t len);
|
|
|
|
RETRO_END_DECLS
|
|
|
|
#endif
|