Merge pull request #9816 from jdgleaver/manual-scan-arcade-dat

(Manual Content Scanner) Enable automatic naming of arcade content via DAT files
This commit is contained in:
Twinaphex 2019-12-05 04:02:15 +01:00 committed by GitHub
commit ed20c14f23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 927 additions and 27 deletions

View File

@ -852,9 +852,6 @@ ifeq ($(HAVE_VIDEO_LAYOUT), 1)
gfx/video_layout/internal.o \
gfx/video_layout/scope.o \
gfx/video_layout/load.o
OBJ += \
$(LIBRETRO_COMM_DIR)/formats/xml/rxml.o \
deps/yxml/yxml.o
endif
ifeq ($(HAVE_STB_FONT), 1)
@ -989,6 +986,12 @@ ifeq ($(HAVE_WAYLAND), 1)
endif
# XML
OBJ += \
$(LIBRETRO_COMM_DIR)/formats/xml/rxml.o \
$(LIBRETRO_COMM_DIR)/formats/logiqx_dat/logiqx_dat.o \
deps/yxml/yxml.o
#Input
ifeq ($(HAVE_DINPUT), 1)

View File

@ -1484,10 +1484,9 @@ DEPENDENCIES
/*============================================================
XML
============================================================ */
#ifdef HAVE_VIDEO_LAYOUT
#include "../libretro-common/formats/xml/rxml.c"
#include "../libretro-common/formats/logiqx_dat/logiqx_dat.c"
#include "../deps/yxml/yxml.c"
#endif
/*============================================================
AUDIO UTILS

View File

@ -2129,6 +2129,8 @@ MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_FILE_EXTS,
"manual_content_scan_file_exts")
MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES,
"manual_content_scan_search_archives")
MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DAT_FILE,
"manual_content_scan_dat_file")
MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_OVERWRITE,
"manual_content_scan_overwrite")
MSG_HASH(MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_START,

View File

@ -10108,6 +10108,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES,
"When enabled, archive files (.zip, .7z, etc.) will be searched for valid/supported content. May have a significant impact on scan performance."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_DAT_FILE,
"Arcade DAT File"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_DAT_FILE,
"Select a Logiqx or MAME List XML DAT file to enable automatic naming of scanned arcade content (MAME, FinalBurn Neo, etc.)."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_OVERWRITE,
"Overwrite Existing Playlist"
@ -10136,6 +10144,18 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_CORE_NAME_DETECT,
"<Unspecified>"
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_DAT_FILE_INVALID,
"Invalid arcade DAT file selected"
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_DAT_FILE_TOO_LARGE,
"Selected arcade DAT file is too large (insufficient free memory)"
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_DAT_FILE_LOAD_ERROR,
"Failed to load arcade DAT file (invalid format?)"
)
MSG_HASH(
MSG_MANUAL_CONTENT_SCAN_INVALID_CONFIG,
"Invalid manual scan configuration"

View File

@ -0,0 +1,455 @@
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (logiqx_dat.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <file/file_path.h>
#include <string/stdstring.h>
#include <formats/rxml.h>
#include <formats/logiqx_dat.h>
/* Holds all internal DAT file data */
struct logiqx_dat
{
rxml_document_t *data;
rxml_node_t *current_node;
};
/* List of HTML formatting codes that must
* be replaced when parsing XML data */
const char *logiqx_dat_html_code_list[][2] = {
{"&amp;", "&"},
{"&apos;", "'"},
{"&gt;", ">"},
{"&lt;", "<"},
{"&quot;", "\""}
};
#define LOGIQX_DAT_HTML_CODE_LIST_SIZE 5
/* Validation */
/* Performs rudimentary validation of the specified
* Logiqx XML DAT file path (not rigorous - just
* enough to prevent obvious errors).
* Also provides access to file size (DAT files can
* be very large, so it is useful to have this information
* on hand - i.e. so we can check that the system has
* enough free memory to load the file). */
bool logiqx_dat_path_is_valid(const char *path, uint64_t *file_size)
{
const char *file_ext = NULL;
int32_t file_size_int;
if (string_is_empty(path))
return false;
/* Check file extension */
file_ext = path_get_extension(path);
if (string_is_empty(file_ext))
return false;
if (!string_is_equal_noncase(file_ext, "dat") &&
!string_is_equal_noncase(file_ext, "xml"))
return false;
/* Ensure file exists */
if (!path_is_valid(path))
return false;
/* Get file size */
file_size_int = path_get_size(path);
if (file_size_int <= 0)
return false;
if (file_size)
*file_size = (uint64_t)file_size_int;
return true;
}
/* File initialisation/de-initialisation */
/* Loads specified Logiqx XML DAT file from disk.
* Returned logiqx_dat_t object must be free'd using
* logiqx_dat_free().
* Returns NULL if file is invalid or a read error
* occurs. */
logiqx_dat_t *logiqx_dat_init(const char *path)
{
logiqx_dat_t *dat_file = NULL;
rxml_node_t *root_node = NULL;
/* Check file path */
if (!logiqx_dat_path_is_valid(path, NULL))
goto error;
/* Create logiqx_dat_t object */
dat_file = (logiqx_dat_t*)calloc(1, sizeof(*dat_file));
if (!dat_file)
goto error;
/* Read file from disk */
dat_file->data = rxml_load_document(path);
if (!dat_file->data)
goto error;
/* Ensure root node has the correct name */
root_node = rxml_root_node(dat_file->data);
if (!root_node)
goto error;
if (string_is_empty(root_node->name))
goto error;
/* > Logiqx XML uses: 'datafile'
* > MAME List XML uses: 'mame'
* > MAME 'Software List' uses: 'softwarelist' */
if (!string_is_equal(root_node->name, "datafile") &&
!string_is_equal(root_node->name, "mame") &&
!string_is_equal(root_node->name, "softwarelist"))
goto error;
/* Get pointer to initial child node */
dat_file->current_node = root_node->children;
if (!dat_file->current_node)
goto error;
/* All is well - return logiqx_dat_t object */
return dat_file;
error:
logiqx_dat_free(dat_file);
return NULL;
}
/* Frees specified DAT file */
void logiqx_dat_free(logiqx_dat_t *dat_file)
{
if (!dat_file)
return;
dat_file->current_node = NULL;
if (dat_file->data)
{
rxml_free_document(dat_file->data);
dat_file->data = NULL;
}
free(dat_file);
dat_file = NULL;
}
/* Game information access */
/* Returns true if specified node is a 'game' entry */
static bool logiqx_dat_is_game_node(rxml_node_t *node)
{
const char *node_name = NULL;
if (!node)
return false;
/* Check node name */
node_name = node->name;
if (string_is_empty(node_name))
return false;
/* > Logiqx XML uses: 'game'
* > MAME List XML uses: 'machine'
* > MAME 'Software List' uses: 'software' */
return string_is_equal(node_name, "game") ||
string_is_equal(node_name, "machine") ||
string_is_equal(node_name, "software");
}
/* Returns true if specified node is a game
* node containing information for a game with
* the specified name */
static bool logiqx_dat_game_node_matches_name(
rxml_node_t *node, const char *game_name)
{
const char *node_game_name = NULL;
if (!logiqx_dat_is_game_node(node) ||
string_is_empty(game_name))
return false;
/* Get 'name' attribute of XML node */
node_game_name = rxml_node_attrib(node, "name");
if (string_is_empty(node_game_name))
return false;
return string_is_equal(node_game_name, game_name);
}
/* The XML element data strings returned from
* DAT files are very 'messy'. This function
* removes all cruft, replaces formatting strings
* and copies the result (if valid) to 'str' */
static void logiqx_dat_sanitise_element_data(
const char *data, char *str, size_t len)
{
char sanitised_data[PATH_MAX_LENGTH];
size_t i;
sanitised_data[0] = '\0';
if (string_is_empty(data))
return;
strlcpy(sanitised_data, data, sizeof(sanitised_data));
/* Element data includes leading/trailing
* newline characters - trim them away */
string_trim_whitespace(sanitised_data);
if (string_is_empty(sanitised_data))
return;
/* XML has a number of special characters that
* are handled using a HTML formatting codes.
* All of these have to be replaced...
* &amp; -> &
* &apos; -> '
* &gt; -> >
* &lt; -> <
* &quot; -> "
*/
for (i = 0; i < LOGIQX_DAT_HTML_CODE_LIST_SIZE; i++)
{
const char *find_string = logiqx_dat_html_code_list[i][0];
const char *replace_string = logiqx_dat_html_code_list[i][1];
/* string_replace_substring() is expensive
* > only invoke if element string contains
* HTML code */
if (strstr(sanitised_data, find_string))
{
char *tmp = string_replace_substring(
sanitised_data, find_string, replace_string);
if (!string_is_empty(tmp))
strlcpy(sanitised_data, tmp, sizeof(sanitised_data));
if (tmp)
free(tmp);
}
}
if (string_is_empty(sanitised_data))
return;
/* All is well - can copy result */
strlcpy(str, sanitised_data, len);
}
/* Extracts game information from specified node.
* Returns false if node is invalid */
static bool logiqx_dat_parse_game_node(
rxml_node_t *node, logiqx_dat_game_info_t *game_info)
{
const char *game_name = NULL;
const char *is_bios = NULL;
const char *is_runnable = NULL;
rxml_node_t *info_node = NULL;
bool description_found = false;
bool year_found = false;
bool manufacturer_found = false;
if (!logiqx_dat_is_game_node(node))
return false;
if (!game_info)
return false;
/* Initialise logiqx_dat_game_info_t object */
game_info->name[0] = '\0';
game_info->description[0] = '\0';
game_info->year[0] = '\0';
game_info->manufacturer[0] = '\0';
game_info->is_bios = false;
game_info->is_runnable = true;
/* Get game name */
game_name = rxml_node_attrib(node, "name");
if (!string_is_empty(game_name))
strlcpy(game_info->name, game_name, sizeof(game_info->name));
/* Get 'is bios' status */
is_bios = rxml_node_attrib(node, "isbios");
if (!string_is_empty(is_bios))
game_info->is_bios = string_is_equal(is_bios, "yes");
/* Get 'is runnable' status
* > Note: This attribute only exists in MAME List
* XML files, but there is no harm in checking for
* it generally. For normal Logiqx XML files,
* 'is runnable' is just the inverse of 'is bios' */
is_runnable = rxml_node_attrib(node, "runnable");
if (!string_is_empty(is_runnable))
game_info->is_runnable = string_is_equal(is_runnable, "yes");
else
game_info->is_runnable = !game_info->is_bios;
/* Loop over all game info nodes */
for (info_node = node->children; info_node; info_node = info_node->next)
{
const char *info_node_name = info_node->name;
const char *info_node_data = info_node->data;
if (string_is_empty(info_node_name))
continue;
/* Check description */
if (string_is_equal(info_node_name, "description"))
{
logiqx_dat_sanitise_element_data(
info_node_data, game_info->description,
sizeof(game_info->description));
description_found = true;
}
/* Check year */
else if (string_is_equal(info_node_name, "year"))
{
logiqx_dat_sanitise_element_data(
info_node_data, game_info->year,
sizeof(game_info->year));
year_found = true;
}
/* Check manufacturer */
else if (string_is_equal(info_node_name, "manufacturer"))
{
logiqx_dat_sanitise_element_data(
info_node_data, game_info->manufacturer,
sizeof(game_info->manufacturer));
manufacturer_found = true;
}
/* If all required entries have been found,
* can end loop */
if (description_found && year_found && manufacturer_found)
break;
}
return true;
}
/* Sets/resets internal node pointer to the first
* entry in the DAT file */
void logiqx_dat_set_first(logiqx_dat_t *dat_file)
{
rxml_node_t *root_node = NULL;
if (!dat_file)
return;
if (!dat_file->data)
return;
/* Get root node */
root_node = rxml_root_node(dat_file->data);
if (!root_node)
{
dat_file->current_node = NULL;
return;
}
/* Get pointer to initial child node */
dat_file->current_node = root_node->children;
}
/* Fetches game information for the current entry
* in the DAT file and increments the internal node
* pointer.
* Returns false if the end of the DAT file has been
* reached (in which case 'game_info' will be invalid) */
bool logiqx_dat_get_next(
logiqx_dat_t *dat_file, logiqx_dat_game_info_t *game_info)
{
if (!dat_file || !game_info)
return false;
if (!dat_file->data)
return false;
while (dat_file->current_node)
{
rxml_node_t *current_node = dat_file->current_node;
/* Whatever happens, internal node pointer must
* be 'incremented' */
dat_file->current_node = dat_file->current_node->next;
/* If this is a game node, extract info
* and return */
if (logiqx_dat_is_game_node(current_node))
return logiqx_dat_parse_game_node(current_node, game_info);
}
return false;
}
/* Fetches information for the specified game.
* Returns false if game does not exist, or arguments
* are invalid. */
bool logiqx_dat_search(
logiqx_dat_t *dat_file, const char *game_name,
logiqx_dat_game_info_t *game_info)
{
rxml_node_t *root_node = NULL;
rxml_node_t *game_node = NULL;
if (!dat_file || !game_info || string_is_empty(game_name))
return false;
if (!dat_file->data)
return false;
/* Get root node */
root_node = rxml_root_node(dat_file->data);
if (!root_node)
return false;
/* Loop over all child nodes of the DAT file */
for (game_node = root_node->children; game_node; game_node = game_node->next)
{
/* If this is the requested game, fetch info and return */
if (logiqx_dat_game_node_matches_name(game_node, game_name))
return logiqx_dat_parse_game_node(game_node, game_info);
}
return false;
}

