3 Commits 54bbd6bd10 ... b49a403f6c

Autor SHA1 Mensaje Fecha
  ZengGengSen b49a403f6c remove gfx font driver hace 1 año
  ZengGengSen 6a52c5c984 fix the buf for video_filter and video_viewport option hace 1 año
  ZengGengSen 22a70d11d3 remove the playlist hace 1 año
Se han modificado 47 ficheros con 190 adiciones y 15282 borrados
  1. 1 1
      app/build.gradle
  2. 2 2
      app/src/main/cpp/CMakeLists.txt
  3. 4 0
      app/src/main/cpp/defaults.h
  4. 2 0
      app/src/main/cpp/gfx/common/vulkan_common.h
  5. 16 0
      app/src/main/cpp/gfx/drivers/gl2.c
  6. 14 0
      app/src/main/cpp/gfx/drivers/vulkan.c
  7. 0 524
      app/src/main/cpp/gfx/drivers_font/gl2_raster_font.c
  8. 0 490
      app/src/main/cpp/gfx/drivers_font/vulkan_raster_font.c
  9. 0 310
      app/src/main/cpp/gfx/drivers_font_renderer/bitmap.h
  10. 0 252
      app/src/main/cpp/gfx/drivers_font_renderer/bitmapfont.c
  11. 0 253
      app/src/main/cpp/gfx/drivers_font_renderer/stb.c
  12. 0 362
      app/src/main/cpp/gfx/drivers_font_renderer/stb_unicode.c
  13. 1 0
      app/src/main/cpp/gfx/drivers_shader/shader_vulkan.cpp
  14. 0 946
      app/src/main/cpp/gfx/font_driver.c
  15. 0 142
      app/src/main/cpp/gfx/font_driver.h
  16. 13 2
      app/src/main/cpp/gfx/gfx_animation.c
  17. 6 0
      app/src/main/cpp/gfx/gfx_animation.h
  18. 6 0
      app/src/main/cpp/gfx/gfx_display.c
  19. 10 0
      app/src/main/cpp/gfx/gfx_display.h
  20. 0 1053
      app/src/main/cpp/gfx/gfx_thumbnail.c
  21. 0 337
      app/src/main/cpp/gfx/gfx_thumbnail.h
  22. 0 830
      app/src/main/cpp/gfx/gfx_thumbnail_path.c
  23. 0 157
      app/src/main/cpp/gfx/gfx_thumbnail_path.h
  24. 0 2179
      app/src/main/cpp/gfx/gfx_widgets.c
  25. 0 424
      app/src/main/cpp/gfx/gfx_widgets.h
  26. 18 6
      app/src/main/cpp/gfx/video_driver.c
  27. 1 1
      app/src/main/cpp/gfx/video_driver.h
  28. 2 0
      app/src/main/cpp/gfx/video_thread_wrapper.c
  29. 2 0
      app/src/main/cpp/gfx/video_thread_wrapper.h
  30. 10 0
      app/src/main/cpp/griffin/griffin.c
  31. 0 1400
      app/src/main/cpp/manual_content_scan.c
  32. 0 278
      app/src/main/cpp/manual_content_scan.h
  33. 2 2
      app/src/main/cpp/play_feature_delivery/play_feature_delivery.c
  34. 0 3510
      app/src/main/cpp/playlist.c
  35. 0 406
      app/src/main/cpp/playlist.h
  36. 14 2
      app/src/main/cpp/retroarch.c
  37. 2 0
      app/src/main/cpp/retroarch.h
  38. 11 7
      app/src/main/cpp/runloop.c
  39. 2 0
      app/src/main/cpp/runtime_file.c
  40. 4 1
      app/src/main/cpp/runtime_file.h
  41. 8 0
      app/src/main/cpp/tasks/task_content.c
  42. 18 2
      app/src/main/cpp/tasks/task_database.c
  43. 0 614
      app/src/main/cpp/tasks/task_manual_content_scan.c
  44. 0 776
      app/src/main/cpp/tasks/task_playlist_manager.c
  45. 2 0
      app/src/main/cpp/tasks/task_screenshot.c
  46. 6 0
      app/src/main/cpp/tasks/tasks_internal.h
  47. 13 13
      app/src/main/java/com/xugame/gameconsole/emulator/RetroArchEmulatorActivity.java

+ 1 - 1
app/build.gradle

@@ -15,7 +15,7 @@ android {
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 
         ndk{
-            abiFilters  "armeabi-v7a","x86_64","arm64-v8a"
+            abiFilters  "armeabi-v7a"
         }
     }
 

+ 2 - 2
app/src/main/cpp/CMakeLists.txt

@@ -63,7 +63,7 @@ endif ()
 target_compile_definitions(${PROJECT_NAME} PRIVATE
         RARCH_MOBILE
         HAVE_GRIFFIN
-        HAVE_STB_VORBIS
+        # HAVE_STB_VORBIS
         ANDROID
         HAVE_DYNAMIC
         HAVE_OPENGL
@@ -103,7 +103,7 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE
         # HAVE_OZONE
         HAVE_SHADERPIPELINE
         HAVE_LIBRETRODB
-        HAVE_STB_FONT
+        # HAVE_STB_FONT
         HAVE_IMAGEVIEWER
         # HAVE_ONLINE_UPDATER
         # HAVE_UPDATE_ASSETS

+ 4 - 0
app/src/main/cpp/defaults.h

@@ -23,8 +23,10 @@
 #include <retro_miscellaneous.h>
 
 #ifndef IS_SALAMANDER
+#ifdef HAVE_PLAYLIST
 #include "playlist.h"
 #endif
+#endif
 
 enum default_dirs
 {
@@ -70,6 +72,7 @@ enum default_dirs
 
 struct defaults
 {
+#ifdef HAVE_PLAYLIST
 #ifndef IS_SALAMANDER
    playlist_t *content_history;
    playlist_t *content_favorites;
@@ -80,6 +83,7 @@ struct defaults
 #if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
    playlist_t *video_history;
 #endif
+#endif
 #endif
    int settings_out_latency;
 #ifdef HAVE_MENU

+ 2 - 0
app/src/main/cpp/gfx/common/vulkan_common.h

@@ -48,7 +48,9 @@
 #include <libretro_vulkan.h>
 
 #include "../video_defines.h"
+#ifdef HAVE_FONT
 #include "../font_driver.h"
+#endif
 #include "../drivers_shader/shader_vulkan.h"
 #include "../include/vulkan/vulkan.h"
 

+ 16 - 0
app/src/main/cpp/gfx/drivers/gl2.c

@@ -64,7 +64,9 @@
 #include "../video_thread_wrapper.h"
 #endif
 
+#ifdef HAVE_FONT
 #include "../font_driver.h"
+#endif
 
 #ifdef HAVE_GLSL
 #include "../drivers_shader/shader_glsl.h"
@@ -2350,7 +2352,11 @@ static void gl2_render_osd_background(gl2_t *gl, const char *msg)
    settings_t *settings    = config_get_ptr();
    float video_font_size   = settings->floats.video_font_size;
    int msg_width           =
+#if HAVE_FONT
       font_driver_get_message_width(NULL, msg, strlen(msg), 1.0f);
+#else
+      0;
+#endif
 
    /* shader driver expects vertex coords as 0..1 */
    float x                 = settings->floats.video_msg_pos_x;
@@ -2858,12 +2864,14 @@ static bool gl2_frame(void *data, const void *frame,
       gfx_widgets_frame(video_info);
 #endif
 
+#if HAVE_FONT
    if (!string_is_empty(msg))
    {
       if (msg_bgcolor_enable)
          gl2_render_osd_background(gl, msg);
       font_driver_render_msg(gl, msg, NULL, NULL);
    }
+#endif
 
    if (gl->ctx_driver->update_window_title)
       gl->ctx_driver->update_window_title(gl->ctx_data);
@@ -2976,7 +2984,9 @@ static void gl2_free(void *data)
             (gl2_renderchain_data_t*)
             gl->renderchain_data);
 
+#if HAVE_FONT
    font_driver_free_osd();
+#endif
 
    gl->shader->deinit(gl->shader_data);
 
@@ -3800,11 +3810,13 @@ static void *gl2_init(const video_info_t *video,
             input, input_data);
    }
 
+#if HAVE_FONT
    if (video->font_enable)
       font_driver_init_osd(gl, video,
             false,
             video->is_threaded,
             FONT_DRIVER_RENDER_OPENGL_API);
+#endif
 
    /* Only bother with PBO readback if we're doing GPU recording.
     * Check recording_st->enable and not
@@ -4458,7 +4470,11 @@ static const video_poke_interface_t gl2_poke_interface = {
    gl2_apply_state_changes,
    gl2_set_texture_frame,
    gl2_set_texture_enable,
+#ifdef HAVE_FONT
    font_driver_render_msg,
+#else
+   NULL,
+#endif
    gl2_show_mouse,
    NULL,
    gl2_get_current_shader,

+ 14 - 0
app/src/main/cpp/gfx/drivers/vulkan.c

@@ -39,7 +39,9 @@
 #include "../gfx_widgets.h"
 #endif
 
+#ifdef HAVE_FONT
 #include "../font_driver.h"
+#endif
 
 #include "../common/vulkan_common.h"
 
@@ -1193,7 +1195,9 @@ static void vulkan_free(void *data)
       /* No need to init this since textures are create on-demand. */
       vulkan_deinit_menu(vk);
 
+#ifdef HAVE_FONT
       font_driver_free_osd();
+#endif
 
       vulkan_deinit_static_resources(vk);
 #ifdef HAVE_OVERLAY
@@ -1610,12 +1614,14 @@ static void *vulkan_init(const video_info_t *video,
             input, input_data);
    }
 
+#if HAVE_FONT
    if (video->font_enable)
       font_driver_init_osd(vk,
             video,
             false,
             video->is_threaded,
             FONT_DRIVER_RENDER_VULKAN_API);
+#endif
 
 #if OSX
    // The MoltenVK driver needs this, particularly after driver reinit
@@ -2586,8 +2592,10 @@ static bool vulkan_frame(void *data, const void *frame,
          vulkan_render_overlay(vk, video_width, video_height);
 #endif
 
+#if HAVE_FONT
       if (!string_is_empty(msg))
          font_driver_render_msg(vk, msg, NULL, NULL);
+#endif
 
 #ifdef HAVE_GFX_WIDGETS
       if (widgets_active)
@@ -3428,7 +3436,13 @@ static const video_poke_interface_t vulkan_poke_interface = {
    vulkan_apply_state_changes,
    vulkan_set_texture_frame,
    vulkan_set_texture_enable,
+
+#if HAVE_FONT
    font_driver_render_msg,
+#else
+   NULL,
+#endif
+
    vulkan_show_mouse,
    NULL,                               /* grab_mouse_toggle */
    vulkan_get_current_shader,

+ 0 - 524
app/src/main/cpp/gfx/drivers_font/gl2_raster_font.c

@@ -1,524 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- *  Copyright (C) 2011-2017 - Daniel De Matteis
- *  Copyright (C) 2016-2019 - Brad Parker
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-
-#include <encodings/utf.h>
-#include <string/stdstring.h>
-#include <retro_math.h>
-
-#include "../common/gl2_common.h"
-#include "../font_driver.h"
-#include "../../configuration.h"
-
-/* TODO: Move viewport side effects to the caller: it's a source of bugs. */
-
-#define GL_RASTER_FONT_EMIT(c, vx, vy) \
-   font_vertex[     2 * (6 * i + c) + 0] = (x + (delta_x + off_x + vx * width) * scale) * inv_win_width; \
-   font_vertex[     2 * (6 * i + c) + 1] = (y + (delta_y - off_y - vy * height) * scale) * inv_win_height; \
-   font_tex_coords[ 2 * (6 * i + c) + 0] = (tex_x + vx * width) * inv_tex_size_x; \
-   font_tex_coords[ 2 * (6 * i + c) + 1] = (tex_y + vy * height) * inv_tex_size_y; \
-   font_color[      4 * (6 * i + c) + 0] = color[0]; \
-   font_color[      4 * (6 * i + c) + 1] = color[1]; \
-   font_color[      4 * (6 * i + c) + 2] = color[2]; \
-   font_color[      4 * (6 * i + c) + 3] = color[3]; \
-   font_lut_tex_coord[    2 * (6 * i + c) + 0] = gl->coords.lut_tex_coord[0]; \
-   font_lut_tex_coord[    2 * (6 * i + c) + 1] = gl->coords.lut_tex_coord[1]
-
-#define MAX_MSG_LEN_CHUNK 64
-
-typedef struct
-{
-   gl2_t *gl;
-   GLuint tex;
-   unsigned tex_width, tex_height;
-
-   const font_renderer_driver_t *font_driver;
-   void *font_data;
-   struct font_atlas *atlas;
-
-   video_font_raster_block_t *block;
-} gl2_raster_t;
-
-static void gl2_raster_font_free(void *data,
-      bool is_threaded)
-{
-   gl2_raster_t *font = (gl2_raster_t*)data;
-   if (!font)
-      return;
-
-   if (font->font_driver && font->font_data)
-      font->font_driver->free(font->font_data);
-
-   if (is_threaded)
-   {
-      if (
-            font->gl && 
-            font->gl->ctx_driver &&
-            font->gl->ctx_driver->make_current)
-         font->gl->ctx_driver->make_current(true);
-   }
-
-   if (font->tex)
-   {
-      glDeleteTextures(1, &font->tex);
-      font->tex = 0;
-   }
-
-   free(font);
-}
-
-static void gl2_raster_font_upload_atlas(gl2_raster_t *font)
-{
-   int i, j;
-   GLint  gl_internal          = GL_LUMINANCE_ALPHA;
-   GLenum gl_format            = GL_LUMINANCE_ALPHA;
-   size_t ncomponents          = 2;
-   uint8_t *tmp                = (uint8_t*)calloc(font->tex_height, font->tex_width * ncomponents);
-
-   switch (ncomponents)
-   {
-      case 1:
-         for (i = 0; i < (int)font->atlas->height; ++i)
-         {
-            const uint8_t *src = &font->atlas->buffer[i * font->atlas->width];
-            uint8_t       *dst = &tmp[i * font->tex_width * ncomponents];
-
-            memcpy(dst, src, font->atlas->width);
-         }
-         break;
-      case 2:
-         for (i = 0; i < (int)font->atlas->height; ++i)
-         {
-            const uint8_t *src = &font->atlas->buffer[i * font->atlas->width];
-            uint8_t       *dst = &tmp[i * font->tex_width * ncomponents];
-
-            for (j = 0; j < (int)font->atlas->width; ++j)
-            {
-               *dst++ = 0xff;
-               *dst++ = *src++;
-            }
-         }
-         break;
-   }
-
-   glTexImage2D(GL_TEXTURE_2D, 0, gl_internal,
-         font->tex_width, font->tex_height,
-         0, gl_format, GL_UNSIGNED_BYTE, tmp);
-
-   free(tmp);
-}
-
-static void *gl2_raster_font_init(void *data,
-      const char *font_path, float font_size,
-      bool is_threaded)
-{
-   gl2_raster_t   *font  = (gl2_raster_t*)calloc(1, sizeof(*font));
-
-   if (!font)
-      return NULL;
-
-   font->gl              = (gl2_t*)data;
-
-   if (!font_renderer_create_default(
-            &font->font_driver,
-            &font->font_data, font_path, font_size))
-   {
-      free(font);
-      return NULL;
-   }
-
-   if (is_threaded)
-      if (
-            font->gl && 
-            font->gl->ctx_driver &&
-            font->gl->ctx_driver->make_current)
-         font->gl->ctx_driver->make_current(false);
-
-   glGenTextures(1, &font->tex);
-
-   GL2_BIND_TEXTURE(font->tex, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR);
-
-   font->atlas      = font->font_driver->get_atlas(font->font_data);
-   font->tex_width  = next_pow2(font->atlas->width);
-   font->tex_height = next_pow2(font->atlas->height);
-
-   gl2_raster_font_upload_atlas(font);
-
-   font->atlas->dirty = false;
-
-   if (font->gl)
-      glBindTexture(GL_TEXTURE_2D, font->gl->texture[font->gl->tex_index]);
-
-   return font;
-}
-
-static int gl2_raster_font_get_message_width(void *data, const char *msg,
-      size_t msg_len, float scale)
-{
-   const struct font_glyph* glyph_q = NULL;
-   gl2_raster_t *font               = (gl2_raster_t*)data;
-   const char* msg_end              = msg + msg_len;
-   int delta_x                      = 0;
-
-   if (     !font
-         || !font->font_driver
-         || !font->font_driver->get_glyph
-         || !font->font_data )
-      return 0;
-
-   glyph_q = font->font_driver->get_glyph(font->font_data, '?');
-
-   while (msg < msg_end)
-   {
-      const struct font_glyph *glyph;
-      unsigned code                  = utf8_walk(&msg);
-
-      /* Do something smarter here ... */
-      if (!(glyph = font->font_driver->get_glyph(
-            font->font_data, code)))
-         if (!(glyph = glyph_q))
-            continue;
-
-      delta_x += glyph->advance_x;
-   }
-
-   return delta_x * scale;
-}
-
-static void gl2_raster_font_draw_vertices(gl2_raster_t *font,
-      const video_coords_t *coords)
-{
-   if (font->atlas->dirty)
-   {
-      gl2_raster_font_upload_atlas(font);
-      font->atlas->dirty   = false;
-   }
-
-   if (font->gl && font->gl->shader)
-   {
-      font->gl->shader->set_coords(font->gl->shader_data, coords);
-      font->gl->shader->set_mvp(font->gl->shader_data,
-            &font->gl->mvp_no_rot);
-   }
-
-   glDrawArrays(GL_TRIANGLES, 0, coords->vertices);
-}
-
-static void gl2_raster_font_render_line(gl2_t *gl,
-      gl2_raster_t *font, const char *msg, size_t msg_len,
-      GLfloat scale, const GLfloat color[4], GLfloat pos_x,
-      GLfloat pos_y, unsigned text_align)
-{
-   int i;
-   struct video_coords coords;
-   const struct font_glyph* glyph_q = NULL;
-   GLfloat font_tex_coords[2 * 6 * MAX_MSG_LEN_CHUNK];
-   GLfloat font_vertex[2 * 6 * MAX_MSG_LEN_CHUNK];
-   GLfloat font_color[4 * 6 * MAX_MSG_LEN_CHUNK];
-   GLfloat font_lut_tex_coord[2 * 6 * MAX_MSG_LEN_CHUNK];
-   const char* msg_end  = msg + msg_len;
-   int x                = roundf(pos_x * gl->vp.width);
-   int y                = roundf(pos_y * gl->vp.height);
-   int delta_x          = 0;
-   int delta_y          = 0;
-   float inv_tex_size_x = 1.0f / font->tex_width;
-   float inv_tex_size_y = 1.0f / font->tex_height;
-   float inv_win_width  = 1.0f / gl->vp.width;
-   float inv_win_height = 1.0f / gl->vp.height;
-
-   switch (text_align)
-   {
-      case TEXT_ALIGN_RIGHT:
-         x -= gl2_raster_font_get_message_width(font, msg, msg_len, scale);
-         break;
-      case TEXT_ALIGN_CENTER:
-         x -= gl2_raster_font_get_message_width(font, msg, msg_len, scale) / 2.0;
-         break;
-   }
-
-   glyph_q = font->font_driver->get_glyph(font->font_data, '?');
-
-   while (msg < msg_end)
-   {
-      i = 0;
-      while ((i < MAX_MSG_LEN_CHUNK) && (msg < msg_end))
-      {
-         const struct font_glyph *glyph;
-         int off_x, off_y, tex_x, tex_y, width, height;
-         unsigned                  code = utf8_walk(&msg);
-
-         /* Do something smarter here ... */
-         if (!(glyph = font->font_driver->get_glyph(
-               font->font_data, code)))
-            if (!(glyph = glyph_q))
-               continue;
-
-         off_x  = glyph->draw_offset_x;
-         off_y  = glyph->draw_offset_y;
-         tex_x  = glyph->atlas_offset_x;
-         tex_y  = glyph->atlas_offset_y;
-         width  = glyph->width;
-         height = glyph->height;
-
-         GL_RASTER_FONT_EMIT(0, 0, 1); /* Bottom-left */
-         GL_RASTER_FONT_EMIT(1, 1, 1); /* Bottom-right */
-         GL_RASTER_FONT_EMIT(2, 0, 0); /* Top-left */
-
-         GL_RASTER_FONT_EMIT(3, 1, 0); /* Top-right */
-         GL_RASTER_FONT_EMIT(4, 0, 0); /* Top-left */
-         GL_RASTER_FONT_EMIT(5, 1, 1); /* Bottom-right */
-
-         i++;
-
-         delta_x += glyph->advance_x;
-         delta_y -= glyph->advance_y;
-      }
-
-      coords.tex_coord     = font_tex_coords;
-      coords.vertex        = font_vertex;
-      coords.color         = font_color;
-      coords.vertices      = i * 6;
-      coords.lut_tex_coord = font_lut_tex_coord;
-
-      if (font->block)
-         video_coord_array_append(&font->block->carr, &coords, coords.vertices);
-      else
-         gl2_raster_font_draw_vertices(font, &coords);
-   }
-}
-
-static void gl2_raster_font_render_message(
-      gl2_raster_t *font, const char *msg, GLfloat scale,
-      const GLfloat color[4], GLfloat pos_x, GLfloat pos_y,
-      unsigned text_align)
-{
-   struct font_line_metrics *line_metrics = NULL;
-   int lines                              = 0;
-   float line_height;
-
-   /* If font line metrics are not supported just draw as usual */
-   if (!font->font_driver->get_line_metrics ||
-       !font->font_driver->get_line_metrics(font->font_data, &line_metrics))
-   {
-      gl2_raster_font_render_line(font->gl, font,
-            msg, strlen(msg), scale, color, pos_x,
-            pos_y, text_align);
-      return;
-   }
-
-   line_height = line_metrics->height * scale / font->gl->vp.height;
-
-   for (;;)
-   {
-      const char *delim = strchr(msg, '\n');
-      size_t msg_len    = delim
-         ? (delim - msg) : strlen(msg);
-
-      /* Draw the line */
-      gl2_raster_font_render_line(font->gl, font,
-            msg, msg_len, scale, color, pos_x,
-            pos_y - (float)lines*line_height, text_align);
-
-      if (!delim)
-         break;
-
-      msg += msg_len + 1;
-      lines++;
-   }
-}
-
-static void gl2_raster_font_setup_viewport(
-      gl2_raster_t *font,
-      unsigned width, unsigned height,
-      bool full_screen)
-{
-   video_driver_set_viewport(width, height, full_screen, false);
-
-   glEnable(GL_BLEND);
-   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-   glBlendEquation(GL_FUNC_ADD);
-
-   glBindTexture(GL_TEXTURE_2D, font->tex);
-
-   if (font->gl && font->gl->shader && font->gl->shader->use)
-      font->gl->shader->use(font->gl,
-            font->gl->shader_data, VIDEO_SHADER_STOCK_BLEND, true);
-}
-
-static void gl2_raster_font_render_msg(
-      void *userdata,
-      void *data,
-      const char *msg,
-      const struct font_params *params)
-{
-   GLfloat color[4];
-   int drop_x, drop_y;
-   GLfloat x, y, scale, drop_mod, drop_alpha;
-   enum text_alignment text_align    = TEXT_ALIGN_LEFT;
-   bool full_screen                  = false ;
-   gl2_raster_t                *font = (gl2_raster_t*)data;
-   unsigned width                    = font->gl->video_width;
-   unsigned height                   = font->gl->video_height;
-
-   if (!font || string_is_empty(msg))
-      return;
-
-   if (params)
-   {
-      x           = params->x;
-      y           = params->y;
-      scale       = params->scale;
-      full_screen = params->full_screen;
-      text_align  = params->text_align;
-      drop_x      = params->drop_x;
-      drop_y      = params->drop_y;
-      drop_mod    = params->drop_mod;
-      drop_alpha  = params->drop_alpha;
-
-      color[0]    = FONT_COLOR_GET_RED(params->color)   / 255.0f;
-      color[1]    = FONT_COLOR_GET_GREEN(params->color) / 255.0f;
-      color[2]    = FONT_COLOR_GET_BLUE(params->color)  / 255.0f;
-      color[3]    = FONT_COLOR_GET_ALPHA(params->color) / 255.0f;
-
-      /* If alpha is 0.0f, turn it into default 1.0f */
-      if (color[3] <= 0.0f)
-         color[3] = 1.0f;
-   }
-   else
-   {
-      settings_t *settings     = config_get_ptr();
-      float video_msg_pos_x    = settings->floats.video_msg_pos_x;
-      float video_msg_pos_y    = settings->floats.video_msg_pos_y;
-      float video_msg_color_r  = settings->floats.video_msg_color_r;
-      float video_msg_color_g  = settings->floats.video_msg_color_g;
-      float video_msg_color_b  = settings->floats.video_msg_color_b;
-      x                        = video_msg_pos_x;
-      y                        = video_msg_pos_y;
-      scale                    = 1.0f;
-      full_screen              = true;
-      text_align               = TEXT_ALIGN_LEFT;
-
-      color[0]                 = video_msg_color_r;
-      color[1]                 = video_msg_color_g;
-      color[2]                 = video_msg_color_b;
-      color[3]                 = 1.0f;
-
-      drop_x                   = -2;
-      drop_y                   = -2;
-      drop_mod                 = 0.3f;
-      drop_alpha               = 1.0f;
-   }
-
-   if (font->block)
-      font->block->fullscreen  = full_screen;
-   else
-      gl2_raster_font_setup_viewport(font, width, height, full_screen);
-
-   if (font->gl)
-   {
-      if (    !string_is_empty(msg)
-            && font->font_data  
-            && font->font_driver)
-      {
-         if (drop_x || drop_y)
-         {
-            GLfloat color_dark[4];
-            color_dark[0] = color[0] * drop_mod;
-            color_dark[1] = color[1] * drop_mod;
-            color_dark[2] = color[2] * drop_mod;
-            color_dark[3] = color[3] * drop_alpha;
-
-            gl2_raster_font_render_message(font, msg, scale, color_dark,
-                  x + scale * drop_x / font->gl->vp.width, y +
-                      scale * drop_y / font->gl->vp.height, text_align);
-         }
-
-         gl2_raster_font_render_message(font, msg, scale, color,
-               x, y, text_align);
-      }
-
-      if (!font->block)
-      {
-         /* restore viewport */
-         glBindTexture(GL_TEXTURE_2D,
-               font->gl->texture[font->gl->tex_index]);
-         glDisable(GL_BLEND);
-         video_driver_set_viewport(width, height, false, true);
-      }
-   }
-}
-
-static const struct font_glyph *gl2_raster_font_get_glyph(
-      void *data, uint32_t code)
-{
-   gl2_raster_t *font = (gl2_raster_t*)data;
-   if (font && font->font_driver && font->font_driver->ident)
-      return font->font_driver->get_glyph((void*)font->font_driver, code);
-   return NULL;
-}
-
-static void gl2_raster_font_flush_block(unsigned width, unsigned height,
-      void *data)
-{
-   gl2_raster_t          *font       = (gl2_raster_t*)data;
-   video_font_raster_block_t *block = font ? font->block : NULL;
-
-   if (!font || !block || !block->carr.coords.vertices)
-      return;
-
-   gl2_raster_font_setup_viewport(font, width, height, block->fullscreen);
-   gl2_raster_font_draw_vertices(font, (video_coords_t*)&block->carr.coords);
-
-   if (font->gl)
-   {
-      /* restore viewport */
-      glBindTexture(GL_TEXTURE_2D, font->gl->texture[font->gl->tex_index]);
-
-      glDisable(GL_BLEND);
-      video_driver_set_viewport(width, height, block->fullscreen, true);
-   }
-}
-
-static void gl2_raster_font_bind_block(void *data, void *userdata)
-{
-   gl2_raster_t                *font = (gl2_raster_t*)data;
-   video_font_raster_block_t *block = (video_font_raster_block_t*)userdata;
-
-   if (font)
-      font->block = block;
-}
-
-static bool gl2_raster_font_get_line_metrics(void* data, struct font_line_metrics **metrics)
-{
-   gl2_raster_t *font   = (gl2_raster_t*)data;
-   if (font && font->font_driver && font->font_data)
-      return font->font_driver->get_line_metrics(font->font_data, metrics);
-   return false;
-}
-
-font_renderer_t gl2_raster_font = {
-   gl2_raster_font_init,
-   gl2_raster_font_free,
-   gl2_raster_font_render_msg,
-   "gl2_raster_font",
-   gl2_raster_font_get_glyph,
-   gl2_raster_font_bind_block,
-   gl2_raster_font_flush_block,
-   gl2_raster_font_get_message_width,
-   gl2_raster_font_get_line_metrics
-};

+ 0 - 490
app/src/main/cpp/gfx/drivers_font/vulkan_raster_font.c

@@ -1,490 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2016-2017 - Hans-Kristian Arntzen
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <string.h>
-
-#include <encodings/utf.h>
-#include <compat/strl.h>
-
-#include "../common/vulkan_common.h"
-
-#include "../font_driver.h"
-
-#include "../../configuration.h"
-
-typedef struct
-{
-   vk_t *vk;
-   void *font_data;
-   struct font_atlas *atlas;
-   const font_renderer_driver_t *font_driver;
-   struct vk_vertex *pv;
-   struct vk_texture texture;
-   struct vk_texture texture_optimal;
-   struct vk_buffer_range range;
-   unsigned vertices;
-
-   bool needs_update;
-} vulkan_raster_t;
-
-static INLINE void vulkan_font_update_glyph(
-      vulkan_raster_t *font, const struct font_glyph *glyph)
-{
-   unsigned row;
-   for (row = glyph->atlas_offset_y; row < (glyph->atlas_offset_y + glyph->height); row++)
-   {
-      uint8_t *src = font->atlas->buffer + row * font->atlas->width + glyph->atlas_offset_x;
-      uint8_t *dst = (uint8_t*)font->texture.mapped + row * font->texture.stride + glyph->atlas_offset_x;
-      memcpy(dst, src, glyph->width);
-   }
-}
-
-static void vulkan_font_free(void *data, bool is_threaded)
-{
-   vulkan_raster_t *font = (vulkan_raster_t*)data;
-   if (!font)
-      return;
-
-   if (font->font_driver && font->font_data)
-      font->font_driver->free(font->font_data);
-
-   vkQueueWaitIdle(font->vk->context->queue);
-   vulkan_destroy_texture(
-         font->vk->context->device, &font->texture);
-   vulkan_destroy_texture(
-         font->vk->context->device, &font->texture_optimal);
-
-   free(font);
-}
-
-static void *vulkan_font_init(void *data,
-      const char *font_path, float font_size,
-      bool is_threaded)
-{
-   vulkan_raster_t *font          =
-      (vulkan_raster_t*)calloc(1, sizeof(*font));
-
-   if (!font)
-      return NULL;
-
-   font->vk = (vk_t*)data;
-
-   if (!font_renderer_create_default(
-            &font->font_driver,
-            &font->font_data, font_path, font_size))
-   {
-      free(font);
-      return NULL;
-   }
-
-   font->atlas   = font->font_driver->get_atlas(font->font_data);
-   font->texture = vulkan_create_texture(font->vk, NULL,
-         font->atlas->width, font->atlas->height, VK_FORMAT_R8_UNORM, font->atlas->buffer,
-         NULL, VULKAN_TEXTURE_STAGING);
-
-   {
-      struct vk_texture *texture = &font->texture;
-      VK_MAP_PERSISTENT_TEXTURE(font->vk->context->device, texture);
-   }
-
-   font->texture_optimal = vulkan_create_texture(font->vk, NULL,
-         font->atlas->width, font->atlas->height, VK_FORMAT_R8_UNORM, NULL,
-         NULL, VULKAN_TEXTURE_DYNAMIC);
-
-   font->needs_update = true;
-
-   return font;
-}
-
-static int vulkan_get_message_width(void *data, const char *msg,
-      size_t msg_len, float scale)
-{
-   const struct font_glyph* glyph_q = NULL;
-   vulkan_raster_t *font = (vulkan_raster_t*)data;
-   const char* msg_end   = msg + msg_len;
-   int delta_x           = 0;
-
-   if (     !font
-         || !font->font_driver
-         || !font->font_driver->get_glyph
-         || !font->font_data )
-      return 0;
-
-   glyph_q = font->font_driver->get_glyph(font->font_data, '?');
-
-   while (msg < msg_end)
-   {
-      const struct font_glyph *glyph;
-      uint32_t code                  = utf8_walk(&msg);
-
-      /* Do something smarter here ... */
-      if (!(glyph = font->font_driver->get_glyph(
-                  font->font_data, code)))
-         if (!(glyph = glyph_q))
-            continue;
-
-      if (font->atlas->dirty)
-      {
-         vulkan_font_update_glyph(font, glyph);
-         font->atlas->dirty = false;
-         font->needs_update = true;
-      }
-      delta_x += glyph->advance_x;
-   }
-
-   return delta_x * scale;
-}
-
-static void vulkan_font_render_line(vk_t *vk,
-      vulkan_raster_t *font, const char *msg, size_t msg_len,
-      float scale, const float color[4], float pos_x,
-      float pos_y, unsigned text_align)
-{
-   struct vk_color vk_color;
-   const struct font_glyph* glyph_q = NULL;
-   const char* msg_end              = msg + msg_len;
-   int x                            = roundf(pos_x * vk->vp.width);
-   int y                            = roundf((1.0f - pos_y) * vk->vp.height);
-   int delta_x                      = 0;
-   int delta_y                      = 0;
-   float inv_tex_size_x             = 1.0f / font->texture.width;
-   float inv_tex_size_y             = 1.0f / font->texture.height;
-   float inv_win_width              = 1.0f / font->vk->vp.width;
-   float inv_win_height             = 1.0f / font->vk->vp.height;
-
-   vk_color.r                       = color[0];
-   vk_color.g                       = color[1];
-   vk_color.b                       = color[2];
-   vk_color.a                       = color[3];
-
-   switch (text_align)
-   {
-      case TEXT_ALIGN_RIGHT:
-         x -= vulkan_get_message_width(font, msg, msg_len, scale);
-         break;
-      case TEXT_ALIGN_CENTER:
-         x -= vulkan_get_message_width(font, msg, msg_len, scale) / 2;
-         break;
-   }
-
-   glyph_q = font->font_driver->get_glyph(font->font_data, '?');
-
-   while (msg < msg_end)
-   {
-      const struct font_glyph *glyph;
-      int off_x, off_y, tex_x, tex_y, width, height;
-      unsigned code = utf8_walk(&msg);
-
-      /* Do something smarter here ... */
-      if (!(glyph =
-               font->font_driver->get_glyph(font->font_data, code)))
-         if (!(glyph = glyph_q))
-            continue;
-
-      if (font->atlas->dirty)
-      {
-         vulkan_font_update_glyph(font, glyph);
-         font->atlas->dirty = false;
-         font->needs_update = true;
-      }
-
-      off_x  = glyph->draw_offset_x;
-      off_y  = glyph->draw_offset_y;
-      tex_x  = glyph->atlas_offset_x;
-      tex_y  = glyph->atlas_offset_y;
-      width  = glyph->width;
-      height = glyph->height;
-
-      {
-         struct vk_vertex *pv          = font->pv + font->vertices;
-         float _x                      = (x + (off_x + delta_x) * scale)
-            * inv_win_width;
-         float _y                      = (y + (off_y + delta_y) * scale)
-            * inv_win_height;
-         float _width                  = width  * scale * inv_win_width;
-         float _height                 = height * scale * inv_win_height;
-         float _tex_x                  = tex_x * inv_tex_size_x;
-         float _tex_y                  = tex_y * inv_tex_size_y;
-         float _tex_width              = width * inv_tex_size_x;
-         float _tex_height             = height * inv_tex_size_y;
-         const struct vk_color *_color = &vk_color;
-
-         VULKAN_WRITE_QUAD_VBO(pv, _x, _y, _width, _height, _tex_x, _tex_y, _tex_width, _tex_height, _color);
-      }
-
-      font->vertices += 6;
-
-      delta_x        += glyph->advance_x;
-      delta_y        += glyph->advance_y;
-   }
-}
-
-static void vulkan_font_render_message(
-      vulkan_raster_t *font, const char *msg, float scale,
-      const float color[4], float pos_x, float pos_y,
-      unsigned text_align)
-{
-   struct font_line_metrics *line_metrics = NULL;
-   int lines                              = 0;
-   float line_height;
-
-   if (!msg || !*msg || !font->vk)
-      return;
-
-   /* If font line metrics are not supported just draw as usual */
-   if (!font->font_driver->get_line_metrics ||
-       !font->font_driver->get_line_metrics(font->font_data, &line_metrics))
-   {
-      vulkan_font_render_line(font->vk, font, msg, strlen(msg),
-            scale, color, pos_x, pos_y, text_align);
-      return;
-   }
-
-   line_height = line_metrics->height * scale / font->vk->vp.height;
-
-   for (;;)
-   {
-      const char *delim = strchr(msg, '\n');
-      size_t msg_len    = delim
-         ? (delim - msg) : strlen(msg);
-
-      /* Draw the line */
-      vulkan_font_render_line(font->vk, font, msg, msg_len,
-            scale, color, pos_x, pos_y - (float)lines * line_height,
-            text_align);
-
-      if (!delim)
-         break;
-
-      msg += msg_len + 1;
-      lines++;
-   }
-}
-
-static void vulkan_font_flush(vulkan_raster_t *font)
-{
-   struct vk_draw_triangles call;
-
-   call.pipeline     = font->vk->pipelines.font;
-   call.texture      = &font->texture_optimal;
-   call.sampler      = font->vk->samplers.mipmap_linear;
-   call.uniform      = &font->vk->mvp;
-   call.uniform_size = sizeof(font->vk->mvp);
-   call.vbo          = &font->range;
-   call.vertices     = font->vertices;
-
-   if (font->needs_update)
-   {
-      VkCommandBuffer staging;
-      VkSubmitInfo submit_info;
-      VkCommandBufferAllocateInfo cmd_info;
-      VkCommandBufferBeginInfo begin_info;
-      struct vk_texture *dynamic_tex  = NULL;
-      struct vk_texture *staging_tex  = NULL;
-
-      cmd_info.sType              = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
-      cmd_info.pNext              = NULL;
-      cmd_info.commandPool        = font->vk->staging_pool;
-      cmd_info.level              = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
-      cmd_info.commandBufferCount = 1;
-      vkAllocateCommandBuffers(font->vk->context->device, &cmd_info, &staging);
-
-      begin_info.sType            = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
-      begin_info.pNext            = NULL;
-      begin_info.flags            = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
-      begin_info.pInheritanceInfo = NULL;
-      vkBeginCommandBuffer(staging, &begin_info);
-
-      VULKAN_SYNC_TEXTURE_TO_GPU_COND_OBJ(font->vk, font->texture);
-
-      dynamic_tex                 = &font->texture_optimal;
-      staging_tex                 = &font->texture;
-
-      vulkan_copy_staging_to_dynamic(font->vk, staging,
-            dynamic_tex, staging_tex);
-
-      vkEndCommandBuffer(staging);
-
-#ifdef HAVE_THREADS
-      slock_lock(font->vk->context->queue_lock);
-#endif
-
-      submit_info.sType                = VK_STRUCTURE_TYPE_SUBMIT_INFO;
-      submit_info.pNext                = NULL;
-      submit_info.waitSemaphoreCount   = 0;
-      submit_info.pWaitSemaphores      = NULL;
-      submit_info.pWaitDstStageMask    = NULL;
-      submit_info.commandBufferCount   = 1;
-      submit_info.pCommandBuffers      = &staging;
-      submit_info.signalSemaphoreCount = 0;
-      submit_info.pSignalSemaphores    = NULL;
-      vkQueueSubmit(font->vk->context->queue,
-            1, &submit_info, VK_NULL_HANDLE);
-
-      vkQueueWaitIdle(font->vk->context->queue);
-
-#ifdef HAVE_THREADS
-      slock_unlock(font->vk->context->queue_lock);
-#endif
-
-      vkFreeCommandBuffers(font->vk->context->device,
-            font->vk->staging_pool, 1, &staging);
-
-      font->needs_update = false;
-   }
-
-   vulkan_draw_triangles(font->vk, &call);
-}
-
-static void vulkan_font_render_msg(
-      void *userdata,
-      void *data,
-      const char *msg,
-      const struct font_params *params)
-{
-   float color[4];
-   int drop_x, drop_y;
-   bool full_screen;
-   size_t max_glyphs;
-   unsigned width, height;
-   enum text_alignment text_align;
-   float x, y, scale, drop_mod, drop_alpha;
-   vk_t *vk                         = NULL;
-   vulkan_raster_t *font            = (vulkan_raster_t*)data;
-   settings_t *settings             = config_get_ptr();
-   float video_msg_pos_x            = settings->floats.video_msg_pos_x;
-   float video_msg_pos_y            = settings->floats.video_msg_pos_y;
-   float video_msg_color_r          = settings->floats.video_msg_color_r;
-   float video_msg_color_g          = settings->floats.video_msg_color_g;
-   float video_msg_color_b          = settings->floats.video_msg_color_b;
-
-   if (!font || !msg || !*msg)
-      return;
-
-   vk             = font->vk;
-
-   width          = vk->video_width;
-   height         = vk->video_height;
-
-   if (params)
-   {
-      x           = params->x;
-      y           = params->y;
-      scale       = params->scale;
-      full_screen = params->full_screen;
-      text_align  = params->text_align;
-      drop_x      = params->drop_x;
-      drop_y      = params->drop_y;
-      drop_mod    = params->drop_mod;
-      drop_alpha  = params->drop_alpha;
-
-      color[0]    = FONT_COLOR_GET_RED(params->color)   / 255.0f;
-      color[1]    = FONT_COLOR_GET_GREEN(params->color) / 255.0f;
-      color[2]    = FONT_COLOR_GET_BLUE(params->color)  / 255.0f;
-      color[3]    = FONT_COLOR_GET_ALPHA(params->color) / 255.0f;
-
-      /* If alpha is 0.0f, turn it into default 1.0f */
-      if (color[3] <= 0.0f)
-         color[3] = 1.0f;
-   }
-   else
-   {
-      x           = video_msg_pos_x;
-      y           = video_msg_pos_y;
-      scale       = 1.0f;
-      full_screen = true;
-      text_align  = TEXT_ALIGN_LEFT;
-      drop_x      = -2;
-      drop_y      = -2;
-      drop_mod    = 0.3f;
-      drop_alpha  = 1.0f;
-
-      color[0]    = video_msg_color_r;
-      color[1]    = video_msg_color_g;
-      color[2]    = video_msg_color_b;
-      color[3]    = 1.0f;
-   }
-
-   video_driver_set_viewport(width, height, full_screen, false);
-
-   max_glyphs = strlen(msg);
-   if (drop_x || drop_y)
-      max_glyphs *= 2;
-
-   if (!vulkan_buffer_chain_alloc(font->vk->context, &font->vk->chain->vbo,
-         6 * sizeof(struct vk_vertex) * max_glyphs, &font->range))
-      return;
-
-   font->vertices   = 0;
-   font->pv         = (struct vk_vertex*)font->range.data;
-
-   if (drop_x || drop_y)
-   {
-      float color_dark[4];
-      color_dark[0] = color[0] * drop_mod;
-      color_dark[1] = color[1] * drop_mod;
-      color_dark[2] = color[2] * drop_mod;
-      color_dark[3] = color[3] * drop_alpha;
-
-      vulkan_font_render_message(font, msg, scale, color_dark,
-            x + scale * drop_x / vk->vp.width, y +
-            scale * drop_y / vk->vp.height, text_align);
-   }
-
-   vulkan_font_render_message(font, msg, scale,
-         color, x, y, text_align);
-   vulkan_font_flush(font);
-}
-
-static const struct font_glyph *vulkan_font_get_glyph(
-      void *data, uint32_t code)
-{
-   const struct font_glyph* glyph;
-   vulkan_raster_t *font = (vulkan_raster_t*)data;
-
-   if (!font || !font->font_driver || !font->font_driver->ident)
-      return NULL;
-
-   glyph = font->font_driver->get_glyph((void*)font->font_driver, code);
-
-   if (glyph && font->atlas->dirty)
-   {
-      vulkan_font_update_glyph(font, glyph);
-      font->atlas->dirty = false;
-      font->needs_update = true;
-   }
-   return glyph;
-}
-
-static bool vulkan_get_line_metrics(void* data,
-      struct font_line_metrics **metrics)
-{
-   vulkan_raster_t *font = (vulkan_raster_t*)data;
-   if (font && font->font_driver && font->font_data)
-      return font->font_driver->get_line_metrics(font->font_data, metrics);
-   return false;
-}
-
-font_renderer_t vulkan_raster_font = {
-   vulkan_font_init,
-   vulkan_font_free,
-   vulkan_font_render_msg,
-   "vulkan_font",
-   vulkan_font_get_glyph,
-   NULL,                            /* bind_block */
-   NULL,                            /* flush_block */
-   vulkan_get_message_width,
-   vulkan_get_line_metrics
-};

+ 0 - 310
app/src/main/cpp/gfx/drivers_font_renderer/bitmap.h

@@ -1,310 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- *  Copyright (C) 2011-2017 - Daniel De Matteis
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __RARCH_FONT_BITMAP_H
-#define __RARCH_FONT_BITMAP_H
-
-#include <stdint.h>
-
-#define FONT_WIDTH 5
-#define FONT_HEIGHT 10
-/* FONT_HEIGHT_BASELINE_OFFSET:
- * Distance in pixels from top of character
- * to baseline */
-#define FONT_HEIGHT_BASELINE_OFFSET 8
-#define FONT_WIDTH_STRIDE (FONT_WIDTH + 1)
-#define FONT_HEIGHT_STRIDE (FONT_HEIGHT + 1)
-
-#define FONT_OFFSET(x) ((x) * ((FONT_HEIGHT * FONT_WIDTH + 7) / 8))
-
-static const unsigned char bitmap_bin[1792] = {
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x00 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x01 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x02 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x03 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x04 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x05 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x06 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x07 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x08 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x09 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x0a */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x0b */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x0c */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x0d */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x0e */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x0f */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x10 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x11 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x12 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x13 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x14 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x15 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x16 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x17 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x18 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x19 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x1a */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x1b */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x1c */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x1d */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x1e */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x1f */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x20 */
-   0x80,0x10,0x42,0x08,0x20,0x00,0x00, /* code=0x21 */
-   0x4A,0x29,0x00,0x00,0x00,0x00,0x00, /* code=0x22 */
-   0x00,0xA8,0xAF,0xD4,0x57,0x00,0x00, /* code=0x23 */
-   0x80,0xF8,0xE2,0xE8,0x23,0x00,0x00, /* code=0x24 */
-   0x60,0x4E,0x44,0x44,0xCE,0x00,0x00, /* code=0x25 */
-   0xC0,0xA4,0x64,0x6A,0xB2,0x00,0x00, /* code=0x26 */
-   0x84,0x10,0x00,0x00,0x00,0x00,0x00, /* code=0x27 */
-   0x88,0x08,0x21,0x84,0x20,0x08,0x00, /* code=0x28 */
-   0x82,0x20,0x84,0x10,0x22,0x02,0x00, /* code=0x29 */
-   0x00,0x90,0xEA,0x2A,0x01,0x00,0x00, /* code=0x2a */
-   0x00,0x10,0xF2,0x09,0x01,0x00,0x00, /* code=0x2b */
-   0x00,0x00,0x00,0x00,0x20,0x02,0x00, /* code=0x2c */
-   0x00,0x00,0xF0,0x01,0x00,0x00,0x00, /* code=0x2d */
-   0x00,0x00,0x00,0x00,0x20,0x00,0x00, /* code=0x2e */
-   0x10,0x22,0x44,0x88,0x10,0x01,0x00, /* code=0x2f */
-   0xC0,0xC5,0x58,0x63,0x74,0x00,0x00, /* code=0x30 */
-   0x80,0x18,0x42,0x08,0x71,0x00,0x00, /* code=0x31 */
-   0xC0,0x45,0xC8,0x44,0xF8,0x00,0x00, /* code=0x32 */
-   0xC0,0x45,0xC8,0x60,0x74,0x00,0x00, /* code=0x33 */
-   0x00,0x31,0x95,0x3E,0x42,0x00,0x00, /* code=0x34 */
-   0xE0,0x87,0xF0,0x60,0x74,0x00,0x00, /* code=0x35 */
-   0x80,0x89,0xF0,0x62,0x74,0x00,0x00, /* code=0x36 */
-   0xE0,0x43,0x84,0x08,0x11,0x00,0x00, /* code=0x37 */
-   0xC0,0xC5,0xE8,0x62,0x74,0x00,0x00, /* code=0x38 */
-   0xC0,0xC5,0xE8,0x21,0x32,0x00,0x00, /* code=0x39 */
-   0x00,0x00,0x02,0x00,0x01,0x00,0x00, /* code=0x3a */
-   0x00,0x00,0x02,0x00,0x11,0x00,0x00, /* code=0x3b */
-   0x00,0x40,0x36,0x18,0x04,0x00,0x00, /* code=0x3c */
-   0x00,0x80,0x0F,0x3E,0x00,0x00,0x00, /* code=0x3d */
-   0x00,0x04,0x83,0x4D,0x00,0x00,0x00, /* code=0x3e */
-   0xC0,0x45,0x88,0x08,0x20,0x00,0x00, /* code=0x3f */
-   0xC0,0xC5,0x5A,0x7B,0xF0,0x00,0x00, /* code=0x40 */
-   0x80,0x10,0xA5,0x5C,0x8C,0x00,0x00, /* code=0x41 */
-   0xE0,0xC5,0xF8,0x62,0x7C,0x00,0x00, /* code=0x42 */
-   0xC0,0xC5,0x10,0x42,0x74,0x00,0x00, /* code=0x43 */
-   0xE0,0xA4,0x18,0x63,0x3A,0x00,0x00, /* code=0x44 */
-   0xE0,0x87,0xF0,0x42,0xF8,0x00,0x00, /* code=0x45 */
-   0xE0,0x87,0xF0,0x42,0x08,0x00,0x00, /* code=0x46 */
-   0xC0,0xC5,0x90,0x63,0xF4,0x00,0x00, /* code=0x47 */
-   0x20,0xC6,0xF8,0x63,0x8C,0x00,0x00, /* code=0x48 */
-   0xC0,0x11,0x42,0x08,0x71,0x00,0x00, /* code=0x49 */
-   0x80,0x43,0x08,0x21,0x7C,0x00,0x00, /* code=0x4a */
-   0x20,0xA6,0x32,0x4A,0x8A,0x00,0x00, /* code=0x4b */
-   0x20,0x84,0x10,0x42,0xF8,0x00,0x00, /* code=0x4c */
-   0x20,0xC6,0xBD,0x6B,0x8D,0x00,0x00, /* code=0x4d */
-   0x60,0xCE,0x5A,0x6B,0xCE,0x00,0x00, /* code=0x4e */
-   0xC0,0xC5,0x18,0x63,0x74,0x00,0x00, /* code=0x4f */
-   0xE0,0xC5,0xF8,0x42,0x08,0x00,0x00, /* code=0x50 */
-   0xC0,0xC5,0x18,0x63,0xF6,0x00,0x00, /* code=0x51 */
-   0xE0,0xC5,0xF8,0x62,0x8C,0x00,0x00, /* code=0x52 */
-   0xC0,0xC5,0xE0,0x60,0x74,0x00,0x00, /* code=0x53 */
-   0xE0,0x13,0x42,0x08,0x21,0x00,0x00, /* code=0x54 */
-   0x20,0xC6,0x18,0x63,0x74,0x00,0x00, /* code=0x55 */
-   0x20,0xC6,0xA8,0x14,0x21,0x00,0x00, /* code=0x56 */
-   0x20,0xD6,0x5A,0x95,0x52,0x00,0x00, /* code=0x57 */
-   0x20,0x46,0x45,0x54,0x8C,0x00,0x00, /* code=0x58 */
-   0x20,0xC6,0xE8,0x08,0x21,0x00,0x00, /* code=0x59 */
-   0xE0,0x43,0x44,0x44,0xF8,0x00,0x00, /* code=0x5a */
-   0x4E,0x08,0x21,0x84,0x10,0x0E,0x00, /* code=0x5b */
-   0x21,0x08,0x41,0x08,0x42,0x10,0x00, /* code=0x5c */
-   0x0E,0x21,0x84,0x10,0x42,0x0E,0x00, /* code=0x5d */
-   0x80,0xA8,0x08,0x00,0x00,0x00,0x00, /* code=0x5e */
-   0x00,0x00,0x00,0x00,0x00,0x1F,0x00, /* code=0x5f */
-   0x80,0x20,0x00,0x00,0x00,0x00,0x00, /* code=0x60 */
-   0x00,0x00,0x07,0x7D,0xF4,0x00,0x00, /* code=0x61 */
-   0x21,0x84,0x17,0x63,0x7C,0x00,0x00, /* code=0x62 */
-   0x00,0x00,0x1F,0x42,0xF0,0x00,0x00, /* code=0x63 */
-   0x10,0x42,0x1F,0x63,0xF4,0x00,0x00, /* code=0x64 */
-   0x00,0x00,0x17,0x7F,0xF0,0x00,0x00, /* code=0x65 */
-   0x5C,0x88,0x27,0x84,0x10,0x00,0x00, /* code=0x66 */
-   0x00,0x00,0x17,0x63,0xF4,0xD0,0x01, /* code=0x67 */
-   0x21,0x84,0x17,0x63,0x8C,0x00,0x00, /* code=0x68 */
-   0x80,0x00,0x43,0x08,0x21,0x00,0x00, /* code=0x69 */
-   0x00,0x01,0x86,0x10,0x42,0xE8,0x00, /* code=0x6a */
-   0x42,0x08,0xA9,0x8C,0x92,0x00,0x00, /* code=0x6b */
-   0x86,0x10,0x42,0x08,0x21,0x00,0x00, /* code=0x6c */
-   0x00,0x80,0x55,0x6B,0xAD,0x00,0x00, /* code=0x6d */
-   0x00,0x80,0x17,0x63,0x8C,0x00,0x00, /* code=0x6e */
-   0x00,0x00,0x17,0x63,0x74,0x00,0x00, /* code=0x6f */
-   0x00,0x80,0x17,0x63,0x7C,0x21,0x00, /* code=0x70 */
-   0x00,0x00,0x1F,0x63,0xF4,0x10,0x02, /* code=0x71 */
-   0x00,0x80,0x36,0x43,0x08,0x00,0x00, /* code=0x72 */
-   0x00,0x00,0x1F,0x1C,0x7C,0x00,0x00, /* code=0x73 */
-   0x40,0x08,0x27,0x84,0xE0,0x00,0x00, /* code=0x74 */
-   0x00,0x80,0x18,0x63,0xF4,0x00,0x00, /* code=0x75 */
-   0x00,0x80,0x18,0x95,0x22,0x00,0x00, /* code=0x76 */
-   0x00,0x80,0x58,0xAB,0x52,0x00,0x00, /* code=0x77 */
-   0x00,0x80,0xA8,0x88,0x8A,0x00,0x00, /* code=0x78 */
-   0x00,0x80,0x18,0x63,0xF4,0xD0,0x01, /* code=0x79 */
-   0x00,0x80,0x8F,0x88,0xF8,0x00,0x00, /* code=0x7a */
-   0x88,0x10,0x22,0x08,0x21,0x08,0x00, /* code=0x7b */
-   0x84,0x10,0x42,0x08,0x21,0x04,0x00, /* code=0x7c */
-   0x82,0x10,0x82,0x08,0x21,0x02,0x00, /* code=0x7d */
-   0x00,0x00,0x60,0x1B,0x00,0x00,0x00, /* code=0x7e */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x7f */
-   0x3F,0xC6,0x18,0x63,0x8C,0xF1,0x03, /* code=0x80 */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x81 */
-   0x00,0x00,0x00,0x00,0x40,0x04,0x00, /* code=0x82 */
-   0x98,0x10,0x47,0x08,0x21,0x44,0x00, /* code=0x83 */
-   0x00,0x00,0x00,0x00,0x50,0x0A,0x00, /* code=0x84 */
-   0x00,0x00,0x00,0x00,0xA8,0x00,0x00, /* code=0x85 */
-   0x84,0x7C,0x42,0x08,0x21,0x04,0x00, /* code=0x86 */
-   0x84,0x7C,0xF2,0x09,0x21,0x04,0x00, /* code=0x87 */
-   0x44,0x01,0x00,0x00,0x00,0x00,0x00, /* code=0x88 */
-   0x60,0x4E,0x44,0x44,0xAD,0x00,0x00, /* code=0x89 */
-   0x8A,0x00,0x1F,0x3E,0x7C,0x00,0x00, /* code=0x8a */
-   0x00,0x00,0x44,0x04,0x41,0x00,0x00, /* code=0x8b */
-   0x40,0x97,0xD2,0x4A,0xD1,0x00,0x00, /* code=0x8c */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x8d */
-   0x8A,0x80,0x8F,0x88,0xF8,0x00,0x00, /* code=0x8e */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x8f */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x90 */
-   0x84,0x20,0x00,0x00,0x00,0x00,0x00, /* code=0x91 */
-   0x08,0x11,0x00,0x00,0x00,0x00,0x00, /* code=0x92 */
-   0x4A,0x51,0x00,0x00,0x00,0x00,0x00, /* code=0x93 */
-   0x94,0x2A,0x00,0x00,0x00,0x00,0x00, /* code=0x94 */
-   0x00,0x00,0xC0,0x18,0x00,0x00,0x00, /* code=0x95 */
-   0x00,0x00,0x00,0x3E,0x00,0x00,0x00, /* code=0x96 */
-   0x00,0x00,0x00,0x3E,0x00,0x00,0x00, /* code=0x97 */
-   0x00,0x00,0x40,0x15,0x00,0x00,0x00, /* code=0x98 */
-   0x00,0x80,0xAF,0xB5,0x06,0x00,0x00, /* code=0x99 */
-   0x8A,0x00,0x1F,0x1C,0x7C,0x00,0x00, /* code=0x9a */
-   0x00,0x00,0x82,0x20,0x22,0x00,0x00, /* code=0x9b */
-   0x00,0x00,0x5D,0x5A,0xD1,0x00,0x00, /* code=0x9c */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0x9d */
-   0x8A,0x80,0x8F,0x88,0xF8,0x00,0x00, /* code=0x9e */
-   0x0A,0xC4,0x18,0x1D,0x21,0x00,0x00, /* code=0x9f */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0xa0 */
-   0x00,0x00,0x02,0x08,0x21,0x84,0x00, /* code=0xa1 */
-   0x00,0x20,0xAE,0x14,0x47,0x00,0x00, /* code=0xa2 */
-   0x98,0x10,0x4F,0x44,0xF8,0x00,0x00, /* code=0xa3 */
-   0x00,0x44,0xA7,0x5C,0x04,0x00,0x00, /* code=0xa4 */
-   0x20,0x46,0x47,0x3E,0x21,0x00,0x00, /* code=0xa5 */
-   0x84,0x10,0x02,0x08,0x21,0x04,0x00, /* code=0xa6 */
-   0x98,0x10,0x17,0xA3,0x23,0x64,0x00, /* code=0xa7 */
-   0x40,0x01,0x00,0x00,0x00,0x00,0x00, /* code=0xa8 */
-   0xC0,0xC5,0x3B,0x6F,0x74,0x00,0x00, /* code=0xa9 */
-   0x04,0x31,0xC5,0x00,0x00,0x00,0x00, /* code=0xaa */
-   0x00,0x00,0xAA,0x8A,0xA2,0x00,0x00, /* code=0xab */
-   0x00,0x00,0xE0,0x21,0x84,0x00,0x00, /* code=0xac */
-   0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0xad */
-   0xC0,0xC5,0x3A,0x67,0x74,0x00,0x00, /* code=0xae */
-   0xE0,0x03,0x00,0x00,0x00,0x00,0x00, /* code=0xaf */
-   0x80,0x38,0x02,0x00,0x00,0x00,0x00, /* code=0xb0 */
-   0x00,0x10,0xF2,0x09,0xF9,0x00,0x00, /* code=0xb1 */
-   0x06,0x11,0xE1,0x00,0x00,0x00,0x00, /* code=0xb2 */
-   0x06,0x19,0x64,0x00,0x00,0x00,0x00, /* code=0xb3 */
-   0x88,0x00,0x00,0x00,0x00,0x00,0x00, /* code=0xb4 */
-   0x00,0x80,0x94,0x52,0xBA,0x21,0x00, /* code=0xb5 */
-   0xC0,0xDF,0x6B,0x29,0xA5,0x94,0x02, /* code=0xb6 */
-   0x00,0x00,0x40,0x00,0x00,0x00,0x00, /* code=0xb7 */
-   0x00,0x00,0x00,0x00,0x60,0x88,0x00, /* code=0xb8 */
-   0xC4,0x10,0xE2,0x00,0x00,0x00,0x00, /* code=0xb9 */
-   0x44,0x29,0x02,0x00,0x00,0x00,0x00, /* code=0xba */
-   0x00,0x80,0xA2,0xA8,0x2A,0x00,0x00, /* code=0xbb */
-   0x30,0xC6,0x44,0x75,0x8F,0x01,0x00, /* code=0xbc */
-   0x30,0xA6,0xA2,0x63,0xCA,0x01,0x00, /* code=0xbd */
-   0x51,0x4E,0x55,0x75,0x8F,0x01,0x00, /* code=0xbe */
-   0x00,0x00,0x02,0x88,0x08,0xD1,0x01, /* code=0xbf */
-   0x82,0x00,0xE2,0x5C,0x8C,0x00,0x00, /* code=0xc0 */
-   0x88,0x00,0xE2,0x5C,0x8C,0x00,0x00, /* code=0xc1 */
-   0x44,0x01,0xE2,0x5C,0x8C,0x00,0x00, /* code=0xc2 */
-   0x54,0x01,0xE2,0x5C,0x8C,0x00,0x00, /* code=0xc3 */
-   0x40,0x01,0xE2,0x5C,0x8C,0x00,0x00, /* code=0xc4 */
-   0xC4,0x11,0xE2,0x5C,0x8C,0x00,0x00, /* code=0xc5 */
-   0xC0,0x1B,0xE3,0x4A,0xE9,0x00,0x00, /* code=0xc6 */
-   0xC0,0xC5,0x10,0x42,0x74,0x44,0x00, /* code=0xc7 */
-   0x82,0x80,0x1F,0x5E,0xF8,0x00,0x00, /* code=0xc8 */
-   0x88,0x80,0x1F,0x5E,0xF8,0x00,0x00, /* code=0xc9 */
-   0x44,0x81,0x1F,0x5E,0xF8,0x00,0x00, /* code=0xca */
-   0x40,0x81,0x1F,0x5E,0xF8,0x00,0x00, /* code=0xcb */
-   0x82,0x00,0x47,0x08,0x71,0x00,0x00, /* code=0xcc */
-   0x88,0x00,0x47,0x08,0x71,0x00,0x00, /* code=0xcd */
-   0x44,0x01,0x47,0x08,0x71,0x00,0x00, /* code=0xce */
-   0x40,0x01,0x47,0x08,0x71,0x00,0x00, /* code=0xcf */
-   0xC0,0x28,0x79,0xA5,0x32,0x00,0x00, /* code=0xd0 */
-   0x54,0x81,0x59,0x6B,0xCE,0x00,0x00, /* code=0xd1 */
-   0x82,0x00,0x17,0x63,0x74,0x00,0x00, /* code=0xd2 */
-   0x88,0x00,0x17,0x63,0x74,0x00,0x00, /* code=0xd3 */
-   0x44,0x01,0x17,0x63,0x74,0x00,0x00, /* code=0xd4 */
-   0x54,0x01,0x17,0x63,0x74,0x00,0x00, /* code=0xd5 */
-   0x40,0x01,0x17,0x63,0x74,0x00,0x00, /* code=0xd6 */
-   0x00,0x44,0x45,0x54,0x04,0x00,0x00, /* code=0xd7 */
-   0xC0,0xC5,0x5C,0x67,0x74,0x00,0x00, /* code=0xd8 */
-   0x82,0x80,0x18,0x63,0x74,0x00,0x00, /* code=0xd9 */
-   0x88,0x80,0x18,0x63,0x74,0x00,0x00, /* code=0xda */
-   0x44,0x81,0x18,0x63,0x74,0x00,0x00, /* code=0xdb */
-   0x40,0x81,0x18,0x63,0x74,0x00,0x00, /* code=0xdc */
-   0x88,0xC4,0x18,0x1D,0x21,0x00,0x00, /* code=0xdd */
-   0x40,0x08,0x27,0xA5,0x13,0x02,0x00, /* code=0xde */
-   0xC0,0x49,0xE9,0xA4,0x74,0x01,0x00, /* code=0xdf */
-   0x82,0x00,0x07,0x7D,0xF4,0x00,0x00, /* code=0xe0 */
-   0x88,0x00,0x07,0x7D,0xF4,0x00,0x00, /* code=0xe1 */
-   0x44,0x01,0x07,0x7D,0xF4,0x00,0x00, /* code=0xe2 */
-   0x54,0x01,0x07,0x7D,0xF4,0x00,0x00, /* code=0xe3 */
-   0x40,0x01,0x07,0x7D,0xF4,0x00,0x00, /* code=0xe4 */
-   0xC4,0x11,0x07,0x7D,0xF4,0x00,0x00, /* code=0xe5 */
-   0x00,0x00,0x45,0x5D,0xD1,0x00,0x00, /* code=0xe6 */
-   0x00,0x00,0x1F,0x42,0xF0,0x44,0x00, /* code=0xe7 */
-   0x82,0x00,0x17,0x7F,0xF0,0x00,0x00, /* code=0xe8 */
-   0x88,0x00,0x17,0x7F,0xF0,0x00,0x00, /* code=0xe9 */
-   0x44,0x01,0x17,0x7F,0xF0,0x00,0x00, /* code=0xea */
-   0x40,0x01,0x17,0x7F,0xF0,0x00,0x00, /* code=0xeb */
-   0x82,0x00,0x43,0x08,0x21,0x00,0x00, /* code=0xec */
-   0x88,0x00,0x43,0x08,0x21,0x00,0x00, /* code=0xed */
-   0x44,0x01,0x43,0x08,0x21,0x00,0x00, /* code=0xee */
-   0x40,0x01,0x43,0x08,0x21,0x00,0x00, /* code=0xef */
-   0xC0,0x22,0xE8,0x63,0x74,0x00,0x00, /* code=0xf0 */
-   0x54,0x81,0x17,0x63,0x8C,0x00,0x00, /* code=0xf1 */
-   0x82,0x00,0x17,0x63,0x74,0x00,0x00, /* code=0xf2 */
-   0x88,0x00,0x17,0x63,0x74,0x00,0x00, /* code=0xf3 */
-   0x44,0x01,0x17,0x63,0x74,0x00,0x00, /* code=0xf4 */
-   0xAA,0x00,0x17,0x63,0x74,0x00,0x00, /* code=0xf5 */
-   0x40,0x01,0x17,0x63,0x74,0x00,0x00, /* code=0xf6 */
-   0x00,0x10,0xF0,0x01,0x01,0x00,0x00, /* code=0xf7 */
-   0x00,0x00,0x97,0xEB,0x74,0x00,0x00, /* code=0xf8 */
-   0x82,0x80,0x18,0x63,0xF4,0x00,0x00, /* code=0xf9 */
-   0x88,0x80,0x18,0x63,0xF4,0x00,0x00, /* code=0xfa */
-   0x44,0x81,0x18,0x63,0xF4,0x00,0x00, /* code=0xfb */
-   0x40,0x81,0x18,0x63,0xF4,0x00,0x00, /* code=0xfc */
-   0x88,0x80,0x18,0x63,0xF4,0xD0,0x01, /* code=0xfd */
-   0x20,0x84,0x17,0x63,0x7C,0x21,0x00, /* code=0xfe */
-   0x40,0x81,0x18,0x63,0xF4,0xD0,0x01  /* code=0xff */
-};
-
-typedef struct
-{
-   bool **lut;
-   uint16_t glyph_min;
-   uint16_t glyph_max;
-} bitmapfont_lut_t;
-
-/* Generates a boolean LUT:
- *   lut[num_glyphs][glyph_width * glyph_height]
- * LUT value is 'true' if glyph pixel has a
- * non-zero value.
- * Returned object must be freed using
- * bitmapfont_free_lut().
- * Returns NULL in the event of an error. */
-bitmapfont_lut_t *bitmapfont_get_lut(void);
-
-void bitmapfont_free_lut(bitmapfont_lut_t *font);
-
-#endif

+ 0 - 252
app/src/main/cpp/gfx/drivers_font_renderer/bitmapfont.c

@@ -1,252 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- *  Copyright (C) 2011-2017 - Daniel De Matteis
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#include <boolean.h>
-
-#include "bitmap.h"
-
-#include "../font_driver.h"
-
-#define BMP_ATLAS_COLS 16
-#define BMP_ATLAS_ROWS 16
-#define BMP_ATLAS_SIZE (BMP_ATLAS_COLS * BMP_ATLAS_ROWS)
-
-/* Padding is required between each glyph in
- * the atlas to prevent texture bleed when
- * drawing with linear filtering enabled */
-#define BMP_ATLAS_PADDING 1
-
-typedef struct bm_renderer
-{
-   unsigned scale_factor;
-   struct font_glyph glyphs[BMP_ATLAS_SIZE];
-   struct font_atlas atlas;
-   struct font_line_metrics line_metrics;
-} bm_renderer_t;
-
-/* Generates a boolean LUT:
- *   lut[num_glyphs][glyph_width * glyph_height]
- * LUT value is 'true' if glyph pixel has a
- * non-zero value.
- * Returned object must be freed using
- * bitmapfont_free_lut().
- * Returns NULL in the event of an error. */
-bitmapfont_lut_t *bitmapfont_get_lut(void)
-{
-   bitmapfont_lut_t *font = NULL;
-   size_t symbol_index;
-   size_t i, j;
-
-   /* Initialise font struct */
-   font = (bitmapfont_lut_t*)calloc(1, sizeof(bitmapfont_lut_t));
-   if (!font)
-      goto error;
-
-   font->glyph_min = 0;
-   font->glyph_max = BMP_ATLAS_SIZE - 1;
-
-   /* Note: Need to use a calloc() here, otherwise
-    * we'll get undefined behaviour when calling
-    * bitmapfont_free_lut() if the following loop fails */
-   font->lut = (bool**)calloc(1, BMP_ATLAS_SIZE * sizeof(bool*));
-   if (!font->lut)
-      goto error;
-
-   /* Loop over all possible characters */
-   for (symbol_index = 0; symbol_index < BMP_ATLAS_SIZE; symbol_index++)
-   {
-      /* Allocate memory for current symbol */
-      font->lut[symbol_index] = (bool*)malloc(FONT_WIDTH *
-            FONT_HEIGHT * sizeof(bool));
-      if (!font->lut[symbol_index])
-         goto error;
-
-      for (j = 0; j < FONT_HEIGHT; j++)
-      {
-         for (i = 0; i < FONT_WIDTH; i++)
-         {
-            uint8_t rem     = 1 << ((i + j * FONT_WIDTH) & 7);
-            size_t offset   = (i + j * FONT_WIDTH) >> 3;
-
-            /* LUT value is 'true' if specified glyph
-             * position contains a pixel */
-            font->lut[symbol_index][i + (j * FONT_WIDTH)] =
-                  (bitmap_bin[FONT_OFFSET(symbol_index) + offset] & rem) > 0;
-         }
-      }
-   }
-
-   return font;
-
-error:
-   if (font)
-      bitmapfont_free_lut(font);
-
-   return NULL;
-}
-
-void bitmapfont_free_lut(bitmapfont_lut_t *font)
-{
-   if (!font)
-      return;
-
-   if (font->lut)
-   {
-      size_t num_glyphs = (font->glyph_max - font->glyph_min) + 1;
-      size_t i;
-
-      for (i = 0; i < num_glyphs; i++)
-      {
-         if (font->lut[i])
-            free(font->lut[i]);
-         font->lut[i] = NULL;
-      }
-
-      free(font->lut);
-   }
-
-   free(font);
-}
-
-static struct font_atlas *font_renderer_bmp_get_atlas(void *data)
-{
-   bm_renderer_t *handle = (bm_renderer_t*)data;
-   if (!handle)
-      return NULL;
-   return &handle->atlas;
-}
-
-static const struct font_glyph *font_renderer_bmp_get_glyph(
-      void *data, uint32_t code)
-{
-   bm_renderer_t *handle = (bm_renderer_t*)data;
-   if (!handle)
-      return NULL;
-   return code < BMP_ATLAS_SIZE ? &handle->glyphs[code] : NULL;
-}
-
-static void char_to_texture(bm_renderer_t *handle, uint8_t letter,
-      unsigned atlas_x, unsigned atlas_y)
-{
-   unsigned y, x;
-   uint8_t *target = handle->atlas.buffer + atlas_x +
-      atlas_y * handle->atlas.width;
-
-   for (y = 0; y < FONT_HEIGHT; y++)
-   {
-      for (x = 0; x < FONT_WIDTH; x++)
-      {
-         unsigned xo, yo;
-         unsigned font_pixel = x + y * FONT_WIDTH;
-         uint8_t rem         = 1 << (font_pixel & 7);
-         unsigned offset     = font_pixel >> 3;
-         uint8_t col         = (bitmap_bin[FONT_OFFSET(letter) + offset] & rem) ? 0xff : 0;
-         uint8_t *dst        = target;
-
-         dst                += x * handle->scale_factor;
-         dst                += y * handle->scale_factor * handle->atlas.width;
-
-         for (yo = 0; yo < handle->scale_factor; yo++)
-            for (xo = 0; xo < handle->scale_factor; xo++)
-               dst[xo + yo * handle->atlas.width] = col;
-      }
-   }
-   handle->atlas.dirty = true;
-}
-
-static void *font_renderer_bmp_init(const char *font_path, float font_size)
-{
-   unsigned i;
-   bm_renderer_t *handle = (bm_renderer_t*)calloc(1, sizeof(*handle));
-
-   if (!handle)
-      return NULL;
-
-   (void)font_path;
-
-   handle->scale_factor    = (unsigned)roundf(font_size / FONT_HEIGHT);
-   if (!handle->scale_factor)
-      handle->scale_factor = 1;
-
-   handle->atlas.width  = (BMP_ATLAS_PADDING + (FONT_WIDTH  * handle->scale_factor)) * BMP_ATLAS_COLS;
-   handle->atlas.height = (BMP_ATLAS_PADDING + (FONT_HEIGHT * handle->scale_factor)) * BMP_ATLAS_ROWS;
-   handle->atlas.buffer = (uint8_t*)calloc(handle->atlas.width * handle->atlas.height, 1);
-
-   for (i = 0; i < BMP_ATLAS_SIZE; i++)
-   {
-      unsigned x                       = (i % BMP_ATLAS_COLS) *
-         (BMP_ATLAS_PADDING + (handle->scale_factor * FONT_WIDTH));
-      unsigned y                       = (i / BMP_ATLAS_COLS) *
-         (BMP_ATLAS_PADDING + (handle->scale_factor * FONT_HEIGHT));
-
-      char_to_texture(handle, i, x, y);
-
-      handle->glyphs[i].width          = FONT_WIDTH * handle->scale_factor;
-      handle->glyphs[i].height         = FONT_HEIGHT * handle->scale_factor;
-      handle->glyphs[i].atlas_offset_x = x;
-      handle->glyphs[i].atlas_offset_y = y;
-      handle->glyphs[i].draw_offset_x  = 0;
-      handle->glyphs[i].draw_offset_y  = -FONT_HEIGHT_BASELINE_OFFSET * handle->scale_factor;
-      handle->glyphs[i].advance_x      = FONT_WIDTH_STRIDE * handle->scale_factor;
-      handle->glyphs[i].advance_y      = 0;
-   }
-
-   handle->line_metrics.ascender       = (float)FONT_HEIGHT_BASELINE_OFFSET * handle->scale_factor;
-   handle->line_metrics.descender      = (float)(FONT_HEIGHT - FONT_HEIGHT_BASELINE_OFFSET) * handle->scale_factor;
-   handle->line_metrics.height         = (float)FONT_HEIGHT_STRIDE * handle->scale_factor;
-
-   return handle;
-}
-
-static void font_renderer_bmp_free(void *data)
-{
-   bm_renderer_t *handle = (bm_renderer_t*)data;
-   if (!handle)
-      return;
-   free(handle->atlas.buffer);
-   free(handle);
-}
-
-static const char *font_renderer_bmp_get_default_font(void)
-{
-   return "";
-}
-
-static bool font_renderer_bmp_get_line_metrics(
-      void* data, struct font_line_metrics **metrics)
-{
-   bm_renderer_t *handle = (bm_renderer_t*)data;
-
-   if (!handle)
-      return false;
-
-   *metrics = &handle->line_metrics;
-   return true;
-}
-
-font_renderer_driver_t bitmap_font_renderer = {
-   font_renderer_bmp_init,
-   font_renderer_bmp_get_atlas,
-   font_renderer_bmp_get_glyph,
-   font_renderer_bmp_free,
-   font_renderer_bmp_get_default_font,
-   "font_renderer_bmp",
-   font_renderer_bmp_get_line_metrics
-};

+ 0 - 253
app/src/main/cpp/gfx/drivers_font_renderer/stb.c

@@ -1,253 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2015-2014 - Hans-Kristian Arntzen
- *  Copyright (C) 2011-2017 - Daniel De Matteis
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <ctype.h>
-
-#include <file/file_path.h>
-#include <streams/file_stream.h>
-#include <retro_miscellaneous.h>
-#include <string/stdstring.h>
-
-#include "../font_driver.h"
-
-#ifndef STB_TRUETYPE_IMPLEMENTATION
-#define STB_TRUETYPE_IMPLEMENTATION
-#define STB_RECT_PACK_IMPLEMENTATION
-#define STBTT_STATIC
-#define STBRP_STATIC
-#define STATIC static INLINE
-#include "../../deps/stb/stb_rect_pack.h"
-#include "../../deps/stb/stb_truetype.h"
-#undef STATIC
-#endif
-
-typedef struct
-{
-   struct font_atlas atlas;               /* ptr   alignment */
-   struct font_glyph glyphs[256];         /* unsigned alignment */
-   struct font_line_metrics line_metrics; /* float alignment */
-} stb_font_renderer_t;
-
-static struct font_atlas *font_renderer_stb_get_atlas(void *data)
-{
-   stb_font_renderer_t *self = (stb_font_renderer_t*)data;
-   return &self->atlas;
-}
-
-static const struct font_glyph *font_renderer_stb_get_glyph(
-      void *data, uint32_t code)
-{
-   stb_font_renderer_t *self = (stb_font_renderer_t*)data;
-   return code < 256 ? &self->glyphs[code] : NULL;
-}
-
-static void font_renderer_stb_free(void *data)
-{
-   stb_font_renderer_t *self = (stb_font_renderer_t*)data;
-
-   free(self->atlas.buffer);
-   free(self);
-}
-
-static bool font_renderer_stb_create_atlas(stb_font_renderer_t *self,
-      uint8_t *font_data, float font_size, unsigned width, unsigned height)
-{
-   int i;
-   stbtt_packedchar   chardata[256];
-   stbtt_pack_context pc = {NULL};
-
-   if (width > 2048 || height > 2048)
-      goto error;
-
-   if (self->atlas.buffer)
-      free(self->atlas.buffer);
-
-   self->atlas.buffer = (uint8_t*)calloc(height, width);
-   self->atlas.width  = width;
-   self->atlas.height = height;
-
-   if (!self->atlas.buffer)
-      goto error;
-
-   /* Note: 1 pixel of padding is added to
-    * prevent texture bleed when drawing with
-    * linear filtering enabled */
-   stbtt_PackBegin(&pc, self->atlas.buffer,
-         self->atlas.width, self->atlas.height,
-         self->atlas.width, 1, NULL);
-
-   stbtt_PackFontRange(&pc, font_data, 0, font_size, 0, 256, chardata);
-   stbtt_PackEnd(&pc);
-
-   self->atlas.dirty = true;
-
-   for (i = 0; i < 256; ++i)
-   {
-      struct font_glyph *g = &self->glyphs[i];
-      stbtt_packedchar  *c = &chardata[i];
-
-      g->advance_x         = c->xadvance;
-      g->atlas_offset_x    = c->x0;
-      g->atlas_offset_y    = c->y0;
-      g->draw_offset_x     = c->xoff;
-      g->draw_offset_y     = c->yoff;
-      g->width             = c->x1 - c->x0;
-      g->height            = c->y1 - c->y0;
-
-      /* Make sure important characters fit */
-      if (ISALNUM(i) && (!g->width || !g->height))
-      {
-         int new_width  = width  * 1.2;
-         int new_height = height * 1.2;
-
-         /* Limit growth to 2048x2048 unless we already reached that */
-         if (width < 2048 || height < 2048)
-         {
-            new_width  = MIN(new_width,  2048);
-            new_height = MIN(new_height, 2048);
-         }
-
-         return font_renderer_stb_create_atlas(self, font_data, font_size,
-               new_width, new_height);
-      }
-   }
-
-   return true;
-
-error:
-   self->atlas.width = self->atlas.height = 0;
-
-   if (self->atlas.buffer)
-      free(self->atlas.buffer);
-
-   self->atlas.buffer = NULL;
-
-   return false;
-}
-
-static void *font_renderer_stb_init(const char *font_path, float font_size)
-{
-   int ascent, descent, line_gap;
-   float scale_factor;
-   stbtt_fontinfo info;
-   uint8_t *font_data = NULL;
-   stb_font_renderer_t *self = (stb_font_renderer_t*) calloc(1, sizeof(*self));
-
-   /* See https://github.com/nothings/stb/blob/master/stb_truetype.h#L539 */
-   font_size = STBTT_POINT_SIZE(font_size);
-
-   if (!self)
-      goto error;
-
-   if (!path_is_valid(font_path) || !filestream_read_file(font_path, (void**)&font_data, NULL))
-      goto error;
-
-   if (!font_renderer_stb_create_atlas(self, font_data, font_size, 512, 512))
-      goto error;
-
-   if (!stbtt_InitFont(&info, font_data, stbtt_GetFontOffsetForIndex(font_data, 0)))
-      goto error;
-
-   stbtt_GetFontVMetrics(&info, &ascent, &descent, &line_gap);
-
-   scale_factor = (font_size < 0) ?
-         stbtt_ScaleForMappingEmToPixels(&info, -font_size) :
-         stbtt_ScaleForPixelHeight(&info, font_size);
-
-   /* Ascender, descender and line_gap values always
-    * end up ~0.5 pixels too small when scaled...
-    * > Add a manual correction factor */
-   self->line_metrics.ascender  = 0.5f + (float)ascent * scale_factor;
-   self->line_metrics.descender = 0.5f + (float)(-descent) * scale_factor;
-   self->line_metrics.height    = 0.5f + (float)(ascent - descent + line_gap) * scale_factor;
-
-   free(font_data);
-
-   return self;
-
-error:
-   if (font_data)
-      free(font_data);
-
-   if (self)
-      font_renderer_stb_free(self);
-   return NULL;
-}
-
-static const char *font_renderer_stb_get_default_font(void)
-{
-   static const char *paths[] = {
-#if defined(_WIN32) && !defined(__WINRT__)
-      "C:\\Windows\\Fonts\\consola.ttf",
-      "C:\\Windows\\Fonts\\verdana.ttf",
-#elif defined(__APPLE__)
-      "/Library/Fonts/Microsoft/Candara.ttf",
-      "/Library/Fonts/Verdana.ttf",
-      "/Library/Fonts/Tahoma.ttf",
-      "/Library/Fonts/Andale Mono.ttf",
-      "/Library/Fonts/Courier New.ttf",
-#elif defined(__ANDROID_API__)
-      "/system/fonts/DroidSansMono.ttf",
-      "/system/fonts/CutiveMono.ttf",
-      "/system/fonts/DroidSans.ttf",
-#elif defined(VITA)
-      "vs0:data/external/font/pvf/c041056ts.ttf",
-      "vs0:data/external/font/pvf/d013013ds.ttf",
-      "vs0:data/external/font/pvf/e046323ms.ttf",
-      "vs0:data/external/font/pvf/e046323ts.ttf",
-      "vs0:data/external/font/pvf/k006004ds.ttf",
-      "vs0:data/external/font/pvf/n023055ms.ttf",
-      "vs0:data/external/font/pvf/n023055ts.ttf",
-#elif !defined(__WINRT__)
-      "/usr/share/fonts/TTF/DejaVuSansMono.ttf",
-      "/usr/share/fonts/TTF/DejaVuSans.ttf",
-      "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf",
-      "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf",
-      "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",
-      "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
-      "osd-font.ttf",
-#endif
-      NULL
-   };
-
-   const char **p;
-
-   for (p = paths; *p; ++p)
-      if (path_is_valid(*p))
-         return *p;
-
-   return NULL;
-}
-
-static bool font_renderer_stb_get_line_metrics(
-      void* data, struct font_line_metrics **metrics)
-{
-   stb_font_renderer_t *handle = (stb_font_renderer_t*)data;
-   if (!handle)
-      return false;
-   *metrics = &handle->line_metrics;
-   return true;
-}
-
-font_renderer_driver_t stb_font_renderer = {
-   font_renderer_stb_init,
-   font_renderer_stb_get_atlas,
-   font_renderer_stb_get_glyph,
-   font_renderer_stb_free,
-   font_renderer_stb_get_default_font,
-   "font_renderer_stb",
-   font_renderer_stb_get_line_metrics
-};

+ 0 - 362
app/src/main/cpp/gfx/drivers_font_renderer/stb_unicode.c

@@ -1,362 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2015-2014 - Hans-Kristian Arntzen
- *  Copyright (C) 2011-2017 - Daniel De Matteis
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <ctype.h>
-
-#include <file/file_path.h>
-#include <streams/file_stream.h>
-#include <string/stdstring.h>
-#include <retro_miscellaneous.h>
-
-#ifdef WIIU
-#include <wiiu/os.h>
-#endif
-
-#include "../font_driver.h"
-
-#ifndef STB_TRUETYPE_IMPLEMENTATION
-#define STB_TRUETYPE_IMPLEMENTATION
-#define STB_RECT_PACK_IMPLEMENTATION
-#define STBTT_STATIC
-#define STBRP_STATIC
-#define STATIC static INLINE
-#include "../../deps/stb/stb_rect_pack.h"
-#include "../../deps/stb/stb_truetype.h"
-#undef STATIC
-#endif
-
-#define STB_UNICODE_ATLAS_ROWS 16
-#define STB_UNICODE_ATLAS_COLS 16
-#define STB_UNICODE_ATLAS_SIZE (STB_UNICODE_ATLAS_ROWS * STB_UNICODE_ATLAS_COLS)
-/* Padding is required between each glyph in
- * the atlas to prevent texture bleed when
- * drawing with linear filtering enabled */
-#define STB_UNICODE_ATLAS_PADDING 1
-
-typedef struct stb_unicode_atlas_slot
-{
-   struct stb_unicode_atlas_slot* next;
-   struct font_glyph glyph;      /* unsigned alignment */
-   unsigned charcode;
-   unsigned last_used;
-}stb_unicode_atlas_slot_t;
-
-typedef struct
-{
-   uint8_t *font_data;
-   struct font_atlas atlas;               /* ptr alignment */
-   stb_unicode_atlas_slot_t* uc_map[0x100];
-   stb_unicode_atlas_slot_t atlas_slots[STB_UNICODE_ATLAS_SIZE];
-   stbtt_fontinfo info;                   /* ptr alignment */
-   int max_glyph_width;
-   int max_glyph_height;
-   unsigned usage_counter;
-   float scale_factor;
-   struct font_line_metrics line_metrics; /* float alignment */
-} stb_unicode_font_renderer_t;
-
-static struct font_atlas *font_renderer_stb_unicode_get_atlas(void *data)
-{
-   stb_unicode_font_renderer_t *self = (stb_unicode_font_renderer_t*)data;
-   return &self->atlas;
-}
-
-static void font_renderer_stb_unicode_free(void *data)
-{
-   stb_unicode_font_renderer_t *self = (stb_unicode_font_renderer_t*)data;
-
-   free(self->atlas.buffer);
-   free(self->font_data);
-   free(self);
-}
-
-static stb_unicode_atlas_slot_t* font_renderer_stb_unicode_get_slot(stb_unicode_font_renderer_t *handle)
-{
-   int i, map_id;
-   unsigned oldest = 0;
-
-   for (i = 1; i < STB_UNICODE_ATLAS_SIZE; i++)
-      if ((handle->usage_counter - handle->atlas_slots[i].last_used) >
-         (handle->usage_counter - handle->atlas_slots[oldest].last_used))
-         oldest = i;
-
-   /* remove from map */
-   map_id = handle->atlas_slots[oldest].charcode & 0xFF;
-   if (handle->uc_map[map_id] == &handle->atlas_slots[oldest])
-      handle->uc_map[map_id] = handle->atlas_slots[oldest].next;
-   else if (handle->uc_map[map_id])
-   {
-      stb_unicode_atlas_slot_t* ptr = handle->uc_map[map_id];
-      while (ptr->next && ptr->next != &handle->atlas_slots[oldest])
-         ptr = ptr->next;
-      ptr->next = handle->atlas_slots[oldest].next;
-   }
-
-   return &handle->atlas_slots[oldest];
-}
-
-static const struct font_glyph *font_renderer_stb_unicode_get_glyph(
-      void *data, uint32_t charcode)
-{
-   int glyph_index                      = 0;
-   int x0                               = 0;
-   int y1                               = 0;
-   int advance_width                    = 0;
-   int left_side_bearing                = 0;
-   unsigned map_id                      = 0;
-   uint8_t *dst                         = NULL;
-   stb_unicode_atlas_slot_t* atlas_slot = NULL;
-   stb_unicode_font_renderer_t *self    = (stb_unicode_font_renderer_t*)data;
-   float glyph_advance_x                = 0.0f;
-   float glyph_draw_offset_y            = 0.0f;
-
-   if (!self)
-      return NULL;
-
-   map_id                               = charcode & 0xFF;
-   atlas_slot                           = self->uc_map[map_id];
-
-   while (atlas_slot)
-   {
-      if (atlas_slot->charcode == charcode)
-      {
-         atlas_slot->last_used = self->usage_counter++;
-         return &atlas_slot->glyph;
-      }
-      atlas_slot = atlas_slot->next;
-   }
-
-   atlas_slot             = font_renderer_stb_unicode_get_slot(self);
-   atlas_slot->charcode   = charcode;
-   atlas_slot->next       = self->uc_map[map_id];
-   self->uc_map[map_id]   = atlas_slot;
-
-   glyph_index            = stbtt_FindGlyphIndex(&self->info, charcode);
-
-   dst = (uint8_t*)self->atlas.buffer + atlas_slot->glyph.atlas_offset_x
-         + atlas_slot->glyph.atlas_offset_y * self->atlas.width;
-
-   stbtt_GetGlyphHMetrics(&self->info, glyph_index, &advance_width, &left_side_bearing);
-   if (stbtt_GetGlyphBox(&self->info, glyph_index, &x0, NULL, NULL, &y1))
-   {
-      stbtt_MakeGlyphBitmap(&self->info, dst, self->max_glyph_width, self->max_glyph_height,
-            self->atlas.width, self->scale_factor, self->scale_factor, glyph_index);
-   }
-   else
-   {
-      /* This means the glyph is empty. In this case, stbtt_MakeGlyphBitmap()
-       * fills the corresponding region of the atlas buffer with garbage,
-       * so just zero it */
-      int x, y;
-      for (x = 0; x < self->max_glyph_width; x++)
-         for (y = 0; y < self->max_glyph_height; y++)
-            dst[x + (y * self->atlas.width)] = 0;
-   }
-
-   atlas_slot->glyph.width          = self->max_glyph_width;
-   atlas_slot->glyph.height         = self->max_glyph_height;
-   /* advance_x must always be rounded to the
-    * *nearest* integer */
-   glyph_advance_x = (float)advance_width * self->scale_factor;
-   atlas_slot->glyph.advance_x      = (int)((glyph_advance_x > 0.0f) ?
-         (glyph_advance_x + 0.5f) : (glyph_advance_x - 0.5f));
-   /* advance_y is always zero */
-   atlas_slot->glyph.advance_y      = 0;
-   /* draw_offset_x must always be rounded *down*
-    * to the nearest integer */
-   atlas_slot->glyph.draw_offset_x  = (int)((float)x0 * self->scale_factor);
-   /* draw_offset_y must always be rounded *up*
-    * to the nearest integer */
-   glyph_draw_offset_y = (float)(-y1) * self->scale_factor;
-   atlas_slot->glyph.draw_offset_y  = (int)((glyph_draw_offset_y < 0.0f) ?
-         floor((double)glyph_draw_offset_y) : ceil((double)glyph_draw_offset_y));
-
-   self->atlas.dirty = true;
-   atlas_slot->last_used = self->usage_counter++;
-   return &atlas_slot->glyph;
-}
-
-static bool font_renderer_stb_unicode_create_atlas(
-      stb_unicode_font_renderer_t *self, float font_size)
-{
-   unsigned i, x, y;
-   stb_unicode_atlas_slot_t* slot = NULL;
-
-   self->max_glyph_width  = font_size < 0 ? -font_size : font_size;
-   self->max_glyph_height = font_size < 0 ? -font_size : font_size;
-
-   self->atlas.width      = (self->max_glyph_width  + STB_UNICODE_ATLAS_PADDING) * STB_UNICODE_ATLAS_COLS;
-   self->atlas.height     = (self->max_glyph_height + STB_UNICODE_ATLAS_PADDING) * STB_UNICODE_ATLAS_ROWS;
-
-   self->atlas.buffer     = (uint8_t*)
-      calloc(self->atlas.width * self->atlas.height, sizeof(uint8_t));
-
-   if (!self->atlas.buffer)
-      return false;
-
-   slot = self->atlas_slots;
-
-   for (y = 0; y < STB_UNICODE_ATLAS_ROWS; y++)
-   {
-      for (x = 0; x < STB_UNICODE_ATLAS_COLS; x++)
-      {
-         slot->glyph.atlas_offset_x = x * (self->max_glyph_width  + STB_UNICODE_ATLAS_PADDING);
-         slot->glyph.atlas_offset_y = y * (self->max_glyph_height + STB_UNICODE_ATLAS_PADDING);
-         slot++;
-      }
-   }
-
-   for (i = 0; i < 256; i++)
-      font_renderer_stb_unicode_get_glyph(self, i);
-
-   for (i = 0; i < 256; i++)
-   {
-      if (ISALNUM(i))
-         font_renderer_stb_unicode_get_glyph(self, i);
-   }
-
-   return true;
-}
-
-static void *font_renderer_stb_unicode_init(const char *font_path, float font_size)
-{
-   int ascent, descent, line_gap;
-   stb_unicode_font_renderer_t *self =
-      (stb_unicode_font_renderer_t*)calloc(1, sizeof(*self));
-
-   if (!self || font_size < 1.0)
-      goto error;
-
-   /* See https://github.com/nothings/stb/blob/master/stb_truetype.h#L539 */
-   font_size = STBTT_POINT_SIZE(font_size);
-
-#ifdef WIIU
-   if (!*font_path)
-   {
-      uint32_t size = 0;
-      if (!OSGetSharedData(SHARED_FONT_DEFAULT, 0, (void**)&self->font_data, &size))
-         goto error;
-   }
-   else
-#endif
-   if (!path_is_valid(font_path) || !filestream_read_file(font_path, (void**)&self->font_data, NULL))
-      goto error;
-
-   if (!stbtt_InitFont(&self->info, self->font_data,
-            stbtt_GetFontOffsetForIndex(self->font_data, 0)))
-      goto error;
-
-   stbtt_GetFontVMetrics(&self->info, &ascent, &descent, &line_gap);
-
-   if (font_size < 0)
-      self->scale_factor = stbtt_ScaleForMappingEmToPixels(&self->info, -font_size);
-   else
-      self->scale_factor = stbtt_ScaleForPixelHeight(&self->info, font_size);
-
-   /* Ascender, descender and line_gap values always
-    * end up ~0.5 pixels too small when scaled...
-    * > Add a manual correction factor */
-   self->line_metrics.ascender  = 0.5f + (float)ascent * self->scale_factor;
-   self->line_metrics.descender = 0.5f + ((float)(-descent) * self->scale_factor);
-   self->line_metrics.height    = 0.5f + (float)(ascent - descent + line_gap) * self->scale_factor;
-
-   if (!font_renderer_stb_unicode_create_atlas(self, font_size))
-      goto error;
-
-   return self;
-
-error:
-   if (self)
-      font_renderer_stb_unicode_free(self);
-   return NULL;
-}
-
-static const char *font_renderer_stb_unicode_get_default_font(void)
-{
-#ifdef WIIU
-   return "";
-#else
-   static const char *paths[] = {
-#if defined(_WIN32) && !defined(__WINRT__)
-      "C:\\Windows\\Fonts\\consola.ttf",
-      "C:\\Windows\\Fonts\\verdana.ttf",
-#elif defined(__APPLE__)
-      "/Library/Fonts/Microsoft/Candara.ttf",
-      "/Library/Fonts/Verdana.ttf",
-      "/Library/Fonts/Tahoma.ttf",
-      "/Library/Fonts/Andale Mono.ttf",
-      "/Library/Fonts/Courier New.ttf",
-#elif defined(__ANDROID_API__)
-      "/system/fonts/DroidSansMono.ttf",
-      "/system/fonts/CutiveMono.ttf",
-      "/system/fonts/DroidSans.ttf",
-#elif defined(VITA)
-      "vs0:data/external/font/pvf/c041056ts.ttf",
-      "vs0:data/external/font/pvf/d013013ds.ttf",
-      "vs0:data/external/font/pvf/e046323ms.ttf",
-      "vs0:data/external/font/pvf/e046323ts.ttf",
-      "vs0:data/external/font/pvf/k006004ds.ttf",
-      "vs0:data/external/font/pvf/n023055ms.ttf",
-      "vs0:data/external/font/pvf/n023055ts.ttf",
-#elif defined(ORBIS)
-      "/preinst/common/font/c041056ts.ttf",
-      "/preinst/common/font/d013013ds.ttf",
-      "/preinst/common/font/e046323ms.ttf",
-      "/preinst/common/font/e046323ts.ttf",
-      "/preinst/common/font/k006004ds.ttf",
-      "/preinst/common/font/n023055ms.ttf",
-      "/preinst/common/font/n023055ts.ttf",
-#elif !defined(__WINRT__)
-      "/usr/share/fonts/TTF/DejaVuSansMono.ttf",
-      "/usr/share/fonts/TTF/DejaVuSans.ttf",
-      "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf",
-      "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf",
-      "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",
-      "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
-      "osd-font.ttf",
-#endif
-      NULL
-   };
-
-   const char **p;
-
-   for (p = paths; *p; ++p)
-      if (path_is_valid(*p))
-         return *p;
-
-   return NULL;
-#endif
-}
-
-static bool font_renderer_stb_unicode_get_line_metrics(
-      void* data, struct font_line_metrics **metrics)
-{
-   stb_unicode_font_renderer_t *handle = (stb_unicode_font_renderer_t*)data;
-   if (!handle)
-      return false;
-   *metrics = &handle->line_metrics;
-   return true;
-}
-
-font_renderer_driver_t stb_unicode_font_renderer = {
-   font_renderer_stb_unicode_init,
-   font_renderer_stb_unicode_get_atlas,
-   font_renderer_stb_unicode_get_glyph,
-   font_renderer_stb_unicode_free,
-   font_renderer_stb_unicode_get_default_font,
-   "font_renderer_stb_unicode",
-   font_renderer_stb_unicode_get_line_metrics
-};

+ 1 - 0
app/src/main/cpp/gfx/drivers_shader/shader_vulkan.cpp

@@ -33,6 +33,7 @@
 #include "slang_reflection.h"
 #include "slang_reflection.hpp"
 
+#include "../video_driver.h"
 #include "../common/vulkan_common.h"
 #include "../../retroarch.h"
 #include "../../verbosity.h"

+ 0 - 946
app/src/main/cpp/gfx/font_driver.c

@@ -1,946 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- *  Copyright (C) 2011-2017 - Daniel De Matteis
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <math.h>
-
-#ifdef HAVE_CONFIG_H
-#include "../config.h"
-#endif
-
-#include "font_driver.h"
-#include "video_thread_wrapper.h"
-
-/* TODO/FIXME - global */
-static void *video_font_driver = NULL;
-
-int font_renderer_create_default(
-      const font_renderer_driver_t **drv,
-      void **handle, const char *font_path, unsigned font_size)
-{
-   static const font_renderer_driver_t *font_backends[] = {
-#ifdef HAVE_FREETYPE
-      &freetype_font_renderer,
-#endif
-#if defined(__APPLE__) && defined(HAVE_CORETEXT)
-      &coretext_font_renderer,
-#endif
-#ifdef HAVE_STB_FONT
-#if defined(VITA) || defined(ORBIS) || defined(WIIU) || defined(ANDROID) || (defined(_WIN32) && !defined(_XBOX) && !defined(_MSC_VER) && _MSC_VER >= 1400) || (defined(_WIN32) && !defined(_XBOX) && defined(_MSC_VER)) || defined(HAVE_LIBNX) || defined(__linux__) || defined (HAVE_EMSCRIPTEN) || defined(__APPLE__) || defined(HAVE_ODROIDGO2) || defined(__PS3__)
-      &stb_unicode_font_renderer,
-#else
-      &stb_unicode_font_renderer,
-#endif
-#endif
-      &bitmap_font_renderer,
-      NULL
-   };
-   unsigned i;
-
-   for (i = 0; font_backends[i]; i++)
-   {
-      const char *path = font_path;
-
-      if (!path)
-         path = font_backends[i]->get_default_font();
-      if (!path)
-         continue;
-
-      *handle = font_backends[i]->init(path, font_size);
-      if (*handle)
-      {
-         *drv = font_backends[i];
-         return 1;
-      }
-   }
-
-   *drv    = NULL;
-   *handle = NULL;
-
-   return 0;
-}
-
-static bool font_init_first(
-      const void **font_driver, void **font_handle,
-      void *video_data, const char *font_path, float font_size,
-      enum font_driver_render_api api, bool is_threaded)
-{
-   if (font_path && !font_path[0])
-      font_path = NULL;
-
-   switch (api)
-   {
-#ifdef HAVE_OPENGL1
-      case FONT_DRIVER_RENDER_OPENGL1_API:
-         {
-            void *data = gl1_raster_font.init(
-                  video_data, font_path, font_size, is_threaded);
-            if (data)
-            {
-               *font_driver = &gl1_raster_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef HAVE_OPENGL
-      case FONT_DRIVER_RENDER_OPENGL_API:
-         {
-            void *data = gl2_raster_font.init(
-                  video_data, font_path, font_size, is_threaded);
-            if (data)
-            {
-               *font_driver = &gl2_raster_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef HAVE_OPENGL_CORE
-      case FONT_DRIVER_RENDER_OPENGL_CORE_API:
-         {
-            void *data = gl3_raster_font.init(
-                  video_data, font_path, font_size, is_threaded);
-            if (data)
-            {
-               *font_driver = &gl3_raster_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef HAVE_VULKAN
-      case FONT_DRIVER_RENDER_VULKAN_API:
-         {
-            void *data = vulkan_raster_font.init(video_data,
-                  font_path, font_size, is_threaded);
-            if (data)
-            {
-               *font_driver = &vulkan_raster_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef HAVE_METAL
-   case FONT_DRIVER_RENDER_METAL_API:
-         {
-            void *data = metal_raster_font.init(video_data,
-                  font_path, font_size, is_threaded);
-            if (data)
-            {
-               *font_driver = &metal_raster_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-#endif
-#ifdef HAVE_D3D8
-      case FONT_DRIVER_RENDER_D3D8_API:
-      {
-         static const font_renderer_t *d3d8_font_backends[] = {
-#if defined(_XBOX1)
-            &d3d_xdk1_font,
-#endif
-            NULL
-         };
-         unsigned i;
-
-         for (i = 0; i < ARRAY_SIZE(d3d8_font_backends); i++)
-         {
-            void *data = d3d8_font_backends[i] ? d3d8_font_backends[i]->init(
-                  video_data, font_path, font_size, is_threaded) : NULL;
-            if (data)
-            {
-               *font_driver = d3d8_font_backends[i];
-               *font_handle = data;
-
-               return true;
-            }
-         }
-      }
-      break;
-#endif
-#ifdef HAVE_D3D9
-      case FONT_DRIVER_RENDER_D3D9_API:
-      {
-         static const font_renderer_t *d3d9_font_backends[] = {
-#if defined(_WIN32) && defined(HAVE_D3DX)
-            &d3d_win32_font,
-#endif
-            NULL
-         };
-         unsigned i;
-
-         for (i = 0; i < ARRAY_SIZE(d3d9_font_backends); i++)
-         {
-            void *data = d3d9_font_backends[i] ? d3d9_font_backends[i]->init(
-                  video_data, font_path, font_size, is_threaded) : NULL;
-            if (data)
-            {
-               *font_driver = d3d9_font_backends[i];
-               *font_handle = data;
-
-               return true;
-            }
-         }
-      }
-      break;
-#endif
-#ifdef HAVE_D3D10
-      case FONT_DRIVER_RENDER_D3D10_API:
-         {
-            void *data = d3d10_font.init(video_data,
-                  font_path, font_size, is_threaded);
-            if (data)
-            {
-               *font_driver = &d3d10_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef HAVE_D3D11
-      case FONT_DRIVER_RENDER_D3D11_API:
-         {
-            void *data = d3d11_font.init(video_data,
-                  font_path, font_size, is_threaded);
-            if (data)
-            {
-               *font_driver = &d3d11_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef HAVE_D3D12
-      case FONT_DRIVER_RENDER_D3D12_API:
-         {
-            void *data = d3d12_font.init(video_data,
-                  font_path, font_size, is_threaded);
-            if (data)
-            {
-               *font_driver = &d3d12_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef HAVE_VITA2D
-      case FONT_DRIVER_RENDER_VITA2D:
-         {
-            void *data = vita2d_vita_font.init(
-                  video_data, font_path, font_size, is_threaded);
-            if (data)
-            {
-               *font_driver = &vita2d_vita_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef PS2
-      case FONT_DRIVER_RENDER_PS2:
-         {
-            void *data = ps2_font.init(
-                  video_data, font_path, font_size,
-                  is_threaded);
-            if (data)
-            {
-               *font_driver = &ps2_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef _3DS
-      case FONT_DRIVER_RENDER_CTR:
-         {
-            void *data = ctr_font.init(
-                  video_data, font_path, font_size,
-                  is_threaded);
-            if (data)
-            {
-               *font_driver = &ctr_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef WIIU
-      case FONT_DRIVER_RENDER_WIIU:
-         {
-            void *data = wiiu_font.init(
-                  video_data, font_path, font_size, is_threaded);
-            if (data)
-            {
-               *font_driver = &wiiu_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef HAVE_CACA
-      case FONT_DRIVER_RENDER_CACA:
-         {
-            void *data = caca_font.init(
-                  video_data, font_path, font_size,
-                  is_threaded);
-            if (data)
-            {
-               *font_driver = &caca_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef HAVE_SIXEL
-      case FONT_DRIVER_RENDER_SIXEL:
-         {
-            void *data = sixel_font.init(
-                  video_data, font_path, font_size,
-                  is_threaded);
-            if (data)
-            {
-               *font_driver = &sixel_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef HAVE_LIBNX
-      case FONT_DRIVER_RENDER_SWITCH:
-         {
-            void *data = switch_font.init(
-                  video_data, font_path, font_size,
-                  is_threaded);
-            if (data)
-            {
-               *font_driver = &switch_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef HAVE_GCM
-      case FONT_DRIVER_RENDER_RSX:
-         {
-            void *data = rsx_font.init(
-                  video_data, font_path, font_size,
-                  is_threaded);
-            if (data)
-            {
-               *font_driver = &rsx_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#ifdef HAVE_GDI
-#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
-      case FONT_DRIVER_RENDER_GDI:
-         {
-            void *data = gdi_font.init(
-                  video_data, font_path, font_size,
-                  is_threaded);
-            if (data)
-            {
-               *font_driver = &gdi_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-#endif
-#ifdef DJGPP
-      case FONT_DRIVER_RENDER_VGA:
-         {
-            void *data = vga_font.init(
-                  video_data, font_path, font_size,
-                  is_threaded);
-            if (data)
-            {
-               *font_driver = &vga_font;
-               *font_handle = data;
-               return true;
-            }
-         }
-         break;
-#endif
-      case FONT_DRIVER_RENDER_DONT_CARE:
-         /* TODO/FIXME - lookup graphics driver's 'API' */
-         break;
-      default:
-         break;
-   }
-
-   return false;
-}
-
-#ifdef HAVE_LANGEXTRA
-/* ASCII:       0xxxxxxx  (c & 0x80) == 0x00
- * other start: 11xxxxxx  (c & 0xC0) == 0xC0
- * other cont:  10xxxxxx  (c & 0xC0) == 0x80
- * Neutral:
- * 0020 - 002F: 001xxxxx (c & 0xE0) == 0x20
- * misc. white space:
- * 2000 - 200D: 11100010 10000000 1000xxxx (c[2] < 0x8E) (3 bytes)
- * Hebrew:
- * 0591 - 05F4: 1101011x (c & 0xFE) == 0xD6 (2 bytes)
- * Arabic:
- * 0600 - 06FF: 110110xx (c & 0xFC) == 0xD8 (2 bytes)
- */
-
-/* clang-format off */
-#define IS_ASCII(p)        ((*(p)&0x80) == 0x00)
-#define IS_MBSTART(p)      ((*(p)&0xC0) == 0xC0)
-#define IS_MBCONT(p)       ((*(p)&0xC0) == 0x80)
-#define IS_DIR_NEUTRAL(p)  ((*(p)&0xE0) == 0x20)
-#define IS_HEBREW(p)       ((*(p)&0xFE) == 0xD6)
-#define IS_ARABIC(p)       ((*(p)&0xFC) == 0xD8)
-#define IS_RTL(p)          (IS_HEBREW(p) || IS_ARABIC(p))
-#define GET_ID_ARABIC(p)   (((unsigned char)(p)[0] << 6) | ((unsigned char)(p)[1] & 0x3F))
-
-
-/* Checks for miscellaneous whitespace characters in the range U+2000 to U+200D */
-static INLINE unsigned is_misc_ws(const unsigned char* src)
-{
-   unsigned res = 0;
-   if (*(src) == 0xE2) /* first byte */
-   {
-      src++;
-      if (*(src) == 0x80) /* second byte */
-      {
-         src++;
-         res = (*(src) < 0x8E); /* third byte */
-      }
-   }
-   return res;
-}
-
-static INLINE unsigned font_get_arabic_replacement(
-      const char* src, const char* start)
-{
-   /* 0x0620 to 0x064F */
-   static const unsigned arabic_shape_map[0x100][0x4] = {
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0600 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0610 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-      { 0 },                               /* 0x0620 */
-      { 0xFE80 },
-      { 0xFE81, 0xFE82 },
-      { 0xFE83, 0xFE84 },
-      { 0xFE85, 0xFE86 },
-      { 0xFE87, 0xFE88 },
-      { 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C },
-      { 0xFE8D, 0xFE8E },
-
-      { 0xFE8F, 0xFE90, 0xFE91, 0xFE92 },
-      { 0xFE93, 0xFE94 },
-      { 0xFE95, 0xFE96, 0xFE97, 0xFE98 },
-      { 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C },
-      { 0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0 },
-      { 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4 },
-      { 0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8 },
-      { 0xFEA9, 0xFEAA },
-
-      { 0xFEAB, 0xFEAC },                  /* 0x0630 */
-      { 0xFEAD, 0xFEAE },
-      { 0xFEAF, 0xFEB0 },
-      { 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4 },
-      { 0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8 },
-      { 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC },
-      { 0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0 },
-      { 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4 },
-
-      { 0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8 },
-      { 0xFEC9, 0xFECA, 0xFECB, 0xFECC },
-      { 0xFECD, 0xFECE, 0xFECF, 0xFED0 },
-      { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-      { 0 },                               /* 0x0640 */
-      { 0xFED1, 0xFED2, 0xFED3, 0xFED4 },
-      { 0xFED5, 0xFED6, 0xFED7, 0xFED8 },
-      { 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC },
-      { 0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0 },
-      { 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4 },
-      { 0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8 },
-      { 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC },
-
-      { 0xFEED, 0xFEEE },
-      { 0xFEEF, 0xFEF0, 0xFBE8, 0xFBE9 },
-      { 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4 },
-      { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0650 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0660 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0670 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-      { 0 }, { 0 },
-      { 0xFB56, 0xFB57, 0xFB58, 0xFB59 },
-      { 0 },
-
-
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0680 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0690 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x06A0 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-      { 0 },
-      { 0xFB8E, 0xFB8F, 0xFB90, 0xFB91 },
-      { 0 }, { 0 },
-
-      { 0 }, { 0 }, { 0 },
-      { 0xFB92, 0xFB93, 0xFB94, 0xFB95 },
-
-
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x06B0 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x06C0 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-      { 0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF },
-      { 0 }, { 0 }, { 0 },
-
-
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x06D0 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x06E0 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-
-
-      { 0 }, { 0 }, { 0 }, { 0 },          /* 0x06F0 */
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-      { 0 }, { 0 }, { 0 }, { 0 },
-   };
-   unsigned      result         = 0;
-   bool          prev_connected = false;
-   bool          next_connected = false;
-   unsigned char id             = GET_ID_ARABIC(src);
-   const char*   prev           = src - 2;
-   const char*   next           = src + 2;
-
-   if (IS_ARABIC(prev) && (prev >= start))
-   {
-      unsigned char prev_id = GET_ID_ARABIC(prev);
-
-      /* nonspacing diacritics 0x4b -- 0x5f */
-      while (prev_id > 0x4A && prev_id < 0x60)
-      {
-         prev -= 2;
-         if ((prev >= start) && IS_ARABIC(prev))
-            prev_id = GET_ID_ARABIC(prev);
-         else
-            break;
-      }
-
-      if (prev_id == 0x44) /* Arabic Letter Lam */
-      {
-         unsigned char prev2_id = 0;
-         const char*   prev2    = prev - 2;
-
-         if (prev2 >= start)
-            prev2_id            = (prev2[0] << 6) | (prev2[1] & 0x3F);
-
-         /* nonspacing diacritics 0x4b -- 0x5f */
-         while (prev2_id > 0x4A && prev2_id < 0x60)
-         {
-            prev2 -= 2;
-            if ((prev2 >= start) && IS_ARABIC(prev2))
-               prev2_id = GET_ID_ARABIC(prev2);
-            else
-               break;
-         }
-
-         prev_connected = !!arabic_shape_map[prev2_id][2];
-
-         switch (id)
-         {
-            case 0x22: /* Arabic Letter Alef with Madda Above */
-               return 0xFEF5 + prev_connected;
-            case 0x23: /* Arabic Letter Alef with Hamza Above */
-               return 0xFEF7 + prev_connected;
-            case 0x25: /* Arabic Letter Alef with Hamza Below */
-               return 0xFEF9 + prev_connected;
-            case 0x27: /* Arabic Letter Alef */
-               return 0xFEFB + prev_connected;
-         }
-      }
-      prev_connected = !!arabic_shape_map[prev_id][2];
-   }
-
-   if (IS_ARABIC(next))
-   {
-      unsigned char next_id = GET_ID_ARABIC(next);
-
-      /* nonspacing diacritics 0x4b -- 0x5f */
-      while (next_id > 0x4A && next_id < 0x60)
-      {
-         next += 2;
-         if (!IS_ARABIC(next))
-            break;
-         next_id = GET_ID_ARABIC(next);
-      }
-
-      next_connected = !!arabic_shape_map[next_id][1];
-   }
-
-   if ((result = 
-            arabic_shape_map[id][prev_connected | (next_connected <<
-               1)]))
-      return result;
-   return arabic_shape_map[id][prev_connected];
-}
-/* clang-format on */
-
-static char* font_driver_reshape_msg(const char* msg, unsigned char *buffer, size_t buffer_size)
-{
-   const unsigned char *src        = (const unsigned char*)msg;
-   bool                 reverse    = false;
-   size_t              msg_size    = (strlen(msg) * 2) + 1;
-   /* Fallback to heap allocated buffer if the buffer is too small */
-   /* worst case transformations are 2 bytes to 4 bytes -- aliaspider */
-   unsigned char*       dst_buffer = (buffer_size < msg_size) 
-                                   ? (unsigned char*)malloc(msg_size)
-                                   : buffer;
-   unsigned char *dst              = (unsigned char*)dst_buffer;
-
-   while (*src || reverse)
-   {
-      if (reverse)
-      {
-         src--;
-         while (src > (const unsigned char*)msg && IS_MBCONT(src))
-            src--;
-
-         if (src >= (const unsigned char*)msg && (IS_RTL(src) || IS_DIR_NEUTRAL(src) || is_misc_ws(src)))
-         {
-            if (IS_ARABIC(src))
-            {
-               unsigned replacement = font_get_arabic_replacement(
-                     (const char*)src, msg);
-
-               if (replacement)
-               {
-                  if (replacement < 0x80)
-                     *dst++ = replacement;
-                  else if (replacement < 0x800)
-                  {
-                     *dst++ = 0xC0 | (replacement >> 6);
-                     *dst++ = 0x80 | (replacement & 0x3F);
-                  }
-                  else if (replacement < 0x10000)
-                  {
-                     /* merged glyphs */
-                     if ((replacement >= 0xFEF5) && (replacement <= 0xFEFC))
-                        src -= 2;
-
-                     *dst++ = 0xE0 | ( replacement >> 12);
-                     *dst++ = 0x80 | ((replacement >> 6) & 0x3F);
-                     *dst++ = 0x80 | ( replacement       & 0x3F);
-                  }
-                  else
-                  {
-                     *dst++ = 0xF0 |  (replacement >> 18);
-                     *dst++ = 0x80 | ((replacement >> 12) & 0x3F);
-                     *dst++ = 0x80 | ((replacement >> 6)  & 0x3F);
-                     *dst++ = 0x80 | ( replacement        & 0x3F);
-                  }
-
-                  continue;
-               }
-            }
-
-            *dst++ = *src++;
-            while (IS_MBCONT(src))
-               *dst++ = *src++;
-            src--;
-
-            while (IS_MBCONT(src))
-               src--;
-         }
-         else
-         {
-            reverse = false;
-            src++;
-            while (  IS_MBCONT(src) 
-                  || IS_RTL(src) 
-                  || IS_DIR_NEUTRAL(src) 
-                  || is_misc_ws(src))
-               src++;
-         }
-      }
-      else
-      {
-         if (IS_RTL(src))
-         {
-            reverse = true;
-            while (  IS_MBCONT(src) 
-                  || IS_RTL(src)
-                  || IS_DIR_NEUTRAL(src)
-                  || is_misc_ws(src))
-               src++;
-         }
-         else
-            *dst++ = *src++;
-      }
-   }
-
-   *dst = '\0';
-
-   return (char*)dst_buffer;
-}
-#endif
-
-void font_driver_render_msg(void *data, const char *msg,
-      const struct font_params *params, void *font_data)
-{
-   font_data_t                *font = (font_data_t*)(font_data
-         ? font_data : video_font_driver);
-
-   if (msg && *msg && font && font->renderer && font->renderer->render_msg)
-   {
-#ifdef HAVE_LANGEXTRA
-      unsigned char tmp_buffer[64];
-      char *new_msg = font_driver_reshape_msg(msg, tmp_buffer, sizeof(tmp_buffer));
-#else
-      char *new_msg = (char*)msg;
-#endif
-      font->renderer->render_msg(data,
-            font->renderer_data, new_msg, params);
-#ifdef HAVE_LANGEXTRA
-      if (new_msg != (char*)tmp_buffer)
-         free(new_msg);
-#endif
-   }
-}
-
-void font_driver_bind_block(void *font_data, void *block)
-{
-   font_data_t *font = (font_data_t*)(font_data ? font_data : video_font_driver);
-
-   if (font && font->renderer && font->renderer->bind_block)
-      font->renderer->bind_block(font->renderer_data, block);
-}
-
-void font_driver_flush(unsigned width, unsigned height, void *font_data)
-{
-   font_data_t *font = (font_data_t*)(font_data ? font_data : video_font_driver);
-   if (font && font->renderer && font->renderer->flush)
-      font->renderer->flush(width, height, font->renderer_data);
-}
-
-int font_driver_get_message_width(void *font_data,
-      const char *msg, size_t len, float scale)
-{
-   font_data_t *font = (font_data_t*)(font_data ? font_data : video_font_driver);
-   if (len == 0 && msg)
-      len = strlen(msg);
-   if (font && font->renderer && font->renderer->get_message_width)
-      return font->renderer->get_message_width(font->renderer_data, msg, len, scale);
-   return -1;
-}
-
-int font_driver_get_line_height(font_data_t *font, float scale)
-{
-   struct font_line_metrics *metrics = NULL;
-   /* First try the line metrics implementation */
-   if (font && font->renderer && font->renderer->get_line_metrics)
-      if ((font->renderer->get_line_metrics(
-                  font->renderer_data, &metrics)))
-         return (int)roundf(metrics->height * scale);
-   /* Else return an approximation
-    * (uses a fudge of standard font metrics - mostly garbage...)
-    * > font_size = (width of 'a') / 0.6
-    * > line_height = font_size * 1.7f */
-   return (int)roundf(1.7f * (float)font_driver_get_message_width(font, "a", 1, scale) / 0.6f);
-}
-
-int font_driver_get_line_ascender(font_data_t *font, float scale)
-{
-   struct font_line_metrics *metrics = NULL;
-   /* First try the line metrics implementation */
-   if (font && font->renderer && font->renderer->get_line_metrics)
-      if ((font->renderer->get_line_metrics(font->renderer_data, &metrics)))
-         return (int)roundf(metrics->ascender * scale);
-   /* Else return an approximation
-    * (uses a fudge of standard font metrics - mostly garbage...)
-    * > font_size = (width of 'a') / 0.6
-    * > ascender = 1.58 * font_size * 0.75 */
-   return (int)roundf(1.58f * 0.75f * (float)font_driver_get_message_width(font, "a", 1, scale) / 0.6f);
-}
-
-int font_driver_get_line_descender(font_data_t *font, float scale)
-{
-   struct font_line_metrics *metrics = NULL;
-   /* First try the line metrics implementation */
-   if (font && font->renderer && font->renderer->get_line_metrics)
-      if ((font->renderer->get_line_metrics(font->renderer_data, &metrics)))
-         return (int)roundf(metrics->descender * scale);
-   /* Else return an approximation
-    * (uses a fudge of standard font metrics - mostly garbage...)
-    * > font_size = (width of 'a') / 0.6
-    * > descender = 1.58 * font_size * 0.25 */
-   return (int)roundf(1.58f * 0.25f * (float)font_driver_get_message_width(font, "a", 1, scale) / 0.6f);
-}
-
-int font_driver_get_line_centre_offset(font_data_t *font, float scale)
-{
-   struct font_line_metrics *metrics = NULL;
-   /* First try the line metrics implementation */
-   if (font && font->renderer && font->renderer->get_line_metrics)
-      if ((font->renderer->get_line_metrics(font->renderer_data, &metrics)))
-         return (int)roundf((metrics->ascender - metrics->descender) * 0.5f * scale);
-   /* Else return an approximation... */
-   return (int)roundf((1.58f * 0.5f * (float)font_driver_get_message_width(font, "a", 1, scale) / 0.6f) / 2.0f);
-}
-
-void font_driver_free(font_data_t *font)
-{
-   if (font)
-   {
-      bool is_threaded        = false;
-#ifdef HAVE_THREADS
-      bool *is_threaded_tmp   = video_driver_get_threaded();
-      is_threaded             = *is_threaded_tmp;
-#endif
-
-      if (font->renderer && font->renderer->free)
-         font->renderer->free(font->renderer_data, is_threaded);
-
-      font->renderer      = NULL;
-      font->renderer_data = NULL;
-
-      free(font);
-   }
-}
-
-font_data_t *font_driver_init_first(
-      void *video_data, const char *font_path, float font_size,
-      bool threading_hint, bool is_threaded,
-      enum font_driver_render_api api)
-{
-   const void *font_driver = NULL;
-   void *font_handle       = NULL;
-   bool ok                 = false;
-#ifdef HAVE_THREADS
-   if (     threading_hint
-         && is_threaded
-         && !video_driver_is_hw_context())
-      ok = video_thread_font_init(&font_driver, &font_handle,
-            video_data, font_path, font_size, api, font_init_first,
-            is_threaded);
-   else
-#endif
-   ok = font_init_first(&font_driver, &font_handle,
-         video_data, font_path, font_size, api, is_threaded);
-
-   if (ok)
-   {
-      font_data_t *font      = (font_data_t*)malloc(sizeof(*font));
-
-      if (font)
-      {
-         font->renderer      = (const font_renderer_t*)font_driver;
-         font->renderer_data = font_handle;
-         font->size          = font_size;
-         return font;
-      }
-   }
-
-   return NULL;
-}
-
-void font_driver_init_osd(
-      void *video_data,
-      const void *video_info_data,
-      bool threading_hint,
-      bool is_threaded,
-      enum font_driver_render_api api)
-{
-   const video_info_t *video_info = (const video_info_t*)video_info_data;
-   if (!video_font_driver && video_info)
-      video_font_driver = font_driver_init_first(video_data,
-            *video_info->path_font ? video_info->path_font : NULL,
-            video_info->font_size, threading_hint, is_threaded, api);
-}
-
-void font_driver_free_osd(void)
-{
-   if (video_font_driver)
-      font_driver_free(video_font_driver);
-
-   video_font_driver = NULL;
-}

+ 0 - 142
app/src/main/cpp/gfx/font_driver.h

@@ -1,142 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- *  Copyright (C) 2011-2017 - Daniel De Matteis
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __FONT_DRIVER_H__
-#define __FONT_DRIVER_H__
-
-#include <stdint.h>
-
-#include <boolean.h>
-#include <retro_common_api.h>
-
-#include "../retroarch.h"
-
-#include "video_defines.h"
-
-RETRO_BEGIN_DECLS
-
-typedef struct font_renderer
-{
-   void *(*init)(void *data, const char *font_path,
-         float font_size, bool is_threaded);
-   void (*free)(void *data, bool is_threaded);
-   void (*render_msg)(void *userdata,
-         void *data, const char *msg,
-         const struct font_params *params);
-   const char *ident;
-
-   const struct font_glyph *(*get_glyph)(void *data, uint32_t code);
-   void (*bind_block)(void *data, void *block);
-   void (*flush)(unsigned width, unsigned height, void *data);
-
-   int (*get_message_width)(void *data, const char *msg, size_t msg_len, float scale);
-   bool (*get_line_metrics)(void* data, struct font_line_metrics **metrics);
-} font_renderer_t;
-
-typedef struct font_renderer_driver
-{
-   void *(*init)(const char *font_path, float font_size);
-
-   struct font_atlas *(*get_atlas)(void *data);
-
-   /* Returns NULL if no glyph for this code is found. */
-   const struct font_glyph *(*get_glyph)(void *data, uint32_t code);
-
-   void (*free)(void *data);
-
-   const char *(*get_default_font)(void);
-
-   const char *ident;
-
-   bool (*get_line_metrics)(void* data, struct font_line_metrics **metrics);
-} font_renderer_driver_t;
-
-typedef struct
-{
-   const font_renderer_t *renderer;
-   void *renderer_data;
-   float size;
-} font_data_t;
-
-/* font_path can be NULL for default font. */
-int font_renderer_create_default(
-      const font_renderer_driver_t **drv,
-      void **handle,
-      const char *font_path, unsigned font_size);
-
-void font_driver_render_msg(void *data,
-      const char *msg, const struct font_params *params, void *font_data);
-
-void font_driver_bind_block(void *font_data, void *block);
-
-int font_driver_get_message_width(void *font_data, const char *msg, size_t len, float scale);
-
-void font_driver_flush(unsigned width, unsigned height, void *font_data);
-
-void font_driver_free(font_data_t *font);
-
-font_data_t *font_driver_init_first(
-      void *video_data,
-      const char *font_path,
-      float font_size,
-      bool threading_hint,
-      bool is_threaded,
-      enum font_driver_render_api api);
-
-void font_driver_init_osd(
-      void *video_data,
-      const void *video_info_data,
-      bool threading_hint,
-      bool is_threaded,
-      enum font_driver_render_api api);
-
-void font_driver_free_osd(void);
-
-int font_driver_get_line_height(font_data_t *font, float scale);
-int font_driver_get_line_ascender(font_data_t *font, float scale);
-int font_driver_get_line_descender(font_data_t *font, float scale);
-int font_driver_get_line_centre_offset(font_data_t *font, float scale);
-
-extern font_renderer_t gl2_raster_font;
-extern font_renderer_t gl3_raster_font;
-extern font_renderer_t gl1_raster_font;
-extern font_renderer_t d3d_xdk1_font;
-extern font_renderer_t d3d_win32_font;
-extern font_renderer_t ps2_font;
-extern font_renderer_t vita2d_vita_font;
-extern font_renderer_t ctr_font;
-extern font_renderer_t wiiu_font;
-extern font_renderer_t vulkan_raster_font;
-extern font_renderer_t metal_raster_font;
-extern font_renderer_t d3d10_font;
-extern font_renderer_t d3d11_font;
-extern font_renderer_t d3d12_font;
-extern font_renderer_t caca_font;
-extern font_renderer_t gdi_font;
-extern font_renderer_t vga_font;
-extern font_renderer_t sixel_font;
-extern font_renderer_t switch_font;
-extern font_renderer_t rsx_font;
-
-extern font_renderer_driver_t stb_font_renderer;
-extern font_renderer_driver_t stb_unicode_font_renderer;
-extern font_renderer_driver_t freetype_font_renderer;
-extern font_renderer_driver_t coretext_font_renderer;
-extern font_renderer_driver_t bitmap_font_renderer;
-
-RETRO_END_DECLS
-
-#endif

+ 13 - 2
app/src/main/cpp/gfx/gfx_animation.c

@@ -1668,15 +1668,23 @@ bool gfx_animation_ticker_smooth(gfx_animation_ctx_ticker_smooth_t *ticker)
    /* Sanity check */
    if (string_is_empty(ticker->src_str) ||
        (ticker->dst_str_len < 1) ||
-       (ticker->field_width < 1) ||
-       (!ticker->font && (ticker->glyph_width < 1)))
+       (ticker->field_width < 1) || (
+#ifdef HAVE_FONT
+      !ticker->font &&
+#endif
+         (ticker->glyph_width < 1))
+    )
       goto end;
 
    /* If we are using a fixed width font (ticker->font == NULL),
     * switch to optimised code path */
+
+#ifdef HAVE_FONT
    if (!ticker->font)
+#endif
       return gfx_animation_ticker_smooth_fw(p_anim, ticker);
 
+#ifdef HAVE_FONT
    /* Find the display width of each character in
     * the src string + total width */
    if ((src_str_len = utf8len(ticker->src_str)) < 1)
@@ -1860,6 +1868,7 @@ bool gfx_animation_ticker_smooth(gfx_animation_ctx_ticker_smooth_t *ticker)
    success                  = true;
    is_active                = true;
    p_anim->flags           |= GFX_ANIM_FLAG_TICKER_IS_ACTIVE;
+#endif
 
 end:
 
@@ -2011,6 +2020,7 @@ bool gfx_animation_line_ticker_smooth(gfx_animation_ctx_line_ticker_smooth_t *li
    if (!line_ticker)
       return false;
 
+#ifdef HAVE_FONT
    if (!line_ticker->font ||
        string_is_empty(line_ticker->src_str) ||
        (line_ticker->field_width < 1) ||
@@ -2162,6 +2172,7 @@ bool gfx_animation_line_ticker_smooth(gfx_animation_ctx_line_ticker_smooth_t *li
    p_anim->flags           |= GFX_ANIM_FLAG_TICKER_IS_ACTIVE;
 
 end:
+#endif
 
    if (wrapped_str)
    {

+ 6 - 0
app/src/main/cpp/gfx/gfx_animation.h

@@ -23,7 +23,9 @@
 #include <boolean.h>
 #include <retro_common_api.h>
 
+#ifdef HAVE_FONT
 #include "font_driver.h"
+#endif
 
 RETRO_BEGIN_DECLS
 
@@ -128,7 +130,9 @@ typedef struct gfx_animation_ctx_ticker_smooth
    unsigned *dst_str_width; /* May be set to NULL 
                                (RGUI + XMB do not require this info) */
    unsigned *x_offset;
+#ifdef HAVE_FONT
    font_data_t *font;
+#endif
    size_t dst_str_len;
    unsigned glyph_width; /* Fallback if font == NULL */
    unsigned field_width;
@@ -151,7 +155,9 @@ typedef struct gfx_animation_ctx_line_ticker
 typedef struct gfx_animation_ctx_line_ticker_smooth
 {
    uint64_t idx;
+#ifdef HAVE_FONT
    font_data_t *font;
+#endif
    const char *src_str;
    float *y_offset;
    char *dst_str;

+ 6 - 0
app/src/main/cpp/gfx/gfx_display.c

@@ -457,6 +457,7 @@ void gfx_display_scissor_begin(
    }
 }
 
+#ifdef HAVE_FONT
 font_data_t *gfx_display_font_file(
       gfx_display_t *p_disp,
       char* fontpath, float menu_font_size, bool is_threaded)
@@ -522,6 +523,7 @@ void gfx_display_draw_text(
       video_st->poke->set_osd_msg(video_st->data,
             text, &params, (void*)font);
 }
+#endif
 
 void gfx_display_draw_bg(
       gfx_display_t *p_disp,
@@ -1041,6 +1043,7 @@ int gfx_display_osk_ptr_at_pos(void *data, int x, int y,
    return -1;
 }
 
+#if HAVE_FONT
 void gfx_display_draw_keyboard(
       gfx_display_t *p_disp,
       void *userdata,
@@ -1134,6 +1137,7 @@ void gfx_display_draw_keyboard(
             false, 0, false);
    }
 }
+#endif
 
 /* NOTE: Reads image from memory buffer */
 bool gfx_display_reset_textures_list_buffer(
@@ -1200,12 +1204,14 @@ bool gfx_display_reset_textures_list(
    return true;
 }
 
+#if HAVE_FONT
 /* Teardown; deinitializes and frees all
  * fonts associated to the display driver */
 void gfx_display_font_free(font_data_t *font)
 {
    font_driver_free(font);
 }
+#endif
 
 void gfx_display_deinit_white_texture(void)
 {

+ 10 - 0
app/src/main/cpp/gfx/gfx_display.h

@@ -30,7 +30,9 @@
 #include <gfx/math/matrix_4x4.h>
 
 #include "../retroarch.h"
+#ifdef HAVE_FONT
 #include "../gfx/font_driver.h"
+#endif
 
 RETRO_BEGIN_DECLS
 
@@ -223,12 +225,14 @@ void gfx_display_draw_cursor(
       float *color, float cursor_size, uintptr_t texture,
       float x, float y, unsigned width, unsigned height);
 
+#if HAVE_FONT
 void gfx_display_draw_text(
       const font_data_t *font, const char *text,
       float x, float y, int width, int height,
       uint32_t color, enum text_alignment text_align,
       float scale_factor, bool shadows_enable, float shadow_offset,
       bool draw_outside);
+#endif
 
 void gfx_display_scissor_begin(
       gfx_display_t *p_disp,
@@ -237,13 +241,16 @@ void gfx_display_scissor_begin(
       unsigned video_height,
       int x, int y, unsigned width, unsigned height);
 
+#if HAVE_FONT
 void gfx_display_font_free(font_data_t *font);
+#endif
 
 bool gfx_display_init_first_driver(gfx_display_t *p_disp,
       bool video_is_threaded);
 
 gfx_display_t *disp_get_ptr(void);
 
+#if HAVE_FONT
 void gfx_display_draw_keyboard(
       gfx_display_t *p_disp,
       void *userdata,
@@ -253,6 +260,7 @@ void gfx_display_draw_keyboard(
       const font_data_t *font,
       char *grid[], unsigned id,
       unsigned text_color);
+#endif
 
 void gfx_display_draw_bg(
       gfx_display_t *p_disp,
@@ -284,8 +292,10 @@ void gfx_display_draw_texture_slice(
 void gfx_display_rotate_z(gfx_display_t *p_disp,
       math_matrix_4x4 *matrix, float cosine, float sine, void *data);
 
+#ifdef HAVE_FONT
 font_data_t *gfx_display_font_file(gfx_display_t *p_disp,
       char* fontpath, float font_size, bool is_threaded);
+#endif
 
 bool gfx_display_reset_textures_list(
       const char *texture_path,

+ 0 - 1053
app/src/main/cpp/gfx/gfx_thumbnail.c

@@ -1,1053 +0,0 @@
-/* Copyright  (C) 2010-2019 The RetroArch team
- *
- * ---------------------------------------------------------------------------------------
- * The following license statement only applies to this file (gfx_thumbnail.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 <features/features_cpu.h>
-#include <file/file_path.h>
-#include <string/stdstring.h>
-
-#include "gfx_display.h"
-#include "gfx_animation.h"
-
-#include "gfx_thumbnail.h"
-
-#include "../tasks/tasks_internal.h"
-
-#define DEFAULT_GFX_THUMBNAIL_STREAM_DELAY  83.333333f
-#define DEFAULT_GFX_THUMBNAIL_FADE_DURATION 166.66667f
-
-/* Utility structure, sent as userdata when pushing
- * an image load */
-typedef struct
-{
-   uint64_t list_id;
-   gfx_thumbnail_t *thumbnail;
-} gfx_thumbnail_tag_t;
-
-static gfx_thumbnail_state_t gfx_thumb_st = {0}; /* uint64_t alignment */
-
-gfx_thumbnail_state_t *gfx_thumb_get_ptr(void)
-{
-   return &gfx_thumb_st;
-}
-
-/* Setters */
-
-/* When streaming thumbnails, sets time in ms that an
- * entry must be on screen before an image load is
- * requested
- * > if 'delay' is negative, default value is set */
-void gfx_thumbnail_set_stream_delay(float delay)
-{
-   gfx_thumbnail_state_t *p_gfx_thumb = &gfx_thumb_st;
-
-   p_gfx_thumb->stream_delay = (delay >= 0.0f) ?
-         delay : DEFAULT_GFX_THUMBNAIL_STREAM_DELAY;
-}
-
-/* Sets duration in ms of the thumbnail 'fade in'
- * animation
- * > If 'duration' is negative, default value is set */
-void gfx_thumbnail_set_fade_duration(float duration)
-{
-   gfx_thumbnail_state_t *p_gfx_thumb = &gfx_thumb_st;
-
-   p_gfx_thumb->fade_duration = (duration >= 0.0f) ?
-         duration : DEFAULT_GFX_THUMBNAIL_FADE_DURATION;
-}
-
-/* Specifies whether 'fade in' animation should be
- * triggered for missing thumbnails
- * > When 'true', allows menu driver to animate
- *   any 'thumbnail unavailable' notifications */
-void gfx_thumbnail_set_fade_missing(bool fade_missing)
-{
-   gfx_thumbnail_state_t *p_gfx_thumb = &gfx_thumb_st;
-
-   p_gfx_thumb->fade_missing = fade_missing;
-}
-
-/* Callbacks */
-
-/* Fade animation callback - simply resets thumbnail
- * 'fade_active' status */
-static void gfx_thumbnail_fade_cb(void *userdata)
-{
-   gfx_thumbnail_t *thumbnail = (gfx_thumbnail_t*)userdata;
-   if (thumbnail)
-      thumbnail->flags |= GFX_THUMB_FLAG_FADE_ACTIVE;
-}
-
-/* Initialises thumbnail 'fade in' animation */
-static void gfx_thumbnail_init_fade(
-      gfx_thumbnail_state_t *p_gfx_thumb,
-      gfx_thumbnail_t *thumbnail)
-{
-   /* Sanity check */
-   if (!thumbnail)
-      return;
-
-   /* A 'fade in' animation is triggered if:
-    * - Thumbnail is available
-    * - Thumbnail is missing and 'fade_missing' is enabled */
-   if ((thumbnail->status == GFX_THUMBNAIL_STATUS_AVAILABLE) ||
-       (p_gfx_thumb->fade_missing &&
-            (thumbnail->status == GFX_THUMBNAIL_STATUS_MISSING)))
-   {
-      if (p_gfx_thumb->fade_duration > 0.0f)
-      {
-         gfx_animation_ctx_entry_t animation_entry;
-
-         thumbnail->alpha                 = 0.0f;
-         thumbnail->flags                |= GFX_THUMB_FLAG_FADE_ACTIVE;
-
-         animation_entry.easing_enum      = EASING_OUT_QUAD;
-         animation_entry.tag              = (uintptr_t)&thumbnail->alpha;
-         animation_entry.duration         = p_gfx_thumb->fade_duration;
-         animation_entry.target_value     = 1.0f;
-         animation_entry.subject          = &thumbnail->alpha;
-         animation_entry.cb               = gfx_thumbnail_fade_cb;
-         animation_entry.userdata         = thumbnail;
-
-         gfx_animation_push(&animation_entry);
-      }
-      else
-         thumbnail->alpha = 1.0f;
-   }
-}
-
-/* Used to process thumbnail data following completion
- * of image load task */
-static void gfx_thumbnail_handle_upload(
-      retro_task_t *task, void *task_data, void *user_data, const char *err)
-{
-   gfx_thumbnail_state_t *p_gfx_thumb = &gfx_thumb_st;
-   struct texture_image *img          = (struct texture_image*)task_data;
-   gfx_thumbnail_tag_t *thumbnail_tag = (gfx_thumbnail_tag_t*)user_data;
-   bool fade_enabled                  = false;
-
-   /* Sanity check */
-   if (!thumbnail_tag)
-      goto end;
-
-   /* Ensure that we are operating on the correct
-    * thumbnail... */
-   if (thumbnail_tag->list_id != p_gfx_thumb->list_id)
-      goto end;
-
-   /* Only process image if we are waiting for it */
-   if (thumbnail_tag->thumbnail->status != GFX_THUMBNAIL_STATUS_PENDING)
-      goto end;
-
-   /* Sanity check: if thumbnail already has a texture,
-    * we're in some kind of weird error state - in this
-    * case, the best course of action is to just reset
-    * the thumbnail... */
-   if (thumbnail_tag->thumbnail->texture)
-      gfx_thumbnail_reset(thumbnail_tag->thumbnail);
-
-   /* Set thumbnail 'missing' status by default
-    * (saves a number of checks later) */
-   thumbnail_tag->thumbnail->status = GFX_THUMBNAIL_STATUS_MISSING;
-
-   /* If we reach this stage, thumbnail 'fade in'
-    * animations should be applied (based on current
-    * thumbnail status and global configuration) */
-   fade_enabled = true;
-
-   /* Check we have a valid image */
-   if (!img || (img->width < 1) || (img->height < 1))
-      goto end;
-
-   /* Upload texture to GPU */
-   if (!video_driver_texture_load(
-            img, TEXTURE_FILTER_MIPMAP_LINEAR,
-            &thumbnail_tag->thumbnail->texture))
-      goto end;
-
-   /* Cache dimensions */
-   thumbnail_tag->thumbnail->width  = img->width;
-   thumbnail_tag->thumbnail->height = img->height;
-
-   /* Update thumbnail status */
-   thumbnail_tag->thumbnail->status = GFX_THUMBNAIL_STATUS_AVAILABLE;
-
-end:
-   /* Clean up */
-   if (img)
-   {
-      image_texture_free(img);
-      free(img);
-   }
-
-   if (thumbnail_tag)
-   {
-      /* Trigger 'fade in' animation, if required */
-      if (fade_enabled)
-         gfx_thumbnail_init_fade(p_gfx_thumb,
-               thumbnail_tag->thumbnail);
-
-      free(thumbnail_tag);
-   }
-}
-
-/* Core interface */
-
-/* When called, prevents the handling of any pending
- * thumbnail load requests
- * >> **MUST** be called before deleting any gfx_thumbnail_t
- *    objects passed to gfx_thumbnail_request() or
- *    gfx_thumbnail_process_stream(), otherwise
- *    heap-use-after-free errors *will* occur */
-void gfx_thumbnail_cancel_pending_requests(void)
-{
-   gfx_thumbnail_state_t *p_gfx_thumb = &gfx_thumb_st;
-
-   p_gfx_thumb->list_id++;
-}
-
-/* Requests loading of the specified thumbnail
- * - If operation fails, 'thumbnail->status' will be set to
- *   GFX_THUMBNAIL_STATUS_MISSING
- * - If operation is successful, 'thumbnail->status' will be
- *   set to GFX_THUMBNAIL_STATUS_PENDING
- * 'thumbnail' will be populated with texture info/metadata
- * once the image load is complete
- * NOTE 1: Must be called *after* gfx_thumbnail_set_system()
- *         and gfx_thumbnail_set_content*()
- * NOTE 2: 'playlist' and 'idx' are only required here for
- *         on-demand thumbnail download support
- *         (an annoyance...) */ 
-void gfx_thumbnail_request(
-      gfx_thumbnail_path_data_t *path_data, enum gfx_thumbnail_id thumbnail_id,
-      playlist_t *playlist, size_t idx, gfx_thumbnail_t *thumbnail,
-      unsigned gfx_thumbnail_upscale_threshold,
-      bool network_on_demand_thumbnails)
-{
-   const char *thumbnail_path         = NULL;
-   bool has_thumbnail                 = false;
-   gfx_thumbnail_state_t *p_gfx_thumb = &gfx_thumb_st;
-   
-   if (!path_data || !thumbnail)
-      return;
-
-   /* Reset thumbnail, then set 'missing' status by default
-    * (saves a number of checks later) */
-   gfx_thumbnail_reset(thumbnail);
-   thumbnail->status = GFX_THUMBNAIL_STATUS_MISSING;
-
-   /* Update/extract thumbnail path */
-   if (gfx_thumbnail_is_enabled(path_data, thumbnail_id))
-      if (gfx_thumbnail_update_path(path_data, thumbnail_id))
-         has_thumbnail = gfx_thumbnail_get_path(path_data, thumbnail_id, &thumbnail_path);
-
-   /* Load thumbnail, if required */
-   if (has_thumbnail)
-   {
-      if (path_is_valid(thumbnail_path))
-      {
-         gfx_thumbnail_tag_t *thumbnail_tag =
-               (gfx_thumbnail_tag_t*)malloc(sizeof(gfx_thumbnail_tag_t));
-
-         if (!thumbnail_tag)
-            goto end;
-
-         /* Configure user data */
-         thumbnail_tag->thumbnail = thumbnail;
-         thumbnail_tag->list_id   = p_gfx_thumb->list_id;
-
-         /* Would like to cancel any existing image load tasks
-          * here, but can't see how to do it... */
-         if (task_push_image_load(
-               thumbnail_path, video_driver_supports_rgba(),
-               gfx_thumbnail_upscale_threshold,
-               gfx_thumbnail_handle_upload, thumbnail_tag))
-            thumbnail->status = GFX_THUMBNAIL_STATUS_PENDING;
-      }
-#ifdef HAVE_NETWORKING
-      /* Handle on demand thumbnail downloads */
-      else if (network_on_demand_thumbnails)
-      {
-         const char *system                         = NULL;
-         const char *img_name                       = NULL;
-         static char last_img_name[PATH_MAX_LENGTH] = {0};
-
-         if (!playlist)
-            goto end;
-
-         /* Get current image name */
-         if (!gfx_thumbnail_get_img_name(path_data, &img_name))
-            goto end;
-
-         /* Only trigger a thumbnail download if image
-          * name has changed since the last call of
-          * gfx_thumbnail_request()
-          * > Allows gfx_thumbnail_request() to be used
-          *   for successive right/left thumbnail requests
-          *   with minimal duplication of effort
-          *   (i.e. task_push_pl_entry_thumbnail_download()
-          *   will automatically cancel if a download for the
-          *   existing playlist entry is pending, but the
-          *   checks required for this involve significant
-          *   overheads. We can avoid this entirely with
-          *   a simple string comparison) */
-         if (string_is_equal(img_name, last_img_name))
-            goto end;
-
-         strlcpy(last_img_name, img_name, sizeof(last_img_name));
-
-         /* Get system name */
-         if (!gfx_thumbnail_get_system(path_data, &system))
-            goto end;
-
-         /* Trigger thumbnail download */
-         task_push_pl_entry_thumbnail_download(
-               system, playlist, (unsigned)idx,
-               false, true);
-      }
-#endif
-   }
-
-end:
-   /* Trigger 'fade in' animation, if required */
-   if (thumbnail->status != GFX_THUMBNAIL_STATUS_PENDING)
-      gfx_thumbnail_init_fade(p_gfx_thumb,
-            thumbnail);
-}
-
-/* Requests loading of a specific thumbnail image file
- * (may be used, for example, to load savestate images)
- * - If operation fails, 'thumbnail->status' will be set to
- *   MUI_THUMBNAIL_STATUS_MISSING
- * - If operation is successful, 'thumbnail->status' will be
- *   set to MUI_THUMBNAIL_STATUS_PENDING
- * 'thumbnail' will be populated with texture info/metadata
- * once the image load is complete */
-void gfx_thumbnail_request_file(
-      const char *file_path, gfx_thumbnail_t *thumbnail,
-      unsigned gfx_thumbnail_upscale_threshold)
-{
-   gfx_thumbnail_state_t *p_gfx_thumb = &gfx_thumb_st;
-   gfx_thumbnail_tag_t *thumbnail_tag = NULL;
-
-   if (!thumbnail)
-      return;
-
-   /* Reset thumbnail, then set 'missing' status by default
-    * (saves a number of checks later) */
-   gfx_thumbnail_reset(thumbnail);
-   thumbnail->status = GFX_THUMBNAIL_STATUS_MISSING;
-
-   /* Check if file path is valid */
-   if (   string_is_empty(file_path)
-       || !path_is_valid(file_path))
-      return;
-
-   /* Load thumbnail */
-   if (!(thumbnail_tag = (gfx_thumbnail_tag_t*)malloc(sizeof(gfx_thumbnail_tag_t))))
-      return;
-
-   /* Configure user data */
-   thumbnail_tag->thumbnail = thumbnail;
-   thumbnail_tag->list_id   = p_gfx_thumb->list_id;
-
-   /* Would like to cancel any existing image load tasks
-    * here, but can't see how to do it... */
-   if (task_push_image_load(
-         file_path, video_driver_supports_rgba(),
-         gfx_thumbnail_upscale_threshold,
-         gfx_thumbnail_handle_upload, thumbnail_tag))
-      thumbnail->status = GFX_THUMBNAIL_STATUS_PENDING;
-}
-
-/* Resets (and free()s the current texture of) the
- * specified thumbnail */
-void gfx_thumbnail_reset(gfx_thumbnail_t *thumbnail)
-{
-   if (!thumbnail)
-      return;
-
-   /* Unload texture */
-   if (thumbnail->texture)
-      video_driver_texture_unload(&thumbnail->texture);
-
-   /* Ensure any 'fade in' animation is killed */
-   if (thumbnail->flags & GFX_THUMB_FLAG_FADE_ACTIVE)
-   {
-      uintptr_t tag = (uintptr_t)&thumbnail->alpha;
-      gfx_animation_kill_by_tag(&tag);
-   }
-
-   /* Reset all parameters */
-   thumbnail->status      = GFX_THUMBNAIL_STATUS_UNKNOWN;
-   thumbnail->texture     = 0;
-   thumbnail->width       = 0;
-   thumbnail->height      = 0;
-   thumbnail->alpha       = 0.0f;
-   thumbnail->delay_timer = 0.0f;
-   thumbnail->flags      &= ~(GFX_THUMB_FLAG_FADE_ACTIVE
-                            | GFX_THUMB_FLAG_CORE_ASPECT);
-}
-
-/* Stream processing */
-
-/* Requests loading of the specified thumbnail via
- * the stream interface
- * - Must be called on each frame for the duration
- *   that specified thumbnail is on-screen
- * - Actual load request is deferred by currently
- *   set stream delay
- * - Function becomes a no-op once load request is
- *   made
- * - Thumbnails loaded via this function must be
- *   deleted manually via gfx_thumbnail_reset()
- *   when they move off-screen
- * NOTE 1: Must be called *after* gfx_thumbnail_set_system()
- *         and gfx_thumbnail_set_content*()
- * NOTE 2: 'playlist' and 'idx' are only required here for
- *         on-demand thumbnail download support
- *         (an annoyance...)
- * NOTE 3: This function is intended for use in situations
- *         where each menu entry has a *single* thumbnail.
- *         If each entry has two thumbnails, use
- *         gfx_thumbnail_request_streams() for improved
- *         performance */
-void gfx_thumbnail_request_stream(
-      gfx_thumbnail_path_data_t *path_data,
-      gfx_animation_t *p_anim,
-      enum gfx_thumbnail_id thumbnail_id,
-      playlist_t *playlist, size_t idx,
-      gfx_thumbnail_t *thumbnail,
-      unsigned gfx_thumbnail_upscale_threshold,
-      bool network_on_demand_thumbnails)
-{
-   gfx_thumbnail_state_t *p_gfx_thumb = &gfx_thumb_st;
-
-   /* Only process request if current status
-    * is GFX_THUMBNAIL_STATUS_UNKNOWN */
-   if (!thumbnail ||
-       (thumbnail->status != GFX_THUMBNAIL_STATUS_UNKNOWN))
-      return;
-
-   /* Check if stream delay timer has elapsed */
-   thumbnail->delay_timer += p_anim->delta_time;
-
-   if (thumbnail->delay_timer > p_gfx_thumb->stream_delay)
-   {
-      /* Sanity check */
-      if (!path_data)
-      {
-         /* No path information
-          * > Reset thumbnail and set missing status
-          *   to prevent repeated load attempts */
-         gfx_thumbnail_reset(thumbnail);
-         thumbnail->status = GFX_THUMBNAIL_STATUS_MISSING;
-         thumbnail->alpha  = 1.0f;
-         return;
-      }
-
-      /* Request image load */
-      gfx_thumbnail_request(
-            path_data, thumbnail_id, playlist, idx, thumbnail,
-            gfx_thumbnail_upscale_threshold,
-            network_on_demand_thumbnails);
-   }
-}
-
-/* Requests loading of the specified thumbnails via
- * the stream interface
- * - Must be called on each frame for the duration
- *   that specified thumbnails are on-screen
- * - Actual load request is deferred by currently
- *   set stream delay
- * - Function becomes a no-op once load request is
- *   made
- * - Thumbnails loaded via this function must be
- *   deleted manually via gfx_thumbnail_reset()
- *   when they move off-screen
- * NOTE 1: Must be called *after* gfx_thumbnail_set_system()
- *         and gfx_thumbnail_set_content*()
- * NOTE 2: 'playlist' and 'idx' are only required here for
- *         on-demand thumbnail download support
- *         (an annoyance...)
- * NOTE 3: This function is intended for use in situations
- *         where each menu entry has *two* thumbnails.
- *         If each entry only has a single thumbnail, use
- *         gfx_thumbnail_request_stream() for improved
- *         performance */
-void gfx_thumbnail_request_streams(
-      gfx_thumbnail_path_data_t *path_data,
-      gfx_animation_t *p_anim,
-      playlist_t *playlist, size_t idx,
-      gfx_thumbnail_t *right_thumbnail,
-      gfx_thumbnail_t *left_thumbnail,
-      unsigned gfx_thumbnail_upscale_threshold,
-      bool network_on_demand_thumbnails)
-{
-   bool process_right = false;
-   bool process_left  = false;
-
-   if (!right_thumbnail || !left_thumbnail)
-      return;
-
-   /* Only process request if current status
-    * is GFX_THUMBNAIL_STATUS_UNKNOWN */
-   process_right = (right_thumbnail->status == GFX_THUMBNAIL_STATUS_UNKNOWN);
-   process_left  = (left_thumbnail->status  == GFX_THUMBNAIL_STATUS_UNKNOWN);
-
-   if (process_right || process_left)
-   {
-      /* Check if stream delay timer has elapsed */
-      gfx_thumbnail_state_t *p_gfx_thumb = &gfx_thumb_st;
-      float delta_time                   = p_anim->delta_time;
-      bool request_right                 = false;
-      bool request_left                  = false;
-
-      if (process_right)
-      {
-         right_thumbnail->delay_timer += delta_time;
-         request_right                 =
-               (right_thumbnail->delay_timer > p_gfx_thumb->stream_delay);
-      }
-
-      if (process_left)
-      {
-         left_thumbnail->delay_timer  += delta_time;
-         request_left                  =
-               (left_thumbnail->delay_timer > p_gfx_thumb->stream_delay);
-      }
-
-      /* Check if one or more thumbnails should be requested */
-      if (request_right || request_left)
-      {
-         /* Sanity check */
-         if (!path_data)
-         {
-            /* No path information
-             * > Reset thumbnail and set missing status
-             *   to prevent repeated load attempts */
-            if (request_right)
-            {
-               gfx_thumbnail_reset(right_thumbnail);
-               right_thumbnail->status = GFX_THUMBNAIL_STATUS_MISSING;
-               right_thumbnail->alpha  = 1.0f;
-            }
-
-            if (request_left)
-            {
-               gfx_thumbnail_reset(left_thumbnail);
-               left_thumbnail->status  = GFX_THUMBNAIL_STATUS_MISSING;
-               left_thumbnail->alpha   = 1.0f;
-            }
-
-            return;
-         }
-
-         /* Request image load */
-         if (request_right)
-            gfx_thumbnail_request(
-                  path_data, GFX_THUMBNAIL_RIGHT, playlist, idx, right_thumbnail,
-                  gfx_thumbnail_upscale_threshold,
-                  network_on_demand_thumbnails);
-
-         if (request_left)
-            gfx_thumbnail_request(
-                  path_data, GFX_THUMBNAIL_LEFT, playlist, idx, left_thumbnail,
-                  gfx_thumbnail_upscale_threshold,
-                  network_on_demand_thumbnails);
-      }
-   }
-}
-
-/* Handles streaming of the specified thumbnail as it moves
- * on/off screen
- * - Must be called each frame for every on-screen entry
- * - Must be called once for each entry as it moves off-screen
- *   (or can be called each frame - overheads are small)
- * NOTE 1: Must be called *after* gfx_thumbnail_set_system()
- * NOTE 2: This function calls gfx_thumbnail_set_content*()
- * NOTE 3: This function is intended for use in situations
- *         where each menu entry has a *single* thumbnail.
- *         If each entry has two thumbnails, use
- *         gfx_thumbnail_process_streams() for improved
- *         performance */
-void gfx_thumbnail_process_stream(
-      gfx_thumbnail_path_data_t *path_data,
-      gfx_animation_t *p_anim,
-      enum gfx_thumbnail_id thumbnail_id,
-      playlist_t *playlist, size_t idx,
-      gfx_thumbnail_t *thumbnail,
-      bool on_screen,
-      unsigned gfx_thumbnail_upscale_threshold,
-      bool network_on_demand_thumbnails)
-{
-   if (!thumbnail)
-      return;
-
-   if (on_screen)
-   {
-      /* Entry is on-screen
-       * > Only process if current status is
-       *   GFX_THUMBNAIL_STATUS_UNKNOWN */
-      if (thumbnail->status == GFX_THUMBNAIL_STATUS_UNKNOWN)
-      {
-         gfx_thumbnail_state_t *p_gfx_thumb = &gfx_thumb_st;
-
-         /* Check if stream delay timer has elapsed */
-         thumbnail->delay_timer += p_anim->delta_time;
-
-         if (thumbnail->delay_timer > p_gfx_thumb->stream_delay)
-         {
-            /* Update thumbnail content */
-            if (!path_data ||
-                !playlist ||
-                !gfx_thumbnail_set_content_playlist(path_data, playlist, idx))
-            {
-               /* Content is invalid
-                * > Reset thumbnail and set missing status */
-               gfx_thumbnail_reset(thumbnail);
-               thumbnail->status = GFX_THUMBNAIL_STATUS_MISSING;
-               thumbnail->alpha  = 1.0f;
-               return;
-            }
-
-            /* Request image load */
-            gfx_thumbnail_request(
-                  path_data, thumbnail_id, playlist, idx, thumbnail,
-                  gfx_thumbnail_upscale_threshold,
-                  network_on_demand_thumbnails);
-         }
-      }
-   }
-   else
-   {
-      /* Entry is off-screen
-       * > If status is GFX_THUMBNAIL_STATUS_UNKNOWN,
-       *   thumbnail is already in a blank state - but we
-       *   must ensure that delay timer is set to zero */
-      if (thumbnail->status == GFX_THUMBNAIL_STATUS_UNKNOWN)
-         thumbnail->delay_timer = 0.0f;
-      /* In all other cases, reset thumbnail */
-      else
-         gfx_thumbnail_reset(thumbnail);
-   }
-}
-
-/* Handles streaming of the specified thumbnails as they move
- * on/off screen
- * - Must be called each frame for every on-screen entry
- * - Must be called once for each entry as it moves off-screen
- *   (or can be called each frame - overheads are small)
- * NOTE 1: Must be called *after* gfx_thumbnail_set_system()
- * NOTE 2: This function calls gfx_thumbnail_set_content*()
- * NOTE 3: This function is intended for use in situations
- *         where each menu entry has *two* thumbnails.
- *         If each entry only has a single thumbnail, use
- *         gfx_thumbnail_process_stream() for improved
- *         performance */
-void gfx_thumbnail_process_streams(
-      gfx_thumbnail_path_data_t *path_data,
-      gfx_animation_t *p_anim,
-      playlist_t *playlist, size_t idx,
-      gfx_thumbnail_t *right_thumbnail,
-      gfx_thumbnail_t *left_thumbnail,
-      bool on_screen,
-      unsigned gfx_thumbnail_upscale_threshold,
-      bool network_on_demand_thumbnails)
-{
-   if (!right_thumbnail || !left_thumbnail)
-      return;
-
-   if (on_screen)
-   {
-      /* Entry is on-screen
-       * > Only process if current status is
-       *   GFX_THUMBNAIL_STATUS_UNKNOWN */
-      bool process_right = (right_thumbnail->status == GFX_THUMBNAIL_STATUS_UNKNOWN);
-      bool process_left  = (left_thumbnail->status  == GFX_THUMBNAIL_STATUS_UNKNOWN);
-
-      if (process_right || process_left)
-      {
-         /* Check if stream delay timer has elapsed */
-         gfx_thumbnail_state_t *p_gfx_thumb = &gfx_thumb_st;
-         float delta_time                   = p_anim->delta_time;
-         bool request_right                 = false;
-         bool request_left                  = false;
-
-         if (process_right)
-         {
-            right_thumbnail->delay_timer += delta_time;
-            request_right                 =
-                  (right_thumbnail->delay_timer > p_gfx_thumb->stream_delay);
-         }
-
-         if (process_left)
-         {
-            left_thumbnail->delay_timer  += delta_time;
-            request_left                  =
-                  (left_thumbnail->delay_timer > p_gfx_thumb->stream_delay);
-         }
-
-         /* Check if one or more thumbnails should be requested */
-         if (request_right || request_left)
-         {
-            /* Update thumbnail content */
-            if (!path_data ||
-                !playlist ||
-                !gfx_thumbnail_set_content_playlist(path_data, playlist, idx))
-            {
-               /* Content is invalid
-                * > Reset thumbnail and set missing status */
-               if (request_right)
-               {
-                  gfx_thumbnail_reset(right_thumbnail);
-                  right_thumbnail->status = GFX_THUMBNAIL_STATUS_MISSING;
-                  right_thumbnail->alpha  = 1.0f;
-               }
-
-               if (request_left)
-               {
-                  gfx_thumbnail_reset(left_thumbnail);
-                  left_thumbnail->status  = GFX_THUMBNAIL_STATUS_MISSING;
-                  left_thumbnail->alpha   = 1.0f;
-               }
-
-               return;
-            }
-
-            /* Request image load */
-            if (request_right)
-               gfx_thumbnail_request(
-                     path_data, GFX_THUMBNAIL_RIGHT, playlist, idx, right_thumbnail,
-                     gfx_thumbnail_upscale_threshold,
-                     network_on_demand_thumbnails);
-
-            if (request_left)
-               gfx_thumbnail_request(
-                     path_data, GFX_THUMBNAIL_LEFT, playlist, idx, left_thumbnail,
-                     gfx_thumbnail_upscale_threshold,
-                     network_on_demand_thumbnails);
-         }
-      }
-   }
-   else
-   {
-      /* Entry is off-screen
-       * > If status is GFX_THUMBNAIL_STATUS_UNKNOWN,
-       *   thumbnail is already in a blank state - but we
-       *   must ensure that delay timer is set to zero
-       * > In all other cases, reset thumbnail */
-      if (right_thumbnail->status == GFX_THUMBNAIL_STATUS_UNKNOWN)
-         right_thumbnail->delay_timer = 0.0f;
-      else
-         gfx_thumbnail_reset(right_thumbnail);
-
-      if (left_thumbnail->status == GFX_THUMBNAIL_STATUS_UNKNOWN)
-         left_thumbnail->delay_timer = 0.0f;
-      else
-         gfx_thumbnail_reset(left_thumbnail);
-   }
-}
-
-/* Thumbnail rendering */
-
-/* Determines the actual screen dimensions of a
- * thumbnail when centred with aspect correct
- * scaling within a rectangle of (width x height) */
-void gfx_thumbnail_get_draw_dimensions(
-      gfx_thumbnail_t *thumbnail,
-      unsigned width, unsigned height, float scale_factor,
-      float *draw_width, float *draw_height)
-{
-   float core_aspect;
-   float display_aspect;
-   float thumbnail_aspect;
-   video_driver_state_t *video_st = video_state_get_ptr();
-
-   /* Sanity check */
-   if (   !thumbnail 
-       || (width             < 1) 
-       || (height            < 1)
-       || (thumbnail->width  < 1) 
-       || (thumbnail->height < 1))
-   {
-      *draw_width  = 0.0f;
-      *draw_height = 0.0f;
-      return;
-   }
-
-   /* Account for display/thumbnail/core aspect ratio
-    * differences */
-   display_aspect   = (float)width            / (float)height;
-   thumbnail_aspect = (float)thumbnail->width / (float)thumbnail->height;
-   core_aspect      = ((thumbnail->flags & GFX_THUMB_FLAG_CORE_ASPECT) 
-         && video_st && video_st->av_info.geometry.aspect_ratio > 0)
-               ? video_st->av_info.geometry.aspect_ratio
-               : thumbnail_aspect;
-
-   if (thumbnail_aspect > display_aspect)
-   {
-      *draw_width  = (float)width;
-      *draw_height = (float)thumbnail->height * (*draw_width / (float)thumbnail->width);
-
-      if (thumbnail->flags & GFX_THUMB_FLAG_CORE_ASPECT)
-      {
-         *draw_height = *draw_height * (thumbnail_aspect / core_aspect);
-
-         if (*draw_height > height)
-         {
-            *draw_height = (float)height;
-            *draw_width  = (float)thumbnail->width * (*draw_height / (float)thumbnail->height);
-            *draw_width  = *draw_width / (thumbnail_aspect / core_aspect);
-         }
-      }
-   }
-   else
-   {
-      *draw_height = (float)height;
-      *draw_width  = (float)thumbnail->width * (*draw_height / (float)thumbnail->height);
-
-      if (thumbnail->flags & GFX_THUMB_FLAG_CORE_ASPECT)
-         *draw_width  = *draw_width / (thumbnail_aspect / core_aspect);
-   }
-
-   /* Account for scale factor
-    * > Side note: We cannot use the gfx_display_ctx_draw_t
-    *   'scale_factor' parameter for scaling thumbnails,
-    *   since this clips off any part of the expanded image
-    *   that extends beyond the bounding box. But even if
-    *   it didn't, we can't get real screen dimensions
-    *   without scaling manually... */
-   *draw_width  *= scale_factor;
-   *draw_height *= scale_factor;
-}
-
-/* Draws specified thumbnail with specified alignment
- * (and aspect correct scaling) within a rectangle of
- * (width x height).
- * 'shadow' defines an optional shadow effect (may be
- * set to NULL if a shadow effect is not required).
- * NOTE: Setting scale_factor > 1.0f will increase the
- *       size of the thumbnail beyond the limits of the
- *       (width x height) rectangle (alignment + aspect
- *       correct scaling is preserved). Use with caution */
-
-void gfx_thumbnail_draw(
-      void *userdata,
-      unsigned video_width,
-      unsigned video_height,
-      gfx_thumbnail_t *thumbnail,
-      float x, float y, unsigned width, unsigned height,
-      enum gfx_thumbnail_alignment alignment,
-      float alpha, float scale_factor,
-      gfx_thumbnail_shadow_t *shadow)
-{
-   gfx_display_t            *p_disp  = disp_get_ptr();
-   gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
-   /* Sanity check */
-   if (
-            !thumbnail
-         || !dispctx
-         || (width         < 1)
-         || (height        < 1)
-         || (alpha        <= 0.0f)
-         || (scale_factor <= 0.0f)
-      )
-      return;
-
-   /* Only draw thumbnail if it is available... */
-   if (thumbnail->status == GFX_THUMBNAIL_STATUS_AVAILABLE)
-   {
-      gfx_display_ctx_draw_t draw;
-      struct video_coords coords;
-      math_matrix_4x4 mymat;
-      float draw_width;
-      float draw_height;
-      float draw_x;
-      float draw_y;
-      float thumbnail_alpha     = thumbnail->alpha * alpha;
-      float thumbnail_color[16] = {
-         1.0f, 1.0f, 1.0f, 1.0f,
-         1.0f, 1.0f, 1.0f, 1.0f,
-         1.0f, 1.0f, 1.0f, 1.0f,
-         1.0f, 1.0f, 1.0f, 1.0f
-      };
-
-      /* Set thumbnail opacity */
-      if (thumbnail_alpha <= 0.0f)
-         return;
-      if (thumbnail_alpha < 1.0f)
-         gfx_display_set_alpha(thumbnail_color, thumbnail_alpha);
-
-      /* Get thumbnail dimensions */
-      gfx_thumbnail_get_draw_dimensions(
-            thumbnail, width, height, scale_factor,
-            &draw_width, &draw_height);
-
-      if (dispctx->blend_begin)
-         dispctx->blend_begin(userdata);
-
-      if (!p_disp->dispctx->handles_transform)
-      {
-         /* Perform 'rotation' step
-          * > Note that rotation does not actually work...
-          * > It rotates the image all right, but distorts it
-          *   to fit the aspect of the bounding box while clipping
-          *   off any 'corners' that extend beyond the bounding box
-          * > Since the result is visual garbage, we disable
-          *   rotation entirely
-          * > But we still have to call gfx_display_rotate_z(),
-          *   or nothing will be drawn...
-          */
-         float cosine             = 1.0f; /* cos(rad)  = cos(0)  = 1.0f */
-         float sine               = 0.0f; /* sine(rad) = sine(0) = 0.0f */
-         gfx_display_rotate_z(p_disp, &mymat, cosine, sine, userdata);
-      }
-
-      /* Configure draw object
-       * > Note: Colour, width/height and position must
-       *   be set *after* drawing any shadow effects */
-      coords.vertices      = 4;
-      coords.vertex        = NULL;
-      coords.tex_coord     = NULL;
-      coords.lut_tex_coord = NULL;
-
-      draw.scale_factor    = 1.0f;
-      draw.rotation        = 0.0f;
-      draw.coords          = &coords;
-      draw.matrix_data     = &mymat;
-      draw.texture         = thumbnail->texture;
-      draw.prim_type       = GFX_DISPLAY_PRIM_TRIANGLESTRIP;
-      draw.pipeline_id     = 0;
-
-      /* Set thumbnail alignment within bounding box */
-      switch (alignment)
-      {
-         case GFX_THUMBNAIL_ALIGN_TOP:
-            /* Centred horizontally */
-            draw_x = x + ((float)width - draw_width) / 2.0f;
-            /* Drawn at top of bounding box */
-            draw_y = (float)video_height - y - draw_height;
-            break;
-         case GFX_THUMBNAIL_ALIGN_BOTTOM:
-            /* Centred horizontally */
-            draw_x = x + ((float)width - draw_width) / 2.0f;
-            /* Drawn at bottom of bounding box */
-            draw_y = (float)video_height - y - (float)height;
-            break;
-         case GFX_THUMBNAIL_ALIGN_LEFT:
-            /* Drawn at left side of bounding box */
-            draw_x = x;
-            /* Centred vertically */
-            draw_y = (float)video_height - y - draw_height - ((float)height - draw_height) / 2.0f;
-            break;
-         case GFX_THUMBNAIL_ALIGN_RIGHT:
-            /* Drawn at right side of bounding box */
-            draw_x = x + (float)width - draw_width;
-            /* Centred vertically */
-            draw_y = (float)video_height - y - draw_height - ((float)height - draw_height) / 2.0f;
-            break;
-         case GFX_THUMBNAIL_ALIGN_CENTRE:
-         default:
-            /* Centred both horizontally and vertically */
-            draw_x = x + ((float)width - draw_width) / 2.0f;
-            draw_y = (float)video_height - y - draw_height - ((float)height - draw_height) / 2.0f;
-            break;
-      }
-
-      /* Draw shadow effect, if required */
-      if (shadow)
-      {
-         /* Sanity check */
-         if ((shadow->type != GFX_THUMBNAIL_SHADOW_NONE) &&
-               (shadow->alpha > 0.0f))
-         {
-            float shadow_width;
-            float shadow_height;
-            float shadow_x;
-            float shadow_y;
-            float shadow_color[16] = {
-               0.0f, 0.0f, 0.0f, 1.0f,
-               0.0f, 0.0f, 0.0f, 1.0f,
-               0.0f, 0.0f, 0.0f, 1.0f,
-               0.0f, 0.0f, 0.0f, 1.0f
-            };
-            float shadow_alpha     = thumbnail_alpha;
-
-            /* Set shadow opacity */
-            if (shadow->alpha < 1.0f)
-               shadow_alpha *= shadow->alpha;
-
-            gfx_display_set_alpha(shadow_color, shadow_alpha);
-
-            /* Configure shadow based on effect type
-             * > Not using a switch() here, since we've
-             *   already eliminated GFX_THUMBNAIL_SHADOW_NONE */
-            if (shadow->type == GFX_THUMBNAIL_SHADOW_OUTLINE)
-            {
-               shadow_width  = draw_width  + (float)(shadow->outline.width * 2);
-               shadow_height = draw_height + (float)(shadow->outline.width * 2);
-               shadow_x      = draw_x - (float)shadow->outline.width;
-               shadow_y      = draw_y - (float)shadow->outline.width;
-            }
-            /* Default: GFX_THUMBNAIL_SHADOW_DROP */
-            else
-            {
-               shadow_width  = draw_width;
-               shadow_height = draw_height;
-               shadow_x      = draw_x + shadow->drop.x_offset;
-               shadow_y      = draw_y - shadow->drop.y_offset;
-            }
-
-            /* Apply shadow draw object configuration */
-            coords.color = (const float*)shadow_color;
-            draw.width   = (unsigned)shadow_width;
-            draw.height  = (unsigned)shadow_height;
-            draw.x       = shadow_x;
-            draw.y       = shadow_y;
-
-            /* Draw shadow */
-            if (draw.height > 0 && draw.width > 0)
-               if (dispctx->draw)
-                  dispctx->draw(&draw, userdata, video_width, video_height);
-         }
-      }
-
-      /* Final thumbnail draw object configuration */
-      coords.color = (const float*)thumbnail_color;
-      draw.width   = (unsigned)draw_width;
-      draw.height  = (unsigned)draw_height;
-      draw.x       = draw_x;
-      draw.y       = draw_y;
-
-      /* Draw thumbnail */
-      if (draw.height > 0 && draw.width > 0)
-         if (dispctx->draw)
-            dispctx->draw(&draw, userdata, video_width, video_height);
-
-      if (dispctx->blend_end)
-         dispctx->blend_end(userdata);
-   }
-}

+ 0 - 337
app/src/main/cpp/gfx/gfx_thumbnail.h

@@ -1,337 +0,0 @@
-/* Copyright  (C) 2010-2019 The RetroArch team
- *
- * ---------------------------------------------------------------------------------------
- * The following license statement only applies to this file (gfx_thumbnail.c).
- * ---------------------------------------------------------------------------------------
- *
- * Permission is hereby granted, free of charge,
- * to any person obtaining a copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#ifndef __GFX_THUMBNAIL_H
-#define __GFX_THUMBNAIL_H
-
-#include <retro_common_api.h>
-#include <libretro.h>
-
-#include <boolean.h>
-
-#include "gfx_animation.h"
-#include "gfx_thumbnail_path.h"
-
-RETRO_BEGIN_DECLS
-
-/* Defines the current status of an entry
- * thumbnail texture */
-enum gfx_thumbnail_status
-{
-   GFX_THUMBNAIL_STATUS_UNKNOWN = 0,
-   GFX_THUMBNAIL_STATUS_PENDING,
-   GFX_THUMBNAIL_STATUS_AVAILABLE,
-   GFX_THUMBNAIL_STATUS_MISSING
-};
-
-/* Defines thumbnail alignment within
- * gfx_thumbnail_draw() bounding box */
-enum gfx_thumbnail_alignment
-{
-   GFX_THUMBNAIL_ALIGN_CENTRE = 0,
-   GFX_THUMBNAIL_ALIGN_TOP,
-   GFX_THUMBNAIL_ALIGN_BOTTOM,
-   GFX_THUMBNAIL_ALIGN_LEFT,
-   GFX_THUMBNAIL_ALIGN_RIGHT
-};
-
-/* Defines all possible thumbnail shadow
- * effect types */
-enum gfx_thumbnail_shadow_type
-{
-   GFX_THUMBNAIL_SHADOW_NONE = 0,
-   GFX_THUMBNAIL_SHADOW_DROP,
-   GFX_THUMBNAIL_SHADOW_OUTLINE
-};
-
-enum gfx_thumbnail_flags
-{
-   GFX_THUMB_FLAG_FADE_ACTIVE = (1 << 0),
-   GFX_THUMB_FLAG_CORE_ASPECT = (1 << 1)
-};
-
-/* Holds all runtime parameters associated with
- * an entry thumbnail */
-typedef struct
-{
-   uintptr_t texture;
-   unsigned width;
-   unsigned height;
-   float alpha;
-   float delay_timer;
-   enum gfx_thumbnail_status status;
-   uint8_t flags;
-} gfx_thumbnail_t;
-
-/* Holds all configuration parameters associated
- * with a thumbnail shadow effect */
-typedef struct
-{
-   struct
-   {
-      unsigned width;
-   } outline;
-   float alpha;
-   struct
-   {
-      float x_offset;
-      float y_offset;
-   } drop;
-   enum gfx_thumbnail_shadow_type type;
-} gfx_thumbnail_shadow_t;
-
-/* Structure containing all gfx_thumbnail
- * variables */
-struct gfx_thumbnail_state
-{
-   /* Due to the asynchronous nature of thumbnail
-    * loading, it is quite possible to trigger a load
-    * then navigate to a different menu list before
-    * the load is complete/handled. As an additional
-    * safety check, we therefore tag the current menu
-    * list with counter value that is incremented whenever
-    * a list is cleared/set. This is sent as userdata when
-    * requesting a thumbnail, and the upload is only
-    * handled if the tag matches the most recent value
-    * at the time when the load completes */
-   uint64_t list_id;
-
-   /* When streaming thumbnails, to minimise the processing
-    * of unnecessary images (i.e. when scrolling rapidly through
-    * playlists), we delay loading until an entry has been on screen
-    * for at least gfx_thumbnail_delay ms */
-   float stream_delay;
-
-   /* Duration in ms of the thumbnail 'fade in' animation */
-   float fade_duration;
-
-   /* When true, 'fade in' animation will also be
-    * triggered for missing thumbnails */
-   bool fade_missing;
-};
-
-typedef struct gfx_thumbnail_state gfx_thumbnail_state_t;
-
-
-/* Setters */
-
-/* When streaming thumbnails, sets time in ms that an
- * entry must be on screen before an image load is
- * requested
- * > if 'delay' is negative, default value is set */
-void gfx_thumbnail_set_stream_delay(float delay);
-
-/* Sets duration in ms of the thumbnail 'fade in'
- * animation
- * > If 'duration' is negative, default value is set */
-void gfx_thumbnail_set_fade_duration(float duration);
-
-/* Specifies whether 'fade in' animation should be
- * triggered for missing thumbnails
- * > When 'true', allows menu driver to animate
- *   any 'thumbnail unavailable' notifications */
-void gfx_thumbnail_set_fade_missing(bool fade_missing);
-
-/* Core interface */
-
-/* When called, prevents the handling of any pending
- * thumbnail load requests
- * >> **MUST** be called before deleting any gfx_thumbnail_t
- *    objects passed to gfx_thumbnail_request() or
- *    gfx_thumbnail_process_stream(), otherwise
- *    heap-use-after-free errors *will* occur */
-void gfx_thumbnail_cancel_pending_requests(void);
-
-/* Requests loading of the specified thumbnail
- * - If operation fails, 'thumbnail->status' will be set to
- *   MUI_THUMBNAIL_STATUS_MISSING
- * - If operation is successful, 'thumbnail->status' will be
- *   set to MUI_THUMBNAIL_STATUS_PENDING
- * 'thumbnail' will be populated with texture info/metadata
- * once the image load is complete
- * NOTE 1: Must be called *after* gfx_thumbnail_set_system()
- *         and gfx_thumbnail_set_content*()
- * NOTE 2: 'playlist' and 'idx' are only required here for
- *         on-demand thumbnail download support
- *         (an annoyance...) */ 
-void gfx_thumbnail_request(
-      gfx_thumbnail_path_data_t *path_data, enum gfx_thumbnail_id thumbnail_id,
-      playlist_t *playlist, size_t idx, gfx_thumbnail_t *thumbnail,
-      unsigned gfx_thumbnail_upscale_threshold,
-      bool network_on_demand_thumbnails);
-
-/* Requests loading of a specific thumbnail image file
- * (may be used, for example, to load savestate images)
- * - If operation fails, 'thumbnail->status' will be set to
- *   MUI_THUMBNAIL_STATUS_MISSING
- * - If operation is successful, 'thumbnail->status' will be
- *   set to MUI_THUMBNAIL_STATUS_PENDING
- * 'thumbnail' will be populated with texture info/metadata
- * once the image load is complete */
-void gfx_thumbnail_request_file(
-      const char *file_path, gfx_thumbnail_t *thumbnail,
-      unsigned gfx_thumbnail_upscale_threshold);
-
-/* Resets (and free()s the current texture of) the
- * specified thumbnail */
-void gfx_thumbnail_reset(gfx_thumbnail_t *thumbnail);
-
-/* Stream processing */
-
-/* Requests loading of the specified thumbnail via
- * the stream interface
- * - Must be called on each frame for the duration
- *   that specified thumbnail is on-screen
- * - Actual load request is deferred by currently
- *   set stream delay
- * - Function becomes a no-op once load request is
- *   made
- * - Thumbnails loaded via this function must be
- *   deleted manually via gfx_thumbnail_reset()
- *   when they move off-screen
- * NOTE 1: Must be called *after* gfx_thumbnail_set_system()
- *         and gfx_thumbnail_set_content*()
- * NOTE 2: 'playlist' and 'idx' are only required here for
- *         on-demand thumbnail download support
- *         (an annoyance...)
- * NOTE 3: This function is intended for use in situations
- *         where each menu entry has a *single* thumbnail.
- *         If each entry has two thumbnails, use
- *         gfx_thumbnail_request_streams() for improved
- *         performance */
-void gfx_thumbnail_request_stream(
-      gfx_thumbnail_path_data_t *path_data,
-      gfx_animation_t *p_anim,
-      enum gfx_thumbnail_id thumbnail_id,
-      playlist_t *playlist, size_t idx,
-      gfx_thumbnail_t *thumbnail,
-      unsigned gfx_thumbnail_upscale_threshold,
-      bool network_on_demand_thumbnails);
-
-/* Requests loading of the specified thumbnails via
- * the stream interface
- * - Must be called on each frame for the duration
- *   that specified thumbnails are on-screen
- * - Actual load request is deferred by currently
- *   set stream delay
- * - Function becomes a no-op once load request is
- *   made
- * - Thumbnails loaded via this function must be
- *   deleted manually via gfx_thumbnail_reset()
- *   when they move off-screen
- * NOTE 1: Must be called *after* gfx_thumbnail_set_system()
- *         and gfx_thumbnail_set_content*()
- * NOTE 2: 'playlist' and 'idx' are only required here for
- *         on-demand thumbnail download support
- *         (an annoyance...)
- * NOTE 3: This function is intended for use in situations
- *         where each menu entry has *two* thumbnails.
- *         If each entry only has a single thumbnail, use
- *         gfx_thumbnail_request_stream() for improved
- *         performance */
-void gfx_thumbnail_request_streams(
-      gfx_thumbnail_path_data_t *path_data,
-      gfx_animation_t *p_anim,
-      playlist_t *playlist, size_t idx,
-      gfx_thumbnail_t *right_thumbnail,
-      gfx_thumbnail_t *left_thumbnail,
-      unsigned gfx_thumbnail_upscale_threshold,
-      bool network_on_demand_thumbnails);
-
-/* Handles streaming of the specified thumbnail as it moves
- * on/off screen
- * - Must be called each frame for every on-screen entry
- * - Must be called once for each entry as it moves off-screen
- *   (or can be called each frame - overheads are small)
- * NOTE 1: Must be called *after* gfx_thumbnail_set_system()
- * NOTE 2: This function calls gfx_thumbnail_set_content*()
- * NOTE 3: This function is intended for use in situations
- *         where each menu entry has a *single* thumbnail.
- *         If each entry has two thumbnails, use
- *         gfx_thumbnail_process_streams() for improved
- *         performance */
-void gfx_thumbnail_process_stream(
-      gfx_thumbnail_path_data_t *path_data,
-      gfx_animation_t *p_anim,
-      enum gfx_thumbnail_id thumbnail_id,
-      playlist_t *playlist, size_t idx,
-      gfx_thumbnail_t *thumbnail,
-      bool on_screen,
-      unsigned gfx_thumbnail_upscale_threshold,
-      bool network_on_demand_thumbnails);
-
-/* Handles streaming of the specified thumbnails as they move
- * on/off screen
- * - Must be called each frame for every on-screen entry
- * - Must be called once for each entry as it moves off-screen
- *   (or can be called each frame - overheads are small)
- * NOTE 1: Must be called *after* gfx_thumbnail_set_system()
- * NOTE 2: This function calls gfx_thumbnail_set_content*()
- * NOTE 3: This function is intended for use in situations
- *         where each menu entry has *two* thumbnails.
- *         If each entry only has a single thumbnail, use
- *         gfx_thumbnail_process_stream() for improved
- *         performance */
-void gfx_thumbnail_process_streams(
-      gfx_thumbnail_path_data_t *path_data,
-      gfx_animation_t *p_anim,
-      playlist_t *playlist, size_t idx,
-      gfx_thumbnail_t *right_thumbnail,
-      gfx_thumbnail_t *left_thumbnail,
-      bool on_screen,
-      unsigned gfx_thumbnail_upscale_threshold,
-      bool network_on_demand_thumbnails);
-
-/* Thumbnail rendering */
-
-/* Determines the actual screen dimensions of a
- * thumbnail when centred with aspect correct
- * scaling within a rectangle of (width x height) */
-void gfx_thumbnail_get_draw_dimensions(
-      gfx_thumbnail_t *thumbnail,
-      unsigned width, unsigned height, float scale_factor,
-      float *draw_width, float *draw_height);
-
-/* Draws specified thumbnail with specified alignment
- * (and aspect correct scaling) within a rectangle of
- * (width x height).
- * 'shadow' defines an optional shadow effect (may be
- * set to NULL if a shadow effect is not required).
- * NOTE: Setting scale_factor > 1.0f will increase the
- *       size of the thumbnail beyond the limits of the
- *       (width x height) rectangle (alignment + aspect
- *       correct scaling is preserved). Use with caution */
-void gfx_thumbnail_draw(
-      void *userdata,
-      unsigned video_width,
-      unsigned video_height,
-      gfx_thumbnail_t *thumbnail,
-      float x, float y, unsigned width, unsigned height,
-      enum gfx_thumbnail_alignment alignment,
-      float alpha, float scale_factor,
-      gfx_thumbnail_shadow_t *shadow);
-
-gfx_thumbnail_state_t *gfx_thumb_get_ptr(void);
-
-RETRO_END_DECLS
-
-#endif

+ 0 - 830
app/src/main/cpp/gfx/gfx_thumbnail_path.c

@@ -1,830 +0,0 @@
-/* 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"
-
-/* Used fixed size char arrays here, just to avoid
- * the inconvenience of having to calloc()/free()
- * each individual entry by hand... */
-struct gfx_thumbnail_path_data
-{
-   enum playlist_thumbnail_mode playlist_right_mode;
-   enum playlist_thumbnail_mode playlist_left_mode;
-   size_t playlist_index;
-   char system[PATH_MAX_LENGTH];
-   char content_path[PATH_MAX_LENGTH];
-   char content_label[PATH_MAX_LENGTH];
-   char content_core_name[PATH_MAX_LENGTH];
-   char content_db_name[PATH_MAX_LENGTH];
-   char content_img[PATH_MAX_LENGTH];
-   char right_path[PATH_MAX_LENGTH];
-   char left_path[PATH_MAX_LENGTH];
-};
-
-/* 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 */
-
-/* Fetches the thumbnail subdirectory (Named_Snaps,
- * Named_Titles, Named_Boxarts) corresponding to the
- * specified 'type index' (1, 2, 3).
- * Returns true if 'type index' is valid */
-bool gfx_thumbnail_get_sub_directory(
-      unsigned type_idx, const char **sub_directory)
-{
-   if (!sub_directory)
-      return false;
-   
-   switch (type_idx)
-   {
-      case 1:
-         *sub_directory = "Named_Snaps";
-         return true;
-      case 2:
-         *sub_directory = "Named_Titles";
-         return true;
-      case 3:
-         *sub_directory = "Named_Boxarts";
-         return true;
-      case 0:
-      default:
-         break;
-   }
-   
-   return false;
-}
-
-/* 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)
-{
-   unsigned        type          = 0;
-   unsigned gfx_thumbnails       = settings->uints.gfx_thumbnails;
-   unsigned menu_left_thumbnails = settings->uints.menu_left_thumbnails;
-   const char *val_off           = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF);
-   
-   if (!path_data)
-      return val_off;
-   
-   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:
-         return val_off;
-   }
-   
-   switch (type)
-   {
-      case 1:
-         return "Named_Snaps";
-      case 2:
-         return "Named_Titles";
-      case 3:
-         return "Named_Boxarts";
-      case 0:
-      default:
-         break;
-   }
-   
-   return val_off;
-}
-
-/* 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)
-{
-   settings_t          *settings = config_get_ptr();
-   unsigned gfx_thumbnails       = settings->uints.gfx_thumbnails;
-   unsigned menu_left_thumbnails = settings->uints.menu_left_thumbnails;
-   
-   if (!path_data)
-      return false;
-   
-   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 */
-
-/* Fills content_img field of path_data using existing
- * content_label field (for internal use only) */
-static void fill_content_img(gfx_thumbnail_path_data_t *path_data)
-{
-   char *scrub_char_pointer = NULL;
-   
-   /* Copy source label string */
-   strlcpy(path_data->content_img,
-         path_data->content_label, sizeof(path_data->content_img));
-   
-   /* Scrub characters that are not cross-platform and/or violate the
-    * No-Intro filename standard:
-    * http://datomatic.no-intro.org/stuff/The%20Official%20No-Intro%20Convention%20(20071030).zip
-    * Replace these characters in the entry name with underscores */
-   while ((scrub_char_pointer = 
-            strpbrk(path_data->content_img, "&*/:`\"<>?\\|")))
-      *scrub_char_pointer = '_';
-   
-   /* Add PNG extension */
-   strlcat(path_data->content_img, ".png", sizeof(path_data->content_img));
-}
-
-/* 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)
-   {
-      path_data->system[0] = path_data->system[2] = 'M';
-      path_data->system[1] = 'A';
-      path_data->system[3] = 'E';
-      path_data->system[4] = '\0';
-   }
-   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 */
-   fill_content_img(path_data);
-   
-   /* 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;
-   
-   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_nocompression(content_path),
-            "", sizeof(path_data->content_label));
-   
-   /* Determine content image name */
-   fill_content_img(path_data);
-
-   /* 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))
-   {
-      /* 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));
-      
-      /* >> Add content image */
-      thumbnail_path[0] = '\0';
-      fill_pathname_join_special(thumbnail_path, tmp_buf,
-            path_data->content_img, PATH_MAX_LENGTH * sizeof(char));
-   }
-   
-   /* 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 content path.
- * Returns true if content path is valid. */
-bool gfx_thumbnail_get_content_path(
-      gfx_thumbnail_path_data_t *path_data, const char **content_path)
-{
-   if (!path_data || !content_path)
-      return false;
-   if (string_is_empty(path_data->content_path))
-      return false;
-   
-   *content_path = path_data->content_path;
-   
-   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 database name.
- * Returns true if database name is valid. */
-bool gfx_thumbnail_get_db_name(
-      gfx_thumbnail_path_data_t *path_data, const char **db_name)
-{
-   if (!path_data || !db_name)
-      return false;
-   if (string_is_empty(path_data->content_db_name))
-      return false;
-   
-   *db_name = path_data->content_db_name;
-   
-   return true;
-}
-
-/* Fetches current thumbnail image name
- * (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)
-{
-   if (!path_data || !img_name)
-      return false;
-   if (string_is_empty(path_data->content_img))
-      return false;
-   
-   *img_name = path_data->content_img;
-   
-   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);
-}
-
-/* Fetches current playlist index. */
-size_t gfx_thumbnail_get_playlist_index(
-      gfx_thumbnail_path_data_t *path_data)
-{
-   return (path_data) ? path_data->playlist_index : 0;
-}

+ 0 - 157
app/src/main/cpp/gfx/gfx_thumbnail_path.h

@@ -1,157 +0,0 @@
-/* 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.
- */
-
-#ifndef __GFX_THUMBNAIL_PATH_H
-#define __GFX_THUMBNAIL_PATH_H
-
-#include <retro_common_api.h>
-#include <libretro.h>
-
-#include <boolean.h>
-
-#include "../playlist.h"
-
-RETRO_BEGIN_DECLS
-
-/* Note: This implementation reflects the current
- * setup of:
- * - menu_driver_set_thumbnail_system()
- * - menu_driver_set_thumbnail_content()
- * - menu_driver_update_thumbnail_path()
- * This is absolutely not the best way to handle things,
- * but I have no interest in rewriting the existing
- * menu code... */
-
-enum gfx_thumbnail_id
-{
-   GFX_THUMBNAIL_RIGHT = 0,
-   GFX_THUMBNAIL_LEFT
-};
-
-/* Prevent direct access to gfx_thumbnail_path_data_t members */
-typedef struct gfx_thumbnail_path_data gfx_thumbnail_path_data_t;
-
-/* 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);
-
-/* Resets thumbnail path data
- * (blanks all internal string containers) */
-void gfx_thumbnail_path_reset(gfx_thumbnail_path_data_t *path_data);
-
-/* Utility Functions */
-
-/* Fetches the thumbnail subdirectory (Named_Snaps,
- * Named_Titles, Named_Boxarts) corresponding to the
- * specified 'type index' (1, 2, 3).
- * Returns true if 'type index' is valid */
-bool gfx_thumbnail_get_sub_directory(unsigned type_idx, const char **sub_directory);
-
-/* 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);
-
-/* 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);
-
-/* 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);
-
-/* 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);
-
-/* 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);
-
-/* 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);
-
-/* 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);
-
-/* 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);
-
-/* Fetches current content path.
- * Returns true if content path is valid. */
-bool gfx_thumbnail_get_content_path(gfx_thumbnail_path_data_t *path_data, const char **content_path);
-
-/* 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);
-
-/* 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);
-
-/* Fetches current database name.
- * Returns true if database name is valid. */
-bool gfx_thumbnail_get_db_name(gfx_thumbnail_path_data_t *path_data, const char **db_name);
-
-/* Fetches current thumbnail image name
- * (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);
-
-/* 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);
-
-/* Fetches current playlist index. */
-size_t gfx_thumbnail_get_playlist_index(gfx_thumbnail_path_data_t *path_data);
-
-RETRO_END_DECLS
-
-#endif

+ 0 - 2179
app/src/main/cpp/gfx/gfx_widgets.c

@@ -1,2179 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2014-2017 - Jean-André Santoni
- *  Copyright (C) 2015-2018 - Andre Leiradella
- *  Copyright (C) 2018-2020 - natinusala
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <retro_miscellaneous.h>
-#include <retro_inline.h>
-
-#ifdef HAVE_CONFIG_H
-#include "../config.h"
-#endif
-
-#include <queues/fifo_queue.h>
-#include <file/file_path.h>
-#include <streams/file_stream.h>
-#include <string/stdstring.h>
-#include <retro_math.h>
-
-#include "gfx_display.h"
-#include "gfx_widgets.h"
-#include "font_driver.h"
-
-#include "../configuration.h"
-#include "../file_path_special.h"
-#include "../msg_hash.h"
-
-#include "../tasks/task_content.h"
-#include "../tasks/tasks_internal.h"
-
-#define BASE_FONT_SIZE 32.0f
-
-#define MSG_QUEUE_FONT_SIZE (BASE_FONT_SIZE * 0.69f)
-
-/* Icons */
-static const char 
-*gfx_widgets_icons_names[MENU_WIDGETS_ICON_LAST]         = {
-   "menu_pause.png",
-   "menu_frameskip.png",
-   "menu_rewind.png",
-   "resume.png",
-
-   "menu_hourglass.png",
-   "menu_check.png",
-
-   "menu_info.png",
-
-   "menu_achievements.png"
-};
-
-static dispgfx_widget_t dispwidget_st = {0}; /* uint64_t alignment */
-
-static void INLINE gfx_widgets_font_free(gfx_widget_font_data_t *font_data)
-{
-   if (font_data->font)
-      gfx_display_font_free(font_data->font);
-
-   font_data->font        = NULL;
-   font_data->usage_count = 0;
-}
-
-/* Widgets list */
-const static gfx_widget_t* const widgets[] = {
-#ifdef HAVE_NETWORKING
-   &gfx_widget_netplay_chat,
-   &gfx_widget_netplay_ping,
-#endif
-#ifdef HAVE_SCREENSHOTS
-   &gfx_widget_screenshot,
-#endif
-   &gfx_widget_volume,
-#ifdef HAVE_CHEEVOS
-   &gfx_widget_achievement_popup,
-   &gfx_widget_leaderboard_display,
-#endif
-   &gfx_widget_generic_message,
-   &gfx_widget_libretro_message,
-   &gfx_widget_progress_message,
-   &gfx_widget_load_content_animation
-};
-
-#if defined(HAVE_MENU) && defined(HAVE_XMB)
-static float gfx_display_get_widget_pixel_scale(
-      gfx_display_t *p_disp,
-      settings_t *settings,
-      unsigned width, unsigned height, bool fullscreen)
-{
-   static unsigned last_width                          = 0;
-   static unsigned last_height                         = 0;
-   static float scale                                  = 0.0f;
-   static bool scale_cached                            = false;
-   bool scale_updated                                  = false;
-   static float last_menu_scale_factor                 = 0.0f;
-   static enum menu_driver_id_type last_menu_driver_id = MENU_DRIVER_ID_UNKNOWN;
-   static float adjusted_scale                         = 1.0f;
-   bool gfx_widget_scale_auto                          = settings->bools.menu_widget_scale_auto;
-#if (defined(RARCH_CONSOLE) || defined(RARCH_MOBILE))
-   float menu_widget_scale_factor                      = settings->floats.menu_widget_scale_factor;
-#else
-   float menu_widget_scale_factor_fullscreen           = settings->floats.menu_widget_scale_factor;
-   float menu_widget_scale_factor_windowed             = settings->floats.menu_widget_scale_factor_windowed;
-   float menu_widget_scale_factor                      = fullscreen ?
-         menu_widget_scale_factor_fullscreen : menu_widget_scale_factor_windowed;
-#endif
-   float menu_scale_factor                             = menu_widget_scale_factor;
-
-   if (gfx_widget_scale_auto)
-   {
-#ifdef HAVE_RGUI
-      /* When using RGUI, _menu_scale_factor
-       * is ignored
-       * > If we are not using a widget scale factor override,
-       *   just set menu_scale_factor to 1.0 */
-      if (p_disp->menu_driver_id == MENU_DRIVER_ID_RGUI)
-         menu_scale_factor                             = 1.0f;
-      else
-#endif
-      {
-         float _menu_scale_factor                      = 
-            settings->floats.menu_scale_factor;
-         menu_scale_factor                             = _menu_scale_factor;
-      }
-   }
-
-   /* We need to perform a square root here, which
-    * can be slow on some platforms (not *slow*, but
-    * it involves enough work that it's worth trying
-    * to optimise). We therefore cache the pixel scale,
-    * and only update on first run or when the video
-    * size changes */
-   if (!scale_cached ||
-       (width  != last_width) ||
-       (height != last_height))
-   {
-      /* Baseline reference is a 1080p display */
-      scale = (float)(
-            sqrt((double)((width * width) + (height * height))) /
-            DIAGONAL_PIXELS_1080P);
-
-      scale_cached  = true;
-      scale_updated = true;
-      last_width    = width;
-      last_height   = height;
-   }
-
-   /* Adjusted scale calculation may also be slow, so
-    * only update if something changes */
-   if (scale_updated ||
-       (menu_scale_factor != last_menu_scale_factor) ||
-       (p_disp->menu_driver_id != last_menu_driver_id))
-   {
-      adjusted_scale         = gfx_display_get_adjusted_scale(
-            p_disp,
-            scale, menu_scale_factor, width);
-      last_menu_scale_factor = menu_scale_factor;
-      last_menu_driver_id    = p_disp->menu_driver_id;
-   }
-
-   return adjusted_scale;
-}
-#endif
-
-static void msg_widget_msg_transition_animation_done(void *userdata)
-{
-   disp_widget_msg_t *msg = (disp_widget_msg_t*)userdata;
-
-   if (msg->msg)
-      free(msg->msg);
-   msg->msg = NULL;
-
-   if (msg->msg_new)
-      msg->msg = strdup(msg->msg_new);
-
-   msg->msg_transition_animation = 0.0f;
-}
-
-void gfx_widgets_msg_queue_push(
-      retro_task_t *task,
-      const char *msg,
-      unsigned duration,
-      char *title,
-      enum message_queue_icon icon,
-      enum message_queue_category category,
-      unsigned prio, bool flush,
-      bool menu_is_alive)
-{
-   disp_widget_msg_t    *msg_widget = NULL;
-   dispgfx_widget_t *p_dispwidget   = &dispwidget_st;
-
-   if (FIFO_WRITE_AVAIL_NONPTR(p_dispwidget->msg_queue) > 0)
-   {
-      /* Get current msg if it exists */
-      if (task && task->frontend_userdata)
-      {
-         msg_widget           = (disp_widget_msg_t*)task->frontend_userdata;
-         /* msg_widgets can be passed between tasks */
-         msg_widget->task_ptr = task;
-      }
-
-      /* Spawn a new notification */
-      if (!msg_widget)
-      {
-         const char *title                      = msg;
-
-         msg_widget                             = (disp_widget_msg_t*)malloc(sizeof(*msg_widget));
-
-         msg_widget->msg                        = NULL;
-         msg_widget->msg_new                    = NULL;
-         msg_widget->msg_transition_animation   = 0.0f;
-         msg_widget->msg_len                    = 0;
-         msg_widget->duration                   = duration;
-
-         msg_widget->text_height                = 0;
-
-         msg_widget->offset_y                   = 0;
-         msg_widget->alpha                      = 1.0f;
-
-         msg_widget->width                      = 0;
-
-         msg_widget->expiration_timer           = 0;
-
-         msg_widget->task_ptr                   = task;
-         msg_widget->task_count                 = 0;
-
-         msg_widget->task_progress              = 0;
-         msg_widget->task_ident                 = 0;
-
-         msg_widget->unfold                     = 0.0f;
-
-         msg_widget->hourglass_rotation         = 0.0f;
-         msg_widget->hourglass_timer            = 0.0f;
-         msg_widget->flags                      = 0;
-
-         if (!(p_dispwidget->flags & DISPGFX_WIDGET_FLAG_MSG_QUEUE_HAS_ICONS))
-         {
-            msg_widget->flags                  |=  DISPWIDG_FLAG_UNFOLDED;
-            msg_widget->flags                  &= ~DISPWIDG_FLAG_UNFOLDING;
-            msg_widget->unfold                  = 1.0f;
-         }
-
-         if (task)
-         {
-            title = msg_widget->msg             = strdup(task->title);
-            msg_widget->msg_new                 = strdup(title);
-            msg_widget->msg_len                 = strlen(title);
-
-            if (!string_is_empty(task->error))
-               msg_widget->flags               |= DISPWIDG_FLAG_TASK_ERROR;
-            if (task->cancelled)
-               msg_widget->flags               |= DISPWIDG_FLAG_TASK_CANCELLED;
-            if (task->finished)
-               msg_widget->flags               |= DISPWIDG_FLAG_TASK_FINISHED;
-            msg_widget->task_progress           = task->progress;
-            msg_widget->task_ident              = task->ident;
-            msg_widget->task_count              = 1;
-
-            msg_widget->flags                  |= DISPWIDG_FLAG_UNFOLDED;
-
-            msg_widget->width                   = font_driver_get_message_width(
-                  p_dispwidget->gfx_widget_fonts.msg_queue.font,
-                  title,
-                  msg_widget->msg_len, 1.0f) +
-                  p_dispwidget->simple_widget_padding / 2;
-
-            task->frontend_userdata             = msg_widget;
-
-            msg_widget->hourglass_rotation      = 0;
-         }
-         else
-         {
-            /* Compute rect width, wrap if necessary */
-            /* Single line text > two lines text > two lines 
-             * text with expanded width */
-            size_t title_length                 = strlen(title);
-            char *msg                           = NULL;
-            size_t msg_len                      = 0;
-            unsigned width                      = menu_is_alive 
-               ? p_dispwidget->msg_queue_default_rect_width_menu_alive 
-               : p_dispwidget->msg_queue_default_rect_width;
-            unsigned text_width                 = font_driver_get_message_width(
-                  p_dispwidget->gfx_widget_fonts.msg_queue.font,
-                  title,
-                  title_length,
-                  1.0f);
-            msg_widget->text_height             = p_dispwidget->gfx_widget_fonts.msg_queue.line_height;
-            /* 1 byte uses for inserting '\n' */
-            msg_len                             = title_length + 1 + 1;
-            if (!(msg = (char *)malloc(msg_len)))
-               return;
-            msg[0] = '\0';
-
-            /* Text is too wide, split it into two lines */
-            if (text_width > width)
-            {
-               /* If the second line is too short, the widget may
-                * look unappealing - ensure that second line is at
-                * least 25% of the total width */
-               if ((text_width - (text_width >> 2)) < width)
-                  width = text_width - (text_width >> 2);
-
-               word_wrap(msg, msg_len, title, title_length,
-                     (int)((title_length * width) / text_width),
-                     100, 2);
-
-               msg_widget->text_height *= 2;
-            }
-            else
-            {
-               width                            = text_width;
-               strlcpy(msg, title, msg_len);
-            }
-
-            msg_widget->msg                     = msg;
-            msg_widget->msg_len                 = strlen(msg);
-            msg_widget->width                   = width + 
-               p_dispwidget->simple_widget_padding / 2;
-         }
-
-         fifo_write(&p_dispwidget->msg_queue,
-               &msg_widget, sizeof(msg_widget));
-      }
-      /* Update task info */
-      else
-      {
-         if (msg_widget->flags & DISPWIDG_FLAG_EXPIRATION_TIMER_STARTED)
-         {
-            uintptr_t _tag     = (uintptr_t)&msg_widget->expiration_timer;
-            gfx_animation_kill_by_tag(&_tag);
-            msg_widget->flags &= ~DISPWIDG_FLAG_EXPIRATION_TIMER_STARTED;
-         }
-
-         if (!string_is_equal(task->title, msg_widget->msg_new))
-         {
-            size_t len;
-            unsigned new_width;
-
-            if (msg_widget->msg_new)
-            {
-               free(msg_widget->msg_new);
-               msg_widget->msg_new                 = NULL;
-            }
-
-            title       = msg_widget->msg_new      = strdup(task->title);
-
-            len         = strlen(title);
-            new_width   = font_driver_get_message_width(
-                  p_dispwidget->gfx_widget_fonts.msg_queue.font,
-                  title,
-                  len,
-                  1.0f);
-
-            msg_widget->msg_len                    = len;
-            msg_widget->msg_transition_animation   = 0;
-
-            if (!task->alternative_look)
-            {
-               gfx_animation_ctx_entry_t entry;
-
-               entry.easing_enum    = EASING_OUT_QUAD;
-               entry.tag            = (uintptr_t)msg_widget;
-               entry.duration       = MSG_QUEUE_ANIMATION_DURATION*2;
-               entry.target_value   = p_dispwidget->msg_queue_height / 2.0f;
-               entry.subject        = &msg_widget->msg_transition_animation;
-               entry.cb             = msg_widget_msg_transition_animation_done;
-               entry.userdata       = msg_widget;
-
-               gfx_animation_push(&entry);
-            }
-            else
-               msg_widget_msg_transition_animation_done(msg_widget);
-
-            msg_widget->task_count++;
-
-            msg_widget->width = new_width;
-         }
-
-         if (!string_is_empty(task->error))
-            msg_widget->flags               |= DISPWIDG_FLAG_TASK_ERROR;
-         if (task->cancelled)
-            msg_widget->flags               |= DISPWIDG_FLAG_TASK_CANCELLED;
-         if (task->finished)
-            msg_widget->flags               |= DISPWIDG_FLAG_TASK_FINISHED;
-         msg_widget->task_progress     = task->progress;
-      }
-   }
-}
-
-static void gfx_widgets_unfold_end(void *userdata)
-{
-   disp_widget_msg_t *unfold        = (disp_widget_msg_t*)userdata;
-   dispgfx_widget_t *p_dispwidget   = &dispwidget_st;
-
-   unfold->flags                   &= ~DISPWIDG_FLAG_UNFOLDING;
-   p_dispwidget->flags             &= ~DISPGFX_WIDGET_FLAG_MOVING;
-}
-
-static void gfx_widgets_move_end(void *userdata)
-{
-   dispgfx_widget_t *p_dispwidget   = &dispwidget_st;
-
-   if (userdata)
-   {
-      gfx_animation_ctx_entry_t entry;
-      disp_widget_msg_t *unfold    = (disp_widget_msg_t*)userdata;
-
-      entry.cb                     = gfx_widgets_unfold_end;
-      entry.duration               = MSG_QUEUE_ANIMATION_DURATION;
-      entry.easing_enum            = EASING_OUT_QUAD;
-      entry.subject                = &unfold->unfold;
-      entry.tag                    = (uintptr_t)unfold;
-      entry.target_value           = 1.0f;
-      entry.userdata               = unfold;
-
-      gfx_animation_push(&entry);
-
-      unfold->flags               |= DISPWIDG_FLAG_UNFOLDED
-                                   | DISPWIDG_FLAG_UNFOLDING;
-   }
-   else
-      p_dispwidget->flags         &= ~DISPGFX_WIDGET_FLAG_MOVING;
-}
-
-static void gfx_widgets_msg_queue_expired(void *userdata)
-{
-   disp_widget_msg_t *msg = (disp_widget_msg_t *)userdata;
-
-   if (msg && !(msg->flags & DISPWIDG_FLAG_EXPIRED))
-      msg->flags  |= DISPWIDG_FLAG_EXPIRED;
-}
-
-static void gfx_widgets_msg_queue_move(dispgfx_widget_t *p_dispwidget)
-{
-   int i;
-   float y = 0;
-   /* there should always be one and only one unfolded message */
-   disp_widget_msg_t *unfold        = NULL; 
-
-#ifdef HAVE_THREADS
-   slock_lock(p_dispwidget->current_msgs_lock);
-#endif
-
-   for (i = (int)(p_dispwidget->current_msgs_size - 1); i >= 0; i--)
-   {
-      disp_widget_msg_t* msg = p_dispwidget->current_msgs[i];
-
-      if (!msg || (msg->flags & DISPWIDG_FLAG_DYING))
-         continue;
-
-      y += p_dispwidget->msg_queue_height 
-         / (msg->task_ptr ? 2 : 1) + p_dispwidget->msg_queue_spacing;
-
-      if (!(msg->flags & DISPWIDG_FLAG_UNFOLDED))
-         unfold = msg;
-
-      if (msg->offset_y != y)
-      {
-         gfx_animation_ctx_entry_t entry;
-
-         entry.cb             = (i == 0) ? gfx_widgets_move_end : NULL;
-         entry.duration       = MSG_QUEUE_ANIMATION_DURATION;
-         entry.easing_enum    = EASING_OUT_QUAD;
-         entry.subject        = &msg->offset_y;
-         entry.tag            = (uintptr_t)msg;
-         entry.target_value   = y;
-         entry.userdata       = unfold;
-
-         gfx_animation_push(&entry);
-
-         p_dispwidget->flags |= DISPGFX_WIDGET_FLAG_MOVING;
-      }
-   }
-
-#ifdef HAVE_THREADS
-   slock_unlock(p_dispwidget->current_msgs_lock);
-#endif
-}
-
-static void gfx_widgets_msg_queue_free(
-      dispgfx_widget_t *p_dispwidget,
-      disp_widget_msg_t *msg)
-{
-   uintptr_t tag = (uintptr_t)msg;
-   uintptr_t hourglass_timer_tag = (uintptr_t)&msg->hourglass_timer;
-
-   if (msg->task_ptr)
-   {
-      /* remove the reference the task has of ourself
-         only if the task is not finished already
-         (finished tasks are freed before the widget) */
-      if (     !(msg->flags & DISPWIDG_FLAG_TASK_FINISHED) 
-            && !(msg->flags & DISPWIDG_FLAG_TASK_ERROR) 
-            && !(msg->flags & DISPWIDG_FLAG_TASK_CANCELLED))
-         msg->task_ptr->frontend_userdata = NULL;
-
-      /* update tasks count */
-      p_dispwidget->msg_queue_tasks_count--;
-   }
-
-   /* Kill all animations */
-   gfx_animation_kill_by_tag(&hourglass_timer_tag);
-   gfx_animation_kill_by_tag(&tag);
-
-   /* Kill all timers */
-   if (msg->flags & DISPWIDG_FLAG_EXPIRATION_TIMER_STARTED)
-   {
-      uintptr_t _tag = (uintptr_t)&msg->expiration_timer;
-      gfx_animation_kill_by_tag(&_tag);
-   }
-
-   /* Free it */
-   if (msg->msg)
-      free(msg->msg);
-
-   if (msg->msg_new)
-      free(msg->msg_new);
-
-   p_dispwidget->flags &= ~DISPGFX_WIDGET_FLAG_MOVING;
-}
-
-static void gfx_widgets_msg_queue_kill_end(void *userdata)
-{
-   disp_widget_msg_t* msg;
-   dispgfx_widget_t *p_dispwidget   = &dispwidget_st;
-
-#ifdef HAVE_THREADS
-   slock_lock(p_dispwidget->current_msgs_lock);
-#endif
-
-   if ((msg = p_dispwidget->current_msgs[p_dispwidget->msg_queue_kill]))
-   {
-      int i;
-      /* Remove it from the list */
-      for (i = p_dispwidget->msg_queue_kill; i < (int)(p_dispwidget->current_msgs_size - 1); i++)
-         p_dispwidget->current_msgs[i] = p_dispwidget->current_msgs[i + 1];
-
-      p_dispwidget->current_msgs_size--;
-      p_dispwidget->current_msgs[p_dispwidget->current_msgs_size] = NULL;
-
-      /* clean up the item */
-      gfx_widgets_msg_queue_free(p_dispwidget, msg);
-
-      /* free the associated memory */
-      free(msg);
-   }
-
-#ifdef HAVE_THREADS
-   slock_unlock(p_dispwidget->current_msgs_lock);
-#endif
-}
-
-static void gfx_widgets_msg_queue_kill(
-      dispgfx_widget_t *p_dispwidget,
-      unsigned idx)
-{
-   gfx_animation_ctx_entry_t entry;
-   disp_widget_msg_t *msg = p_dispwidget->current_msgs[idx];
-
-   if (!msg)
-      return;
-
-   p_dispwidget->flags         |= DISPGFX_WIDGET_FLAG_MOVING;
-   msg->flags                  |= DISPWIDG_FLAG_DYING;
-
-   p_dispwidget->msg_queue_kill = idx;
-
-   /* Drop down */
-   entry.cb                     = NULL;
-   entry.duration               = MSG_QUEUE_ANIMATION_DURATION;
-   entry.easing_enum            = EASING_OUT_QUAD;
-   entry.tag                    = (uintptr_t)msg;
-   entry.userdata               = NULL;
-   entry.subject                = &msg->offset_y;
-   entry.target_value           = msg->offset_y - 
-      p_dispwidget->msg_queue_height / 4;
-
-   gfx_animation_push(&entry);
-
-   /* Fade out */
-   entry.cb                     = gfx_widgets_msg_queue_kill_end;
-   entry.subject                = &msg->alpha;
-   entry.target_value           = 0.0f;
-
-   gfx_animation_push(&entry);
-
-   /* Move all messages back to their correct position */
-   if (p_dispwidget->current_msgs_size != 0)
-      gfx_widgets_msg_queue_move(p_dispwidget);
-}
-
-void gfx_widgets_draw_icon(
-      void *userdata,
-      void *data_disp,
-      unsigned video_width,
-      unsigned video_height,
-      unsigned icon_width,
-      unsigned icon_height,
-      uintptr_t texture,
-      float x, float y,
-      float radians,
-      float cosine,
-      float sine,
-      float *color)
-{
-   gfx_display_ctx_draw_t draw;
-   struct video_coords coords;
-   math_matrix_4x4 mymat;
-   gfx_display_t            *p_disp  = (gfx_display_t*)data_disp;
-   gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
-
-   if (!texture)
-      return;
-
-   if (!p_disp->dispctx->handles_transform)
-      gfx_display_rotate_z(p_disp, &mymat, cosine, sine, userdata);
-
-   coords.vertices      = 4;
-   coords.vertex        = NULL;
-   coords.tex_coord     = NULL;
-   coords.lut_tex_coord = NULL;
-   coords.color         = color;
-
-   draw.x               = x;
-   draw.y               = video_height - y - icon_height;
-   draw.width           = icon_width;
-   draw.height          = icon_height;
-   draw.scale_factor    = 1.0f;
-   draw.rotation        = radians;
-   draw.coords          = &coords;
-   draw.matrix_data     = &mymat;
-   draw.texture         = texture;
-   draw.prim_type       = GFX_DISPLAY_PRIM_TRIANGLESTRIP;
-   draw.pipeline_id     = 0;
-
-   if (draw.height > 0 && draw.width > 0)
-      if (dispctx->draw)
-         dispctx->draw(&draw, userdata, video_width, video_height);
-}
-
-void gfx_widgets_draw_text(
-      gfx_widget_font_data_t* font_data,
-      const char *text,
-      float x, float y,
-      int width, int height,
-      uint32_t color,
-      enum text_alignment text_align,
-      bool draw_outside)
-{
-   if (!font_data || string_is_empty(text))
-      return;
-
-   gfx_display_draw_text(
-         font_data->font,
-         text,
-         x, y,
-         width, height,
-         color,
-         text_align,
-         1.0f,
-         false,
-         0.0f,
-         draw_outside);
-
-   font_data->usage_count++;
-}
-
-void gfx_widgets_flush_text(
-      unsigned video_width, unsigned video_height,
-      gfx_widget_font_data_t* font_data)
-{
-   /* Flushing is slow - only do it if font
-    * has actually been used */
-   if (!font_data || (font_data->usage_count == 0))
-      return;
-
-   font_driver_flush(video_width, video_height, font_data->font);
-   font_data->raster_block.carr.coords.vertices = 0;
-   font_data->usage_count                       = 0;
-}
-
-float gfx_widgets_get_thumbnail_scale_factor(
-      const float dst_width, const float dst_height,
-      const float image_width, const float image_height)
-{
-   float dst_ratio      = dst_width   / dst_height;
-   float image_ratio    = image_width / image_height;
-
-   if (dst_ratio > image_ratio)
-      return (dst_height / image_height);
-   return (dst_width / image_width);
-}
-
-static void gfx_widgets_start_msg_expiration_timer(
-      disp_widget_msg_t *msg_widget, unsigned duration)
-{
-   gfx_timer_ctx_entry_t timer;
-
-   timer.cb       = gfx_widgets_msg_queue_expired;
-   timer.duration = duration;
-   timer.userdata = msg_widget;
-
-   gfx_animation_timer_start(&msg_widget->expiration_timer, &timer);
-
-   msg_widget->flags                   |=
-      DISPWIDG_FLAG_EXPIRATION_TIMER_STARTED;
-}
-
-static void gfx_widgets_hourglass_tick(void *userdata);
-
-static void gfx_widgets_hourglass_end(void *userdata)
-{
-   gfx_timer_ctx_entry_t timer;
-   disp_widget_msg_t *msg  = (disp_widget_msg_t*)userdata;
-
-   msg->hourglass_rotation = 0.0f;
-
-   timer.cb                = gfx_widgets_hourglass_tick;
-   timer.duration          = HOURGLASS_INTERVAL;
-   timer.userdata          = msg;
-
-   gfx_animation_timer_start(&msg->hourglass_timer, &timer);
-}
-
-static void gfx_widgets_hourglass_tick(void *userdata)
-{
-   gfx_animation_ctx_entry_t entry;
-   disp_widget_msg_t *msg = (disp_widget_msg_t*)userdata;
-   uintptr_t          tag = (uintptr_t)msg;
-
-   entry.easing_enum      = EASING_OUT_QUAD;
-   entry.tag              = tag;
-   entry.duration         = HOURGLASS_DURATION;
-   entry.target_value     = -(2 * M_PI);
-   entry.subject          = &msg->hourglass_rotation;
-   entry.cb               = gfx_widgets_hourglass_end;
-   entry.userdata         = msg;
-
-   gfx_animation_push(&entry);
-}
-
-static void gfx_widgets_font_init(
-      gfx_display_t *p_disp,
-      dispgfx_widget_t *p_dispwidget,
-      gfx_widget_font_data_t *font_data,
-      bool is_threaded, char *font_path, float font_size)
-{
-   int                glyph_width   = 0;
-   float                scaled_size = font_size * 
-      p_dispwidget->last_scale_factor;
-
-   /* Free existing font */
-   if (font_data->font)
-   {
-      gfx_display_font_free(font_data->font);
-      font_data->font = NULL;
-   }
-
-   /* Get approximate glyph width */
-   font_data->glyph_width = scaled_size * (3.0f / 4.0f);
-
-   /* Create font */
-   font_data->font = gfx_display_font_file(p_disp,
-         font_path, scaled_size, is_threaded);
-
-   /* Get font metadata */
-   glyph_width = font_driver_get_message_width(font_data->font, "a", 1, 1.0f);
-   if (glyph_width > 0)
-      font_data->glyph_width     = (float)glyph_width;
-   font_data->line_height        = (float)font_driver_get_line_height(font_data->font, 1.0f);
-   font_data->line_ascender      = (float)font_driver_get_line_ascender(font_data->font, 1.0f);
-   font_data->line_descender     = (float)font_driver_get_line_descender(font_data->font, 1.0f);
-   font_data->line_centre_offset = (float)font_driver_get_line_centre_offset(font_data->font, 1.0f);
-
-   font_data->usage_count        = 0;
-}
-
-
-static void gfx_widgets_layout(
-      gfx_display_t *p_disp,
-      dispgfx_widget_t *p_dispwidget,
-      bool is_threaded, const char *dir_assets, char *font_path)
-{
-   size_t i;
-
-   /* Initialise fonts */
-   if (string_is_empty(font_path))
-   {
-      char font_file[PATH_MAX_LENGTH];
-      /* Create regular font */
-      gfx_widgets_font_init(p_disp, p_dispwidget,
-            &p_dispwidget->gfx_widget_fonts.regular,
-            is_threaded, p_dispwidget->ozone_regular_font_path, BASE_FONT_SIZE);
-      /* Create bold font */
-      gfx_widgets_font_init(p_disp, p_dispwidget,
-            &p_dispwidget->gfx_widget_fonts.bold,
-            is_threaded, p_dispwidget->ozone_bold_font_path, BASE_FONT_SIZE);
-
-      /* Create msg_queue font */
-      switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE))
-      {
-         case RETRO_LANGUAGE_ARABIC:
-         case RETRO_LANGUAGE_PERSIAN:
-            fill_pathname_join_special(font_file, p_dispwidget->assets_pkg_dir, "fallback-font.ttf", sizeof(font_file));
-            break;
-         case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
-         case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
-            fill_pathname_join_special(font_file, p_dispwidget->assets_pkg_dir, "chinese-fallback-font.ttf", sizeof(font_file));
-            break;
-         case RETRO_LANGUAGE_KOREAN:
-            fill_pathname_join_special(font_file, p_dispwidget->assets_pkg_dir, "korean-fallback-font.ttf", sizeof(font_file));
-            break;
-         default:
-            strlcpy(font_file, p_dispwidget->ozone_regular_font_path, sizeof(font_file));
-            break;
-      }
-      gfx_widgets_font_init(p_disp, p_dispwidget,
-            &p_dispwidget->gfx_widget_fonts.msg_queue,
-            is_threaded, font_file, MSG_QUEUE_FONT_SIZE);
-   }
-   else
-   {
-      /* Load fonts from user-supplied path */
-      gfx_widgets_font_init(p_disp, p_dispwidget,
-            &p_dispwidget->gfx_widget_fonts.regular,
-            is_threaded, font_path, BASE_FONT_SIZE);
-      gfx_widgets_font_init(p_disp, p_dispwidget,
-            &p_dispwidget->gfx_widget_fonts.bold,
-            is_threaded, font_path, BASE_FONT_SIZE);
-      gfx_widgets_font_init(p_disp, p_dispwidget,
-            &p_dispwidget->gfx_widget_fonts.msg_queue,
-            is_threaded, font_path, MSG_QUEUE_FONT_SIZE);
-   }
-
-   /* Calculate dimensions */
-   p_dispwidget->simple_widget_padding            = p_dispwidget->gfx_widget_fonts.regular.line_height * 2.0f/3.0f;
-   p_dispwidget->simple_widget_height             = p_dispwidget->gfx_widget_fonts.regular.line_height + p_dispwidget->simple_widget_padding;
-
-   p_dispwidget->msg_queue_height                 = p_dispwidget->gfx_widget_fonts.msg_queue.line_height * 2.5f * (BASE_FONT_SIZE / MSG_QUEUE_FONT_SIZE);
-
-   if (p_dispwidget->flags & DISPGFX_WIDGET_FLAG_MSG_QUEUE_HAS_ICONS)
-   {
-#if 0
-      p_dispwidget->msg_queue_icon_size_y         = p_dispwidget->msg_queue_height 
-         * 1.2347826087f; /* original image is 280x284 */
-      p_dispwidget->msg_queue_icon_size_x         = 0.98591549295f * p_dispwidget->msg_queue_icon_size_y;
-#else
-      p_dispwidget->msg_queue_icon_size_y         = p_dispwidget->msg_queue_height * 1.2f;
-      p_dispwidget->msg_queue_icon_size_x         = p_dispwidget->msg_queue_icon_size_y;
-#endif
-   }
-   else
-   {
-      p_dispwidget->msg_queue_icon_size_x         = p_dispwidget->simple_widget_padding * 1.5f;
-      p_dispwidget->msg_queue_icon_size_y         = 0;
-   }
-
-   p_dispwidget->msg_queue_spacing                = p_dispwidget->msg_queue_height / 3.3f;
-   p_dispwidget->msg_queue_rect_start_x           = p_dispwidget->msg_queue_spacing + p_dispwidget->msg_queue_icon_size_x;
-   p_dispwidget->msg_queue_internal_icon_size     = p_dispwidget->msg_queue_icon_size_y;
-   p_dispwidget->msg_queue_internal_icon_offset   = (p_dispwidget->msg_queue_icon_size_y - p_dispwidget->msg_queue_internal_icon_size) / 2;
-   p_dispwidget->msg_queue_icon_offset_y          = (p_dispwidget->msg_queue_icon_size_y - p_dispwidget->msg_queue_height) / 2;
-   p_dispwidget->msg_queue_scissor_start_x        = p_dispwidget->msg_queue_spacing + p_dispwidget->msg_queue_icon_size_x - (p_dispwidget->msg_queue_icon_size_x * 0.28928571428f);
-
-   if (p_dispwidget->flags & DISPGFX_WIDGET_FLAG_MSG_QUEUE_HAS_ICONS)
-      p_dispwidget->msg_queue_regular_padding_x   = p_dispwidget->simple_widget_padding / 2;
-   else
-      p_dispwidget->msg_queue_regular_padding_x   = p_dispwidget->simple_widget_padding;
-
-   p_dispwidget->msg_queue_task_rect_start_x      = p_dispwidget->msg_queue_rect_start_x - p_dispwidget->msg_queue_icon_size_x;
-
-   p_dispwidget->msg_queue_task_text_start_x      = p_dispwidget->msg_queue_task_rect_start_x + p_dispwidget->msg_queue_height / 2;
-
-   if (!p_dispwidget->gfx_widgets_icons_textures[MENU_WIDGETS_ICON_HOURGLASS])
-      p_dispwidget->msg_queue_task_text_start_x         -= 
-         p_dispwidget->gfx_widget_fonts.msg_queue.glyph_width * 2.0f;
-
-   p_dispwidget->msg_queue_regular_text_start            = p_dispwidget->msg_queue_rect_start_x;
-
-   p_dispwidget->msg_queue_task_hourglass_x              = p_dispwidget->msg_queue_rect_start_x - p_dispwidget->msg_queue_icon_size_x;
-
-   p_dispwidget->generic_message_height    = p_dispwidget->gfx_widget_fonts.regular.line_height * 2.0f;
-
-   p_dispwidget->msg_queue_default_rect_width_menu_alive = p_dispwidget
-      ->gfx_widget_fonts.msg_queue.glyph_width * 40.0f;
-   p_dispwidget->msg_queue_default_rect_width            = p_dispwidget->last_video_width 
-      - p_dispwidget->msg_queue_regular_text_start - (2 * p_dispwidget->simple_widget_padding);
-
-   p_dispwidget->divider_width_1px    = 1;
-   if (p_dispwidget->last_scale_factor > 1.0f)
-      p_dispwidget->divider_width_1px = (unsigned)(p_dispwidget->last_scale_factor + 0.5f);
-
-   for (i = 0; i < ARRAY_SIZE(widgets); i++)
-   {
-      const gfx_widget_t* widget = widgets[i];
-
-      if (widget->layout)
-         widget->layout(p_dispwidget,
-               is_threaded, dir_assets, font_path);
-   }
-}
-
-
-void gfx_widgets_iterate(
-      void *data_disp,
-      void *settings_data,
-      unsigned width, unsigned height, bool fullscreen,
-      const char *dir_assets, char *font_path,
-      bool is_threaded)
-{
-   size_t i;
-   dispgfx_widget_t *p_dispwidget   = &dispwidget_st;
-   /* c.f. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
-    * On some platforms (e.g. 32-bit x86 without SSE),
-    * gcc can produce inconsistent floating point results
-    * depending upon optimisation level. This can break
-    * floating point variable comparisons. A workaround is
-    * to declare the affected variable as 'volatile', which
-    * disables optimisations and removes excess precision
-    * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323#c87) */
-   volatile float scale_factor      = 0.0f;
-   gfx_display_t *p_disp            = (gfx_display_t*)data_disp;
-   settings_t *settings             = (settings_t*)settings_data;
-#ifdef HAVE_XMB
-   enum menu_driver_id_type type    = p_disp->menu_driver_id;
-   if (type == MENU_DRIVER_ID_XMB)
-      scale_factor                  = gfx_display_get_widget_pixel_scale(p_disp, settings, width, height, fullscreen);
-   else
-#endif
-      scale_factor                  = gfx_display_get_dpi_scale(
-            p_disp,
-            settings, width, height, fullscreen, true);
-
-   /* Check whether screen dimensions or menu scale
-    * factor have changed */
-   if ((scale_factor != p_dispwidget->last_scale_factor) ||
-       (width        != p_dispwidget->last_video_width) ||
-       (height       != p_dispwidget->last_video_height))
-   {
-      p_dispwidget->last_scale_factor = scale_factor;
-      p_dispwidget->last_video_width  = width;
-      p_dispwidget->last_video_height = height;
-
-      /* Note: We don't need a full context reset here
-       * > Just rescale layout, and reset frame time counter */
-      gfx_widgets_layout(p_disp, p_dispwidget,
-            is_threaded, dir_assets, font_path);
-      video_driver_monitor_reset();
-   }
-
-   for (i = 0; i < ARRAY_SIZE(widgets); i++)
-   {
-      const gfx_widget_t* widget = widgets[i];
-
-      if (widget->iterate)
-         widget->iterate(p_dispwidget,
-               width, height, fullscreen,
-               dir_assets, font_path, is_threaded);
-   }
-
-   /* Messages queue */
-
-   /* Consume one message if available */
-   if ((FIFO_READ_AVAIL_NONPTR(p_dispwidget->msg_queue) > 0)
-         && !(p_dispwidget->flags & DISPGFX_WIDGET_FLAG_MOVING)
-         && (p_dispwidget->current_msgs_size < ARRAY_SIZE(p_dispwidget->current_msgs)))
-   {
-      disp_widget_msg_t *msg_widget = NULL;
-
-#ifdef HAVE_THREADS
-      slock_lock(p_dispwidget->current_msgs_lock);
-#endif
-
-      if (p_dispwidget->current_msgs_size < ARRAY_SIZE(p_dispwidget->current_msgs))
-      {
-         if (FIFO_READ_AVAIL_NONPTR(p_dispwidget->msg_queue) > 0)
-            fifo_read(&p_dispwidget->msg_queue,
-                  &msg_widget, sizeof(msg_widget));
-
-         if (msg_widget)
-         {
-            /* Task messages always appear from the bottom of the screen, append it */
-            if (p_dispwidget->msg_queue_tasks_count == 0 || msg_widget->task_ptr)
-               p_dispwidget->current_msgs[p_dispwidget->current_msgs_size] = msg_widget;
-            /* Regular messages are always above tasks, make room and insert it */
-            else
-            {
-               unsigned idx = (unsigned)(p_dispwidget->current_msgs_size -
-                  p_dispwidget->msg_queue_tasks_count);
-               for (i = p_dispwidget->current_msgs_size; i > idx; i--)
-                  p_dispwidget->current_msgs[i] = p_dispwidget->current_msgs[i - 1];
-               p_dispwidget->current_msgs[idx] = msg_widget;
-            }
-
-            p_dispwidget->current_msgs_size++;
-         }
-      }
-
-#ifdef HAVE_THREADS
-      slock_unlock(p_dispwidget->current_msgs_lock);
-#endif
-
-      if (msg_widget)
-      {
-         /* Start expiration timer if not associated to a task */
-         if (!msg_widget->task_ptr)
-         {
-            if (!(msg_widget->flags & DISPWIDG_FLAG_EXPIRATION_TIMER_STARTED))
-               gfx_widgets_start_msg_expiration_timer(
-                  msg_widget, MSG_QUEUE_ANIMATION_DURATION * 2
-                  + msg_widget->duration);
-         }
-         /* Else, start hourglass animation timer */
-         else
-         {
-            p_dispwidget->msg_queue_tasks_count++;
-            gfx_widgets_hourglass_end(msg_widget);
-         }
-
-         if (p_dispwidget->current_msgs_size != 0)
-            gfx_widgets_msg_queue_move(p_dispwidget);
-      }
-   }
-
-   /* Kill first expired message */
-   /* Start expiration timer of dead tasks */
-   for (i = 0; i < p_dispwidget->current_msgs_size; i++)
-   {
-      disp_widget_msg_t *msg_widget = p_dispwidget->current_msgs[i];
-
-      if (!msg_widget)
-         continue;
-
-      if (msg_widget->task_ptr 
-            &&   ((msg_widget->flags & DISPWIDG_FLAG_TASK_FINISHED) 
-               || (msg_widget->flags & DISPWIDG_FLAG_TASK_CANCELLED)))
-         if (!(msg_widget->flags & DISPWIDG_FLAG_EXPIRATION_TIMER_STARTED))
-            gfx_widgets_start_msg_expiration_timer(msg_widget, TASK_FINISHED_DURATION);
-
-      if (      (msg_widget->flags   & DISPWIDG_FLAG_EXPIRED)
-            && !(p_dispwidget->flags & DISPGFX_WIDGET_FLAG_MOVING))
-      {
-         gfx_widgets_msg_queue_kill(p_dispwidget,
-               (unsigned)i);
-         break;
-      }
-   }
-}
-
-static int gfx_widgets_draw_indicator(
-      dispgfx_widget_t *p_dispwidget,
-      gfx_display_t            *p_disp,
-      gfx_display_ctx_driver_t *dispctx,
-      void *userdata, 
-      unsigned video_width,
-      unsigned video_height,
-      uintptr_t icon, int y, int top_right_x_advance, 
-      enum msg_hash_enums msg)
-{
-   unsigned width;
-
-   gfx_display_set_alpha(p_dispwidget->backdrop_orig, DEFAULT_BACKDROP);
-
-   if (icon)
-   {
-      unsigned height = p_dispwidget->simple_widget_height * 2;
-      width           = height;
-
-      gfx_display_draw_quad(
-            p_disp,
-            userdata,
-            video_width, video_height,
-            top_right_x_advance - width, y,
-            width, height,
-            video_width, video_height,
-            p_dispwidget->backdrop_orig,
-            NULL
-      );
-
-      gfx_display_set_alpha(p_dispwidget->pure_white, 1.0f);
-
-      if (dispctx && dispctx->blend_begin)
-         dispctx->blend_begin(userdata);
-      gfx_widgets_draw_icon(
-            userdata,
-            p_disp,
-            video_width,
-            video_height,
-            width,
-            height,
-            icon,
-            top_right_x_advance - width, y,
-            0.0f, /* rad */
-            1.0f, /* cos(rad)   = cos(0)  = 1.0f */
-            0.0f, /* sine(rad)  = sine(0) = 0.0f */
-            p_dispwidget->pure_white
-            );
-      if (dispctx && dispctx->blend_end)
-         dispctx->blend_end(userdata);
-   }
-   else
-   {
-      unsigned height       = p_dispwidget->simple_widget_height;
-      const char *txt       = msg_hash_to_str(msg);
-
-      width = font_driver_get_message_width(
-            p_dispwidget->gfx_widget_fonts.regular.font,
-            txt,
-            strlen(txt), 1.0f) 
-         + p_dispwidget->simple_widget_padding * 2;
-
-      gfx_display_draw_quad(
-            p_disp,
-            userdata,
-            video_width, video_height,
-            top_right_x_advance - width, y,
-            width, height,
-            video_width, video_height,
-            p_dispwidget->backdrop_orig,
-            NULL
-      );
-
-      gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.regular,
-            txt,
-            top_right_x_advance - width 
-            + p_dispwidget->simple_widget_padding,
-            y + (height / 2.0f) + 
-            p_dispwidget->gfx_widget_fonts.regular.line_centre_offset,
-            video_width, video_height,
-            0xFFFFFFFF, TEXT_ALIGN_LEFT,
-            false);
-   }
-
-   return width;
-}
-
-static void gfx_widgets_draw_task_msg(
-      dispgfx_widget_t *p_dispwidget,
-      gfx_display_t            *p_disp,
-      gfx_display_ctx_driver_t *dispctx,
-      disp_widget_msg_t *msg,
-      void *userdata,
-      unsigned video_width,
-      unsigned video_height)
-{
-   /* Color of first progress bar in a task message */
-   static float msg_queue_task_progress_1[16]               = 
-      COLOR_HEX_TO_FLOAT(0x397869, 1.0f);
-   /* Color of second progress bar in a task message 
-    * (for multiple tasks with same message) */
-   static float msg_queue_task_progress_2[16]               = 
-      COLOR_HEX_TO_FLOAT(0x317198, 1.0f);
-   unsigned text_color;
-   unsigned bar_width;
-
-   unsigned rect_x;
-   unsigned rect_y;
-   unsigned rect_width;
-   unsigned rect_height;
-   float text_y_base;
-
-   float *msg_queue_current_background;
-   float *msg_queue_current_bar;
-
-   char task_percentage[256];
-   bool draw_msg_new                 = false;
-   unsigned task_percentage_offset   = 0;
-
-   if (msg->msg_new)
-      draw_msg_new                   = !string_is_equal(msg->msg_new, msg->msg);
-
-   task_percentage_offset            = 
-      p_dispwidget->gfx_widget_fonts.msg_queue.glyph_width 
-      * ((msg->flags & DISPWIDG_FLAG_TASK_ERROR) ? 12 : 5) 
-      + p_dispwidget->simple_widget_padding * 1.25f; /*11 = STRLEN_CONST("Task failed") + 1 */
-
-   if (msg->flags & DISPWIDG_FLAG_TASK_FINISHED)
-   {
-      if (msg->flags & DISPWIDG_FLAG_TASK_ERROR) /* TODO/FIXME - localize */
-         strlcpy(task_percentage, "Task failed", sizeof(task_percentage));
-      else
-      {
-         task_percentage[0] = ' ';
-         task_percentage[1] = '\0';
-      }
-   }
-   else if (msg->task_progress >= 0 && msg->task_progress <= 100)
-   {
-      task_percentage[0] = '\0';
-      snprintf(task_percentage, sizeof(task_percentage),
-            "%i%%", msg->task_progress);
-   }
-
-   rect_width = p_dispwidget->simple_widget_padding 
-      + msg->width 
-      + task_percentage_offset;
-   bar_width  = rect_width * msg->task_progress/100.0f;
-   text_color = COLOR_TEXT_ALPHA(0xFFFFFF00, (unsigned)(msg->alpha*255.0f));
-
-   /* Rect */
-   if (msg->flags & DISPWIDG_FLAG_TASK_FINISHED)
-      if (msg->task_count == 1)
-         msg_queue_current_background = msg_queue_task_progress_1;
-      else
-         msg_queue_current_background = msg_queue_task_progress_2;
-   else
-      if (msg->task_count == 1)
-         msg_queue_current_background = p_dispwidget->msg_queue_bg;
-      else
-         msg_queue_current_background = msg_queue_task_progress_1;
-
-   rect_x      = p_dispwidget->msg_queue_rect_start_x - p_dispwidget->msg_queue_icon_size_x;
-   rect_y      = video_height - msg->offset_y;
-   rect_height = p_dispwidget->msg_queue_height / 2;
-
-   gfx_display_set_alpha(msg_queue_current_background, msg->alpha);
-   gfx_display_draw_quad(
-         p_disp,
-         userdata,
-         video_width, video_height,
-         rect_x, rect_y,
-         rect_width, rect_height,
-         video_width, video_height,
-         msg_queue_current_background,
-         NULL
-         );
-
-   /* Progress bar */
-   if (    !(msg->flags & DISPWIDG_FLAG_TASK_FINISHED)
-         && (msg->task_progress >= 0) 
-         && (msg->task_progress <= 100))
-   {
-      if (msg->task_count == 1)
-         msg_queue_current_bar = msg_queue_task_progress_1;
-      else
-         msg_queue_current_bar = msg_queue_task_progress_2;
-
-      gfx_display_set_alpha(msg_queue_current_bar, 1.0f);
-      gfx_display_draw_quad(
-            p_disp,
-            userdata,
-            video_width, video_height,
-            p_dispwidget->msg_queue_task_rect_start_x, video_height - msg->offset_y,
-            bar_width, rect_height,
-            video_width, video_height,
-            msg_queue_current_bar,
-            NULL
-            );
-   }
-
-   /* Icon */
-   gfx_display_set_alpha(p_dispwidget->pure_white, msg->alpha);
-   if (dispctx && dispctx->blend_begin)
-      dispctx->blend_begin(userdata);
-   {
-      float radians = 0.0f; /* rad                        */
-      float cosine  = 1.0f; /* cos(rad)  = cos(0)  = 1.0f */
-      float sine    = 0.0f; /* sine(rad) = sine(0) = 0.0f */
-      if (!(msg->flags & DISPWIDG_FLAG_TASK_FINISHED))
-         radians    = msg->hourglass_rotation;
-      gfx_widgets_draw_icon(
-            userdata,
-            p_disp,
-            video_width,
-            video_height,
-            p_dispwidget->msg_queue_height / 2,
-            p_dispwidget->msg_queue_height / 2,
-            p_dispwidget->gfx_widgets_icons_textures[
-            (msg->flags & DISPWIDG_FLAG_TASK_FINISHED)
-            ? MENU_WIDGETS_ICON_CHECK 
-            : MENU_WIDGETS_ICON_HOURGLASS],
-            p_dispwidget->msg_queue_task_hourglass_x,
-            video_height - msg->offset_y,
-            radians,
-            cosine,
-            sine,
-            p_dispwidget->pure_white);
-   }
-   if (dispctx && dispctx->blend_end)
-      dispctx->blend_end(userdata);
-
-   /* Text */
-   text_y_base = video_height 
-      - msg->offset_y 
-      + p_dispwidget->msg_queue_height / 4.0f 
-      + p_dispwidget->gfx_widget_fonts.msg_queue.line_centre_offset;
-
-   if (draw_msg_new)
-   {
-      gfx_widgets_flush_text(video_width, video_height,
-            &p_dispwidget->gfx_widget_fonts.msg_queue);
-
-      gfx_display_scissor_begin(p_disp,
-            userdata,
-            video_width, video_height,
-            rect_x, rect_y, rect_width, rect_height);
-
-      gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.msg_queue,
-            msg->msg_new,
-            p_dispwidget->msg_queue_task_text_start_x,
-            text_y_base 
-            - p_dispwidget->msg_queue_height / 2.0f 
-            + msg->msg_transition_animation,
-            video_width, video_height,
-            text_color,
-            TEXT_ALIGN_LEFT,
-            true);
-   }
-
-   gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.msg_queue,
-         msg->msg,
-         p_dispwidget->msg_queue_task_text_start_x,
-         text_y_base + msg->msg_transition_animation,
-         video_width, video_height,
-         text_color,
-         TEXT_ALIGN_LEFT,
-         true);
-
-   if (draw_msg_new)
-   {
-      gfx_widgets_flush_text(video_width, video_height,
-            &p_dispwidget->gfx_widget_fonts.msg_queue);
-      if (dispctx && dispctx->scissor_end)
-         dispctx->scissor_end(userdata,
-               video_width, video_height);
-   }
-
-   /* Progress text */
-   text_color = COLOR_TEXT_ALPHA(0xFFFFFF00, (unsigned)(msg->alpha/2*255.0f));
-   gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.msg_queue,
-      task_percentage,
-      p_dispwidget->msg_queue_rect_start_x - p_dispwidget->msg_queue_icon_size_x + rect_width - 
-      p_dispwidget->gfx_widget_fonts.msg_queue.glyph_width,
-      text_y_base,
-      video_width, video_height,
-      text_color,
-      TEXT_ALIGN_RIGHT,
-      true);
-}
-
-static void gfx_widgets_draw_regular_msg(
-      dispgfx_widget_t *p_dispwidget,
-      gfx_display_t *p_disp,
-      gfx_display_ctx_driver_t *dispctx,
-      disp_widget_msg_t *msg,
-      void *userdata,
-      unsigned video_width,
-      unsigned video_height)
-{
-   static float msg_queue_info[16] = COLOR_HEX_TO_FLOAT(0x12ACF8, 1.0f);
-   static float msg_queue_bar[16]  = COLOR_HEX_TO_FLOAT(0xDDDDDD, 1.0f);
-   unsigned bar_width;
-   unsigned bar_margin;
-   unsigned text_color;
-   static float last_alpha = 0.0f;
-
-   msg->flags             &= ~DISPWIDG_FLAG_UNFOLDING;
-   msg->flags             |=  DISPWIDG_FLAG_UNFOLDED;
-
-   if (last_alpha != msg->alpha)
-   {
-      /* Icon */
-      gfx_display_set_alpha(msg_queue_info, msg->alpha);
-      gfx_display_set_alpha(p_dispwidget->pure_white, msg->alpha);
-      gfx_display_set_alpha(p_dispwidget->msg_queue_bg, msg->alpha);
-      last_alpha = msg->alpha;
-   }
-
-   if (    !(msg->flags & DISPWIDG_FLAG_UNFOLDED) 
-         || (msg->flags & DISPWIDG_FLAG_UNFOLDING))
-   {
-      gfx_widgets_flush_text(video_width, video_height,
-            &p_dispwidget->gfx_widget_fonts.regular);
-      gfx_widgets_flush_text(video_width, video_height,
-            &p_dispwidget->gfx_widget_fonts.bold);
-      gfx_widgets_flush_text(video_width, video_height,
-            &p_dispwidget->gfx_widget_fonts.msg_queue);
-
-     gfx_display_scissor_begin(p_disp,
-           userdata,
-           video_width, video_height,
-           p_dispwidget->msg_queue_scissor_start_x, 0,
-           (p_dispwidget->msg_queue_scissor_start_x + msg->width - 
-            p_dispwidget->simple_widget_padding * 2) 
-           * msg->unfold, video_height);
-   }
-
-   /* Background */
-   bar_width  = p_dispwidget->simple_widget_padding + msg->width + p_dispwidget->msg_queue_icon_size_x;
-   bar_margin = p_dispwidget->simple_widget_padding * 0.15f;
-
-   gfx_display_draw_quad(
-         p_disp,
-         userdata,
-         video_width,
-         video_height,
-         p_dispwidget->msg_queue_rect_start_x - p_dispwidget->msg_queue_icon_size_x,
-         video_height - msg->offset_y,
-         bar_width - bar_margin,
-         p_dispwidget->msg_queue_height,
-         video_width,
-         video_height,
-         p_dispwidget->msg_queue_bg,
-         NULL
-         );
-
-   gfx_display_draw_quad(
-         p_disp,
-         userdata,
-         video_width,
-         video_height,
-         p_dispwidget->msg_queue_rect_start_x - p_dispwidget->msg_queue_icon_size_x - bar_margin,
-         video_height - msg->offset_y,
-         bar_margin,
-         p_dispwidget->msg_queue_height,
-         video_width,
-         video_height,
-         msg_queue_bar,
-         NULL
-         );
-
-   /* Text */
-   text_color = COLOR_TEXT_ALPHA(0xFFFFFF00, (unsigned)(msg->alpha*255.0f));
-
-   gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.msg_queue,
-      msg->msg,
-      p_dispwidget->msg_queue_regular_text_start,
-      video_height - msg->offset_y + (p_dispwidget->msg_queue_height - msg->text_height)/2.0f + p_dispwidget->gfx_widget_fonts.msg_queue.line_ascender,
-      video_width, video_height,
-      text_color,
-      TEXT_ALIGN_LEFT,
-      true);
-
-   if (    !(msg->flags & DISPWIDG_FLAG_UNFOLDED) 
-         || (msg->flags & DISPWIDG_FLAG_UNFOLDING))
-   {
-      gfx_widgets_flush_text(video_width, video_height, &p_dispwidget->gfx_widget_fonts.regular);
-      gfx_widgets_flush_text(video_width, video_height, &p_dispwidget->gfx_widget_fonts.bold);
-      gfx_widgets_flush_text(video_width, video_height, &p_dispwidget->gfx_widget_fonts.msg_queue);
-
-      if (dispctx && dispctx->scissor_end)
-         dispctx->scissor_end(userdata,
-               video_width, video_height);
-   }
-
-   if (p_dispwidget->flags & DISPGFX_WIDGET_FLAG_MSG_QUEUE_HAS_ICONS)
-   {
-      if (dispctx && dispctx->blend_begin)
-         dispctx->blend_begin(userdata);
-
-      gfx_widgets_draw_icon(
-            userdata,
-            p_disp,
-            video_width,
-            video_height,
-            p_dispwidget->msg_queue_icon_size_x,
-            p_dispwidget->msg_queue_icon_size_y,
-            p_dispwidget->gfx_widgets_icons_textures[MENU_WIDGETS_ICON_INFO],
-            p_dispwidget->msg_queue_spacing,
-            video_height - msg->offset_y  - p_dispwidget->msg_queue_icon_offset_y,
-            0.0f, /* rad                         */
-            1.0f, /* cos(rad)   = cos(0)  = 1.0f */
-            0.0f, /* sine(rad)  = sine(0) = 0.0f */
-            msg_queue_info);
-
-      if (dispctx && dispctx->blend_end)
-         dispctx->blend_end(userdata);
-   }
-}
-
-static void INLINE gfx_widgets_font_bind(gfx_widget_font_data_t *font_data)
-{
-   font_driver_bind_block(font_data->font, &font_data->raster_block);
-   font_data->raster_block.carr.coords.vertices = 0;
-   font_data->usage_count                       = 0;
-}
-
-static void INLINE gfx_widgets_font_unbind(gfx_widget_font_data_t *font_data)
-{
-   font_driver_bind_block(font_data->font, NULL);
-}
-
-void gfx_widgets_frame(void *data)
-{
-   size_t i;
-   video_frame_info_t *video_info   = (video_frame_info_t*)data;
-   gfx_display_t            *p_disp = (gfx_display_t*)video_info->disp_userdata;
-   gfx_display_ctx_driver_t *dispctx= p_disp->dispctx;
-   dispgfx_widget_t *p_dispwidget   = (dispgfx_widget_t*)video_info->widgets_userdata;
-   bool fps_show                    = video_info->fps_show;
-   bool framecount_show             = video_info->framecount_show;
-   bool memory_show                 = video_info->memory_show;
-   bool core_status_msg_show        = video_info->core_status_msg_show;
-   void *userdata                   = video_info->userdata;
-   unsigned video_width             = video_info->width;
-   unsigned video_height            = video_info->height;
-   bool widgets_is_paused           = video_info->widgets_is_paused;
-   bool widgets_is_fastforwarding   = video_info->widgets_is_fast_forwarding;
-   bool widgets_is_rewinding        = video_info->widgets_is_rewinding;
-   bool runloop_is_slowmotion       = video_info->runloop_is_slowmotion;
-   bool menu_screensaver_active     = video_info->menu_screensaver_active;
-   bool notifications_hidden        = video_info->notifications_hidden ||
-         video_info->msg_queue_delay;
-   int top_right_x_advance          = video_width;
-
-   p_dispwidget->gfx_widgets_frame_count++;
-
-   /* If menu screensaver is active or notifications are hidden, draw nothing */
-   if (menu_screensaver_active || notifications_hidden)
-      return;
-
-   video_driver_set_viewport(video_width, video_height, true, false);
-
-   /* Font setup */
-   gfx_widgets_font_bind(&p_dispwidget->gfx_widget_fonts.regular);
-   gfx_widgets_font_bind(&p_dispwidget->gfx_widget_fonts.bold);
-   gfx_widgets_font_bind(&p_dispwidget->gfx_widget_fonts.msg_queue);
-
-#ifdef HAVE_TRANSLATE
-   /* AI Service overlay */
-   if (p_dispwidget->ai_service_overlay_state > 0)
-   {
-      float outline_color[16] = {
-      0.00, 1.00, 0.00, 1.00,
-      0.00, 1.00, 0.00, 1.00,
-      0.00, 1.00, 0.00, 1.00,
-      0.00, 1.00, 0.00, 1.00,
-      };
-      gfx_display_set_alpha(p_dispwidget->pure_white, 1.0f);
-
-      if (p_dispwidget->ai_service_overlay_texture)
-      {
-         if (dispctx->blend_begin)
-            dispctx->blend_begin(userdata);
-         gfx_widgets_draw_icon(
-               userdata,
-               p_disp,
-               video_width,
-               video_height,
-               video_width,
-               video_height,
-               p_dispwidget->ai_service_overlay_texture,
-               0,
-               0,
-               0.0f, /* rad                         */
-               1.0f, /* cos(rad)   = cos(0)  = 1.0f */
-               0.0f, /* sine(rad)  = sine(0) = 0.0f */
-               p_dispwidget->pure_white
-               );
-         if (dispctx->blend_end)
-            dispctx->blend_end(userdata);
-      }
-
-      /* top line */
-      gfx_display_draw_quad(
-            p_disp,
-            userdata,
-            video_width, video_height,
-            0, 0,
-            video_width,
-            p_dispwidget->divider_width_1px,
-            video_width,
-            video_height,
-            outline_color,
-            NULL
-            );
-      /* bottom line */
-      gfx_display_draw_quad(
-            p_disp,
-            userdata,
-            video_width, video_height,
-            0,
-            video_height - p_dispwidget->divider_width_1px,
-            video_width,
-            p_dispwidget->divider_width_1px,
-            video_width,
-            video_height,
-            outline_color,
-            NULL
-            );
-      /* left line */
-      gfx_display_draw_quad(
-            p_disp,
-            userdata,
-            video_width,
-            video_height,
-            0,
-            0,
-            p_dispwidget->divider_width_1px,
-            video_height,
-            video_width,
-            video_height,
-            outline_color,
-            NULL
-            );
-      /* right line */
-      gfx_display_draw_quad(
-            p_disp,
-            userdata,
-            video_width, video_height,
-            video_width - p_dispwidget->divider_width_1px,
-            0,
-            p_dispwidget->divider_width_1px,
-            video_height,
-            video_width,
-            video_height,
-            outline_color,
-            NULL
-            );
-
-      if (p_dispwidget->ai_service_overlay_state == 2)
-          p_dispwidget->ai_service_overlay_state = 3;
-   }
-#endif
-
-   /* Status Text (fps, framecount, memory, core status message) */
-   if (     fps_show
-         || framecount_show
-         || memory_show
-         || core_status_msg_show
-         )
-   {
-      const char *text      = *p_dispwidget->gfx_widgets_status_text == '\0'
-         ? msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE)
-         : p_dispwidget->gfx_widgets_status_text;
-
-      int text_width        = font_driver_get_message_width(
-            p_dispwidget->gfx_widget_fonts.regular.font,
-            text,
-            strlen(text), 1.0f);
-      int total_width       = text_width
-         + p_dispwidget->simple_widget_padding * 2;
-
-      int status_text_x     = top_right_x_advance
-         - p_dispwidget->simple_widget_padding - text_width;
-      /* Ensure that left hand side of text does
-       * not bleed off the edge of the screen */
-      if (status_text_x < 0)
-         status_text_x      = 0;
-
-      gfx_display_set_alpha(p_dispwidget->backdrop_orig, DEFAULT_BACKDROP);
-
-      gfx_display_draw_quad(
-            p_disp,
-            userdata,
-            video_width,
-            video_height,
-            top_right_x_advance - total_width, 0,
-            total_width,
-            p_dispwidget->simple_widget_height,
-            video_width,
-            video_height,
-            p_dispwidget->backdrop_orig,
-            NULL
-            );
-
-      gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.regular,
-            text,
-            status_text_x,
-            p_dispwidget->simple_widget_height / 2.0f
-            + p_dispwidget->gfx_widget_fonts.regular.line_centre_offset,
-            video_width, video_height,
-            0xFFFFFFFF,
-            TEXT_ALIGN_LEFT,
-            true);
-   }
-
-   /* Indicators */
-   if (widgets_is_paused)
-      top_right_x_advance -= gfx_widgets_draw_indicator(
-            p_dispwidget,
-            p_disp,
-            dispctx,
-            userdata,
-            video_width,
-            video_height,
-            p_dispwidget->gfx_widgets_icons_textures[
-            MENU_WIDGETS_ICON_PAUSED],
-            (fps_show 
-             ? p_dispwidget->simple_widget_height 
-             : 0),
-            top_right_x_advance,
-            MSG_PAUSED);
-
-   if (widgets_is_fastforwarding)
-      top_right_x_advance -= gfx_widgets_draw_indicator(
-            p_dispwidget,
-            p_disp,
-            dispctx,
-            userdata,
-            video_width,
-            video_height,
-            p_dispwidget->gfx_widgets_icons_textures[
-            MENU_WIDGETS_ICON_FAST_FORWARD],
-            (fps_show ? p_dispwidget->simple_widget_height : 0),
-            top_right_x_advance,
-            MSG_FAST_FORWARD);
-
-   if (widgets_is_rewinding)
-      top_right_x_advance -= gfx_widgets_draw_indicator(
-            p_dispwidget,
-            p_disp,
-            dispctx,
-            userdata,
-            video_width,
-            video_height,
-            p_dispwidget->gfx_widgets_icons_textures[
-            MENU_WIDGETS_ICON_REWIND],
-            (fps_show ? p_dispwidget->simple_widget_height : 0),
-            top_right_x_advance,
-            MSG_REWINDING);
-
-   if (runloop_is_slowmotion)
-   {
-      top_right_x_advance -= gfx_widgets_draw_indicator(
-            p_dispwidget,
-            p_disp,
-            dispctx,
-            userdata,
-            video_width,
-            video_height,
-            p_dispwidget->gfx_widgets_icons_textures[
-            MENU_WIDGETS_ICON_SLOW_MOTION],
-            (fps_show ? p_dispwidget->simple_widget_height : 0),
-            top_right_x_advance,
-            MSG_SLOW_MOTION);
-      (void)top_right_x_advance;
-   }
-
-   for (i = 0; i < ARRAY_SIZE(widgets); i++)
-   {
-      const gfx_widget_t* widget = widgets[i];
-
-      if (widget->frame)
-         widget->frame(data, p_dispwidget);
-   }
-
-   /* Draw all messages */
-   if (p_dispwidget->current_msgs_size)
-   {
-#ifdef HAVE_THREADS
-      slock_lock(p_dispwidget->current_msgs_lock);
-#endif
-
-      for (i = 0; i < p_dispwidget->current_msgs_size; i++)
-      {
-         disp_widget_msg_t* msg = p_dispwidget->current_msgs[i];
-
-         if (!msg)
-            continue;
-
-         if (msg->task_ptr)
-            gfx_widgets_draw_task_msg(
-               p_dispwidget,
-               p_disp,
-               dispctx,
-               msg, userdata,
-               video_width, video_height);
-         else
-            gfx_widgets_draw_regular_msg(
-               p_dispwidget,
-               p_disp,
-               dispctx,
-               msg, userdata,
-               video_width, video_height);
-      }
-
-#ifdef HAVE_THREADS
-      slock_unlock(p_dispwidget->current_msgs_lock);
-#endif
-   }
-
-   /* Ensure all text is flushed */
-   gfx_widgets_flush_text(video_width, video_height,
-         &p_dispwidget->gfx_widget_fonts.regular);
-   gfx_widgets_flush_text(video_width, video_height,
-         &p_dispwidget->gfx_widget_fonts.bold);
-   gfx_widgets_flush_text(video_width, video_height,
-         &p_dispwidget->gfx_widget_fonts.msg_queue);
-
-   /* Unbind fonts */
-   gfx_widgets_font_unbind(&p_dispwidget->gfx_widget_fonts.regular);
-   gfx_widgets_font_unbind(&p_dispwidget->gfx_widget_fonts.bold);
-   gfx_widgets_font_unbind(&p_dispwidget->gfx_widget_fonts.msg_queue);
-
-   video_driver_set_viewport(video_width, video_height, false, true);
-}
-
-static void gfx_widgets_free(dispgfx_widget_t *p_dispwidget)
-{
-   size_t i;
-
-   p_dispwidget->flags     &= ~DISPGFX_WIDGET_FLAG_INITED;
-
-   for (i = 0; i < ARRAY_SIZE(widgets); i++)
-   {
-      const gfx_widget_t* widget = widgets[i];
-
-      if (widget->free)
-         widget->free();
-   }
-
-   /* Kill all running animations */
-   gfx_animation_kill_by_tag(
-         &p_dispwidget->gfx_widgets_generic_tag);
-
-   /* Purge everything from the fifo */
-   while (FIFO_READ_AVAIL_NONPTR(p_dispwidget->msg_queue) > 0)
-   {
-      disp_widget_msg_t *msg_widget;
-
-      fifo_read(&p_dispwidget->msg_queue,
-            &msg_widget, sizeof(msg_widget));
-
-      /* Note: gfx_widgets_free() is only called when
-       * main_exit() is invoked. At this stage, we cannot
-       * guarantee that any task pointers are valid (the
-       * task may have been free()'d, but we can't know
-       * that here) - so all we can do is unset the task
-       * pointer associated with each message
-       * > If we don't do this, gfx_widgets_msg_queue_free()
-       *   will generate heap-use-after-free errors */
-      msg_widget->task_ptr = NULL;
-
-      gfx_widgets_msg_queue_free(p_dispwidget, msg_widget);
-      free(msg_widget);
-   }
-
-   fifo_deinitialize(&p_dispwidget->msg_queue);
-
-   /* Purge everything from the list */
-#ifdef HAVE_THREADS
-   slock_lock(p_dispwidget->current_msgs_lock);
-#endif
-
-   p_dispwidget->current_msgs_size = 0;
-   for (i = 0; i < ARRAY_SIZE(p_dispwidget->current_msgs); i++)
-   {
-      disp_widget_msg_t *msg = p_dispwidget->current_msgs[i];
-      if (!msg)
-         continue;
-
-      /* Note: gfx_widgets_free() is only called when
-         * main_exit() is invoked. At this stage, we cannot
-         * guarantee that any task pointers are valid (the
-         * task may have been free()'d, but we can't know
-         * that here) - so all we can do is unset the task
-         * pointer associated with each message
-         * > If we don't do this, gfx_widgets_msg_queue_free()
-         *   will generate heap-use-after-free errors */
-      msg->task_ptr = NULL;
-
-      gfx_widgets_msg_queue_free(p_dispwidget, msg);
-   }
-#ifdef HAVE_THREADS
-   slock_unlock(p_dispwidget->current_msgs_lock);
-
-   slock_free(p_dispwidget->current_msgs_lock);
-   p_dispwidget->current_msgs_lock = NULL;
-#endif
-
-   p_dispwidget->msg_queue_tasks_count = 0;
-
-   /* Font */
-   video_coord_array_free(
-         &p_dispwidget->gfx_widget_fonts.regular.raster_block.carr);
-   video_coord_array_free(
-         &p_dispwidget->gfx_widget_fonts.bold.raster_block.carr);
-   video_coord_array_free(
-         &p_dispwidget->gfx_widget_fonts.msg_queue.raster_block.carr);
-
-   font_driver_bind_block(NULL, NULL);
-}
-
-static void gfx_widgets_context_reset(
-      dispgfx_widget_t *p_dispwidget,
-      gfx_display_t *p_disp,
-      settings_t *settings,
-      bool is_threaded,
-      unsigned width, unsigned height, bool fullscreen,
-      const char *dir_assets, char *font_path)
-{
-   size_t i;
-
-   /* Load textures */
-   /* Icons */
-   for (i = 0; i < MENU_WIDGETS_ICON_LAST; i++)
-   {
-      gfx_display_reset_textures_list(
-            gfx_widgets_icons_names[i],
-            p_dispwidget->monochrome_png_path,
-            &p_dispwidget->gfx_widgets_icons_textures[i],
-            TEXTURE_FILTER_MIPMAP_LINEAR,
-            NULL,
-            NULL);
-   }
-
-   /* Message queue */
-   gfx_display_reset_textures_list(
-         "msg_queue_icon.png",
-         p_dispwidget->gfx_widgets_path,
-         &p_dispwidget->msg_queue_icon,
-         TEXTURE_FILTER_LINEAR,
-         NULL,
-         NULL);
-   gfx_display_reset_textures_list(
-         "msg_queue_icon_outline.png",
-         p_dispwidget->gfx_widgets_path,
-         &p_dispwidget->msg_queue_icon_outline,
-         TEXTURE_FILTER_LINEAR,
-         NULL,
-         NULL);
-   gfx_display_reset_textures_list(
-         "msg_queue_icon_rect.png",
-         p_dispwidget->gfx_widgets_path,
-         &p_dispwidget->msg_queue_icon_rect,
-         TEXTURE_FILTER_NEAREST,
-         NULL,
-         NULL);
-
-   if (  p_dispwidget->msg_queue_icon         
-      && p_dispwidget->msg_queue_icon_outline
-      && p_dispwidget->msg_queue_icon_rect)
-      p_dispwidget->flags |=  DISPGFX_WIDGET_FLAG_MSG_QUEUE_HAS_ICONS;
-   else
-      p_dispwidget->flags &= ~DISPGFX_WIDGET_FLAG_MSG_QUEUE_HAS_ICONS;
-
-   for (i = 0; i < ARRAY_SIZE(widgets); i++)
-   {
-      const gfx_widget_t* widget = widgets[i];
-
-      if (widget->context_reset)
-         widget->context_reset(is_threaded, width, height,
-               fullscreen, dir_assets, font_path,
-               p_dispwidget->monochrome_png_path,
-               p_dispwidget->gfx_widgets_path);
-   }
-
-   /* Update scaling/dimensions */
-   p_dispwidget->last_video_width     = width;
-   p_dispwidget->last_video_height    = height;
-#ifdef HAVE_XMB
-   if (p_disp->menu_driver_id == MENU_DRIVER_ID_XMB)
-      p_dispwidget->last_scale_factor = gfx_display_get_widget_pixel_scale(
-            p_disp, settings,
-            p_dispwidget->last_video_width,
-            p_dispwidget->last_video_height, fullscreen);
-   else
-#endif
-      p_dispwidget->last_scale_factor = gfx_display_get_dpi_scale(
-                     p_disp, settings,
-                     p_dispwidget->last_video_width,
-                     p_dispwidget->last_video_height,
-                     fullscreen, true);
-
-   gfx_widgets_layout(p_disp, p_dispwidget,
-         is_threaded, dir_assets, font_path);
-   video_driver_monitor_reset();
-}
-
-bool gfx_widgets_init(
-      void *data_disp,
-      void *data_anim,
-      void *settings_data,
-      uintptr_t widgets_active_ptr,
-      bool video_is_threaded,
-      unsigned width, unsigned height, bool fullscreen,
-      const char *dir_assets, char *font_path)
-{
-   size_t i;
-   unsigned color                              = 0x1A1A1A;
-   dispgfx_widget_t *p_dispwidget              = &dispwidget_st;
-   gfx_display_t *p_disp                       = (gfx_display_t*)data_disp;
-   gfx_animation_t *p_anim                     = (gfx_animation_t*)data_anim;
-   settings_t *settings                        = (settings_t*)settings_data;
-   p_dispwidget->divider_width_1px             = 1;
-   p_dispwidget->gfx_widgets_generic_tag       = (uintptr_t)widgets_active_ptr;
-
-   if (!gfx_display_init_first_driver(p_disp, video_is_threaded))
-      goto error;
-   gfx_display_set_alpha(p_dispwidget->backdrop_orig, 0.75f);
-   for (i = 0; i < 16; i++)
-      p_dispwidget->pure_white[i] = 1.00f;
-
-   p_dispwidget->msg_queue_bg[0]  = HEX_R(color);
-   p_dispwidget->msg_queue_bg[1]  = HEX_G(color);
-   p_dispwidget->msg_queue_bg[2]  = HEX_B(color);
-   p_dispwidget->msg_queue_bg[3]  = 1.0f;
-   p_dispwidget->msg_queue_bg[4]  = HEX_R(color);
-   p_dispwidget->msg_queue_bg[5]  = HEX_G(color);
-   p_dispwidget->msg_queue_bg[6]  = HEX_B(color);
-   p_dispwidget->msg_queue_bg[7]  = 1.0f;
-   p_dispwidget->msg_queue_bg[8]  = HEX_R(color);
-   p_dispwidget->msg_queue_bg[9]  = HEX_G(color);
-   p_dispwidget->msg_queue_bg[10] = HEX_B(color);
-   p_dispwidget->msg_queue_bg[11] = 1.0f;
-   p_dispwidget->msg_queue_bg[12] = HEX_R(color);
-   p_dispwidget->msg_queue_bg[13] = HEX_G(color);
-   p_dispwidget->msg_queue_bg[14] = HEX_B(color);
-   p_dispwidget->msg_queue_bg[15] = 1.0f;
-
-   if (!(p_dispwidget->flags & DISPGFX_WIDGET_FLAG_INITED))
-   {
-      char theme_path[PATH_MAX_LENGTH];
-      p_dispwidget->gfx_widgets_frame_count = 0;
-
-      for (i = 0; i < ARRAY_SIZE(widgets); i++)
-      {
-         const gfx_widget_t* widget = widgets[i];
-
-         if (widget->init)
-            widget->init(p_disp, p_anim, video_is_threaded, fullscreen);
-      }
-
-      if (!fifo_initialize(&p_dispwidget->msg_queue,
-            MSG_QUEUE_PENDING_MAX * sizeof(disp_widget_msg_t*)))
-         goto error;
-
-      memset(&p_dispwidget->current_msgs[0], 0, sizeof(p_dispwidget->current_msgs));
-      p_dispwidget->current_msgs_size = 0;
-
-#ifdef HAVE_THREADS
-      p_dispwidget->current_msgs_lock = slock_new();
-#endif
-
-      fill_pathname_join_special(
-            p_dispwidget->gfx_widgets_path,
-            dir_assets,
-            "menu_widgets",
-            sizeof(p_dispwidget->gfx_widgets_path)
-            );
-      fill_pathname_join_special(
-            p_dispwidget->xmb_path,
-            dir_assets,
-            "xmb",
-            sizeof(p_dispwidget->xmb_path)
-            );
-      /* Base path */
-      fill_pathname_join_special(p_dispwidget->ozone_path,
-            dir_assets,
-            "ozone",
-            sizeof(p_dispwidget->ozone_path));
-      fill_pathname_join_special(p_dispwidget->ozone_regular_font_path,
-            p_dispwidget->ozone_path, "regular.ttf",
-            sizeof(p_dispwidget->ozone_regular_font_path));
-      fill_pathname_join_special(p_dispwidget->ozone_bold_font_path,
-            p_dispwidget->ozone_path, "bold.ttf",
-            sizeof(p_dispwidget->ozone_bold_font_path));
-      fill_pathname_join_special(
-            theme_path,
-            p_dispwidget->xmb_path,
-            "monochrome",
-            sizeof(theme_path)
-            );
-      fill_pathname_join_special(
-            p_dispwidget->monochrome_png_path,
-            theme_path,
-            "png",
-            sizeof(p_dispwidget->monochrome_png_path)
-            );
-      fill_pathname_join_special(p_dispwidget->assets_pkg_dir,
-            settings->paths.directory_assets, "pkg",
-            sizeof(p_dispwidget->assets_pkg_dir));
-
-      p_dispwidget->flags |= DISPGFX_WIDGET_FLAG_INITED;
-   }
-
-   gfx_widgets_context_reset(
-         p_dispwidget,
-         p_disp,
-         settings,
-         video_is_threaded,
-         width, height, fullscreen,
-         dir_assets, font_path);
-
-   return true;
-
-error:
-   gfx_widgets_free(p_dispwidget);
-   return false;
-}
-
-static void gfx_widgets_context_destroy(dispgfx_widget_t *p_dispwidget)
-{
-   size_t i;
-
-   for (i = 0; i < ARRAY_SIZE(widgets); i++)
-   {
-      const gfx_widget_t* widget = widgets[i];
-
-      if (widget->context_destroy)
-         widget->context_destroy();
-   }
-
-   /* TODO: Dismiss onscreen notifications that have been freed */
-
-   /* Textures */
-   for (i = 0; i < MENU_WIDGETS_ICON_LAST; i++)
-      video_driver_texture_unload(&p_dispwidget->gfx_widgets_icons_textures[i]);
-
-   video_driver_texture_unload(&p_dispwidget->msg_queue_icon);
-   video_driver_texture_unload(&p_dispwidget->msg_queue_icon_outline);
-   video_driver_texture_unload(&p_dispwidget->msg_queue_icon_rect);
-
-   p_dispwidget->msg_queue_icon         = 0;
-   p_dispwidget->msg_queue_icon_outline = 0;
-   p_dispwidget->msg_queue_icon_rect    = 0;
-
-   /* Fonts */
-   gfx_widgets_font_free(&p_dispwidget->gfx_widget_fonts.regular);
-   gfx_widgets_font_free(&p_dispwidget->gfx_widget_fonts.bold);
-   gfx_widgets_font_free(&p_dispwidget->gfx_widget_fonts.msg_queue);
-}
-
-
-void gfx_widgets_deinit(bool widgets_persisting)
-{
-   dispgfx_widget_t *p_dispwidget = &dispwidget_st;
-
-   gfx_widgets_context_destroy(p_dispwidget);
-
-   if (!widgets_persisting)
-      gfx_widgets_free(p_dispwidget);
-}
-
-#ifdef HAVE_TRANSLATE
-bool gfx_widgets_ai_service_overlay_load(
-      char* buffer, unsigned buffer_len,
-      enum image_type_enum image_type)
-{
-   dispgfx_widget_t *p_dispwidget   = &dispwidget_st;
-   if (p_dispwidget->ai_service_overlay_state == 0)
-   {
-      if (!gfx_display_reset_textures_list_buffer(
-               &p_dispwidget->ai_service_overlay_texture, 
-               TEXTURE_FILTER_MIPMAP_LINEAR, 
-               (void *) buffer, buffer_len, image_type,
-               &p_dispwidget->ai_service_overlay_width,
-               &p_dispwidget->ai_service_overlay_height))
-         return false;
-      p_dispwidget->ai_service_overlay_state = 1;
-   }
-   return true;
-}
-
-void gfx_widgets_ai_service_overlay_unload(void)
-{
-   dispgfx_widget_t *p_dispwidget   = &dispwidget_st;
-   if (p_dispwidget->ai_service_overlay_state == 1)
-   {
-      video_driver_texture_unload(&p_dispwidget->ai_service_overlay_texture);
-      p_dispwidget->ai_service_overlay_texture = 0;
-      p_dispwidget->ai_service_overlay_state   = 0;
-   }
-}
-#endif
-
-dispgfx_widget_t *dispwidget_get_ptr(void)
-{
-   return &dispwidget_st;
-}
-
-bool gfx_widgets_ready(void)
-{
-#ifdef HAVE_GFX_WIDGETS
-   return dispwidget_st.active;
-#else
-   return false;
-#endif
-}

+ 0 - 424
app/src/main/cpp/gfx/gfx_widgets.h

@@ -1,424 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2018      - natinusala
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef _GFX_WIDGETS_H
-#define _GFX_WIDGETS_H
-
-#ifdef HAVE_CONFIG_H
-#include "../config.h"
-#endif
-
-#include <retro_common_api.h>
-#include <formats/image.h>
-#include <queues/task_queue.h>
-#include <queues/message_queue.h>
-#include <queues/fifo_queue.h>
-
-#ifdef HAVE_THREADS
-#include <rthreads/rthreads.h>
-#endif
-
-#include "gfx_animation.h"
-#include "gfx_display.h"
-
-#define DEFAULT_BACKDROP               0.75f
-
-#define MSG_QUEUE_PENDING_MAX          32
-#define MSG_QUEUE_ONSCREEN_MAX         4
-
-#define MSG_QUEUE_ANIMATION_DURATION      330
-#define TASK_FINISHED_DURATION            3000
-#define HOURGLASS_INTERVAL                5000
-#define HOURGLASS_DURATION                1000
-
-/* TODO: Colors for warning, error and success */
-
-#define TEXT_COLOR_INFO 0xD8EEFFFF
-#if 0
-#define TEXT_COLOR_SUCCESS 0x22B14CFF
-#define TEXT_COLOR_ERROR 0xC23B22FF
-#endif
-#define TEXT_COLOR_FAINT 0x878787FF
-
-RETRO_BEGIN_DECLS
-
-enum gfx_widgets_icon
-{
-   MENU_WIDGETS_ICON_PAUSED = 0,
-   MENU_WIDGETS_ICON_FAST_FORWARD,
-   MENU_WIDGETS_ICON_REWIND,
-   MENU_WIDGETS_ICON_SLOW_MOTION,
-
-   MENU_WIDGETS_ICON_HOURGLASS,
-   MENU_WIDGETS_ICON_CHECK,
-
-   MENU_WIDGETS_ICON_INFO,
-
-   MENU_WIDGETS_ICON_ACHIEVEMENT,
-
-   MENU_WIDGETS_ICON_LAST
-};
-
-enum notification_show_screenshot_duration
-{
-   NOTIFICATION_SHOW_SCREENSHOT_DURATION_NORMAL = 0,
-   NOTIFICATION_SHOW_SCREENSHOT_DURATION_FAST,
-   NOTIFICATION_SHOW_SCREENSHOT_DURATION_VERY_FAST,
-   NOTIFICATION_SHOW_SCREENSHOT_DURATION_INSTANT,
-   NOTIFICATION_SHOW_SCREENSHOT_DURATION_LAST
-};
-
-enum notification_show_screenshot_flash
-{
-   NOTIFICATION_SHOW_SCREENSHOT_FLASH_NORMAL = 0,
-   NOTIFICATION_SHOW_SCREENSHOT_FLASH_FAST,
-   NOTIFICATION_SHOW_SCREENSHOT_FLASH_OFF,
-   NOTIFICATION_SHOW_SCREENSHOT_FLASH_LAST
-};
-
-enum cheevos_appearance_anchor
-{
-   CHEEVOS_APPEARANCE_ANCHOR_TOPLEFT = 0,
-   CHEEVOS_APPEARANCE_ANCHOR_TOPCENTER,
-   CHEEVOS_APPEARANCE_ANCHOR_TOPRIGHT,
-   CHEEVOS_APPEARANCE_ANCHOR_BOTTOMLEFT,
-   CHEEVOS_APPEARANCE_ANCHOR_BOTTOMCENTER,
-   CHEEVOS_APPEARANCE_ANCHOR_BOTTOMRIGHT,
-   CHEEVOS_APPEARANCE_ANCHOR_LAST
-};
-
-/* This structure holds all objects + metadata
- * corresponding to a particular font */
-typedef struct
-{
-   font_data_t *font;
-   video_font_raster_block_t raster_block; /* ptr alignment */
-   size_t usage_count;
-   unsigned glyph_width;
-   float line_height;
-   float line_ascender;
-   float line_descender;
-   float line_centre_offset;
-} gfx_widget_font_data_t;
-
-/* Font data */
-typedef struct
-{
-   gfx_widget_font_data_t regular;
-   gfx_widget_font_data_t bold;
-   gfx_widget_font_data_t msg_queue;
-} gfx_widget_fonts_t;
-
-enum disp_widget_flags_enum
-{
-   DISPWIDG_FLAG_TASK_FINISHED             = (1 << 0),
-   DISPWIDG_FLAG_TASK_ERROR                = (1 << 1),
-   DISPWIDG_FLAG_TASK_CANCELLED            = (1 << 2),
-   DISPWIDG_FLAG_EXPIRATION_TIMER_STARTED  = (1 << 3),
-   /* Is it currently doing the fade out animation ? */
-   DISPWIDG_FLAG_DYING                     = (1 << 4),
-   /* Has the timer expired ? if so, should be set to dying */
-   DISPWIDG_FLAG_EXPIRED                   = (1 << 5),
-   /* Unfold animation */
-   DISPWIDG_FLAG_UNFOLDED                  = (1 << 6),
-   DISPWIDG_FLAG_UNFOLDING                 = (1 << 7)
-};
-
-typedef struct disp_widget_msg
-{
-   char *msg;
-   char *msg_new;
-   retro_task_t *task_ptr;
-
-   uint32_t task_ident;
-   size_t   msg_len;
-   unsigned duration;
-   unsigned text_height;
-   unsigned width;
-
-   float msg_transition_animation;
-   float offset_y;
-   float alpha;
-   float unfold;
-   float hourglass_rotation;
-   float hourglass_timer; /* float alignment */
-   float expiration_timer; /* float alignment */
-
-   uint16_t flags;
-   int8_t task_progress;
-   /* How many tasks have used this notification? */
-   uint8_t task_count;
-} disp_widget_msg_t;
-
-/* There can only be one message animation at a time to 
- * avoid confusing users */
-enum dispgfx_widget_flags
-{
-   DISPGFX_WIDGET_FLAG_MSG_QUEUE_HAS_ICONS = (1 << 0),
-   DISPGFX_WIDGET_FLAG_PERSISTING          = (1 << 1),
-   DISPGFX_WIDGET_FLAG_MOVING              = (1 << 2),
-   DISPGFX_WIDGET_FLAG_INITED              = (1 << 3)
-};
-
-typedef struct dispgfx_widget
-{
-   uint64_t gfx_widgets_frame_count;
-
-#ifdef HAVE_THREADS
-   slock_t* current_msgs_lock;
-#endif
-   fifo_buffer_t msg_queue;
-   disp_widget_msg_t* current_msgs[MSG_QUEUE_ONSCREEN_MAX];
-   gfx_widget_fonts_t gfx_widget_fonts; /* ptr alignment */
-
-#ifdef HAVE_TRANSLATE
-   uintptr_t ai_service_overlay_texture;
-#endif
-   uintptr_t msg_queue_icon;
-   uintptr_t msg_queue_icon_outline;
-   uintptr_t msg_queue_icon_rect;
-   uintptr_t gfx_widgets_icons_textures[
-   MENU_WIDGETS_ICON_LAST];
-   uintptr_t gfx_widgets_generic_tag;
-
-   size_t current_msgs_size;
-
-#ifdef HAVE_TRANSLATE
-   int ai_service_overlay_state;
-#endif
-
-   unsigned last_video_width;
-   unsigned last_video_height;
-   unsigned msg_queue_kill;
-   /* Count of messages bound to a task in current_msgs */
-   unsigned msg_queue_tasks_count;
-
-   unsigned simple_widget_padding;
-   unsigned simple_widget_height;
-
-   /* Used for both generic and libretro messages */
-   unsigned generic_message_height;
-
-   unsigned msg_queue_height;
-   unsigned msg_queue_spacing;
-   unsigned msg_queue_rect_start_x;
-   unsigned msg_queue_internal_icon_size;
-   unsigned msg_queue_internal_icon_offset;
-   unsigned msg_queue_icon_size_x;
-   unsigned msg_queue_icon_size_y;
-   unsigned msg_queue_icon_offset_y;
-   unsigned msg_queue_scissor_start_x;
-   unsigned msg_queue_default_rect_width_menu_alive;
-   unsigned msg_queue_default_rect_width;
-   unsigned msg_queue_regular_padding_x;
-   unsigned msg_queue_regular_text_start;
-   unsigned msg_queue_task_text_start_x;
-   unsigned msg_queue_task_rect_start_x;
-   unsigned msg_queue_task_hourglass_x;
-   unsigned divider_width_1px;
-
-   float last_scale_factor;
-   float backdrop_orig[16];
-   float msg_queue_bg[16];
-   float pure_white[16];
-#ifdef HAVE_TRANSLATE
-   unsigned ai_service_overlay_width;
-   unsigned ai_service_overlay_height;
-#endif
-
-   uint8_t flags;
-
-   char assets_pkg_dir[PATH_MAX_LENGTH];
-   char xmb_path[PATH_MAX_LENGTH];                /* TODO/FIXME - decouple from XMB */
-   char ozone_path[PATH_MAX_LENGTH];              /* TODO/FIXME - decouple from Ozone */
-   char ozone_regular_font_path[PATH_MAX_LENGTH]; /* TODO/FIXME - decouple from Ozone */
-   char ozone_bold_font_path[PATH_MAX_LENGTH];    /* TODO/FIXME - decouple from Ozone */
-
-   char monochrome_png_path[PATH_MAX_LENGTH];
-   char gfx_widgets_path[PATH_MAX_LENGTH];
-   char gfx_widgets_status_text[255];
-
-   bool active;
-} dispgfx_widget_t;
-
-
-/* A widget */
-/* TODO/FIXME: cleanup all unused parameters */
-struct gfx_widget
-{
-   /* called when the widgets system is initialized
-    * -> initialize the widget here */
-   bool (*init)(gfx_display_t *p_disp,
-         gfx_animation_t *p_anim,
-         bool video_is_threaded, bool fullscreen);
-
-   /* called when the widgets system is freed
-    * -> free the widget here */
-   void (*free)(void);
-
-   /* called when the graphics context is reset
-    * -> (re)load the textures here */
-   void (*context_reset)(bool is_threaded,
-      unsigned width, unsigned height, bool fullscreen,
-      const char *dir_assets, char *font_path,
-      char* menu_png_path,
-      char* widgets_png_path);
-
-   /* called when the graphics context is destroyed
-    * -> release the textures here */
-   void (*context_destroy)(void);
-
-   /* called when the window resolution changes
-    * -> (re)layout the widget here */
-   void (*layout)(void *data,
-         bool is_threaded, const char *dir_assets, char *font_path);
-
-   /* called every frame on the main thread
-    * -> update the widget logic here */
-   void (*iterate)(void *user_data,
-      unsigned width, unsigned height, bool fullscreen,
-      const char *dir_assets, char *font_path,
-      bool is_threaded);
-
-   /* called every frame
-    * (on the video thread if threaded video is on)
-    * -- data is a video_frame_info_t
-    * -- userdata is a dispgfx_widget_t
-    * -> draw the widget here */
-   void (*frame)(void* data, void *userdata);
-};
-
-float gfx_widgets_get_thumbnail_scale_factor(
-      const float dst_width, const float dst_height,
-      const float image_width, const float image_height);
-
-void gfx_widgets_draw_icon(
-      void *userdata,
-      void *data_disp,
-      unsigned video_width,
-      unsigned video_height,
-      unsigned icon_width,
-      unsigned icon_height,
-      uintptr_t texture,
-      float x, float y,
-      float radians,
-      float cosine,
-      float sine,
-      float *color);
-
-void gfx_widgets_draw_text(
-      gfx_widget_font_data_t* font_data,
-      const char *text,
-      float x, float y,
-      int width, int height,
-      uint32_t color,
-      enum text_alignment text_align,
-      bool draw_outside);
-
-void gfx_widgets_flush_text(
-      unsigned video_width, unsigned video_height,
-      gfx_widget_font_data_t* font_data);
-
-typedef struct gfx_widget gfx_widget_t;
-
-bool gfx_widgets_init(
-      void *data_disp,
-      void *data_anim,
-      void *settings_data,
-      uintptr_t widgets_active_ptr,
-      bool video_is_threaded,
-      unsigned width, unsigned height, bool fullscreen,
-      const char *dir_assets, char *font_path);
-
-void gfx_widgets_deinit(bool widgets_persisting);
-
-void gfx_widgets_msg_queue_push(
-      retro_task_t *task, const char *msg,
-      unsigned duration,
-      char *title,
-      enum message_queue_icon icon,
-      enum message_queue_category category,
-      unsigned prio, bool flush,
-      bool menu_is_alive);
-
-void gfx_widget_volume_update_and_show(float new_volume,
-      bool mute);
-
-void gfx_widgets_iterate(
-      void *data_disp,
-      void *settings_data,
-      unsigned width, unsigned height, bool fullscreen,
-      const char *dir_assets, char *font_path,
-      bool is_threaded);
-
-void gfx_widget_screenshot_taken(void *data,
-      const char *shotname, const char *filename);
-
-/* AI Service functions */
-#ifdef HAVE_TRANSLATE
-bool gfx_widgets_ai_service_overlay_load(
-      char* buffer, unsigned buffer_len,
-      enum image_type_enum image_type);
-
-void gfx_widgets_ai_service_overlay_unload(void);
-#endif
-
-#ifdef HAVE_CHEEVOS
-void gfx_widgets_update_cheevos_appearance(void);
-void gfx_widgets_push_achievement(const char *title, const char* subtitle, const char *badge);
-void gfx_widgets_set_leaderboard_display(unsigned id, const char* value);
-void gfx_widgets_set_challenge_display(unsigned id, const char* badge);
-void gfx_widget_set_achievement_progress(const char* badge, const char* progress);
-#endif
-
-/* TODO/FIXME/WARNING: Not thread safe! */
-void gfx_widget_set_generic_message(
-      const char *message, unsigned duration);
-void gfx_widget_set_libretro_message(
-      const char *message, unsigned duration);
-void gfx_widget_set_progress_message(
-      const char *message, unsigned duration,
-      unsigned priority, int8_t progress);
-bool gfx_widget_start_load_content_animation(void);
-
-/* All the functions below should be called in
- * the video driver - once they are all added, set
- * enable_menu_widgets to true for that driver */
-void gfx_widgets_frame(void *data);
-
-bool gfx_widgets_ready(void);
-
-dispgfx_widget_t *dispwidget_get_ptr(void);
-
-extern const gfx_widget_t gfx_widget_screenshot;
-extern const gfx_widget_t gfx_widget_volume;
-extern const gfx_widget_t gfx_widget_generic_message;
-extern const gfx_widget_t gfx_widget_libretro_message;
-extern const gfx_widget_t gfx_widget_progress_message;
-extern const gfx_widget_t gfx_widget_load_content_animation;
-
-#ifdef HAVE_NETWORKING
-extern const gfx_widget_t gfx_widget_netplay_chat;
-extern const gfx_widget_t gfx_widget_netplay_ping;
-#endif
-
-#ifdef HAVE_CHEEVOS
-extern const gfx_widget_t gfx_widget_achievement_popup;
-extern const gfx_widget_t gfx_widget_leaderboard_display;
-#endif
-
-RETRO_END_DECLS
-
-#endif

+ 18 - 6
app/src/main/cpp/gfx/video_driver.c

@@ -1417,6 +1417,7 @@ retro_proc_address_t video_driver_get_proc_address(const char *sym)
 #ifdef HAVE_VIDEO_FILTER
 void video_driver_filter_free(void)
 {
+   RARCH_LOG("[VideoFilter]: video_driver_filter_free");
    video_driver_state_t *video_st                 = &video_driver_st;
    if (video_st->state_filter)
       rarch_softfilter_free(video_st->state_filter);
@@ -1437,9 +1438,10 @@ void video_driver_filter_free(void)
    video_st->flags          &= ~(VIDEO_FLAG_STATE_OUT_RGB32);
 }
 
-void video_driver_init_filter(enum retro_pixel_format colfmt_int,
+void video_driver_filter_init(enum retro_pixel_format colfmt_int,
       settings_t *settings)
 {
+   RARCH_LOG("[VideoFilter]: video_driver_filter_init");
    unsigned pow2_x, pow2_y, maxsize;
    void *buf                            = NULL;
    video_driver_state_t *video_st       = &video_driver_st;
@@ -2645,7 +2647,13 @@ void video_driver_build_info(video_frame_info_t *video_info)
    video_info->menu_mouse_enable           = settings->bools.menu_mouse_enable;
    video_info->monitor_index               = settings->uints.video_monitor_index;
 
-   video_info->font_enable                 = settings->bools.video_font_enable;
+   video_info->font_enable                 =
+#if HAVE_FONT
+         settings->bools.video_font_enable;
+#else
+         false;
+#endif
+
    video_info->font_size                   = settings->floats.video_font_size;
    video_info->font_msg_pos_x              = settings->floats.video_msg_pos_x;
    video_info->font_msg_pos_y              = settings->floats.video_msg_pos_y;
@@ -3178,9 +3186,8 @@ bool video_driver_init_internal(bool *video_is_threaded, bool verbosity_enabled)
    const char *path_softfilter_plugin     = settings->paths.path_softfilter_plugin;
 
    /* Init video filter only when game is running */
-   if ((runloop_st->current_core.flags & RETRO_CORE_FLAG_GAME_LOADED) &&
-         !string_is_empty(path_softfilter_plugin))
-      video_driver_init_filter(video_driver_pix_fmt, settings);
+   if (!string_is_empty(path_softfilter_plugin))
+      video_driver_filter_init(video_driver_pix_fmt, settings);
 #endif
 
    max_dim   = MAX(geom->max_width, geom->max_height);
@@ -3395,7 +3402,12 @@ bool video_driver_init_internal(bool *video_is_threaded, bool verbosity_enabled)
    video.smooth                      = settings->bools.video_smooth;
    video.ctx_scaling                 = settings->bools.video_ctx_scaling;
    video.input_scale                 = scale;
-   video.font_enable                 = settings->bools.video_font_enable;
+   video.font_enable                 =
+#if HAVE_FONT
+            settings->bools.video_font_enable;
+#else
+            false;
+#endif
    video.font_size                   = settings->floats.video_font_size;
    video.path_font                   = settings->paths.path_font;
 #ifdef HAVE_VIDEO_FILTER

+ 1 - 1
app/src/main/cpp/gfx/video_driver.h

@@ -1260,7 +1260,7 @@ void recording_dump_frame(
 
 void video_driver_gpu_record_deinit(void);
 
-void video_driver_init_filter(enum retro_pixel_format colfmt_int,
+void video_driver_filter_init(enum retro_pixel_format colfmt_int,
       settings_t *settings);
 
 void video_context_driver_reset(void);

+ 2 - 0
app/src/main/cpp/gfx/video_thread_wrapper.c

@@ -25,7 +25,9 @@
 
 #include "video_driver.h"
 #include "video_thread_wrapper.h"
+#ifdef HAVE_FONT
 #include "font_driver.h"
+#endif
 
 #include "../retroarch.h"
 #include "../runloop.h"

+ 2 - 0
app/src/main/cpp/gfx/video_thread_wrapper.h

@@ -24,7 +24,9 @@
 #include <rthreads/rthreads.h>
 #include <retro_miscellaneous.h>
 
+#ifdef HAVE_FONT
 #include "font_driver.h"
+#endif
 
 RETRO_BEGIN_DECLS
 

+ 10 - 0
app/src/main/cpp/griffin/griffin.c

@@ -573,6 +573,7 @@ VIDEO DRIVER
 FONTS
 ============================================================ */
 
+#ifdef HAVE_FONT
 #include "../gfx/drivers_font_renderer/bitmapfont.c"
 
 #ifdef HAVE_LANGEXTRA
@@ -664,6 +665,7 @@ FONTS
 #if defined(HAVE_D3D12)
 #include "../gfx/drivers_font/d3d12_font.c"
 #endif
+#endif
 
 /*============================================================
 INPUT
@@ -892,8 +894,10 @@ DRIVERS
 #endif
 #include "../gfx/gfx_animation.c"
 #include "../gfx/gfx_display.c"
+#ifdef HAVE_GFX_THUBNAIL
 #include "../gfx/gfx_thumbnail_path.c"
 #include "../gfx/gfx_thumbnail.c"
+#endif
 #include "../gfx/video_coord_array.c"
 #ifdef HAVE_AUDIOMIXER
 #include "../libretro-common/audio/audio_mixer.c"
@@ -1218,8 +1222,10 @@ DATA RUNLOOP
 #include "../tasks/task_movie.c"
 #include "../tasks/task_image.c"
 #include "../tasks/task_file_transfer.c"
+#ifdef HAVE_PLAYLIST
 #include "../tasks/task_playlist_manager.c"
 #include "../tasks/task_manual_content_scan.c"
+#endif
 #include "../tasks/task_core_backup.c"
 #ifdef HAVE_TRANSLATE
 #include "../tasks/task_translation.c"
@@ -1245,7 +1251,9 @@ SCREENSHOTS
 /*============================================================
 PLAYLISTS
 ============================================================ */
+#ifdef HAVE_PLAYLIST
 #include "../playlist.c"
+#endif
 
 /*============================================================
 MENU
@@ -1544,7 +1552,9 @@ PLAYLIST NAME SANITIZATION
 /*============================================================
 MANUAL CONTENT SCAN
 ============================================================ */
+#ifdef HAVE_PLAYLIST
 #include "../manual_content_scan.c"
+#endif
 
 /*============================================================
 DISK CONTROL INTERFACE

+ 0 - 1400
app/src/main/cpp/manual_content_scan.c

@@ -1,1400 +0,0 @@
-/* Copyright  (C) 2010-2019 The RetroArch team
- *
- * ---------------------------------------------------------------------------------------
- * The following license statement only applies to this file (manual_content_scan.c).
- * ---------------------------------------------------------------------------------------
- *
- * Permission is hereby granted, free of charge,
- * to any person obtaining a copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <file/file_path.h>
-#include <file/archive_file.h>
-#include <string/stdstring.h>
-#include <lists/dir_list.h>
-#include <retro_miscellaneous.h>
-
-#include "msg_hash.h"
-#include "list_special.h"
-#include "core_info.h"
-#include "file_path_special.h"
-
-#include "frontend/frontend_driver.h"
-
-#include "manual_content_scan.h"
-
-/* Holds all configuration parameters associated
- * with a manual content scan */
-typedef struct
-{
-   enum manual_content_scan_system_name_type system_name_type;
-   enum manual_content_scan_core_type core_type;
-
-   char content_dir[PATH_MAX_LENGTH];
-   char system_name_content_dir[PATH_MAX_LENGTH];
-   char system_name_database[PATH_MAX_LENGTH];
-   char system_name_custom[PATH_MAX_LENGTH];
-   char core_name[PATH_MAX_LENGTH];
-   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];
-
-   bool search_recursively;
-   bool search_archives;
-   bool filter_dat_content;
-   bool overwrite_playlist;
-   bool validate_entries;
-} scan_settings_t;
-
-/* TODO/FIXME - static public global variables */
-/* Static settings object
- * > Provides easy access to settings parameters
- *   when creating associated menu entries
- * > We are handling this in almost exactly the same
- *   way as the regular global 'static settings_t *configuration_settings;'
- *   object in retroarch.c. This means it is not inherently thread safe,
- *   but this should not be an issue (i.e. regular configuration_settings
- *   are not thread safe, but we only access them when pushing a
- *   task, not in the task thread itself, so all is well) */
-static scan_settings_t scan_settings = {
-   MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR, /* system_name_type */
-   MANUAL_CONTENT_SCAN_CORE_DETECT,             /* core_type */
-   "",                                          /* content_dir */
-   "",                                          /* system_name_content_dir */
-   "",                                          /* system_name_database */
-   "",                                          /* system_name_custom */
-   "",                                          /* core_name */
-   "",                                          /* core_path */
-   "",                                          /* file_exts_core */
-   "",                                          /* file_exts_custom */
-   "",                                          /* dat_file_path */
-   true,                                        /* search_recursively */
-   false,                                       /* search_archives */
-   false,                                       /* filter_dat_content */
-   false,                                       /* overwrite_playlist */
-   false                                        /* validate_entries */
-};
-
-/*****************/
-/* Configuration */
-/*****************/
-
-/* Pointer access */
-
-/* Returns a pointer to the internal
- * 'content_dir' string */
-char *manual_content_scan_get_content_dir_ptr(void)
-{
-   return scan_settings.content_dir;
-}
-
-/* Returns a pointer to the internal
- * 'system_name_custom' string */
-char *manual_content_scan_get_system_name_custom_ptr(void)
-{
-   return scan_settings.system_name_custom;
-}
-
-/* Returns size of the internal
- * 'system_name_custom' string */
-size_t manual_content_scan_get_system_name_custom_size(void)
-{
-   return sizeof(scan_settings.system_name_custom);
-}
-
-/* Returns a pointer to the internal
- * 'file_exts_custom' string */
-char *manual_content_scan_get_file_exts_custom_ptr(void)
-{
-   return scan_settings.file_exts_custom;
-}
-
-/* Returns size of the internal
- * 'file_exts_custom' string */
-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_recursively' bool */
-bool *manual_content_scan_get_search_recursively_ptr(void)
-{
-   return &scan_settings.search_recursively;
-}
-
-/* Returns a pointer to the internal
- * 'search_archives' bool */
-bool *manual_content_scan_get_search_archives_ptr(void)
-{
-   return &scan_settings.search_archives;
-}
-
-/* Returns a pointer to the internal
- * 'filter_dat_content' bool */
-bool *manual_content_scan_get_filter_dat_content_ptr(void)
-{
-   return &scan_settings.filter_dat_content;
-}
-
-/* Returns a pointer to the internal
- * 'overwrite_playlist' bool */
-bool *manual_content_scan_get_overwrite_playlist_ptr(void)
-{
-   return &scan_settings.overwrite_playlist;
-}
-
-/* Returns a pointer to the internal
- * 'validate_entries' bool */
-bool *manual_content_scan_get_validate_entries_ptr(void)
-{
-   return &scan_settings.validate_entries;
-}
-
-/* Sanitisation */
-
-/* Sanitises file extensions list string:
- * > Removes period (full stop) characters
- * > Converts to lower case
- * > Trims leading/trailing whitespace */
-static void manual_content_scan_scrub_file_exts(char *file_exts)
-{
-   if (string_is_empty(file_exts))
-      return;
-
-   string_remove_all_chars(file_exts, '.');
-   string_to_lower(file_exts);
-   string_trim_whitespace(file_exts);
-}
-
-/* Removes invalid characters from
- * 'system_name_custom' string */
-void manual_content_scan_scrub_system_name_custom(void)
-{
-   char *scrub_char_pointer = NULL;
-
-   if (string_is_empty(scan_settings.system_name_custom))
-      return;
-
-   /* Scrub characters that are not cross-platform
-    * and/or violate the No-Intro filename standard:
-    * http://datomatic.no-intro.org/stuff/The%20Official%20No-Intro%20Convention%20(20071030).zip
-    * Replace these characters with underscores */
-   while ((scrub_char_pointer = 
-            strpbrk(scan_settings.system_name_custom, "&*/:`\"<>?\\|")))
-      *scrub_char_pointer = '_';
-}
-
-/* Removes period (full stop) characters from
- * 'file_exts_custom' string and converts to
- * lower case */
-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
- * operation.
- * Returns true if content directory is valid. */
-bool manual_content_scan_set_menu_content_dir(const char *content_dir)
-{
-   size_t len;
-   const char *dir_name = NULL;
-
-   /* Sanity check */
-   if (string_is_empty(content_dir))
-      goto error;
-
-   if (!path_is_directory(content_dir))
-      goto error;
-
-   /* Copy directory path to settings struct */
-   strlcpy(
-         scan_settings.content_dir,
-         content_dir,
-         sizeof(scan_settings.content_dir));
-
-   /* Remove trailing slash, if required */
-   len = strlen(scan_settings.content_dir);
-   if (len <= 0)
-      goto error;
-
-   if (scan_settings.content_dir[len - 1] == PATH_DEFAULT_SLASH_C())
-      scan_settings.content_dir[len - 1] = '\0';
-
-   /* Handle case where path was a single slash... */
-   if (string_is_empty(scan_settings.content_dir))
-      goto error;
-
-   /* Get directory name (used as system name
-    * when scan_settings.system_name_type ==
-    * MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR) */
-   dir_name = path_basename(scan_settings.content_dir);
-
-   if (string_is_empty(dir_name))
-      goto error;
-
-   /* Copy directory name to settings struct */
-   strlcpy(
-         scan_settings.system_name_content_dir,
-         dir_name,
-         sizeof(scan_settings.system_name_content_dir));
-
-   return true;
-
-error:
-   /* Directory is invalid - reset internal
-    * content directory and associated 'directory'
-    * system name */
-   scan_settings.content_dir[0]             = '\0';
-   scan_settings.system_name_content_dir[0] = '\0';
-   return false;
-}
-
-/* Sets system name for the next manual scan
- * operation.
- * Returns true if system name is valid.
- * NOTE:
- * > Only sets 'system_name_type' and 'system_name_database'
- * > 'system_name_content_dir' and 'system_name_custom' are
- *   (by necessity) handled elsewhere
- * > This may look fishy, but it's not - it's a menu-specific
- *   function, and this is simply the cleanest way to handle
- *   the setting... */
-bool manual_content_scan_set_menu_system_name(
-      enum manual_content_scan_system_name_type system_name_type,
-      const char *system_name)
-{
-   /* Sanity check */
-   if (system_name_type > MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE)
-      goto error;
-
-   /* Cache system name 'type' */
-   scan_settings.system_name_type = system_name_type;
-
-   /* Check if we are using a non-database name */
-   if ((scan_settings.system_name_type == MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR) ||
-       (scan_settings.system_name_type == MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM))
-      scan_settings.system_name_database[0] = '\0';
-   else
-   {
-      /* We are using a database name... */
-      if (string_is_empty(system_name))
-         goto error;
-
-      /* Copy database name to settings struct */
-      strlcpy(
-            scan_settings.system_name_database,
-            system_name,
-            sizeof(scan_settings.system_name_database));
-   }
-
-   return true;
-
-error:
-   /* Input parameters are invalid - reset internal
-    * 'system_name_type' and 'system_name_database' */
-   scan_settings.system_name_type        = MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR;
-   scan_settings.system_name_database[0] = '\0';
-   return false;
-}
-
-/* Sets core name for the next manual scan
- * operation (+ core path and other associated
- * parameters).
- * Returns true if core name is valid. */
-bool manual_content_scan_set_menu_core_name(
-      enum manual_content_scan_core_type core_type,
-      const char *core_name)
-{
-   /* Sanity check */
-   if (core_type > MANUAL_CONTENT_SCAN_CORE_SET)
-      goto error;
-
-   /* Cache core 'type' */
-   scan_settings.core_type = core_type;
-
-   /* Check if we are using core autodetection */
-   if (scan_settings.core_type == MANUAL_CONTENT_SCAN_CORE_DETECT)
-   {
-      scan_settings.core_name[0]      = '\0';
-      scan_settings.core_path[0]      = '\0';
-      scan_settings.file_exts_core[0] = '\0';
-   }
-   else
-   {
-      core_info_list_t *core_info_list = NULL;
-      core_info_t *core_info           = NULL;
-      bool core_found                  = false;
-      size_t i;
-
-      /* We are using a manually set core... */
-      if (string_is_empty(core_name))
-         goto error;
-
-      /* Get core list */
-      core_info_get_list(&core_info_list);
-
-      if (!core_info_list)
-         goto error;
-
-      /* Search for the specified core name */
-      for (i = 0; i < core_info_list->count; i++)
-      {
-         core_info = NULL;
-         core_info = core_info_get(core_info_list, i);
-
-         if (core_info)
-         {
-            if (string_is_equal(core_info->display_name, core_name))
-            {
-               /* Core has been found */
-               core_found = true;
-
-               /* Copy core path to settings struct */
-               if (string_is_empty(core_info->path))
-                  goto error;
-
-               strlcpy(
-                     scan_settings.core_path,
-                     core_info->path,
-                     sizeof(scan_settings.core_path));
-
-               /* Copy core name to settings struct */
-               strlcpy(
-                     scan_settings.core_name,
-                     core_info->display_name,
-                     sizeof(scan_settings.core_name));
-
-               /* Copy supported extensions to settings
-                * struct, if required */
-               if (!string_is_empty(core_info->supported_extensions))
-               {
-                  strlcpy(
-                        scan_settings.file_exts_core,
-                        core_info->supported_extensions,
-                        sizeof(scan_settings.file_exts_core));
-
-                  /* Core info extensions are delimited by
-                   * vertical bars. For internal consistency,
-                   * replace them with spaces */
-                  string_replace_all_chars(scan_settings.file_exts_core, '|', ' ');
-
-                  /* Apply standard scrubbing/clean-up
-                   * (should not be required, but must handle the
-                   * case where a core info file is incorrectly
-                   * formatted) */
-                  manual_content_scan_scrub_file_exts(scan_settings.file_exts_core);
-               }
-               else
-                  scan_settings.file_exts_core[0] = '\0';
-
-               break;
-            }
-         }
-      }
-
-      /* Sanity check */
-      if (!core_found)
-         goto error;
-   }
-
-   return true;
-
-error:
-   /* Input parameters are invalid - reset internal
-    * core values */
-   scan_settings.core_type         = MANUAL_CONTENT_SCAN_CORE_DETECT;
-   scan_settings.core_name[0]      = '\0';
-   scan_settings.core_path[0]      = '\0';
-   scan_settings.file_exts_core[0] = '\0';
-   return false;
-}
-
-/* Sets all parameters for the next manual scan
- * operation according the to recorded values in
- * the specified playlist.
- * Returns MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_OK
- * if playlist contains a valid scan record. */
-enum manual_content_scan_playlist_refresh_status
-      manual_content_scan_set_menu_from_playlist(playlist_t *playlist,
-            const char *path_content_database, bool show_hidden_files)
-{
-   const char *playlist_path    = NULL;
-   const char *playlist_file    = NULL;
-   const char *content_dir      = NULL;
-   const char *core_name        = NULL;
-   const char *file_exts        = NULL;
-   const char *dat_file_path    = NULL;
-   bool search_recursively      = false;
-   bool search_archives         = false;
-   bool filter_dat_content      = false;
-#ifdef HAVE_LIBRETRODB
-   struct string_list *rdb_list = NULL;
-#endif
-   enum manual_content_scan_system_name_type
-         system_name_type       = MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR;
-   enum manual_content_scan_core_type
-         core_type              = MANUAL_CONTENT_SCAN_CORE_DETECT;
-   enum manual_content_scan_playlist_refresh_status
-         playlist_status        = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_OK;
-   char system_name[PATH_MAX_LENGTH];
-
-   system_name[0] = '\0';
-
-   if (!playlist_scan_refresh_enabled(playlist))
-   {
-      playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_MISSING_CONFIG;
-      goto end;
-   }
-
-   /* Read scan parameters from playlist */
-   playlist_path      = playlist_get_conf_path(playlist);
-   content_dir        = playlist_get_scan_content_dir(playlist);
-   core_name          = playlist_get_default_core_name(playlist);
-   file_exts          = playlist_get_scan_file_exts(playlist);
-   dat_file_path      = playlist_get_scan_dat_file_path(playlist);
-
-   search_recursively = playlist_get_scan_search_recursively(playlist);
-   search_archives    = playlist_get_scan_search_archives(playlist);
-   filter_dat_content = playlist_get_scan_filter_dat_content(playlist);
-
-   /* Determine system name (playlist basename
-    * without extension) */
-   if (string_is_empty(playlist_path))
-   {
-      /* Cannot happen, but would constitute a
-       * 'system name' error */
-      playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_SYSTEM_NAME;
-      goto end;
-   }
-
-   if ((playlist_file = path_basename(playlist_path)))
-   {
-      strlcpy(system_name, playlist_file, sizeof(system_name));
-      path_remove_extension(system_name);
-   }
-
-   if (string_is_empty(system_name))
-   {
-      /* Cannot happen, but would constitute a
-       * 'system name' error */
-      playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_SYSTEM_NAME;
-      goto end;
-   }
-
-   /* Set content directory */
-   if (!manual_content_scan_set_menu_content_dir(content_dir))
-   {
-      playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CONTENT_DIR;
-      goto end;
-   }
-
-   /* Set system name */
-#ifdef HAVE_LIBRETRODB
-   /* > If platform has database support, get names
-    *   of all installed database files */
-   rdb_list = dir_list_new_special(
-         path_content_database,
-         DIR_LIST_DATABASES, NULL, show_hidden_files);
-
-   if (rdb_list && rdb_list->size)
-   {
-      size_t i;
-
-      /* Loop over database files */
-      for (i = 0; i < rdb_list->size; i++)
-      {
-         const char *rdb_path = rdb_list->elems[i].data;
-         const char *rdb_file = NULL;
-         char rdb_name[PATH_MAX_LENGTH];
-
-         rdb_name[0] = '\0';
-
-         /* Sanity check */
-         if (string_is_empty(rdb_path))
-            continue;
-
-         rdb_file = path_basename(rdb_path);
-
-         if (string_is_empty(rdb_file))
-            continue;
-
-         /* Remove file extension */
-         strlcpy(rdb_name, rdb_file, sizeof(rdb_name));
-         path_remove_extension(rdb_name);
-
-         if (string_is_empty(rdb_name))
-            continue;
-
-         /* Check whether playlist system name
-          * matches current database file */
-         if (string_is_equal(system_name, rdb_name))
-         {
-            system_name_type = MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE;
-            break;
-         }
-      }
-   }
-
-   string_list_free(rdb_list);
-#endif
-
-   /* > If system name does not match a database
-    *   file, then check whether it matches the
-    *   content directory name */
-   if (system_name_type !=
-         MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE)
-   {
-      /* system_name_type is set to
-       * MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR
-       * by default - so if a match is found just
-       * reset 'custom name' field */
-      if (string_is_equal(system_name,
-            scan_settings.system_name_content_dir))
-         scan_settings.system_name_custom[0] = '\0';
-      else
-      {
-         /* Playlist is using a custom system name */
-         system_name_type = MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM;
-         strlcpy(scan_settings.system_name_custom, system_name,
-               sizeof(scan_settings.system_name_custom));
-      }
-   }
-
-   if (!manual_content_scan_set_menu_system_name(
-         system_name_type, system_name))
-   {
-      playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_SYSTEM_NAME;
-      goto end;
-   }
-
-   /* Set core path/name */
-   if (!string_is_empty(core_name) &&
-       !string_is_equal(core_name, FILE_PATH_DETECT))
-      core_type = MANUAL_CONTENT_SCAN_CORE_SET;
-
-   if (!manual_content_scan_set_menu_core_name(
-         core_type, core_name))
-   {
-      playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CORE;
-      goto end;
-   }
-
-   /* Set custom file extensions */
-   if (string_is_empty(file_exts))
-      scan_settings.file_exts_custom[0] = '\0';
-   else
-   {
-      strlcpy(scan_settings.file_exts_custom, file_exts,
-            sizeof(scan_settings.file_exts_custom));
-
-      /* File extensions read from playlist should
-       * be correctly formatted, with '|' characters
-       * as delimiters
-       * > For menu purposes, must replace these
-       *   delimiters with space characters
-       * > Additionally scrub the resultant string,
-       *   to handle the case where a user has
-       *   'corrupted' it by manually tampering with
-       *   the playlist file */
-      string_replace_all_chars(scan_settings.file_exts_custom, '|', ' ');
-      manual_content_scan_scrub_file_exts(scan_settings.file_exts_custom);
-   }
-
-   /* Set DAT file path */
-   if (string_is_empty(dat_file_path))
-      scan_settings.dat_file_path[0] = '\0';
-   else
-   {
-      strlcpy(scan_settings.dat_file_path, dat_file_path,
-            sizeof(scan_settings.dat_file_path));
-
-      switch (manual_content_scan_validate_dat_file_path())
-      {
-         case MANUAL_CONTENT_SCAN_DAT_FILE_INVALID:
-            playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_DAT_FILE;
-            goto end;
-         case MANUAL_CONTENT_SCAN_DAT_FILE_TOO_LARGE:
-            playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_DAT_FILE_TOO_LARGE;
-            goto end;
-         default:
-            /* No action required */
-            break;
-      }
-   }
-
-   /* Set remaining boolean parameters */
-   scan_settings.search_recursively = search_recursively;
-   scan_settings.search_archives    = search_archives;
-   scan_settings.filter_dat_content = filter_dat_content;
-   /* When refreshing a playlist:
-    * > We never overwrite the existing file
-    * > We always validate entries in the
-    *   existing file */
-   scan_settings.overwrite_playlist = false;
-   scan_settings.validate_entries   = true;
-
-end:
-   return playlist_status;
-}
-
-/* Menu getters */
-
-/* Fetches content directory for next manual scan
- * operation.
- * Returns true if content directory is valid. */
-bool manual_content_scan_get_menu_content_dir(const char **content_dir)
-{
-   if (!content_dir)
-      return false;
-
-   if (string_is_empty(scan_settings.content_dir))
-      return false;
-
-   *content_dir = scan_settings.content_dir;
-
-   return true;
-}
-
-/* Fetches system name for the next manual scan operation.
- * Returns true if system name is valid.
- * NOTE: This corresponds to the 'System Name' value
- * displayed in menus - this is not identical to the
- * actual system name used when generating the playlist */
-bool manual_content_scan_get_menu_system_name(const char **system_name)
-{
-   if (!system_name)
-      return false;
-
-   switch (scan_settings.system_name_type)
-   {
-      case MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR:
-         *system_name = msg_hash_to_str(
-               MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CONTENT_DIR);
-         return true;
-      case MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM:
-         *system_name = msg_hash_to_str(
-               MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CUSTOM);
-         return true;
-      case MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE:
-         if (string_is_empty(scan_settings.system_name_database))
-            return false;
-         else
-         {
-            *system_name = scan_settings.system_name_database;
-            return true;
-         }
-      default:
-         break;
-   }
-
-   return false;
-}
-
-/* Fetches core name for the next manual scan operation.
- * Returns true if core name is valid. */
-bool manual_content_scan_get_menu_core_name(const char **core_name)
-{
-   if (!core_name)
-      return false;
-
-   switch (scan_settings.core_type)
-   {
-      case MANUAL_CONTENT_SCAN_CORE_DETECT:
-         *core_name = msg_hash_to_str(
-               MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_CORE_NAME_DETECT);
-         return true;
-      case MANUAL_CONTENT_SCAN_CORE_SET:
-         if (string_is_empty(scan_settings.core_name))
-            return false;
-         else
-         {
-            *core_name = scan_settings.core_name;
-            return true;
-         }
-      default:
-         break;
-   }
-
-   return false;
-}
-
-/* Menu utility functions */
-
-/* Creates a list of all possible 'system name' menu
- * strings, for use in 'menu_displaylist' drop-down
- * lists and 'menu_cbs_left/right'
- * > Returns NULL in the event of failure
- * > Returned string list must be free()'d */
-struct string_list *manual_content_scan_get_menu_system_name_list(
-      const char *path_content_database, bool show_hidden_files)
-{
-   union string_list_elem_attr attr;
-   struct string_list *name_list = string_list_new();
-
-   /* Sanity check */
-   if (!name_list)
-      return NULL;
-
-   attr.i = 0;
-
-   /* Add 'use content directory' entry */
-   if (!string_list_append(name_list, msg_hash_to_str(
-         MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CONTENT_DIR), attr))
-      goto error;
-
-   /* Add 'use custom' entry */
-   if (!string_list_append(name_list, msg_hash_to_str(
-         MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CUSTOM), attr))
-      goto error;
-
-#ifdef HAVE_LIBRETRODB
-   /* If platform has database support, get names
-    * of all installed database files */
-   {
-      /* Note: dir_list_new_special() is well behaved - the
-       * returned string list will only include database
-       * files (i.e. don't have to check for directories,
-       * or verify file extensions) */
-      struct string_list *rdb_list = dir_list_new_special(
-            path_content_database,
-            DIR_LIST_DATABASES, NULL, show_hidden_files);
-
-      if (rdb_list && rdb_list->size)
-      {
-         unsigned i;
-
-         /* Ensure database list is in alphabetical order */
-         dir_list_sort(rdb_list, true);
-
-         /* Loop over database files */
-         for (i = 0; i < rdb_list->size; i++)
-         {
-            const char *rdb_path = rdb_list->elems[i].data;
-            const char *rdb_file = NULL;
-            char rdb_name[PATH_MAX_LENGTH];
-
-            rdb_name[0] = '\0';
-
-            /* Sanity check */
-            if (string_is_empty(rdb_path))
-               continue;
-
-            rdb_file = path_basename(rdb_path);
-
-            if (string_is_empty(rdb_file))
-               continue;
-
-            /* Remove file extension */
-            strlcpy(rdb_name, rdb_file, sizeof(rdb_name));
-            path_remove_extension(rdb_name);
-
-            if (string_is_empty(rdb_name))
-               continue;
-
-            /* Add database name to list */
-            if (!string_list_append(name_list, rdb_name, attr))
-               goto error;
-         }
-      }
-
-      /* Clean up */
-      string_list_free(rdb_list);
-   }
-
-#endif
-
-   return name_list;
-
-error:
-   if (name_list)
-      string_list_free(name_list);
-   return NULL;
-}
-
-/* Creates a list of all possible 'core name' menu
- * strings, for use in 'menu_displaylist' drop-down
- * lists and 'menu_cbs_left/right'
- * > Returns NULL in the event of failure
- * > Returned string list must be free()'d */
-struct string_list *manual_content_scan_get_menu_core_name_list(void)
-{
-   struct string_list *name_list    = string_list_new();
-   core_info_list_t *core_info_list = NULL;
-   union string_list_elem_attr attr;
-
-   /* Sanity check */
-   if (!name_list)
-      goto error;
-
-   attr.i = 0;
-
-   /* Add 'DETECT' entry */
-   if (!string_list_append(name_list, msg_hash_to_str(
-         MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_CORE_NAME_DETECT), attr))
-      goto error;
-
-   /* Get core list */
-   core_info_get_list(&core_info_list);
-
-   if (core_info_list)
-   {
-      core_info_t *core_info = NULL;
-      size_t i;
-
-      /* Sort cores alphabetically */
-      core_info_qsort(core_info_list, CORE_INFO_LIST_SORT_DISPLAY_NAME);
-
-      /* Loop through cores */
-      for (i = 0; i < core_info_list->count; i++)
-      {
-         core_info = NULL;
-         core_info = core_info_get(core_info_list, i);
-
-         if (core_info)
-         {
-            if (string_is_empty(core_info->display_name))
-               continue;
-
-            /* Add core name to list */
-            if (!string_list_append(name_list, core_info->display_name, attr))
-               goto error;
-         }
-      }
-   }
-
-   return name_list;
-
-error:
-   if (name_list)
-      string_list_free(name_list);
-   return NULL;
-}
-
-/****************/
-/* Task Helpers */
-/****************/
-
-/* Parses current manual content scan settings,
- * and extracts all information required to configure
- * a manual content scan task.
- * Returns false if current settings are invalid. */
-bool manual_content_scan_get_task_config(
-      manual_content_scan_task_config_t *task_config,
-      const char *path_dir_playlist
-      )
-{
-   size_t len;
-   if (!task_config)
-      return false;
-
-   /* Ensure all 'task_config' strings are
-    * correctly initialised */
-   task_config->playlist_file[0] = '\0';
-   task_config->content_dir[0]   = '\0';
-   task_config->system_name[0]   = '\0';
-   task_config->database_name[0] = '\0';
-   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))
-      return false;
-
-   if (!path_is_directory(scan_settings.content_dir))
-      return false;
-
-   strlcpy(
-         task_config->content_dir,
-         scan_settings.content_dir,
-         sizeof(task_config->content_dir));
-
-   /* Get system name */
-   switch (scan_settings.system_name_type)
-   {
-      case MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR:
-         if (string_is_empty(scan_settings.system_name_content_dir))
-            return false;
-
-         strlcpy(
-               task_config->system_name,
-               scan_settings.system_name_content_dir,
-               sizeof(task_config->system_name));
-
-         break;
-      case MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM:
-         if (string_is_empty(scan_settings.system_name_custom))
-            return false;
-
-         strlcpy(
-               task_config->system_name,
-               scan_settings.system_name_custom,
-               sizeof(task_config->system_name));
-
-         break;
-      case MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE:
-         if (string_is_empty(scan_settings.system_name_database))
-            return false;
-
-         strlcpy(
-               task_config->system_name,
-               scan_settings.system_name_database,
-               sizeof(task_config->system_name));
-
-         break;
-      default:
-         return false;
-   }
-
-   /* Now we have a valid system name, can generate
-    * a 'database' name... */
-   len = strlcpy(
-         task_config->database_name,
-         task_config->system_name,
-         sizeof(task_config->database_name));
-   task_config->database_name[len  ] = '.';
-   task_config->database_name[len+1] = 'l';
-   task_config->database_name[len+2] = 'p';
-   task_config->database_name[len+3] = 'l';
-   task_config->database_name[len+4] = '\0';
-
-   /* ...which can in turn be used to generate the
-    * playlist path */
-   if (string_is_empty(path_dir_playlist))
-      return false;
-
-   fill_pathname_join_special(
-         task_config->playlist_file,
-         path_dir_playlist,
-         task_config->database_name,
-         sizeof(task_config->playlist_file));
-
-   if (string_is_empty(task_config->playlist_file))
-      return false;
-
-   /* Get core name and path */
-   switch (scan_settings.core_type)
-   {
-      case MANUAL_CONTENT_SCAN_CORE_DETECT:
-         task_config->core_set = false;
-         break;
-      case MANUAL_CONTENT_SCAN_CORE_SET:
-         task_config->core_set = true;
-
-         if (string_is_empty(scan_settings.core_name))
-            return false;
-         if (string_is_empty(scan_settings.core_path))
-            return false;
-
-         strlcpy(
-               task_config->core_name,
-               scan_settings.core_name,
-               sizeof(task_config->core_name));
-
-         strlcpy(
-               task_config->core_path,
-               scan_settings.core_path,
-               sizeof(task_config->core_path));
-
-         break;
-      default:
-         return false;
-   }
-
-   /* Get file extensions list */
-   task_config->file_exts_custom_set = false;
-   if (!string_is_empty(scan_settings.file_exts_custom))
-   {
-      task_config->file_exts_custom_set = true;
-      strlcpy(
-            task_config->file_exts,
-            scan_settings.file_exts_custom,
-            sizeof(task_config->file_exts));
-   }
-   else if (scan_settings.core_type == MANUAL_CONTENT_SCAN_CORE_SET)
-      if (!string_is_empty(scan_settings.file_exts_core))
-         strlcpy(
-               task_config->file_exts,
-               scan_settings.file_exts_core,
-               sizeof(task_config->file_exts));
-
-   /* Our extension lists are space delimited
-    * > dir_list_new() expects vertical bar
-    *   delimiters, so find and replace */
-   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 recursively' setting */
-   task_config->search_recursively = scan_settings.search_recursively;
-   /* Copy 'search inside archives' setting */
-   task_config->search_archives    = scan_settings.search_archives;
-   /* Copy 'DAT file filter' setting */
-   task_config->filter_dat_content = scan_settings.filter_dat_content;
-   /* Copy 'overwrite playlist' setting */
-   task_config->overwrite_playlist = scan_settings.overwrite_playlist;
-   /* Copy 'validate_entries' setting */
-   task_config->validate_entries   = scan_settings.validate_entries;
-
-   return true;
-}
-
-/* Creates a list of all valid content in the specified
- * content directory
- * > Returns NULL in the event of failure
- * > Returned string list must be free()'d */
-struct string_list *manual_content_scan_get_content_list(
-      manual_content_scan_task_config_t *task_config)
-{
-   struct string_list *dir_list = NULL;
-   bool filter_exts;
-   bool include_compressed;
-
-   /* Sanity check */
-   if (!task_config)
-      goto error;
-
-   if (string_is_empty(task_config->content_dir))
-      goto error;
-
-   /* Check whether files should be filtered by
-    * extension */
-   filter_exts = !string_is_empty(task_config->file_exts);
-
-   /* Check whether compressed files should be
-    * included in the directory list
-    * > If compressed files are already listed in
-    *   the 'file_exts' string, they will be included
-    *   automatically
-    * > If we don't have a 'file_exts' list, then all
-    *   files must be included regardless of type
-    * > If user has enabled 'search inside archives',
-    *   then compressed files must of course be included */
-   include_compressed = (!filter_exts || task_config->search_archives);
-
-   /* Get directory listing
-    * > Exclude directories and hidden files */
-   dir_list = dir_list_new(
-         task_config->content_dir,
-         filter_exts ? task_config->file_exts : NULL,
-         false, /* include_dirs */
-         false, /* include_hidden */
-         include_compressed,
-         task_config->search_recursively
-   );
-
-   /* Sanity check */
-   if (!dir_list)
-      goto error;
-
-   if (dir_list->size < 1)
-      goto error;
-
-   /* Ensure list is in alphabetical order
-    * > Not strictly required, but task status
-    *   messages will be unintuitive if we leave
-    *   the order 'random' */
-   dir_list_sort(dir_list, true);
-
-   return dir_list;
-
-error:
-   if (dir_list)
-      string_list_free(dir_list);
-   return NULL;
-}
-
-/* Converts specified content path string to 'real'
- * file path for use in playlists - i.e. handles
- * identification of content *inside* archive files.
- * Returns false if specified content is invalid. */
-static bool manual_content_scan_get_playlist_content_path(
-      manual_content_scan_task_config_t *task_config,
-      const char *content_path, int content_type,
-      char *s, size_t len)
-{
-   size_t _len;
-   struct string_list *archive_list = NULL;
-
-   /* Sanity check */
-   if (!task_config || string_is_empty(content_path))
-      return false;
-
-   if (!path_is_valid(content_path))
-      return false;
-
-   /* In all cases, base content path must be
-    * copied to @s */
-   _len = strlcpy(s, content_path, len);
-
-   /* Check whether this is an archive file
-    * requiring special attention... */
-   if ((content_type == RARCH_COMPRESSED_ARCHIVE) &&
-       task_config->search_archives)
-   {
-      bool filter_exts         = !string_is_empty(task_config->file_exts);
-      const char *archive_file = NULL;
-
-      /* Important note:
-       * > If an archive file of a particular type is
-       *   included in the task_config->file_exts list,
-       *   dir_list_new() will assign it a file type of
-       *   RARCH_PLAIN_FILE
-       * > Thus we will only reach this point if
-       *   (a) We are not filtering by extension
-       *   (b) This is an archive file type *not*
-       *       already included in the supported
-       *       extensions list
-       * > These guarantees substantially reduce the
-       *   complexity of the following code... */
-
-      /* Get archive file contents */
-      if (!(archive_list = file_archive_get_file_list(
-            content_path, filter_exts ? task_config->file_exts : NULL)))
-         goto error;
-
-      if (archive_list->size < 1)
-         goto error;
-
-      /* Get first file contained in archive */
-      dir_list_sort(archive_list, true);
-      archive_file = archive_list->elems[0].data;
-      if (string_is_empty(archive_file))
-         goto error;
-
-      /* Have to take care to ensure that we don't make
-       * a mess of arcade content...
-       * > If we are filtering by extension, then the
-       *   archive file itself is *not* valid content,
-       *   so link to the first file inside the archive
-       * > If we are not filtering by extension, then:
-       *   - If archive contains one valid file, assume
-       *     it is a compressed ROM
-       *   - If archive contains multiple files, have to
-       *     assume it is MAME/FBA-style content, where
-       *     only the archive itself is valid */
-      if (filter_exts || (archive_list->size == 1))
-      {
-         /* Build path to file inside archive */
-         s[_len  ] = '#';
-         s[_len+1] = '\0';
-         strlcat(s, archive_file, len);
-      }
-
-      string_list_free(archive_list);
-   }
-
-   return true;
-
-error:
-   if (archive_list)
-      string_list_free(archive_list);
-   return false;
-}
-
-/* Extracts content 'label' (name) from content path
- * > If a DAT file is specified, 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,
-      bool filter_dat_content,
-      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_pathname(content_label, path_basename(content_path), 
-         "", len);
-
-   if (string_is_empty(content_label))
-      return false;
-
-   /* Check if a DAT file has been specified */
-   if (dat_file)
-   {
-      bool content_found = false;
-      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);
-            content_found = true;
-         }
-      }
-
-      /* If we are applying a DAT file filter,
-       * unlisted content should be skipped */
-      if (!content_found && filter_dat_content)
-         return false;
-   }
-
-   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, logiqx_dat_t *dat_file)
-{
-   char playlist_content_path[PATH_MAX_LENGTH];
-
-   /* Sanity check */
-   if (!task_config || !playlist)
-      return;
-
-   /* Get 'actual' content path */
-   if (!manual_content_scan_get_playlist_content_path(
-         task_config, content_path, content_type,
-         playlist_content_path, sizeof(playlist_content_path)))
-      return;
-
-   /* Check whether content is already included
-    * in playlist */
-   if (!playlist_entry_exists(playlist, playlist_content_path))
-   {
-      struct playlist_entry entry = {0};
-      char label[PATH_MAX_LENGTH];
-
-      label[0] = '\0';
-
-      /* Get entry label */
-      if (!manual_content_scan_get_playlist_content_label(
-            playlist_content_path, dat_file,
-            task_config->filter_dat_content,
-            label, sizeof(label)))
-         return;
-
-      /* Configure playlist entry
-       * > The push function reads our entry as const,
-       *   so these casts are safe */
-      entry.path       = (char*)playlist_content_path;
-      entry.label      = label;
-      entry.core_path  = (char*)FILE_PATH_DETECT;
-      entry.core_name  = (char*)FILE_PATH_DETECT;
-      entry.crc32      = (char*)"00000000|crc";
-      entry.db_name    = task_config->database_name;
-      entry.entry_slot = 0;
-
-      /* Add entry to playlist */
-      playlist_push(playlist, &entry);
-   }
-}

+ 0 - 278
app/src/main/cpp/manual_content_scan.h

@@ -1,278 +0,0 @@
-/* Copyright  (C) 2010-2019 The RetroArch team
- *
- * ---------------------------------------------------------------------------------------
- * The following license statement only applies to this file (manual_content_scan.c).
- * ---------------------------------------------------------------------------------------
- *
- * Permission is hereby granted, free of charge,
- * to any person obtaining a copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#ifndef __MANUAL_CONTENT_SCAN_H
-#define __MANUAL_CONTENT_SCAN_H
-
-#include <retro_common_api.h>
-#include <libretro.h>
-
-#include <boolean.h>
-
-#include <lists/string_list.h>
-#include <formats/logiqx_dat.h>
-
-#include "playlist.h"
-
-RETRO_BEGIN_DECLS
-
-/* Defines all possible system name types
- * > Use content directory name
- * > Use custom name
- * > Use database name */
-enum manual_content_scan_system_name_type
-{
-   MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR = 0,
-   MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM,
-   MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE
-};
-
-/* Defines all possible core name types
- * > Autodetect core (DETECT)
- * > Use manually set core */
-enum manual_content_scan_core_type
-{
-   MANUAL_CONTENT_SCAN_CORE_DETECT = 0,
-   MANUAL_CONTENT_SCAN_CORE_SET
-};
-
-/* Defines all possible return values for
- * manual_content_scan_validate_dat_file_path() */
-enum manual_content_scan_dat_file_path_status
-{
-   MANUAL_CONTENT_SCAN_DAT_FILE_UNSET = 0,
-   MANUAL_CONTENT_SCAN_DAT_FILE_OK,
-   MANUAL_CONTENT_SCAN_DAT_FILE_INVALID,
-   MANUAL_CONTENT_SCAN_DAT_FILE_TOO_LARGE
-};
-
-/* Defines all possible return values for
- * manual_content_scan_set_menu_from_playlist() */
-enum manual_content_scan_playlist_refresh_status
-{
-   MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_OK = 0,
-   MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_MISSING_CONFIG,
-   MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CONTENT_DIR,
-   MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_SYSTEM_NAME,
-   MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CORE,
-   MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_DAT_FILE,
-   MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_DAT_FILE_TOO_LARGE
-};
-
-/* Holds all configuration parameters required
- * for a manual content scan task */
-typedef struct
-{
-   char playlist_file[PATH_MAX_LENGTH];
-   char content_dir[PATH_MAX_LENGTH];
-   char system_name[PATH_MAX_LENGTH];
-   char database_name[PATH_MAX_LENGTH];
-   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 file_exts_custom_set;
-   bool search_recursively;
-   bool search_archives;
-   bool filter_dat_content;
-   bool overwrite_playlist;
-   bool validate_entries;
-} manual_content_scan_task_config_t;
-
-/*****************/
-/* Configuration */
-/*****************/
-
-/* Pointer access
- * > This is a little ugly, but it allows us to
- *   make use of standard 'menu_settings' code
- *   for several config parameters (rather than
- *   implementing unnecessary custom menu entries) */
-
-/* Returns a pointer to the internal
- * 'content_dir' string */
-char *manual_content_scan_get_content_dir_ptr(void);
-
-/* Returns a pointer to the internal
- * 'system_name_custom' string */
-char *manual_content_scan_get_system_name_custom_ptr(void);
-
-/* Returns size of the internal
- * 'system_name_custom' string */
-size_t manual_content_scan_get_system_name_custom_size(void);
-
-/* Returns a pointer to the internal
- * 'file_exts_custom' string */
-char *manual_content_scan_get_file_exts_custom_ptr(void);
-
-/* Returns size of the internal
- * 'file_exts_custom' string */
-size_t manual_content_scan_get_file_exts_custom_size(void);
-
-/* Returns a pointer to the internal
- * 'dat_file_path' string */
-char *manual_content_scan_get_dat_file_path_ptr(void);
-
-/* Returns size of the internal
- * 'dat_file_path' string */
-size_t manual_content_scan_get_dat_file_path_size(void);
-
-/* Returns a pointer to the internal
- * 'search_recursively' bool */
-bool *manual_content_scan_get_search_recursively_ptr(void);
-
-/* Returns a pointer to the internal
- * 'search_archives' bool */
-bool *manual_content_scan_get_search_archives_ptr(void);
-
-/* Returns a pointer to the internal
- * 'filter_dat_content' bool */
-bool *manual_content_scan_get_filter_dat_content_ptr(void);
-
-/* Returns a pointer to the internal
- * 'overwrite_playlist' bool */
-bool *manual_content_scan_get_overwrite_playlist_ptr(void);
-
-/* Returns a pointer to the internal
- * 'validate_entries' bool */
-bool *manual_content_scan_get_validate_entries_ptr(void);
-
-/* Sanitisation */
-
-/* Removes invalid characters from
- * 'system_name_custom' string */
-void manual_content_scan_scrub_system_name_custom(void);
-
-/* Removes period (full stop) characters from
- * 'file_exts_custom' string and converts to
- * lower case */
-void manual_content_scan_scrub_file_exts_custom(void);
-
-/* Checks 'dat_file_path' string and resets it
- * if invalid */
-enum manual_content_scan_dat_file_path_status
-      manual_content_scan_validate_dat_file_path(void);
-
-/* Menu setters */
-
-/* Sets content directory for next manual scan
- * operation.
- * Returns true if content directory is valid. */
-bool manual_content_scan_set_menu_content_dir(const char *content_dir);
-
-/* Sets system name for the next manual scan
- * operation.
- * Returns true if system name is valid.
- * NOTE:
- * > Only sets 'system_name_type' and 'system_name_database'
- * > 'system_name_content_dir' and 'system_name_custom' are
- *   (by necessity) handled elsewhere
- * > This may look fishy, but it's not - it's a menu-specific
- *   function, and this is simply the cleanest way to handle
- *   the setting... */
-bool manual_content_scan_set_menu_system_name(
-      enum manual_content_scan_system_name_type system_name_type,
-      const char *system_name);
-
-/* Sets core name for the next manual scan
- * operation (+ core path and other associated
- * parameters).
- * Returns true if core name is valid. */
-bool manual_content_scan_set_menu_core_name(
-      enum manual_content_scan_core_type core_type,
-      const char *core_name);
-
-/* Sets all parameters for the next manual scan
- * operation according the to recorded values in
- * the specified playlist.
- * Returns MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_OK
- * if playlist contains a valid scan record. */
-enum manual_content_scan_playlist_refresh_status
-      manual_content_scan_set_menu_from_playlist(playlist_t *playlist,
-            const char *path_content_database, bool show_hidden_files);
-
-/* Menu getters */
-
-/* Fetches content directory for next manual scan
- * operation.
- * Returns true if content directory is valid. */
-bool manual_content_scan_get_menu_content_dir(const char **content_dir);
-
-/* Fetches system name for the next manual scan operation.
- * Returns true if system name is valid.
- * NOTE: This corresponds to the 'System Name' value
- * displayed in menus - this is not identical to the
- * actual system name used when generating the playlist */
-bool manual_content_scan_get_menu_system_name(const char **system_name);
-
-/* Fetches core name for the next manual scan operation.
- * Returns true if core name is valid. */
-bool manual_content_scan_get_menu_core_name(const char **core_name);
-
-/* Menu utility functions */
-
-/* Creates a list of all possible 'system name' menu
- * strings, for use in 'menu_displaylist' drop-down
- * lists and 'menu_cbs_left/right'
- * > Returns NULL in the event of failure
- * > Returned string list must be free()'d */
-struct string_list *manual_content_scan_get_menu_system_name_list(
-      const char *path_content_database, bool show_hidden_files);
-
-/* Creates a list of all possible 'core name' menu
- * strings, for use in 'menu_displaylist' drop-down
- * lists and 'menu_cbs_left/right'
- * > Returns NULL in the event of failure
- * > Returned string list must be free()'d */
-struct string_list *manual_content_scan_get_menu_core_name_list(void);
-
-/****************/
-/* Task Helpers */
-/****************/
-
-/* Parses current manual content scan settings,
- * and extracts all information required to configure
- * a manual content scan task.
- * Returns false if current settings are invalid. */
-bool manual_content_scan_get_task_config(
-      manual_content_scan_task_config_t *task_config,
-      const char *path_dir_playlist
-      );
-
-/* Creates a list of all valid content in the specified
- * content directory
- * > Returns NULL in the event of failure
- * > Returned string list must be free()'d */
-struct string_list *manual_content_scan_get_content_list(
-      manual_content_scan_task_config_t *task_config);
-
-/* Adds specified content to playlist, if not already
- * present */
-void manual_content_scan_add_content_to_playlist(
-      manual_content_scan_task_config_t *task_config,
-      playlist_t *playlist, const char *content_path,
-      int content_type, logiqx_dat_t *dat_file);
-
-RETRO_END_DECLS
-
-#endif

+ 2 - 2
app/src/main/cpp/play_feature_delivery/play_feature_delivery.c

@@ -246,7 +246,7 @@ JNIEXPORT void JNICALL Java_com_retroarch_browser_retroactivity_RetroActivityCom
 
    RARCH_LOG("[Video] Filter path: %s", settings->paths.path_softfilter_plugin);
 
-   runloop_st->flags2 |= RUNLOOP_FLAG_NEED_REINIT_DRIVERS;
+//   runloop_st->flags2 |= RUNLOOP_FLAG_NEED_REINIT_DRIVERS;
 }
 
 JNIEXPORT void JNICALL Java_com_retroarch_browser_retroactivity_RetroActivityCommon_setAspectRatio
@@ -288,7 +288,7 @@ JNIEXPORT void JNICALL Java_com_retroarch_browser_retroactivity_RetroActivityCom
    custom_vp->y = y;
    custom_vp->width = width;
    custom_vp->height = height;
-   command_event(CMD_EVENT_VIDEO_APPLY_STATE_CHANGES, NULL);
+//   command_event(CMD_EVENT_VIDEO_APPLY_STATE_CHANGES, NULL);
 }
 /******************/
 /* Initialisation */

+ 0 - 3510
app/src/main/cpp/playlist.c

@@ -1,3510 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- *  Copyright (C) 2011-2017 - Daniel De Matteis
- *  Copyright (C) 2016-2019 - Brad Parker
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
-#include <libretro.h>
-#include <boolean.h>
-#include <retro_miscellaneous.h>
-#include <compat/posix_string.h>
-#include <string/stdstring.h>
-#include <streams/interface_stream.h>
-#include <file/file_path.h>
-#include <file/archive_file.h>
-#include <lists/string_list.h>
-#include <formats/rjson.h>
-#include <array/rbuf.h>
-
-#include "playlist.h"
-#include "verbosity.h"
-#include "file_path_special.h"
-#include "core_info.h"
-
-#if defined(ANDROID)
-#include "play_feature_delivery/play_feature_delivery.h"
-#endif
-
-#ifndef PLAYLIST_ENTRIES
-#define PLAYLIST_ENTRIES 6
-#endif
-
-#define WINDOWS_PATH_DELIMITER '\\'
-#define POSIX_PATH_DELIMITER '/'
-
-#ifdef _WIN32
-#define LOCAL_FILE_SYSTEM_PATH_DELIMITER WINDOWS_PATH_DELIMITER
-#define USING_WINDOWS_FILE_SYSTEM
-#else
-#define LOCAL_FILE_SYSTEM_PATH_DELIMITER POSIX_PATH_DELIMITER
-#define USING_POSIX_FILE_SYSTEM
-#endif
-
-/* Holds all configuration parameters required
- * to repeat a manual content scan for a
- * previously manual-scan-generated playlist */
-typedef struct
-{
-   char *content_dir;
-   char *file_exts;
-   char *dat_file_path;
-   bool search_recursively;
-   bool search_archives;
-   bool filter_dat_content;
-} playlist_manual_scan_record_t;
-
-struct content_playlist
-{
-   char *default_core_path;
-   char *default_core_name;
-   char *base_content_directory;
-
-   struct playlist_entry *entries;
-
-   playlist_manual_scan_record_t scan_record; /* ptr alignment */
-   playlist_config_t config;                  /* size_t alignment */
-
-   enum playlist_label_display_mode label_display_mode;
-   enum playlist_thumbnail_mode right_thumbnail_mode;
-   enum playlist_thumbnail_mode left_thumbnail_mode;
-   enum playlist_sort_mode sort_mode;
-
-   bool modified;
-   bool old_format;
-   bool compressed;
-   bool cached_external;
-};
-
-typedef struct
-{
-   struct playlist_entry *current_entry;
-   char **current_string_val;
-   unsigned *current_entry_uint_val;
-   enum playlist_label_display_mode *current_meta_label_display_mode_val;
-   enum playlist_thumbnail_mode *current_meta_thumbnail_mode_val;
-   enum playlist_sort_mode *current_meta_sort_mode_val;
-   bool *current_meta_bool_val;
-   playlist_t *playlist;
-
-   unsigned array_depth;
-   unsigned object_depth;
-
-   bool in_items;
-   bool in_subsystem_roms;
-   bool capacity_exceeded;
-   bool out_of_memory;
-} JSONContext;
-
-/* TODO/FIXME - global state - perhaps move outside this file */
-static playlist_t *playlist_cached = NULL;
-
-typedef int (playlist_sort_fun_t)(
-      const struct playlist_entry *a,
-      const struct playlist_entry *b);
-
-/* TODO/FIXME - hack for allowing the explore view to switch 
- * over to a playlist item */
-void playlist_set_cached_external(playlist_t* pl)
-{
-   playlist_free_cached();
-   if (!pl)
-      return;
-
-   playlist_cached = pl;
-   playlist_cached->cached_external = true;
-}
-
-/* Convenience function: copies specified playlist
- * path to specified playlist configuration object */
-void playlist_config_set_path(playlist_config_t *config, const char *path)
-{
-   if (!config)
-      return;
-
-   if (!string_is_empty(path))
-      strlcpy(config->path, path, sizeof(config->path));
-   else
-      config->path[0] = '\0';
-}
-
-/* Convenience function: copies base content directory
- * path to specified playlist configuration object.
- * Also sets autofix_paths boolean, depending on base 
- * content directory value */
-void playlist_config_set_base_content_directory(
-      playlist_config_t* config, const char* path)
-{
-   if (!config)
-      return;
-
-   config->autofix_paths = !string_is_empty(path);
-   if (config->autofix_paths)
-      strlcpy(config->base_content_directory, path,
-            sizeof(config->base_content_directory));
-   else
-      config->base_content_directory[0] = '\0';
-}
-
-
-/* Creates a copy of the specified playlist configuration.
- * Returns false in the event of an error */
-bool playlist_config_copy(const playlist_config_t *src,
-      playlist_config_t *dst)
-{
-   if (!src || !dst)
-      return false;
-
-   strlcpy(dst->path, src->path, sizeof(dst->path));
-   strlcpy(dst->base_content_directory, src->base_content_directory,
-         sizeof(dst->base_content_directory));
-
-   dst->capacity            = src->capacity;
-   dst->old_format          = src->old_format;
-   dst->compress            = src->compress;
-   dst->fuzzy_archive_match = src->fuzzy_archive_match;
-   dst->autofix_paths       = src->autofix_paths;
-
-   return true;
-}
-
-/* Returns internal playlist configuration object
- * of specified playlist.
- * Returns NULL it the event of an error. */
-playlist_config_t *playlist_get_config(playlist_t *playlist)
-{
-   if (!playlist)
-      return NULL;
-
-   return &playlist->config;
-}
-
-static void path_replace_base_path_and_convert_to_local_file_system(
-      char *out_path, const char *in_path,
-      const char *in_oldrefpath, const char *in_refpath,
-      size_t size)
-{
-   size_t in_oldrefpath_length = strlen(in_oldrefpath);
-   size_t in_refpath_length    = strlen(in_refpath);
-
-   /* If entry path is inside playlist base path,
-    * replace it with new base content directory */
-   if (string_starts_with_size(in_path, in_oldrefpath, in_oldrefpath_length))
-   {
-      memcpy(out_path, in_refpath, in_refpath_length);
-      memcpy(out_path + in_refpath_length, in_path + in_oldrefpath_length,
-            strlen(in_path) - in_oldrefpath_length + 1);
-
-#ifdef USING_WINDOWS_FILE_SYSTEM
-      /* If we are running under a Windows filesystem,
-       * '/' characters are not allowed anywhere. 
-       * We replace with '\' and hope for the best... */
-      string_replace_all_chars(out_path,
-            POSIX_PATH_DELIMITER, WINDOWS_PATH_DELIMITER);
-#endif
-
-#ifdef USING_POSIX_FILE_SYSTEM
-      /* Under POSIX filesystem, we replace '\' characters with '/' */
-      string_replace_all_chars(out_path,
-            WINDOWS_PATH_DELIMITER, POSIX_PATH_DELIMITER);
-#endif
-   }
-   else
-      strlcpy(out_path, in_path, size);
-}
-
-/* Generates a case insensitive hash for the
- * specified path string */
-static uint32_t playlist_path_hash(const char *path)
-{
-   unsigned char c;
-   uint32_t hash = (uint32_t)0x811c9dc5;
-   while ((c = (unsigned char)*(path++)) != '\0')
-      hash = ((hash * (uint32_t)0x01000193) ^ (uint32_t)((c >= 'A' && c <= 'Z') ? (c | 0x20) : c));
-   return (hash ? hash : 1);
-}
-
-static void playlist_path_id_free(playlist_path_id_t *path_id)
-{
-   if (!path_id)
-      return;
-
-   if (path_id->archive_path &&
-       (path_id->archive_path != path_id->real_path))
-      free(path_id->archive_path);
-
-   if (path_id->real_path)
-      free(path_id->real_path);
-
-   free(path_id);
-}
-
-static playlist_path_id_t *playlist_path_id_init(const char *path)
-{
-   playlist_path_id_t *path_id  = (playlist_path_id_t*)malloc(sizeof(*path_id));
-
-   if (!path_id)
-      return NULL;
-
-   path_id->real_path           = NULL;
-   path_id->archive_path        = NULL;
-   path_id->real_path_hash      = 0;
-   path_id->archive_path_hash   = 0;
-   path_id->is_archive          = false;
-   path_id->is_in_archive       = false;
-
-   if (!string_is_empty(path))
-   {
-      char real_path[PATH_MAX_LENGTH];
-      const char *archive_delim = NULL;
-      /* Get real path */
-      strlcpy(real_path, path, sizeof(real_path));
-      playlist_resolve_path(PLAYLIST_SAVE, false, real_path,
-            sizeof(real_path));
-
-      path_id->real_path      = strdup(real_path);
-      path_id->real_path_hash = playlist_path_hash(real_path);
-
-      /* Check archive status */
-      path_id->is_archive     = path_is_compressed_file(real_path);
-      archive_delim           = path_get_archive_delim(real_path);
-
-      /* If path refers to a file inside an archive,
-       * extract the path of the parent archive */
-      if (archive_delim)
-      {
-         char archive_path[PATH_MAX_LENGTH];
-         size_t len                  = (1 + archive_delim - real_path);
-         if (len >= PATH_MAX_LENGTH)
-            len                      = PATH_MAX_LENGTH;
-         strlcpy(archive_path, real_path, len * sizeof(char));
-
-         path_id->archive_path       = strdup(archive_path);
-         path_id->archive_path_hash  = playlist_path_hash(archive_path);
-         path_id->is_in_archive      = true;
-      }
-      else if (path_id->is_archive)
-      {
-         path_id->archive_path       = path_id->real_path;
-         path_id->archive_path_hash  = path_id->real_path_hash;
-      }
-   }
-
-   return path_id;
-}
-
-/**
- * playlist_path_equal:
- * @real_path           : 'Real' search path, generated by path_resolve_realpath()
- * @entry_path          : Existing playlist entry 'path' value
- *
- * Returns 'true' if real_path matches entry_path
- * (Taking into account relative paths, case insensitive
- * filesystems, 'incomplete' archive paths)
- **/
-static bool playlist_path_equal(const char *real_path,
-      const char *entry_path, const playlist_config_t *config)
-{
-   bool real_path_is_compressed;
-   bool entry_real_path_is_compressed;
-   char entry_real_path[PATH_MAX_LENGTH];
-
-   /* Sanity check */
-   if (string_is_empty(real_path)  ||
-       string_is_empty(entry_path) ||
-       !config)
-      return false;
-
-   /* Get entry 'real' path */
-   strlcpy(entry_real_path, entry_path, sizeof(entry_real_path));
-   playlist_resolve_path(PLAYLIST_LOAD, false, entry_real_path, sizeof(entry_real_path));
-   path_resolve_realpath(entry_real_path, sizeof(entry_real_path), true);
-
-   if (string_is_empty(entry_real_path))
-      return false;
-
-   /* First pass comparison */
-#ifdef _WIN32
-   /* Handle case-insensitive operating systems*/
-   if (string_is_equal_noncase(real_path, entry_real_path))
-      return true;
-#else
-   if (string_is_equal(real_path, entry_real_path))
-      return true;
-#endif
-
-#ifdef RARCH_INTERNAL
-   /* If fuzzy matching is disabled, we can give up now */
-   if (!config->fuzzy_archive_match)
-      return false;
-#endif
-
-   /* If we reach this point, we have to work
-    * harder...
-    * Need to handle a rather awkward archive file
-    * case where:
-    * - playlist path contains a properly formatted
-    *   [archive_path][delimiter][rom_file]
-    * - search path is just [archive_path]
-    * ...or vice versa.
-    * This pretty much always happens when a playlist
-    * is generated via scan content (which handles the
-    * archive paths correctly), but the user subsequently
-    * loads an archive file via the command line or some
-    * external launcher (where the [delimiter][rom_file]
-    * part is almost always omitted) */
-   real_path_is_compressed       = path_is_compressed_file(real_path);
-   entry_real_path_is_compressed = path_is_compressed_file(entry_real_path);
-
-   if ((real_path_is_compressed  && !entry_real_path_is_compressed) ||
-       (!real_path_is_compressed && entry_real_path_is_compressed))
-   {
-      const char *compressed_path_a  = real_path_is_compressed ? real_path       : entry_real_path;
-      const char *full_path          = real_path_is_compressed ? entry_real_path : real_path;
-      const char *delim              = path_get_archive_delim(full_path);
-
-      if (delim)
-      {
-         char compressed_path_b[PATH_MAX_LENGTH];
-         unsigned len = (unsigned)(1 + delim - full_path);
-
-         strlcpy(compressed_path_b, full_path,
-               (len < PATH_MAX_LENGTH ? len : PATH_MAX_LENGTH) * sizeof(char));
-
-#ifdef _WIN32
-         /* Handle case-insensitive operating systems*/
-         if (string_is_equal_noncase(compressed_path_a, compressed_path_b))
-            return true;
-#else
-         if (string_is_equal(compressed_path_a, compressed_path_b))
-            return true;
-#endif
-      }
-   }
-
-   return false;
-}
-
-/**
- * playlist_path_matches_entry:
- * @path_id           : Path identity, containing 'real' path,
- *                      hash and archive status information
- * @entry             : Playlist entry to compare with path_id
- *
- * Returns 'true' if 'path_id' matches path information
- * contained in specified 'entry'. Will update path_id
- * cache inside specified 'entry', if not already present.
- **/
-static bool playlist_path_matches_entry(playlist_path_id_t *path_id,
-      struct playlist_entry *entry, const playlist_config_t *config)
-{
-   /* Sanity check */
-   if (!path_id ||
-       !entry ||
-       !config)
-      return false;
-
-   /* Check whether entry contains a path ID cache */
-   if (!entry->path_id)
-   {
-      if (!(entry->path_id = playlist_path_id_init(entry->path)))
-         return false;
-   }
-
-   /* Ensure we have valid real_path strings */
-   if (string_is_empty(path_id->real_path) ||
-       string_is_empty(entry->path_id->real_path))
-      return false;
-
-   /* First pass comparison */
-   if (path_id->real_path_hash ==
-         entry->path_id->real_path_hash)
-   {
-#ifdef _WIN32
-      /* Handle case-insensitive operating systems*/
-      if (string_is_equal_noncase(path_id->real_path,
-            entry->path_id->real_path))
-         return true;
-#else
-      if (string_is_equal(path_id->real_path,
-            entry->path_id->real_path))
-         return true;
-#endif
-   }
-
-#ifdef RARCH_INTERNAL
-   /* If fuzzy matching is disabled, we can give up now */
-   if (!config->fuzzy_archive_match)
-      return false;
-#endif
-
-   /* If we reach this point, we have to work
-    * harder...
-    * Need to handle a rather awkward archive file
-    * case where:
-    * - playlist path contains a properly formatted
-    *   [archive_path][delimiter][rom_file]
-    * - search path is just [archive_path]
-    * ...or vice versa.
-    * This pretty much always happens when a playlist
-    * is generated via scan content (which handles the
-    * archive paths correctly), but the user subsequently
-    * loads an archive file via the command line or some
-    * external launcher (where the [delimiter][rom_file]
-    * part is almost always omitted) */
-   if (((path_id->is_archive        && !path_id->is_in_archive)        && entry->path_id->is_in_archive) ||
-       ((entry->path_id->is_archive && !entry->path_id->is_in_archive) && path_id->is_in_archive))
-   {
-      /* Ensure we have valid parent archive path
-       * strings */
-      if (string_is_empty(path_id->archive_path) ||
-          string_is_empty(entry->path_id->archive_path))
-         return false;
-
-      if (path_id->archive_path_hash ==
-            entry->path_id->archive_path_hash)
-      {
-#ifdef _WIN32
-         /* Handle case-insensitive operating systems*/
-         if (string_is_equal_noncase(path_id->archive_path,
-               entry->path_id->archive_path))
-            return true;
-#else
-         if (string_is_equal(path_id->archive_path,
-               entry->path_id->archive_path))
-            return true;
-#endif
-      }
-   }
-
-   return false;
-}
-
-/**
- * playlist_core_path_equal:
- * @real_core_path  : 'Real' search path, generated by path_resolve_realpath()
- * @entry_core_path : Existing playlist entry 'core path' value
- * @config          : Playlist config parameters
- *
- * Returns 'true' if real_core_path matches entry_core_path
- * (Taking into account relative paths, case insensitive
- * filesystems)
- **/
-static bool playlist_core_path_equal(const char *real_core_path, const char *entry_core_path, const playlist_config_t *config)
-{
-   char entry_real_core_path[PATH_MAX_LENGTH];
-
-   /* Sanity check */
-   if (string_is_empty(real_core_path) || string_is_empty(entry_core_path))
-      return false;
-
-   /* Get entry 'real' core path */
-   strlcpy(entry_real_core_path, entry_core_path, sizeof(entry_real_core_path));
-   if (!string_is_equal(entry_real_core_path, FILE_PATH_DETECT) &&
-       !string_is_equal(entry_real_core_path, FILE_PATH_BUILTIN))
-      playlist_resolve_path(PLAYLIST_SAVE, true, entry_real_core_path,
-            sizeof(entry_real_core_path));
-
-   if (string_is_empty(entry_real_core_path))
-      return false;
-
-#ifdef _WIN32
-   /* Handle case-insensitive operating systems*/
-   if (string_is_equal_noncase(real_core_path, entry_real_core_path))
-      return true;
-#else
-   if (string_is_equal(real_core_path, entry_real_core_path))
-      return true;
-#endif
-
-   if (config->autofix_paths &&
-       core_info_core_file_id_is_equal(real_core_path, entry_core_path))
-      return true;
-
-   return false;
-}
-
-uint32_t playlist_get_size(playlist_t *playlist)
-{
-   if (!playlist)
-      return 0;
-   return (uint32_t)RBUF_LEN(playlist->entries);
-}
-
-char *playlist_get_conf_path(playlist_t *playlist)
-{
-   if (!playlist)
-      return NULL;
-   return playlist->config.path;
-}
-
-/**
- * playlist_get_index:
- * @playlist            : Playlist handle.
- * @idx                 : Index of playlist entry.
- * @path                : Path of playlist entry.
- * @core_path           : Core path of playlist entry.
- * @core_name           : Core name of playlist entry.
- *
- * Gets values of playlist index:
- **/
-void playlist_get_index(playlist_t *playlist,
-      size_t idx,
-      const struct playlist_entry **entry)
-{
-   if (!playlist || !entry || (idx >= RBUF_LEN(playlist->entries)))
-      return;
-
-   *entry = &playlist->entries[idx];
-}
-
-/**
- * playlist_free_entry:
- * @entry               : Playlist entry handle.
- *
- * Frees playlist entry.
- **/
-static void playlist_free_entry(struct playlist_entry *entry)
-{
-   if (!entry)
-      return;
-
-   if (entry->path)
-      free(entry->path);
-   if (entry->label)
-      free(entry->label);
-   if (entry->core_path)
-      free(entry->core_path);
-   if (entry->core_name)
-      free(entry->core_name);
-   if (entry->db_name)
-      free(entry->db_name);
-   if (entry->crc32)
-      free(entry->crc32);
-   if (entry->subsystem_ident)
-      free(entry->subsystem_ident);
-   if (entry->subsystem_name)
-      free(entry->subsystem_name);
-   if (entry->runtime_str)
-      free(entry->runtime_str);
-   if (entry->last_played_str)
-      free(entry->last_played_str);
-   if (entry->subsystem_roms)
-      string_list_free(entry->subsystem_roms);
-   if (entry->path_id)
-      playlist_path_id_free(entry->path_id);
-
-   entry->path               = NULL;
-   entry->label              = NULL;
-   entry->core_path          = NULL;
-   entry->core_name          = NULL;
-   entry->db_name            = NULL;
-   entry->crc32              = NULL;
-   entry->subsystem_ident    = NULL;
-   entry->subsystem_name     = NULL;
-   entry->runtime_str        = NULL;
-   entry->last_played_str    = NULL;
-   entry->subsystem_roms     = NULL;
-   entry->path_id            = NULL;
-   entry->entry_slot         = 0;
-   entry->runtime_status     = PLAYLIST_RUNTIME_UNKNOWN;
-   entry->runtime_hours      = 0;
-   entry->runtime_minutes    = 0;
-   entry->runtime_seconds    = 0;
-   entry->last_played_year   = 0;
-   entry->last_played_month  = 0;
-   entry->last_played_day    = 0;
-   entry->last_played_hour   = 0;
-   entry->last_played_minute = 0;
-   entry->last_played_second = 0;
-}
-
-/**
- * playlist_delete_index:
- * @playlist            : Playlist handle.
- * @idx                 : Index of playlist entry.
- *
- * Delete the entry at the index:
- **/
-void playlist_delete_index(playlist_t *playlist,
-      size_t idx)
-{
-   size_t len;
-   struct playlist_entry *entry_to_delete;
-
-   if (!playlist)
-      return;
-
-   len = RBUF_LEN(playlist->entries);
-   if (idx >= len)
-      return;
-
-   /* Free unwanted entry */
-   entry_to_delete = (struct playlist_entry *)(playlist->entries + idx);
-   if (entry_to_delete)
-      playlist_free_entry(entry_to_delete);
-
-   /* Shift remaining entries to fill the gap */
-   memmove(playlist->entries + idx, playlist->entries + idx + 1,
-         (len - 1 - idx) * sizeof(struct playlist_entry));
-
-   RBUF_RESIZE(playlist->entries, len - 1);
-
-   playlist->modified = true;
-}
-
-/**
- * playlist_delete_by_path:
- * @playlist            : Playlist handle.
- * @search_path         : Content path.
- *
- * Deletes all entries with content path
- * matching 'search_path'
- **/
-void playlist_delete_by_path(playlist_t *playlist,
-      const char *search_path)
-{
-   playlist_path_id_t *path_id = NULL;
-   size_t i                    = 0;
-
-   if (!playlist || string_is_empty(search_path))
-      return;
-
-   if (!(path_id = playlist_path_id_init(search_path)))
-      return;
-
-   while (i < RBUF_LEN(playlist->entries))
-   {
-      if (!playlist_path_matches_entry(path_id,
-            &playlist->entries[i], &playlist->config))
-      {
-         i++;
-         continue;
-      }
-
-      /* Paths are equal - delete entry */
-      playlist_delete_index(playlist, i);
-
-      /* Entries are shifted up by the delete
-       * operation - *do not* increment i */
-   }
-
-   playlist_path_id_free(path_id);
-}
-
-void playlist_get_index_by_path(playlist_t *playlist,
-      const char *search_path,
-      const struct playlist_entry **entry)
-{
-   playlist_path_id_t *path_id = NULL;
-   size_t i, len;
-
-   if (!playlist || !entry || string_is_empty(search_path))
-      return;
-
-   if (!(path_id = playlist_path_id_init(search_path)))
-      return;
-
-   for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
-   {
-      if (!playlist_path_matches_entry(path_id,
-            &playlist->entries[i], &playlist->config))
-         continue;
-
-      *entry = &playlist->entries[i];
-      break;
-   }
-
-   playlist_path_id_free(path_id);
-}
-
-bool playlist_entry_exists(playlist_t *playlist,
-      const char *path)
-{
-   playlist_path_id_t *path_id = NULL;
-   size_t i, len;
-
-   if (!playlist || string_is_empty(path))
-      return false;
-
-   if (!(path_id = playlist_path_id_init(path)))
-      return false;
-
-   for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
-   {
-      if (playlist_path_matches_entry(path_id,
-            &playlist->entries[i], &playlist->config))
-      {
-         playlist_path_id_free(path_id);
-         return true;
-      }
-   }
-
-   playlist_path_id_free(path_id);
-   return false;
-}
-
-void playlist_update(playlist_t *playlist, size_t idx,
-      const struct playlist_entry *update_entry)
-{
-   struct playlist_entry *entry = NULL;
-
-   if (!playlist || idx >= RBUF_LEN(playlist->entries))
-      return;
-
-   entry            = &playlist->entries[idx];
-
-   if (update_entry->path && (update_entry->path != entry->path))
-   {
-      if (entry->path)
-         free(entry->path);
-      entry->path        = strdup(update_entry->path);
-
-      if (entry->path_id)
-      {
-         playlist_path_id_free(entry->path_id);
-         entry->path_id  = NULL;
-      }
-
-      playlist->modified = true;
-   }
-
-   if (update_entry->label && (update_entry->label != entry->label))
-   {
-      if (entry->label)
-         free(entry->label);
-      entry->label       = strdup(update_entry->label);
-      playlist->modified = true;
-   }
-
-   if (update_entry->core_path && (update_entry->core_path != entry->core_path))
-   {
-      if (entry->core_path)
-         free(entry->core_path);
-      entry->core_path   = NULL;
-      entry->core_path   = strdup(update_entry->core_path);
-      playlist->modified = true;
-   }
-
-   if (update_entry->core_name && (update_entry->core_name != entry->core_name))
-   {
-      if (entry->core_name)
-         free(entry->core_name);
-      entry->core_name   = strdup(update_entry->core_name);
-      playlist->modified = true;
-   }
-
-   if (update_entry->db_name && (update_entry->db_name != entry->db_name))
-   {
-      if (entry->db_name)
-         free(entry->db_name);
-      entry->db_name     = strdup(update_entry->db_name);
-      playlist->modified = true;
-   }
-
-   if (update_entry->crc32 && (update_entry->crc32 != entry->crc32))
-   {
-      if (entry->crc32)
-         free(entry->crc32);
-      entry->crc32       = strdup(update_entry->crc32);
-      playlist->modified = true;
-   }
-}
-
-void playlist_update_runtime(playlist_t *playlist, size_t idx,
-      const struct playlist_entry *update_entry,
-      bool register_update)
-{
-   struct playlist_entry *entry = NULL;
-
-   if (!playlist || idx >= RBUF_LEN(playlist->entries))
-      return;
-
-   entry            = &playlist->entries[idx];
-
-   if (update_entry->path && (update_entry->path != entry->path))
-   {
-      if (entry->path)
-         free(entry->path);
-      entry->path        = strdup(update_entry->path);
-
-      if (entry->path_id)
-      {
-         playlist_path_id_free(entry->path_id);
-         entry->path_id  = NULL;
-      }
-
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->core_path && (update_entry->core_path != entry->core_path))
-   {
-      if (entry->core_path)
-         free(entry->core_path);
-      entry->core_path   = NULL;
-      entry->core_path   = strdup(update_entry->core_path);
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->runtime_status != entry->runtime_status)
-   {
-      entry->runtime_status = update_entry->runtime_status;
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->runtime_hours != entry->runtime_hours)
-   {
-      entry->runtime_hours = update_entry->runtime_hours;
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->runtime_minutes != entry->runtime_minutes)
-   {
-      entry->runtime_minutes = update_entry->runtime_minutes;
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->runtime_seconds != entry->runtime_seconds)
-   {
-      entry->runtime_seconds = update_entry->runtime_seconds;
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->last_played_year != entry->last_played_year)
-   {
-      entry->last_played_year = update_entry->last_played_year;
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->last_played_month != entry->last_played_month)
-   {
-      entry->last_played_month = update_entry->last_played_month;
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->last_played_day != entry->last_played_day)
-   {
-      entry->last_played_day = update_entry->last_played_day;
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->last_played_hour != entry->last_played_hour)
-   {
-      entry->last_played_hour = update_entry->last_played_hour;
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->last_played_minute != entry->last_played_minute)
-   {
-      entry->last_played_minute = update_entry->last_played_minute;
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->last_played_second != entry->last_played_second)
-   {
-      entry->last_played_second = update_entry->last_played_second;
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->runtime_str && (update_entry->runtime_str != entry->runtime_str))
-   {
-      if (entry->runtime_str)
-         free(entry->runtime_str);
-      entry->runtime_str = NULL;
-      entry->runtime_str = strdup(update_entry->runtime_str);
-      playlist->modified = playlist->modified || register_update;
-   }
-
-   if (update_entry->last_played_str && (update_entry->last_played_str != entry->last_played_str))
-   {
-      if (entry->last_played_str)
-         free(entry->last_played_str);
-      entry->last_played_str = NULL;
-      entry->last_played_str = strdup(update_entry->last_played_str);
-      playlist->modified = playlist->modified || register_update;
-   }
-}
-
-bool playlist_push_runtime(playlist_t *playlist,
-      const struct playlist_entry *entry)
-{
-   playlist_path_id_t *path_id = NULL;
-   size_t i, len;
-   char real_core_path[PATH_MAX_LENGTH];
-
-   if (!playlist || !entry)
-      goto error;
-
-   if (string_is_empty(entry->core_path))
-   {
-      RARCH_ERR("Cannot push NULL or empty core path into the playlist.\n");
-      goto error;
-   }
-
-   /* Get path ID */
-   if (!(path_id = playlist_path_id_init(entry->path)))
-      goto error;
-
-   /* Get 'real' core path */
-   strlcpy(real_core_path, entry->core_path, sizeof(real_core_path));
-   if (!string_is_equal(real_core_path, FILE_PATH_DETECT) &&
-       !string_is_equal(real_core_path, FILE_PATH_BUILTIN))
-      playlist_resolve_path(PLAYLIST_SAVE, true, real_core_path,
-             sizeof(real_core_path));
-
-   if (string_is_empty(real_core_path))
-   {
-      RARCH_ERR("Cannot push NULL or empty core path into the playlist.\n");
-      goto error;
-   }
-
-   len = RBUF_LEN(playlist->entries);
-   for (i = 0; i < len; i++)
-   {
-      struct playlist_entry tmp;
-      bool equal_path  = (string_is_empty(path_id->real_path) &&
-            string_is_empty(playlist->entries[i].path));
-
-      equal_path       = equal_path || playlist_path_matches_entry(
-            path_id, &playlist->entries[i], &playlist->config);
-
-      if (!equal_path)
-         continue;
-
-      /* Core name can have changed while still being the same core.
-       * Differentiate based on the core path only. */
-      if (!playlist_core_path_equal(real_core_path, playlist->entries[i].core_path, &playlist->config))
-         continue;
-
-      /* If top entry, we don't want to push a new entry since
-       * the top and the entry to be pushed are the same. */
-      if (i == 0)
-         goto error;
-
-      /* Seen it before, bump to top. */
-      tmp = playlist->entries[i];
-      memmove(playlist->entries + 1, playlist->entries,
-            i * sizeof(struct playlist_entry));
-      playlist->entries[0] = tmp;
-
-      goto success;
-   }
-
-   if (playlist->config.capacity == 0)
-      goto error;
-
-   if (len == playlist->config.capacity)
-   {
-      struct playlist_entry *last_entry = &playlist->entries[len - 1];
-      playlist_free_entry(last_entry);
-      len--;
-   }
-   else
-   {
-      /* Allocate memory to fit one more item and resize the buffer */
-      if (!RBUF_TRYFIT(playlist->entries, len + 1))
-         goto error; /* out of memory */
-      RBUF_RESIZE(playlist->entries, len + 1);
-   }
-
-   if (playlist->entries)
-   {
-      memmove(playlist->entries + 1, playlist->entries,
-            len * sizeof(struct playlist_entry));
-
-      playlist->entries[0].path            = NULL;
-      playlist->entries[0].core_path       = NULL;
-
-      if (!string_is_empty(path_id->real_path))
-         playlist->entries[0].path         = strdup(path_id->real_path);
-      playlist->entries[0].path_id         = path_id;
-      path_id                              = NULL;
-
-      if (!string_is_empty(real_core_path))
-         playlist->entries[0].core_path    = strdup(real_core_path);
-
-      playlist->entries[0].runtime_status = entry->runtime_status;
-      playlist->entries[0].runtime_hours = entry->runtime_hours;
-      playlist->entries[0].runtime_minutes = entry->runtime_minutes;
-      playlist->entries[0].runtime_seconds = entry->runtime_seconds;
-      playlist->entries[0].last_played_year = entry->last_played_year;
-      playlist->entries[0].last_played_month = entry->last_played_month;
-      playlist->entries[0].last_played_day = entry->last_played_day;
-      playlist->entries[0].last_played_hour = entry->last_played_hour;
-      playlist->entries[0].last_played_minute = entry->last_played_minute;
-      playlist->entries[0].last_played_second = entry->last_played_second;
-
-      playlist->entries[0].runtime_str        = NULL;
-      playlist->entries[0].last_played_str    = NULL;
-
-      if (!string_is_empty(entry->runtime_str))
-         playlist->entries[0].runtime_str     = strdup(entry->runtime_str);
-      if (!string_is_empty(entry->last_played_str))
-         playlist->entries[0].last_played_str = strdup(entry->last_played_str);
-   }
-
-success:
-   if (path_id)
-      playlist_path_id_free(path_id);
-   playlist->modified = true;
-   return true;
-
-error:
-   if (path_id)
-      playlist_path_id_free(path_id);
-   return false;
-}
-
-/**
- * playlist_resolve_path:
- * @mode      : PLAYLIST_LOAD or PLAYLIST_SAVE
- * @is_core   : Set true if path to be resolved is a core file
- * @path      : The path to be modified
- *
- * Resolves the path of an item, such as the content path or path to the core, to a format
- * appropriate for saving or loading depending on the @mode parameter
- *
- * Can be platform specific. File paths for saving can be abbreviated to avoid saving absolute
- * paths, as the base directory (home or application dir) may change after each subsequent
- * install (iOS)
-**/
-void playlist_resolve_path(enum playlist_file_mode mode,
-      bool is_core, char *path, size_t len)
-{
-#ifdef HAVE_COCOATOUCH
-   char tmp[PATH_MAX_LENGTH];
-
-   if (mode == PLAYLIST_LOAD)
-   {
-      fill_pathname_expand_special(tmp, path, sizeof(tmp));
-      strlcpy(path, tmp, len);
-   }
-   else
-   {
-      /* iOS needs to call realpath here since the call
-       * above fails due to possibly buffer related issues.
-       * Try to expand the path to ensure that it gets saved
-       * correctly. The path can be abbreviated if saving to
-       * a playlist from another playlist (ex: content history to favorites)
-       */
-      char tmp2[PATH_MAX_LENGTH];
-      fill_pathname_expand_special(tmp, path, sizeof(tmp));
-      realpath(tmp, tmp2);
-      fill_pathname_abbreviate_special(path, tmp2, len);
-   }
-#else
-   bool resolve_symlinks = true;
-
-   if (mode == PLAYLIST_LOAD)
-      return;
-
-#if defined(ANDROID)
-   /* Can't resolve symlinks when dealing with cores
-    * installed via play feature delivery, because the
-    * source files have non-standard file names (which
-    * will not be recognised by regular core handling
-    * routines) */
-   if (is_core)
-      resolve_symlinks = !play_feature_delivery_enabled();
-#endif
-
-   path_resolve_realpath(path, len, resolve_symlinks);
-#endif
-}
-
-/**
- * playlist_content_path_is_valid:
- * @path      : Content path
- *
- * Checks whether specified playlist content path
- * refers to an existent file. Handles all playlist
- * content path 'types' (i.e. can validate paths
- * referencing files inside archives).
- *
- * Returns true if file referenced by content
- * path exists on the host filesystem.
- **/
-bool playlist_content_path_is_valid(const char *path)
-{
-   /* Sanity check */
-   if (string_is_empty(path))
-      return false;
-
-   /* If content is inside an archive, special
-    * handling is required... */
-   if (path_contains_compressed_file(path))
-   {
-      char archive_path[PATH_MAX_LENGTH];
-      const char *delim                  = path_get_archive_delim(path);
-      size_t len                         = 0;
-      struct string_list *archive_list   = NULL;
-      const char *content_file           = NULL;
-      bool content_found                 = false;
-
-      if (!delim)
-         return false;
-
-      /* Get path of 'parent' archive file */
-      len = (size_t)(1 + delim - path);
-      strlcpy(archive_path, path,
-            (len < PATH_MAX_LENGTH ? len : PATH_MAX_LENGTH) * sizeof(char));
-
-      /* Check if archive itself exists */
-      if (!path_is_valid(archive_path))
-         return false;
-
-      /* Check if file exists inside archive */
-      if (!(archive_list = file_archive_get_file_list(archive_path, NULL)))
-         return false;
-
-      /* > Get playlist entry content file name
-       *   (sans archive file path) */
-      content_file = delim;
-      content_file++;
-
-      if (!string_is_empty(content_file))
-      {
-         size_t i;
-
-         /* > Loop over archive file contents */
-         for (i = 0; i < archive_list->size; i++)
-         {
-            const char *archive_file = archive_list->elems[i].data;
-
-            if (string_is_empty(archive_file))
-               continue;
-
-            if (string_is_equal(content_file, archive_file))
-            {
-               content_found = true;
-               break;
-            }
-         }
-      }
-
-      /* Clean up */
-      string_list_free(archive_list);
-
-      return content_found;
-   }
-   /* This is a 'normal' path - just check if
-    * it's valid */
-   return path_is_valid(path);
-}
-
-/**
- * playlist_push:
- * @playlist        	   : Playlist handle.
- *
- * Push entry to top of playlist.
- **/
-bool playlist_push(playlist_t *playlist,
-      const struct playlist_entry *entry)
-{
-   size_t i, len;
-   char real_core_path[PATH_MAX_LENGTH];
-   playlist_path_id_t *path_id = NULL;
-   const char *core_name       = entry->core_name;
-   bool entry_updated          = false;
-
-   if (!playlist || !entry)
-      goto error;
-
-   if (string_is_empty(entry->core_path))
-   {
-      RARCH_ERR("Cannot push NULL or empty core path into the playlist.\n");
-      goto error;
-   }
-
-   /* Get path ID */
-   if (!(path_id = playlist_path_id_init(entry->path)))
-      goto error;
-
-   /* Get 'real' core path */
-   strlcpy(real_core_path, entry->core_path, sizeof(real_core_path));
-   if (!string_is_equal(real_core_path, FILE_PATH_DETECT) &&
-       !string_is_equal(real_core_path, FILE_PATH_BUILTIN))
-      playlist_resolve_path(PLAYLIST_SAVE, true, real_core_path,
-             sizeof(real_core_path));
-
-   if (string_is_empty(real_core_path))
-   {
-      RARCH_ERR("Cannot push NULL or empty core path into the playlist.\n");
-      goto error;
-   }
-
-   if (string_is_empty(core_name))
-   {
-      static char base_path[255] = {0};
-      fill_pathname_base(base_path, real_core_path, sizeof(base_path));
-      path_remove_extension(base_path);
-
-      core_name = base_path;
-
-      if (string_is_empty(core_name))
-      {
-         RARCH_ERR("Cannot push NULL or empty core name into the playlist.\n");
-         goto error;
-      }
-   }
-
-   len = RBUF_LEN(playlist->entries);
-   for (i = 0; i < len; i++)
-   {
-      struct playlist_entry tmp;
-      bool equal_path  = (string_is_empty(path_id->real_path) &&
-            string_is_empty(playlist->entries[i].path));
-
-      equal_path       = equal_path || playlist_path_matches_entry(
-            path_id, &playlist->entries[i], &playlist->config);
-
-      if (!equal_path)
-         continue;
-
-      /* Core name can have changed while still being the same core.
-       * Differentiate based on the core path only. */
-      if (!playlist_core_path_equal(real_core_path, playlist->entries[i].core_path, &playlist->config))
-         continue;
-
-      if (     !string_is_empty(entry->subsystem_ident)
-            && !string_is_empty(playlist->entries[i].subsystem_ident)
-            && !string_is_equal(playlist->entries[i].subsystem_ident, entry->subsystem_ident))
-         continue;
-
-      if (      string_is_empty(entry->subsystem_ident)
-            && !string_is_empty(playlist->entries[i].subsystem_ident))
-         continue;
-
-      if (    !string_is_empty(entry->subsystem_ident)
-            && string_is_empty(playlist->entries[i].subsystem_ident))
-         continue;
-
-      if (     !string_is_empty(entry->subsystem_name)
-            && !string_is_empty(playlist->entries[i].subsystem_name)
-            && !string_is_equal(playlist->entries[i].subsystem_name, entry->subsystem_name))
-         continue;
-
-      if (      string_is_empty(entry->subsystem_name)
-            && !string_is_empty(playlist->entries[i].subsystem_name))
-         continue;
-
-      if (     !string_is_empty(entry->subsystem_name)
-            &&  string_is_empty(playlist->entries[i].subsystem_name))
-         continue;
-
-      if (entry->subsystem_roms)
-      {
-         unsigned j;
-         const struct string_list *roms = playlist->entries[i].subsystem_roms;
-         bool                   unequal = false;
-
-         if (entry->subsystem_roms->size != roms->size)
-            continue;
-
-         for (j = 0; j < entry->subsystem_roms->size; j++)
-         {
-            char real_rom_path[PATH_MAX_LENGTH];
-
-            if (!string_is_empty(entry->subsystem_roms->elems[j].data))
-            {
-               strlcpy(real_rom_path, entry->subsystem_roms->elems[j].data, sizeof(real_rom_path));
-               path_resolve_realpath(real_rom_path, sizeof(real_rom_path), true);
-            }
-            else
-               real_rom_path[0] = '\0';
-
-            if (!playlist_path_equal(real_rom_path, roms->elems[j].data,
-                     &playlist->config))
-            {
-               unequal = true;
-               break;
-            }
-         }
-
-         if (unequal)
-            continue;
-      }
-
-      if (playlist->entries[i].entry_slot != entry->entry_slot)
-      {
-         playlist->entries[i].entry_slot  = entry->entry_slot;
-         entry_updated                    = true;
-      }
-
-      /* If content was previously loaded via file browser
-       * or command line, certain entry values will be missing.
-       * If we are now loading the same content from a playlist,
-       * fill in any blanks */
-      if (!playlist->entries[i].label && !string_is_empty(entry->label))
-      {
-         playlist->entries[i].label       = strdup(entry->label);
-         entry_updated                    = true;
-      }
-      if (!playlist->entries[i].crc32 && !string_is_empty(entry->crc32))
-      {
-         playlist->entries[i].crc32       = strdup(entry->crc32);
-         entry_updated                    = true;
-      }
-      if (!playlist->entries[i].db_name && !string_is_empty(entry->db_name))
-      {
-         playlist->entries[i].db_name     = strdup(entry->db_name);
-         entry_updated                    = true;
-      }
-
-      /* If top entry, we don't want to push a new entry since
-       * the top and the entry to be pushed are the same. */
-      if (i == 0)
-      {
-         if (entry_updated)
-            goto success;
-
-         goto error;
-      }
-
-      /* Seen it before, bump to top. */
-      tmp = playlist->entries[i];
-      memmove(playlist->entries + 1, playlist->entries,
-            i * sizeof(struct playlist_entry));
-      playlist->entries[0] = tmp;
-
-      goto success;
-   }
-
-   if (playlist->config.capacity == 0)
-      goto error;
-
-   if (len == playlist->config.capacity)
-   {
-      struct playlist_entry *last_entry = &playlist->entries[len - 1];
-      playlist_free_entry(last_entry);
-      len--;
-   }
-   else
-   {
-      /* Allocate memory to fit one more item and resize the buffer */
-      if (!RBUF_TRYFIT(playlist->entries, len + 1))
-         goto error; /* out of memory */
-      RBUF_RESIZE(playlist->entries, len + 1);
-   }
-
-   if (playlist->entries)
-   {
-      memmove(playlist->entries + 1, playlist->entries,
-            len * sizeof(struct playlist_entry));
-
-      playlist->entries[0].path               = NULL;
-      playlist->entries[0].label              = NULL;
-      playlist->entries[0].core_path          = NULL;
-      playlist->entries[0].core_name          = NULL;
-      playlist->entries[0].db_name            = NULL;
-      playlist->entries[0].crc32              = NULL;
-      playlist->entries[0].subsystem_ident    = NULL;
-      playlist->entries[0].subsystem_name     = NULL;
-      playlist->entries[0].runtime_str        = NULL;
-      playlist->entries[0].last_played_str    = NULL;
-      playlist->entries[0].subsystem_roms     = NULL;
-      playlist->entries[0].path_id            = NULL;
-      playlist->entries[0].runtime_status     = PLAYLIST_RUNTIME_UNKNOWN;
-      playlist->entries[0].runtime_hours      = 0;
-      playlist->entries[0].runtime_minutes    = 0;
-      playlist->entries[0].runtime_seconds    = 0;
-      playlist->entries[0].last_played_year   = 0;
-      playlist->entries[0].last_played_month  = 0;
-      playlist->entries[0].last_played_day    = 0;
-      playlist->entries[0].last_played_hour   = 0;
-      playlist->entries[0].last_played_minute = 0;
-      playlist->entries[0].last_played_second = 0;
-
-      if (!string_is_empty(path_id->real_path))
-         playlist->entries[0].path            = strdup(path_id->real_path);
-      playlist->entries[0].path_id            = path_id;
-      path_id                                 = NULL;
-
-      playlist->entries[0].entry_slot         = entry->entry_slot;
-
-      if (!string_is_empty(entry->label))
-         playlist->entries[0].label           = strdup(entry->label);
-      if (!string_is_empty(real_core_path))
-         playlist->entries[0].core_path       = strdup(real_core_path);
-      if (!string_is_empty(core_name))
-         playlist->entries[0].core_name       = strdup(core_name);
-      if (!string_is_empty(entry->db_name))
-         playlist->entries[0].db_name         = strdup(entry->db_name);
-      if (!string_is_empty(entry->crc32))
-         playlist->entries[0].crc32           = strdup(entry->crc32);
-      if (!string_is_empty(entry->subsystem_ident))
-         playlist->entries[0].subsystem_ident = strdup(entry->subsystem_ident);
-      if (!string_is_empty(entry->subsystem_name))
-         playlist->entries[0].subsystem_name  = strdup(entry->subsystem_name);
-
-      if (entry->subsystem_roms)
-      {
-         union string_list_elem_attr attributes = {0};
-
-         playlist->entries[0].subsystem_roms    = string_list_new();
-
-         for (i = 0; i < entry->subsystem_roms->size; i++)
-            string_list_append(playlist->entries[0].subsystem_roms, entry->subsystem_roms->elems[i].data, attributes);
-      }
-   }
-
-success:
-   if (path_id)
-      playlist_path_id_free(path_id);
-   playlist->modified = true;
-   return true;
-
-error:
-   if (path_id)
-      playlist_path_id_free(path_id);
-   return false;
-}
-
-void playlist_write_runtime_file(playlist_t *playlist)
-{
-   size_t i, len;
-   intfstream_t *file  = NULL;
-   rjsonwriter_t* writer;
-
-   if (!playlist || !playlist->modified)
-      return;
-
-   if (!(file = intfstream_open_file(playlist->config.path,
-         RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE)))
-   {
-      RARCH_ERR("Failed to write to playlist file: \"%s\".\n", playlist->config.path);
-      return;
-   }
-
-   if (!(writer = rjsonwriter_open_stream(file)))
-   {
-      RARCH_ERR("Failed to create JSON writer\n");
-      goto end;
-   }
-
-   rjsonwriter_raw(writer, "{", 1);
-   rjsonwriter_raw(writer, "\n", 1);
-   rjsonwriter_add_spaces(writer, 2);
-   rjsonwriter_add_string(writer, "version");
-   rjsonwriter_raw(writer, ":", 1);
-   rjsonwriter_raw(writer, " ", 1);
-   rjsonwriter_add_string(writer, "1.0");
-   rjsonwriter_raw(writer, ",", 1);
-   rjsonwriter_raw(writer, "\n", 1);
-   rjsonwriter_add_spaces(writer, 2);
-   rjsonwriter_add_string(writer, "items");
-   rjsonwriter_raw(writer, ":", 1);
-   rjsonwriter_raw(writer, " ", 1);
-   rjsonwriter_raw(writer, "[", 1);
-   rjsonwriter_raw(writer, "\n", 1);
-
-   for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
-   {
-      rjsonwriter_add_spaces(writer, 4);
-      rjsonwriter_raw(writer, "{", 1);
-
-      rjsonwriter_raw(writer, "\n", 1);
-      rjsonwriter_add_spaces(writer, 6);
-      rjsonwriter_add_string(writer, "path");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_add_string(writer, playlist->entries[i].path);
-      rjsonwriter_raw(writer, ",", 1);
-
-      rjsonwriter_raw(writer, "\n", 1);
-      rjsonwriter_add_spaces(writer, 6);
-      rjsonwriter_add_string(writer, "core_path");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_add_string(writer, playlist->entries[i].core_path);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 6);
-      rjsonwriter_add_string(writer, "runtime_hours");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%u", playlist->entries[i].runtime_hours);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 6);
-      rjsonwriter_add_string(writer, "runtime_minutes");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%u", playlist->entries[i].runtime_minutes);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 6);
-      rjsonwriter_add_string(writer, "runtime_seconds");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%u", playlist->entries[i].runtime_seconds);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 6);
-      rjsonwriter_add_string(writer, "last_played_year");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%u", playlist->entries[i].last_played_year);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 6);
-      rjsonwriter_add_string(writer, "last_played_month");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%u", playlist->entries[i].last_played_month);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 6);
-      rjsonwriter_add_string(writer, "last_played_day");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%u", playlist->entries[i].last_played_day);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 6);
-      rjsonwriter_add_string(writer, "last_played_hour");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%u", playlist->entries[i].last_played_hour);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 6);
-      rjsonwriter_add_string(writer, "last_played_minute");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%u", playlist->entries[i].last_played_minute);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 6);
-      rjsonwriter_add_string(writer, "last_played_second");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%u", playlist->entries[i].last_played_second);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 4);
-      rjsonwriter_raw(writer, "}", 1);
-
-      if (i < len - 1)
-         rjsonwriter_raw(writer, ",", 1);
-
-      rjsonwriter_raw(writer, "\n", 1);
-   }
-
-   rjsonwriter_add_spaces(writer, 2);
-   rjsonwriter_raw(writer, "]", 1);
-   rjsonwriter_raw(writer, "\n", 1);
-   rjsonwriter_raw(writer, "}", 1);
-   rjsonwriter_raw(writer, "\n", 1);
-   rjsonwriter_free(writer);
-
-   playlist->modified        = false;
-   playlist->old_format      = false;
-   playlist->compressed      = false;
-
-   RARCH_LOG("[Playlist]: Written to playlist file: \"%s\".\n", playlist->config.path);
-end:
-   intfstream_close(file);
-   free(file);
-}
-
-void playlist_write_file(playlist_t *playlist)
-{
-   size_t i, len;
-   intfstream_t *file = NULL;
-   bool compressed    = false;
-
-   /* Playlist will be written if any of the
-    * following are true:
-    * > 'modified' flag is set
-    * > Current playlist format (old/new) does not
-    *   match requested
-    * > Current playlist compression status does
-    *   not match requested */
-   if (!playlist ||
-       !(playlist->modified ||
-#if defined(HAVE_ZLIB)
-        (playlist->compressed != playlist->config.compress) ||
-#endif
-        (playlist->old_format != playlist->config.old_format)))
-      return;
-
-#if defined(HAVE_ZLIB)
-   if (playlist->config.compress)
-      file = intfstream_open_rzip_file(playlist->config.path,
-            RETRO_VFS_FILE_ACCESS_WRITE);
-   else
-#endif
-      file = intfstream_open_file(playlist->config.path,
-            RETRO_VFS_FILE_ACCESS_WRITE,
-            RETRO_VFS_FILE_ACCESS_HINT_NONE);
-
-   if (!file)
-   {
-      RARCH_ERR("Failed to write to playlist file: \"%s\".\n", playlist->config.path);
-      return;
-   }
-
-   /* Get current file compression state */
-   compressed = intfstream_is_compressed(file);
-
-#ifdef RARCH_INTERNAL
-   if (playlist->config.old_format)
-   {
-      for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
-         intfstream_printf(file, "%s\n%s\n%s\n%s\n%s\n%s\n",
-               playlist->entries[i].path      ? playlist->entries[i].path      : "",
-               playlist->entries[i].label     ? playlist->entries[i].label     : "",
-               playlist->entries[i].core_path ? playlist->entries[i].core_path : "",
-               playlist->entries[i].core_name ? playlist->entries[i].core_name : "",
-               playlist->entries[i].crc32     ? playlist->entries[i].crc32     : "",
-               playlist->entries[i].db_name   ? playlist->entries[i].db_name   : ""
-               );
-
-      /* Add metadata lines
-       * > We add these at the end of the file to prevent
-       *   breakage if the playlist is loaded with an older
-       *   version of RetroArch */
-      intfstream_printf(
-            file,
-            "default_core_path = \"%s\"\n"
-            "default_core_name = \"%s\"\n"
-            "label_display_mode = \"%d\"\n"
-            "thumbnail_mode = \"%d|%d\"\n"
-            "sort_mode = \"%d\"\n",
-            playlist->default_core_path ? playlist->default_core_path : "",
-            playlist->default_core_name ? playlist->default_core_name : "",
-            playlist->label_display_mode,
-            playlist->right_thumbnail_mode, playlist->left_thumbnail_mode,
-            playlist->sort_mode);
-
-      playlist->old_format = true;
-   }
-   else
-#endif
-   {
-      rjsonwriter_t* writer = rjsonwriter_open_stream(file);
-      if (!writer)
-      {
-         RARCH_ERR("Failed to create JSON writer\n");
-         goto end;
-      }
-      /*  When compressing playlists, human readability
-       *   is not a factor - can skip all indentation
-       *   and new line characters */
-      if (compressed)
-         rjsonwriter_set_options(writer, RJSONWRITER_OPTION_SKIP_WHITESPACE);
-
-      rjsonwriter_raw(writer, "{", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 2);
-      rjsonwriter_add_string(writer, "version");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_add_string(writer, "1.5");
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 2);
-      rjsonwriter_add_string(writer, "default_core_path");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_add_string(writer, playlist->default_core_path);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 2);
-      rjsonwriter_add_string(writer, "default_core_name");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_add_string(writer, playlist->default_core_name);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      if (!string_is_empty(playlist->base_content_directory))
-      {
-         rjsonwriter_add_spaces(writer, 2);
-         rjsonwriter_add_string(writer, "base_content_directory");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         rjsonwriter_add_string(writer, playlist->base_content_directory);
-         rjsonwriter_raw(writer, ",", 1);
-         rjsonwriter_raw(writer, "\n", 1);
-      }
-
-      rjsonwriter_add_spaces(writer, 2);
-      rjsonwriter_add_string(writer, "label_display_mode");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%d", (int)playlist->label_display_mode);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 2);
-      rjsonwriter_add_string(writer, "right_thumbnail_mode");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%d", (int)playlist->right_thumbnail_mode);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 2);
-      rjsonwriter_add_string(writer, "left_thumbnail_mode");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%d", (int)playlist->left_thumbnail_mode);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      rjsonwriter_add_spaces(writer, 2);
-      rjsonwriter_add_string(writer, "sort_mode");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_rawf(writer, "%d", (int)playlist->sort_mode);
-      rjsonwriter_raw(writer, ",", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      if (!string_is_empty(playlist->scan_record.content_dir))
-      {
-         rjsonwriter_add_spaces(writer, 2);
-         rjsonwriter_add_string(writer, "scan_content_dir");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         rjsonwriter_add_string(writer, playlist->scan_record.content_dir);
-         rjsonwriter_raw(writer, ",", 1);
-         rjsonwriter_raw(writer, "\n", 1);
-
-         rjsonwriter_add_spaces(writer, 2);
-         rjsonwriter_add_string(writer, "scan_file_exts");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         rjsonwriter_add_string(writer, playlist->scan_record.file_exts);
-         rjsonwriter_raw(writer, ",", 1);
-         rjsonwriter_raw(writer, "\n", 1);
-
-         rjsonwriter_add_spaces(writer, 2);
-         rjsonwriter_add_string(writer, "scan_dat_file_path");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         rjsonwriter_add_string(writer, playlist->scan_record.dat_file_path);
-         rjsonwriter_raw(writer, ",", 1);
-         rjsonwriter_raw(writer, "\n", 1);
-
-         rjsonwriter_add_spaces(writer, 2);
-         rjsonwriter_add_string(writer, "scan_search_recursively");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         {
-            bool value = playlist->scan_record.search_recursively;
-            rjsonwriter_raw(writer, (value ? "true" : "false"), (value ? 4 : 5));
-         }
-         rjsonwriter_raw(writer, ",", 1);
-         rjsonwriter_raw(writer, "\n", 1);
-
-         rjsonwriter_add_spaces(writer, 2);
-         rjsonwriter_add_string(writer, "scan_search_archives");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         {
-            bool value = playlist->scan_record.search_archives;
-            rjsonwriter_raw(writer, (value ? "true" : "false"), (value ? 4 : 5));
-         }
-         rjsonwriter_raw(writer, ",", 1);
-         rjsonwriter_raw(writer, "\n", 1);
-
-         rjsonwriter_add_spaces(writer, 2);
-         rjsonwriter_add_string(writer, "scan_filter_dat_content");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         {
-            bool value = playlist->scan_record.filter_dat_content;
-            rjsonwriter_raw(writer, (value ? "true" : "false"), (value ? 4 : 5));
-         }
-         rjsonwriter_raw(writer, ",", 1);
-         rjsonwriter_raw(writer, "\n", 1);
-      }
-
-      rjsonwriter_add_spaces(writer, 2);
-      rjsonwriter_add_string(writer, "items");
-      rjsonwriter_raw(writer, ":", 1);
-      rjsonwriter_raw(writer, " ", 1);
-      rjsonwriter_raw(writer, "[", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
-      {
-         rjsonwriter_add_spaces(writer, 4);
-         rjsonwriter_raw(writer, "{", 1);
-
-         rjsonwriter_raw(writer, "\n", 1);
-         rjsonwriter_add_spaces(writer, 6);
-         rjsonwriter_add_string(writer, "path");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         rjsonwriter_add_string(writer, playlist->entries[i].path);
-         rjsonwriter_raw(writer, ",", 1);
-
-         if (playlist->entries[i].entry_slot)
-         {
-            rjsonwriter_raw(writer, "\n", 1);
-            rjsonwriter_add_spaces(writer, 6);
-            rjsonwriter_add_string(writer, "entry_slot");
-            rjsonwriter_raw(writer, ":", 1);
-            rjsonwriter_raw(writer, " ", 1);
-            rjsonwriter_rawf(writer, "%d", (int)playlist->entries[i].entry_slot);
-            rjsonwriter_raw(writer, ",", 1);
-         }
-
-         rjsonwriter_raw(writer, "\n", 1);
-         rjsonwriter_add_spaces(writer, 6);
-         rjsonwriter_add_string(writer, "label");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         rjsonwriter_add_string(writer, playlist->entries[i].label);
-         rjsonwriter_raw(writer, ",", 1);
-
-         rjsonwriter_raw(writer, "\n", 1);
-         rjsonwriter_add_spaces(writer, 6);
-         rjsonwriter_add_string(writer, "core_path");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         rjsonwriter_add_string(writer, playlist->entries[i].core_path);
-         rjsonwriter_raw(writer, ",", 1);
-
-         rjsonwriter_raw(writer, "\n", 1);
-         rjsonwriter_add_spaces(writer, 6);
-         rjsonwriter_add_string(writer, "core_name");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         rjsonwriter_add_string(writer, playlist->entries[i].core_name);
-         rjsonwriter_raw(writer, ",", 1);
-
-         rjsonwriter_raw(writer, "\n", 1);
-         rjsonwriter_add_spaces(writer, 6);
-         rjsonwriter_add_string(writer, "crc32");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         rjsonwriter_add_string(writer, playlist->entries[i].crc32);
-         rjsonwriter_raw(writer, ",", 1);
-
-         rjsonwriter_raw(writer, "\n", 1);
-         rjsonwriter_add_spaces(writer, 6);
-         rjsonwriter_add_string(writer, "db_name");
-         rjsonwriter_raw(writer, ":", 1);
-         rjsonwriter_raw(writer, " ", 1);
-         rjsonwriter_add_string(writer, playlist->entries[i].db_name);
-
-         if (!string_is_empty(playlist->entries[i].subsystem_ident))
-         {
-            rjsonwriter_raw(writer, ",", 1);
-            rjsonwriter_raw(writer, "\n", 1);
-            rjsonwriter_add_spaces(writer, 6);
-            rjsonwriter_add_string(writer, "subsystem_ident");
-            rjsonwriter_raw(writer, ":", 1);
-            rjsonwriter_raw(writer, " ", 1);
-            rjsonwriter_add_string(writer, playlist->entries[i].subsystem_ident);
-         }
-
-         if (!string_is_empty(playlist->entries[i].subsystem_name))
-         {
-            rjsonwriter_raw(writer, ",", 1);
-            rjsonwriter_raw(writer, "\n", 1);
-            rjsonwriter_add_spaces(writer, 6);
-            rjsonwriter_add_string(writer, "subsystem_name");
-            rjsonwriter_raw(writer, ":", 1);
-            rjsonwriter_raw(writer, " ", 1);
-            rjsonwriter_add_string(writer, playlist->entries[i].subsystem_name);
-         }
-
-         if (  playlist->entries[i].subsystem_roms &&
-               playlist->entries[i].subsystem_roms->size > 0)
-         {
-            unsigned j;
-
-            rjsonwriter_raw(writer, ",", 1);
-            rjsonwriter_raw(writer, "\n", 1);
-            rjsonwriter_add_spaces(writer, 6);
-            rjsonwriter_add_string(writer, "subsystem_roms");
-            rjsonwriter_raw(writer, ":", 1);
-            rjsonwriter_raw(writer, " ", 1);
-            rjsonwriter_raw(writer, "[", 1);
-            rjsonwriter_raw(writer, "\n", 1);
-
-            for (j = 0; j < playlist->entries[i].subsystem_roms->size; j++)
-            {
-               const struct string_list *roms = playlist->entries[i].subsystem_roms;
-               rjsonwriter_add_spaces(writer, 8);
-               rjsonwriter_add_string(writer,
-                     !string_is_empty(roms->elems[j].data)
-                     ? roms->elems[j].data
-                     : "");
-
-               if (j < playlist->entries[i].subsystem_roms->size - 1)
-               {
-                  rjsonwriter_raw(writer, ",", 1);
-                  rjsonwriter_raw(writer, "\n", 1);
-               }
-            }
-
-            rjsonwriter_raw(writer, "\n", 1);
-            rjsonwriter_add_spaces(writer, 6);
-            rjsonwriter_raw(writer, "]", 1);
-         }
-
-         rjsonwriter_raw(writer, "\n", 1);
-
-         rjsonwriter_add_spaces(writer, 4);
-         rjsonwriter_raw(writer, "}", 1);
-
-         if (i < len - 1)
-            rjsonwriter_raw(writer, ",", 1);
-
-         rjsonwriter_raw(writer, "\n", 1);
-      }
-
-      rjsonwriter_add_spaces(writer, 2);
-      rjsonwriter_raw(writer, "]", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-      rjsonwriter_raw(writer, "}", 1);
-      rjsonwriter_raw(writer, "\n", 1);
-
-      if (!rjsonwriter_free(writer))
-      {
-         RARCH_ERR("Failed to write to playlist file: \"%s\".\n", playlist->config.path);
-      }
-
-      playlist->old_format = false;
-   }
-
-   playlist->modified   = false;
-   playlist->compressed = compressed;
-
-   RARCH_LOG("[Playlist]: Written to playlist file: \"%s\".\n", playlist->config.path);
-end:
-   intfstream_close(file);
-   free(file);
-}
-
-/**
- * playlist_free:
- * @playlist            : Playlist handle.
- *
- * Frees playlist handle.
- */
-void playlist_free(playlist_t *playlist)
-{
-   size_t i, len;
-
-   if (!playlist)
-      return;
-
-   if (playlist->default_core_path)
-      free(playlist->default_core_path);
-   playlist->default_core_path = NULL;
-
-   if (playlist->default_core_name)
-      free(playlist->default_core_name);
-   playlist->default_core_name = NULL;
-
-   if (playlist->base_content_directory)
-      free(playlist->base_content_directory);
-   playlist->base_content_directory = NULL;
-
-   if (playlist->scan_record.content_dir)
-      free(playlist->scan_record.content_dir);
-   playlist->scan_record.content_dir = NULL;
-
-   if (playlist->scan_record.file_exts)
-      free(playlist->scan_record.file_exts);
-   playlist->scan_record.file_exts = NULL;
-
-   if (playlist->scan_record.dat_file_path)
-      free(playlist->scan_record.dat_file_path);
-   playlist->scan_record.dat_file_path = NULL;
-
-   if (playlist->entries)
-   {
-      for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
-      {
-         struct playlist_entry *entry = &playlist->entries[i];
-
-         if (entry)
-            playlist_free_entry(entry);
-      }
-
-      RBUF_FREE(playlist->entries);
-   }
-
-   free(playlist);
-}
-
-/**
- * playlist_clear:
- * @playlist        	   : Playlist handle.
- *
- * Clears all playlist entries in playlist.
- **/
-void playlist_clear(playlist_t *playlist)
-{
-   size_t i, len;
-   if (!playlist)
-      return;
-
-   for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
-   {
-      struct playlist_entry *entry = &playlist->entries[i];
-
-      if (entry)
-         playlist_free_entry(entry);
-   }
-   RBUF_CLEAR(playlist->entries);
-}
-
-/**
- * playlist_size:
- * @playlist        	   : Playlist handle.
- *
- * Gets size of playlist.
- * Returns: size of playlist.
- **/
-size_t playlist_size(playlist_t *playlist)
-{
-   if (!playlist)
-      return 0;
-   return RBUF_LEN(playlist->entries);
-}
-
-/**
- * playlist_capacity:
- * @playlist        	   : Playlist handle.
- *
- * Gets maximum capacity of playlist.
- * Returns: maximum capacity of playlist.
- **/
-size_t playlist_capacity(playlist_t *playlist)
-{
-   if (!playlist)
-      return 0;
-   return playlist->config.capacity;
-}
-
-static bool JSONStartArrayHandler(void *context)
-{
-   JSONContext *pCtx = (JSONContext *)context;
-
-   pCtx->array_depth++;
-
-   return true;
-}
-
-static bool JSONEndArrayHandler(void *context)
-{
-   JSONContext *pCtx = (JSONContext *)context;
-
-   pCtx->array_depth--;
-
-   if (pCtx->in_items && pCtx->array_depth == 0 && pCtx->object_depth <= 1)
-      pCtx->in_items = false;
-   else if (pCtx->in_subsystem_roms && pCtx->array_depth <= 1 && pCtx->object_depth <= 2)
-      pCtx->in_subsystem_roms = false;
-
-   return true;
-}
-
-static bool JSONStartObjectHandler(void *context)
-{
-   JSONContext *pCtx = (JSONContext *)context;
-
-   pCtx->object_depth++;
-
-   if (pCtx->in_items && pCtx->object_depth == 2)
-   {
-      if ((pCtx->array_depth == 1) && !pCtx->capacity_exceeded)
-      {
-         size_t len = RBUF_LEN(pCtx->playlist->entries);
-         if (len < pCtx->playlist->config.capacity)
-         {
-            /* Allocate memory to fit one more item but don't resize the
-             * buffer just yet, wait until JSONEndObjectHandler for that */
-            if (!RBUF_TRYFIT(pCtx->playlist->entries, len + 1))
-            {
-               pCtx->out_of_memory     = true;
-               return false;
-            }
-            pCtx->current_entry = &pCtx->playlist->entries[len];
-            memset(pCtx->current_entry, 0, sizeof(*pCtx->current_entry));
-         }
-         else
-         {
-            /* Hit max item limit.
-             * Note: We can't just abort here, since there may
-             * be more metadata to read at the end of the file... */
-            RARCH_WARN("JSON file contains more entries than current playlist capacity. Excess entries will be discarded.\n");
-            pCtx->capacity_exceeded  = true;
-            pCtx->current_entry      = NULL;
-            /* In addition, since we are discarding excess entries,
-             * the playlist must be flagged as being modified
-             * (i.e. the playlist is not the same as when it was
-             * last saved to disk...) */
-            pCtx->playlist->modified = true;
-         }
-      }
-   }
-
-   return true;
-}
-
-static bool JSONEndObjectHandler(void *context)
-{
-   JSONContext *pCtx = (JSONContext *)context;
-
-   if (pCtx->in_items && pCtx->object_depth == 2)
-   {
-      if ((pCtx->array_depth == 1) && !pCtx->capacity_exceeded)
-         RBUF_RESIZE(pCtx->playlist->entries,
-               RBUF_LEN(pCtx->playlist->entries) + 1);
-   }
-
-   pCtx->object_depth--;
-
-   return true;
-}
-
-static bool JSONStringHandler(void *context, const char *pValue, size_t length)
-{
-   JSONContext *pCtx = (JSONContext *)context;
-
-   if (pCtx->in_items && pCtx->in_subsystem_roms && pCtx->object_depth == 2 && pCtx->array_depth == 2)
-   {
-      if (length && !string_is_empty(pValue))
-      {
-         union string_list_elem_attr attr = {0};
-
-         if (!pCtx->current_entry->subsystem_roms)
-            pCtx->current_entry->subsystem_roms = string_list_new();
-
-         string_list_append(pCtx->current_entry->subsystem_roms, pValue, attr);
-      }
-   }
-   else if (pCtx->in_items && pCtx->object_depth == 2)
-   {
-      if (pCtx->array_depth == 1)
-      {
-         if (pCtx->current_string_val && length && !string_is_empty(pValue))
-         {
-            if (*pCtx->current_string_val)
-                free(*pCtx->current_string_val);
-             *pCtx->current_string_val = strdup(pValue);
-         }
-      }
-   }
-   else if (pCtx->object_depth == 1)
-   {
-      if (pCtx->array_depth == 0)
-      {
-         if (pCtx->current_string_val && length && !string_is_empty(pValue))
-         {
-            /* handle any top-level playlist metadata here */
-            if (*pCtx->current_string_val)
-                free(*pCtx->current_string_val);
-            *pCtx->current_string_val = strdup(pValue);
-         }
-      }
-   }
-
-   pCtx->current_string_val = NULL;
-
-   return true;
-}
-
-static bool JSONNumberHandler(void *context, const char *pValue, size_t length)
-{
-   JSONContext *pCtx = (JSONContext *)context;
-
-   if (pCtx->in_items && pCtx->object_depth == 2)
-   {
-      if (pCtx->array_depth == 1 && length && !string_is_empty(pValue))
-      {
-         if (pCtx->current_entry_uint_val)
-            *pCtx->current_entry_uint_val = (unsigned)strtoul(pValue, NULL, 10);
-      }
-   }
-   else if (pCtx->object_depth == 1)
-   {
-      if (pCtx->array_depth == 0)
-      {
-         if (length && !string_is_empty(pValue))
-         {
-            /* handle any top-level playlist metadata here */
-            if (pCtx->current_meta_label_display_mode_val)
-               *pCtx->current_meta_label_display_mode_val = (enum playlist_label_display_mode)strtoul(pValue, NULL, 10);
-            else if (pCtx->current_meta_thumbnail_mode_val)
-               *pCtx->current_meta_thumbnail_mode_val = (enum playlist_thumbnail_mode)strtoul(pValue, NULL, 10);
-            else if (pCtx->current_meta_sort_mode_val)
-               *pCtx->current_meta_sort_mode_val = (enum playlist_sort_mode)strtoul(pValue, NULL, 10);
-         }
-      }
-   }
-
-   pCtx->current_entry_uint_val              = NULL;
-   pCtx->current_meta_label_display_mode_val = NULL;
-   pCtx->current_meta_thumbnail_mode_val     = NULL;
-   pCtx->current_meta_sort_mode_val          = NULL;
-
-   return true;
-}
-
-static bool JSONBoolHandler(void *context, bool value)
-{
-   JSONContext *pCtx = (JSONContext *)context;
-
-   if (!pCtx->in_items &&
-       (pCtx->object_depth == 1) &&
-       (pCtx->array_depth == 0) &&
-       pCtx->current_meta_bool_val)
-      *pCtx->current_meta_bool_val = value;
-
-   pCtx->current_meta_bool_val = NULL;
-
-   return true;
-}
-
-static bool JSONObjectMemberHandler(void *context, const char *pValue, size_t length)
-{
-   JSONContext *pCtx = (JSONContext *)context;
-
-   if (pCtx->in_items && pCtx->object_depth == 2)
-   {
-      if (pCtx->array_depth == 1)
-      {
-         if (pCtx->current_string_val)
-         {
-            /* something went wrong */
-            return false;
-         }
-
-         if (length && !pCtx->capacity_exceeded)
-         {
-            pCtx->current_string_val     = NULL;
-            pCtx->current_entry_uint_val = NULL;
-            pCtx->in_subsystem_roms      = false;
-            switch (pValue[0])
-            {
-               case 'c':
-                     if (string_is_equal(pValue, "core_name"))
-                        pCtx->current_string_val = &pCtx->current_entry->core_name;
-                     else if (string_is_equal(pValue, "core_path"))
-                        pCtx->current_string_val = &pCtx->current_entry->core_path;
-                     else if (string_is_equal(pValue, "crc32"))
-                        pCtx->current_string_val = &pCtx->current_entry->crc32;
-                     break;
-               case 'd':
-                     if (string_is_equal(pValue, "db_name"))
-                        pCtx->current_string_val = &pCtx->current_entry->db_name;
-                     break;
-               case 'e':
-                     if (string_is_equal(pValue, "entry_slot"))
-                        pCtx->current_entry_uint_val = &pCtx->current_entry->entry_slot;
-                     break;
-               case 'l':
-                     if (string_is_equal(pValue, "label"))
-                        pCtx->current_string_val = &pCtx->current_entry->label;
-                     else if (string_is_equal(pValue, "last_played_day"))
-                        pCtx->current_entry_uint_val = &pCtx->current_entry->last_played_day;
-                     else if (string_is_equal(pValue, "last_played_hour"))
-                        pCtx->current_entry_uint_val = &pCtx->current_entry->last_played_hour;
-                     else if (string_is_equal(pValue, "last_played_minute"))
-                        pCtx->current_entry_uint_val = &pCtx->current_entry->last_played_minute;
-                     else if (string_is_equal(pValue, "last_played_month"))
-                        pCtx->current_entry_uint_val = &pCtx->current_entry->last_played_month;
-                     else if (string_is_equal(pValue, "last_played_second"))
-                        pCtx->current_entry_uint_val = &pCtx->current_entry->last_played_second;
-                     else if (string_is_equal(pValue, "last_played_year"))
-                        pCtx->current_entry_uint_val = &pCtx->current_entry->last_played_year;
-                     break;
-               case 'p':
-                     if (string_is_equal(pValue, "path"))
-                        pCtx->current_string_val = &pCtx->current_entry->path;
-                     break;
-               case 'r':
-                     if (string_is_equal(pValue, "runtime_hours"))
-                        pCtx->current_entry_uint_val = &pCtx->current_entry->runtime_hours;
-                     else if (string_is_equal(pValue, "runtime_minutes"))
-                        pCtx->current_entry_uint_val = &pCtx->current_entry->runtime_minutes;
-                     else if (string_is_equal(pValue, "runtime_seconds"))
-                        pCtx->current_entry_uint_val = &pCtx->current_entry->runtime_seconds;
-                     break;
-               case 's':
-                     if (string_is_equal(pValue, "subsystem_ident"))
-                        pCtx->current_string_val = &pCtx->current_entry->subsystem_ident;
-                     else if (string_is_equal(pValue, "subsystem_name"))
-                        pCtx->current_string_val = &pCtx->current_entry->subsystem_name;
-                     else if (string_is_equal(pValue, "subsystem_roms"))
-                        pCtx->in_subsystem_roms = true;
-                     break;
-            }
-         }
-      }
-   }
-   else if (pCtx->object_depth == 1 && pCtx->array_depth == 0 && length)
-   {
-      pCtx->current_string_val                  = NULL;
-      pCtx->current_meta_label_display_mode_val = NULL;
-      pCtx->current_meta_thumbnail_mode_val     = NULL;
-      pCtx->current_meta_sort_mode_val          = NULL;
-      pCtx->current_meta_bool_val               = NULL;
-      pCtx->in_items                            = false;
-
-      switch (pValue[0])
-      {
-         case 'b':
-            if (string_is_equal(pValue, "base_content_directory"))
-               pCtx->current_string_val = &pCtx->playlist->base_content_directory;
-            break;
-         case 'd':
-            if (string_is_equal(pValue,      "default_core_path"))
-               pCtx->current_string_val = &pCtx->playlist->default_core_path;
-            else if (string_is_equal(pValue, "default_core_name"))
-               pCtx->current_string_val = &pCtx->playlist->default_core_name;
-            break;
-         case 'i':
-            if (string_is_equal(pValue, "items"))
-               pCtx->in_items = true;
-            break;
-         case 'l':
-            if (string_is_equal(pValue,      "label_display_mode"))
-               pCtx->current_meta_label_display_mode_val = &pCtx->playlist->label_display_mode;
-            else if (string_is_equal(pValue, "left_thumbnail_mode"))
-               pCtx->current_meta_thumbnail_mode_val     = &pCtx->playlist->left_thumbnail_mode;
-            break;
-         case 'r':
-            if (string_is_equal(pValue, "right_thumbnail_mode"))
-               pCtx->current_meta_thumbnail_mode_val = &pCtx->playlist->right_thumbnail_mode;
-            break;
-         case 's':
-            if (string_is_equal(pValue,      "scan_content_dir"))
-               pCtx->current_string_val         = &pCtx->playlist->scan_record.content_dir;
-            else if (string_is_equal(pValue, "scan_file_exts"))
-               pCtx->current_string_val         = &pCtx->playlist->scan_record.file_exts;
-            else if (string_is_equal(pValue, "scan_dat_file_path"))
-               pCtx->current_string_val         = &pCtx->playlist->scan_record.dat_file_path;
-            else if (string_is_equal(pValue, "scan_search_recursively"))
-               pCtx->current_meta_bool_val      = &pCtx->playlist->scan_record.search_recursively;
-            else if (string_is_equal(pValue, "scan_search_archives"))
-               pCtx->current_meta_bool_val      = &pCtx->playlist->scan_record.search_archives;
-            else if (string_is_equal(pValue, "scan_filter_dat_content"))
-               pCtx->current_meta_bool_val      = &pCtx->playlist->scan_record.filter_dat_content;
-            else if (string_is_equal(pValue, "sort_mode"))
-               pCtx->current_meta_sort_mode_val = &pCtx->playlist->sort_mode;
-            break;
-      }
-   }
-
-   return true;
-}
-
-static void playlist_get_old_format_metadata_value(
-      char *metadata_line, char *value, size_t len)
-{
-   char *end   = NULL;
-   char *start = strchr(metadata_line, '\"');
-
-   if (!start)
-      return;
-
-   start++;
-   end         = strchr(start, '\"');
-
-   if (!end)
-      return;
-
-   *end        = '\0';
-   strlcpy(value, start, len);
-}
-
-static bool playlist_read_file(playlist_t *playlist)
-{
-   unsigned i;
-   int test_char;
-   bool res = true;
-#if defined(HAVE_ZLIB)
-      /* Always use RZIP interface when reading playlists
-       * > this will automatically handle uncompressed
-       *   data */
-   intfstream_t *file   = intfstream_open_rzip_file(
-         playlist->config.path,
-         RETRO_VFS_FILE_ACCESS_READ);
-#else
-   intfstream_t *file   = intfstream_open_file(
-         playlist->config.path,
-         RETRO_VFS_FILE_ACCESS_READ,
-         RETRO_VFS_FILE_ACCESS_HINT_NONE);
-#endif
-
-   /* If playlist file does not exist,
-    * create an empty playlist instead */
-   if (!file)
-      return true;
-
-   playlist->compressed = intfstream_is_compressed(file);
-
-   /* Detect format of playlist
-    * > Read file until we find the first printable
-    *   non-whitespace ASCII character */
-   do
-   {
-	   /* Read error or EOF (end of file) */
-      if ((test_char = intfstream_getc(file)) == EOF)
-         goto end;
-   }while (!isgraph(test_char) || test_char > 0x7F);
-
-   playlist->old_format = (test_char != '{');
-
-   /* Reset file to start */
-   intfstream_rewind(file);
-
-   if (!playlist->old_format)
-   {
-      rjson_t* parser;
-      JSONContext context = {0};
-      context.playlist    = playlist;
-
-      if (!(parser = rjson_open_stream(file)))
-      {
-         RARCH_ERR("Failed to create JSON parser\n");
-         goto end;
-      }
-
-      rjson_set_options(parser,
-              RJSON_OPTION_ALLOW_UTF8BOM
-            | RJSON_OPTION_ALLOW_COMMENTS
-            | RJSON_OPTION_ALLOW_UNESCAPED_CONTROL_CHARACTERS
-            | RJSON_OPTION_REPLACE_INVALID_ENCODING);
-
-      if (rjson_parse(parser, &context,
-            JSONObjectMemberHandler,
-            JSONStringHandler,
-            JSONNumberHandler,
-            JSONStartObjectHandler,
-            JSONEndObjectHandler,
-            JSONStartArrayHandler,
-            JSONEndArrayHandler,
-            JSONBoolHandler,
-            NULL) /* Unused null handler */
-            != RJSON_DONE)
-      {
-         if (context.out_of_memory)
-         {
-            RARCH_WARN("Ran out of memory while parsing JSON playlist\n");
-            res = false;
-         }
-         else
-         {
-            RARCH_WARN("Error parsing chunk:\n---snip---\n%.*s\n---snip---\n",
-                  rjson_get_source_context_len(parser),
-                  rjson_get_source_context_buf(parser));
-            RARCH_WARN("Error: Invalid JSON at line %d, column %d - %s.\n",
-                  (int)rjson_get_source_line(parser),
-                  (int)rjson_get_source_column(parser),
-                  (*rjson_get_error(parser) ? rjson_get_error(parser) : "format error"));
-         }
-      }
-      rjson_free(parser);
-   }
-   else
-   {
-      size_t len = RBUF_LEN(playlist->entries);
-      char line_buf[PLAYLIST_ENTRIES][PATH_MAX_LENGTH] = {{0}};
-
-      /* Unnecessary, but harmless */
-      for (i = 0; i < PLAYLIST_ENTRIES; i++)
-         line_buf[i][0] = '\0';
-
-      /* Read playlist entries */
-      while (len < playlist->config.capacity)
-      {
-         size_t i;
-         size_t lines_read = 0;
-
-         /* Attempt to read the next 'PLAYLIST_ENTRIES'
-          * lines from the file */
-         for (i = 0; i < PLAYLIST_ENTRIES; i++)
-         {
-            *line_buf[i] = '\0';
-
-            if (!intfstream_gets(file, line_buf[i], sizeof(line_buf[i])))
-               break;
-            /* Ensure line is NULL terminated, regardless of
-             * Windows or Unix line endings */
-            string_replace_all_chars(line_buf[i], '\r', '\0');
-            string_replace_all_chars(line_buf[i], '\n', '\0');
-
-            lines_read++;
-         }
-
-         /* If a 'full set' of lines were read, then this
-          * is a valid playlist entry */
-         if (lines_read >= PLAYLIST_ENTRIES)
-         {
-            struct playlist_entry* entry;
-
-            if (!RBUF_TRYFIT(playlist->entries, len + 1))
-            {
-               res = false; /* out of memory */
-               goto end;
-            }
-            RBUF_RESIZE(playlist->entries, len + 1);
-            entry = &playlist->entries[len++];
-
-            memset(entry, 0, sizeof(*entry));
-
-            /* path */
-            if (!string_is_empty(line_buf[0]))
-               entry->path      = strdup(line_buf[0]);
-
-            /* label */
-            if (!string_is_empty(line_buf[1]))
-               entry->label     = strdup(line_buf[1]);
-
-            /* core_path */
-            if (!string_is_empty(line_buf[2]))
-               entry->core_path = strdup(line_buf[2]);
-
-            /* core_name */
-            if (!string_is_empty(line_buf[3]))
-               entry->core_name = strdup(line_buf[3]);
-
-            /* crc32 */
-            if (!string_is_empty(line_buf[4]))
-               entry->crc32     = strdup(line_buf[4]);
-
-            /* db_name */
-            if (!string_is_empty(line_buf[5]))
-               entry->db_name   = strdup(line_buf[5]);
-         }
-         /* If fewer than 'PLAYLIST_ENTRIES' lines were
-          * read, then this is metadata */
-         else
-         {
-            char default_core_path[PATH_MAX_LENGTH];
-            char default_core_name[PATH_MAX_LENGTH];
-
-            default_core_path[0] = '\0';
-            default_core_name[0] = '\0';
-
-            /* Get default_core_path */
-            if (lines_read < 1)
-               break;
-
-            if (strncmp("default_core_path",
-                     line_buf[0],
-                     STRLEN_CONST("default_core_path")) == 0)
-               playlist_get_old_format_metadata_value(
-                     line_buf[0], default_core_path, sizeof(default_core_path));
-
-            /* Get default_core_name */
-            if (lines_read < 2)
-               break;
-
-            if (strncmp("default_core_name",
-                     line_buf[1],
-                     STRLEN_CONST("default_core_name")) == 0)
-               playlist_get_old_format_metadata_value(
-                     line_buf[1], default_core_name, sizeof(default_core_name));
-
-            /* > Populate default core path/name, if required
-             *   (if one is empty, the other should be ignored) */
-            if (!string_is_empty(default_core_path) &&
-                !string_is_empty(default_core_name))
-            {
-               playlist->default_core_path = strdup(default_core_path);
-               playlist->default_core_name = strdup(default_core_name);
-            }
-
-            /* Get label_display_mode */
-            if (lines_read < 3)
-               break;
-
-            if (strncmp("label_display_mode",
-                     line_buf[2],
-                     STRLEN_CONST("label_display_mode")) == 0)
-            {
-               unsigned display_mode;
-               char display_mode_str[4] = {0};
-
-               playlist_get_old_format_metadata_value(
-                     line_buf[2], display_mode_str, sizeof(display_mode_str));
-
-               display_mode = string_to_unsigned(display_mode_str);
-
-               if (display_mode <= LABEL_DISPLAY_MODE_KEEP_REGION_AND_DISC_INDEX)
-                  playlist->label_display_mode = (enum playlist_label_display_mode)display_mode;
-            }
-
-            /* Get thumbnail modes */
-            if (lines_read < 4)
-               break;
-
-            if (strncmp("thumbnail_mode",
-                     line_buf[3],
-                     STRLEN_CONST("thumbnail_mode")) == 0)
-            {
-               char thumbnail_mode_str[8]          = {0};
-               struct string_list thumbnail_modes  = {0};
-
-               playlist_get_old_format_metadata_value(
-                     line_buf[3], thumbnail_mode_str,
-                     sizeof(thumbnail_mode_str));
-               string_list_initialize(&thumbnail_modes);
-               if (string_split_noalloc(&thumbnail_modes,
-                        thumbnail_mode_str, "|"))
-               {
-                  if (thumbnail_modes.size == 2)
-                  {
-                     /* Right thumbnail mode */
-                     unsigned thumbnail_mode = string_to_unsigned(
-                           thumbnail_modes.elems[0].data);
-                     if (thumbnail_mode <= PLAYLIST_THUMBNAIL_MODE_BOXARTS)
-                        playlist->right_thumbnail_mode = (enum playlist_thumbnail_mode)thumbnail_mode;
-
-                     /* Left thumbnail mode */
-                     thumbnail_mode = string_to_unsigned(
-                           thumbnail_modes.elems[1].data);
-                     if (thumbnail_mode <= PLAYLIST_THUMBNAIL_MODE_BOXARTS)
-                        playlist->left_thumbnail_mode = (enum playlist_thumbnail_mode)thumbnail_mode;
-                  }
-               }
-               string_list_deinitialize(&thumbnail_modes);
-            }
-
-            /* Get sort_mode */
-            if (lines_read < 5)
-               break;
-
-            if (strncmp("sort_mode",
-                     line_buf[4],
-                     STRLEN_CONST("sort_mode")) == 0)
-            {
-               unsigned sort_mode;
-               char sort_mode_str[4] = {0};
-
-               playlist_get_old_format_metadata_value(
-                     line_buf[4], sort_mode_str, sizeof(sort_mode_str));
-
-               sort_mode = string_to_unsigned(sort_mode_str);
-
-               if (sort_mode <= PLAYLIST_SORT_MODE_OFF)
-                  playlist->sort_mode = (enum playlist_sort_mode)sort_mode;
-            }
-
-            /* All metadata parsed -> end of file */
-            break;
-         }
-      }
-   }
-
-end:
-   intfstream_close(file);
-   free(file);
-   return res;
-}
-
-void playlist_free_cached(void)
-{
-   if (playlist_cached && !playlist_cached->cached_external)
-      playlist_free(playlist_cached);
-   playlist_cached = NULL;
-}
-
-playlist_t *playlist_get_cached(void)
-{
-   if (playlist_cached)
-      return playlist_cached;
-   return NULL;
-}
-
-bool playlist_init_cached(const playlist_config_t *config)
-{
-   playlist_t *playlist = playlist_init(config);
-   if (!playlist)
-      return false;
-
-   /* If playlist format/compression state
-    * does not match requested settings, update
-    * file on disk immediately */
-   if (
-#if defined(HAVE_ZLIB)
-       (playlist->compressed != playlist->config.compress) ||
-#endif
-       (playlist->old_format != playlist->config.old_format))
-      playlist_write_file(playlist);
-
-   playlist_cached      = playlist;
-   return true;
-}
-
-/**
- * playlist_init:
- * @config            	: Playlist configuration object.
- *
- * Creates and initializes a playlist.
- *
- * Returns: handle to new playlist if successful, otherwise NULL
- **/
-playlist_t *playlist_init(const playlist_config_t *config)
-{
-   playlist_t           *playlist = (playlist_t*)malloc(sizeof(*playlist));
-   if (!playlist)
-      goto error;
-
-   /* Set initial values */
-   playlist->modified               = false;
-   playlist->old_format             = false;
-   playlist->compressed             = false;
-   playlist->cached_external        = false;
-   playlist->default_core_name      = NULL;
-   playlist->default_core_path      = NULL;
-   playlist->base_content_directory = NULL;
-   playlist->entries                = NULL;
-   playlist->label_display_mode     = LABEL_DISPLAY_MODE_DEFAULT;
-   playlist->right_thumbnail_mode   = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
-   playlist->left_thumbnail_mode    = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
-   playlist->sort_mode              = PLAYLIST_SORT_MODE_DEFAULT;
-
-   playlist->scan_record.search_recursively = false;
-   playlist->scan_record.search_archives    = false;
-   playlist->scan_record.filter_dat_content = false;
-   playlist->scan_record.content_dir        = NULL;
-   playlist->scan_record.file_exts          = NULL;
-   playlist->scan_record.dat_file_path      = NULL;
-
-   /* Cache configuration parameters */
-   if (!playlist_config_copy(config, &playlist->config))
-      goto error;
-
-   /* Attempt to read any existing playlist file */
-   if (!playlist_read_file(playlist))
-      goto error;
-
-   /* Try auto-fixing paths if enabled, and playlist
-    * base content directory is different */
-   if (config->autofix_paths &&
-       !string_is_equal(playlist->base_content_directory,
-            config->base_content_directory))
-   {
-      if (!string_is_empty(playlist->base_content_directory))
-      {
-         size_t i, j, len;
-         char tmp_entry_path[PATH_MAX_LENGTH];
-
-         for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
-         {
-            struct playlist_entry* entry = &playlist->entries[i];
-
-            if (!entry || string_is_empty(entry->path))
-               continue;
-
-            /* Fix entry path */
-            tmp_entry_path[0] = '\0';
-            path_replace_base_path_and_convert_to_local_file_system(
-                  tmp_entry_path, entry->path,
-                  playlist->base_content_directory, playlist->config.base_content_directory,
-                  sizeof(tmp_entry_path));
-
-            free(entry->path);
-            entry->path = strdup(tmp_entry_path);
-
-            /* Fix subsystem roms paths*/
-            if (entry->subsystem_roms && (entry->subsystem_roms->size > 0))
-            {
-               struct string_list* subsystem_roms_new_paths = string_list_new();
-               union string_list_elem_attr attributes = { 0 };
-
-               if (!subsystem_roms_new_paths)
-                  goto error;
-
-               for (j = 0; j < entry->subsystem_roms->size; j++)
-               {
-                  const char* subsystem_rom_path = entry->subsystem_roms->elems[j].data;
-
-                  if (string_is_empty(subsystem_rom_path))
-                     continue;
-
-                  tmp_entry_path[0] = '\0';
-                  path_replace_base_path_and_convert_to_local_file_system(
-                        tmp_entry_path, subsystem_rom_path,
-                        playlist->base_content_directory, playlist->config.base_content_directory,
-                        sizeof(tmp_entry_path));
-                  string_list_append(subsystem_roms_new_paths, tmp_entry_path, attributes);
-               }
-
-               string_list_free(entry->subsystem_roms);
-               entry->subsystem_roms = subsystem_roms_new_paths;
-            }
-         }
-
-         /* Fix scan record content directory */
-         if (!string_is_empty(playlist->scan_record.content_dir))
-         {
-            tmp_entry_path[0] = '\0';
-            path_replace_base_path_and_convert_to_local_file_system(
-                  tmp_entry_path, playlist->scan_record.content_dir,
-                  playlist->base_content_directory, playlist->config.base_content_directory,
-                  sizeof(tmp_entry_path));
-
-            free(playlist->scan_record.content_dir);
-            playlist->scan_record.content_dir = strdup(tmp_entry_path);
-         }
-
-         /* Fix scan record arcade DAT file */
-         if (!string_is_empty(playlist->scan_record.dat_file_path))
-         {
-            tmp_entry_path[0] = '\0';
-            path_replace_base_path_and_convert_to_local_file_system(
-                  tmp_entry_path, playlist->scan_record.dat_file_path,
-                  playlist->base_content_directory, playlist->config.base_content_directory,
-                  sizeof(tmp_entry_path));
-
-            free(playlist->scan_record.dat_file_path);
-            playlist->scan_record.dat_file_path = strdup(tmp_entry_path);
-         }
-      }
-
-      /* Update playlist base content directory*/
-      if (playlist->base_content_directory)
-         free(playlist->base_content_directory);
-      playlist->base_content_directory = strdup(playlist->config.base_content_directory);
-
-      /* Save playlist */
-      playlist->modified = true;
-      playlist_write_file(playlist);
-   }
-
-   return playlist;
-
-error:
-   playlist_free(playlist);
-   return NULL;
-}
-
-static int playlist_qsort_func(const struct playlist_entry *a,
-      const struct playlist_entry *b)
-{
-   char *a_str            = NULL;
-   char *b_str            = NULL;
-   char *a_fallback_label = NULL;
-   char *b_fallback_label = NULL;
-   int ret                = 0;
-
-   if (!a || !b)
-      goto end;
-
-   a_str                  = a->label;
-   b_str                  = b->label;
-
-   /* It is quite possible for playlist labels
-    * to be blank. If that is the case, have to use
-    * filename as a fallback (this is slow, but we
-    * have no other option...) */
-   if (string_is_empty(a_str))
-   {
-      if (!(a_fallback_label = (char*)calloc(PATH_MAX_LENGTH, sizeof(char))))
-         goto end;
-
-      if (!string_is_empty(a->path))
-         fill_pathname(a_fallback_label,
-               path_basename_nocompression(a->path),
-               "", PATH_MAX_LENGTH * sizeof(char));
-      /* If filename is also empty, use core name
-       * instead -> this matches the behaviour of
-       * menu_displaylist_parse_playlist() */
-      else if (!string_is_empty(a->core_name))
-         strlcpy(a_fallback_label, a->core_name, PATH_MAX_LENGTH * sizeof(char));
-
-      /* If both filename and core name are empty,
-       * then have to compare an empty string
-       * -> again, this is to match the behaviour of
-       * menu_displaylist_parse_playlist() */
-
-      a_str = a_fallback_label;
-   }
-
-   if (string_is_empty(b_str))
-   {
-      if (!(b_fallback_label = (char*)calloc(PATH_MAX_LENGTH, sizeof(char))))
-         goto end;
-
-      if (!string_is_empty(b->path))
-         fill_pathname(b_fallback_label,
-               path_basename_nocompression(b->path), "",
-               PATH_MAX_LENGTH * sizeof(char));
-      else if (!string_is_empty(b->core_name))
-         strlcpy(b_fallback_label, b->core_name, PATH_MAX_LENGTH * sizeof(char));
-
-      b_str = b_fallback_label;
-   }
-
-   ret = strcasecmp(a_str, b_str);
-
-end:
-
-   a_str = NULL;
-   b_str = NULL;
-
-   if (a_fallback_label)
-   {
-      free(a_fallback_label);
-      a_fallback_label = NULL;
-   }
-
-   if (b_fallback_label)
-   {
-      free(b_fallback_label);
-      b_fallback_label = NULL;
-   }
-
-   return ret;
-}
-
-void playlist_qsort(playlist_t *playlist)
-{
-   /* Avoid inadvertent sorting if 'sort mode'
-    * has been set explicitly to PLAYLIST_SORT_MODE_OFF */
-   if (!playlist ||
-       (playlist->sort_mode == PLAYLIST_SORT_MODE_OFF) ||
-       !playlist->entries)
-      return;
-
-   qsort(playlist->entries, RBUF_LEN(playlist->entries),
-         sizeof(struct playlist_entry),
-         (int (*)(const void *, const void *))playlist_qsort_func);
-}
-
-void command_playlist_push_write(
-      playlist_t *playlist,
-      const struct playlist_entry *entry)
-{
-   if (playlist && playlist_push(playlist, entry))
-      playlist_write_file(playlist);
-}
-
-void command_playlist_update_write(
-      playlist_t *plist,
-      size_t idx,
-      const struct playlist_entry *entry)
-{
-   playlist_t *playlist = plist ? plist : playlist_get_cached();
-
-   if (!playlist)
-      return;
-
-   playlist_update(
-         playlist,
-         idx,
-         entry);
-
-   playlist_write_file(playlist);
-}
-
-bool playlist_index_is_valid(playlist_t *playlist, size_t idx,
-      const char *path, const char *core_path)
-{
-   if (!playlist)
-      return false;
-
-   if (idx >= RBUF_LEN(playlist->entries))
-      return false;
-
-   return playlist_path_equal(path, playlist->entries[idx].path, &playlist->config) &&
-          string_is_equal(path_basename_nocompression(playlist->entries[idx].core_path), path_basename_nocompression(core_path));
-}
-
-bool playlist_entries_are_equal(
-      const struct playlist_entry *entry_a,
-      const struct playlist_entry *entry_b,
-      const playlist_config_t *config)
-{
-   char real_path_a[PATH_MAX_LENGTH];
-   char real_core_path_a[PATH_MAX_LENGTH];
-
-   /* Sanity check */
-   if (!entry_a || !entry_b || !config)
-      return false;
-
-   if (   string_is_empty(entry_a->path)
-       && string_is_empty(entry_a->core_path)
-       && string_is_empty(entry_b->path)
-       && string_is_empty(entry_b->core_path))
-      return true;
-
-   /* Check content paths */
-   if (!string_is_empty(entry_a->path))
-   {
-      strlcpy(real_path_a, entry_a->path, sizeof(real_path_a));
-      path_resolve_realpath(real_path_a, sizeof(real_path_a), true);
-   }
-   else
-      real_path_a[0]      = '\0';
-
-   if (!playlist_path_equal(
-         real_path_a, entry_b->path, config))
-      return false;
-
-   /* Check core paths */
-   if (!string_is_empty(entry_a->core_path))
-   {
-      strlcpy(real_core_path_a, entry_a->core_path, sizeof(real_core_path_a));
-      if (!string_is_equal(real_core_path_a, FILE_PATH_DETECT) &&
-          !string_is_equal(real_core_path_a, FILE_PATH_BUILTIN))
-         playlist_resolve_path(PLAYLIST_SAVE, true,
-               real_core_path_a, sizeof(real_core_path_a));
-   }
-   else
-      real_core_path_a[0] = '\0';
-
-   return playlist_core_path_equal(real_core_path_a, entry_b->core_path, config);
-}
-
-/* Returns true if entries at specified indices
- * of specified playlist have identical content
- * and core paths */
-bool playlist_index_entries_are_equal(
-      playlist_t *playlist, size_t idx_a, size_t idx_b)
-{
-   struct playlist_entry *entry_a = NULL;
-   struct playlist_entry *entry_b = NULL;
-   size_t len;
-
-   if (!playlist)
-      return false;
-
-   len = RBUF_LEN(playlist->entries);
-
-   if ((idx_a >= len) || (idx_b >= len))
-      return false;
-
-   /* Fetch entries */
-   entry_a = &playlist->entries[idx_a];
-   entry_b = &playlist->entries[idx_b];
-
-   if (!entry_a || !entry_b)
-      return false;
-
-   /* Initialise path ID for entry A, if required
-    * (entry B will be handled inside
-    * playlist_path_matches_entry()) */
-   if (!entry_a->path_id)
-      entry_a->path_id = playlist_path_id_init(entry_a->path);
-
-   return playlist_path_matches_entry(
-         entry_a->path_id, entry_b, &playlist->config);
-}
-
-void playlist_get_crc32(playlist_t *playlist, size_t idx,
-      const char **crc32)
-{
-   if (!playlist || idx >= RBUF_LEN(playlist->entries))
-      return;
-
-   if (crc32)
-      *crc32 = playlist->entries[idx].crc32;
-}
-
-void playlist_get_db_name(playlist_t *playlist, size_t idx,
-      const char **db_name)
-{
-   if (!playlist || idx >= RBUF_LEN(playlist->entries))
-      return;
-
-   if (db_name)
-   {
-      if (!string_is_empty(playlist->entries[idx].db_name))
-         *db_name = playlist->entries[idx].db_name;
-      else
-      {
-         const char *conf_path_basename = path_basename_nocompression(playlist->config.path);
-
-         /* Only use file basename if this is a 'collection' playlist
-          * (i.e. ignore history/favourites) */
-         if (
-                  !string_is_empty(conf_path_basename)
-               && !string_ends_with_size(conf_path_basename, "_history.lpl",
-                        strlen(conf_path_basename), STRLEN_CONST("_history.lpl"))
-               && !string_is_equal(conf_path_basename,
-                        FILE_PATH_CONTENT_FAVORITES)
-            )
-            *db_name = conf_path_basename;
-      }
-   }
-}
-
-const char *playlist_get_default_core_path(playlist_t *playlist)
-{
-   if (!playlist)
-      return NULL;
-   return playlist->default_core_path;
-}
-
-const char *playlist_get_default_core_name(playlist_t *playlist)
-{
-   if (!playlist)
-      return NULL;
-   return playlist->default_core_name;
-}
-
-enum playlist_label_display_mode playlist_get_label_display_mode(playlist_t *playlist)
-{
-   if (!playlist)
-      return LABEL_DISPLAY_MODE_DEFAULT;
-   return playlist->label_display_mode;
-}
-
-enum playlist_thumbnail_mode playlist_get_thumbnail_mode(
-      playlist_t *playlist, enum playlist_thumbnail_id thumbnail_id)
-{
-   if (playlist)
-   {
-      if (thumbnail_id == PLAYLIST_THUMBNAIL_RIGHT)
-         return playlist->right_thumbnail_mode;
-      else if (thumbnail_id == PLAYLIST_THUMBNAIL_LEFT)
-         return playlist->left_thumbnail_mode;
-   }
-   /* Fallback */
-   return PLAYLIST_THUMBNAIL_MODE_DEFAULT;
-}
-
-enum playlist_sort_mode playlist_get_sort_mode(playlist_t *playlist)
-{
-   if (!playlist)
-      return PLAYLIST_SORT_MODE_DEFAULT;
-   return playlist->sort_mode;
-}
-
-const char *playlist_get_scan_content_dir(playlist_t *playlist)
-{
-   if (!playlist)
-      return NULL;
-   return playlist->scan_record.content_dir;
-}
-
-const char *playlist_get_scan_file_exts(playlist_t *playlist)
-{
-   if (!playlist)
-      return NULL;
-   return playlist->scan_record.file_exts;
-}
-
-const char *playlist_get_scan_dat_file_path(playlist_t *playlist)
-{
-   if (!playlist)
-      return NULL;
-   return playlist->scan_record.dat_file_path;
-}
-
-bool playlist_get_scan_search_recursively(playlist_t *playlist)
-{
-   if (!playlist)
-      return false;
-   return playlist->scan_record.search_recursively;
-}
-
-bool playlist_get_scan_search_archives(playlist_t *playlist)
-{
-   if (!playlist)
-      return false;
-   return playlist->scan_record.search_archives;
-}
-
-bool playlist_get_scan_filter_dat_content(playlist_t *playlist)
-{
-   if (!playlist)
-      return false;
-   return playlist->scan_record.filter_dat_content;
-}
-
-bool playlist_scan_refresh_enabled(playlist_t *playlist)
-{
-   if (!playlist)
-      return false;
-   return !string_is_empty(playlist->scan_record.content_dir);
-}
-
-void playlist_set_default_core_path(playlist_t *playlist,
-      const char *core_path)
-{
-   char real_core_path[PATH_MAX_LENGTH];
-
-   if (!playlist || string_is_empty(core_path))
-      return;
-
-   /* Get 'real' core path */
-   strlcpy(real_core_path, core_path, sizeof(real_core_path));
-   if (!string_is_equal(real_core_path, FILE_PATH_DETECT) &&
-       !string_is_equal(real_core_path, FILE_PATH_BUILTIN))
-       playlist_resolve_path(PLAYLIST_SAVE, true,
-             real_core_path, sizeof(real_core_path));
-
-   if (string_is_empty(real_core_path))
-      return;
-
-   if (!string_is_equal(playlist->default_core_path, real_core_path))
-   {
-      if (playlist->default_core_path)
-         free(playlist->default_core_path);
-      playlist->default_core_path  = strdup(real_core_path);
-      playlist->modified           = true;
-   }
-}
-
-void playlist_set_default_core_name(
-      playlist_t *playlist, const char *core_name)
-{
-   if (!playlist || string_is_empty(core_name))
-      return;
-
-   if (!string_is_equal(playlist->default_core_name, core_name))
-   {
-      if (playlist->default_core_name)
-         free(playlist->default_core_name);
-      playlist->default_core_name  = strdup(core_name);
-      playlist->modified           = true;
-   }
-}
-
-void playlist_set_label_display_mode(playlist_t *playlist,
-      enum playlist_label_display_mode label_display_mode)
-{
-   if (playlist && playlist->label_display_mode != label_display_mode)
-   {
-      playlist->label_display_mode = label_display_mode;
-      playlist->modified           = true;
-   }
-}
-
-void playlist_set_thumbnail_mode(
-      playlist_t *playlist, enum playlist_thumbnail_id thumbnail_id,
-      enum playlist_thumbnail_mode thumbnail_mode)
-{
-   if (!playlist)
-      return;
-
-   switch (thumbnail_id)
-   {
-      case PLAYLIST_THUMBNAIL_RIGHT:
-         playlist->right_thumbnail_mode = thumbnail_mode;
-         playlist->modified             = true;
-         break;
-      case PLAYLIST_THUMBNAIL_LEFT:
-         playlist->left_thumbnail_mode = thumbnail_mode;
-         playlist->modified            = true;
-         break;
-   }
-}
-
-void playlist_set_sort_mode(playlist_t *playlist,
-      enum playlist_sort_mode sort_mode)
-{
-   if (playlist && playlist->sort_mode != sort_mode)
-   {
-      playlist->sort_mode = sort_mode;
-      playlist->modified  = true;
-   }
-}
-
-void playlist_set_scan_content_dir(playlist_t *playlist, const char *content_dir)
-{
-   bool current_string_empty;
-   bool new_string_empty;
-
-   if (!playlist)
-      return;
-
-   current_string_empty = string_is_empty(playlist->scan_record.content_dir);
-   new_string_empty     = string_is_empty(content_dir);
-
-   /* Check whether string value has changed
-    * (note that a NULL or empty argument will
-    * unset the playlist value) */
-   if (( current_string_empty && !new_string_empty) ||
-       (!current_string_empty &&  new_string_empty) ||
-       !string_is_equal(playlist->scan_record.content_dir, content_dir))
-      playlist->modified = true;
-   else
-      return; /* Strings are identical; do nothing */
-
-   if (playlist->scan_record.content_dir)
-   {
-      free(playlist->scan_record.content_dir);
-      playlist->scan_record.content_dir = NULL;
-   }
-
-   if (!new_string_empty)
-      playlist->scan_record.content_dir = strdup(content_dir);
-}
-
-void playlist_set_scan_file_exts(playlist_t *playlist, const char *file_exts)
-{
-   bool current_string_empty;
-   bool new_string_empty;
-
-   if (!playlist)
-      return;
-
-   current_string_empty = string_is_empty(playlist->scan_record.file_exts);
-   new_string_empty     = string_is_empty(file_exts);
-
-   /* Check whether string value has changed
-    * (note that a NULL or empty argument will
-    * unset the playlist value) */
-   if (   ( current_string_empty && !new_string_empty)
-       || (!current_string_empty &&  new_string_empty)
-       || !string_is_equal(playlist->scan_record.file_exts, file_exts))
-      playlist->modified = true;
-   else
-      return; /* Strings are identical; do nothing */
-
-   if (playlist->scan_record.file_exts)
-   {
-      free(playlist->scan_record.file_exts);
-      playlist->scan_record.file_exts = NULL;
-   }
-
-   if (!new_string_empty)
-      playlist->scan_record.file_exts = strdup(file_exts);
-}
-
-void playlist_set_scan_dat_file_path(playlist_t *playlist, const char *dat_file_path)
-{
-   bool current_string_empty;
-   bool new_string_empty;
-
-   if (!playlist)
-      return;
-
-   current_string_empty = string_is_empty(playlist->scan_record.dat_file_path);
-   new_string_empty     = string_is_empty(dat_file_path);
-
-   /* Check whether string value has changed
-    * (note that a NULL or empty argument will
-    * unset the playlist value) */
-   if (( current_string_empty && !new_string_empty) ||
-       (!current_string_empty &&  new_string_empty) ||
-       !string_is_equal(playlist->scan_record.dat_file_path, dat_file_path))
-      playlist->modified = true;
-   else
-      return; /* Strings are identical; do nothing */
-
-   if (playlist->scan_record.dat_file_path)
-   {
-      free(playlist->scan_record.dat_file_path);
-      playlist->scan_record.dat_file_path = NULL;
-   }
-
-   if (!new_string_empty)
-      playlist->scan_record.dat_file_path = strdup(dat_file_path);
-}
-
-void playlist_set_scan_search_recursively(playlist_t *playlist, bool search_recursively)
-{
-   if (playlist && playlist->scan_record.search_recursively != search_recursively)
-   {
-      playlist->scan_record.search_recursively = search_recursively;
-      playlist->modified = true;
-   }
-}
-
-void playlist_set_scan_search_archives(playlist_t *playlist, bool search_archives)
-{
-   if (playlist && playlist->scan_record.search_archives != search_archives)
-   {
-      playlist->scan_record.search_archives = search_archives;
-      playlist->modified = true;
-   }
-}
-
-void playlist_set_scan_filter_dat_content(playlist_t *playlist, bool filter_dat_content)
-{
-   if (playlist && playlist->scan_record.filter_dat_content != filter_dat_content)
-   {
-      playlist->scan_record.filter_dat_content = filter_dat_content;
-      playlist->modified = true;
-   }
-}
-
-/* Returns true if specified entry has a valid
- * core association (i.e. a non-empty string
- * other than DETECT) */
-bool playlist_entry_has_core(const struct playlist_entry *entry)
-{
-   if (  !entry
-       || string_is_empty(entry->core_path)
-       || string_is_empty(entry->core_name)
-       || string_is_equal(entry->core_path, FILE_PATH_DETECT)
-       || string_is_equal(entry->core_name, FILE_PATH_DETECT))
-      return false;
-   return true;
-}
-
-/* Fetches core info object corresponding to the
- * currently associated core of the specified
- * playlist entry.
- * Returns NULL if entry does not have a valid
- * core association */
-core_info_t *playlist_entry_get_core_info(const struct playlist_entry* entry)
-{
-   if (playlist_entry_has_core(entry))
-   {
-      core_info_t *core_info = NULL;
-      /* Search for associated core */
-      if (core_info_find(entry->core_path, &core_info))
-         return core_info;
-   }
-   return NULL;
-}
-
-/* Fetches core info object corresponding to the
- * currently associated default core of the
- * specified playlist.
- * Returns NULL if playlist does not have a valid
- * default core association */
-core_info_t *playlist_get_default_core_info(playlist_t* playlist)
-{
-   core_info_t *core_info = NULL;
-
-   if (  !playlist
-       || string_is_empty(playlist->default_core_path)
-       || string_is_empty(playlist->default_core_name)
-       || string_is_equal(playlist->default_core_path, FILE_PATH_DETECT)
-       || string_is_equal(playlist->default_core_name, FILE_PATH_DETECT))
-      return NULL;
-
-   /* Search for associated core */
-   if (core_info_find(playlist->default_core_path, &core_info))
-      return core_info;
-
-   return NULL;
-}

+ 0 - 406
app/src/main/cpp/playlist.h

@@ -1,406 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- *  Copyright (C) 2011-2017 - Daniel De Matteis
- *  Copyright (C) 2016-2019 - Brad Parker
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef _PLAYLIST_H__
-#define _PLAYLIST_H__
-
-#include <stddef.h>
-
-#include <retro_common_api.h>
-#include <boolean.h>
-#include <lists/string_list.h>
-
-#include "core_info.h"
-
-RETRO_BEGIN_DECLS
-
-/* Default maximum playlist size */
-#define COLLECTION_SIZE 0x7FFFFFFF
-
-typedef struct content_playlist playlist_t;
-
-enum playlist_runtime_status
-{
-   PLAYLIST_RUNTIME_UNKNOWN = 0,
-   PLAYLIST_RUNTIME_MISSING,
-   PLAYLIST_RUNTIME_VALID
-};
-
-enum playlist_file_mode
-{
-   PLAYLIST_LOAD = 0,
-   PLAYLIST_SAVE
-};
-
-enum playlist_label_display_mode
-{
-   LABEL_DISPLAY_MODE_DEFAULT = 0,
-   LABEL_DISPLAY_MODE_REMOVE_PARENTHESES,
-   LABEL_DISPLAY_MODE_REMOVE_BRACKETS,
-   LABEL_DISPLAY_MODE_REMOVE_PARENTHESES_AND_BRACKETS,
-   LABEL_DISPLAY_MODE_KEEP_REGION,
-   LABEL_DISPLAY_MODE_KEEP_DISC_INDEX,
-   LABEL_DISPLAY_MODE_KEEP_REGION_AND_DISC_INDEX
-};
-
-enum playlist_thumbnail_mode
-{
-   PLAYLIST_THUMBNAIL_MODE_DEFAULT = 0,
-   PLAYLIST_THUMBNAIL_MODE_OFF,
-   PLAYLIST_THUMBNAIL_MODE_SCREENSHOTS,
-   PLAYLIST_THUMBNAIL_MODE_TITLE_SCREENS,
-   PLAYLIST_THUMBNAIL_MODE_BOXARTS
-};
-
-enum playlist_sort_mode
-{
-   PLAYLIST_SORT_MODE_DEFAULT = 0,
-   PLAYLIST_SORT_MODE_ALPHABETICAL,
-   PLAYLIST_SORT_MODE_OFF
-};
-
-/* TODO/FIXME - since gfx_thumbnail_path.h has now
- * been divorced from the menu code, perhaps jdgleaver
- * can refactor this? */
-
-/* Note: We already have a left/right enum defined
- * in gfx_thumbnail_path.h - but we can't include
- * menu code here, so have to make a 'duplicate'... */
-enum playlist_thumbnail_id
-{
-   PLAYLIST_THUMBNAIL_RIGHT = 0,
-   PLAYLIST_THUMBNAIL_LEFT
-};
-
-/* Holds all parameters required to uniquely
- * identify a playlist content path */
-typedef struct
-{
-   char *real_path;
-   char *archive_path;
-   uint32_t real_path_hash;
-   uint32_t archive_path_hash;
-   bool is_archive;
-   bool is_in_archive;
-} playlist_path_id_t;
-
-struct playlist_entry
-{
-   char *path;
-   char *label;
-   char *core_path;
-   char *core_name;
-   char *db_name;
-   char *crc32;
-   char *subsystem_ident;
-   char *subsystem_name;
-   char *runtime_str;
-   char *last_played_str;
-   struct string_list *subsystem_roms;
-   playlist_path_id_t *path_id;
-   unsigned entry_slot;
-   unsigned runtime_hours;
-   unsigned runtime_minutes;
-   unsigned runtime_seconds;
-   /* Note: due to platform dependence, have to record
-    * timestamp as either a string or independent integer
-    * values. The latter is more verbose, but more efficient. */
-   unsigned last_played_year;
-   unsigned last_played_month;
-   unsigned last_played_day;
-   unsigned last_played_hour;
-   unsigned last_played_minute;
-   unsigned last_played_second;
-   enum playlist_runtime_status runtime_status;
-};
-
-/* Holds all configuration parameters required
- * when initialising/saving playlists */
-typedef struct
-{
-   size_t capacity;
-   bool old_format;
-   bool compress;
-   bool fuzzy_archive_match;
-   bool autofix_paths;   
-   char path[PATH_MAX_LENGTH];
-   char base_content_directory[PATH_MAX_LENGTH];
-} playlist_config_t;
-
-/* Convenience function: copies specified playlist
- * path to specified playlist configuration object */
-void playlist_config_set_path(playlist_config_t *config, const char *path);
-
-/* Convenience function: copies base content directory
- * path to specified playlist configuration object */
-void playlist_config_set_base_content_directory(playlist_config_t* config, const char* path);
-
-/* Creates a copy of the specified playlist configuration.
- * Returns false in the event of an error */
-bool playlist_config_copy(const playlist_config_t *src, playlist_config_t *dst);
-
-/* Returns internal playlist configuration object
- * of specified playlist.
- * Returns NULL it the event of an error. */
-playlist_config_t *playlist_get_config(playlist_t *playlist);
-
-/**
- * playlist_init:
- * @config            	: Playlist configuration object.
- *
- * Creates and initializes a playlist.
- *
- * Returns: handle to new playlist if successful, otherwise NULL
- **/
-playlist_t *playlist_init(const playlist_config_t *config);
-
-/**
- * playlist_free:
- * @playlist        	   : Playlist handle.
- *
- * Frees playlist handle.
- */
-void playlist_free(playlist_t *playlist);
-
-/**
- * playlist_clear:
- * @playlist        	   : Playlist handle.
- *
- * Clears all playlist entries in playlist.
- **/
-void playlist_clear(playlist_t *playlist);
-
-/**
- * playlist_size:
- * @playlist        	   : Playlist handle.
- *
- * Gets size of playlist.
- * Returns: size of playlist.
- **/
-size_t playlist_size(playlist_t *playlist);
-
-/**
- * playlist_capacity:
- * @playlist        	   : Playlist handle.
- *
- * Gets maximum capacity of playlist.
- * Returns: maximum capacity of playlist.
- **/
-size_t playlist_capacity(playlist_t *playlist);
-
-/**
- * playlist_get_index:
- * @playlist               : Playlist handle.
- * @idx                 : Index of playlist entry.
- *
- * Gets values of playlist index:
- **/
-void playlist_get_index(playlist_t *playlist,
-      size_t idx,
-      const struct playlist_entry **entry);
-
-/**
- * playlist_delete_index:
- * @playlist               : Playlist handle.
- * @idx                 : Index of playlist entry.
- *
- * Deletes the entry at index:
- **/
-void playlist_delete_index(playlist_t *playlist,
-      size_t idx);
-
-/**
- * playlist_delete_by_path:
- * @playlist            : Playlist handle.
- * @search_path         : Content path.
- *
- * Deletes all entries with content path
- * matching 'search_path'
- **/
-void playlist_delete_by_path(playlist_t *playlist,
-      const char *search_path);
-
-/**
- * playlist_resolve_path:
- * @mode      : PLAYLIST_LOAD or PLAYLIST_SAVE
- * @is_core   : Set true if path to be resolved is a core file
- * @path      : The path to be modified
- *
- * Resolves the path of an item, such as the content path or path to the core, to a format
- * appropriate for saving or loading depending on the @mode parameter
- *
- * Can be platform specific. File paths for saving can be abbreviated to avoid saving absolute
- * paths, as the base directory (home or application dir) may change after each subsequent
- * install (iOS)
- **/
-void playlist_resolve_path(enum playlist_file_mode mode,
-      bool is_core, char *path, size_t len);
-
-/**
- * playlist_content_path_is_valid:
- * @path      : Content path
- *
- * Checks whether specified playlist content path
- * refers to an existent file. Handles all playlist
- * content path 'types' (i.e. can validate paths
- * referencing files inside archives).
- *
- * Returns true if file referenced by content
- * path exists on the host filesystem.
- **/
-bool playlist_content_path_is_valid(const char *path);
-
-/**
- * playlist_push:
- * @playlist        	   : Playlist handle.
- *
- * Push entry to top of playlist.
- **/
-bool playlist_push(playlist_t *playlist,
-      const struct playlist_entry *entry);
-
-bool playlist_push_runtime(playlist_t *playlist,
-      const struct playlist_entry *entry);
-
-void playlist_update(playlist_t *playlist, size_t idx,
-      const struct playlist_entry *update_entry);
-
-/* Note: register_update determines whether the internal
- * 'playlist->modified' flag is set when updating runtime
- * values. Since these are normally set temporarily (for
- * display purposes), we do not always want this function
- * to trigger a re-write of the playlist file. */
-void playlist_update_runtime(playlist_t *playlist, size_t idx,
-      const struct playlist_entry *update_entry,
-      bool register_update);
-
-void playlist_get_index_by_path(playlist_t *playlist,
-      const char *search_path,
-      const struct playlist_entry **entry);
-
-bool playlist_entry_exists(playlist_t *playlist,
-      const char *path);
-
-char *playlist_get_conf_path(playlist_t *playlist);
-
-uint32_t playlist_get_size(playlist_t *playlist);
-
-void playlist_write_file(playlist_t *playlist);
-
-void playlist_write_runtime_file(playlist_t *playlist);
-
-void playlist_qsort(playlist_t *playlist);
-
-void playlist_free_cached(void);
-
-playlist_t *playlist_get_cached(void);
-
-/* If current on-disk playlist file referenced
- * by 'config->path' does not match requested
- * 'old format' or 'compression' state, file will
- * be updated automatically
- * > Since this function is called whenever a
- *   playlist is browsed via the menu, this is
- *   a simple method for ensuring that files
- *   are always kept synced with user settings */
-bool playlist_init_cached(const playlist_config_t *config);
-
-void command_playlist_push_write(
-      playlist_t *playlist,
-      const struct playlist_entry *entry);
-
-void command_playlist_update_write(
-      playlist_t *playlist,
-      size_t idx,
-      const struct playlist_entry *entry);
-
-/* Returns true if specified playlist index matches
- * specified content/core paths */
-bool playlist_index_is_valid(playlist_t *playlist, size_t idx,
-      const char *path, const char *core_path);
-
-/* Returns true if specified playlist entries have
- * identical content and core paths */
-bool playlist_entries_are_equal(
-      const struct playlist_entry *entry_a,
-      const struct playlist_entry *entry_b,
-      const playlist_config_t *config);
-
-/* Returns true if entries at specified indices
- * of specified playlist have identical content
- * and core paths */
-bool playlist_index_entries_are_equal(
-      playlist_t *playlist, size_t idx_a, size_t idx_b);
-
-void playlist_get_crc32(playlist_t *playlist, size_t idx,
-      const char **crc32);
-
-/* If db_name is empty, 'returns' playlist file basename */
-void playlist_get_db_name(playlist_t *playlist, size_t idx,
-      const char **db_name);
-
-const char *playlist_get_default_core_path(playlist_t *playlist);
-const char *playlist_get_default_core_name(playlist_t *playlist);
-enum playlist_label_display_mode playlist_get_label_display_mode(playlist_t *playlist);
-enum playlist_thumbnail_mode playlist_get_thumbnail_mode(
-      playlist_t *playlist, enum playlist_thumbnail_id thumbnail_id);
-enum playlist_sort_mode playlist_get_sort_mode(playlist_t *playlist);
-const char *playlist_get_scan_content_dir(playlist_t *playlist);
-const char *playlist_get_scan_file_exts(playlist_t *playlist);
-const char *playlist_get_scan_dat_file_path(playlist_t *playlist);
-bool playlist_get_scan_search_recursively(playlist_t *playlist);
-bool playlist_get_scan_search_archives(playlist_t *playlist);
-bool playlist_get_scan_filter_dat_content(playlist_t *playlist);
-bool playlist_scan_refresh_enabled(playlist_t *playlist);
-
-void playlist_set_default_core_path(playlist_t *playlist, const char *core_path);
-void playlist_set_default_core_name(playlist_t *playlist, const char *core_name);
-void playlist_set_label_display_mode(playlist_t *playlist, enum playlist_label_display_mode label_display_mode);
-void playlist_set_thumbnail_mode(
-      playlist_t *playlist, enum playlist_thumbnail_id thumbnail_id, enum playlist_thumbnail_mode thumbnail_mode);
-void playlist_set_sort_mode(playlist_t *playlist, enum playlist_sort_mode sort_mode);
-void playlist_set_scan_content_dir(playlist_t *playlist, const char *content_dir);
-void playlist_set_scan_file_exts(playlist_t *playlist, const char *file_exts);
-void playlist_set_scan_dat_file_path(playlist_t *playlist, const char *dat_file_path);
-void playlist_set_scan_search_recursively(playlist_t *playlist, bool search_recursively);
-void playlist_set_scan_search_archives(playlist_t *playlist, bool search_archives);
-void playlist_set_scan_filter_dat_content(playlist_t *playlist, bool filter_dat_content);
-
-/* Returns true if specified entry has a valid
- * core association (i.e. a non-empty string
- * other than DETECT) */
-bool playlist_entry_has_core(const struct playlist_entry *entry);
-
-/* Fetches core info object corresponding to the
- * currently associated core of the specified
- * playlist entry.
- * Returns NULL if entry does not have a valid
- * core association */
-core_info_t *playlist_entry_get_core_info(const struct playlist_entry* entry);
-
-/* Fetches core info object corresponding to the
- * currently associated default core of the
- * specified playlist.
- * Returns NULL if playlist does not have a valid
- * default core association */
-core_info_t *playlist_get_default_core_info(playlist_t* playlist);
-
-void playlist_set_cached_external(playlist_t* pl);
-
-RETRO_END_DECLS
-
-#endif

+ 14 - 2
app/src/main/cpp/retroarch.c

@@ -3022,6 +3022,7 @@ bool command_event(enum event_command cmd, void *data)
          }
          break;
       case CMD_EVENT_HISTORY_DEINIT:
+#ifdef HAVE_PLAYLIST
          if (g_defaults.content_history)
          {
             playlist_write_file(g_defaults.content_history);
@@ -3052,9 +3053,11 @@ bool command_event(enum event_command cmd, void *data)
             playlist_free(g_defaults.image_history);
          }
          g_defaults.image_history = NULL;
+#endif
 #endif
          break;
       case CMD_EVENT_HISTORY_INIT:
+#ifdef HAVE_PLAYLIST
          {
             playlist_config_t playlist_config;
             const char *_msg                       = NULL;
@@ -3115,6 +3118,7 @@ bool command_event(enum event_command cmd, void *data)
                   g_defaults.image_history, PLAYLIST_SORT_MODE_OFF);
 #endif
          }
+#endif
          break;
       case CMD_EVENT_CORE_INFO_DEINIT:
          core_info_deinit_list();
@@ -3359,6 +3363,7 @@ bool command_event(enum event_command cmd, void *data)
          }
          break;
       case CMD_EVENT_ADD_TO_FAVORITES:
+#ifdef HAVE_PLAYLIST
          {
             struct string_list *str_list = (struct string_list*)data;
 
@@ -3403,9 +3408,11 @@ bool command_event(enum event_command cmd, void *data)
                }
             }
 
-            break;
          }
+#endif
+           break;
       case CMD_EVENT_RESET_CORE_ASSOCIATION:
+#ifdef HAVE_PLAYLIST
          {
             const char *core_name          = "DETECT";
             const char *core_path          = "DETECT";
@@ -3433,8 +3440,9 @@ bool command_event(enum event_command cmd, void *data)
 #endif
 
             runloop_msg_queue_push(msg_hash_to_str(MSG_RESET_CORE_ASSOCIATION), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
-            break;
          }
+#endif
+           break;
       case CMD_EVENT_RESTART_RETROARCH:
          if (!frontend_driver_set_fork(FRONTEND_FORK_RESTART))
             return false;
@@ -4682,7 +4690,9 @@ int rarch_main(int argc, char *argv[], void *data)
 
    libretro_free_system_info(&runloop_st->system.info);
    command_event(CMD_EVENT_HISTORY_DEINIT, NULL);
+#ifdef HAVE_PLAYLIST
    retroarch_favorites_deinit();
+#endif
 
    retroarch_config_init();
 
@@ -7247,6 +7257,7 @@ enum retro_language retroarch_get_language_from_iso(const char *iso639)
    return lang;
 }
 
+#ifdef HAVE_PLAYLIST
 void retroarch_favorites_init(void)
 {
    settings_t *settings                = config_get_ptr();
@@ -7295,6 +7306,7 @@ void retroarch_favorites_deinit(void)
    playlist_free(g_defaults.content_favorites);
    g_defaults.content_favorites = NULL;
 }
+#endif
 
 #ifdef HAVE_ACCESSIBILITY
 bool accessibility_speak_priority(

+ 2 - 0
app/src/main/cpp/retroarch.h

@@ -126,9 +126,11 @@ void retroarch_menu_running_finished(bool quit);
 
 enum retro_language retroarch_get_language_from_iso(const char *lang);
 
+#ifdef HAVE_PLAYLIST
 void retroarch_favorites_init(void);
 
 void retroarch_favorites_deinit(void);
+#endif
 
 /* Audio */
 

+ 11 - 7
app/src/main/cpp/runloop.c

@@ -137,7 +137,9 @@
 #include "audio/audio_driver.h"
 #include "gfx/gfx_animation.h"
 #include "gfx/gfx_display.h"
+#ifdef HAVE_GFX_THUBNAIL
 #include "gfx/gfx_thumbnail.h"
+#endif
 #include "gfx/video_filter.h"
 
 #include "input/input_osk.h"
@@ -6888,17 +6890,19 @@ int runloop_iterate(void)
             && !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL);
 #endif
 
-      if (want_runahead)
+      if (want_runahead) {
          runahead_run(
-               runloop_st,
-               run_ahead_num_frames,
-               run_ahead_hide_warnings,
-               run_ahead_secondary_instance);
-      else if (runloop_st->preempt_data)
+                 runloop_st,
+                 run_ahead_num_frames,
+                 run_ahead_hide_warnings,
+                 run_ahead_secondary_instance);
+      } else if (runloop_st->preempt_data) {
          preempt_run(runloop_st->preempt_data, runloop_st);
-      else
+      } else
 #endif
+      {
          core_run();
+      }
    }
 
    /* Increment runtime tick counter after each call to

+ 2 - 0
app/src/main/cpp/runtime_file.c

@@ -1271,6 +1271,7 @@ void runtime_log_convert_usec2hms(retro_time_t usec,
 
 /* Updates specified playlist entry runtime values with
  * contents of associated log file */
+#ifdef HAVE_PLAYLIST
 void runtime_update_playlist(
       playlist_t *playlist, size_t idx,
       const char *dir_runtime_log,
@@ -1373,6 +1374,7 @@ void runtime_update_playlist(
    /* Update playlist */
    playlist_update_runtime(playlist, idx, &update_entry, false);
 }
+#endif
 
 #if defined(HAVE_MENU)
 /* Contentless cores manipulation */

+ 4 - 1
app/src/main/cpp/runtime_file.h

@@ -29,7 +29,9 @@
 #include <time.h>
 #include <boolean.h>
 
+#ifdef HAVE_PLAYLIST
 #include "playlist.h"
+#endif
 #include "runtime_file_defines.h"
 
 RETRO_BEGIN_DECLS
@@ -147,7 +149,7 @@ void runtime_log_save(runtime_log_t *runtime_log);
 void runtime_log_convert_usec2hms(retro_time_t usec, unsigned *hours, unsigned *minutes, unsigned *seconds);
 
 /* Playlist manipulation */
-
+#ifdef HAVE_PLAYLIST
 /* Updates specified playlist entry runtime values with
  * contents of associated log file */
 void runtime_update_playlist(
@@ -157,6 +159,7 @@ void runtime_update_playlist(
       bool log_per_core,
       enum playlist_sublabel_last_played_style_type timedate_style,
       enum playlist_sublabel_last_played_date_separator_type date_separator);
+#endif
 
 #if defined(HAVE_MENU)
 /* Contentless cores manipulation */

+ 8 - 0
app/src/main/cpp/tasks/task_content.c

@@ -92,7 +92,9 @@
 #include "../file_path_special.h"
 #include "../frontend/frontend.h"
 #include "../msg_hash.h"
+#ifdef HAVE_PLAYLIST
 #include "../playlist.h"
+#endif
 #include "../paths.h"
 #include "../retroarch.h"
 #include "../runloop.h"
@@ -1489,7 +1491,9 @@ static bool content_load(content_ctx_info_t *info,
 #endif
 
    command_event(CMD_EVENT_HISTORY_INIT, NULL);
+#ifdef HAVE_PLAYLIST
    retroarch_favorites_init();
+#endif
    command_event(CMD_EVENT_RESUME, NULL);
    command_event(CMD_EVENT_VIDEO_SET_ASPECT_RATIO, NULL);
 
@@ -1540,6 +1544,7 @@ void menu_content_environment_get(int *argc, char *argv[],
          : path_get(RARCH_PATH_CORE);
 }
 
+#ifdef HAVE_PLAYLIST
 /**
  * task_push_to_history_list:
  *
@@ -1689,6 +1694,7 @@ static void task_push_to_history_list(
       }
    }
 }
+#endif
 
 #ifndef HAVE_DYNAMIC
 /**
@@ -2222,7 +2228,9 @@ bool task_push_start_current_core(content_ctx_info_t *content_info)
       goto end;
    }
 
+#ifdef HAVE_PLAYLIST
    task_push_to_history_list(p_content, true, false, false);
+#endif
 
 #ifdef HAVE_MENU
    /* Push Quick Menu onto menu stack */

+ 18 - 2
app/src/main/cpp/tasks/task_database.c

@@ -34,7 +34,9 @@
 
 #include "../file_path_special.h"
 #include "../msg_hash.h"
+#ifdef HAVE_PLAYLIST
 #include "../playlist.h"
+#endif
 #ifdef RARCH_INTERNAL
 #include "../configuration.h"
 #include "../ui/ui_companion_driver.h"
@@ -72,7 +74,9 @@ typedef struct db_handle
    char *fullpath;
    database_info_handle_t *handle;
    database_state_handle_t state;
+#ifdef HAVE_PLAYLIST
    playlist_config_t playlist_config; /* size_t alignment */
+#endif
    unsigned status;
    uint8_t flags;
 } db_handle_t;
@@ -703,7 +707,9 @@ static int database_info_list_iterate_found_match(
    char* entry_path_str           = (char*)malloc(str_len);
    char* entry_label              = (char*)malloc(str_len);
    char *hash                     = NULL;
+#ifdef HAVE_PLAYLIST
    playlist_t   *playlist         = NULL;
+#endif
    const char         *db_path    =
       database_info_get_current_name(db_state);
    const char         *entry_path =
@@ -725,8 +731,10 @@ static int database_info_list_iterate_found_match(
       fill_pathname_join_special(db_playlist_path, _db->playlist_directory,
             db_playlist_base_str, str_len);
 
+#ifdef HAVE_PLAYLIST
    playlist_config_set_path(&_db->playlist_config, db_playlist_path);
    playlist = playlist_init(&_db->playlist_config);
+#endif
 
    if (!string_is_empty(db_state->serial))
    {
@@ -777,6 +785,7 @@ static int database_info_list_iterate_found_match(
    fprintf(stderr, "entry path str: %s\n", entry_path_str);
 #endif
 
+#ifdef HAVE_PLAYLIST
    if (!playlist_entry_exists(playlist, entry_path_str))
    {
       struct playlist_entry entry;
@@ -811,6 +820,7 @@ static int database_info_list_iterate_found_match(
 
    playlist_write_file(playlist);
    playlist_free(playlist);
+#endif
 
    database_info_list_free(db_state->info);
    free(db_state->info);
@@ -955,8 +965,9 @@ static int task_database_iterate_playlist_lutro(
       const char *path)
 {
    char db_playlist_path[PATH_MAX_LENGTH];
+#ifdef HAVE_PLAYLIST
    playlist_t   *playlist  = NULL;
-
+#endif
    db_playlist_path[0]     = '\0';
 
    if (!string_is_empty(_db->playlist_directory))
@@ -964,6 +975,7 @@ static int task_database_iterate_playlist_lutro(
             _db->playlist_directory,
             "Lutro.lpl", sizeof(db_playlist_path));
 
+#ifdef HAVE_PLAYLIST
    playlist_config_set_path(&_db->playlist_config, db_playlist_path);
    playlist = playlist_init(&_db->playlist_config);
 
@@ -1002,7 +1014,7 @@ static int task_database_iterate_playlist_lutro(
 
    playlist_write_file(playlist);
    playlist_free(playlist);
-
+#endif
    return 0;
 }
 
@@ -1348,17 +1360,21 @@ bool task_push_dbscan(
    t->progress_cb                          = task_database_progress_cb;
    if (settings->bools.scan_without_core_match)
       db->flags |= DB_HANDLE_FLAG_SCAN_WITHOUT_CORE_MATCH;
+#ifdef HAVE_PLAYLIST
    db->playlist_config.capacity            = COLLECTION_SIZE;
    db->playlist_config.old_format          = settings->bools.playlist_use_old_format;
    db->playlist_config.compress            = settings->bools.playlist_compression;
    db->playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
    playlist_config_set_base_content_directory(&db->playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
+#endif
 #else
+#ifdef HAVE_PLAYLIST
    db->playlist_config.capacity            = COLLECTION_SIZE;
    db->playlist_config.old_format          = false;
    db->playlist_config.compress            = false;
    db->playlist_config.fuzzy_archive_match = false;
    playlist_config_set_base_content_directory(&db->playlist_config, NULL);
+#endif
 #endif
    if (db_dir_show_hidden_files)
       db->flags |= DB_HANDLE_FLAG_SHOW_HIDDEN_FILES;

+ 0 - 614
app/src/main/cpp/tasks/task_manual_content_scan.c

@@ -1,614 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2011-2017 - Daniel De Matteis
- *  Copyright (C) 2014-2017 - Jean-André Santoni
- *  Copyright (C) 2016-2019 - Brad Parker
- *  Copyright (C)      2019 - James Leaver
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <boolean.h>
-
-#include <string/stdstring.h>
-#include <lists/string_list.h>
-#include <file/file_path.h>
-#include <formats/logiqx_dat.h>
-#include <formats/m3u_file.h>
-
-#include "tasks_internal.h"
-
-#include "../msg_hash.h"
-#include "../playlist.h"
-#include "../manual_content_scan.h"
-
-#ifdef RARCH_INTERNAL
-#ifdef HAVE_MENU
-#include "../menu/menu_driver.h"
-#endif
-#endif
-
-enum manual_scan_status
-{
-   MANUAL_SCAN_BEGIN = 0,
-   MANUAL_SCAN_ITERATE_CLEAN,
-   MANUAL_SCAN_ITERATE_CONTENT,
-   MANUAL_SCAN_ITERATE_M3U,
-   MANUAL_SCAN_END
-};
-
-typedef struct manual_scan_handle
-{
-   manual_content_scan_task_config_t *task_config;
-   playlist_t *playlist;
-   struct string_list *file_exts_list;
-   struct string_list *content_list;
-   logiqx_dat_t *dat_file;
-   struct string_list *m3u_list;
-   playlist_config_t playlist_config; /* size_t alignment */
-   size_t playlist_size;
-   size_t playlist_index;
-   size_t content_list_size;
-   size_t content_list_index;
-   size_t m3u_index;
-   enum manual_scan_status status;
-} manual_scan_handle_t;
-
-/* Frees task handle + all constituent objects */
-static void free_manual_content_scan_handle(manual_scan_handle_t *manual_scan)
-{
-   if (!manual_scan)
-      return;
-
-   if (manual_scan->task_config)
-   {
-      free(manual_scan->task_config);
-      manual_scan->task_config = NULL;
-   }
-
-   if (manual_scan->playlist)
-   {
-      playlist_free(manual_scan->playlist);
-      manual_scan->playlist = NULL;
-   }
-
-   if (manual_scan->file_exts_list)
-   {
-      string_list_free(manual_scan->file_exts_list);
-      manual_scan->file_exts_list = NULL;
-   }
-
-   if (manual_scan->content_list)
-   {
-      string_list_free(manual_scan->content_list);
-      manual_scan->content_list = NULL;
-   }
-
-   if (manual_scan->m3u_list)
-   {
-      string_list_free(manual_scan->m3u_list);
-      manual_scan->m3u_list = NULL;
-   }
-
-   if (manual_scan->dat_file)
-   {
-      logiqx_dat_free(manual_scan->dat_file);
-      manual_scan->dat_file = NULL;
-   }
-
-   free(manual_scan);
-   manual_scan = NULL;
-}
-
-static void cb_task_manual_content_scan(
-      retro_task_t *task, void *task_data,
-      void *user_data, const char *err)
-{
-   manual_scan_handle_t *manual_scan = NULL;
-   playlist_t *cached_playlist       = playlist_get_cached();
-#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
-   struct menu_state *menu_st        = menu_state_get_ptr();
-   if (!task)
-      goto end;
-#else
-   if (!task)
-      return;
-#endif
-
-   if (!(manual_scan = (manual_scan_handle_t*)task->state))
-   {
-#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
-      goto end;
-#else
-      return;
-#endif
-   }
-
-   /* If the manual content scan task has modified the
-    * currently cached playlist, then it must be re-cached
-    * (otherwise changes will be lost if the currently
-    * cached playlist is saved to disk for any reason...) */
-   if (cached_playlist)
-   {
-      if (string_is_equal(
-            manual_scan->playlist_config.path,
-            playlist_get_conf_path(cached_playlist)))
-      {
-         playlist_config_t playlist_config;
-
-         /* Copy configuration of cached playlist
-          * (could use manual_scan->playlist_config,
-          * but doing it this way guarantees that
-          * the cached playlist is preserved in
-          * its original state) */
-         if (playlist_config_copy(
-               playlist_get_config(cached_playlist),
-               &playlist_config))
-         {
-            playlist_free_cached();
-            playlist_init_cached(&playlist_config);
-         }
-      }
-   }
-
-#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
-end:
-   /* When creating playlists, the playlist tabs of
-    * any active menu driver must be refreshed */
-   if (menu_st->driver_ctx->environ_cb)
-      menu_st->driver_ctx->environ_cb(MENU_ENVIRON_RESET_HORIZONTAL_LIST,
-            NULL, menu_st->userdata);
-#endif
-}
-
-static void task_manual_content_scan_free(retro_task_t *task)
-{
-   manual_scan_handle_t *manual_scan = NULL;
-
-   if (!task)
-      return;
-
-   manual_scan = (manual_scan_handle_t*)task->state;
-
-   free_manual_content_scan_handle(manual_scan);
-}
-
-static void task_manual_content_scan_handler(retro_task_t *task)
-{
-   manual_scan_handle_t *manual_scan = NULL;
-
-   if (!task)
-      goto task_finished;
-
-   if (!(manual_scan = (manual_scan_handle_t*)task->state))
-      goto task_finished;
-
-   if (task_get_cancelled(task))
-      goto task_finished;
-
-   switch (manual_scan->status)
-   {
-      case MANUAL_SCAN_BEGIN:
-         {
-            /* Get allowed file extensions list */
-            if (!string_is_empty(manual_scan->task_config->file_exts))
-               manual_scan->file_exts_list = string_split(
-                     manual_scan->task_config->file_exts, "|");
-
-            /* Get content list */
-            if (!(manual_scan->content_list 
-                     = manual_content_scan_get_content_list(
-                        manual_scan->task_config)))
-            {
-               runloop_msg_queue_push(
-                     msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_INVALID_CONTENT),
-                     1, 100, true,
-                     NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
-               goto task_finished;
-            }
-
-            manual_scan->content_list_size = manual_scan->content_list->size;
-
-            /* Load DAT file, if required */
-            if (!string_is_empty(manual_scan->task_config->dat_file_path))
-            {
-               if (!(manual_scan->dat_file =
-                     logiqx_dat_init(
-                        manual_scan->task_config->dat_file_path)))
-               {
-                  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 */
-            if (!(manual_scan->playlist =
-                     playlist_init(&manual_scan->playlist_config)))
-               goto task_finished;
-
-            /* Reset playlist, if required */
-            if (manual_scan->task_config->overwrite_playlist)
-               playlist_clear(manual_scan->playlist);
-
-            /* Get initial playlist size */
-            manual_scan->playlist_size = 
-               playlist_size(manual_scan->playlist);
-
-            /* Set default core, if required */
-            if (manual_scan->task_config->core_set)
-            {
-               playlist_set_default_core_path(manual_scan->playlist,
-                     manual_scan->task_config->core_path);
-               playlist_set_default_core_name(manual_scan->playlist,
-                     manual_scan->task_config->core_name);
-            }
-
-            /* Record remaining scan parameters to enable
-             * subsequent 'refresh playlist' operations */
-            playlist_set_scan_content_dir(manual_scan->playlist,
-                  manual_scan->task_config->content_dir);
-            playlist_set_scan_file_exts(manual_scan->playlist,
-                  manual_scan->task_config->file_exts_custom_set ?
-                        manual_scan->task_config->file_exts : NULL);
-            playlist_set_scan_dat_file_path(manual_scan->playlist,
-                  manual_scan->task_config->dat_file_path);
-            playlist_set_scan_search_recursively(manual_scan->playlist,
-                  manual_scan->task_config->search_recursively);
-            playlist_set_scan_search_archives(manual_scan->playlist,
-                  manual_scan->task_config->search_archives);
-            playlist_set_scan_filter_dat_content(manual_scan->playlist,
-                  manual_scan->task_config->filter_dat_content);
-
-            /* All good - can start iterating
-             * > If playlist has content and 'validate
-             *   entries' is enabled, go to clean-up phase
-             * > Otherwise go straight to content scan phase */
-            if (manual_scan->task_config->validate_entries &&
-                (manual_scan->playlist_size > 0))
-               manual_scan->status = MANUAL_SCAN_ITERATE_CLEAN;
-            else
-               manual_scan->status = MANUAL_SCAN_ITERATE_CONTENT;
-         }
-         break;
-      case MANUAL_SCAN_ITERATE_CLEAN:
-         {
-            const struct playlist_entry *entry = NULL;
-            bool delete_entry                  = false;
-
-            /* Get current entry */
-            playlist_get_index(manual_scan->playlist,
-                  manual_scan->playlist_index, &entry);
-
-            if (entry)
-            {
-               const char *entry_file     = NULL;
-               const char *entry_file_ext = NULL;
-               char task_title[PATH_MAX_LENGTH];
-
-               /* Update progress display */
-               task_free_title(task);
-
-               strlcpy(task_title,
-                     msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_PLAYLIST_CLEANUP),
-                     sizeof(task_title));
-
-               if (!string_is_empty(entry->path) &&
-                   (entry_file = path_basename(entry->path)))
-                  strlcat(task_title, entry_file, sizeof(task_title));
-
-               task_set_title(task, strdup(task_title));
-               task_set_progress(task, (manual_scan->playlist_index * 100) /
-                     manual_scan->playlist_size);
-
-               /* Check whether playlist content exists on
-                * the filesystem */
-               if (!playlist_content_path_is_valid(entry->path))
-                  delete_entry = true;
-               /* If file exists, check whether it has a
-                * permitted file extension */
-               else if (manual_scan->file_exts_list &&
-                        (entry_file_ext = path_get_extension(entry->path)) &&
-                        !string_list_find_elem_prefix(
-                              manual_scan->file_exts_list,
-                              ".", entry_file_ext))
-                  delete_entry = true;
-
-               if (delete_entry)
-               {
-                  /* Invalid content - delete entry */
-                  playlist_delete_index(manual_scan->playlist,
-                        manual_scan->playlist_index);
-
-                  /* Update playlist_size */
-                  manual_scan->playlist_size = playlist_size(manual_scan->playlist);
-               }
-            }
-
-            /* Increment entry index *if* current entry still
-             * exists (i.e. if entry was deleted, current index
-             * will already point to the *next* entry) */
-            if (!delete_entry)
-               manual_scan->playlist_index++;
-
-            if (manual_scan->playlist_index >=
-                  manual_scan->playlist_size)
-               manual_scan->status = MANUAL_SCAN_ITERATE_CONTENT;
-         }
-         break;
-      case MANUAL_SCAN_ITERATE_CONTENT:
-         {
-            const char *content_path = manual_scan->content_list->elems[
-                  manual_scan->content_list_index].data;
-            int content_type         = manual_scan->content_list->elems[
-                  manual_scan->content_list_index].attr.i;
-
-            if (!string_is_empty(content_path))
-            {
-               char task_title[PATH_MAX_LENGTH];
-               const char *content_file = path_basename(content_path);
-
-               /* Update progress display */
-               task_free_title(task);
-
-               strlcpy(task_title,
-                     msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_IN_PROGRESS),
-                     sizeof(task_title));
-
-               if (!string_is_empty(content_file))
-                  strlcat(task_title, content_file, sizeof(task_title));
-
-               task_set_title(task, strdup(task_title));
-               task_set_progress(task,
-                     (manual_scan->content_list_index * 100) /
-                     manual_scan->content_list_size);
-
-               /* Add content to playlist */
-               manual_content_scan_add_content_to_playlist(
-                     manual_scan->task_config, manual_scan->playlist,
-                     content_path, content_type, manual_scan->dat_file);
-
-               /* If this is an M3U file, add it to the
-                * M3U list for later processing */
-               if (m3u_file_is_m3u(content_path))
-               {
-                  union string_list_elem_attr attr;
-                  attr.i = 0;
-                  /* Note: If string_list_append() fails, there is
-                   * really nothing we can do. The M3U file will
-                   * just be ignored... */
-                  string_list_append(
-                        manual_scan->m3u_list, content_path, attr);
-               }
-            }
-
-            /* Increment content index */
-            manual_scan->content_list_index++;
-            if (manual_scan->content_list_index >=
-                  manual_scan->content_list_size)
-            {
-               /* Check whether we have any M3U files
-                * to process */
-               if (manual_scan->m3u_list->size > 0)
-                  manual_scan->status = MANUAL_SCAN_ITERATE_M3U;
-               else
-                  manual_scan->status = MANUAL_SCAN_END;
-            }
-         }
-         break;
-      case MANUAL_SCAN_ITERATE_M3U:
-         {
-            const char *m3u_path = manual_scan->m3u_list->elems[
-                  manual_scan->m3u_index].data;
-
-            if (!string_is_empty(m3u_path))
-            {
-               char task_title[PATH_MAX_LENGTH];
-               const char *m3u_name = path_basename_nocompression(m3u_path);
-               m3u_file_t *m3u_file = NULL;
-
-               /* Update progress display */
-               task_free_title(task);
-
-               strlcpy(task_title,
-                     msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_M3U_CLEANUP),
-                     sizeof(task_title));
-
-               if (!string_is_empty(m3u_name))
-                  strlcat(task_title, m3u_name, sizeof(task_title));
-
-               task_set_title(task, strdup(task_title));
-               task_set_progress(task, (manual_scan->m3u_index * 100) /
-                     manual_scan->m3u_list->size);
-
-               /* Load M3U file */
-               if ((m3u_file = m3u_file_init(m3u_path)))
-               {
-                  size_t i;
-
-                  /* Loop over M3U entries */
-                  for (i = 0; i < m3u_file_get_size(m3u_file); i++)
-                  {
-                     m3u_file_entry_t *m3u_entry = NULL;
-
-                     /* Delete any playlist items matching the
-                      * content path of the M3U entry */
-                     if (m3u_file_get_entry(m3u_file, i, &m3u_entry))
-                        playlist_delete_by_path(
-                              manual_scan->playlist, m3u_entry->full_path);
-                  }
-
-                  m3u_file_free(m3u_file);
-               }
-            }
-
-            /* Increment M3U file index */
-            manual_scan->m3u_index++;
-            if (manual_scan->m3u_index >= manual_scan->m3u_list->size)
-               manual_scan->status = MANUAL_SCAN_END;
-         }
-         break;
-      case MANUAL_SCAN_END:
-         {
-            char task_title[PATH_MAX_LENGTH];
-
-            /* Ensure playlist is alphabetically sorted
-             * > Override user settings here */
-            playlist_set_sort_mode(manual_scan->playlist, PLAYLIST_SORT_MODE_DEFAULT);
-            playlist_qsort(manual_scan->playlist);
-
-            /* Save playlist changes to disk */
-            playlist_write_file(manual_scan->playlist);
-
-            /* Update progress display */
-            task_free_title(task);
-
-            strlcpy(
-                  task_title, msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_END),
-                  sizeof(task_title));
-            strlcat(task_title, manual_scan->task_config->system_name,
-                  sizeof(task_title));
-
-            task_set_title(task, strdup(task_title));
-         }
-         /* fall-through */
-      default:
-         task_set_progress(task, 100);
-         goto task_finished;
-   }
-   
-   return;
-   
-task_finished:
-
-   if (task)
-      task_set_finished(task, true);
-}
-
-static bool task_manual_content_scan_finder(retro_task_t *task, void *user_data)
-{
-   manual_scan_handle_t *manual_scan = NULL;
-
-   if (!task || !user_data)
-      return false;
-   if (task->handler != task_manual_content_scan_handler)
-      return false;
-   if (!(manual_scan = (manual_scan_handle_t*)task->state))
-      return false;
-   return string_is_equal(
-         (const char*)user_data, manual_scan->playlist_config.path);
-}
-
-bool task_push_manual_content_scan(
-      const playlist_config_t *playlist_config,
-      const char *playlist_directory)
-{
-   task_finder_data_t find_data;
-   char task_title[PATH_MAX_LENGTH];
-   retro_task_t *task                = NULL;
-   manual_scan_handle_t *manual_scan = NULL;
-
-   /* Sanity check */
-   if (  !playlist_config
-       || string_is_empty(playlist_directory))
-      return false;
-
-   if (!(manual_scan = (manual_scan_handle_t*)
-         calloc(1, sizeof(manual_scan_handle_t))))
-      return false;
-
-   /* Configure handle */
-   manual_scan->task_config         = NULL;
-   manual_scan->playlist            = NULL;
-   manual_scan->file_exts_list      = NULL;
-   manual_scan->content_list        = NULL;
-   manual_scan->dat_file            = NULL;
-   manual_scan->playlist_size       = 0;
-   manual_scan->playlist_index      = 0;
-   manual_scan->content_list_size   = 0;
-   manual_scan->content_list_index  = 0;
-   manual_scan->status              = MANUAL_SCAN_BEGIN;
-   manual_scan->m3u_index           = 0;
-   manual_scan->m3u_list            = string_list_new();
-
-   if (!manual_scan->m3u_list)
-      goto error;
-
-   /* > Get current manual content scan configuration */
-   if (!(manual_scan->task_config = (manual_content_scan_task_config_t*)
-         calloc(1, sizeof(manual_content_scan_task_config_t))))
-      goto error;
-
-   if ( !manual_content_scan_get_task_config(
-         manual_scan->task_config, playlist_directory))
-   {
-      runloop_msg_queue_push(
-            msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_INVALID_CONFIG),
-            1, 100, true,
-            NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
-      goto error;
-   }
-
-   /* > Cache playlist configuration */
-   if (!playlist_config_copy(playlist_config,
-         &manual_scan->playlist_config))
-      goto error;
-
-   playlist_config_set_path(
-         &manual_scan->playlist_config,
-         manual_scan->task_config->playlist_file);
-
-   /* Concurrent scanning of content to the same
-    * playlist is not allowed */
-   find_data.func     = task_manual_content_scan_finder;
-   find_data.userdata = (void*)manual_scan->playlist_config.path;
-
-   if (task_queue_find(&find_data))
-      goto error;
-
-   /* Create task */
-   if (!(task = task_init()))
-      goto error;
-
-   /* > Get task title */
-   strlcpy(
-         task_title, msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_START),
-         sizeof(task_title));
-   strlcat(task_title, manual_scan->task_config->system_name,
-         sizeof(task_title));
-
-   /* > Configure task */
-   task->handler                 = task_manual_content_scan_handler;
-   task->state                   = manual_scan;
-   task->title                   = strdup(task_title);
-   task->alternative_look        = true;
-   task->progress                = 0;
-   task->callback                = cb_task_manual_content_scan;
-   task->cleanup                 = task_manual_content_scan_free;
-
-   /* > Push task */
-   task_queue_push(task);
-
-   return true;
-
-error:
-   /* Clean up handle */
-   free_manual_content_scan_handle(manual_scan);
-   manual_scan = NULL;
-
-   return false;
-}

+ 0 - 776
app/src/main/cpp/tasks/task_playlist_manager.c

@@ -1,776 +0,0 @@
-/*  RetroArch - A frontend for libretro.
- *  Copyright (C) 2011-2017 - Daniel De Matteis
- *  Copyright (C) 2014-2017 - Jean-André Santoni
- *  Copyright (C) 2016-2019 - Brad Parker
- *
- *  RetroArch is free software: you can redistribute it and/or modify it under the terms
- *  of the GNU General Public License as published by the Free Software Found-
- *  ation, either version 3 of the License, or (at your option) any later version.
- *
- *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- *  PURPOSE.  See the GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along with RetroArch.
- *  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
-#include <string/stdstring.h>
-#include <lists/string_list.h>
-#include <file/file_path.h>
-#include <formats/m3u_file.h>
-
-#include "tasks_internal.h"
-
-#include "../msg_hash.h"
-#include "../file_path_special.h"
-#include "../playlist.h"
-#include "../core_info.h"
-
-enum pl_manager_status
-{
-   PL_MANAGER_BEGIN = 0,
-   PL_MANAGER_ITERATE_ENTRY_RESET_CORE,
-   PL_MANAGER_ITERATE_ENTRY_VALIDATE,
-   PL_MANAGER_VALIDATE_END,
-   PL_MANAGER_ITERATE_ENTRY_CHECK_DUPLICATE,
-   PL_MANAGER_CHECK_DUPLICATE_END,
-   PL_MANAGER_ITERATE_FETCH_M3U,
-   PL_MANAGER_ITERATE_CLEAN_M3U,
-   PL_MANAGER_END
-};
-
-typedef struct pl_manager_handle
-{
-   struct string_list *m3u_list;
-   char *playlist_name;
-   playlist_t *playlist;
-   size_t list_size;
-   size_t list_index;
-   size_t m3u_index;
-   playlist_config_t playlist_config; /* size_t alignment */
-   enum pl_manager_status status;
-} pl_manager_handle_t;
-
-/*********************/
-/* Utility Functions */
-/*********************/
-
-static void free_pl_manager_handle(pl_manager_handle_t *pl_manager)
-{
-   if (!pl_manager)
-      return;
-   
-   if (pl_manager->m3u_list)
-   {
-      string_list_free(pl_manager->m3u_list);
-      pl_manager->m3u_list = NULL;
-   }
-   
-   if (!string_is_empty(pl_manager->playlist_name))
-   {
-      free(pl_manager->playlist_name);
-      pl_manager->playlist_name = NULL;
-   }
-   
-   if (pl_manager->playlist)
-   {
-      playlist_free(pl_manager->playlist);
-      pl_manager->playlist = NULL;
-   }
-   
-   free(pl_manager);
-   pl_manager = NULL;
-}
-
-static void cb_task_pl_manager(
-      retro_task_t *task, void *task_data,
-      void *user_data, const char *err)
-{
-   pl_manager_handle_t *pl_manager = NULL;
-   playlist_t *cached_playlist     = playlist_get_cached();
-
-   /* If no playlist is currently cached, no action
-    * is required */
-   if (!task || !cached_playlist)
-      return;
-
-   pl_manager = (pl_manager_handle_t*)task->state;
-
-   if (!pl_manager)
-      return;
-
-   /* If the playlist manager task has modified the
-    * currently cached playlist, then it must be re-cached
-    * (otherwise changes will be lost if the currently
-    * cached playlist is saved to disk for any reason...) */
-   if (string_is_equal(
-         pl_manager->playlist_config.path,
-         playlist_get_conf_path(cached_playlist)))
-   {
-      playlist_config_t playlist_config;
-
-      /* Copy configuration of cached playlist
-       * (could use pl_manager->playlist_config,
-       * but doing it this way guarantees that
-       * the cached playlist is preserved in
-       * its original state) */
-      if (playlist_config_copy(
-            playlist_get_config(cached_playlist),
-            &playlist_config))
-      {
-         playlist_free_cached();
-         playlist_init_cached(&playlist_config);
-      }
-   }
-}
-
-static void task_pl_manager_free(retro_task_t *task)
-{
-   pl_manager_handle_t *pl_manager = NULL;
-
-   if (!task)
-      return;
-
-   pl_manager = (pl_manager_handle_t*)task->state;
-
-   free_pl_manager_handle(pl_manager);
-}
-
-/**************************/
-/* Reset Associated Cores */
-/**************************/
-
-static void task_pl_manager_reset_cores_handler(retro_task_t *task)
-{
-   pl_manager_handle_t *pl_manager = NULL;
-   
-   if (!task)
-      goto task_finished;
-   
-   pl_manager = (pl_manager_handle_t*)task->state;
-   
-   if (!pl_manager)
-      goto task_finished;
-   
-   if (task_get_cancelled(task))
-      goto task_finished;
-   
-   switch (pl_manager->status)
-   {
-      case PL_MANAGER_BEGIN:
-         /* Load playlist */
-         if (!path_is_valid(pl_manager->playlist_config.path))
-            goto task_finished;
-
-         pl_manager->playlist = playlist_init(&pl_manager->playlist_config);
-
-         if (!pl_manager->playlist)
-            goto task_finished;
-
-         pl_manager->list_size = playlist_size(pl_manager->playlist);
-
-         if (pl_manager->list_size < 1)
-            goto task_finished;
-
-         /* All good - can start iterating */
-         pl_manager->status = PL_MANAGER_ITERATE_ENTRY_RESET_CORE;
-         break;
-      case PL_MANAGER_ITERATE_ENTRY_RESET_CORE:
-         {
-            const struct playlist_entry *entry = NULL;
-            
-            /* Get current entry */
-            playlist_get_index(
-                  pl_manager->playlist, pl_manager->list_index, &entry);
-            
-            if (entry)
-            {
-               struct playlist_entry update_entry = {0};
-               char task_title[PATH_MAX_LENGTH];
-               /* Update progress display */
-               task_free_title(task);
-               strlcpy(
-                     task_title,
-		     msg_hash_to_str(MSG_PLAYLIST_MANAGER_RESETTING_CORES),
-                     sizeof(task_title));
-               
-               if (!string_is_empty(entry->label))
-                  strlcat(task_title, entry->label, sizeof(task_title));
-               else if (!string_is_empty(entry->path))
-               {
-                  char entry_name[PATH_MAX_LENGTH];
-                  fill_pathname_base(entry_name, entry->path, sizeof(entry_name));
-                  path_remove_extension(entry_name);
-                  strlcat(task_title, entry_name, sizeof(task_title));
-               }
-               
-               task_set_title(task, strdup(task_title));
-               task_set_progress(task, (pl_manager->list_index * 100) / pl_manager->list_size);
-               
-               /* Reset core association
-                * > The update function reads our entry as const,
-                *   so these casts are safe */
-               update_entry.core_path = (char*)"DETECT";
-               update_entry.core_name = (char*)"DETECT";
-               
-               playlist_update(
-                     pl_manager->playlist, pl_manager->list_index, &update_entry);
-            }
-            
-            /* Increment entry index */
-            pl_manager->list_index++;
-            if (pl_manager->list_index >= pl_manager->list_size)
-               pl_manager->status = PL_MANAGER_END;
-         }
-         break;
-      case PL_MANAGER_END:
-         {
-            char task_title[PATH_MAX_LENGTH];
-            /* Save playlist changes to disk */
-            playlist_write_file(pl_manager->playlist);
-            /* Update progress display */
-            task_free_title(task);
-            strlcpy(
-                  task_title, msg_hash_to_str(MSG_PLAYLIST_MANAGER_CORES_RESET),
-                  sizeof(task_title));
-            strlcat(task_title, pl_manager->playlist_name, sizeof(task_title));
-            
-            task_set_title(task, strdup(task_title));
-         }
-         /* fall-through */
-      default:
-         task_set_progress(task, 100);
-         goto task_finished;
-   }
-   
-   return;
-   
-task_finished:
-   
-   if (task)
-      task_set_finished(task, true);
-}
-
-static bool task_pl_manager_reset_cores_finder(
-      retro_task_t *task, void *user_data)
-{
-   pl_manager_handle_t *pl_manager = NULL;
-   
-   if (!task || !user_data)
-      return false;
-   
-   if (task->handler != task_pl_manager_reset_cores_handler)
-      return false;
-   
-   if (!(pl_manager = (pl_manager_handle_t*)task->state))
-      return false;
-   
-   return string_is_equal((const char*)user_data,
-         pl_manager->playlist_config.path);
-}
-
-bool task_push_pl_manager_reset_cores(const playlist_config_t *playlist_config)
-{
-   task_finder_data_t find_data;
-   char playlist_name[PATH_MAX_LENGTH];
-   char task_title[PATH_MAX_LENGTH];
-   retro_task_t *task              = task_init();
-   pl_manager_handle_t *pl_manager = (pl_manager_handle_t*)
-      calloc(1, sizeof(pl_manager_handle_t));
-   /* Sanity check */
-   if (!playlist_config || !task || !pl_manager)
-      goto error;
-   if (string_is_empty(playlist_config->path))
-      goto error;
-   
-   fill_pathname_base(playlist_name,
-         playlist_config->path, sizeof(playlist_name));
-   path_remove_extension(playlist_name);
-   
-   if (string_is_empty(playlist_name))
-      goto error;
-   
-   /* Concurrent management of the same playlist
-    * is not allowed */
-   find_data.func                = task_pl_manager_reset_cores_finder;
-   find_data.userdata            = (void*)playlist_config->path;
-   
-   if (task_queue_find(&find_data))
-      goto error;
-   
-   /* Configure handle */
-   if (!playlist_config_copy(playlist_config, &pl_manager->playlist_config))
-      goto error;
-   
-   pl_manager->playlist_name       = strdup(playlist_name);
-   pl_manager->playlist            = NULL;
-   pl_manager->list_size           = 0;
-   pl_manager->list_index          = 0;
-   pl_manager->m3u_list            = NULL;
-   pl_manager->m3u_index           = 0;
-   pl_manager->status              = PL_MANAGER_BEGIN;
-   
-   /* Configure task */
-   strlcpy(
-         task_title,
-	 msg_hash_to_str(MSG_PLAYLIST_MANAGER_RESETTING_CORES),
-         sizeof(task_title));
-   strlcat(task_title, playlist_name, sizeof(task_title));
-   
-   task->handler                 = task_pl_manager_reset_cores_handler;
-   task->state                   = pl_manager;
-   task->title                   = strdup(task_title);
-   task->alternative_look        = true;
-   task->progress                = 0;
-   task->callback                = cb_task_pl_manager;
-   task->cleanup                 = task_pl_manager_free;
-   
-   task_queue_push(task);
-   
-   return true;
-   
-error:
-   
-   if (task)
-   {
-      free(task);
-      task = NULL;
-   }
-   
-   free_pl_manager_handle(pl_manager);
-   pl_manager = NULL;
-   
-   return false;
-}
-
-/******************/
-/* Clean Playlist */
-/******************/
-
-static void pl_manager_validate_core_association(
-      playlist_t *playlist, size_t entry_index,
-      const char *core_path, const char *core_name)
-{
-   struct playlist_entry update_entry = {0};
-   
-   /* Sanity check */
-   if (!playlist)
-      return;
-   
-   if (entry_index >= playlist_size(playlist))
-      return;
-   
-   if (string_is_empty(core_path))
-      goto reset_core;
-   
-   /* Handle 'DETECT' entries */
-   if (string_is_equal(core_path, "DETECT"))
-   {
-      if (!string_is_equal(core_name, "DETECT"))
-         goto reset_core;
-   }
-   /* Handle 'builtin' entries */
-   else if (string_is_equal(core_path, "builtin"))
-   {
-      if (string_is_empty(core_name))
-         goto reset_core;
-   }
-   /* Handle file path entries */
-   else if (!path_is_valid(core_path))
-      goto reset_core;
-   else
-   {
-      char core_display_name[PATH_MAX_LENGTH];
-      core_info_t *core_info = NULL;
-      
-      /* Search core info */
-      if (core_info_find(core_path, &core_info) &&
-          !string_is_empty(core_info->display_name))
-         strlcpy(core_display_name, core_info->display_name,
-               sizeof(core_display_name));
-      else
-         core_display_name[0] = '\0';
-      
-      /* If core_display_name string is empty, it means the
-       * core wasn't found -> reset association */
-      if (string_is_empty(core_display_name))
-         goto reset_core;
-      
-      /* ...Otherwise, check that playlist entry
-       * core name is correct */
-      if (!string_is_equal(core_name, core_display_name))
-      {
-         update_entry.core_name = core_display_name;
-         playlist_update(playlist, entry_index, &update_entry);
-      }
-   }
-   
-   return;
-   
-reset_core:
-   /* The update function reads our entry as const,
-    * so these casts are safe */
-   update_entry.core_path = (char*)"DETECT";
-   update_entry.core_name = (char*)"DETECT";
-   
-   playlist_update(playlist, entry_index, &update_entry);
-}
-
-static void task_pl_manager_clean_playlist_handler(retro_task_t *task)
-{
-   pl_manager_handle_t *pl_manager = NULL;
-   
-   if (!task)
-      goto task_finished;
-   
-   pl_manager = (pl_manager_handle_t*)task->state;
-   
-   if (!pl_manager)
-      goto task_finished;
-   
-   if (task_get_cancelled(task))
-      goto task_finished;
-   
-   switch (pl_manager->status)
-   {
-      case PL_MANAGER_BEGIN:
-         {
-            /* Load playlist */
-            if (!path_is_valid(pl_manager->playlist_config.path))
-               goto task_finished;
-            
-            pl_manager->playlist = playlist_init(&pl_manager->playlist_config);
-            
-            if (!pl_manager->playlist)
-               goto task_finished;
-            
-            pl_manager->list_size = playlist_size(pl_manager->playlist);
-            
-            if (pl_manager->list_size < 1)
-               goto task_finished;
-            
-            /* All good - can start iterating */
-            pl_manager->status = PL_MANAGER_ITERATE_ENTRY_VALIDATE;
-         }
-         break;
-      case PL_MANAGER_ITERATE_ENTRY_VALIDATE:
-         {
-            const struct playlist_entry *entry = NULL;
-            bool entry_deleted                 = false;
-            
-            /* Update progress display */
-            task_set_progress(task, (pl_manager->list_index * 100) / pl_manager->list_size);
-            
-            /* Get current entry */
-            playlist_get_index(
-                  pl_manager->playlist, pl_manager->list_index, &entry);
-            
-            if (entry)
-            {
-               /* Check whether playlist content exists on
-                * the filesystem */
-               if (!playlist_content_path_is_valid(entry->path))
-               {
-                  /* Invalid content - delete entry */
-                  playlist_delete_index(pl_manager->playlist, pl_manager->list_index);
-                  entry_deleted = true;
-                  
-                  /* Update list_size */
-                  pl_manager->list_size = playlist_size(pl_manager->playlist);
-               }
-               /* Content is valid - check if core is valid */
-               else
-                  pl_manager_validate_core_association(
-                        pl_manager->playlist, pl_manager->list_index,
-                        entry->core_path, entry->core_name);
-            }
-            
-            /* Increment entry index *if* current entry still
-             * exists (i.e. if entry was deleted, current index
-             * will already point to the *next* entry) */
-            if (!entry_deleted)
-               pl_manager->list_index++;
-            
-            if (pl_manager->list_index >= pl_manager->list_size)
-               pl_manager->status = PL_MANAGER_VALIDATE_END;
-         }
-         break;
-      case PL_MANAGER_VALIDATE_END:
-         /* Sanity check - if all (or all but one)
-          * playlist entries were removed during the
-          * 'validate' phase, we can stop now */
-         if (pl_manager->list_size < 2)
-         {
-            pl_manager->status = PL_MANAGER_END;
-            break;
-         }
-
-         /* ...otherwise, reset index counter and
-          * start the duplicates check */
-         pl_manager->list_index = 0;
-         pl_manager->status = PL_MANAGER_ITERATE_ENTRY_CHECK_DUPLICATE;
-         break;
-      case PL_MANAGER_ITERATE_ENTRY_CHECK_DUPLICATE:
-         {
-            bool entry_deleted = false;
-            size_t i;
-            
-            /* Update progress display */
-            task_set_progress(task, (pl_manager->list_index * 100) / pl_manager->list_size);
-            
-            /* Check whether the content + core paths of the
-             * current entry match those of any subsequent
-             * entry */
-            for (i = pl_manager->list_index + 1; i < pl_manager->list_size; i++)
-            {
-               if (playlist_index_entries_are_equal(pl_manager->playlist,
-                     pl_manager->list_index, i))
-               {
-                  /* Duplicate found - delete entry */
-                  playlist_delete_index(pl_manager->playlist, pl_manager->list_index);
-                  entry_deleted = true;
-                  
-                  /* Update list_size */
-                  pl_manager->list_size = playlist_size(pl_manager->playlist);
-                  break;
-               }
-            }
-            
-            /* Increment entry index *if* current entry still
-             * exists (i.e. if entry was deleted, current index
-             * will already point to the *next* entry) */
-            if (!entry_deleted)
-               pl_manager->list_index++;
-            
-            if (pl_manager->list_index + 1 >= pl_manager->list_size)
-               pl_manager->status = PL_MANAGER_CHECK_DUPLICATE_END;
-         }
-         break;
-      case PL_MANAGER_CHECK_DUPLICATE_END:
-         /* Sanity check - if all (or all but one)
-          * playlist entries were removed during the
-          * 'check duplicate' phase, we can stop now */
-         if (pl_manager->list_size < 2)
-         {
-            pl_manager->status = PL_MANAGER_END;
-            break;
-         }
-
-         /* ...otherwise, reset index counter and
-          * start building the M3U file list */
-         pl_manager->list_index = 0;
-         pl_manager->status = PL_MANAGER_ITERATE_FETCH_M3U;
-         break;
-      case PL_MANAGER_ITERATE_FETCH_M3U:
-         {
-            const struct playlist_entry *entry = NULL;
-            
-            /* Update progress display */
-            task_set_progress(task, (pl_manager->list_index * 100) / pl_manager->list_size);
-            
-            /* Get current entry */
-            playlist_get_index(
-                  pl_manager->playlist, pl_manager->list_index, &entry);
-            
-            if (entry)
-            {
-               /* If this is an M3U file, add it to the
-                * M3U list for later processing */
-               if (m3u_file_is_m3u(entry->path))
-               {
-                  union string_list_elem_attr attr;
-                  attr.i = 0;
-                  /* Note: If string_list_append() fails, there is
-                   * really nothing we can do. The M3U file will
-                   * just be ignored... */
-                  string_list_append(
-                        pl_manager->m3u_list, entry->path, attr);
-               }
-            }
-            
-            /* Increment entry index */
-            pl_manager->list_index++;
-            
-            if (pl_manager->list_index >= pl_manager->list_size)
-            {
-               /* Check whether we have any M3U files
-                * to process */
-               if (pl_manager->m3u_list->size > 0)
-                  pl_manager->status = PL_MANAGER_ITERATE_CLEAN_M3U;
-               else
-                  pl_manager->status = PL_MANAGER_END;
-            }
-         }
-         break;
-      case PL_MANAGER_ITERATE_CLEAN_M3U:
-         {
-            const char *m3u_path =
-                  pl_manager->m3u_list->elems[pl_manager->m3u_index].data;
-            
-            if (!string_is_empty(m3u_path))
-            {
-               m3u_file_t *m3u_file = NULL;
-               
-               /* Update progress display */
-               task_set_progress(task, (pl_manager->m3u_index * 100) / pl_manager->m3u_list->size);
-               
-               /* Load M3U file */
-               m3u_file = m3u_file_init(m3u_path);
-               
-               if (m3u_file)
-               {
-                  size_t i;
-                  
-                  /* Loop over M3U entries */
-                  for (i = 0; i < m3u_file_get_size(m3u_file); i++)
-                  {
-                     m3u_file_entry_t *m3u_entry = NULL;
-
-                     /* Delete any playlist items matching the
-                      * content path of the M3U entry */
-                     if (m3u_file_get_entry(m3u_file, i, &m3u_entry))
-                        playlist_delete_by_path(
-                              pl_manager->playlist, m3u_entry->full_path);
-                  }
-                  
-                  m3u_file_free(m3u_file);
-               }
-            }
-            
-            /* Increment M3U file index */
-            pl_manager->m3u_index++;
-            if (pl_manager->m3u_index >= pl_manager->m3u_list->size)
-               pl_manager->status = PL_MANAGER_END;
-         }
-         break;
-      case PL_MANAGER_END:
-         {
-            char task_title[PATH_MAX_LENGTH];
-            /* Save playlist changes to disk */
-            playlist_write_file(pl_manager->playlist);
-            /* Update progress display */
-            task_free_title(task);
-            strlcpy(
-                  task_title, msg_hash_to_str(MSG_PLAYLIST_MANAGER_PLAYLIST_CLEANED),
-                  sizeof(task_title));
-            strlcat(task_title, pl_manager->playlist_name, sizeof(task_title));
-            
-            task_set_title(task, strdup(task_title));
-         }
-         /* fall-through */
-      default:
-         task_set_progress(task, 100);
-         goto task_finished;
-   }
-   
-   return;
-   
-task_finished:
-   
-   if (task)
-      task_set_finished(task, true);
-}
-
-static bool task_pl_manager_clean_playlist_finder(
-      retro_task_t *task, void *user_data)
-{
-   pl_manager_handle_t *pl_manager = NULL;
-   
-   if (!task || !user_data)
-      return false;
-   
-   if (task->handler != task_pl_manager_clean_playlist_handler)
-      return false;
-   
-   pl_manager = (pl_manager_handle_t*)task->state;
-   if (!pl_manager)
-      return false;
-   
-   return string_is_equal((const char*)user_data,
-         pl_manager->playlist_config.path);
-}
-
-bool task_push_pl_manager_clean_playlist(
-      const playlist_config_t *playlist_config)
-{
-   task_finder_data_t find_data;
-   char playlist_name[PATH_MAX_LENGTH];
-   char task_title[PATH_MAX_LENGTH];
-   retro_task_t *task              = task_init();
-   pl_manager_handle_t *pl_manager = (pl_manager_handle_t*)
-      calloc(1, sizeof(pl_manager_handle_t));
-   /* Sanity check */
-   if (!playlist_config || !task || !pl_manager)
-      goto error;
-   if (string_is_empty(playlist_config->path))
-      goto error;
-   
-   fill_pathname_base(playlist_name,
-         playlist_config->path, sizeof(playlist_name));
-   path_remove_extension(playlist_name);
-   
-   if (string_is_empty(playlist_name))
-      goto error;
-   
-   /* Concurrent management of the same playlist
-    * is not allowed */
-   find_data.func                = task_pl_manager_clean_playlist_finder;
-   find_data.userdata            = (void*)playlist_config->path;
-   
-   if (task_queue_find(&find_data))
-      goto error;
-   
-   /* Configure handle */
-   if (!playlist_config_copy(playlist_config, &pl_manager->playlist_config))
-      goto error;
-   
-   pl_manager->playlist_name       = strdup(playlist_name);
-   pl_manager->playlist            = NULL;
-   pl_manager->list_size           = 0;
-   pl_manager->list_index          = 0;
-   pl_manager->m3u_list            = string_list_new();
-   pl_manager->m3u_index           = 0;
-   pl_manager->status              = PL_MANAGER_BEGIN;
-   
-   if (!pl_manager->m3u_list)
-      goto error;
-   
-   /* Configure task */
-   strlcpy(
-         task_title,
-	 msg_hash_to_str(MSG_PLAYLIST_MANAGER_CLEANING_PLAYLIST),
-         sizeof(task_title));
-   strlcat(task_title, playlist_name, sizeof(task_title));
-   
-   task->handler                 = task_pl_manager_clean_playlist_handler;
-   task->state                   = pl_manager;
-   task->title                   = strdup(task_title);
-   task->alternative_look        = true;
-   task->progress                = 0;
-   task->callback                = cb_task_pl_manager;
-   task->cleanup                 = task_pl_manager_free;
-   
-   task_queue_push(task);
-   
-   return true;
-   
-error:
-   
-   if (task)
-   {
-      free(task);
-      task = NULL;
-   }
-   
-   free_pl_manager_handle(pl_manager);
-   pl_manager = NULL;
-   
-   return false;
-}

+ 2 - 0
app/src/main/cpp/tasks/task_screenshot.c

@@ -134,6 +134,7 @@ static void task_screenshot_handler(retro_task_t *task)
    ret = screenshot_dump_direct(state);
 
    /* Push screenshot to image history playlist */
+#ifdef HAVE_PLAYLIST
 #ifdef HAVE_IMAGEVIEWER
    if (       ret
          && !(state->flags & SS_TASK_FLAG_SILENCE)
@@ -149,6 +150,7 @@ static void task_screenshot_handler(retro_task_t *task)
 
       command_playlist_push_write(g_defaults.image_history, &entry);
    }
+#endif
 #endif
 
    task_set_progress(task, 100);

+ 6 - 0
app/src/main/cpp/tasks/tasks_internal.h

@@ -33,7 +33,9 @@
 #include "../core_updater_list.h"
 #endif
 
+#ifdef HAVE_PLAYLIST
 #include "../playlist.h"
+#endif
 
 /* Required for task_push_core_backup() */
 #include "../core_backup.h"
@@ -163,8 +165,10 @@ bool task_push_core_restore(const char *backup_path,
       const char *dir_libretro,
       bool *core_loaded);
 
+#ifdef HAVE_PLAYLIST
 bool task_push_pl_manager_reset_cores(const playlist_config_t *playlist_config);
 bool task_push_pl_manager_clean_playlist(const playlist_config_t *playlist_config);
+#endif
 
 bool task_push_image_load(const char *fullpath,
       bool supports_rgba, unsigned upscale_threshold,
@@ -179,9 +183,11 @@ bool task_push_dbscan(
       retro_task_callback_t cb);
 #endif
 
+#ifdef HAVE_PLAYLIST
 bool task_push_manual_content_scan(
       const playlist_config_t *playlist_config,
       const char *playlist_directory);
+#endif
 
 #ifdef HAVE_OVERLAY
 bool task_push_overlay_load_default(

+ 13 - 13
app/src/main/java/com/xugame/gameconsole/emulator/RetroArchEmulatorActivity.java

@@ -405,19 +405,19 @@ public class RetroArchEmulatorActivity extends RetroActivityCamera {
                                 break;
                         }
                     }
-//                    if (scanLine >= 0 && scanLine < 3) {
-//                        switch (ScreenType.values()[scanLine]) {
-//                            case NORMAL:
-//                                switchFilter(null);
-//                                break;
-//                            case SAI_2X:
-//                                switchFilter("/data/user/0/com.xugame.gameconsole/filters/video/2xSaI.filt");
-//                                break;
-//                            case SCANLINE:
-//                                switchFilter("/data/user/0/com.xugame.gameconsole/filters/video/Scanline2x.filt");
-//                                break;
-//                        }
-//                    }
+                    if (scanLine >= 0 && scanLine < 3) {
+                        switch (ScreenType.values()[scanLine]) {
+                            case NORMAL:
+                                switchFilter(null);
+                                break;
+                            case SAI_2X:
+                                switchFilter("/data/user/0/com.xugame.gameconsole/filters/video/2xSaI.filt");
+                                break;
+                            case SCANLINE:
+                                switchFilter("/data/user/0/com.xugame.gameconsole/filters/video/Scanline2x.filt");
+                                break;
+                        }
+                    }
                 }
             } else if (resultCode == 0) {
                 exitLocalGame();