mirror of
https://github.com/libretro/RetroArch
synced 2025-02-01 00:32:46 +00:00
3ce56c5b42
Add logic to handle 3 possible thumbnail names, in following order: - most exact name derived from content file (same name, with .png extension) - usual name derived from playlist (usually coming from database) - shortened name up to first bracket, chopping off region/publisher etc. info For local file system, names are checked always. For thumbnail downloads, names are checked each time the item comes up in the playlist, meaning that it may take going back and forth 3 times for a thumbnail to appear. However, as a positive change, failed thumbnail downloads are not repeated for the same playlist, which was not the case earlier.
802 lines
27 KiB
C
802 lines
27 KiB
C
/* Copyright (C) 2010-2019 The RetroArch team
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
* The following license statement only applies to this file (gfx_thumbnail_path.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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include <string/stdstring.h>
|
|
#include <file/file_path.h>
|
|
#include <lists/file_list.h>
|
|
|
|
#include "../configuration.h"
|
|
#include "../msg_hash.h"
|
|
#include "../paths.h"
|
|
#include "../file_path_special.h"
|
|
|
|
#include "gfx_thumbnail_path.h"
|
|
|
|
/* Fills content_img field of path_data using existing
|
|
* content_label field (for internal use only) */
|
|
static void gfx_thumbnail_fill_content_img(char *s, size_t len, const char *src, bool shorten)
|
|
{
|
|
const char* cut = " (";
|
|
char *scrub_char_ptr = NULL;
|
|
/* Copy source label string */
|
|
size_t _len = strlcpy(s, src, len);
|
|
int bracketpos = -1;
|
|
|
|
/* Shortening logic: up to first space + bracket */
|
|
if (shorten) {
|
|
bracketpos = string_find_index_substring_string(src, cut);
|
|
if (bracketpos > 2)
|
|
_len = bracketpos;
|
|
}
|
|
/* Scrub characters that are not cross-platform and/or violate the
|
|
* No-Intro filename standard:
|
|
* https://datomatic.no-intro.org/stuff/The%20Official%20No-Intro%20Convention%20(20071030).pdf
|
|
* Replace these characters in the entry name with underscores */
|
|
while ((scrub_char_ptr = strpbrk(s, "&*/:`\"<>?\\|")))
|
|
*scrub_char_ptr = '_';
|
|
/* Add PNG extension */
|
|
strlcpy(s + _len, ".png", len - _len);
|
|
}
|
|
|
|
/* Returns currently set thumbnail 'type' (Named_Snaps,
|
|
* Named_Titles, Named_Boxarts) for specified thumbnail
|
|
* identifier (right, left) */
|
|
static const char *gfx_thumbnail_get_type(
|
|
settings_t *settings,
|
|
gfx_thumbnail_path_data_t *path_data,
|
|
enum gfx_thumbnail_id thumbnail_id)
|
|
{
|
|
if (path_data)
|
|
{
|
|
unsigned type = 0;
|
|
unsigned menu_left_thumbnails = settings->uints.menu_left_thumbnails;
|
|
unsigned gfx_thumbnails = settings->uints.gfx_thumbnails;
|
|
switch (thumbnail_id)
|
|
{
|
|
case GFX_THUMBNAIL_RIGHT:
|
|
if (path_data->playlist_right_mode != PLAYLIST_THUMBNAIL_MODE_DEFAULT)
|
|
type = (unsigned)path_data->playlist_right_mode - 1;
|
|
else
|
|
type = gfx_thumbnails;
|
|
break;
|
|
case GFX_THUMBNAIL_LEFT:
|
|
if (path_data->playlist_left_mode != PLAYLIST_THUMBNAIL_MODE_DEFAULT)
|
|
type = (unsigned)path_data->playlist_left_mode - 1;
|
|
else
|
|
type = menu_left_thumbnails;
|
|
break;
|
|
default:
|
|
goto end;
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case 1:
|
|
return "Named_Snaps";
|
|
case 2:
|
|
return "Named_Titles";
|
|
case 3:
|
|
return "Named_Boxarts";
|
|
case 0:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
end:
|
|
return msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF);
|
|
}
|
|
|
|
/* Resets thumbnail path data
|
|
* (blanks all internal string containers) */
|
|
void gfx_thumbnail_path_reset(gfx_thumbnail_path_data_t *path_data)
|
|
{
|
|
if (!path_data)
|
|
return;
|
|
|
|
path_data->system[0] = '\0';
|
|
path_data->content_path[0] = '\0';
|
|
path_data->content_label[0] = '\0';
|
|
path_data->content_core_name[0] = '\0';
|
|
path_data->content_db_name[0] = '\0';
|
|
path_data->content_img[0] = '\0';
|
|
path_data->right_path[0] = '\0';
|
|
path_data->left_path[0] = '\0';
|
|
|
|
path_data->playlist_right_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
|
|
path_data->playlist_left_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
|
|
}
|
|
|
|
/* Initialisation */
|
|
|
|
/* Creates new thumbnail path data container.
|
|
* Returns handle to new gfx_thumbnail_path_data_t object.
|
|
* on success, otherwise NULL.
|
|
* Note: Returned object must be free()d */
|
|
gfx_thumbnail_path_data_t *gfx_thumbnail_path_init(void)
|
|
{
|
|
gfx_thumbnail_path_data_t *path_data = (gfx_thumbnail_path_data_t*)
|
|
malloc(sizeof(*path_data));
|
|
if (!path_data)
|
|
return NULL;
|
|
|
|
gfx_thumbnail_path_reset(path_data);
|
|
|
|
return path_data;
|
|
}
|
|
|
|
/* Utility Functions */
|
|
|
|
/* Returns true if specified thumbnail is enabled
|
|
* (i.e. if 'type' is not equal to MENU_ENUM_LABEL_VALUE_OFF) */
|
|
bool gfx_thumbnail_is_enabled(gfx_thumbnail_path_data_t *path_data, enum gfx_thumbnail_id thumbnail_id)
|
|
{
|
|
if (path_data)
|
|
{
|
|
settings_t *settings = config_get_ptr();
|
|
unsigned gfx_thumbnails = settings->uints.gfx_thumbnails;
|
|
unsigned menu_left_thumbnails = settings->uints.menu_left_thumbnails;
|
|
switch (thumbnail_id)
|
|
{
|
|
case GFX_THUMBNAIL_RIGHT:
|
|
if (path_data->playlist_right_mode != PLAYLIST_THUMBNAIL_MODE_DEFAULT)
|
|
return path_data->playlist_right_mode != PLAYLIST_THUMBNAIL_MODE_OFF;
|
|
return gfx_thumbnails != 0;
|
|
case GFX_THUMBNAIL_LEFT:
|
|
if (path_data->playlist_left_mode != PLAYLIST_THUMBNAIL_MODE_DEFAULT)
|
|
return path_data->playlist_left_mode != PLAYLIST_THUMBNAIL_MODE_OFF;
|
|
return menu_left_thumbnails != 0;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Setters */
|
|
|
|
/* Sets current 'system' (default database name).
|
|
* Returns true if 'system' is valid.
|
|
* If playlist is provided, extracts system-specific
|
|
* thumbnail assignment metadata (required for accurate
|
|
* usage of gfx_thumbnail_is_enabled())
|
|
* > Used as a fallback when individual content lacks an
|
|
* associated database name */
|
|
bool gfx_thumbnail_set_system(gfx_thumbnail_path_data_t *path_data,
|
|
const char *system, playlist_t *playlist)
|
|
{
|
|
if (!path_data)
|
|
return false;
|
|
|
|
/* When system is updated, must regenerate right/left
|
|
* thumbnail paths */
|
|
path_data->right_path[0] = '\0';
|
|
path_data->left_path[0] = '\0';
|
|
|
|
/* 'Reset' path_data system string */
|
|
path_data->system[0] = '\0';
|
|
|
|
/* Must also reset playlist thumbnail display modes */
|
|
path_data->playlist_right_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
|
|
path_data->playlist_left_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
|
|
|
|
if (string_is_empty(system))
|
|
return false;
|
|
|
|
/* Hack: There is only one MAME thumbnail repo,
|
|
* so filter any input starting with 'MAME...' */
|
|
if (strncmp(system, "MAME", 4) == 0)
|
|
strlcpy(path_data->system, "MAME", sizeof(path_data->system));
|
|
else
|
|
strlcpy(path_data->system, system, sizeof(path_data->system));
|
|
|
|
/* Addendum: Now that we have per-playlist thumbnail display
|
|
* modes, we must extract them here - otherwise
|
|
* gfx_thumbnail_is_enabled() will go out of sync */
|
|
if (playlist)
|
|
{
|
|
const char *playlist_path = playlist_get_conf_path(playlist);
|
|
|
|
/* Note: This is not considered an error
|
|
* (just means that input playlist is ignored) */
|
|
if (!string_is_empty(playlist_path))
|
|
{
|
|
const char *playlist_file = path_basename_nocompression(playlist_path);
|
|
/* Note: This is not considered an error
|
|
* (just means that input playlist is ignored) */
|
|
if (!string_is_empty(playlist_file))
|
|
{
|
|
/* Check for history/favourites playlists */
|
|
bool playlist_valid =
|
|
(string_is_equal(system, "history")
|
|
&& string_is_equal(playlist_file,
|
|
FILE_PATH_CONTENT_HISTORY))
|
|
|| (string_is_equal(system, "favorites")
|
|
&& string_is_equal(playlist_file,
|
|
FILE_PATH_CONTENT_FAVORITES));
|
|
|
|
if (!playlist_valid)
|
|
{
|
|
/* This means we have to work a little harder
|
|
* i.e. check whether the cached playlist file
|
|
* matches the database name */
|
|
char *playlist_name = NULL;
|
|
char tmp[PATH_MAX_LENGTH];
|
|
strlcpy(tmp, playlist_file, sizeof(tmp));
|
|
playlist_name = path_remove_extension(tmp);
|
|
playlist_valid = string_is_equal(playlist_name, system);
|
|
}
|
|
|
|
/* If we have a valid playlist, extract thumbnail modes */
|
|
if (playlist_valid)
|
|
{
|
|
path_data->playlist_right_mode =
|
|
playlist_get_thumbnail_mode(playlist, PLAYLIST_THUMBNAIL_RIGHT);
|
|
path_data->playlist_left_mode =
|
|
playlist_get_thumbnail_mode(playlist, PLAYLIST_THUMBNAIL_LEFT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Sets current thumbnail content according to the specified label.
|
|
* Returns true if content is valid */
|
|
bool gfx_thumbnail_set_content(gfx_thumbnail_path_data_t *path_data, const char *label)
|
|
{
|
|
if (!path_data)
|
|
return false;
|
|
|
|
/* When content is updated, must regenerate right/left
|
|
* thumbnail paths */
|
|
path_data->right_path[0] = '\0';
|
|
path_data->left_path[0] = '\0';
|
|
|
|
/* 'Reset' path_data content strings */
|
|
path_data->content_path[0] = '\0';
|
|
path_data->content_label[0] = '\0';
|
|
path_data->content_core_name[0] = '\0';
|
|
path_data->content_db_name[0] = '\0';
|
|
path_data->content_img[0] = '\0';
|
|
|
|
/* Must also reset playlist thumbnail display modes */
|
|
path_data->playlist_right_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
|
|
path_data->playlist_left_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
|
|
path_data->playlist_index = 0;
|
|
|
|
if (string_is_empty(label))
|
|
return false;
|
|
|
|
/* Cache content label */
|
|
strlcpy(path_data->content_label, label, sizeof(path_data->content_label));
|
|
|
|
/* Determine content image name */
|
|
gfx_thumbnail_fill_content_img(path_data->content_img,
|
|
sizeof(path_data->content_img), path_data->content_label, false);
|
|
gfx_thumbnail_fill_content_img(path_data->content_img_short,
|
|
sizeof(path_data->content_img_short), path_data->content_label, true);
|
|
|
|
/* Have to set content path to *something*...
|
|
* Just use label value (it doesn't matter) */
|
|
strlcpy(path_data->content_path, label, sizeof(path_data->content_path));
|
|
|
|
/* Redundant error check... */
|
|
return !string_is_empty(path_data->content_img);
|
|
}
|
|
|
|
/* Sets current thumbnail content to the specified image.
|
|
* Returns true if content is valid */
|
|
bool gfx_thumbnail_set_content_image(
|
|
gfx_thumbnail_path_data_t *path_data,
|
|
const char *img_dir, const char *img_name)
|
|
{
|
|
char *content_img_no_ext = NULL;
|
|
|
|
if (!path_data)
|
|
return false;
|
|
|
|
/* When content is updated, must regenerate right/left
|
|
* thumbnail paths */
|
|
path_data->right_path[0] = '\0';
|
|
path_data->left_path[0] = '\0';
|
|
|
|
/* 'Reset' path_data content strings */
|
|
path_data->content_path[0] = '\0';
|
|
path_data->content_label[0] = '\0';
|
|
path_data->content_core_name[0] = '\0';
|
|
path_data->content_db_name[0] = '\0';
|
|
path_data->content_img[0] = '\0';
|
|
|
|
/* Must also reset playlist thumbnail display modes */
|
|
path_data->playlist_right_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
|
|
path_data->playlist_left_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
|
|
path_data->playlist_index = 0;
|
|
|
|
if (string_is_empty(img_dir) || string_is_empty(img_name))
|
|
return false;
|
|
|
|
if (path_is_media_type(img_name) != RARCH_CONTENT_IMAGE)
|
|
return false;
|
|
|
|
/* Cache content image name */
|
|
strlcpy(path_data->content_img,
|
|
img_name, sizeof(path_data->content_img));
|
|
|
|
/* Get image label */
|
|
content_img_no_ext = path_remove_extension(path_data->content_img);
|
|
if (!string_is_empty(content_img_no_ext))
|
|
strlcpy(path_data->content_label,
|
|
content_img_no_ext, sizeof(path_data->content_label));
|
|
else
|
|
strlcpy(path_data->content_label,
|
|
path_data->content_img, sizeof(path_data->content_label));
|
|
|
|
/* Set file path */
|
|
fill_pathname_join_special(path_data->content_path,
|
|
img_dir, img_name, sizeof(path_data->content_path));
|
|
|
|
/* Set core name to "imageviewer" */
|
|
strlcpy(
|
|
path_data->content_core_name,
|
|
"imageviewer", sizeof(path_data->content_core_name));
|
|
|
|
/* Set database name (arbitrarily) to "_images_"
|
|
* (required for compatibility with gfx_thumbnail_update_path(),
|
|
* but not actually used...) */
|
|
strlcpy(path_data->content_db_name,
|
|
"_images_", sizeof(path_data->content_db_name));
|
|
|
|
/* Redundant error check */
|
|
return !string_is_empty(path_data->content_path);
|
|
}
|
|
|
|
/* Sets current thumbnail content to the specified playlist entry.
|
|
* Returns true if content is valid.
|
|
* > Note: It is always best to use playlists when setting
|
|
* thumbnail content, since there is no guarantee that the
|
|
* corresponding menu entry label will contain a useful
|
|
* identifier (it may be 'tainted', e.g. with the current
|
|
* core name). 'Real' labels should be extracted from source */
|
|
bool gfx_thumbnail_set_content_playlist(
|
|
gfx_thumbnail_path_data_t *path_data, playlist_t *playlist, size_t idx)
|
|
{
|
|
const char *content_path = NULL;
|
|
const char *content_label = NULL;
|
|
const char *core_name = NULL;
|
|
const char *db_name = NULL;
|
|
const struct playlist_entry *entry = NULL;
|
|
settings_t* settings = config_get_ptr();
|
|
|
|
if (!path_data)
|
|
return false;
|
|
|
|
/* When content is updated, must regenerate right/left
|
|
* thumbnail paths */
|
|
path_data->right_path[0] = '\0';
|
|
path_data->left_path[0] = '\0';
|
|
|
|
/* 'Reset' path_data content strings */
|
|
path_data->content_path[0] = '\0';
|
|
path_data->content_label[0] = '\0';
|
|
path_data->content_core_name[0] = '\0';
|
|
path_data->content_db_name[0] = '\0';
|
|
path_data->content_img[0] = '\0';
|
|
|
|
/* Must also reset playlist thumbnail display modes */
|
|
path_data->playlist_right_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
|
|
path_data->playlist_left_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
|
|
path_data->playlist_index = 0;
|
|
|
|
if (!playlist)
|
|
return false;
|
|
|
|
if (idx >= playlist_get_size(playlist))
|
|
return false;
|
|
|
|
/* Read playlist values */
|
|
playlist_get_index(playlist, idx, &entry);
|
|
|
|
if (!entry)
|
|
return false;
|
|
|
|
content_path = entry->path;
|
|
content_label = entry->label;
|
|
core_name = entry->core_name;
|
|
db_name = entry->db_name;
|
|
|
|
/* Content without a path is invalid by definition */
|
|
if (string_is_empty(content_path))
|
|
return false;
|
|
|
|
/* Cache content path
|
|
* (This is required for imageviewer, history and favourites content) */
|
|
strlcpy(path_data->content_path,
|
|
content_path, sizeof(path_data->content_path));
|
|
|
|
/* Cache core name
|
|
* (This is required for imageviewer content) */
|
|
if (!string_is_empty(core_name))
|
|
strlcpy(path_data->content_core_name,
|
|
core_name, sizeof(path_data->content_core_name));
|
|
|
|
/* Get content label */
|
|
if (!string_is_empty(content_label))
|
|
strlcpy(path_data->content_label,
|
|
content_label, sizeof(path_data->content_label));
|
|
else
|
|
fill_pathname(path_data->content_label,
|
|
path_basename(content_path),
|
|
"", sizeof(path_data->content_label));
|
|
|
|
/* Determine content image name */
|
|
{
|
|
char* content_name_no_ext = NULL;
|
|
char tmp_buf[PATH_MAX_LENGTH];
|
|
/* Remove rom file extension
|
|
* > path_remove_extension() requires a char * (not const)
|
|
* so have to use a temporary buffer... */
|
|
|
|
const char* base_name = path_basename(path_data->content_path);
|
|
strlcpy(tmp_buf, base_name, sizeof(tmp_buf));
|
|
content_name_no_ext = path_remove_extension(tmp_buf);
|
|
|
|
gfx_thumbnail_fill_content_img(path_data->content_img_full,
|
|
sizeof(path_data->content_img_full), content_name_no_ext,false);
|
|
gfx_thumbnail_fill_content_img(path_data->content_img,
|
|
sizeof(path_data->content_img), path_data->content_label,false);
|
|
gfx_thumbnail_fill_content_img(path_data->content_img_short,
|
|
sizeof(path_data->content_img_short), path_data->content_label,true);
|
|
}
|
|
|
|
/* Store playlist index */
|
|
path_data->playlist_index = idx;
|
|
|
|
/* Redundant error check... */
|
|
if (string_is_empty(path_data->content_img))
|
|
return false;
|
|
|
|
/* Thumbnail image name is done -> now check if
|
|
* per-content database name is defined */
|
|
if (string_is_empty(db_name))
|
|
playlist_get_db_name(playlist, idx, &db_name);
|
|
if (!string_is_empty(db_name))
|
|
{
|
|
/* Hack: There is only one MAME thumbnail repo,
|
|
* so filter any input starting with 'MAME...' */
|
|
if (strncmp(db_name, "MAME", 4) == 0)
|
|
{
|
|
path_data->content_db_name[0] = path_data->content_db_name[2] = 'M';
|
|
path_data->content_db_name[1] = 'A';
|
|
path_data->content_db_name[3] = 'E';
|
|
path_data->content_db_name[4] = '\0';
|
|
}
|
|
else
|
|
{
|
|
char *db_name_no_ext = NULL;
|
|
char tmp_buf[PATH_MAX_LENGTH];
|
|
/* Remove .lpl extension
|
|
* > path_remove_extension() requires a char * (not const)
|
|
* so have to use a temporary buffer... */
|
|
strlcpy(tmp_buf, db_name, sizeof(tmp_buf));
|
|
db_name_no_ext = path_remove_extension(tmp_buf);
|
|
|
|
if (!string_is_empty(db_name_no_ext))
|
|
strlcpy(path_data->content_db_name,
|
|
db_name_no_ext, sizeof(path_data->content_db_name));
|
|
else
|
|
strlcpy(path_data->content_db_name,
|
|
tmp_buf, sizeof(path_data->content_db_name));
|
|
}
|
|
}
|
|
|
|
/* Playlist entry is valid -> it is now 'safe' to
|
|
* extract any remaining playlist metadata
|
|
* (i.e. thumbnail display modes) */
|
|
path_data->playlist_right_mode =
|
|
playlist_get_thumbnail_mode(playlist, PLAYLIST_THUMBNAIL_RIGHT);
|
|
path_data->playlist_left_mode =
|
|
playlist_get_thumbnail_mode(playlist, PLAYLIST_THUMBNAIL_LEFT);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Updaters */
|
|
|
|
/* Updates path for specified thumbnail identifier (right, left).
|
|
* Must be called after:
|
|
* - gfx_thumbnail_set_system()
|
|
* - gfx_thumbnail_set_content*()
|
|
* ...and before:
|
|
* - gfx_thumbnail_get_path()
|
|
* Returns true if generated path is valid */
|
|
bool gfx_thumbnail_update_path(
|
|
gfx_thumbnail_path_data_t *path_data,
|
|
enum gfx_thumbnail_id thumbnail_id)
|
|
{
|
|
char content_dir[PATH_MAX_LENGTH];
|
|
settings_t *settings = config_get_ptr();
|
|
const char *system_name = NULL;
|
|
char *thumbnail_path = NULL;
|
|
const char *dir_thumbnails = NULL;
|
|
|
|
if (!path_data)
|
|
return false;
|
|
|
|
/* Determine which path we are updating... */
|
|
switch (thumbnail_id)
|
|
{
|
|
case GFX_THUMBNAIL_RIGHT:
|
|
thumbnail_path = path_data->right_path;
|
|
break;
|
|
case GFX_THUMBNAIL_LEFT:
|
|
thumbnail_path = path_data->left_path;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
content_dir[0] = '\0';
|
|
|
|
if (settings)
|
|
dir_thumbnails = settings->paths.directory_thumbnails;
|
|
|
|
/* Sundry error checking */
|
|
if (string_is_empty(dir_thumbnails))
|
|
return false;
|
|
|
|
if (!gfx_thumbnail_is_enabled(path_data, thumbnail_id))
|
|
return false;
|
|
|
|
/* Generate new path */
|
|
|
|
/* > Check path_data for empty strings */
|
|
if ( string_is_empty(path_data->content_path)
|
|
|| string_is_empty(path_data->content_img)
|
|
|| ( string_is_empty(path_data->system)
|
|
&& string_is_empty(path_data->content_db_name)))
|
|
return false;
|
|
|
|
/* > Get current system */
|
|
if (string_is_empty(path_data->content_db_name))
|
|
{
|
|
/* If this is a content history or favorites playlist
|
|
* then the current 'path_data->system' string is
|
|
* meaningless. In this case, we fall back to the
|
|
* content directory name */
|
|
if ( string_is_equal(path_data->system, "history")
|
|
|| string_is_equal(path_data->system, "favorites"))
|
|
{
|
|
if (!gfx_thumbnail_get_content_dir(
|
|
path_data, content_dir, sizeof(content_dir)))
|
|
return false;
|
|
|
|
system_name = content_dir;
|
|
}
|
|
else
|
|
system_name = path_data->system;
|
|
}
|
|
else
|
|
system_name = path_data->content_db_name;
|
|
|
|
/* > Special case: thumbnail for imageviewer content
|
|
* is the image file itself */
|
|
if ( string_is_equal(system_name, "images_history")
|
|
|| string_is_equal(path_data->content_core_name, "imageviewer"))
|
|
{
|
|
/* imageviewer content is identical for left and right thumbnails */
|
|
if (path_is_media_type(path_data->content_path) == RARCH_CONTENT_IMAGE)
|
|
strlcpy(thumbnail_path,
|
|
path_data->content_path, PATH_MAX_LENGTH * sizeof(char));
|
|
}
|
|
else
|
|
{
|
|
char tmp_buf[PATH_MAX_LENGTH];
|
|
const char *type = gfx_thumbnail_get_type(settings,
|
|
path_data, thumbnail_id);
|
|
/* > Normal content: assemble path */
|
|
|
|
/* >> Base + system name */
|
|
fill_pathname_join_special(thumbnail_path, dir_thumbnails,
|
|
system_name, PATH_MAX_LENGTH * sizeof(char));
|
|
|
|
/* >> Add type */
|
|
fill_pathname_join_special(tmp_buf, thumbnail_path, type, sizeof(tmp_buf));
|
|
|
|
thumbnail_path[0] = '\0';
|
|
/* >> Add content image - first try with full file name */
|
|
if(path_data->content_img_full[0] != '\0') {
|
|
fill_pathname_join_special(thumbnail_path, tmp_buf,
|
|
path_data->content_img_full, PATH_MAX_LENGTH * sizeof(char));
|
|
}
|
|
/* >> Add content image - second try with label (database name) */
|
|
if(!path_is_valid(thumbnail_path) && path_data->content_img[0] != '\0')
|
|
{
|
|
thumbnail_path[0] = '\0';
|
|
fill_pathname_join_special(thumbnail_path, tmp_buf,
|
|
path_data->content_img, PATH_MAX_LENGTH * sizeof(char));
|
|
}
|
|
/* >> Add content image - third try with shortened name (title only) */
|
|
if(!path_is_valid(thumbnail_path) && path_data->content_img_short[0] != '\0')
|
|
{
|
|
thumbnail_path[0] = '\0';
|
|
fill_pathname_join_special(thumbnail_path, tmp_buf,
|
|
path_data->content_img_short, PATH_MAX_LENGTH * sizeof(char));
|
|
}
|
|
/* This logic is valid for locally stored thumbnails. For optional downloads,
|
|
* gfx_thumbnail_get_img_name() is used */
|
|
}
|
|
|
|
/* Final error check - is cached path empty? */
|
|
return !string_is_empty(thumbnail_path);
|
|
}
|
|
|
|
/* Getters */
|
|
|
|
/* Fetches the current thumbnail file path of the
|
|
* specified thumbnail 'type'.
|
|
* Returns true if path is valid. */
|
|
bool gfx_thumbnail_get_path(
|
|
gfx_thumbnail_path_data_t *path_data,
|
|
enum gfx_thumbnail_id thumbnail_id,
|
|
const char **path)
|
|
{
|
|
char *thumbnail_path = NULL;
|
|
|
|
if (!path_data || !path)
|
|
return false;
|
|
|
|
switch (thumbnail_id)
|
|
{
|
|
case GFX_THUMBNAIL_RIGHT:
|
|
if (!string_is_empty(path_data->right_path))
|
|
{
|
|
thumbnail_path = path_data->right_path;
|
|
*path = thumbnail_path;
|
|
return true;
|
|
}
|
|
break;
|
|
case GFX_THUMBNAIL_LEFT:
|
|
if (!string_is_empty(path_data->left_path))
|
|
{
|
|
thumbnail_path = path_data->left_path;
|
|
*path = thumbnail_path;
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Fetches current 'system' (default database name).
|
|
* Returns true if 'system' is valid. */
|
|
bool gfx_thumbnail_get_system(
|
|
gfx_thumbnail_path_data_t *path_data, const char **system)
|
|
{
|
|
if (!path_data || !system)
|
|
return false;
|
|
if (string_is_empty(path_data->system))
|
|
return false;
|
|
|
|
*system = path_data->system;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Fetches current thumbnail label.
|
|
* Returns true if label is valid. */
|
|
bool gfx_thumbnail_get_label(
|
|
gfx_thumbnail_path_data_t *path_data, const char **label)
|
|
{
|
|
if (!path_data || !label)
|
|
return false;
|
|
if (string_is_empty(path_data->content_label))
|
|
return false;
|
|
|
|
*label = path_data->content_label;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Fetches current thumbnail core name.
|
|
* Returns true if core name is valid. */
|
|
bool gfx_thumbnail_get_core_name(
|
|
gfx_thumbnail_path_data_t *path_data, const char **core_name)
|
|
{
|
|
if (!path_data || !core_name)
|
|
return false;
|
|
if (string_is_empty(path_data->content_core_name))
|
|
return false;
|
|
|
|
*core_name = path_data->content_core_name;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Fetches current thumbnail image name according to name flag
|
|
* (name is the same for all thumbnail types).
|
|
* Returns true if image name is valid. */
|
|
bool gfx_thumbnail_get_img_name(
|
|
gfx_thumbnail_path_data_t *path_data, const char **img_name,
|
|
enum playlist_thumbnail_name_flags name_flags)
|
|
{
|
|
if (!path_data || !img_name || name_flags == PLAYLIST_THUMBNAIL_FLAG_NONE)
|
|
return false;
|
|
if (name_flags & PLAYLIST_THUMBNAIL_FLAG_SHORT_NAME) {
|
|
if (string_is_empty(path_data->content_img_short))
|
|
return false;
|
|
|
|
*img_name = path_data->content_img_short;
|
|
}
|
|
else if (name_flags & PLAYLIST_THUMBNAIL_FLAG_STD_NAME) {
|
|
if (string_is_empty(path_data->content_img))
|
|
return false;
|
|
|
|
*img_name = path_data->content_img;
|
|
}
|
|
else if (name_flags & PLAYLIST_THUMBNAIL_FLAG_FULL_NAME) {
|
|
if (string_is_empty(path_data->content_img_full))
|
|
return false;
|
|
|
|
*img_name = path_data->content_img_full;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Fetches current content directory.
|
|
* Returns true if content directory is valid. */
|
|
bool gfx_thumbnail_get_content_dir(
|
|
gfx_thumbnail_path_data_t *path_data, char *content_dir, size_t len)
|
|
{
|
|
size_t path_length;
|
|
char tmp_buf[PATH_MAX_LENGTH];
|
|
const char *last_slash = NULL;
|
|
|
|
if (!path_data || string_is_empty(path_data->content_path))
|
|
return false;
|
|
|
|
if (!(last_slash = find_last_slash(path_data->content_path)))
|
|
return false;
|
|
|
|
path_length = last_slash + 1 - path_data->content_path;
|
|
|
|
if (!((path_length > 1) && (path_length < PATH_MAX_LENGTH)))
|
|
return false;
|
|
|
|
strlcpy(tmp_buf, path_data->content_path, path_length * sizeof(char));
|
|
strlcpy(content_dir, path_basename_nocompression(tmp_buf), len);
|
|
|
|
return !string_is_empty(content_dir);
|
|
}
|