View File

@ -66,19 +66,15 @@ static void rxml_free_node(struct rxml_node *node)
for (attrib_node_head = node->attrib; attrib_node_head; )
{
struct rxml_attrib_node *next_attrib = NULL;
struct rxml_attrib_node *next_attrib =
(struct rxml_attrib_node*)attrib_node_head->next;
next_attrib = (struct rxml_attrib_node*)attrib_node_head->next;
if (next_attrib)
{
if (attrib_node_head->attrib)
free(attrib_node_head->attrib);
if (attrib_node_head->value)
free(attrib_node_head->value);
if (attrib_node_head)
free(attrib_node_head);
}
if (attrib_node_head->attrib)
free(attrib_node_head->attrib);
if (attrib_node_head->value)
free(attrib_node_head->value);
if (attrib_node_head)
free(attrib_node_head);
attrib_node_head = next_attrib;
}
@ -161,6 +157,7 @@ rxml_document_t *rxml_load_document_string(const char *str)
case YXML_ELEMSTART:
if (node) {
if (level > stack_i) {
buf->stack[stack_i] = node;
++stack_i;
@ -177,7 +174,10 @@ rxml_document_t *rxml_load_document_string(const char *str)
node = doc->root_node = (rxml_node_t*)calloc(1, sizeof(*node));
}
if (node->name)
free(node->name);
node->name = strdup(x.elem);
attr = NULL;
++level;
@ -188,7 +188,25 @@ rxml_document_t *rxml_load_document_string(const char *str)
if (valptr > buf->val) {
*valptr = '\0';
node->data = strdup(buf->val);
/* Original code was broken here:
* > If an element ended on two successive
* iterations, on the second iteration
* the 'data' for the *previous* node would
* get overwritten
* > This effectively erased the data for the
* previous node, *and* caused a memory leak
* (due to the double strdup())
* It seems the correct thing to do here is
* only copy the data if the current 'level'
* and 'stack index' are the same... */
if (level == stack_i)
{
if (node->data)
free(node->data);
node->data = strdup(buf->val);
}
valptr = buf->val;
}
@ -211,7 +229,10 @@ rxml_document_t *rxml_load_document_string(const char *str)
else
attr = node->attrib = (struct rxml_attrib_node*)calloc(1, sizeof(*attr));
if (attr->attrib)
free(attr->attrib);
attr->attrib = strdup(x.attr);
valptr = buf->val;
break;
@ -225,7 +246,11 @@ rxml_document_t *rxml_load_document_string(const char *str)
case YXML_ATTREND:
if (valptr > buf->val) {
*valptr = '\0';
if (attr->value)
free(attr->value);
attr->value = strdup(buf->val);
valptr = buf->val;
}
break;

View File

@ -0,0 +1,107 @@
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (logiqx_dat.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_FORMAT_LOGIQX_DAT_H__
#define __LIBRETRO_SDK_FORMAT_LOGIQX_DAT_H__
#include <retro_common_api.h>
#include <retro_miscellaneous.h>
#include <boolean.h>
RETRO_BEGIN_DECLS
/* Trivial handler for DAT files in Logiqx XML format
* (http://www.logiqx.com/). Provides bare minimum
* functionality - predominantly concerned with obtaining
* description text for specific arcade ROM images.
*
* Note: Also supports the following alternative DAT
* formats, since they are functionally identical to
* Logiqx XML (but with different element names):
* > MAME List XML
* > MAME 'Software List' */
/* Prevent direct access to logiqx_dat_t members */
typedef struct logiqx_dat logiqx_dat_t;
/* Holds all metadata for a single game entry
* in the DAT file (minimal at present - may be
* expanded with individual internal ROM data
* if required) */
typedef struct
{
char name[PATH_MAX_LENGTH];
char description[PATH_MAX_LENGTH];
char year[8];
char manufacturer[128];
bool is_bios;
bool is_runnable;
} logiqx_dat_game_info_t;
/* Validation */
/* Performs rudimentary validation of the specified
* Logiqx XML DAT file path (not rigorous - just
* enough to prevent obvious errors).
* Also provides access to file size (DAT files can
* be very large, so it is useful to have this information
* on hand - i.e. so we can check that the system has
* enough free memory to load the file). */
bool logiqx_dat_path_is_valid(const char *path, uint64_t *file_size);
/* File initialisation/de-initialisation */
/* Loads specified Logiqx XML DAT file from disk.
* Returned logiqx_dat_t object must be free'd using
* logiqx_dat_free().
* Returns NULL if file is invalid or a read error
* occurs. */
logiqx_dat_t *logiqx_dat_init(const char *path);
/* Frees specified DAT file */
void logiqx_dat_free(logiqx_dat_t *dat_file);
/* Game information access */
/* Sets/resets internal node pointer to the first
* entry in the DAT file */
void logiqx_dat_set_first(logiqx_dat_t *dat_file);
/* Fetches game information for the current entry
* in the DAT file and increments the internal node
* pointer.
* Returns false if the end of the DAT file has been
* reached (in which case 'game_info' will be invalid) */
bool logiqx_dat_get_next(
logiqx_dat_t *dat_file, logiqx_dat_game_info_t *game_info);
/* Fetches information for the specified game.
* Returns false if game does not exist, or arguments
* are invalid. */
bool logiqx_dat_search(
logiqx_dat_t *dat_file, const char *game_name,
logiqx_dat_game_info_t *game_info);
RETRO_END_DECLS
#endif

View File

@ -64,4 +64,6 @@ struct rxml_node *rxml_root_node(rxml_document_t *doc);
const char *rxml_node_attrib(struct rxml_node *node, const char *attrib);
RETRO_END_DECLS
#endif

View File

@ -32,6 +32,8 @@
#include "core_info.h"
#include "file_path_special.h"
#include "frontend/frontend_driver.h"
#include "manual_content_scan.h"
/* Holds all configuration parameters associated
@ -46,6 +48,7 @@ typedef struct
char core_path[PATH_MAX_LENGTH];
char file_exts_core[PATH_MAX_LENGTH];
char file_exts_custom[PATH_MAX_LENGTH];
char dat_file_path[PATH_MAX_LENGTH];
enum manual_content_scan_system_name_type system_name_type;
enum manual_content_scan_core_type core_type;
bool search_archives;
@ -70,6 +73,7 @@ static scan_settings_t scan_settings = {
"", /* core_path */
"", /* file_exts_core */
"", /* file_exts_custom */
"", /* dat_file_path */
MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR, /* system_name_type */
MANUAL_CONTENT_SCAN_CORE_DETECT, /* core_type */
false, /* search_archives */
@ -110,6 +114,20 @@ size_t manual_content_scan_get_file_exts_custom_size(void)
return sizeof(scan_settings.file_exts_custom);
}
/* Returns a pointer to the internal
* 'dat_file_path' string */
char *manual_content_scan_get_dat_file_path_ptr(void)
{
return scan_settings.dat_file_path;
}
/* Returns size of the internal
* 'dat_file_path' string */
size_t manual_content_scan_get_dat_file_path_size(void)
{
return sizeof(scan_settings.dat_file_path);
}
/* Returns a pointer to the internal
* 'search_archives' bool */
bool *manual_content_scan_get_search_archives_ptr(void)
@ -165,6 +183,61 @@ void manual_content_scan_scrub_file_exts_custom(void)
manual_content_scan_scrub_file_exts(scan_settings.file_exts_custom);
}
/* 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)
{
enum manual_content_scan_dat_file_path_status dat_file_path_status =
MANUAL_CONTENT_SCAN_DAT_FILE_UNSET;
/* Check if 'dat_file_path' has been set */
if (!string_is_empty(scan_settings.dat_file_path))
{
uint64_t file_size;
/* Check if path itself is valid */
if (logiqx_dat_path_is_valid(scan_settings.dat_file_path, &file_size))
{
uint64_t free_memory = frontend_driver_get_free_memory();
dat_file_path_status = MANUAL_CONTENT_SCAN_DAT_FILE_OK;
/* DAT files can be *very* large...
* Try to enforce sane behaviour by requiring
* the system to have an amount of free memory
* at least twice the size of the DAT file...
* > Note that desktop (and probably mobile)
* platforms should always have enough memory
* for this - we're really only protecting the
* console ports here */
if (free_memory > 0)
{
if (free_memory < (2 * file_size))
dat_file_path_status = MANUAL_CONTENT_SCAN_DAT_FILE_TOO_LARGE;
}
/* This is an annoying condition - it means the
* current platform doesn't have a 'free_memory'
* implementation...
* Have to make some assumptions in this case:
* > Typically the lowest system RAM of a supported
* platform in 32MB
* > Propose that (2 * file_size) should be no more
* than 1/4 of this total RAM value */
else if ((2 * file_size) > (8 * 1048576))
dat_file_path_status = MANUAL_CONTENT_SCAN_DAT_FILE_TOO_LARGE;
}
else
dat_file_path_status = MANUAL_CONTENT_SCAN_DAT_FILE_INVALID;
}
/* Reset 'dat_file_path' if status is anything other
* that 'OK' */
if (dat_file_path_status != MANUAL_CONTENT_SCAN_DAT_FILE_OK)
scan_settings.dat_file_path[0] = '\0';
return dat_file_path_status;
}
/* Menu setters */
/* Sets content directory for next manual scan
@ -646,6 +719,7 @@ bool manual_content_scan_get_task_config(manual_content_scan_task_config_t *task
task_config->core_name[0] = '\0';
task_config->core_path[0] = '\0';
task_config->file_exts[0] = '\0';
task_config->dat_file_path[0] = '\0';
/* Get content directory */
if (string_is_empty(scan_settings.content_dir))
@ -770,6 +844,18 @@ bool manual_content_scan_get_task_config(manual_content_scan_task_config_t *task
if (!string_is_empty(task_config->file_exts))
string_replace_all_chars(task_config->file_exts, ' ', '|');
/* Get DAT file path */
if (!string_is_empty(scan_settings.dat_file_path))
{
if (!logiqx_dat_path_is_valid(scan_settings.dat_file_path, NULL))
return false;
strlcpy(
task_config->dat_file_path,
scan_settings.dat_file_path,
sizeof(task_config->dat_file_path));
}
/* Copy 'search inside archives' setting */
task_config->search_archives = scan_settings.search_archives;
@ -932,12 +1018,68 @@ error:
return false;
}
/* Extracts content 'label' (name) from content path
* > If a DAT file is specified and content is an
* archive, performs a lookup of content file name
* in an attempt to find a valid 'description' string.
* Returns false if specified content is invalid. */
static bool manual_content_scan_get_playlist_content_label(
const char *content_path, logiqx_dat_t *dat_file,
char *content_label, size_t len)
{
/* Sanity check */
if (string_is_empty(content_path))
return false;
/* In most cases, content label is just the
* filename without extension */
fill_short_pathname_representation(
content_label, content_path, len);
if (string_is_empty(content_label))
return false;
/* Check if a DAT file has been specified */
if (dat_file)
{
/* DAT files are only relevant for arcade
* content. We have no idea what kind of
* content we are dealing with here, but
* since arcade ROMs are always archives
* we can at least filter by file type... */
if (path_is_compressed_file(content_path))
{
logiqx_dat_game_info_t game_info;
/* Search for current content
* > If content is not listed in DAT file,
* use existing filename without extension */
if (logiqx_dat_search(dat_file, content_label, &game_info))
{
/* BIOS files should always be skipped */
if (game_info.is_bios)
return false;
/* Only include 'runnable' content */
if (!game_info.is_runnable)
return false;
/* Copy game description */
if (!string_is_empty(game_info.description))
strlcpy(content_label, game_info.description, len);
}
}
}
return true;
}
/* 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)
int content_type, logiqx_dat_t *dat_file)
{
char playlist_content_path[PATH_MAX_LENGTH];
@ -963,10 +1105,9 @@ void manual_content_scan_add_content_to_playlist(
label[0] = '\0';
/* Get entry label */
fill_short_pathname_representation(
label, playlist_content_path, sizeof(label));
if (string_is_empty(label))
if (!manual_content_scan_get_playlist_content_label(
playlist_content_path, dat_file,
label, sizeof(label)))
return;
/* Configure playlist entry

View File

@ -29,6 +29,7 @@
#include <boolean.h>
#include <lists/string_list.h>
#include <formats/logiqx_dat.h>
#include "playlist.h"
@ -54,6 +55,16 @@ enum manual_content_scan_core_type
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
};
/* Holds all configuration parameters required
* for a manual content scan task */
typedef struct
@ -65,6 +76,7 @@ typedef struct
char core_name[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 search_archives;
bool overwrite_playlist;
@ -96,6 +108,14 @@ char *manual_content_scan_get_file_exts_custom_ptr(void);
* 'file_exts_custom' string */
size_t manual_content_scan_get_file_exts_custom_size(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_archives' bool */
bool *manual_content_scan_get_search_archives_ptr(void);
@ -115,6 +135,11 @@ void manual_content_scan_scrub_system_name_custom(void);
* 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 content directory for next manual scan
@ -199,7 +224,7 @@ struct string_list *manual_content_scan_get_content_list(manual_content_scan_tas
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);
int content_type, logiqx_dat_t *dat_file);
RETRO_END_DECLS

View File

@ -219,6 +219,7 @@ generic_deferred_push(deferred_push_switch_backlight_control, DISPLAYLIST_
#endif
generic_deferred_push(deferred_push_manual_content_scan_list, DISPLAYLIST_MANUAL_CONTENT_SCAN_LIST)
generic_deferred_push(deferred_push_manual_content_scan_dat_file, DISPLAYLIST_MANUAL_CONTENT_SCAN_DAT_FILES)
static int deferred_push_cursor_manager_list_deferred(
menu_displaylist_info_t *info)
@ -1100,6 +1101,9 @@ static int menu_cbs_init_bind_deferred_push_compare_label(
case MENU_ENUM_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST:
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_manual_content_scan_list);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DAT_FILE:
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_manual_content_scan_dat_file);
break;
default:
return -1;
}
@ -1316,6 +1320,9 @@ static int menu_cbs_init_bind_deferred_push_compare_label(
case MENU_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST:
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_manual_content_scan_list);
break;
case MENU_LABEL_MANUAL_CONTENT_SCAN_DAT_FILE:
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_manual_content_scan_dat_file);
break;
default:
return -1;
}

