1 /* Copyright  (C) 2010-2019 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (gfx_thumbnail_path.c).
5  * ---------------------------------------------------------------------------------------
6  *
7  * Permission is hereby granted, free of charge,
8  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26 
27 #include <string/stdstring.h>
28 #include <file/file_path.h>
29 #include <lists/file_list.h>
30 
31 #include "../configuration.h"
32 #include "../msg_hash.h"
33 #include "../paths.h"
34 #include "../file_path_special.h"
35 
36 #include "gfx_thumbnail_path.h"
37 
38 /* Used fixed size char arrays here, just to avoid
39  * the inconvenience of having to calloc()/free()
40  * each individual entry by hand... */
41 struct gfx_thumbnail_path_data
42 {
43    enum playlist_thumbnail_mode playlist_right_mode;
44    enum playlist_thumbnail_mode playlist_left_mode;
45    char system[PATH_MAX_LENGTH];
46    char content_path[PATH_MAX_LENGTH];
47    char content_label[PATH_MAX_LENGTH];
48    char content_core_name[PATH_MAX_LENGTH];
49    char content_db_name[PATH_MAX_LENGTH];
50    char content_img[PATH_MAX_LENGTH];
51    char right_path[PATH_MAX_LENGTH];
52    char left_path[PATH_MAX_LENGTH];
53 };
54 
55 /* Resets thumbnail path data
56  * (blanks all internal string containers) */
gfx_thumbnail_path_reset(gfx_thumbnail_path_data_t * path_data)57 void gfx_thumbnail_path_reset(gfx_thumbnail_path_data_t *path_data)
58 {
59    if (!path_data)
60       return;
61 
62    path_data->system[0]            = '\0';
63    path_data->content_path[0]      = '\0';
64    path_data->content_label[0]     = '\0';
65    path_data->content_core_name[0] = '\0';
66    path_data->content_db_name[0]   = '\0';
67    path_data->content_img[0]       = '\0';
68    path_data->right_path[0]        = '\0';
69    path_data->left_path[0]         = '\0';
70 
71    path_data->playlist_right_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
72    path_data->playlist_left_mode  = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
73 }
74 
75 /* Initialisation */
76 
77 /* Creates new thumbnail path data container.
78  * Returns handle to new gfx_thumbnail_path_data_t object.
79  * on success, otherwise NULL.
80  * Note: Returned object must be free()d */
gfx_thumbnail_path_init(void)81 gfx_thumbnail_path_data_t *gfx_thumbnail_path_init(void)
82 {
83    gfx_thumbnail_path_data_t *path_data = (gfx_thumbnail_path_data_t*)
84       malloc(sizeof(*path_data));
85    if (!path_data)
86       return NULL;
87 
88    gfx_thumbnail_path_reset(path_data);
89 
90    return path_data;
91 }
92 
93 
94 /* Utility Functions */
95 
96 /* Fetches the thumbnail subdirectory (Named_Snaps,
97  * Named_Titles, Named_Boxarts) corresponding to the
98  * specified 'type index' (1, 2, 3).
99  * Returns true if 'type index' is valid */
gfx_thumbnail_get_sub_directory(unsigned type_idx,const char ** sub_directory)100 bool gfx_thumbnail_get_sub_directory(
101       unsigned type_idx, const char **sub_directory)
102 {
103    if (!sub_directory)
104       return false;
105 
106    switch (type_idx)
107    {
108       case 1:
109          *sub_directory = "Named_Snaps";
110          return true;
111       case 2:
112          *sub_directory = "Named_Titles";
113          return true;
114       case 3:
115          *sub_directory = "Named_Boxarts";
116          return true;
117       case 0:
118       default:
119          break;
120    }
121 
122    return false;
123 }
124 
125 /* Returns currently set thumbnail 'type' (Named_Snaps,
126  * Named_Titles, Named_Boxarts) for specified thumbnail
127  * identifier (right, left) */
gfx_thumbnail_get_type(settings_t * settings,gfx_thumbnail_path_data_t * path_data,enum gfx_thumbnail_id thumbnail_id)128 static const char *gfx_thumbnail_get_type(
129       settings_t *settings,
130       gfx_thumbnail_path_data_t *path_data,
131       enum gfx_thumbnail_id thumbnail_id)
132 {
133    unsigned        type          = 0;
134    unsigned gfx_thumbnails       = settings->uints.gfx_thumbnails;
135    unsigned menu_left_thumbnails = settings->uints.menu_left_thumbnails;
136 
137    if (!path_data)
138       return msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF);
139 
140    switch (thumbnail_id)
141    {
142       case GFX_THUMBNAIL_RIGHT:
143          if (path_data->playlist_right_mode != PLAYLIST_THUMBNAIL_MODE_DEFAULT)
144             type = (unsigned)path_data->playlist_right_mode - 1;
145          else
146             type = gfx_thumbnails;
147          break;
148       case GFX_THUMBNAIL_LEFT:
149          if (path_data->playlist_left_mode != PLAYLIST_THUMBNAIL_MODE_DEFAULT)
150             type = (unsigned)path_data->playlist_left_mode - 1;
151          else
152             type = menu_left_thumbnails;
153          break;
154       default:
155          return msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF);
156    }
157 
158    switch (type)
159    {
160       case 1:
161          return "Named_Snaps";
162       case 2:
163          return "Named_Titles";
164       case 3:
165          return "Named_Boxarts";
166       case 0:
167       default:
168          break;
169    }
170 
171    return msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF);
172 }
173 
174 /* Returns true if specified thumbnail is enabled
175  * (i.e. if 'type' is not equal to MENU_ENUM_LABEL_VALUE_OFF) */
gfx_thumbnail_is_enabled(gfx_thumbnail_path_data_t * path_data,enum gfx_thumbnail_id thumbnail_id)176 bool gfx_thumbnail_is_enabled(gfx_thumbnail_path_data_t *path_data, enum gfx_thumbnail_id thumbnail_id)
177 {
178    settings_t          *settings = config_get_ptr();
179    unsigned gfx_thumbnails       = settings->uints.gfx_thumbnails;
180    unsigned menu_left_thumbnails = settings->uints.menu_left_thumbnails;
181 
182    if (!path_data)
183       return false;
184 
185    switch (thumbnail_id)
186    {
187       case GFX_THUMBNAIL_RIGHT:
188          if (path_data->playlist_right_mode != PLAYLIST_THUMBNAIL_MODE_DEFAULT)
189             return path_data->playlist_right_mode != PLAYLIST_THUMBNAIL_MODE_OFF;
190          return gfx_thumbnails != 0;
191       case GFX_THUMBNAIL_LEFT:
192          if (path_data->playlist_left_mode != PLAYLIST_THUMBNAIL_MODE_DEFAULT)
193             return path_data->playlist_left_mode != PLAYLIST_THUMBNAIL_MODE_OFF;
194          return menu_left_thumbnails != 0;
195       default:
196          break;
197    }
198 
199    return false;
200 }
201 
202 /* Setters */
203 
204 /* Fills content_img field of path_data using existing
205  * content_label field (for internal use only) */
fill_content_img(gfx_thumbnail_path_data_t * path_data)206 static void fill_content_img(gfx_thumbnail_path_data_t *path_data)
207 {
208    char *scrub_char_pointer = NULL;
209 
210    /* Copy source label string */
211    strlcpy(path_data->content_img,
212          path_data->content_label, sizeof(path_data->content_img));
213 
214    /* Scrub characters that are not cross-platform and/or violate the
215     * No-Intro filename standard:
216     * http://datomatic.no-intro.org/stuff/The%20Official%20No-Intro%20Convention%20(20071030).zip
217     * Replace these characters in the entry name with underscores */
218    while ((scrub_char_pointer =
219             strpbrk(path_data->content_img, "&*/:`\"<>?\\|")))
220       *scrub_char_pointer = '_';
221 
222    /* Add PNG extension */
223    strlcat(path_data->content_img, ".png", sizeof(path_data->content_img));
224 }
225 
226 /* Sets current 'system' (default database name).
227  * Returns true if 'system' is valid.
228  * If playlist is provided, extracts system-specific
229  * thumbnail assignment metadata (required for accurate
230  * usage of gfx_thumbnail_is_enabled())
231  * > Used as a fallback when individual content lacks an
232  *   associated database name */
gfx_thumbnail_set_system(gfx_thumbnail_path_data_t * path_data,const char * system,playlist_t * playlist)233 bool gfx_thumbnail_set_system(gfx_thumbnail_path_data_t *path_data,
234       const char *system, playlist_t *playlist)
235 {
236    if (!path_data)
237       return false;
238 
239    /* When system is updated, must regenerate right/left
240     * thumbnail paths */
241    path_data->right_path[0]       = '\0';
242    path_data->left_path[0]        = '\0';
243 
244    /* 'Reset' path_data system string */
245    path_data->system[0]           = '\0';
246 
247    /* Must also reset playlist thumbnail display modes */
248    path_data->playlist_right_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
249    path_data->playlist_left_mode  = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
250 
251    if (string_is_empty(system))
252       return false;
253 
254    /* Hack: There is only one MAME thumbnail repo,
255     * so filter any input starting with 'MAME...' */
256    if (strncmp(system, "MAME", 4) == 0)
257       strlcpy(path_data->system, "MAME", sizeof(path_data->system));
258    else
259       strlcpy(path_data->system, system, sizeof(path_data->system));
260 
261    /* Addendum: Now that we have per-playlist thumbnail display
262     * modes, we must extract them here - otherwise
263     * gfx_thumbnail_is_enabled() will go out of sync */
264    if (playlist)
265    {
266       const char *playlist_path = playlist_get_conf_path(playlist);
267       const char *playlist_file = NULL;
268       bool playlist_valid       = false;
269 
270       /* Note: This is not considered an error
271        * (just means that input playlist is ignored) */
272       if (string_is_empty(playlist_path))
273          return true;
274 
275       playlist_file = path_basename_nocompression(playlist_path);
276 
277       /* Note: This is not considered an error
278        * (just means that input playlist is ignored) */
279       if (string_is_empty(playlist_file))
280          return true;
281 
282       /* Check for history/favourites playlists */
283       playlist_valid =
284             (string_is_equal(system, "history") &&
285              string_is_equal(playlist_file,
286                 FILE_PATH_CONTENT_HISTORY)) ||
287             (string_is_equal(system, "favorites") &&
288              string_is_equal(playlist_file,
289                 FILE_PATH_CONTENT_FAVORITES));
290 
291       if (!playlist_valid)
292       {
293          /* This means we have to work a little harder
294           * i.e. check whether the cached playlist file
295           * matches the database name */
296          char *playlist_name = NULL;
297          char tmp[PATH_MAX_LENGTH];
298 
299          tmp[0] = '\0';
300 
301          strlcpy(tmp, playlist_file, sizeof(tmp));
302          playlist_name  = path_remove_extension(tmp);
303          playlist_valid = string_is_equal(playlist_name, system);
304       }
305 
306       /* If we have a valid playlist, extract thumbnail modes */
307       if (playlist_valid)
308       {
309          path_data->playlist_right_mode =
310                playlist_get_thumbnail_mode(playlist, PLAYLIST_THUMBNAIL_RIGHT);
311          path_data->playlist_left_mode =
312                playlist_get_thumbnail_mode(playlist, PLAYLIST_THUMBNAIL_LEFT);
313       }
314    }
315 
316    return true;
317 }
318 
319 /* Sets current thumbnail content according to the specified label.
320  * Returns true if content is valid */
gfx_thumbnail_set_content(gfx_thumbnail_path_data_t * path_data,const char * label)321 bool gfx_thumbnail_set_content(gfx_thumbnail_path_data_t *path_data, const char *label)
322 {
323    if (!path_data)
324       return false;
325 
326    /* When content is updated, must regenerate right/left
327     * thumbnail paths */
328    path_data->right_path[0]        = '\0';
329    path_data->left_path[0]         = '\0';
330 
331    /* 'Reset' path_data content strings */
332    path_data->content_path[0]      = '\0';
333    path_data->content_label[0]     = '\0';
334    path_data->content_core_name[0] = '\0';
335    path_data->content_db_name[0]   = '\0';
336    path_data->content_img[0]       = '\0';
337 
338    /* Must also reset playlist thumbnail display modes */
339    path_data->playlist_right_mode  = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
340    path_data->playlist_left_mode   = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
341 
342    if (string_is_empty(label))
343       return false;
344 
345    /* Cache content label */
346    strlcpy(path_data->content_label, label, sizeof(path_data->content_label));
347 
348    /* Determine content image name */
349    fill_content_img(path_data);
350 
351    /* Have to set content path to *something*...
352     * Just use label value (it doesn't matter) */
353    strlcpy(path_data->content_path, label, sizeof(path_data->content_path));
354 
355    /* Redundant error check... */
356    return !string_is_empty(path_data->content_img);
357 }
358 
359 /* Sets current thumbnail content to the specified image.
360  * Returns true if content is valid */
gfx_thumbnail_set_content_image(gfx_thumbnail_path_data_t * path_data,const char * img_dir,const char * img_name)361 bool gfx_thumbnail_set_content_image(
362       gfx_thumbnail_path_data_t *path_data,
363       const char *img_dir, const char *img_name)
364 {
365    char *content_img_no_ext = NULL;
366 
367    if (!path_data)
368       return false;
369 
370    /* When content is updated, must regenerate right/left
371     * thumbnail paths */
372    path_data->right_path[0]        = '\0';
373    path_data->left_path[0]         = '\0';
374 
375    /* 'Reset' path_data content strings */
376    path_data->content_path[0]      = '\0';
377    path_data->content_label[0]     = '\0';
378    path_data->content_core_name[0] = '\0';
379    path_data->content_db_name[0]   = '\0';
380    path_data->content_img[0]       = '\0';
381 
382    /* Must also reset playlist thumbnail display modes */
383    path_data->playlist_right_mode  = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
384    path_data->playlist_left_mode   = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
385 
386    if (string_is_empty(img_dir) || string_is_empty(img_name))
387       return false;
388 
389    if (path_is_media_type(img_name) != RARCH_CONTENT_IMAGE)
390       return false;
391 
392    /* Cache content image name */
393    strlcpy(path_data->content_img,
394             img_name, sizeof(path_data->content_img));
395 
396    /* Get image label */
397    content_img_no_ext = path_remove_extension(path_data->content_img);
398    if (!string_is_empty(content_img_no_ext))
399       strlcpy(path_data->content_label,
400             content_img_no_ext, sizeof(path_data->content_label));
401    else
402       strlcpy(path_data->content_label,
403             path_data->content_img, sizeof(path_data->content_label));
404 
405    /* Set file path */
406    fill_pathname_join(path_data->content_path,
407       img_dir, img_name, sizeof(path_data->content_path));
408 
409    /* Set core name to "imageviewer" */
410    strlcpy(
411          path_data->content_core_name,
412          "imageviewer", sizeof(path_data->content_core_name));
413 
414    /* Set database name (arbitrarily) to "_images_"
415     * (required for compatibility with gfx_thumbnail_update_path(),
416     * but not actually used...) */
417    strlcpy(path_data->content_db_name,
418          "_images_", sizeof(path_data->content_db_name));
419 
420    /* Redundant error check */
421    return !string_is_empty(path_data->content_path);
422 }
423 
424 /* Sets current thumbnail content to the specified playlist entry.
425  * Returns true if content is valid.
426  * > Note: It is always best to use playlists when setting
427  *   thumbnail content, since there is no guarantee that the
428  *   corresponding menu entry label will contain a useful
429  *   identifier (it may be 'tainted', e.g. with the current
430  *   core name). 'Real' labels should be extracted from source */
gfx_thumbnail_set_content_playlist(gfx_thumbnail_path_data_t * path_data,playlist_t * playlist,size_t idx)431 bool gfx_thumbnail_set_content_playlist(
432       gfx_thumbnail_path_data_t *path_data, playlist_t *playlist, size_t idx)
433 {
434    const char *content_path  = NULL;
435    const char *content_label = NULL;
436    const char *core_name     = NULL;
437    const char *db_name       = NULL;
438    const struct playlist_entry *entry = NULL;
439 
440    if (!path_data)
441       return false;
442 
443    /* When content is updated, must regenerate right/left
444     * thumbnail paths */
445    path_data->right_path[0]        = '\0';
446    path_data->left_path[0]         = '\0';
447 
448    /* 'Reset' path_data content strings */
449    path_data->content_path[0]      = '\0';
450    path_data->content_label[0]     = '\0';
451    path_data->content_core_name[0] = '\0';
452    path_data->content_db_name[0]   = '\0';
453    path_data->content_img[0]       = '\0';
454 
455    /* Must also reset playlist thumbnail display modes */
456    path_data->playlist_right_mode  = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
457    path_data->playlist_left_mode   = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
458 
459    if (!playlist)
460       return false;
461 
462    if (idx >= playlist_get_size(playlist))
463       return false;
464 
465    /* Read playlist values */
466    playlist_get_index(playlist, idx, &entry);
467 
468    if (!entry)
469       return false;
470 
471    content_path  = entry->path;
472    content_label = entry->label;
473    core_name     = entry->core_name;
474    db_name       = entry->db_name;
475 
476    /* Content without a path is invalid by definition */
477    if (string_is_empty(content_path))
478       return false;
479 
480    /* Cache content path
481     * (This is required for imageviewer, history and favourites content) */
482    strlcpy(path_data->content_path,
483             content_path, sizeof(path_data->content_path));
484 
485    /* Cache core name
486     * (This is required for imageviewer content) */
487    if (!string_is_empty(core_name))
488       strlcpy(path_data->content_core_name,
489             core_name, sizeof(path_data->content_core_name));
490 
491    /* Get content label */
492    if (!string_is_empty(content_label))
493       strlcpy(path_data->content_label,
494             content_label, sizeof(path_data->content_label));
495    else
496       fill_short_pathname_representation(path_data->content_label,
497             content_path, sizeof(path_data->content_label));
498 
499    /* Determine content image name */
500    fill_content_img(path_data);
501 
502    /* Redundant error check... */
503    if (string_is_empty(path_data->content_img))
504       return false;
505 
506    /* Thumbnail image name is done -> now check if
507     * per-content database name is defined */
508    if (!string_is_empty(db_name))
509    {
510       /* Hack: There is only one MAME thumbnail repo,
511        * so filter any input starting with 'MAME...' */
512       if (strncmp(db_name, "MAME", 4) == 0)
513          strlcpy(path_data->content_db_name, "MAME",
514                sizeof(path_data->content_db_name));
515       else
516       {
517          char *db_name_no_ext = NULL;
518          char tmp_buf[PATH_MAX_LENGTH];
519          tmp_buf[0] = '\0';
520 
521          /* Remove .lpl extension
522           * > path_remove_extension() requires a char * (not const)
523           *   so have to use a temporary buffer... */
524          strlcpy(tmp_buf, db_name, sizeof(tmp_buf));
525          db_name_no_ext = path_remove_extension(tmp_buf);
526 
527          if (!string_is_empty(db_name_no_ext))
528             strlcpy(path_data->content_db_name,
529                   db_name_no_ext, sizeof(path_data->content_db_name));
530          else
531             strlcpy(path_data->content_db_name,
532                   tmp_buf, sizeof(path_data->content_db_name));
533       }
534    }
535 
536    /* Playlist entry is valid -> it is now 'safe' to
537     * extract any remaining playlist metadata
538     * (i.e. thumbnail display modes) */
539    path_data->playlist_right_mode =
540          playlist_get_thumbnail_mode(playlist, PLAYLIST_THUMBNAIL_RIGHT);
541    path_data->playlist_left_mode =
542          playlist_get_thumbnail_mode(playlist, PLAYLIST_THUMBNAIL_LEFT);
543 
544    return true;
545 }
546 
547 /* Updaters */
548 
549 /* Updates path for specified thumbnail identifier (right, left).
550  * Must be called after:
551  * - gfx_thumbnail_set_system()
552  * - gfx_thumbnail_set_content*()
553  * ...and before:
554  * - gfx_thumbnail_get_path()
555  * Returns true if generated path is valid */
gfx_thumbnail_update_path(gfx_thumbnail_path_data_t * path_data,enum gfx_thumbnail_id thumbnail_id)556 bool gfx_thumbnail_update_path(
557       gfx_thumbnail_path_data_t *path_data,
558       enum gfx_thumbnail_id thumbnail_id)
559 {
560    char content_dir[PATH_MAX_LENGTH];
561    settings_t *settings       = config_get_ptr();
562    const char *type           = gfx_thumbnail_get_type(settings,
563          path_data, thumbnail_id);
564    const char *system_name    = NULL;
565    char *thumbnail_path       = NULL;
566    const char *dir_thumbnails = settings ? settings->paths.directory_thumbnails : NULL;
567 
568    if (!path_data)
569       return false;
570 
571    /* Determine which path we are updating... */
572    switch (thumbnail_id)
573    {
574       case GFX_THUMBNAIL_RIGHT:
575          thumbnail_path = path_data->right_path;
576          break;
577       case GFX_THUMBNAIL_LEFT:
578          thumbnail_path = path_data->left_path;
579          break;
580       default:
581          return false;
582    }
583 
584    thumbnail_path[0] = '\0';
585    content_dir[0]    = '\0';
586 
587    /* Sundry error checking */
588    if (string_is_empty(dir_thumbnails))
589       return false;
590 
591    if (!gfx_thumbnail_is_enabled(path_data, thumbnail_id))
592       return false;
593 
594    /* Generate new path */
595 
596    /* > Check path_data for empty strings */
597    if (string_is_empty(path_data->content_path) ||
598        string_is_empty(path_data->content_img) ||
599          (string_is_empty(path_data->system) &&
600           string_is_empty(path_data->content_db_name)))
601       return false;
602 
603    /* > Get current system */
604    if (string_is_empty(path_data->content_db_name))
605    {
606       /* If this is a content history or favorites playlist
607        * then the current 'path_data->system' string is
608        * meaningless. In this case, we fall back to the
609        * content directory name */
610       if (string_is_equal(path_data->system, "history") ||
611           string_is_equal(path_data->system, "favorites"))
612       {
613          if (!gfx_thumbnail_get_content_dir(
614                   path_data, content_dir, sizeof(content_dir)))
615             return false;
616 
617          system_name = content_dir;
618       }
619       else
620          system_name = path_data->system;
621    }
622    else
623       system_name = path_data->content_db_name;
624 
625    /* > Special case: thumbnail for imageviewer content
626     *   is the image file itself */
627    if (string_is_equal(system_name, "images_history") ||
628        string_is_equal(path_data->content_core_name, "imageviewer"))
629    {
630       /* imageviewer content is identical for left and right thumbnails */
631       if (path_is_media_type(path_data->content_path) == RARCH_CONTENT_IMAGE)
632          strlcpy(thumbnail_path,
633             path_data->content_path, PATH_MAX_LENGTH * sizeof(char));
634    }
635    else
636    {
637       char tmp_buf[PATH_MAX_LENGTH];
638       tmp_buf[0] = '\0';
639 
640       /* > Normal content: assemble path */
641 
642       /* >> Base + system name */
643       fill_pathname_join(thumbnail_path, dir_thumbnails,
644             system_name, PATH_MAX_LENGTH * sizeof(char));
645 
646       /* >> Add type */
647       fill_pathname_join(tmp_buf, thumbnail_path, type, sizeof(tmp_buf));
648 
649       /* >> Add content image */
650       thumbnail_path[0] = '\0';
651       fill_pathname_join(thumbnail_path, tmp_buf,
652             path_data->content_img, PATH_MAX_LENGTH * sizeof(char));
653    }
654 
655    /* Final error check - is cached path empty? */
656    return !string_is_empty(thumbnail_path);
657 }
658 
659 /* Getters */
660 
661 /* Fetches the current thumbnail file path of the
662  * specified thumbnail 'type'.
663  * Returns true if path is valid. */
gfx_thumbnail_get_path(gfx_thumbnail_path_data_t * path_data,enum gfx_thumbnail_id thumbnail_id,const char ** path)664 bool gfx_thumbnail_get_path(
665       gfx_thumbnail_path_data_t *path_data,
666       enum gfx_thumbnail_id thumbnail_id, const char **path)
667 {
668    char *thumbnail_path = NULL;
669 
670    if (!path_data || !path)
671       return false;
672 
673    switch (thumbnail_id)
674    {
675       case GFX_THUMBNAIL_RIGHT:
676          if (!string_is_empty(path_data->right_path))
677          {
678             thumbnail_path = path_data->right_path;
679             *path          = thumbnail_path;
680             return true;
681          }
682          break;
683       case GFX_THUMBNAIL_LEFT:
684          if (!string_is_empty(path_data->left_path))
685          {
686             thumbnail_path = path_data->left_path;
687             *path          = thumbnail_path;
688             return true;
689          }
690          break;
691       default:
692          break;
693    }
694 
695    return false;
696 }
697 
698 /* Fetches current 'system' (default database name).
699  * Returns true if 'system' is valid. */
gfx_thumbnail_get_system(gfx_thumbnail_path_data_t * path_data,const char ** system)700 bool gfx_thumbnail_get_system(
701       gfx_thumbnail_path_data_t *path_data, const char **system)
702 {
703    if (!path_data || !system)
704       return false;
705    if (string_is_empty(path_data->system))
706       return false;
707 
708    *system = path_data->system;
709 
710    return true;
711 }
712 
713 /* Fetches current content path.
714  * Returns true if content path is valid. */
gfx_thumbnail_get_content_path(gfx_thumbnail_path_data_t * path_data,const char ** content_path)715 bool gfx_thumbnail_get_content_path(
716       gfx_thumbnail_path_data_t *path_data, const char **content_path)
717 {
718    if (!path_data || !content_path)
719       return false;
720    if (string_is_empty(path_data->content_path))
721       return false;
722 
723    *content_path = path_data->content_path;
724 
725    return true;
726 }
727 
728 /* Fetches current thumbnail label.
729  * Returns true if label is valid. */
gfx_thumbnail_get_label(gfx_thumbnail_path_data_t * path_data,const char ** label)730 bool gfx_thumbnail_get_label(
731       gfx_thumbnail_path_data_t *path_data, const char **label)
732 {
733    if (!path_data || !label)
734       return false;
735    if (string_is_empty(path_data->content_label))
736       return false;
737 
738    *label = path_data->content_label;
739 
740    return true;
741 }
742 
743 /* Fetches current thumbnail core name.
744  * Returns true if core name is valid. */
gfx_thumbnail_get_core_name(gfx_thumbnail_path_data_t * path_data,const char ** core_name)745 bool gfx_thumbnail_get_core_name(
746       gfx_thumbnail_path_data_t *path_data, const char **core_name)
747 {
748    if (!path_data || !core_name)
749       return false;
750    if (string_is_empty(path_data->content_core_name))
751       return false;
752 
753    *core_name = path_data->content_core_name;
754 
755    return true;
756 }
757 
758 /* Fetches current database name.
759  * Returns true if database name is valid. */
gfx_thumbnail_get_db_name(gfx_thumbnail_path_data_t * path_data,const char ** db_name)760 bool gfx_thumbnail_get_db_name(
761       gfx_thumbnail_path_data_t *path_data, const char **db_name)
762 {
763    if (!path_data || !db_name)
764       return false;
765    if (string_is_empty(path_data->content_db_name))
766       return false;
767 
768    *db_name = path_data->content_db_name;
769 
770    return true;
771 }
772 
773 /* Fetches current thumbnail image name
774  * (name is the same for all thumbnail types).
775  * Returns true if image name is valid. */
gfx_thumbnail_get_img_name(gfx_thumbnail_path_data_t * path_data,const char ** img_name)776 bool gfx_thumbnail_get_img_name(
777       gfx_thumbnail_path_data_t *path_data, const char **img_name)
778 {
779    if (!path_data || !img_name)
780       return false;
781    if (string_is_empty(path_data->content_img))
782       return false;
783 
784    *img_name = path_data->content_img;
785 
786    return true;
787 }
788 
789 /* Fetches current content directory.
790  * Returns true if content directory is valid. */
gfx_thumbnail_get_content_dir(gfx_thumbnail_path_data_t * path_data,char * content_dir,size_t len)791 bool gfx_thumbnail_get_content_dir(
792       gfx_thumbnail_path_data_t *path_data, char *content_dir, size_t len)
793 {
794    size_t path_length;
795    char tmp_buf[PATH_MAX_LENGTH];
796    const char *last_slash        = NULL;
797 
798    if (!path_data || string_is_empty(path_data->content_path))
799       return false;
800 
801    last_slash = find_last_slash(path_data->content_path);
802 
803    if (!last_slash)
804       return false;
805 
806    path_length = last_slash + 1 - path_data->content_path;
807 
808    if (!((path_length > 1) && (path_length < PATH_MAX_LENGTH)))
809       return false;
810 
811    tmp_buf[0] = '\0';
812 
813    strlcpy(tmp_buf, path_data->content_path, path_length * sizeof(char));
814    strlcpy(content_dir, path_basename_nocompression(tmp_buf), len);
815 
816    return !string_is_empty(content_dir);
817 }
818