View File

@ -116,7 +116,8 @@ enum
ACTION_OK_SET_DIRECTORY,
ACTION_OK_SHOW_WIMP,
ACTION_OK_LOAD_CHEAT_FILE_APPEND,
ACTION_OK_LOAD_RGUI_MENU_THEME_PRESET
ACTION_OK_LOAD_RGUI_MENU_THEME_PRESET,
ACTION_OK_SET_MANUAL_CONTENT_SCAN_DAT_FILE
};
enum
@ -638,6 +639,14 @@ int generic_action_ok_displaylist_push(const char *path,
info_label = label;
dl_type = DISPLAYLIST_FILE_BROWSER_SELECT_DIR;
break;
case ACTION_OK_DL_MANUAL_CONTENT_SCAN_DAT_FILE:
filebrowser_clear_type();
info.type = type;
info.directory_ptr = idx;
info_path = settings->paths.directory_menu_content;
info_label = label;
dl_type = DISPLAYLIST_FILE_BROWSER_SELECT_FILE;
break;
case ACTION_OK_DL_REMAP_FILE:
filebrowser_clear_type();
info.type = type;
@ -1706,6 +1715,10 @@ static int generic_action_ok(const char *path,
ret = set_path_generic(menu_label, action_path);
break;
#endif
case ACTION_OK_SET_MANUAL_CONTENT_SCAN_DAT_FILE:
flush_char = msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST);
ret = set_path_generic(menu_label, action_path);
break;
case ACTION_OK_SET_PATH:
flush_type = MENU_SETTINGS;
ret = set_path_generic(menu_label, action_path);
@ -1782,6 +1795,7 @@ default_action_ok_set(action_ok_shader_preset_load, ACTION_OK_LOAD_PRESET ,
default_action_ok_set(action_ok_shader_pass_load, ACTION_OK_LOAD_SHADER_PASS, MENU_ENUM_LABEL_SHADER_OPTIONS)
#endif
default_action_ok_set(action_ok_rgui_menu_theme_preset_load, ACTION_OK_LOAD_RGUI_MENU_THEME_PRESET, MENU_ENUM_LABEL_MENU_SETTINGS)
default_action_ok_set(action_ok_set_manual_content_scan_dat_file, ACTION_OK_SET_MANUAL_CONTENT_SCAN_DAT_FILE, MENU_ENUM_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST)
static int action_ok_file_load(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
@ -4738,6 +4752,7 @@ default_action_ok_func(action_ok_open_archive, ACTION_OK_DL_OPEN_ARCHIVE)
default_action_ok_func(action_ok_rgui_menu_theme_preset, ACTION_OK_DL_RGUI_MENU_THEME_PRESET)
default_action_ok_func(action_ok_pl_thumbnails_updater_list, ACTION_OK_DL_PL_THUMBNAILS_UPDATER_LIST)
default_action_ok_func(action_ok_push_manual_content_scan_list, ACTION_OK_DL_MANUAL_CONTENT_SCAN_LIST)
default_action_ok_func(action_ok_manual_content_scan_dat_file, ACTION_OK_DL_MANUAL_CONTENT_SCAN_DAT_FILE)
static int action_ok_open_uwp_permission_settings(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
@ -6771,6 +6786,9 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME:
BIND_ACTION_OK(cbs, action_ok_manual_content_scan_core_name);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DAT_FILE:
BIND_ACTION_OK(cbs, action_ok_manual_content_scan_dat_file);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_START:
BIND_ACTION_OK(cbs, action_ok_manual_content_scan_start);
break;
@ -6908,6 +6926,9 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
case MENU_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME:
BIND_ACTION_OK(cbs, action_ok_manual_content_scan_core_name);
break;
case MENU_LABEL_MANUAL_CONTENT_SCAN_DAT_FILE:
BIND_ACTION_OK(cbs, action_ok_manual_content_scan_dat_file);
break;
default:
return -1;
}
@ -7122,6 +7143,9 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs,
case FILE_TYPE_MANUAL_SCAN_DIRECTORY:
BIND_ACTION_OK(cbs, action_ok_path_manual_scan_directory);
break;
case FILE_TYPE_MANUAL_SCAN_DAT:
BIND_ACTION_OK(cbs, action_ok_set_manual_content_scan_dat_file);
break;
case FILE_TYPE_CONFIG:
BIND_ACTION_OK(cbs, action_ok_config_load);
break;

View File

@ -726,6 +726,7 @@ default_sublabel_macro(action_bind_sublabel_manual_content_scan_system_name_cust
default_sublabel_macro(action_bind_sublabel_manual_content_scan_core_name, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_CORE_NAME)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_file_exts, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_FILE_EXTS)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_search_archives, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_dat_file, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_DAT_FILE)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_overwrite, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_OVERWRITE)
default_sublabel_macro(action_bind_sublabel_manual_content_scan_start, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_START)
@ -3097,6 +3098,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_search_archives);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DAT_FILE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_dat_file);
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_OVERWRITE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_overwrite);
break;

View File

@ -171,7 +171,8 @@ enum
ACTION_OK_DL_CONTENT_SETTINGS,
ACTION_OK_DL_CDROM_INFO_DETAIL_LIST,
ACTION_OK_DL_RGUI_MENU_THEME_PRESET,
ACTION_OK_DL_MANUAL_CONTENT_SCAN_LIST
ACTION_OK_DL_MANUAL_CONTENT_SCAN_LIST,
ACTION_OK_DL_MANUAL_CONTENT_SCAN_DAT_FILE
};
/* Function callbacks */

View File

@ -3771,6 +3771,12 @@ static bool menu_displaylist_parse_manual_content_scan_list(
false) == 0)
count++;
/* Arcade DAT file */
if (menu_displaylist_parse_settings_enum(info->list,
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DAT_FILE, PARSE_ONLY_PATH,
false) == 0)
count++;
/* Overwrite playlist */
if (menu_displaylist_parse_settings_enum(info->list,
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_OVERWRITE, PARSE_ONLY_BOOL,
@ -9009,6 +9015,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
case DISPLAYLIST_FONTS:
case DISPLAYLIST_AUDIO_FILTERS:
case DISPLAYLIST_CHEAT_FILES:
case DISPLAYLIST_MANUAL_CONTENT_SCAN_DAT_FILES:
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
filebrowser_clear_type();
if (!string_is_empty(info->exts))
@ -9055,6 +9062,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
info->type_default = FILE_TYPE_CHEAT;
info->exts = strdup("cht");
break;
case DISPLAYLIST_MANUAL_CONTENT_SCAN_DAT_FILES:
info->type_default = FILE_TYPE_MANUAL_SCAN_DAT;
info->exts = strdup("dat|xml");
break;
default:
break;
}

View File

@ -216,6 +216,7 @@ enum menu_displaylist_ctl_state
DISPLAYLIST_SWITCH_CPU_PROFILE,
#endif
DISPLAYLIST_MANUAL_CONTENT_SCAN_LIST,
DISPLAYLIST_MANUAL_CONTENT_SCAN_DAT_FILES,
DISPLAYLIST_PENDING_CLEAR
};

View File

@ -6695,6 +6695,31 @@ void general_write_handler(rarch_setting_t *setting)
* string to lower case */
manual_content_scan_scrub_file_exts_custom();
break;
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DAT_FILE:
/* Ensure that DAT file path is valid
* (cannot check file contents here - DAT
* files are too large to load in the UI
* thread - have to wait until the scan task
* is pushed) */
switch (manual_content_scan_validate_dat_file_path())
{
case MANUAL_CONTENT_SCAN_DAT_FILE_INVALID:
runloop_msg_queue_push(
msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_DAT_FILE_INVALID),
1, 100, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
break;
case MANUAL_CONTENT_SCAN_DAT_FILE_TOO_LARGE:
runloop_msg_queue_push(
msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_DAT_FILE_TOO_LARGE),
1, 100, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
break;
default:
/* No action required */
break;
}
break;
default:
break;
}
@ -16551,6 +16576,20 @@ static bool setting_append_list(
general_read_handler,
SD_FLAG_NONE);
CONFIG_PATH(
list, list_info,
manual_content_scan_get_dat_file_path_ptr(),
manual_content_scan_get_dat_file_path_size(),
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_DAT_FILE,
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_DAT_FILE,
"",
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler);
menu_settings_list_current_add_values(list, list_info, "dat|xml");
CONFIG_BOOL(
list, list_info,
manual_content_scan_get_overwrite_playlist_ptr(),

View File

@ -109,6 +109,9 @@ void filebrowser_parse(menu_displaylist_info_t *info, unsigned type_data)
(filter_ext && info) ? subsystem->roms[content_get_subsystem_rom_id()].valid_extensions : NULL,
true, settings->bools.show_hidden_files, true, false);
}
else if (info && (info->type_default == FILE_TYPE_MANUAL_SCAN_DAT))
str_list = dir_list_new(path,
info->exts, true, settings->bools.show_hidden_files, false, false);
else
str_list = dir_list_new(path,
(filter_ext && info) ? info->exts : NULL,

View File

@ -152,6 +152,7 @@ enum msg_file_type
FILE_TYPE_DOWNLOAD_PL_THUMBNAIL_CONTENT,
FILE_TYPE_MANUAL_SCAN_DIRECTORY,
FILE_TYPE_MANUAL_SCAN_DAT,
FILE_TYPE_LAST
};
@ -2676,6 +2677,7 @@ enum msg_hash_enums
MENU_LABEL(MANUAL_CONTENT_SCAN_CORE_NAME),
MENU_LABEL(MANUAL_CONTENT_SCAN_FILE_EXTS),
MENU_LABEL(MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES),
MENU_LABEL(MANUAL_CONTENT_SCAN_DAT_FILE),
MENU_LABEL(MANUAL_CONTENT_SCAN_OVERWRITE),
MENU_LABEL(MANUAL_CONTENT_SCAN_START),
@ -2684,6 +2686,9 @@ enum msg_hash_enums
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_CORE_NAME_DETECT,
MSG_MANUAL_CONTENT_SCAN_DAT_FILE_INVALID,
MSG_MANUAL_CONTENT_SCAN_DAT_FILE_TOO_LARGE,
MSG_MANUAL_CONTENT_SCAN_DAT_FILE_LOAD_ERROR,
MSG_MANUAL_CONTENT_SCAN_INVALID_CONFIG,
MSG_MANUAL_CONTENT_SCAN_INVALID_CONTENT,
MSG_MANUAL_CONTENT_SCAN_START,
@ -2946,6 +2951,7 @@ enum msg_hash_enums
#define MENU_LABEL_MANUAL_CONTENT_SCAN_DIR 0x6674149FU
#define MENU_LABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME 0xA3EC34C5U
#define MENU_LABEL_MANUAL_CONTENT_SCAN_CORE_NAME 0xD13B7849U
#define MENU_LABEL_MANUAL_CONTENT_SCAN_DAT_FILE 0x4CCE0EB8U
/* Main menu */
#define MENU_LABEL_LOAD_CONTENT_LIST 0x5745de1fU

View File

@ -25,6 +25,7 @@
#include <string/stdstring.h>
#include <lists/string_list.h>
#include <file/file_path.h>
#include <formats/logiqx_dat.h>
#include "tasks_internal.h"
@ -51,6 +52,7 @@ typedef struct manual_scan_handle
manual_content_scan_task_config_t *task_config;
playlist_t *playlist;
struct string_list *content_list;
logiqx_dat_t *dat_file;
size_t list_size;
size_t list_index;
enum manual_scan_status status;
@ -80,6 +82,12 @@ static void free_manual_content_scan_handle(manual_scan_handle_t *manual_scan)
manual_scan->content_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;
}
@ -118,6 +126,22 @@ static void task_manual_content_scan_handler(retro_task_t *task)
manual_scan->list_size = manual_scan->content_list->size;
/* Load DAT file, if required */
if (!string_is_empty(manual_scan->task_config->dat_file_path))
{
manual_scan->dat_file =
logiqx_dat_init(manual_scan->task_config->dat_file_path);
if (!manual_scan->dat_file)
{
runloop_msg_queue_push(
msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_DAT_FILE_LOAD_ERROR),
1, 100, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
goto task_finished;
}
}
/* Open playlist */
manual_scan->playlist = playlist_init(
manual_scan->task_config->playlist_file, COLLECTION_SIZE);
@ -172,7 +196,7 @@ static void task_manual_content_scan_handler(retro_task_t *task)
/* Add content to playlist */
manual_content_scan_add_content_to_playlist(
manual_scan->task_config, manual_scan->playlist,
content_path, content_type);
content_path, content_type, manual_scan->dat_file);
}
/* Increment content index */
@ -286,6 +310,7 @@ bool task_push_manual_content_scan(void)
manual_scan->task_config = NULL;
manual_scan->playlist = NULL;
manual_scan->content_list = NULL;
manual_scan->dat_file = NULL;
manual_scan->list_size = 0;
manual_scan->list_index = 0;
manual_scan->status = MANUAL_SCAN_BEGIN;