/*  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) 2018      - Alfredo Monclús
 *  Copyright (C) 2018-2020 - natinusala
 *  Copyright (C) 2019      - Patrick Scheurenbrand
 *
 *  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 <file/file_path.h>
#include <string/stdstring.h>
#include <encodings/utf.h>
#include <features/features_cpu.h>
#include <formats/image.h>
#include <math/float_minmax.h>
#include <array/rhmap.h>
#include <retro_math.h>

#include "../menu_cbs.h"
#include "../menu_driver.h"
#include "../menu_screensaver.h"

#include "../../gfx/gfx_animation.h"
#include "../../gfx/gfx_display.h"
#include "../../gfx/gfx_thumbnail_path.h"
#include "../../gfx/gfx_thumbnail.h"

#include "../../config.def.h"
#include "../../configuration.h"
#include "../../content.h"
#include "../../core_info.h"
#include "../../defaults.h"
#include "../../file_path_special.h"
#include "../../runtime_file.h"
#include "../../input/input_osk.h"

#ifdef HAVE_AUDIOMIXER
#include "../../audio/audio_driver.h"
#endif

#ifdef HAVE_CHEEVOS
#include "../../cheevos/cheevos_menu.h"
#endif

#define ANIMATION_PUSH_ENTRY_DURATION 166.66667f
#define ANIMATION_CURSOR_DURATION     166.66667f
#define ANIMATION_CURSOR_PULSE        166.66667f * 3

#define OZONE_THUMBNAIL_STREAM_DELAY  16.66667f * 3

#define OZONE_EASING_ALPHA            EASING_OUT_CIRC
#define OZONE_EASING_ALPHA_IN         EASING_IN_QUAD
#define OZONE_EASING_XY               EASING_OUT_QUAD

#define FONT_SIZE_FOOTER              18
#define FONT_SIZE_TITLE               36
#define FONT_SIZE_TIME                22
#define FONT_SIZE_ENTRIES_LABEL       24
#define FONT_SIZE_ENTRIES_SUBLABEL    18
#define FONT_SIZE_SIDEBAR             24

#define HEADER_HEIGHT                 87
#define FOOTER_HEIGHT                 78

#define ENTRY_PADDING_HORIZONTAL_HALF 40
#define ENTRY_PADDING_HORIZONTAL_FULL 130
#define ENTRY_PADDING_VERTICAL        20
#define ENTRY_HEIGHT                  50
#define ENTRY_SPACING                 8
#define ENTRY_ICON_SIZE               46
#define ENTRY_ICON_PADDING            15

/* > 'SIDEBAR_WIDTH' must be kept in sync with
 *   menu driver metrics */
#define SIDEBAR_WIDTH                 408
#define SIDEBAR_X_PADDING             40
#define SIDEBAR_Y_PADDING             20
#define SIDEBAR_ENTRY_HEIGHT          50
#define SIDEBAR_ENTRY_Y_PADDING       10
#define SIDEBAR_ENTRY_ICON_SIZE       46
#define SIDEBAR_ENTRY_ICON_PADDING    15
#define SIDEBAR_GRADIENT_HEIGHT       28

#define FULLSCREEN_THUMBNAIL_PADDING  20

#define CURSOR_SIZE                   64
/* Cursor becomes active when it moves more
 * than CURSOR_ACTIVE_DELTA pixels (adjusted
 * by current scale factor) */
#define CURSOR_ACTIVE_DELTA           3

#define INTERVAL_OSK_CURSOR           (0.5f * 1000000)

#if defined(__APPLE__)
/* UTF-8 support is currently broken on Apple devices... */
#define OZONE_TICKER_SPACER           TICKER_SPACER_DEFAULT
#else
/* <EM SPACE><BULLET><EM SPACE>
 * UCN equivalent: "\u2003\u2022\u2003" */
#define OZONE_TICKER_SPACER           "\xE2\x80\x83\xE2\x80\xA2\xE2\x80\x83"
#endif

#define OZONE_WIGGLE_DURATION         15
#define OZONE_TAB_MAX_LENGTH          255

/* Returns true if specified entry is currently
 * displayed on screen */
/* Check whether selected item is already on screen */
#define OZONE_ENTRY_ONSCREEN(ozone, idx) (((idx) >= (ozone)->first_onscreen_entry) && ((idx) <= (ozone)->last_onscreen_entry))

enum ozone_onscreen_entry_position_type
{
   OZONE_ONSCREEN_ENTRY_FIRST = 0,
   OZONE_ONSCREEN_ENTRY_LAST,
   OZONE_ONSCREEN_ENTRY_CENTRE
};

enum
{
   OZONE_SYSTEM_TAB_MAIN = 0,
   OZONE_SYSTEM_TAB_SETTINGS,
   OZONE_SYSTEM_TAB_HISTORY,
   OZONE_SYSTEM_TAB_FAVORITES,
#ifdef HAVE_IMAGEVIEWER
   OZONE_SYSTEM_TAB_IMAGES,
#endif
   OZONE_SYSTEM_TAB_MUSIC,
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
   OZONE_SYSTEM_TAB_VIDEO,
#endif
#ifdef HAVE_NETWORKING
   OZONE_SYSTEM_TAB_NETPLAY,
#endif
   OZONE_SYSTEM_TAB_ADD,
   OZONE_SYSTEM_TAB_CONTENTLESS_CORES,
#if defined(HAVE_LIBRETRODB)
   OZONE_SYSTEM_TAB_EXPLORE,
#endif

   /* End of this enum - use the last one to determine num of possible tabs */
   OZONE_SYSTEM_TAB_LAST
};

enum OZONE_TEXTURE
{
   OZONE_TEXTURE_RETROARCH = 0,
   OZONE_TEXTURE_CURSOR_BORDER,
   OZONE_TEXTURE_LAST
};

enum OZONE_THEME_TEXTURES
{
   OZONE_THEME_TEXTURE_SWITCH = 0,
   OZONE_THEME_TEXTURE_CHECK,

   OZONE_THEME_TEXTURE_CURSOR_NO_BORDER,
   OZONE_THEME_TEXTURE_CURSOR_STATIC,

   OZONE_THEME_TEXTURE_LAST
};

enum OZONE_TAB_TEXTURES
{
   OZONE_TAB_TEXTURE_MAIN_MENU = 0,
   OZONE_TAB_TEXTURE_SETTINGS,
   OZONE_TAB_TEXTURE_HISTORY,
   OZONE_TAB_TEXTURE_FAVORITES,
   OZONE_TAB_TEXTURE_IMAGE,
   OZONE_TAB_TEXTURE_MUSIC,
   OZONE_TAB_TEXTURE_VIDEO,
   OZONE_TAB_TEXTURE_NETWORK,
   OZONE_TAB_TEXTURE_SCAN_CONTENT,
   OZONE_TAB_TEXTURE_CONTENTLESS_CORES,
   OZONE_TAB_TEXTURE_EXPLORE,

   OZONE_TAB_TEXTURE_LAST
};

enum
{
   OZONE_ENTRIES_ICONS_TEXTURE_MAIN_MENU = 0,
   OZONE_ENTRIES_ICONS_TEXTURE_SETTINGS,
   OZONE_ENTRIES_ICONS_TEXTURE_HISTORY,
   OZONE_ENTRIES_ICONS_TEXTURE_FAVORITES,
#ifdef HAVE_IMAGEVIEWER
   OZONE_ENTRIES_ICONS_TEXTURE_IMAGES,
#endif
   OZONE_ENTRIES_ICONS_TEXTURE_MUSICS,
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
   OZONE_ENTRIES_ICONS_TEXTURE_MOVIES,
#endif
#ifdef HAVE_NETWORKING
   OZONE_ENTRIES_ICONS_TEXTURE_NETPLAY,
   OZONE_ENTRIES_ICONS_TEXTURE_ROOM,
   OZONE_ENTRIES_ICONS_TEXTURE_ROOM_LAN,
   OZONE_ENTRIES_ICONS_TEXTURE_ROOM_RELAY,
#endif
   OZONE_ENTRIES_ICONS_TEXTURE_SETTING,
   OZONE_ENTRIES_ICONS_TEXTURE_SUBSETTING,
   OZONE_ENTRIES_ICONS_TEXTURE_ARROW,
   OZONE_ENTRIES_ICONS_TEXTURE_RUN,
   OZONE_ENTRIES_ICONS_TEXTURE_CLOSE,
   OZONE_ENTRIES_ICONS_TEXTURE_RESUME,
   OZONE_ENTRIES_ICONS_TEXTURE_SAVESTATE,
   OZONE_ENTRIES_ICONS_TEXTURE_LOADSTATE,
   OZONE_ENTRIES_ICONS_TEXTURE_RECORDREPLAY,
   OZONE_ENTRIES_ICONS_TEXTURE_PLAYREPLAY,
   OZONE_ENTRIES_ICONS_TEXTURE_HALTREPLAY,
   OZONE_ENTRIES_ICONS_TEXTURE_UNDO,
   OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO,
   OZONE_ENTRIES_ICONS_TEXTURE_BLUETOOTH,
   OZONE_ENTRIES_ICONS_TEXTURE_WIFI,
   OZONE_ENTRIES_ICONS_TEXTURE_CORE_OPTIONS,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_REMAPPING_OPTIONS,
   OZONE_ENTRIES_ICONS_TEXTURE_CHEAT_OPTIONS,
   OZONE_ENTRIES_ICONS_TEXTURE_DISK_OPTIONS,
   OZONE_ENTRIES_ICONS_TEXTURE_SHADER_OPTIONS,
   OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENT_LIST,
   OZONE_ENTRIES_ICONS_TEXTURE_SCREENSHOT,
   OZONE_ENTRIES_ICONS_TEXTURE_RELOAD,
   OZONE_ENTRIES_ICONS_TEXTURE_RENAME,
   OZONE_ENTRIES_ICONS_TEXTURE_FILE,
   OZONE_ENTRIES_ICONS_TEXTURE_FOLDER,
   OZONE_ENTRIES_ICONS_TEXTURE_ZIP,
   OZONE_ENTRIES_ICONS_TEXTURE_FAVORITE,
   OZONE_ENTRIES_ICONS_TEXTURE_ADD_FAVORITE,
   OZONE_ENTRIES_ICONS_TEXTURE_IMAGE,
   OZONE_ENTRIES_ICONS_TEXTURE_MUSIC,
   OZONE_ENTRIES_ICONS_TEXTURE_MOVIE,
   OZONE_ENTRIES_ICONS_TEXTURE_CORE,
   OZONE_ENTRIES_ICONS_TEXTURE_RDB,
   OZONE_ENTRIES_ICONS_TEXTURE_CURSOR,
   OZONE_ENTRIES_ICONS_TEXTURE_SWITCH_ON,
   OZONE_ENTRIES_ICONS_TEXTURE_SWITCH_OFF,
   OZONE_ENTRIES_ICONS_TEXTURE_CLOCK,
   OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_FULL,
   OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_CHARGING,
   OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_80,
   OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_60,
   OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_40,
   OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_20,
   OZONE_ENTRIES_ICONS_TEXTURE_POINTER,
   OZONE_ENTRIES_ICONS_TEXTURE_ADD,
   OZONE_ENTRIES_ICONS_TEXTURE_DISC,
   OZONE_ENTRIES_ICONS_TEXTURE_KEY,
   OZONE_ENTRIES_ICONS_TEXTURE_KEY_HOVER,
   OZONE_ENTRIES_ICONS_TEXTURE_DIALOG_SLICE,
   OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENTS,
   OZONE_ENTRIES_ICONS_TEXTURE_AUDIO,
   OZONE_ENTRIES_ICONS_TEXTURE_EXIT,
   OZONE_ENTRIES_ICONS_TEXTURE_FRAMESKIP,
   OZONE_ENTRIES_ICONS_TEXTURE_INFO,
   OZONE_ENTRIES_ICONS_TEXTURE_HELP,
   OZONE_ENTRIES_ICONS_TEXTURE_NETWORK,
   OZONE_ENTRIES_ICONS_TEXTURE_POWER,
   OZONE_ENTRIES_ICONS_TEXTURE_SAVING,
   OZONE_ENTRIES_ICONS_TEXTURE_UPDATER,
   OZONE_ENTRIES_ICONS_TEXTURE_VIDEO,
   OZONE_ENTRIES_ICONS_TEXTURE_RECORD,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SETTINGS,
   OZONE_ENTRIES_ICONS_TEXTURE_MIXER,
   OZONE_ENTRIES_ICONS_TEXTURE_LOG,
   OZONE_ENTRIES_ICONS_TEXTURE_OSD,
   OZONE_ENTRIES_ICONS_TEXTURE_UI,
   OZONE_ENTRIES_ICONS_TEXTURE_USER,
   OZONE_ENTRIES_ICONS_TEXTURE_PRIVACY,
   OZONE_ENTRIES_ICONS_TEXTURE_LATENCY,
   OZONE_ENTRIES_ICONS_TEXTURE_DRIVERS,
   OZONE_ENTRIES_ICONS_TEXTURE_PLAYLIST,
   OZONE_ENTRIES_ICONS_TEXTURE_QUICKMENU,
   OZONE_ENTRIES_ICONS_TEXTURE_REWIND,
   OZONE_ENTRIES_ICONS_TEXTURE_OVERLAY,
   OZONE_ENTRIES_ICONS_TEXTURE_OVERRIDE,
   OZONE_ENTRIES_ICONS_TEXTURE_NOTIFICATIONS,
   OZONE_ENTRIES_ICONS_TEXTURE_STREAM,
   OZONE_ENTRIES_ICONS_TEXTURE_SHUTDOWN,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_U,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_D,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_L,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_R,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_U,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_D,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_L,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_R,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_P,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SELECT,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_START,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_U,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_D,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_L,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_R,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LB,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_RB,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LT,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_RT,
   OZONE_ENTRIES_ICONS_TEXTURE_CHECKMARK,
   OZONE_ENTRIES_ICONS_TEXTURE_MENU_ADD,
   OZONE_ENTRIES_ICONS_TEXTURE_BRIGHTNESS,
   OZONE_ENTRIES_ICONS_TEXTURE_PAUSE,
   OZONE_ENTRIES_ICONS_TEXTURE_MENU_APPLY_TOGGLE,
   OZONE_ENTRIES_ICONS_TEXTURE_MENU_APPLY_COG,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_ADC,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BIND_ALL,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_MOUSE,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LGUN,
   OZONE_ENTRIES_ICONS_TEXTURE_INPUT_TURBO,
   OZONE_ENTRIES_ICONS_TEXTURE_LAST
};

enum ozone_pending_thumbnail_type
{
   OZONE_PENDING_THUMBNAIL_NONE = 0,
   OZONE_PENDING_THUMBNAIL_RIGHT,
   OZONE_PENDING_THUMBNAIL_LEFT,
   OZONE_PENDING_THUMBNAIL_BOTH
};

/* Container for a footer text label */
typedef struct
{
   const char *str;
   float x;
   int width; /* -1 on error */
   bool show;
} ozone_footer_label_t;

typedef struct ozone_theme
{
   /* Background color */
   float background[16];
   float *background_libretro_running;

   /* Float colors for quads and icons */
   float header_footer_separator[16];
   float text[16];
   float selection[16];
   float selection_border[16];
   float entries_border[16];
   float entries_icon[16];
   float text_selected[16];
   float message_background[16];

   /* RGBA colors for text */
   uint32_t text_rgba;
   uint32_t text_sidebar_rgba;
   uint32_t text_selected_rgba;
   uint32_t text_sublabel_rgba;

   /* Screensaver 'tint' (RGB24) */
   uint32_t screensaver_tint;

   /* Sidebar color */
   float *sidebar_background;
   float *sidebar_top_gradient;
   float *sidebar_bottom_gradient;

   /* Fancy cursor colors */
   float *cursor_border_0;
   float *cursor_border_1;

   uintptr_t textures[OZONE_THEME_TEXTURE_LAST];

   const char *name;
} ozone_theme_t;

/* If you change this struct, also
   change ozone_alloc_node and
   ozone_copy_node */
typedef struct ozone_node
{
   char *fullpath;            /* Entry fullpath */
   char *console_name;        /* Console tab name */
   uintptr_t icon;            /* Console tab icon */
   uintptr_t content_icon;    /* console content icon */
   unsigned height;           /* Entry height */
   unsigned position_y;       /* Entry position Y */
   uint8_t sublabel_lines;    /* Entry sublabel lines */
   bool wrap;                 /* Wrap entry? */
} ozone_node_t;


enum ozone_handle_flags
{
   OZONE_FLAG_IS_DB_MANAGER_LIST              = (1 << 0),
   OZONE_FLAG_IS_EXPLORE_LIST                 = (1 << 1),
   OZONE_FLAG_IS_CONTENTLESS_CORES            = (1 << 2),
   OZONE_FLAG_IS_FILE_LIST                    = (1 << 3),
   OZONE_FLAG_IS_STATE_SLOT                   = (1 << 4),
   OZONE_FLAG_WAS_QUICK_MENU                  = (1 << 5),
   OZONE_FLAG_LIBRETRO_RUNNING                = (1 << 6),
   OZONE_FLAG_FIRST_FRAME                     = (1 << 7),
   OZONE_FLAG_NEED_COMPUTE                    = (1 << 8),
   OZONE_FLAG_DRAW_OLD_LIST                   = (1 << 9),
   OZONE_FLAG_HAS_ALL_ASSETS                  = (1 << 10),
   OZONE_FLAG_IS_PLAYLIST                     = (1 << 11),
   OZONE_FLAG_IS_PLAYLIST_OLD                 = (1 << 12),
   OZONE_FLAG_CURSOR_IN_SIDEBAR               = (1 << 13),
   OZONE_FLAG_CURSOR_IN_SIDEBAR_OLD           = (1 << 14),
   /* false = left to right, true = right to left */
   OZONE_FLAG_FADE_DIRECTION                  = (1 << 15),
   OZONE_FLAG_DRAW_SIDEBAR                    = (1 << 16),
   OZONE_FLAG_EMPTY_PLAYLIST                  = (1 << 17),
   /* true = display it, false = don't */
   OZONE_FLAG_OSK_CURSOR                      = (1 << 18),
   OZONE_FLAG_MSGBOX_STATE                    = (1 << 19),
   OZONE_FLAG_MSGBOX_STATE_OLD                = (1 << 20),
   OZONE_FLAG_SHOULD_DRAW_MSGBOX              = (1 << 21),
   OZONE_FLAG_WANT_THUMBNAIL_BAR              = (1 << 22),
   OZONE_FLAG_SKIP_THUMBNAIL_RESET            = (1 << 23),
   OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE = (1 << 24),
   OZONE_FLAG_PENDING_HIDE_THUMBNAIL_BAR      = (1 << 25),
   OZONE_FLAG_CURSOR_MODE                     = (1 << 26),
   OZONE_FLAG_SHOW_CURSOR                     = (1 << 27),
   OZONE_FLAG_SHOW_SCREENSAVER                = (1 << 28),
   OZONE_FLAG_NO_THUMBNAIL_AVAILABLE          = (1 << 29),
   OZONE_FLAG_FORCE_METADATA_DISPLAY          = (1 << 30)
};

enum ozone_handle_flags2
{
   OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS            = (1 <<  0),
   OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS            = (1 <<  1),
   OZONE_FLAG2_SELECTION_CORE_IS_VIEWER              = (1 <<  2),
   OZONE_FLAG2_SELECTION_CORE_IS_VIEWER_REAL         = (1 <<  3),
   OZONE_FLAG2_POINTER_IN_SIDEBAR                    = (1 <<  4),
   OZONE_FLAG2_LAST_POINTER_IN_SIDEBAR               = (1 <<  5),
   OZONE_FLAG2_CURSOR_WIGGLING                       = (1 <<  6),
   OZONE_FLAG2_LAST_USE_PREFERRED_SYSTEM_COLOR_THEME = (1 <<  7),
   OZONE_FLAG2_RESET_DEPTH                           = (1 <<  8),
   OZONE_FLAG2_PENDING_CURSOR_IN_SIDEBAR             = (1 <<  9),
   OZONE_FLAG2_IS_QUICK_MENU                         = (1 << 10),
   OZONE_FLAG2_IS_PLAYLISTS_TAB                      = (1 << 11),
   OZONE_FLAG2_IGNORE_MISSING_ASSETS                 = (1 << 12),
   OZONE_FLAG2_BLOCK_ANIMATION                       = (1 << 13)
};

struct ozone_handle
{
   menu_input_pointer_t pointer; /* retro_time_t alignment */

   ozone_theme_t *theme;
   char *pending_message;
   file_list_t selection_buf_old;                  /* ptr alignment */
   file_list_t horizontal_list; /* console tabs */ /* ptr alignment */
   /* Maps console tabs to playlist database names */
   ozone_node_t **playlist_db_node_map;
   menu_screensaver_t *screensaver;

   struct
   {
      font_data_impl_t footer;
      font_data_impl_t title;
      font_data_impl_t time;
      font_data_impl_t entries_label;
      font_data_impl_t entries_sublabel;
      font_data_impl_t sidebar;
   } fonts;

   size_t (*word_wrap)(
         char *s, size_t len,
         const char *src, size_t src_len,
         int line_width, int wideglyph_width, unsigned max_lines);

   struct
   {
      ozone_footer_label_t ok;
      ozone_footer_label_t back;
      ozone_footer_label_t scan;
      ozone_footer_label_t clear_setting;
      ozone_footer_label_t random_select;
      ozone_footer_label_t search;
      ozone_footer_label_t cycle_thumbnails;
      ozone_footer_label_t fullscreen_thumbnails;
      ozone_footer_label_t reset_to_default;
      ozone_footer_label_t manage;
      ozone_footer_label_t metadata_override;
      ozone_footer_label_t help;
      ozone_footer_label_t resume;
   } footer_labels;

   struct
   {
      gfx_thumbnail_t right;  /* uintptr_t alignment */
      gfx_thumbnail_t left;   /* uintptr_t alignment */
      gfx_thumbnail_t savestate;
      float stream_delay;
      enum ozone_pending_thumbnail_type pending;
   } thumbnails;
   uintptr_t textures[OZONE_THEME_TEXTURE_LAST];
   uintptr_t icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LAST];
   uintptr_t tab_textures[OZONE_TAB_TEXTURE_LAST];

   size_t categories_selection_ptr; /* active tab id  */
   size_t categories_active_idx_old;
   size_t playlist_index;
   size_t tab_selection[OZONE_TAB_MAX_LENGTH];

   size_t selection; /* currently selected entry */
   size_t selection_old; /* previously selected entry (for fancy animation) */
   size_t selection_old_list;
   size_t fullscreen_thumbnail_selection;
   size_t num_search_terms_old;
   size_t pointer_categories_selection;
   size_t first_onscreen_entry;
   size_t last_onscreen_entry;
   size_t first_onscreen_category;
   size_t last_onscreen_category;

   int depth;

   struct
   {
      int header_height;
      int footer_height;

      int entry_padding_horizontal_half;
      int entry_padding_horizontal_full;
      int entry_padding_vertical;
      int entry_height;
      int entry_spacing;
      int entry_icon_size;
      int entry_icon_padding;

      int sidebar_width_normal;
      int sidebar_width_collapsed;

      int sidebar_padding_horizontal;
      int sidebar_padding_vertical;
      int sidebar_entry_padding_vertical;
      int sidebar_entry_height;
      int sidebar_entry_icon_size;
      int sidebar_entry_icon_padding;
      int sidebar_gradient_height;

      int cursor_size;

      int thumbnail_bar_width;
      int fullscreen_thumbnail_padding;

      int spacer_1px;
      int spacer_2px;
      int spacer_3px;
      int spacer_5px;
   } dimensions;

   unsigned footer_labels_language;
   unsigned last_width;
   unsigned last_height;
   unsigned entries_height;
   unsigned theme_dynamic_cursor_state; /* 0 -> 1 -> 0 -> 1 [...] */
   unsigned selection_core_name_lines;
   unsigned old_list_offset_y;
   unsigned draw_entry_delay;

   uint32_t flags;

   float dimensions_sidebar_width; /* animated field */
   float sidebar_offset;
   float last_scale_factor;
   float last_thumbnail_scale_factor;
   float last_padding_factor;
   float last_font_scale_factor_global;
   float last_font_scale_factor_title;
   float last_font_scale_factor_sidebar;
   float last_font_scale_factor_label;
   float last_font_scale_factor_sublabel;
   float last_font_scale_factor_time;
   float last_font_scale_factor_footer;
   float pure_white[16];

   struct
   {
      float cursor_alpha;
      float scroll_y;
      float scroll_y_sidebar;

      float alpha;
      float list_alpha;
      float messagebox_alpha;
      float sidebar_text_alpha;
      float thumbnail_bar_position;

      float fullscreen_thumbnail_alpha;
      float left_thumbnail_alpha;
   } animations;

   struct
   {
      float selection_border[16];
      float selection[16];
      float entries_border[16];
      float entries_icon[16];
      float entries_checkmark[16];
      float cursor_alpha[16];

      float cursor_border[16];
      float message_background[16];
   } theme_dynamic;

   float scroll_old;

   int16_t pointer_active_delta;
   int16_t cursor_x_old;
   int16_t cursor_y_old;

   uint16_t flags2;

   uint8_t selection_lastplayed_lines;
   uint8_t system_tab_end;
   uint8_t tabs[OZONE_SYSTEM_TAB_LAST];
   uint8_t sidebar_index_list[SCROLL_INDEX_SIZE];
   uint8_t sidebar_index_size;

   char title[NAME_MAX_LENGTH];
   char selection_core_name[NAME_MAX_LENGTH];
   char selection_playtime[NAME_MAX_LENGTH];
   char selection_lastplayed[NAME_MAX_LENGTH];
   char selection_entry_enumeration[NAME_MAX_LENGTH];
   char fullscreen_thumbnail_label[NAME_MAX_LENGTH];

   char assets_path[PATH_MAX_LENGTH];
   char png_path[PATH_MAX_LENGTH];
   char icons_path[PATH_MAX_LENGTH];
   char icons_path_default[PATH_MAX_LENGTH];
   char tab_path[PATH_MAX_LENGTH];

   /* These have to be huge, because runloop_st->name.savestate
    * has a hard-coded size of (PATH_MAX_LENGTH * 2)... */
   char savestate_thumbnail_file_path[(PATH_MAX_LENGTH * 2)];
   char prev_savestate_thumbnail_file_path[(PATH_MAX_LENGTH * 2)];

   char thumbnails_left_status_prev;
   char thumbnails_right_status_prev;

   bool show_thumbnail_bar;
   bool show_playlist_tabs;
   bool sidebar_collapsed;
   bool font_unicode;

   struct
   {
      retro_time_t start_time;
      float amplitude;
      enum menu_action direction;
   } cursor_wiggle_state;
};

typedef struct ozone_handle ozone_handle_t;

static float ozone_sidebar_gradient_top_light[16]                     = {
   0.94f,  0.94f,  0.94f,  1.00f,
   0.94f,  0.94f,  0.94f,  1.00f,
   0.922f, 0.922f, 0.922f, 1.00f,
   0.922f, 0.922f, 0.922f, 1.00f,
};

static float ozone_sidebar_gradient_bottom_light[16]                  = {
   0.922f, 0.922f, 0.922f, 1.00f,
   0.922f, 0.922f, 0.922f, 1.00f,
   0.94f,  0.94f,  0.94f,  1.00f,
   0.94f,  0.94f,  0.94f,  1.00f,
};

static float ozone_sidebar_gradient_top_dark[16]                      = {
   0.2f,  0.2f,  0.2f,  1.00f,
   0.2f,  0.2f,  0.2f,  1.00f,
   0.18f, 0.18f, 0.18f, 1.00f,
   0.18f, 0.18f, 0.18f, 1.00f,
};

static float ozone_sidebar_gradient_bottom_dark[16]                   = {
   0.18f, 0.18f, 0.18f, 1.00f,
   0.18f, 0.18f, 0.18f, 1.00f,
   0.2f,  0.2f,  0.2f,  1.00f,
   0.2f,  0.2f,  0.2f,  1.00f,
};

static float ozone_sidebar_gradient_top_nord[16]                      = {
   0.2078431f, 0.2352941f, 0.2901961f, 1.0f,
   0.2078431f, 0.2352941f, 0.2901961f, 1.0f,
   0.1921569f, 0.2196078f, 0.2705882f, 0.9f,
   0.1921569f, 0.2196078f, 0.2705882f, 0.9f,
};

static float ozone_sidebar_gradient_bottom_nord[16]                   = {
   0.1921569f, 0.2196078f, 0.2705882f, 0.9f,
   0.1921569f, 0.2196078f, 0.2705882f, 0.9f,
   0.2078431f, 0.2352941f, 0.2901961f, 1.0f,
   0.2078431f, 0.2352941f, 0.2901961f, 1.0f,
};

static float ozone_sidebar_gradient_top_gruvbox_dark[16]              = {
   0.1960784f, 0.1882353f, 0.1843137f, 1.0f,
   0.1960784f, 0.1882353f, 0.1843137f, 1.0f,
   0.1686275f, 0.1686275f, 0.1686275f, 0.9f,
   0.1686275f, 0.1686275f, 0.1686275f, 0.9f,
};

static float ozone_sidebar_gradient_bottom_gruvbox_dark[16]           = {
   0.1686275f, 0.1686275f, 0.1686275f, 0.9f,
   0.1686275f, 0.1686275f, 0.1686275f, 0.9f,
   0.1960784f, 0.1882353f, 0.1843137f, 1.0f,
   0.1960784f, 0.1882353f, 0.1843137f, 1.0f,
};

static float ozone_sidebar_gradient_top_boysenberry[16]               = {
   0.27058823529f, 0.09803921568f, 0.14117647058f, 1.00f,
   0.27058823529f, 0.09803921568f, 0.14117647058f, 1.00f,
   0.19215686274f, 0.0f,           0.04705882352f, 1.00f,
   0.19215686274f, 0.0f,           0.04705882352f, 1.00f,
};

static float ozone_sidebar_gradient_bottom_boysenberry[16]            = {
   0.19215686274f, 0.0f,           0.04705882352f, 1.00f,
   0.19215686274f, 0.0f,           0.04705882352f, 1.00f,
   0.27058823529f, 0.09803921568f, 0.14117647058f, 1.00f,
   0.27058823529f, 0.09803921568f, 0.14117647058f, 1.00f,
};

static float ozone_sidebar_gradient_top_hacking_the_kernel[16]        = {
   0.0f, 0.13333333f, 0.0f, 1.0f,
   0.0f, 0.13333333f, 0.0f, 1.0f,
   0.0f, 0.13333333f, 0.0f, 1.0f,
   0.0f, 0.13333333f, 0.0f, 1.0f,
};

static float ozone_sidebar_gradient_bottom_hacking_the_kernel[16]     = {
   0.0f, 0.0666666f,  0.0f, 1.0f,
   0.0f, 0.0666666f,  0.0f, 1.0f,
   0.0f, 0.13333333f, 0.0f, 1.0f,
   0.0f, 0.13333333f, 0.0f, 1.0f,
};

static float ozone_sidebar_gradient_top_twilight_zone[16]             = {
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
};

static float ozone_sidebar_gradient_bottom_twilight_zone[16]          = {
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
};

static float ozone_sidebar_gradient_top_dracula[16]                   = {
   0.2666666f, 0.2784314f, 0.3529412f, 1.0f,
   0.2666666f, 0.2784314f, 0.3529412f, 1.0f,
   0.2666666f, 0.2784314f, 0.3529412f, 1.0f,
   0.2666666f, 0.2784314f, 0.3529412f, 1.0f,
};

static float ozone_sidebar_gradient_bottom_dracula[16]                = {
   0.2666666f, 0.2784314f, 0.3529412f, 1.0f,
   0.2666666f, 0.2784314f, 0.3529412f, 1.0f,
   0.2666666f, 0.2784314f, 0.3529412f, 1.0f,
   0.2666666f, 0.2784314f, 0.3529412f, 1.0f,
};

static float ozone_sidebar_gradient_top_selenium[16]            = {
   0.1019608f, 0.1019608f, 0.1019608f, 1.0f,
   0.1019608f, 0.1019608f, 0.1019608f, 1.0f,
   0.1019608f, 0.1019608f, 0.1019608f, 1.0f,
   0.1019608f, 0.1019608f, 0.1019608f, 1.0f,
};

static float ozone_sidebar_gradient_top_solarized_dark[16]            = {
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
};

static float ozone_sidebar_gradient_bottom_selenium[16]         = {
   0.1019608f, 0.1019608f, 0.1019608f, 1.0f,
   0.1019608f, 0.1019608f, 0.1019608f, 1.0f,
   0.1019608f, 0.1019608f, 0.1019608f, 1.0f,
   0.1019608f, 0.1019608f, 0.1019608f, 1.0f,
};
static float ozone_sidebar_gradient_bottom_solarized_dark[16]         = {
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
};

static float ozone_sidebar_gradient_top_solarized_light[16]           = {
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
};

static float ozone_sidebar_gradient_bottom_solarized_light[16]        = {
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
};

static float ozone_sidebar_background_gray_dark[16]                   =
   COLOR_HEX_TO_FLOAT(0x101010, 0.0f);

static float ozone_sidebar_background_gray_light[16]                  =
   COLOR_HEX_TO_FLOAT(0x202020, 0.0f);

static float ozone_sidebar_background_light[16]                       = {
   0.94f, 0.94f, 0.94f, 1.00f,
   0.94f, 0.94f, 0.94f, 1.00f,
   0.94f, 0.94f, 0.94f, 1.00f,
   0.94f, 0.94f, 0.94f, 1.00f,
};

static float ozone_sidebar_background_dark[16]                        = {
   0.2f, 0.2f, 0.2f, 1.00f,
   0.2f, 0.2f, 0.2f, 1.00f,
   0.2f, 0.2f, 0.2f, 1.00f,
   0.2f, 0.2f, 0.2f, 1.00f,
};

static float ozone_sidebar_background_nord[16]                        = {
   0.2078431f, 0.2352941f, 0.2901961f, 1.0f,
   0.2078431f, 0.2352941f, 0.2901961f, 1.0f,
   0.2078431f, 0.2352941f, 0.2901961f, 1.0f,
   0.2078431f, 0.2352941f, 0.2901961f, 1.0f,
};

static float ozone_sidebar_background_gruvbox_dark[16]                = {
   0.1960784f, 0.1882353f, 0.1843137f, 1.0f,
   0.1960784f, 0.1882353f, 0.1843137f, 1.0f,
   0.1960784f, 0.1882353f, 0.1843137f, 1.0f,
   0.1960784f, 0.1882353f, 0.1843137f, 1.0f,
};

static float ozone_sidebar_background_boysenberry[16]                 = {
   0.27058823529f, 0.09803921568f, 0.14117647058f, 1.00f,
   0.27058823529f, 0.09803921568f, 0.14117647058f, 1.00f,
   0.27058823529f, 0.09803921568f, 0.14117647058f, 1.00f,
   0.27058823529f, 0.09803921568f, 0.14117647058f, 1.00f,
};

static float ozone_sidebar_background_hacking_the_kernel[16]          = {
   0.0f, 0.1333333f, 0.0f, 1.0f,
   0.0f, 0.1333333f, 0.0f, 1.0f,
   0.0f, 0.1333333f, 0.0f, 1.0f,
   0.0f, 0.1333333f, 0.0f, 1.0f,
};

static float ozone_sidebar_background_twilight_zone[16]               = {
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
};

static float ozone_sidebar_background_dracula[16]                     = {
   0.2666666f, 0.2784314f, 0.3529412f, 1.0f,
   0.2666666f, 0.2784314f, 0.3529412f, 1.0f,
   0.2666666f, 0.2784314f, 0.3529412f, 1.0f,
   0.2666666f, 0.2784314f, 0.3529412f, 1.0f,
};

static float ozone_sidebar_background_selenium[16]              = {
   0.1019608f, 0.1019608f, 0.1019608f, 1.0f,
   0.1019608f, 0.1019608f, 0.1019608f, 1.0f,
   0.1019608f, 0.1019608f, 0.1019608f, 1.0f,
   0.1019608f, 0.1019608f, 0.1019608f, 1.0f,
};
static float ozone_sidebar_background_solarized_dark[16]              = {
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
};

static float ozone_sidebar_background_solarized_light[16]             = {
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
};

static float ozone_sidebar_background_purple_rain[16] = {
   0.0862745f, 0.0f, 0.1294117f, 1.0f,
   0.0862745f, 0.0f, 0.1294117f, 1.0f,
   0.0862745f, 0.0f, 0.1294117f, 1.0f,
   0.0862745f, 0.0f, 0.1294117f, 1.0f,
};

static float ozone_background_libretro_running_gray_dark[16]          =
   COLOR_HEX_TO_FLOAT(0x101010, 1.0f);

static float ozone_background_libretro_running_gray_light[16]         =
   COLOR_HEX_TO_FLOAT(0x202020, 1.0f);

static float ozone_background_libretro_running_light[16]              = {
   0.690f, 0.690f, 0.690f, 0.75f,
   0.690f, 0.690f, 0.690f, 0.75f,
   0.922f, 0.922f, 0.922f, 1.0f,
   0.922f, 0.922f, 0.922f, 1.0f
};

static float ozone_background_libretro_running_dark[16]               = {
   0.176f, 0.176f, 0.176f, 0.75f,
   0.176f, 0.176f, 0.176f, 0.75f,
   0.178f, 0.178f, 0.178f, 1.0f,
   0.178f, 0.178f, 0.178f, 1.0f,
};

static float ozone_background_libretro_running_nord[16]               = {
   0.1803922f, 0.2039216f, 0.2509804f, 0.75f,
   0.1803922f, 0.2039216f, 0.2509804f, 0.75f,
   0.1803922f, 0.2039216f, 0.2509804f, 1.0f,
   0.1803922f, 0.2039216f, 0.2509804f, 1.0f,
};

static float ozone_background_libretro_running_gruvbox_dark[16]       = {
   0.1568627f, 0.1568627f, 0.1568627f, 0.75f,
   0.1568627f, 0.1568627f, 0.1568627f, 0.75f,
   0.1568627f, 0.1568627f, 0.1568627f, 1.0f,
   0.1568627f, 0.1568627f, 0.1568627f, 1.0f,
};

static float ozone_background_libretro_running_boysenberry[16]        = {
   0.27058823529f, 0.09803921568f, 0.14117647058f, 0.75f,
   0.27058823529f, 0.09803921568f, 0.14117647058f, 0.75f,
   0.27058823529f, 0.09803921568f, 0.14117647058f, 0.75f,
   0.27058823529f, 0.09803921568f, 0.14117647058f, 0.75f,
};

static float ozone_background_libretro_running_hacking_the_kernel[16] = {
   0.0f, 0.0666666f, 0.0f, 0.75f,
   0.0f, 0.0666666f, 0.0f, 0.75f,
   0.0f, 0.0666666f, 0.0f, 1.0f,
   0.0f, 0.0666666f, 0.0f, 1.0f,
};

static float ozone_background_libretro_running_twilight_zone[16]      = {
   0.0078431f, 0.0f, 0.0156862f, 0.75f,
   0.0078431f, 0.0f, 0.0156862f, 0.75f,
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
   0.0078431f, 0.0f, 0.0156862f, 1.0f,
};

static float ozone_background_libretro_running_dracula[16]            = {
   0.1568627f, 0.1647058f, 0.2117647f, 0.75f,
   0.1568627f, 0.1647058f, 0.2117647f, 0.75f,
   0.1568627f, 0.1647058f, 0.2117647f, 1.0f,
   0.1568627f, 0.1647058f, 0.2117647f, 1.0f,
};

static float ozone_background_libretro_running_selenium[16]     = {
   0.1647059f, 0.1647059f, 0.1647059f, 1.0f,
   0.1647059f, 0.1647059f, 0.1647059f, 1.0f,
   0.1647059f, 0.1647059f, 0.1647059f, 1.0f,
   0.1647059f, 0.1647059f, 0.1647059f, 1.0f,
};
static float ozone_background_libretro_running_solarized_dark[16]     = {
   0.0000000f, 0.1294118f, 0.1725490f, .85f,
   0.0000000f, 0.1294118f, 0.1725490f, .85f,
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
   0.0000000f, 0.1294118f, 0.1725490f, 1.0f,
};

static float ozone_background_libretro_running_solarized_light[16]    = {
   1.0000000f, 1.0000000f, 0.9294118f, 0.85f,
   1.0000000f, 1.0000000f, 0.9294118f, 0.85f,
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
   1.0000000f, 1.0000000f, 0.9294118f, 1.0f,
};

static float ozone_background_libretro_running_purple_rain[16] = {
   0.0862745f, 0.0f, 0.1294117f, 0.75f,
   0.0862745f, 0.0f, 0.1294117f, 0.75f,
   0.0862745f, 0.0f, 0.1294117f, 1.0f,
   0.0862745f, 0.0f, 0.1294117f, 1.0f,
};

static float ozone_border_gray[16]                 = COLOR_HEX_TO_FLOAT(0x303030, 1.0f);

static float ozone_border_0_light[16]              = COLOR_HEX_TO_FLOAT(0x50EFD9, 1.00);
static float ozone_border_1_light[16]              = COLOR_HEX_TO_FLOAT(0x0DB6D5, 1.00);

static float ozone_border_0_dark[16]               = COLOR_HEX_TO_FLOAT(0x198AC6, 1.00);
static float ozone_border_1_dark[16]               = COLOR_HEX_TO_FLOAT(0x89F1F2, 1.00);

static float ozone_border_0_nord[16]               = COLOR_HEX_TO_FLOAT(0x5E81AC, 1.0f);
static float ozone_border_1_nord[16]               = COLOR_HEX_TO_FLOAT(0x88C0D0, 1.0f);

static float ozone_border_0_gruvbox_dark[16]       = COLOR_HEX_TO_FLOAT(0xAF3A03, 1.0f);
static float ozone_border_1_gruvbox_dark[16]       = COLOR_HEX_TO_FLOAT(0xFE8019, 1.0f);

static float ozone_border_0_boysenberry[16]        = COLOR_HEX_TO_FLOAT(0x50EFD9, 1.00);
static float ozone_border_1_boysenberry[16]        = COLOR_HEX_TO_FLOAT(0x0DB6D5, 1.00);

static float ozone_border_0_hacking_the_kernel[16] = COLOR_HEX_TO_FLOAT(0x008C00, 1.0f);
static float ozone_border_1_hacking_the_kernel[16] = COLOR_HEX_TO_FLOAT(0x00E000, 1.0f);

static float ozone_border_0_twilight_zone[16]      = COLOR_HEX_TO_FLOAT(0xC3A0E0, 1.0f);
static float ozone_border_1_twilight_zone[16]      = COLOR_HEX_TO_FLOAT(0x9B61CC, 1.0f);

static float ozone_border_0_dracula[16]            = COLOR_HEX_TO_FLOAT(0xC3A0E0, 1.0f);
static float ozone_border_1_dracula[16]            = COLOR_HEX_TO_FLOAT(0x9B61CC, 1.0f);

static float ozone_border_0_selenium[16]           = COLOR_HEX_TO_FLOAT(0x91a666, 1.0f);
static float ozone_border_1_selenium[16]           = COLOR_HEX_TO_FLOAT(0x566646, 1.0f);

static float ozone_border_0_solarized_dark[16]     = COLOR_HEX_TO_FLOAT(0x67ECE2, 1.0f);
static float ozone_border_1_solarized_dark[16]     = COLOR_HEX_TO_FLOAT(0x2AA198, 1.0f);

static float ozone_border_0_solarized_light[16]    = COLOR_HEX_TO_FLOAT(0x8F120F, 1.0f);
static float ozone_border_1_solarized_light[16]    = COLOR_HEX_TO_FLOAT(0xDC322F, 1.0f);

static float ozone_border_0_purple_rain[16]        = COLOR_HEX_TO_FLOAT(0xC3A0E0, 1.0f);
static float ozone_border_1_purple_rain[16]        = COLOR_HEX_TO_FLOAT(0x8C3DCC, 1.0f);

static ozone_theme_t ozone_theme_light = {
   COLOR_HEX_TO_FLOAT(0xEBEBEB, 1.00f),                  /* background */
   ozone_background_libretro_running_light,              /* background_libretro_running */

   COLOR_HEX_TO_FLOAT(0x2B2B2B, 1.00f),                  /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0x333333, 1.00f),                  /* text */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.00f),                  /* selection */
   COLOR_HEX_TO_FLOAT(0x10BEC5, 1.00f),                  /* selection_border */
   COLOR_HEX_TO_FLOAT(0xCDCDCD, 1.00f),                  /* entries_border */
   COLOR_HEX_TO_FLOAT(0x333333, 1.00f),                  /* entries_icon */
   COLOR_HEX_TO_FLOAT(0x374CFF, 1.00f),                  /* text_selected */
   COLOR_HEX_TO_FLOAT(0xF0F0F0, 1.00f),                  /* message_background */

   0x333333FF,                                           /* text_rgba */
   0x333333FF,                                           /* text_sidebar_rgba */
   0x374CFFFF,                                           /* text_selected_rgba */
   0x878787FF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0xEBEBEB,                                             /* screensaver_tint */

   ozone_sidebar_background_light,                       /* sidebar_background */
   ozone_sidebar_gradient_top_light,                     /* sidebar_top_gradient */
   ozone_sidebar_gradient_bottom_light,                  /* sidebar_bottom_gradient */

   ozone_border_0_light,                                 /* cursor_border_0 */
   ozone_border_1_light,                                 /* cursor_border_1 */

   {0},                                                  /* textures */

   "light"                                               /* name */
};

static ozone_theme_t ozone_theme_dark = {
   COLOR_HEX_TO_FLOAT(0x2D2D2D, 1.00f),                  /* background */
   ozone_background_libretro_running_dark,               /* background_libretro_running */

   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.00f),                  /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.00f),                  /* text */
   COLOR_HEX_TO_FLOAT(0x212227, 1.00f),                  /* selection */
   COLOR_HEX_TO_FLOAT(0x2DA3CB, 1.00f),                  /* selection_border */
   COLOR_HEX_TO_FLOAT(0x51514F, 1.00f),                  /* entries_border */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.00f),                  /* entries_icon */
   COLOR_HEX_TO_FLOAT(0x00D9AE, 1.00f),                  /* text_selected */
   COLOR_HEX_TO_FLOAT(0x464646, 1.00f),                  /* message_background */

   0xFFFFFFFF,                                           /* text_rgba */
   0xFFFFFFFF,                                           /* text_sidebar_rgba */
   0x00FFC5FF,                                           /* text_selected_rgba */
   0x9F9FA1FF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0xFFFFFF,                                             /* screensaver_tint */

   ozone_sidebar_background_dark,                        /* sidebar_background */
   ozone_sidebar_gradient_top_dark,                      /* sidebar_top_gradient */
   ozone_sidebar_gradient_bottom_dark,                   /* sidebar_bottom_gradient */

   ozone_border_0_dark,                                  /* cursor_border_0 */
   ozone_border_1_dark,                                  /* cursor_border_1 */

   {0},                                                  /* textures */

   "dark"                                                /* name */
};

static ozone_theme_t ozone_theme_nord = {
   /* Background color */
   COLOR_HEX_TO_FLOAT(0x2E3440, 1.0f),                   /* background */
   ozone_background_libretro_running_nord,               /* background_libretro_running */

   /* Float colors for quads and icons */
   COLOR_HEX_TO_FLOAT(0xD8DEE9, 1.0f),                   /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0xECEFF4, 1.0f),                   /* text */
   COLOR_HEX_TO_FLOAT(0x232730, 1.0f),                   /* selection */
   COLOR_HEX_TO_FLOAT(0x73A1BE, 1.0f),                   /* selection_border */
   COLOR_HEX_TO_FLOAT(0x4C566A, 1.0f),                   /* entries_border */
   COLOR_HEX_TO_FLOAT(0xE5E9F0, 1.0f),                   /* entries_icon */
   COLOR_HEX_TO_FLOAT(0xA9C791, 1.0f),                   /* text_selected */
   COLOR_HEX_TO_FLOAT(0x434C5E, 1.0f),                   /* message_background */

   /* RGBA colors for text */
   0xECEFF4FF,                                           /* text_rgba */
   0xECEFF4FF,                                           /* text_sidebar_rgba */
   0xA9C791FF,                                           /* text_selected_rgba */
   0x8FBCBBFF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0xECEFF4,                                             /* screensaver_tint */

   /* Sidebar color */
   ozone_sidebar_background_nord,                        /* sidebar_background */
   ozone_sidebar_gradient_top_nord,                      /* sidebar_top_gradient */
   ozone_sidebar_gradient_bottom_nord,                   /* sidebar_bottom_gradient */

   /* Fancy cursor colors */
   ozone_border_0_nord,                                  /* cursor_border_0 */
   ozone_border_1_nord,                                  /* cursor_border_1 */

   {0},                                                  /* textures */

   "nord"                                                /* name */
};

static ozone_theme_t ozone_theme_gruvbox_dark = {
   /* Background color */
   COLOR_HEX_TO_FLOAT(0x282828, 1.0f),                   /* background */
   ozone_background_libretro_running_gruvbox_dark,       /* background_libretro_running */

   /* Float colors for quads and icons */
   COLOR_HEX_TO_FLOAT(0xD5C4A1, 1.0f),                   /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0xEBDBB2, 1.0f),                   /* text */
   COLOR_HEX_TO_FLOAT(0x1D2021, 1.0f),                   /* selection */
   COLOR_HEX_TO_FLOAT(0xD75D0E, 1.0f),                   /* selection_border */
   COLOR_HEX_TO_FLOAT(0x665C54, 1.0f),                   /* entries_border */
   COLOR_HEX_TO_FLOAT(0xEBDBB2, 1.0f),                   /* entries_icon */
   COLOR_HEX_TO_FLOAT(0x8EC07C, 1.0f),                   /* text_selected */
   COLOR_HEX_TO_FLOAT(0x32302F, 1.0f),                   /* message_background */

   /* RGBA colors for text */
   0xEBDBB2FF,                                           /* text_rgba */
   0xEBDBB2FF,                                           /* text_sidebar_rgba */
   0x8EC07CFF,                                           /* text_selected_rgba */
   0xD79921FF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0xEBDBB2,                                             /* screensaver_tint */

   /* Sidebar color */
   ozone_sidebar_background_gruvbox_dark,                /* sidebar_background */
   ozone_sidebar_gradient_top_gruvbox_dark,              /* sidebar_top_gradient */
   ozone_sidebar_gradient_bottom_gruvbox_dark,           /* sidebar_bottom_gradient */

   /* Fancy cursor colors */
   ozone_border_0_gruvbox_dark,                          /* cursor_border_0 */
   ozone_border_1_gruvbox_dark,                          /* cursor_border_1 */

   {0},                                                  /* textures */

   "gruvbox_dark"                                        /* name */
};

static ozone_theme_t ozone_theme_boysenberry = {
   /* Background color */
   COLOR_HEX_TO_FLOAT(0x31000C, 1.0f),                   /* background */
   ozone_background_libretro_running_boysenberry,        /* background_libretro_running */

   /* Float colors for quads and icons */
   COLOR_HEX_TO_FLOAT(0x85535F, 1.0f),                   /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0xEBDBB2, 1.0f),                   /* text */
   COLOR_HEX_TO_FLOAT(0x4E2A35, 1.0f),                   /* selection */
   COLOR_HEX_TO_FLOAT(0xD599FF, 1.0f),                   /* selection_border */
   COLOR_HEX_TO_FLOAT(0x73434C, 1.0f),                   /* entries_border */
   COLOR_HEX_TO_FLOAT(0xFEBCFF, 1.0f),                   /* entries_icon */
   COLOR_HEX_TO_FLOAT(0xD599FF, 1.0f),                   /* text_selected */
   COLOR_HEX_TO_FLOAT(0x32302F, 1.0f),                   /* message_background */

   /* RGBA colors for text */
   0xFEBCFFFF,                                           /* text_rgba */
   0xFEBCFFFF,                                           /* text_sidebar_rgba */
   0xFEBCFFFF,                                           /* text_selected_rgba */
   0xD599FFFF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0xFEBCFF,                                             /* screensaver_tint */

   /* Sidebar color */
   ozone_sidebar_background_boysenberry,                 /* sidebar_background */
   ozone_sidebar_gradient_top_boysenberry,               /* sidebar_top_gradient */
   ozone_sidebar_gradient_bottom_boysenberry,            /* sidebar_bottom_gradient */

   /* Fancy cursor colors */
   ozone_border_0_boysenberry,                           /* cursor_border_0 */
   ozone_border_1_boysenberry,                           /* cursor_border_1 */

   {0},                                                  /* textures */

   "boysenberry"                                         /* name */
};

static ozone_theme_t ozone_theme_hacking_the_kernel = {
   /* Background color */
   COLOR_HEX_TO_FLOAT(0x001100, 1.0f),                   /* background */
   ozone_background_libretro_running_hacking_the_kernel, /* background_libretro_running */

   /* Float colors for quads and icons */
   COLOR_HEX_TO_FLOAT(0x17C936, 1.0f),                   /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0x00FF29, 1.0f),                   /* text */
   COLOR_HEX_TO_FLOAT(0x003400, 1.0f),                   /* selection */
   COLOR_HEX_TO_FLOAT(0x1BDA3C, 1.0f),                   /* selection_border */
   COLOR_HEX_TO_FLOAT(0x008C00, 0.1f),                   /* entries_border */
   COLOR_HEX_TO_FLOAT(0x00FF00, 1.0f),                   /* entries_icon */
   COLOR_HEX_TO_FLOAT(0x8EC07C, 1.0f),                   /* text_selected */
   COLOR_HEX_TO_FLOAT(0x0D0E0F, 1.0f),                   /* message_background */

   /* RGBA colors for text */
   0x00E528FF,                                           /* text_rgba */
   0x00E528FF,                                           /* text_sidebar_rgba */
   0x83FF83FF,                                           /* text_selected_rgba */
   0x53E63DFF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0x00E528,                                             /* screensaver_tint */

   /* Sidebar color */
   ozone_sidebar_background_hacking_the_kernel,          /* sidebar_background */
   ozone_sidebar_gradient_top_hacking_the_kernel,        /* sidebar_top_gradient */
   ozone_sidebar_gradient_bottom_hacking_the_kernel,     /* sidebar_bottom_gradient */

   /* Fancy cursor colors */
   ozone_border_0_hacking_the_kernel,                    /* cursor_border_0 */
   ozone_border_1_hacking_the_kernel,                    /* cursor_border_1 */

   {0},                                                  /* textures */

   "hacking_the_kernel"                                  /* name */
};

static ozone_theme_t ozone_theme_twilight_zone = {
   /* Background color */
   COLOR_HEX_TO_FLOAT(0x020004, 1.0f),                   /* background */
   ozone_background_libretro_running_twilight_zone,      /* background_libretro_running */

   /* Float colors for quads and icons */
   COLOR_HEX_TO_FLOAT(0x5B5069, 1.0f),                   /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0xF7F0FA, 1.0f),                   /* text */
   COLOR_HEX_TO_FLOAT(0x232038, 1.0f),                   /* selection */
   COLOR_HEX_TO_FLOAT(0x9B61CC, 1.0f),                   /* selection_border */
   COLOR_HEX_TO_FLOAT(0xC27AFF, 1.0f),                   /* entries_border */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.0f),                   /* entries_icon */
   COLOR_HEX_TO_FLOAT(0xB78CC8, 1.0f),                   /* text_selected */
   COLOR_HEX_TO_FLOAT(0xB78CC8, 1.0f),                   /* message_background */

   /* RGBA colors for text */
   0xFDFCFEFF,                                           /* text_rgba */
   0xFDFCFEFF,                                           /* text_sidebar_rgba */
   0xB78CC8FF,                                           /* text_selected_rgba */
   0x9A6C99FF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0xFDFCFE,                                             /* screensaver_tint */

   /* Sidebar color */
   ozone_sidebar_background_twilight_zone,               /* sidebar_background */
   ozone_sidebar_gradient_top_twilight_zone,             /* sidebar_top_gradient */
   ozone_sidebar_gradient_bottom_twilight_zone,          /* sidebar_bottom_gradient */

   /* Fancy cursor colors */
   ozone_border_0_twilight_zone,                         /* cursor_border_0 */
   ozone_border_1_twilight_zone,                         /* cursor_border_1 */

   {0},                                                  /* textures */

   "twilight_zone"                                       /* name */
};

static ozone_theme_t ozone_theme_dracula = {
   /* Background color */
   COLOR_HEX_TO_FLOAT(0x282A36, 1.0f),                   /* background */
   ozone_background_libretro_running_dracula,            /* background_libretro_running */

   /* Float colors for quads and icons */
   COLOR_HEX_TO_FLOAT(0x282A36, 0.0f),                   /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0xF8F8F2, 1.0f),                   /* text */
   COLOR_HEX_TO_FLOAT(0x44475A, 1.0f),                   /* selection */
   COLOR_HEX_TO_FLOAT(0x44475A, 0.0f),                   /* selection_border */
   COLOR_HEX_TO_FLOAT(0x282A36, 0.0f),                   /* entries_border */
   COLOR_HEX_TO_FLOAT(0xF8F8F2, 1.0f),                   /* entries_icon */
   COLOR_HEX_TO_FLOAT(0xFF79C6, 1.0f),                   /* text_selected */
   COLOR_HEX_TO_FLOAT(0x6272A4, 1.0f),                   /* message_background */

   /* RGBA colors for text */
   0xF8F8F2FF,                                           /* text_rgba */
   0xF8F8F2FF,                                           /* text_sidebar_rgba */
   0xFF79C6FF,                                           /* text_selected_rgba */
   0x6272A4FF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0xF8F8F2,                                             /* screensaver_tint */

   /* Sidebar color */
   ozone_sidebar_background_dracula,                     /* sidebar_background */
   ozone_sidebar_gradient_top_dracula,                   /* sidebar_top_gradient */
   ozone_sidebar_gradient_bottom_dracula,                /* sidebar_bottom_gradient */

   /* Fancy cursor colors */
   ozone_border_0_dracula,                               /* cursor_border_0 */
   ozone_border_1_dracula,                               /* cursor_border_1 */

   {0},                                                  /* textures */

   "dracula"                                             /* name */
};


static ozone_theme_t ozone_theme_solarized_dark = {
   /* Background color */
   COLOR_HEX_TO_FLOAT(0x002B36, 1.0f),                   /* background */
   ozone_background_libretro_running_solarized_dark,     /* background_libretro_running */

   /* Float colors for quads and icons */
   COLOR_HEX_TO_FLOAT(0x839496, 1.0f),                   /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0x93A1A1, 1.0f),                   /* text */
   COLOR_HEX_TO_FLOAT(0x073642, 1.0f),                   /* selection */
   COLOR_HEX_TO_FLOAT(0x2AA198, 1.0f),                   /* selection_border */
   COLOR_HEX_TO_FLOAT(0x073642, 1.0f),                   /* entries_border */
   COLOR_HEX_TO_FLOAT(0x268BD2, 1.0f),                   /* entries_icon */
   COLOR_HEX_TO_FLOAT(0x93A1A1, 1.0f),                   /* text_selected */
   COLOR_HEX_TO_FLOAT(0x002B36, 1.0f),                   /* message_background */

   /* RGBA colors for text */
   0x93A1A1FF,                                           /* text_rgba */
   0x93A1A1FF,                                           /* text_sidebar_rgba */
   0x2AA198FF,                                           /* text_selected_rgba */
   0x657B83FF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0x073642,                                             /* screensaver_tint */

   /* Sidebar color */
   ozone_sidebar_background_solarized_dark,              /* sidebar_background */
   ozone_sidebar_gradient_top_solarized_dark,            /* sidebar_top_gradient */
   ozone_sidebar_gradient_bottom_solarized_dark,         /* sidebar_bottom_gradient */

   /* Fancy cursor colors */
   ozone_border_0_solarized_dark,                        /* cursor_border_0 */
   ozone_border_1_solarized_dark,                        /* cursor_border_1 */

   {0},                                                  /* textures */

   "solarized_dark"                                      /* name */
};

static ozone_theme_t ozone_theme_solarized_light = {
   /* Background color */
   COLOR_HEX_TO_FLOAT(0xFDF6E3, 1.0f),                   /* background */
   ozone_background_libretro_running_solarized_light,    /* background_libretro_running */

   /* Float colors for quads and icons */
   COLOR_HEX_TO_FLOAT(0x657B83, 1.0f),                   /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0x586E75, 1.0f),                   /* text */
   COLOR_HEX_TO_FLOAT(0xEEE8D5, 1.0f),                   /* selection */
   COLOR_HEX_TO_FLOAT(0xDC322F, 1.0f),                   /* selection_border */
   COLOR_HEX_TO_FLOAT(0xEEE8D5, 1.0f),                   /* entries_border */
   COLOR_HEX_TO_FLOAT(0xCB4B16, 1.0f),                   /* entries_icon */
   COLOR_HEX_TO_FLOAT(0x586E75, 1.0f),                   /* text_selected */
   COLOR_HEX_TO_FLOAT(0xFDF6E3, 1.0f),                   /* message_background */

   /* RGBA colors for text */
   0x586E75FF,                                           /* text_rgba */
   0x586E75FF,                                           /* text_sidebar_rgba */
   0xDC322FFF,                                           /* text_selected_rgba */
   0x839496FF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0xEEE8D5,                                             /* screensaver_tint */

   /* Sidebar color */
   ozone_sidebar_background_solarized_light,             /* sidebar_background */
   ozone_sidebar_gradient_top_solarized_light,           /* sidebar_top_gradient */
   ozone_sidebar_gradient_bottom_solarized_light,        /* sidebar_bottom_gradient */

   /* Fancy cursor colors */
   ozone_border_0_solarized_light,                       /* cursor_border_0 */
   ozone_border_1_solarized_light,                       /* cursor_border_1 */

   {0},                                                  /* textures */

   "solarized_light"                                     /* name */
};

static ozone_theme_t ozone_theme_gray_dark = {
   /* Background color */
   COLOR_HEX_TO_FLOAT(0x101010, 1.0f),                   /* background */
   ozone_background_libretro_running_gray_dark,          /* background_libretro_running */

   /* Float colors for quads and icons */
   COLOR_HEX_TO_FLOAT(0x000000, 0.0f),                   /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.0f),                   /* text */
   COLOR_HEX_TO_FLOAT(0x303030, 1.0f),                   /* selection */
   COLOR_HEX_TO_FLOAT(0x303030, 1.0f),                   /* selection_border */
   COLOR_HEX_TO_FLOAT(0x101010, 1.0f),                   /* entries_border */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.0f),                   /* entries_icon */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.0f),                   /* text_selected */
   COLOR_HEX_TO_FLOAT(0x202020, 1.0f),                   /* message_background */

   /* RGBA colors for text */
   0xC0C0C0FF,                                           /* text_rgba */
   0x808080FF,                                           /* text_sidebar_rgba */
   0xFFFFFFFF,                                           /* text_selected_rgba */
   0x606060FF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0xFFFFFF,                                             /* screensaver_tint */

   /* Sidebar color */
   ozone_sidebar_background_gray_dark,                   /* sidebar_background */
   ozone_sidebar_background_gray_dark,                   /* sidebar_top_gradient */
   ozone_sidebar_background_gray_dark,                   /* sidebar_bottom_gradient */

   /* Fancy cursor colors */
   ozone_border_gray,                                    /* cursor_border_0 */
   ozone_border_gray,                                    /* cursor_border_1 */

   {0},                                                  /* textures */

   /* No theme assets */
   NULL,                                                 /* name */
};

static ozone_theme_t ozone_theme_gray_light = {
   /* Background color */
   COLOR_HEX_TO_FLOAT(0x202020, 1.0f),                   /* background */
   ozone_background_libretro_running_gray_light,         /* background_libretro_running */

   /* Float colors for quads and icons */
   COLOR_HEX_TO_FLOAT(0x000000, 0.0f),                   /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.0f),                   /* text */
   COLOR_HEX_TO_FLOAT(0x101010, 1.0f),                   /* selection */
   COLOR_HEX_TO_FLOAT(0x101010, 1.0f),                   /* selection_border */
   COLOR_HEX_TO_FLOAT(0x202020, 1.0f),                   /* entries_border */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.0f),                   /* entries_icon */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.0f),                   /* text_selected */
   COLOR_HEX_TO_FLOAT(0x202020, 1.0f),                   /* message_background */

   /* RGBA colors for text */
   0xC0C0C0FF,                                           /* text_rgba */
   0x808080FF,                                           /* text_sidebar_rgba */
   0xFFFFFFFF,                                           /* text_selected_rgba */
   0x606060FF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0xFFFFFF,                                             /* screensaver_tint */

   /* Sidebar color */
   ozone_sidebar_background_gray_light,                  /* sidebar_background */
   ozone_sidebar_background_gray_light,                  /* sidebar_top_gradient */
   ozone_sidebar_background_gray_light,                  /* sidebar_bottom_gradient */

   /* Fancy cursor colors */
   ozone_border_gray,                                    /* cursor_border_0 */
   ozone_border_gray,                                    /* cursor_border_1 */

   {0},                                                  /* textures */

   /* No theme assets */
   NULL,                                                 /* name */
};

static ozone_theme_t ozone_theme_purple_rain = {
   /* Background color */
   COLOR_HEX_TO_FLOAT(0x160021, 1.0f),                   /* background */
   ozone_background_libretro_running_purple_rain,        /* background_libretro_running */

   /* Float colors for quads and icons */
   COLOR_HEX_TO_FLOAT(0xAA00CC, 1.0f),                   /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.0f),                   /* text */
   COLOR_HEX_TO_FLOAT(0x660099, 1.0f),                   /* selection */
   COLOR_HEX_TO_FLOAT(0x660099, 1.0f),                   /* selection_border */
   COLOR_HEX_TO_FLOAT(0xAA00CC, 1.0f),                   /* entries_border */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.0f),                   /* entries_icon */
   COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.0f),                   /* text_selected */
   COLOR_HEX_TO_FLOAT(0x660099, 1.0f),                   /* message_background */

   /* RGBA colors for text */
   0xFFFFFFFF,                                           /* text_rgba */
   0xFFFFFFFF,                                           /* text_sidebar_rgba */
   0xFFFFFFFF,                                           /* text_selected_rgba */
   0xFFFFFFFF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0xFFFFFF,                                             /* screensaver_tint */

   /* Sidebar color */
   ozone_sidebar_background_purple_rain,                 /* sidebar_background */
   ozone_sidebar_background_purple_rain,                 /* sidebar_top_gradient */
   ozone_sidebar_background_purple_rain,                 /* sidebar_bottom_gradient */

   /* Fancy cursor colors */
   ozone_border_0_purple_rain,                           /* cursor_border_0 */
   ozone_border_1_purple_rain,                           /* cursor_border_1 */

   {0},                                                  /* textures */

   "purple_rain"                                         /* name */
};

static ozone_theme_t ozone_theme_selenium = {
   /* Background color */
   COLOR_HEX_TO_FLOAT(0x2a2a2a, 1.0f),                   /* background */
   ozone_background_libretro_running_selenium,     /* background_libretro_running */

   /* Float colors for quads and icons */
   COLOR_HEX_TO_FLOAT(0x6c6c6c, 1.0f),                   /* header_footer_separator */
   COLOR_HEX_TO_FLOAT(0xa6a6a6, 1.0f),                   /* text */
   COLOR_HEX_TO_FLOAT(0x566646, 1.0f),                   /* selection */
   COLOR_HEX_TO_FLOAT(0x91a666, 1.0f),                   /* selection_border */
   COLOR_HEX_TO_FLOAT(0x666666, 1.0f),                   /* entries_border */
   COLOR_HEX_TO_FLOAT(0xa6a6a6, 1.0f),                   /* entries_icon */
   COLOR_HEX_TO_FLOAT(0x91a666, 1.0f),                   /* text_selected */
   COLOR_HEX_TO_FLOAT(0x1a1a1a, 1.0f),                   /* message_background */

   /* RGBA colors for text */
   0xA6A6A6FF,                                           /* text_rgba */
   0xA6A6A6FF,                                           /* text_sidebar_rgba */
   0x91a666FF,                                           /* text_selected_rgba */
   0x666666FF,                                           /* text_sublabel_rgba */

   /* Screensaver 'tint' (RGB24) */
   0x1a1a1a,                                             /* screensaver_tint */

   /* Sidebar color */
   ozone_sidebar_background_selenium,              /* sidebar_background */
   ozone_sidebar_gradient_top_selenium,            /* sidebar_top_gradient */
   ozone_sidebar_gradient_bottom_selenium,         /* sidebar_bottom_gradient */

   /* Fancy cursor colors */
   ozone_border_0_selenium,                        /* cursor_border_0 */
   ozone_border_1_selenium,                        /* cursor_border_1 */

   {0},                                                  /* textures */

   /* No theme assets */
   NULL,                                            /* name */
};

static ozone_theme_t *ozone_themes[] = {
   &ozone_theme_light,
   &ozone_theme_dark,
   &ozone_theme_nord,
   &ozone_theme_gruvbox_dark,
   &ozone_theme_boysenberry,
   &ozone_theme_hacking_the_kernel,
   &ozone_theme_twilight_zone,
   &ozone_theme_dracula,
   &ozone_theme_solarized_dark,
   &ozone_theme_solarized_light,
   &ozone_theme_gray_dark,
   &ozone_theme_gray_light,
   &ozone_theme_purple_rain,
   &ozone_theme_selenium,
};

/* TODO/FIXME - global variables referenced outside */
static unsigned ozone_last_color_theme      = 0;
static ozone_theme_t *ozone_default_theme   = &ozone_theme_dark; /* also used as a tag for cursor animation */
/* Enable runtime configuration of framebuffer opacity */
static float ozone_last_framebuffer_opacity = -1.0f;

/* Forward declarations */
static void ozone_animation_list_alpha(ozone_handle_t *ozone, bool fade_in);
static void ozone_cursor_animation_cb(void *userdata);
static void ozone_selection_changed(ozone_handle_t *ozone, bool allow_animation);
static void ozone_unload_thumbnail_textures(void *data);
extern int action_switch_thumbnail(const char *path, const char *label, unsigned type, size_t idx);

static INLINE uint8_t ozone_count_lines(const char *str)
{
   unsigned c     = 0;
   uint8_t lines  = 1;

   for (c = 0; str[c]; c++)
      lines += (str[c] == '\n');
   return lines;
}

static void ozone_animate_cursor(ozone_handle_t *ozone,
      float *dst,
      float *target)
{
   int i;
   gfx_animation_ctx_entry_t entry;

   entry.easing_enum = OZONE_EASING_XY;
   entry.tag         = (uintptr_t)&ozone_default_theme;
   entry.duration    = ANIMATION_CURSOR_PULSE;
   entry.userdata    = ozone;

   for (i = 0; i < 16; i++)
   {
      if (i == 3 || i == 7 || i == 11 || i == 15)
         continue;

      if (i == 14)
         entry.cb = ozone_cursor_animation_cb;
      else
         entry.cb = NULL;

      entry.subject        = &dst[i];
      entry.target_value   = target[i];

      gfx_animation_push(&entry);
   }
}

static void ozone_cursor_animation_cb(void *userdata)
{
   float *target         = NULL;
   ozone_handle_t *ozone = (ozone_handle_t*)userdata;

   switch (ozone->theme_dynamic_cursor_state)
   {
      case 0:
         target = ozone->theme->cursor_border_1;
         break;
      case 1:
         target = ozone->theme->cursor_border_0;
         break;
   }

   ozone->theme_dynamic_cursor_state = (ozone->theme_dynamic_cursor_state + 1) % 2;

   ozone_animate_cursor(ozone, ozone->theme_dynamic.cursor_border, target);
}

static void ozone_restart_cursor_animation(ozone_handle_t *ozone)
{
   uintptr_t tag = (uintptr_t)&ozone_default_theme;

   ozone->theme_dynamic_cursor_state = 1;
   memcpy(ozone->theme_dynamic.cursor_border,
         ozone->theme->cursor_border_0,
         sizeof(ozone->theme_dynamic.cursor_border));
   gfx_animation_kill_by_tag(&tag);

   ozone_animate_cursor(ozone,
         ozone->theme_dynamic.cursor_border,
         ozone->theme->cursor_border_1);
}

static void ozone_set_color_theme(
      ozone_handle_t *ozone,
      unsigned color_theme)
{
   ozone_theme_t *theme = ozone_default_theme;

   switch (color_theme)
   {
      case OZONE_COLOR_THEME_BASIC_WHITE:
         theme = &ozone_theme_light;
         break;
      case OZONE_COLOR_THEME_BASIC_BLACK:
         theme = &ozone_theme_dark;
         break;
      case OZONE_COLOR_THEME_NORD:
         theme = &ozone_theme_nord;
         break;
      case OZONE_COLOR_THEME_GRUVBOX_DARK:
         theme = &ozone_theme_gruvbox_dark;
         break;
      case OZONE_COLOR_THEME_BOYSENBERRY:
         theme = &ozone_theme_boysenberry;
         break;
      case OZONE_COLOR_THEME_HACKING_THE_KERNEL:
         theme = &ozone_theme_hacking_the_kernel;
         break;
      case OZONE_COLOR_THEME_TWILIGHT_ZONE:
         theme = &ozone_theme_twilight_zone;
         break;
      case OZONE_COLOR_THEME_DRACULA:
         theme = &ozone_theme_dracula;
         break;
      case OZONE_COLOR_THEME_SELENIUM:
         theme = &ozone_theme_selenium;
         break;
      case OZONE_COLOR_THEME_SOLARIZED_DARK:
         theme = &ozone_theme_solarized_dark;
         break;
      case OZONE_COLOR_THEME_SOLARIZED_LIGHT:
         theme = &ozone_theme_solarized_light;
         break;
      case OZONE_COLOR_THEME_GRAY_DARK:
         theme = &ozone_theme_gray_dark;
         break;
      case OZONE_COLOR_THEME_GRAY_LIGHT:
         theme = &ozone_theme_gray_light;
         break;
      case OZONE_COLOR_THEME_PURPLE_RAIN:
         theme = &ozone_theme_purple_rain;
         break;
      default:
         break;
   }

   ozone->theme = theme;

   memcpy(ozone->theme_dynamic.selection_border,
         ozone->theme->selection_border,
         sizeof(ozone->theme_dynamic.selection_border));
   memcpy(ozone->theme_dynamic.selection,
         ozone->theme->selection,
         sizeof(ozone->theme_dynamic.selection));
   memcpy(ozone->theme_dynamic.entries_border,
         ozone->theme->entries_border,
         sizeof(ozone->theme_dynamic.entries_border));
   memcpy(ozone->theme_dynamic.entries_icon,
         ozone->theme->entries_icon,
         sizeof(ozone->theme_dynamic.entries_icon));
   memcpy(ozone->theme_dynamic.entries_checkmark,
         ozone->pure_white,
         sizeof(ozone->theme_dynamic.entries_checkmark));
   memcpy(ozone->theme_dynamic.cursor_alpha,
         ozone->pure_white,
         sizeof(ozone->theme_dynamic.cursor_alpha));
   memcpy(ozone->theme_dynamic.message_background,
         ozone->theme->message_background,
         sizeof(ozone->theme_dynamic.message_background));

   if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS || ozone->flags2 & OZONE_FLAG2_IGNORE_MISSING_ASSETS)
      ozone_restart_cursor_animation(ozone);

   ozone_last_color_theme = color_theme;
}

static unsigned ozone_get_system_theme(void)
{
#ifdef HAVE_LIBNX
   if (R_SUCCEEDED(setsysInitialize()))
   {
      ColorSetId theme;
      unsigned ret = 0;
      setsysGetColorSetId(&theme);
      if (theme == ColorSetId_Dark)
         ret = 1;
      setsysExit();
      return ret;
   }
   return 0;
#else
   return DEFAULT_OZONE_COLOR_THEME;
#endif
}

/* Running background gradient disabled for now due to
 * rather jarring steps with certain themes because they
 * already have background gradient */
static void ozone_set_background_running_opacity(
      ozone_handle_t *ozone,
      float framebuffer_opacity)
{
#if USE_BG_GRADIENT
   static float background_running_alpha_top    = 1.0f;
   static float background_running_alpha_bottom = 0.75f;
   float *background                            =
         ozone->theme->background_libretro_running;

   /* When content is running, background is a
    * gradient that from top to bottom transitions
    * from maximum to minimum opacity
    * > RetroArch default 'framebuffer_opacity'
    *   is 0.900. At this setting:
    *   - Background top has an alpha of 1.0
    *   - Background bottom has an alpha of 0.75 */
   background_running_alpha_top                 = framebuffer_opacity / 0.9f;
   background_running_alpha_top                 = (background_running_alpha_top > 1.0f)
         ?  1.0f
         : (background_running_alpha_top < 0.0f)
               ?  0.0f
               : background_running_alpha_top;

   background_running_alpha_bottom              = (2.5f * framebuffer_opacity) - 1.5f;
   background_running_alpha_bottom              = (background_running_alpha_bottom > 1.0f)
         ?  1.0f
         : (background_running_alpha_bottom < 0.0f)
               ?  0.0f
               : background_running_alpha_bottom;

   background[11]                               = background_running_alpha_top;
   background[15]                               = background_running_alpha_top;
   background[3]                                = background_running_alpha_bottom;
   background[7]                                = background_running_alpha_bottom;
#else
   float *background                            =
         ozone->theme->background_libretro_running;

   background[11]                               = framebuffer_opacity;
   background[15]                               = framebuffer_opacity;
   background[3]                                = framebuffer_opacity;
   background[7]                                = framebuffer_opacity;
#endif

   ozone_last_framebuffer_opacity               = framebuffer_opacity;

   /* Set sidebar background to half opacity if transparent */
   if (ozone->theme->sidebar_background[3] > 0)
   {
      gfx_display_set_alpha(ozone->theme->sidebar_top_gradient, 0.5f);
      gfx_display_set_alpha(ozone->theme->sidebar_background, 0.5f);
      gfx_display_set_alpha(ozone->theme->sidebar_bottom_gradient, 0.5f);
   }
}

static uintptr_t ozone_entries_icon_get_texture(
      ozone_handle_t *ozone,
      enum msg_hash_enums enum_idx,
      const char *enum_path,
      const char *enum_label,
      unsigned type,
      bool active)
{
   switch (enum_idx)
   {
      case MENU_ENUM_LABEL_LOAD_DISC:
      case MENU_ENUM_LABEL_DUMP_DISC:
#ifdef HAVE_LAKKA
      case MENU_ENUM_LABEL_EJECT_DISC:
#endif
      case MENU_ENUM_LABEL_DISC_INFORMATION:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DISC];
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_OPTIONS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE_OPTIONS];
      case MENU_ENUM_LABEL_ADD_TO_FAVORITES:
      case MENU_ENUM_LABEL_ADD_TO_FAVORITES_PLAYLIST:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_ADD_TO_FAVORITES:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ADD_FAVORITE];
      case MENU_ENUM_LABEL_ADD_TO_PLAYLIST:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_ADD_TO_PLAYLIST:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_PLAYLIST];
      case MENU_ENUM_LABEL_CREATE_NEW_PLAYLIST:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ADD];
      case MENU_ENUM_LABEL_PARENT_DIRECTORY:
      case MENU_ENUM_LABEL_UNDO_LOAD_STATE:
      case MENU_ENUM_LABEL_UNDO_SAVE_STATE:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_UNDO_SAVE_LOAD_STATE:
      case MENU_ENUM_LABEL_RESET_CORE_ASSOCIATION:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_RESET_CORE_ASSOCIATION:
      case MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_UNDO];
      case MENU_ENUM_LABEL_CORE_INPUT_REMAPPING_OPTIONS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_REMAPPING_OPTIONS];
      case MENU_ENUM_LABEL_CORE_CHEAT_OPTIONS:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_CHEATS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CHEAT_OPTIONS];
      case MENU_ENUM_LABEL_DISK_OPTIONS:
      case MENU_ENUM_LABEL_DISK_TRAY_EJECT:
      case MENU_ENUM_LABEL_DISK_TRAY_INSERT:
      case MENU_ENUM_LABEL_DISK_IMAGE_APPEND:
      case MENU_ENUM_LABEL_DISK_INDEX:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DISK_OPTIONS];
      case MENU_ENUM_LABEL_SHADER_OPTIONS:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_SHADERS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SHADER_OPTIONS];
      case MENU_ENUM_LABEL_ACHIEVEMENT_LIST:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENT_LIST];
      case MENU_ENUM_LABEL_ACHIEVEMENT_LIST_HARDCORE:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENT_LIST];
      case MENU_ENUM_LABEL_STATE_SLOT:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING];
      case MENU_ENUM_LABEL_REPLAY_SLOT:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING];
      case MENU_ENUM_LABEL_SAVESTATE_LIST:
      case MENU_ENUM_LABEL_SAVE_STATE:
      case MENU_ENUM_LABEL_CORE_CREATE_BACKUP:
      case MENU_ENUM_LABEL_GAME_SPECIFIC_CORE_OPTIONS_CREATE:
      case MENU_ENUM_LABEL_FOLDER_SPECIFIC_CORE_OPTIONS_CREATE:
      case MENU_ENUM_LABEL_REMAP_FILE_SAVE_CORE:
      case MENU_ENUM_LABEL_REMAP_FILE_SAVE_CONTENT_DIR:
      case MENU_ENUM_LABEL_REMAP_FILE_SAVE_GAME:
      case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CORE:
      case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CONTENT_DIR:
      case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_GAME:
      case MENU_ENUM_LABEL_NETWORK_ON_DEMAND_THUMBNAILS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SAVESTATE];
      case MENU_ENUM_LABEL_LOAD_STATE:
      case MENU_ENUM_LABEL_CORE_RESTORE_BACKUP_LIST:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LOADSTATE];
      case MENU_ENUM_LABEL_TAKE_SCREENSHOT:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_TAKE_SCREENSHOT:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SCREENSHOT];
      case MENU_ENUM_LABEL_CORE_LIST_UNLOAD:
      case MENU_ENUM_LABEL_DELETE_ENTRY:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_CLOSE_CONTENT:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOSE];
      case MENU_ENUM_LABEL_RESTART_CONTENT:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_RESTART_CONTENT:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD];
      case MENU_ENUM_LABEL_RENAME_ENTRY:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RENAME];
      case MENU_ENUM_LABEL_RESUME_CONTENT:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_RESUME_CONTENT:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RESUME];
      case MENU_ENUM_LABEL_FAVORITES:
      case MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST:
      case MENU_ENUM_LABEL_DOWNLOAD_CORE_CONTENT_DIRS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FOLDER];
      case MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RDB];

      /* Menu collection submenus */
      case MENU_ENUM_LABEL_PLAYLISTS_TAB:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_PLAYLIST];
      case MENU_ENUM_LABEL_LOAD_CONTENT_HISTORY:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_HISTORY];
      case MENU_ENUM_LABEL_GOTO_FAVORITES:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FAVORITES];
#ifdef HAVE_IMAGEVIEWER
      case MENU_ENUM_LABEL_GOTO_IMAGES:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_IMAGES];
#endif
      case MENU_ENUM_LABEL_GOTO_MUSIC:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MUSICS];
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
      case MENU_ENUM_LABEL_GOTO_VIDEO:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MOVIES];
#endif
      case MENU_ENUM_LABEL_GOTO_EXPLORE:
         if (!string_is_equal(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_EXPLORE)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CURSOR];
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RDB];
      case MENU_ENUM_LABEL_GOTO_CONTENTLESS_CORES:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE];

      /* Menu icons */
      case MENU_ENUM_LABEL_CONTENT_SETTINGS:
      case MENU_ENUM_LABEL_UPDATE_ASSETS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_QUICKMENU];
      case MENU_ENUM_LABEL_START_CORE:
      case MENU_ENUM_LABEL_CHEAT_START_OR_CONT:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RUN];
      case MENU_ENUM_LABEL_CORE_LIST:
      case MENU_ENUM_LABEL_SIDELOAD_CORE_LIST:
      case MENU_ENUM_LABEL_CORE_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_CORE:
      case MENU_ENUM_LABEL_CORE_UPDATER_LIST:
      case MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES:
      case MENU_ENUM_LABEL_SWITCH_INSTALLED_CORES_PFD:
      case MENU_ENUM_LABEL_SET_CORE_ASSOCIATION:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_SET_CORE_ASSOCIATION:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE];
      case MENU_ENUM_LABEL_CORE_INFORMATION:
            if (!string_is_equal(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFORMATION)))
               return 0;
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE];
      case MENU_ENUM_LABEL_CORE_INFO_ENTRY:
            if (strstr(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MISSING_REQUIRED)))
               return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOSE];
            else if (strstr(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MISSING_OPTIONAL)))
               return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INFO];
            else if (strstr(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PRESENT_REQUIRED))
                  || strstr(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PRESENT_OPTIONAL)))
               return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CHECKMARK];
            return 0;
      case MENU_ENUM_LABEL_LOAD_CONTENT_LIST:
      case MENU_ENUM_LABEL_SUBSYSTEM_SETTINGS:
      case MENU_ENUM_LABEL_SCAN_FILE:
      case MENU_ENUM_LABEL_DOWNLOAD_CORE_SYSTEM_FILES:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FILE];
      case MENU_ENUM_LABEL_ONLINE_UPDATER:
      case MENU_ENUM_LABEL_UPDATER_SETTINGS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_UPDATER];
      case MENU_ENUM_LABEL_UPDATE_LAKKA:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MAIN_MENU];
      case MENU_ENUM_LABEL_UPDATE_CHEATS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CHEAT_OPTIONS];
      case MENU_ENUM_LABEL_PL_THUMBNAILS_UPDATER_LIST:
      case MENU_ENUM_LABEL_DOWNLOAD_PL_ENTRY_THUMBNAILS:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_DOWNLOAD_THUMBNAILS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_IMAGE];
      case MENU_ENUM_LABEL_UPDATE_OVERLAYS:
      case MENU_ENUM_LABEL_ONSCREEN_OVERLAY_SETTINGS:
      case MENU_ENUM_LABEL_CONTENT_SHOW_OVERLAYS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_OVERLAY];
      case MENU_ENUM_LABEL_OVERLAY_LIGHTGUN_SETTINGS:
      case MENU_ENUM_LABEL_OVERLAY_MOUSE_SETTINGS:
      case MENU_ENUM_LABEL_OSK_OVERLAY_SETTINGS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING];
      case MENU_ENUM_LABEL_UPDATE_CG_SHADERS:
      case MENU_ENUM_LABEL_UPDATE_GLSL_SHADERS:
      case MENU_ENUM_LABEL_UPDATE_SLANG_SHADERS:
      case MENU_ENUM_LABEL_AUTO_SHADERS_ENABLE:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PARAMETERS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SHADER_OPTIONS];
      case MENU_ENUM_LABEL_INFORMATION:
      case MENU_ENUM_LABEL_INFORMATION_LIST:
      case MENU_ENUM_LABEL_SYSTEM_INFORMATION:
      case MENU_ENUM_LABEL_UPDATE_CORE_INFO_FILES:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_INFORMATION:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INFO];
      case MENU_ENUM_LABEL_UPDATE_DATABASES:
      case MENU_ENUM_LABEL_DATABASE_MANAGER_LIST:
      case MENU_ENUM_LABEL_RDB_ENTRY_DETAIL:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RDB];
      case MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE];
      case MENU_ENUM_LABEL_CURSOR_MANAGER_LIST:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CURSOR];
      case MENU_ENUM_LABEL_HELP_LIST:
      case MENU_ENUM_LABEL_HELP_CONTROLS:
      case MENU_ENUM_LABEL_HELP_LOADING_CONTENT:
      case MENU_ENUM_LABEL_HELP_SCANNING_CONTENT:
      case MENU_ENUM_LABEL_HELP_WHAT_IS_A_CORE:
      case MENU_ENUM_LABEL_HELP_CHANGE_VIRTUAL_GAMEPAD:
      case MENU_ENUM_LABEL_HELP_AUDIO_VIDEO_TROUBLESHOOTING:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_HELP];
      case MENU_ENUM_LABEL_QUIT_RETROARCH:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_EXIT];

      /* Settings icons */
      case MENU_ENUM_LABEL_DRIVER_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_DRIVERS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DRIVERS];
      case MENU_ENUM_LABEL_VIDEO_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_VIDEO:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_VIDEO];
      case MENU_ENUM_LABEL_AUDIO_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_AUDIO:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_AUDIO];
      case MENU_ENUM_LABEL_AUDIO_MIXER_SETTINGS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MIXER];
      case MENU_ENUM_LABEL_CONFIGURATION_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_CONFIGURATION:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_FILE_BROWSER:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING];
      case MENU_ENUM_LABEL_GLOBAL_CORE_OPTIONS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_AI_SERVICE:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_ACCESSIBILITY:
      case MENU_ENUM_LABEL_PLAYLIST_MANAGER_DEFAULT_CORE:
      case MENU_ENUM_LABEL_PLAYLIST_MANAGER_LABEL_DISPLAY_MODE:
      case MENU_ENUM_LABEL_PLAYLIST_MANAGER_SORT_MODE:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SUBSETTING];
      case MENU_ENUM_LABEL_PLAYLIST_MANAGER_LEFT_THUMBNAIL_MODE:
      case MENU_ENUM_LABEL_PLAYLIST_MANAGER_RIGHT_THUMBNAIL_MODE:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SUBSETTING];
      case MENU_ENUM_LABEL_INPUT_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_INPUT:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_CONTROLS:
      case MENU_ENUM_LABEL_UPDATE_AUTOCONFIG_PROFILES:
      case MENU_ENUM_LABEL_INPUT_RETROPAD_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_1_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_2_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_3_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_4_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_5_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_6_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_7_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_8_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_9_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_10_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_11_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_12_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_13_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_14_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_15_BINDS:
      case MENU_ENUM_LABEL_INPUT_USER_16_BINDS:
      case MENU_ENUM_LABEL_START_NET_RETROPAD:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SETTINGS];
      case MENU_ENUM_LABEL_INPUT_TURBO_FIRE_SETTINGS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_TURBO];
      case MENU_ENUM_LABEL_LATENCY_SETTINGS:
      case MENU_ENUM_LABEL_CONTENT_SHOW_LATENCY:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_LATENCY:
      case MENU_ENUM_LABEL_MENU_THROTTLE_FRAMERATE:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LATENCY];
      case MENU_ENUM_LABEL_SAVING_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_SAVING:
      case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG:
      case MENU_ENUM_LABEL_SAVE_NEW_CONFIG:
      case MENU_ENUM_LABEL_SAVE_AS_CONFIG:
      case MENU_ENUM_LABEL_SAVE_MAIN_CONFIG:
      case MENU_ENUM_LABEL_CONFIG_SAVE_ON_EXIT:
      case MENU_ENUM_LABEL_REMAP_SAVE_ON_EXIT:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_CURRENT:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_AS:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_GLOBAL:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_CORE:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_PARENT:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_GAME:
      case MENU_ENUM_LABEL_CHEAT_FILE_SAVE_AS:
      case MENU_ENUM_LABEL_OVERRIDE_FILE_SAVE_AS:
      case MENU_ENUM_LABEL_REMAP_FILE_SAVE_AS:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_SAVESTATE_SUBMENU:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_SAVE_LOAD_STATE:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_REPLAY:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SAVING];
      case MENU_ENUM_LABEL_LOGGING_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_LOGGING:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LOG];
      case MENU_ENUM_LABEL_FASTFORWARD_RATIO:
      case MENU_ENUM_LABEL_FRAME_THROTTLE_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_FRAME_THROTTLE:
      case MENU_ENUM_LABEL_FASTFORWARD_FRAMESKIP:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FRAMESKIP];
      case MENU_ENUM_LABEL_QUICK_MENU_START_RECORDING:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_START_RECORDING:
      case MENU_ENUM_LABEL_RECORDING_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_RECORDING:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RECORD];
      case MENU_ENUM_LABEL_QUICK_MENU_START_STREAMING:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_START_STREAMING:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_STREAM];
      case MENU_ENUM_LABEL_QUICK_MENU_STOP_STREAMING:
      case MENU_ENUM_LABEL_QUICK_MENU_STOP_RECORDING:
      case MENU_ENUM_LABEL_CHEAT_DELETE:
      case MENU_ENUM_LABEL_CHEAT_DELETE_ALL:
      case MENU_ENUM_LABEL_CORE_DELETE:
      case MENU_ENUM_LABEL_DELETE_PLAYLIST:
      case MENU_ENUM_LABEL_CORE_DELETE_BACKUP_LIST:
      case MENU_ENUM_LABEL_VIDEO_FILTER_REMOVE:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_REMOVE:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_REMOVE_GLOBAL:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_REMOVE_CORE:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_REMOVE_PARENT:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_REMOVE_GAME:
      case MENU_ENUM_LABEL_AUDIO_DSP_PLUGIN_REMOVE:
      case MENU_ENUM_LABEL_GAME_SPECIFIC_CORE_OPTIONS_REMOVE:
      case MENU_ENUM_LABEL_FOLDER_SPECIFIC_CORE_OPTIONS_REMOVE:
      case MENU_ENUM_LABEL_REMAP_FILE_REMOVE_CORE:
      case MENU_ENUM_LABEL_REMAP_FILE_REMOVE_CONTENT_DIR:
      case MENU_ENUM_LABEL_REMAP_FILE_REMOVE_GAME:
      case MENU_ENUM_LABEL_REMOVE_CURRENT_CONFIG_OVERRIDE_CORE:
      case MENU_ENUM_LABEL_REMOVE_CURRENT_CONFIG_OVERRIDE_CONTENT_DIR:
      case MENU_ENUM_LABEL_REMOVE_CURRENT_CONFIG_OVERRIDE_GAME:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOSE];
      case MENU_ENUM_LABEL_CORE_OPTIONS_RESET:
      case MENU_ENUM_LABEL_REMAP_FILE_RESET:
      case MENU_ENUM_LABEL_OVERRIDE_UNLOAD:
      case MENU_ENUM_LABEL_MENU_DISABLE_KIOSK_MODE:
      case MENU_ENUM_LABEL_XMB_MAIN_MENU_ENABLE_SETTINGS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_UNDO];
      case MENU_ENUM_LABEL_CORE_OPTIONS_FLUSH:
      case MENU_ENUM_LABEL_REMAP_FILE_FLUSH:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_CORE_OPTIONS_FLUSH:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FILE];
      case MENU_ENUM_LABEL_CORE_LOCK:
      case MENU_ENUM_LABEL_CORE_SET_STANDALONE_EXEMPT:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE];
      case MENU_ENUM_LABEL_ONSCREEN_DISPLAY_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_ONSCREEN_DISPLAY:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_OSD];
      case MENU_ENUM_LABEL_SHOW_WIMP:
      case MENU_ENUM_LABEL_USER_INTERFACE_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_USER_INTERFACE:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_UI];
#if defined(HAVE_LIBNX)
      case MENU_ENUM_LABEL_SWITCH_CPU_PROFILE:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_POWER];
#endif
      case MENU_ENUM_LABEL_POWER_MANAGEMENT_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_POWER_MANAGEMENT:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_POWER];
      case MENU_ENUM_LABEL_RETRO_ACHIEVEMENTS_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_ACHIEVEMENTS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENTS];
      case MENU_ENUM_LABEL_NETWORK_INFORMATION:
      case MENU_ENUM_LABEL_NETWORK_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_NETWORK:
      case MENU_ENUM_LABEL_WIFI_SETTINGS:
      case MENU_ENUM_LABEL_NETWORK_INFO_ENTRY:
      case MENU_ENUM_LABEL_NETWORK_HOSTING_SETTINGS:
      case MENU_ENUM_LABEL_NETPLAY_LOBBY_FILTERS:
      case MENU_ENUM_LABEL_NETPLAY_KICK:
      case MENU_ENUM_LABEL_NETPLAY_BAN:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_NETWORK];
      case MENU_ENUM_LABEL_BLUETOOTH_SETTINGS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_BLUETOOTH];
      case MENU_ENUM_LABEL_PLAYLIST_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_PLAYLISTS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_PLAYLIST];
      case MENU_ENUM_LABEL_USER_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_USER:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_USER];
      case MENU_ENUM_LABEL_ADD_CONTENT_LIST:
            if (ozone->depth > 1)
               return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ADD];
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MENU_ADD];
      case MENU_ENUM_LABEL_DIRECTORY_SETTINGS:
      case MENU_ENUM_LABEL_SETTINGS_SHOW_DIRECTORY:
      case MENU_ENUM_LABEL_SCAN_DIRECTORY:
      case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FOLDER];
      case MENU_ENUM_LABEL_PRIVACY_SETTINGS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_PRIVACY];
      case MENU_ENUM_LABEL_REWIND_SETTINGS:
      case MENU_ENUM_LABEL_CONTENT_SHOW_REWIND:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_REWIND];
      case MENU_ENUM_LABEL_QUICK_MENU_OVERRIDE_OPTIONS:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_SAVE_CORE_OVERRIDES:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_SAVE_CONTENT_DIR_OVERRIDES:
      case MENU_ENUM_LABEL_QUICK_MENU_SHOW_SAVE_GAME_OVERRIDES:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_OVERRIDE];
      case MENU_ENUM_LABEL_ONSCREEN_NOTIFICATIONS_SETTINGS:
      case MENU_ENUM_LABEL_CHEEVOS_APPEARANCE_SETTINGS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_NOTIFICATIONS];
#ifdef HAVE_NETWORKING
      case MENU_ENUM_LABEL_NETPLAY:
            return ozone->tab_textures[OZONE_TAB_TEXTURE_NETWORK];
      case MENU_ENUM_LABEL_NETPLAY_ENABLE_HOST:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RUN];
      case MENU_ENUM_LABEL_NETPLAY_DISCONNECT:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOSE];
      case MENU_ENUM_LABEL_NETPLAY_ENABLE_CLIENT:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ROOM];
      case MENU_ENUM_LABEL_NETPLAY_REFRESH_ROOMS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD];
#ifdef HAVE_NETPLAYDISCOVERY
      case MENU_ENUM_LABEL_NETPLAY_REFRESH_LAN:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD];
#endif
#endif
      case MENU_ENUM_LABEL_REBOOT:
      case MENU_ENUM_LABEL_RESET_TO_DEFAULT_CONFIG:
      case MENU_ENUM_LABEL_CHEAT_COPY_AFTER:
      case MENU_ENUM_LABEL_CHEAT_COPY_BEFORE:
      case MENU_ENUM_LABEL_CHEAT_RELOAD_CHEATS:
      case MENU_ENUM_LABEL_RESTART_RETROARCH:
      case MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_NOW:
      case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_SETTINGS:
      case MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST:
      case MENU_ENUM_LABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST:
      case MENU_ENUM_LABEL_CLOUD_SYNC_SETTINGS:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD];
      case MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE:
            /* Only show icon in Throttle settings */
            if (ozone->depth < 3)
               return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD];
            break;
      case MENU_ENUM_LABEL_SHUTDOWN:
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SHUTDOWN];
      case MENU_ENUM_LABEL_CONFIGURATIONS:
      case MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS:
      case MENU_ENUM_LABEL_REMAP_FILE_LOAD:
      case MENU_ENUM_LABEL_OVERRIDE_FILE_LOAD:
      case MENU_ENUM_LABEL_AUTO_OVERRIDES_ENABLE:
      case MENU_ENUM_LABEL_AUTO_REMAPS_ENABLE:
      case MENU_ENUM_LABEL_INITIAL_DISK_CHANGE_ENABLE:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_PREPEND:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_APPEND:
      case MENU_ENUM_LABEL_CHEAT_FILE_LOAD:
      case MENU_ENUM_LABEL_CHEAT_FILE_LOAD_APPEND:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LOADSTATE];
      case MENU_ENUM_LABEL_CHEAT_APPLY_CHANGES:
      case MENU_ENUM_LABEL_SHADER_APPLY_CHANGES:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CHECKMARK];
      case MENU_ENUM_LABEL_CHEAT_ADD_NEW_AFTER:
      case MENU_ENUM_LABEL_CHEAT_ADD_NEW_BEFORE:
      case MENU_ENUM_LABEL_CHEAT_ADD_NEW_TOP:
      case MENU_ENUM_LABEL_CHEAT_ADD_NEW_BOTTOM:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MENU_ADD];
      case MENU_ENUM_LABEL_CHEAT_APPLY_AFTER_TOGGLE:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MENU_APPLY_TOGGLE];
      case MENU_ENUM_LABEL_CHEAT_APPLY_AFTER_LOAD:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MENU_APPLY_COG];
      case MENU_ENUM_LABEL_SLOWMOTION_RATIO:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RESUME];
      case MENU_ENUM_LABEL_START_VIDEO_PROCESSOR:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MOVIE];
#ifdef HAVE_LIBRETRODB
      case MENU_ENUM_LABEL_EXPLORE_ITEM:
      {
         uintptr_t icon = menu_explore_get_entry_icon(type);
         if (icon)
            return icon;
         else if (string_is_equal(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_SAVE_VIEW)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SAVING];
         else if (string_is_equal(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_DELETE_VIEW)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOSE];
         else if (type != FILE_TYPE_RDB)
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CURSOR];
         break;
      }
#endif
      case MENU_ENUM_LABEL_CONTENTLESS_CORE:
      {
         uintptr_t icon = menu_contentless_cores_get_entry_icon(enum_label);
         if (icon)
            return icon;
         break;
      }
      /* No icon */
      case MENU_ENUM_LABEL_SCREEN_RESOLUTION:
         return 0;
      default:
         break;
   }

   switch (type)
   {
      case MENU_SET_CDROM_INFO:
      case MENU_SET_CDROM_LIST:
#ifdef HAVE_LAKKA
      case MENU_SET_EJECT_DISC:
#endif
      case MENU_SET_LOAD_CDROM_LIST:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DISC];
      case FILE_TYPE_DIRECTORY:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FOLDER];
      case FILE_TYPE_PLAIN:
      case FILE_TYPE_IN_CARCHIVE:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FILE];
      case FILE_TYPE_RPL_ENTRY:
      case FILE_TYPE_PLAYLIST_COLLECTION:
         switch (ozone->tabs[ozone->categories_selection_ptr])
         {
            case OZONE_SYSTEM_TAB_MAIN:
               if (string_is_equal(ozone->title, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_IMAGES_TAB)))
                  return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_IMAGE];
               else if (string_is_equal(ozone->title, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MUSIC_TAB)))
                  return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MUSIC];
               else if (string_is_equal(ozone->title, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_TAB)))
                  return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MOVIE];
               break;
#ifdef HAVE_IMAGEVIEWER
            case OZONE_SYSTEM_TAB_IMAGES:
               return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_IMAGE];
#endif
            case OZONE_SYSTEM_TAB_MUSIC:
               return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MUSIC];
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
            case OZONE_SYSTEM_TAB_VIDEO:
               return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MOVIE];
#endif
            default:
               break;
         }
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FILE];
      case FILE_TYPE_SHADER:
      case FILE_TYPE_SHADER_PRESET:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SHADER_OPTIONS];
      case FILE_TYPE_CARCHIVE:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ZIP];
      case FILE_TYPE_IMAGE:
      case FILE_TYPE_IMAGEVIEWER:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_IMAGE];
      case FILE_TYPE_MUSIC:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MUSIC];
      case FILE_TYPE_MOVIE:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MOVIE];
      case FILE_TYPE_CORE:
      case FILE_TYPE_DIRECT_LOAD:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE];
      case FILE_TYPE_RDB:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RDB];
      case FILE_TYPE_RDB_ENTRY:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FILE];
      case FILE_TYPE_PLAYLIST_ENTRY:
      case MENU_SETTING_ACTION_RUN:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RUN];
      case MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RESUME];
      case MENU_SETTING_ACTION_CLOSE:
      case MENU_SETTING_ACTION_CLOSE_HORIZONTAL:
      case MENU_SETTING_ACTION_DELETE_ENTRY:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOSE];
      case MENU_SETTING_ACTION_SAVESTATE:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SAVESTATE];
      case MENU_SETTING_ACTION_LOADSTATE:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LOADSTATE];
      case MENU_SETTING_ACTION_PLAYREPLAY:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_PLAYREPLAY];
      case MENU_SETTING_ACTION_RECORDREPLAY:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RECORDREPLAY];
      case MENU_SETTING_ACTION_HALTREPLAY:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_HALTREPLAY];
      case MENU_SETTING_ACTION_CORE_OPTIONS:
         if (string_starts_with(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SETTINGS)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_VIDEO];
         else if (   string_starts_with(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_AUDIO_SETTINGS))
                  || string_starts_with(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SOUND_SETTINGS)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_AUDIO];
         else if (string_starts_with(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_SETTINGS)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SETTINGS];
         else if (string_is_equal(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ONSCREEN_DISPLAY_SETTINGS)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_OSD];
         else if (   string_is_equal(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_LATENCY_SETTINGS))
                  || string_is_equal(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_TIMING_SETTINGS)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LATENCY];
         else if (string_starts_with(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PERFORMANCE_SETTINGS)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FRAMESKIP];
         else if (   string_is_equal(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MEDIA_SETTINGS))
                  || string_is_equal(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_STORAGE_SETTINGS)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RDB];
         else if (   string_is_equal(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_SETTINGS))
                  || string_is_equal(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SPECS_SETTINGS)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DRIVERS];
         else if (strstr(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_HACKS_SETTINGS)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_POWER];
         else if (strstr(enum_path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MAPPING_SETTINGS)))
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_OVERRIDE];
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE_OPTIONS];
      case MENU_SETTING_ACTION_CORE_OPTION_OVERRIDE_LIST:
      case MENU_SETTING_ACTION_REMAP_FILE_MANAGER_LIST:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING];
      case MENU_SETTING_ACTION_CORE_INPUT_REMAPPING_OPTIONS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_REMAPPING_OPTIONS];
      case MENU_SETTING_ACTION_CORE_CHEAT_OPTIONS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CHEAT_OPTIONS];
      case MENU_SETTING_ACTION_CORE_DISK_OPTIONS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DISK_OPTIONS];
      case MENU_SETTING_ACTION_CORE_SHADER_OPTIONS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SHADER_OPTIONS];
      case MENU_SETTING_ACTION_SCREENSHOT:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SCREENSHOT];
      case MENU_SETTING_ACTION_RESET:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD];
      case MENU_SETTING_ACTION_PAUSE_ACHIEVEMENTS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_PAUSE];
      case MENU_INFO_ACHIEVEMENTS_SERVER_UNREACHABLE:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_NETWORK];
      case MENU_SETTING_GROUP:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING];
      case MENU_SET_SCREEN_BRIGHTNESS:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_BRIGHTNESS];
      case MENU_INFO_MESSAGE:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO];
      case MENU_BLUETOOTH:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_BLUETOOTH];
      case MENU_WIFI:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_WIFI];
#ifdef HAVE_NETWORKING
      case MENU_ROOM:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ROOM];
      case MENU_ROOM_LAN:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ROOM_LAN];
      case MENU_ROOM_RELAY:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ROOM_RELAY];
#endif
      case MENU_SETTING_ACTION:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING];
      case MENU_SETTINGS_INPUT_LIBRETRO_DEVICE:
      case MENU_SETTINGS_INPUT_INPUT_REMAP_PORT:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING];
      case MENU_SETTINGS_INPUT_ANALOG_DPAD_MODE:
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_ADC];
   }

#ifdef HAVE_CHEEVOS
   if (     type >= MENU_SETTINGS_CHEEVOS_START
         && type <  MENU_SETTINGS_NETPLAY_ROOMS_START)
   {
      char buffer[64];
      int               index = type - MENU_SETTINGS_CHEEVOS_START;
      uintptr_t badge_texture = rcheevos_menu_get_badge_texture(index);
      if (badge_texture)
         return badge_texture;

      /* No state means it's a header, show info icon */
      if (!rcheevos_menu_get_state(index, buffer, sizeof(buffer)))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INFO];

      /* Placeholder badge image was not found, show generic menu icon */
      return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENTS];
   }
#endif

   if (     type >= MENU_SETTINGS_INPUT_BEGIN
         && type <= MENU_SETTINGS_INPUT_DESC_KBD_END)
   {
      /* This part is only utilized by Input User # Binds */
      unsigned input_id;
      if (type < MENU_SETTINGS_INPUT_DESC_BEGIN)
      {
         input_id = MENU_SETTINGS_INPUT_BEGIN;
         if (type == input_id)
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_ADC];
#ifdef HAVE_LIBNX
         /* Account for the additional split JoyCon option in Input Port # Binds */
         input_id++;
#endif
         if (type >= input_id + 1 && type <= input_id + 3)
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SETTINGS];
         if (type == input_id + 4)
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_MOUSE];
         if (type == input_id + 5)
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BIND_ALL];
         if (type == input_id + 6)
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD];
         if (type == input_id + 7)
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SAVING];
         if (type >= input_id + 32 && type <= input_id + 42)
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LGUN];
         if (type == input_id + 43)
            return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_TURBO];
         /* Align to use the same code of Quickmenu controls */
         input_id = input_id + 8;
      }
      else
      {
         /* Quickmenu controls repeats the same icons for all users */
         if (type < MENU_SETTINGS_INPUT_DESC_KBD_BEGIN)
            input_id = MENU_SETTINGS_INPUT_DESC_BEGIN;
         else
            input_id = MENU_SETTINGS_INPUT_DESC_KBD_BEGIN;
         while (type > (input_id + 23))
            input_id = (input_id + 24);

         /* Human readable bind order */
         if (type < (input_id + RARCH_ANALOG_BIND_LIST_END))
         {
            unsigned index = 0;
            int input_num  = type - input_id;
            for (index = 0; index < ARRAY_SIZE(input_config_bind_order); index++)
            {
               if (input_num == (int)input_config_bind_order[index])
               {
                  type = input_id + index;
                  break;
               }
            }
         }
      }

      /* This is used for both Input Port Binds and Quickmenu controls */
      if (type == input_id)
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_U];
      if (type == (input_id + 1))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_D];
      if (type == (input_id + 2))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_L];
      if (type == (input_id + 3))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_R];
      if (type == (input_id + 4))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_D];
      if (type == (input_id + 5))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_R];
      if (type == (input_id + 6))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_L];
      if (type == (input_id + 7))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_U];
      if (type == (input_id + 8))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SELECT];
      if (type == (input_id + 9))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_START];
      if (type == (input_id + 10))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LB];
      if (type == (input_id + 11))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_RB];
      if (type == (input_id + 12))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LT];
      if (type == (input_id + 13))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_RT];
      if (type == (input_id + 14))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_P];
      if (type == (input_id + 15))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_P];
      if (type == (input_id + 16))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_U];
      if (type == (input_id + 17))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_D];
      if (type == (input_id + 18))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_L];
      if (type == (input_id + 19))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_R];
      if (type == (input_id + 20))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_U];
      if (type == (input_id + 21))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_D];
      if (type == (input_id + 22))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_L];
      if (type == (input_id + 23))
         return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_R];
   }

   if (     type >= MENU_SETTINGS_REMAPPING_PORT_BEGIN
         && type <= MENU_SETTINGS_REMAPPING_PORT_END)
      return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SETTINGS];

   /* No icon by default */
   return 0;
}

static const char *ozone_entries_icon_texture_path(unsigned id)
{
   switch (id)
   {
      case OZONE_ENTRIES_ICONS_TEXTURE_MAIN_MENU:
#if defined(HAVE_LAKKA)
         return "lakka.png";
#else
         return "retroarch.png";
#endif
      case OZONE_ENTRIES_ICONS_TEXTURE_SETTINGS:
         return "settings.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_HISTORY:
         return "history.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_FAVORITES:
         return "favorites.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_ADD_FAVORITE:
         return "add-favorite.png";
#ifdef HAVE_IMAGEVIEWER
      case OZONE_ENTRIES_ICONS_TEXTURE_IMAGES:
         return "images.png";
#endif
      case OZONE_ENTRIES_ICONS_TEXTURE_MUSICS:
         return "musics.png";
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
      case OZONE_ENTRIES_ICONS_TEXTURE_MOVIES:
         return "movies.png";
#endif
      case OZONE_ENTRIES_ICONS_TEXTURE_SETTING:
         return "setting.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_SUBSETTING:
         return "subsetting.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_ARROW:
         return "arrow.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_RUN:
         return "run.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_CLOSE:
         return "close.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_RESUME:
         return "resume.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_CLOCK:
         return "clock.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_FULL:
         return "battery-full.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_CHARGING:
         return "battery-charging.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_80:
         return "battery-80.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_60:
         return "battery-60.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_40:
         return "battery-40.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_20:
         return "battery-20.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_POINTER:
         return "pointer.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_SAVESTATE:
         return "savestate.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_LOADSTATE:
         return "loadstate.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_RECORDREPLAY:
         return "savestate.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_PLAYREPLAY:
         return "loadstate.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_HALTREPLAY:
         return "close.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_UNDO:
         return "undo.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO:
         return "core-infos.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_BLUETOOTH:
         return "bluetooth.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_WIFI:
         return "wifi.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_CORE_OPTIONS:
         return "core-options.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_REMAPPING_OPTIONS:
         return "core-input-remapping-options.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_CHEAT_OPTIONS:
         return "core-cheat-options.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_DISK_OPTIONS:
         return "core-disk-options.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_SHADER_OPTIONS:
         return "core-shader-options.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENT_LIST:
         return "achievement-list.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_SCREENSHOT:
         return "screenshot.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_RELOAD:
         return "reload.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_RENAME:
         return "rename.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_FILE:
         return "file.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_FOLDER:
         return "folder.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_ZIP:
         return "zip.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_FAVORITE:
         return "favorites-content.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_IMAGE:
         return "image.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_MUSIC:
         return "music.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_MOVIE:
         return "movie.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_CORE:
         return "core.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_RDB:
         return "database.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_CURSOR:
         return "cursor.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_SWITCH_ON:
         return "on.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_SWITCH_OFF:
         return "off.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_DISC:
         return "disc.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_ADD:
         return "add.png";
#ifdef HAVE_NETWORKING
      case OZONE_ENTRIES_ICONS_TEXTURE_NETPLAY:
         return "netplay.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_ROOM:
         return "menu_room.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_ROOM_LAN:
         return "menu_room_lan.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_ROOM_RELAY:
         return "menu_room_relay.png";
#endif
      case OZONE_ENTRIES_ICONS_TEXTURE_KEY:
         return "key.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_KEY_HOVER:
         return "key-hover.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_DIALOG_SLICE:
         return "dialog-slice.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENTS:
         return "menu_achievements.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_AUDIO:
         return "menu_audio.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_DRIVERS:
         return "menu_drivers.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_EXIT:
         return "menu_exit.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_FRAMESKIP:
         return "menu_frameskip.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_HELP:
         return "menu_help.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INFO:
         return "menu_info.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SETTINGS:
         return "Libretro - Pad.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_LATENCY:
         return "menu_latency.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_NETWORK:
         return "menu_network.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_POWER:
         return "menu_power.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_RECORD:
         return "menu_record.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_SAVING:
         return "menu_saving.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_UPDATER:
         return "menu_updater.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_VIDEO:
         return "menu_video.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_MIXER:
         return "menu_mixer.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_LOG:
         return "menu_log.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_OSD:
         return "menu_osd.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_UI:
         return "menu_ui.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_USER:
         return "menu_user.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_PRIVACY:
         return "menu_privacy.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_PLAYLIST:
         return "menu_playlist.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_QUICKMENU:
         return "menu_quickmenu.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_REWIND:
         return "menu_rewind.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_OVERLAY:
         return "menu_overlay.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_OVERRIDE:
         return "menu_override.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_NOTIFICATIONS:
         return "menu_notifications.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_STREAM:
         return "menu_stream.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_SHUTDOWN:
         return "menu_shutdown.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_U:
         return "input_DPAD-U.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_D:
         return "input_DPAD-D.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_L:
         return "input_DPAD-L.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_R:
         return "input_DPAD-R.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_U:
         return "input_STCK-U.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_D:
         return "input_STCK-D.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_L:
         return "input_STCK-L.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_R:
         return "input_STCK-R.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_P:
         return "input_STCK-P.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_U:
         return "input_BTN-U.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_D:
         return "input_BTN-D.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_L:
         return "input_BTN-L.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_R:
         return "input_BTN-R.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LB:
         return "input_LB.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_RB:
         return "input_RB.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LT:
         return "input_LT.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_RT:
         return "input_RT.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SELECT:
         return "input_SELECT.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_START:
         return "input_START.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_CHECKMARK:
         return "menu_check.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_MENU_ADD:
         return "menu_add.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_BRIGHTNESS:
         return "menu_brightness.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_PAUSE:
         return "menu_pause.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_MENU_APPLY_TOGGLE:
         return "menu_apply_toggle.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_MENU_APPLY_COG:
         return "menu_apply_cog.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_ADC:
         return "input_ADC.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BIND_ALL:
         return "input_BIND_ALL.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_MOUSE:
         return "input_MOUSE.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LGUN:
         return "input_LGUN.png";
      case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_TURBO:
         return "input_TURBO.png";
   }
   return NULL;
}

static void ozone_unload_theme_textures(ozone_handle_t *ozone)
{
   unsigned i, j;

   for (j = 0; j < ARRAY_SIZE(ozone_themes); j++)
   {
      ozone_theme_t *theme = ozone_themes[j];
      for (i = 0; i < OZONE_THEME_TEXTURE_LAST; i++)
            video_driver_texture_unload(&theme->textures[i]);
   }
}

static bool ozone_reset_theme_textures(ozone_handle_t *ozone)
{
   static const char *OZONE_THEME_TEXTURES_FILES[OZONE_THEME_TEXTURE_LAST] = {
      "switch.png",
      "check.png",

      "cursor_noborder.png",
      "cursor_static.png"
   };
   unsigned i, j;
   char theme_path[NAME_MAX_LENGTH];
   bool result = true;

   for (j = 0; j < ARRAY_SIZE(ozone_themes); j++)
   {
      ozone_theme_t *theme = ozone_themes[j];

      if (!theme->name || j != ozone_last_color_theme)
         continue;

      fill_pathname_join_special(
            theme_path,
            ozone->png_path,
            theme->name,
            sizeof(theme_path)
      );

      for (i = 0; i < OZONE_THEME_TEXTURE_LAST; i++)
      {
         if (!gfx_display_reset_textures_list(
               OZONE_THEME_TEXTURES_FILES[i],
               theme_path,
               &theme->textures[i],
               TEXTURE_FILTER_MIPMAP_LINEAR,
               NULL,
               NULL))
            result = false;
      }
   }

   return result;
}

static void ozone_sidebar_collapse_end(void *userdata)
{
   ozone_handle_t *ozone    = (ozone_handle_t*)userdata;

   ozone->sidebar_collapsed = true;
}

static unsigned ozone_get_sidebar_height(ozone_handle_t *ozone)
{
   unsigned horizontal_list_size = (ozone->show_playlist_tabs)
         ? (unsigned)ozone->horizontal_list.size
         : 0;
   int entries = (int)(ozone->system_tab_end + 1 + horizontal_list_size);
   return entries * ozone->dimensions.sidebar_entry_height
         + (entries - 1) * ozone->dimensions.sidebar_entry_padding_vertical
         + ozone->dimensions.sidebar_padding_vertical
         + (horizontal_list_size > 0
               ? ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px
               : 0);
}

static size_t ozone_get_selected_sidebar_y_position(ozone_handle_t *ozone)
{
   return ozone->categories_selection_ptr * ozone->dimensions.sidebar_entry_height
         + (ozone->categories_selection_ptr - 1) * ozone->dimensions.sidebar_entry_padding_vertical
         + ozone->dimensions.sidebar_padding_vertical
         + (ozone->categories_selection_ptr > ozone->system_tab_end
               ? ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px
               : 0);
}

static float ozone_sidebar_get_scroll_y(
      ozone_handle_t *ozone,
      unsigned video_height)
{
   float scroll_y                          = ozone->animations.scroll_y_sidebar;
   float selected_position_y               = ozone_get_selected_sidebar_y_position(ozone);
   float current_selection_middle_onscreen = ozone->dimensions.header_height
         + ozone->dimensions.spacer_1px
         + ozone->animations.scroll_y_sidebar
         + selected_position_y
         + ozone->dimensions.sidebar_entry_height / 2.0f;
   float bottom_boundary                   = (float)video_height
         - (ozone->dimensions.header_height + ozone->dimensions.spacer_1px)
         - ozone->dimensions.footer_height;
   float entries_middle                    = (float)video_height / 2.0f;
   float entries_height                    = ozone_get_sidebar_height(ozone);

   if (current_selection_middle_onscreen != entries_middle)
      scroll_y = ozone->animations.scroll_y_sidebar - (current_selection_middle_onscreen - entries_middle);

   if (scroll_y + entries_height < bottom_boundary)
      scroll_y = bottom_boundary - entries_height - ozone->dimensions.sidebar_padding_vertical;

   if (scroll_y > 0.0f)
      return 0.0f;
   return scroll_y;
}

static void ozone_draw_icon(
      gfx_display_t *p_disp,
      void *userdata,
      unsigned video_width,
      unsigned video_height,
      unsigned icon_width,
      unsigned icon_height,
      uintptr_t texture,
      float x, float y,
      unsigned width, unsigned height,
      float rotation, float scale_factor,
      float *color,
      math_matrix_4x4 *mymat)
{
   gfx_display_ctx_draw_t draw;
   struct video_coords coords;
   gfx_display_ctx_driver_t
         *dispctx       = p_disp->dispctx;

   coords.vertices      = 4;
   coords.vertex        = NULL;
   coords.tex_coord     = NULL;
   coords.lut_tex_coord = NULL;
   coords.color         = (const float*)color;

   draw.x               = x;
   draw.y               = height - y - icon_height;
   draw.width           = icon_width;
   draw.height          = icon_height;
   draw.scale_factor    = scale_factor;
   draw.rotation        = rotation;
   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 && dispctx->draw)
         dispctx->draw(&draw, userdata, video_width, video_height);
   }
}

static int ozone_wiggle(ozone_handle_t* ozone, float t)
{
   float a = ozone->cursor_wiggle_state.amplitude;
   /* Damped sine wave */
   float w = 0.8f;   /* period */
   float c = 0.35f;  /* damp factor */
   return roundf(a * exp(-(c * t)) * sin(w * t));
}

/* Changes x and y to the current offset of the cursor wiggle animation */
static void ozone_apply_cursor_wiggle_offset(ozone_handle_t *ozone, int *x, size_t *y)
{
   retro_time_t cur_time, t;

   /* Don't do anything if we are not wiggling */
   if (!ozone || (!(ozone->flags2 & OZONE_FLAG2_CURSOR_WIGGLING)))
      return;

   cur_time = menu_driver_get_current_time() / 1000;
   t        = (cur_time - ozone->cursor_wiggle_state.start_time) / 10;

   /* Has the animation ended? */
   if (t >= OZONE_WIGGLE_DURATION)
   {
      ozone->flags2 &= ~OZONE_FLAG2_CURSOR_WIGGLING;
      return;
   }

   /* Change cursor position depending on wiggle direction */
   switch (ozone->cursor_wiggle_state.direction)
   {
      case MENU_ACTION_RIGHT:
         *x += ozone_wiggle(ozone, t);
         break;
      case MENU_ACTION_LEFT:
         *x -= ozone_wiggle(ozone, t);
         break;
      case MENU_ACTION_DOWN:
         *y += ozone_wiggle(ozone, t);
         break;
      case MENU_ACTION_UP:
         *y -= ozone_wiggle(ozone, t);
         break;
      default:
         break;
   }
}

static void ozone_draw_cursor_slice(
      ozone_handle_t *ozone,
      gfx_display_t *p_disp,
      void *userdata,
      unsigned video_width,
      unsigned video_height,
      int x_offset,
      unsigned width,
      unsigned height,
      size_t y,
      float alpha,
      math_matrix_4x4 *mymat)
{
   gfx_display_ctx_driver_t
         *dispctx                  = p_disp->dispctx;
   static float last_alpha         = 0.0f;
   float scale_factor              = ozone->last_scale_factor;
   int slice_x                     = x_offset - (12 + 1) * scale_factor;
   int slice_y                     = (int)y + 8 * scale_factor;
   unsigned slice_new_w            = width + (24 + 1) * scale_factor;
   unsigned slice_new_h            = height + 20 * scale_factor;
   unsigned slice_w                = 80;
   unsigned slice_h                = 80;
   unsigned offset                 = 20;

   if (alpha != last_alpha)
   {
      gfx_display_set_alpha(ozone->theme_dynamic.cursor_alpha, alpha);
      gfx_display_set_alpha(ozone->theme_dynamic.cursor_border, alpha);
      last_alpha = alpha;
   }

   if (dispctx && dispctx->blend_begin)
      dispctx->blend_begin(userdata);

   /* Cursor without border */
   gfx_display_draw_texture_slice(
         p_disp,
         userdata,
         video_width,
         video_height,
         slice_x,
         slice_y,
         slice_w,
         slice_h,
         slice_new_w,
         slice_new_h,
         video_width,
         video_height,
         ozone->theme_dynamic.cursor_alpha,
         offset,
         scale_factor,
         ozone->theme->textures[OZONE_THEME_TEXTURE_CURSOR_NO_BORDER],
         mymat
         );

   /* Tainted border */
   gfx_display_draw_texture_slice(
         p_disp,
         userdata,
         video_width,
         video_height,
         slice_x,
         slice_y,
         slice_w,
         slice_h,
         slice_new_w,
         slice_new_h,
         video_width,
         video_height,
         ozone->theme_dynamic.cursor_border,
         offset,
         scale_factor,
         ozone->textures[OZONE_TEXTURE_CURSOR_BORDER],
         mymat
         );

   if (dispctx && dispctx->blend_end)
      dispctx->blend_end(userdata);
}

static void ozone_draw_cursor_fallback(
      ozone_handle_t *ozone,
      gfx_display_t *p_disp,
      void *userdata,
      unsigned video_width,
      unsigned video_height,
      int x_offset,
      unsigned width,
      unsigned height,
      size_t y,
      float alpha)
{
   static float last_alpha         = 0.0f;

   if (alpha != last_alpha)
   {
      gfx_display_set_alpha(ozone->theme_dynamic.selection_border, alpha);
      gfx_display_set_alpha(ozone->theme_dynamic.selection, alpha);
      last_alpha = alpha;
   }

   /* Fill */
   gfx_display_draw_quad(
         p_disp,
         userdata,
         video_width,
         video_height,
         x_offset - ozone->dimensions.spacer_3px,
         (int)y,
         width + ozone->dimensions.spacer_3px * 2,
         height,
         video_width,
         video_height,
         ozone->theme_dynamic.selection,
         NULL);

   /* Borders (can't do one single quad because of alpha) */

   /* Top */
   gfx_display_draw_quad(
         p_disp,
         userdata,
         video_width,
         video_height,
         x_offset - ozone->dimensions.spacer_5px,
         (int)(y - ozone->dimensions.spacer_3px),
         width + 1 + ozone->dimensions.spacer_5px * 2,
         ozone->dimensions.spacer_3px,
         video_width,
         video_height,
         ozone->theme_dynamic.selection_border,
         NULL);

   /* Bottom */
   gfx_display_draw_quad(
         p_disp,
         userdata,
         video_width,
         video_height,
         x_offset - ozone->dimensions.spacer_5px,
         (int)(y + height),
         width + 1 + ozone->dimensions.spacer_5px * 2,
         ozone->dimensions.spacer_3px,
         video_width,
         video_height,
         ozone->theme_dynamic.selection_border,
         NULL);

   /* Left */
   gfx_display_draw_quad(
         p_disp,
         userdata,
         video_width,
         video_height,
         x_offset - ozone->dimensions.spacer_5px,
         (int)y,
         ozone->dimensions.spacer_3px,
         height,
         video_width,
         video_height,
         ozone->theme_dynamic.selection_border,
         NULL);

   /* Right */
   gfx_display_draw_quad(
         p_disp,
         userdata,
         video_width,
         video_height,
         x_offset + width + ozone->dimensions.spacer_3px,
         (int)y,
         ozone->dimensions.spacer_3px,
         height,
         video_width,
         video_height,
         ozone->theme_dynamic.selection_border,
         NULL);
}

static void ozone_draw_cursor(
      ozone_handle_t *ozone,
      gfx_display_t *p_disp,
      void *userdata,
      unsigned video_width,
      unsigned video_height,
      int x_offset,
      unsigned width,
      unsigned height,
      size_t y,
      float alpha,
      math_matrix_4x4 *mymat)
{
   int new_x    = x_offset;
   size_t new_y = y;

   /* Apply wiggle animation if needed */
   if (ozone->flags2 & OZONE_FLAG2_CURSOR_WIGGLING)
      ozone_apply_cursor_wiggle_offset(ozone, &new_x, &new_y);

   /* Draw the cursor */
   if (     (ozone->theme->name)
         && (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS || ozone->flags2 & OZONE_FLAG2_IGNORE_MISSING_ASSETS))
      ozone_draw_cursor_slice(ozone,
            p_disp,
            userdata,
            video_width,
            video_height,
            new_x,
            width,
            height,
            new_y,
            alpha,
            mymat);
   else
      ozone_draw_cursor_fallback(ozone,
            p_disp,
            userdata,
            video_width,
            video_height,
            new_x,
            width,
            height,
            new_y,
            alpha);
}

const enum msg_hash_enums ozone_system_tabs_label[OZONE_SYSTEM_TAB_LAST] = {
      MENU_ENUM_LABEL_MAIN_MENU,
      MENU_ENUM_LABEL_SETTINGS_TAB,
      MENU_ENUM_LABEL_HISTORY_TAB,
      MENU_ENUM_LABEL_FAVORITES_TAB,
#ifdef HAVE_IMAGEVIEWER
      MENU_ENUM_LABEL_IMAGES_TAB,
#endif
      MENU_ENUM_LABEL_MUSIC_TAB,
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
      MENU_ENUM_LABEL_VIDEO_TAB,
#endif
#ifdef HAVE_NETWORKING
      MENU_ENUM_LABEL_NETPLAY_TAB,
#endif
      MENU_ENUM_LABEL_ADD_TAB,
      MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB,
#ifdef HAVE_LIBRETRODB
      MENU_ENUM_LABEL_EXPLORE_TAB
#endif
};

static void ozone_draw_sidebar(
      ozone_handle_t *ozone,
      gfx_display_t *p_disp,
      gfx_animation_t *p_anim,
      bool use_smooth_ticker,
      enum gfx_animation_ticker_type menu_ticker_type,
      void *userdata,
      unsigned video_width,
      unsigned video_height,
      bool libretro_running,
      float menu_framebuffer_opacity,
      math_matrix_4x4 *mymat)
{
   static const enum msg_hash_enums ozone_system_tabs_value[OZONE_SYSTEM_TAB_LAST] = {
      MENU_ENUM_LABEL_VALUE_MAIN_MENU,
      MENU_ENUM_LABEL_VALUE_SETTINGS_TAB,
      MENU_ENUM_LABEL_VALUE_HISTORY_TAB,
      MENU_ENUM_LABEL_VALUE_FAVORITES_TAB,
#ifdef HAVE_IMAGEVIEWER
      MENU_ENUM_LABEL_VALUE_IMAGES_TAB,
#endif
      MENU_ENUM_LABEL_VALUE_MUSIC_TAB,
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
      MENU_ENUM_LABEL_VALUE_VIDEO_TAB,
#endif
#ifdef HAVE_NETWORKING
      MENU_ENUM_LABEL_VALUE_NETPLAY_TAB,
#endif
      MENU_ENUM_LABEL_VALUE_ADD_TAB,
      MENU_ENUM_LABEL_VALUE_CONTENTLESS_CORES_TAB,
#ifdef HAVE_LIBRETRODB
      MENU_ENUM_LABEL_VALUE_EXPLORE_TAB
#endif
   };
   static const unsigned ozone_system_tabs_icons[OZONE_SYSTEM_TAB_LAST]            = {
      OZONE_TAB_TEXTURE_MAIN_MENU,
      OZONE_TAB_TEXTURE_SETTINGS,
      OZONE_TAB_TEXTURE_HISTORY,
      OZONE_TAB_TEXTURE_FAVORITES,
#ifdef HAVE_IMAGEVIEWER
      OZONE_TAB_TEXTURE_IMAGE,
#endif
      OZONE_TAB_TEXTURE_MUSIC,
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
      OZONE_TAB_TEXTURE_VIDEO,
#endif
#ifdef HAVE_NETWORKING
      OZONE_TAB_TEXTURE_NETWORK,
#endif
      OZONE_TAB_TEXTURE_SCAN_CONTENT,
      OZONE_TAB_TEXTURE_CONTENTLESS_CORES,
#ifdef HAVE_LIBRETRODB
      OZONE_TAB_TEXTURE_EXPLORE
#endif
   };
   size_t y;
   int entry_width;
   char console_title[NAME_MAX_LENGTH];
   unsigned i, sidebar_height;
   gfx_animation_ctx_ticker_t ticker;
   gfx_animation_ctx_ticker_smooth_t ticker_smooth;
   unsigned ticker_x_offset          = 0;
   uint32_t text_alpha               = ozone->animations.sidebar_text_alpha * 255.0f;
   float scale_factor                = ozone->last_scale_factor;
   unsigned selection_y              = 0;
   unsigned selection_old_y          = 0;
   unsigned horizontal_list_size     = 0;
   gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;

   /* Initial ticker configuration */
   if (use_smooth_ticker)
   {
      ticker_smooth.idx           = p_anim->ticker_pixel_idx;
      ticker_smooth.font          = ozone->fonts.sidebar.font;
      ticker_smooth.font_scale    = 1.0f;
      ticker_smooth.type_enum     = menu_ticker_type;
      ticker_smooth.spacer        = (ozone->font_unicode) ? OZONE_TICKER_SPACER : NULL;
      ticker_smooth.x_offset      = &ticker_x_offset;
      ticker_smooth.dst_str_width = NULL;
   }
   else
   {
      ticker.idx                  = p_anim->ticker_idx;
      ticker.type_enum            = menu_ticker_type;
      ticker.spacer               = (ozone->font_unicode) ? OZONE_TICKER_SPACER : NULL;
   }

   horizontal_list_size           = (ozone->show_playlist_tabs)
         ? (unsigned)ozone->horizontal_list.size
         : 0;

   if (p_disp->dispctx && p_disp->dispctx->scissor_begin)
      gfx_display_scissor_begin(
            p_disp,
            userdata,
            video_width, video_height,
            0,
            ozone->dimensions.header_height + ozone->dimensions.spacer_1px,
            (unsigned)ozone->dimensions_sidebar_width,
            video_height
                  - ozone->dimensions.header_height
                  - ozone->dimensions.footer_height
                  - ozone->dimensions.spacer_1px);

   /* Background */
   sidebar_height = video_height
         - ozone->dimensions.header_height
         - ozone->dimensions.sidebar_gradient_height * 2
         - ozone->dimensions.footer_height;

   if (sidebar_height)
   {
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            ozone->sidebar_offset,
            ozone->dimensions.header_height,
            (unsigned)ozone->dimensions_sidebar_width,
            ozone->dimensions.sidebar_gradient_height,
            video_width,
            video_height,
            ozone->theme->sidebar_top_gradient,
            NULL);
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            ozone->sidebar_offset,
            ozone->dimensions.header_height
                  + ozone->dimensions.sidebar_gradient_height,
            (unsigned)ozone->dimensions_sidebar_width,
            sidebar_height,
            video_width,
            video_height,
            ozone->theme->sidebar_background,
            NULL);
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            ozone->sidebar_offset,
            video_height
                  - ozone->dimensions.footer_height
                  - ozone->dimensions.sidebar_gradient_height,
            (unsigned)ozone->dimensions_sidebar_width,
            ozone->dimensions.sidebar_gradient_height,
            video_width,
            video_height,
            ozone->theme->sidebar_bottom_gradient,
            NULL);
   }

   /* Tabs */
   /* y offset computation */
   y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.sidebar_padding_vertical;
   for (i = 0; i < ozone->system_tab_end + horizontal_list_size + 1; i++)
   {
      if (i == ozone->categories_selection_ptr)
      {
         selection_y = (unsigned)y;
         if (ozone->categories_selection_ptr > ozone->system_tab_end)
            selection_y += ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px;
      }

      if (i == ozone->categories_active_idx_old)
      {
         selection_old_y = (unsigned)y;
         if (ozone->categories_active_idx_old > ozone->system_tab_end)
            selection_old_y += ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px;
      }

      y += ozone->dimensions.sidebar_entry_height + ozone->dimensions.sidebar_entry_padding_vertical;
   }

   entry_width = (unsigned)ozone->dimensions_sidebar_width - ozone->dimensions.sidebar_padding_horizontal * 2;

   /* Cursor */
   if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
      ozone_draw_cursor(
            ozone,
            p_disp,
            userdata,
            video_width,
            video_height,
            ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal + ozone->dimensions.spacer_3px,
            entry_width - ozone->dimensions.spacer_5px,
            ozone->dimensions.sidebar_entry_height + ozone->dimensions.spacer_1px,
            selection_y + ozone->animations.scroll_y_sidebar,
            ozone->animations.cursor_alpha,
            mymat);

   if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR_OLD)
      ozone_draw_cursor(
            ozone,
            p_disp,
            userdata,
            video_width,
            video_height,
            ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal + ozone->dimensions.spacer_3px,
            entry_width - ozone->dimensions.spacer_5px,
            ozone->dimensions.sidebar_entry_height + ozone->dimensions.spacer_1px,
            selection_old_y + ozone->animations.scroll_y_sidebar,
            1 - ozone->animations.cursor_alpha,
            mymat);

   /* Menu tabs */
   y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.sidebar_padding_vertical;
   if (dispctx && dispctx->blend_begin)
      dispctx->blend_begin(userdata);

   text_alpha *= ozone->animations.alpha;

   for (i = 0; i < (unsigned)(ozone->system_tab_end + 1); i++)
   {
      float *col                     = NULL;
      bool selected                  = (ozone->categories_selection_ptr == i);
      unsigned icon                  = ozone_system_tabs_icons[ozone->tabs[i]];

      if (!(col = selected ? ozone->theme->text_selected : ozone->theme->entries_icon))
         col = ozone->pure_white;

      gfx_display_set_alpha(col, ozone->animations.alpha);

      /* Icon */
      ozone_draw_icon(
            p_disp,
            userdata,
            video_width,
            video_height,
            ozone->dimensions.sidebar_entry_icon_size,
            ozone->dimensions.sidebar_entry_icon_size,
            ozone->tab_textures[icon],
            ozone->sidebar_offset
                  + ozone->dimensions.sidebar_padding_horizontal
                  + ozone->dimensions.sidebar_entry_icon_padding,
            y
                  + ozone->dimensions.sidebar_entry_height / 2
                  - ozone->dimensions.sidebar_entry_icon_size / 2
                  + ozone->animations.scroll_y_sidebar,
            video_width,
            video_height,
            0.0f,
            1.0f,
            col,
            mymat);

      /* Text */
      if (!ozone->sidebar_collapsed)
      {
         enum msg_hash_enums value_idx  = ozone_system_tabs_value[ozone->tabs[i]];
         const char *title              = msg_hash_to_str(value_idx);
         uint32_t text_color            = 0;
         if (ozone->theme)
            text_color                  = selected
               ? COLOR_TEXT_ALPHA(ozone->theme->text_selected_rgba, text_alpha)
               : COLOR_TEXT_ALPHA(ozone->theme->text_sidebar_rgba, text_alpha);

         if (use_smooth_ticker)
         {
            ticker_smooth.selected    = selected;
            /* TODO/FIXME - undefined behavior reported by ASAN -
             *-12.549 is outside the range of representable values
             of type 'unsigned int'
             * */
            ticker_smooth.field_width = (entry_width
                  - ozone->dimensions.sidebar_entry_icon_size
                  - 40 * scale_factor);
            ticker_smooth.src_str     = title;
            ticker_smooth.dst_str     = console_title;
            ticker_smooth.dst_str_len = sizeof(console_title);

            gfx_animation_ticker_smooth(&ticker_smooth);
         }
         else
         {
            ticker.len      = (entry_width
                  - ozone->dimensions.sidebar_entry_icon_size
                  - 40 * scale_factor)
                  / ozone->fonts.sidebar.glyph_width;
            ticker.s        = console_title;
            ticker.selected = selected;
            ticker.str      = title;

            gfx_animation_ticker(&ticker);
         }

         gfx_display_draw_text(
               ozone->fonts.sidebar.font,
               console_title,
               ticker_x_offset
                     + ozone->sidebar_offset
                     + ozone->dimensions.sidebar_padding_horizontal
                     + ozone->dimensions.sidebar_entry_icon_padding * 2
                     + ozone->dimensions.sidebar_entry_icon_size,
               y
                     + ozone->dimensions.sidebar_entry_height / 2
                     + ozone->fonts.sidebar.line_centre_offset
                     + ozone->animations.scroll_y_sidebar,
               video_width,
               video_height,
               text_color,
               TEXT_ALIGN_LEFT,
               1.0f,
               false,
               1.0f,
               true);
      }

      y += ozone->dimensions.sidebar_entry_height + ozone->dimensions.sidebar_entry_padding_vertical;
   }

   if (dispctx && dispctx->blend_end)
      dispctx->blend_end(userdata);

   /* Console tabs */
   if (horizontal_list_size > 0)
   {
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal,
            y + ozone->animations.scroll_y_sidebar,
            entry_width,
            ozone->dimensions.spacer_1px,
            video_width,
            video_height,
            ozone->theme->entries_border,
            NULL);

      y += ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px;

      if (dispctx && dispctx->blend_begin)
         dispctx->blend_begin(userdata);

      for (i = 0; i < horizontal_list_size; i++)
      {
         ozone_node_t *node   = (ozone_node_t*)ozone->horizontal_list.list[i].userdata;
         float *col           = NULL;
         bool selected        = (ozone->categories_selection_ptr == ozone->system_tab_end + 1 + i);
         uint32_t text_color  = 0;
         if (ozone->theme)
            text_color        = COLOR_TEXT_ALPHA((selected
               ? ozone->theme->text_selected_rgba
               : ozone->theme->text_sidebar_rgba), text_alpha);

         if (!node)
            goto console_iterate;

         if (!(col = (selected ? ozone->theme->text_selected : ozone->theme->entries_icon)))
            col = ozone->pure_white;

         gfx_display_set_alpha(col, ozone->animations.alpha);

         /* Icon */
         ozone_draw_icon(
               p_disp,
               userdata,
               video_width,
               video_height,
               ozone->dimensions.sidebar_entry_icon_size,
               ozone->dimensions.sidebar_entry_icon_size,
               node->icon,
               ozone->sidebar_offset
                     + ozone->dimensions.sidebar_padding_horizontal
                     + ozone->dimensions.sidebar_entry_icon_padding,
               y
                     + ozone->dimensions.sidebar_entry_height / 2
                     - ozone->dimensions.sidebar_entry_icon_size / 2
                     + ozone->animations.scroll_y_sidebar,
               video_width,
               video_height,
               0.0f,
               1.0f,
               col,
               mymat);

         /* Text */
         if (ozone->sidebar_collapsed)
            goto console_iterate;

         if (use_smooth_ticker)
         {
            ticker_smooth.selected    = selected;
            /* TODO/FIXME - undefined behavior reported by ASAN -
             *-12.549 is outside the range of representable values
             of type 'unsigned int'
             * */
            ticker_smooth.field_width = (entry_width
                  - ozone->dimensions.sidebar_entry_icon_size
                  - 40 * scale_factor);
            ticker_smooth.src_str     = node->console_name;
            ticker_smooth.dst_str     = console_title;
            ticker_smooth.dst_str_len = sizeof(console_title);

            gfx_animation_ticker_smooth(&ticker_smooth);
         }
         else
         {
            ticker.len      = (entry_width
                  - ozone->dimensions.sidebar_entry_icon_size
                  - 40 * scale_factor)
                  / ozone->fonts.sidebar.glyph_width;
            ticker.s        = console_title;
            ticker.selected = selected;
            ticker.str      = node->console_name;

            gfx_animation_ticker(&ticker);
         }

         gfx_display_draw_text(
               ozone->fonts.sidebar.font,
               console_title,
               ticker_x_offset
                     + ozone->sidebar_offset
                     + ozone->dimensions.sidebar_padding_horizontal
                     + ozone->dimensions.sidebar_entry_icon_padding * 2
                     + ozone->dimensions.sidebar_entry_icon_size,
               y
                     + ozone->dimensions.sidebar_entry_height / 2
                     + ozone->fonts.sidebar.line_centre_offset
                     + ozone->animations.scroll_y_sidebar,
               video_width,
               video_height,
               text_color,
               TEXT_ALIGN_LEFT,
               1.0f,
               false,
               1.0f,
               true);

console_iterate:
         y += ozone->dimensions.sidebar_entry_height + ozone->dimensions.sidebar_entry_padding_vertical;
      }

      if (dispctx && dispctx->blend_end)
         dispctx->blend_end(userdata);
   }

   font_flush(video_width, video_height, &ozone->fonts.sidebar);

   if (dispctx && dispctx->scissor_end)
      dispctx->scissor_end(userdata, video_width, video_height);
}

static void ozone_thumbnail_bar_hide_end(void *userdata)
{
   ozone_handle_t *ozone      = (ozone_handle_t*)userdata;
   ozone->show_thumbnail_bar  = false;
   ozone->flags              &= ~(OZONE_FLAG_PENDING_HIDE_THUMBNAIL_BAR);

   if (!((ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU) && menu_is_running_quick_menu()))
      ozone->flags           |= OZONE_FLAG_NEED_COMPUTE;
}

static bool ozone_is_main_menu_playlist(void *userdata)
{
   menu_entry_t entry;
   MENU_ENTRY_INITIALIZE(entry);
   menu_entry_get(&entry, 0, 0, NULL, true);
   return entry.type == FILE_TYPE_RPL_ENTRY;
}

static void ozone_update_savestate_thumbnail_path(void *data, unsigned i)
{
   settings_t *settings     = config_get_ptr();
   ozone_handle_t *ozone    = (ozone_handle_t*)data;
   int state_slot           = settings->ints.state_slot;
   bool savestate_thumbnail = settings->bools.savestate_thumbnail_enable;

   if (!ozone)
      return;

   /* Cache previous savestate thumbnail path */
   strlcpy(
         ozone->prev_savestate_thumbnail_file_path,
         ozone->savestate_thumbnail_file_path,
         sizeof(ozone->prev_savestate_thumbnail_file_path));

   if (ozone->flags2 & OZONE_FLAG2_SELECTION_CORE_IS_VIEWER_REAL)
      ozone->flags2 |=  OZONE_FLAG2_SELECTION_CORE_IS_VIEWER;
   else
      ozone->flags2 &= ~OZONE_FLAG2_SELECTION_CORE_IS_VIEWER;

   if (ozone->flags & OZONE_FLAG_SKIP_THUMBNAIL_RESET)
      return;

   ozone->savestate_thumbnail_file_path[0] = '\0';

   /* Savestate thumbnails are only relevant
    * when viewing the running quick menu or state slots */
   if (!(   ((ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU) && menu_is_running_quick_menu())
         || (ozone->flags & OZONE_FLAG_IS_STATE_SLOT)))
      return;

   ozone->flags &= ~OZONE_FLAG_WANT_THUMBNAIL_BAR;
   ozone->flags &= ~OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE;

   if (savestate_thumbnail)
   {
      menu_entry_t entry;

      MENU_ENTRY_INITIALIZE(entry);
      entry.flags |= MENU_ENTRY_FLAG_LABEL_ENABLED;
      menu_entry_get(&entry, 0, i, NULL, true);

      if (!string_is_empty(entry.label))
      {
         if (     string_to_unsigned(entry.label) == MENU_ENUM_LABEL_STATE_SLOT
               || string_is_equal(entry.label, msg_hash_to_str(MENU_ENUM_LABEL_STATE_SLOT))
               || string_is_equal(entry.label, msg_hash_to_str(MENU_ENUM_LABEL_LOAD_STATE))
               || string_is_equal(entry.label, msg_hash_to_str(MENU_ENUM_LABEL_SAVE_STATE)))
         {
            size_t _len;
            char path[PATH_MAX_LENGTH * 2];
            runloop_state_t *runloop_st = runloop_state_get_ptr();

            /* State slot dropdown */
            if (string_to_unsigned(entry.label) == MENU_ENUM_LABEL_STATE_SLOT)
            {
               state_slot    = i - 1;
               ozone->flags |= OZONE_FLAG_IS_STATE_SLOT;
            }

            if (state_slot < 0)
            {
               path[0] = '\0';
               _len    = fill_pathname_join_delim(path,
                     runloop_st->name.savestate, "auto", '.', sizeof(path));
            }
            else
            {
               _len    = strlcpy(path, runloop_st->name.savestate, sizeof(path));
               if (state_slot > 0)
                  _len += snprintf(path + _len, sizeof(path) - _len, "%d", state_slot);
            }

            strlcpy(path + _len, FILE_PATH_PNG_EXTENSION, sizeof(path) - _len);

            strlcpy(
                  ozone->savestate_thumbnail_file_path, path,
                  sizeof(ozone->savestate_thumbnail_file_path));

            ozone->flags |= OZONE_FLAG_WANT_THUMBNAIL_BAR
                          | OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE;

            menu_update_fullscreen_thumbnail_label(
                  ozone->fullscreen_thumbnail_label,
                  sizeof(ozone->fullscreen_thumbnail_label),
                  (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU) ? true : false,
                  NULL);
         }

         if (     ((ozone->show_thumbnail_bar) != ((ozone->flags & OZONE_FLAG_WANT_THUMBNAIL_BAR) > 0)
               && (!(ozone->flags & OZONE_FLAG_PENDING_HIDE_THUMBNAIL_BAR))))
            ozone->flags |= OZONE_FLAG_NEED_COMPUTE;
      }
   }
}

static void ozone_update_savestate_thumbnail_image(void *data)
{
   ozone_handle_t *ozone = (ozone_handle_t*)data;
   unsigned thumbnail_upscale_threshold =
      config_get_ptr()->uints.gfx_thumbnail_upscale_threshold;

   if ((!ozone) || (ozone->flags & OZONE_FLAG_SKIP_THUMBNAIL_RESET))
      return;

   /* If path is empty, just reset thumbnail */
   if (string_is_empty(ozone->savestate_thumbnail_file_path))
      gfx_thumbnail_reset(&ozone->thumbnails.savestate);
   else
   {
      /* Only request thumbnail if:
       * > Thumbnail has never been loaded *OR*
       * > Thumbnail path has changed */
      if (     (ozone->thumbnails.savestate.status == GFX_THUMBNAIL_STATUS_UNKNOWN)
            || !string_is_equal(ozone->savestate_thumbnail_file_path,
                     ozone->prev_savestate_thumbnail_file_path))
      {
         gfx_thumbnail_request_file(
               ozone->savestate_thumbnail_file_path,
               &ozone->thumbnails.savestate,
               thumbnail_upscale_threshold);
      }
   }

   ozone->thumbnails.savestate.flags |= GFX_THUMB_FLAG_CORE_ASPECT;
}

static void ozone_entries_update_thumbnail_bar(ozone_handle_t *ozone,
      bool savestate_thumbnail,
      bool is_playlist, bool allow_animation)
{
   struct gfx_animation_ctx_entry entry;
   uintptr_t tag     = (uintptr_t)&ozone->show_thumbnail_bar;

   if (!savestate_thumbnail)
      ozone->flags  &= ~OZONE_FLAG_IS_STATE_SLOT;

   entry.duration    = ANIMATION_CURSOR_DURATION;
   entry.easing_enum = OZONE_EASING_XY;
   entry.tag         = tag;
   entry.subject     = &ozone->animations.thumbnail_bar_position;

   gfx_animation_kill_by_tag(&tag);

   /* Show it
    * > We only want to trigger a 'show' animation
    *   if 'show_thumbnail_bar' is currently false.
    *   However: 'show_thumbnail_bar' is only set
    *   to false by the 'ozone_thumbnail_bar_hide_end'
    *   callback. If the above 'gfx_animation_kill_by_tag()'
    *   kills an existing 'hide' animation, then the
    *   callback will not fire - so the sidebar will be
    *   off screen, but a subsequent attempt to show it
    *   here will fail, since 'show_thumbnail_bar' will
    *   be a false positive. We therefore require an
    *   additional OZONE_FLAG_PENDING_HIDE_THUMBNAIL_BAR parameter
    *   to track mid-animation state changes... */
   if ( (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR))
         && ( (!(ozone->show_thumbnail_bar))
            ||  (ozone->flags & OZONE_FLAG_PENDING_HIDE_THUMBNAIL_BAR))
         && (   (ozone->flags & OZONE_FLAG_WANT_THUMBNAIL_BAR)
            ||  (ozone->flags & OZONE_FLAG_IS_STATE_SLOT))
      )
   {
      if (allow_animation)
      {
         entry.cb                  = NULL;
         entry.userdata            = NULL;
         entry.target_value        = ozone->dimensions.thumbnail_bar_width;

         gfx_animation_push(&entry);
      }
      else
      {
         ozone->animations.thumbnail_bar_position
                                   = ozone->dimensions.thumbnail_bar_width;
      }

      ozone->show_thumbnail_bar    = true;
      ozone->flags                &= ~OZONE_FLAG_PENDING_HIDE_THUMBNAIL_BAR;

      /* Want thumbnails to load instantly when thumbnail
       * sidebar first opens */
      ozone->thumbnails.stream_delay = 0.0f;
      gfx_thumbnail_set_stream_delay(ozone->thumbnails.stream_delay);
   }
   /* Hide it */
   else if ((ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         || ((ozone->show_thumbnail_bar)
            && (!(ozone->flags & OZONE_FLAG_WANT_THUMBNAIL_BAR))
            && (!(ozone->flags & OZONE_FLAG_IS_STATE_SLOT))))
   {
      if (allow_animation)
      {
         entry.cb                  = ozone_thumbnail_bar_hide_end;
         entry.userdata            = ozone;
         entry.target_value        = 0.0f;

         ozone->flags             |= OZONE_FLAG_PENDING_HIDE_THUMBNAIL_BAR;
         gfx_animation_push(&entry);
      }
      else
      {
         ozone->animations.thumbnail_bar_position = 0.0f;
         ozone_thumbnail_bar_hide_end(ozone);
      }
   }
}

static unsigned ozone_get_horizontal_selection_type(ozone_handle_t *ozone)
{
   if (ozone->categories_selection_ptr > ozone->system_tab_end)
   {
      size_t i = ozone->categories_selection_ptr - ozone->system_tab_end - 1;
      return (ozone->horizontal_list.size) ? ozone->horizontal_list.list[i].type : 0;
   }
   return 0;
}

static bool ozone_is_playlist(ozone_handle_t *ozone, bool depth)
{
   bool is_playlist;

   if (ozone->categories_selection_ptr > ozone->system_tab_end)
   {
      unsigned type = ozone_get_horizontal_selection_type(ozone);
      is_playlist   = type == FILE_TYPE_PLAYLIST_COLLECTION;
   }
   else
   {
      switch (ozone->tabs[ozone->categories_selection_ptr])
      {
         case OZONE_SYSTEM_TAB_MAIN:
            if (ozone_is_main_menu_playlist(ozone))
               return true;
         case OZONE_SYSTEM_TAB_SETTINGS:
         case OZONE_SYSTEM_TAB_ADD:
#ifdef HAVE_NETWORKING
         case OZONE_SYSTEM_TAB_NETPLAY:
#endif
#ifdef HAVE_LIBRETRODB
         case OZONE_SYSTEM_TAB_EXPLORE:
#endif
         case OZONE_SYSTEM_TAB_CONTENTLESS_CORES:
            is_playlist = false;
            break;
         case OZONE_SYSTEM_TAB_HISTORY:
         case OZONE_SYSTEM_TAB_FAVORITES:
         case OZONE_SYSTEM_TAB_MUSIC:
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
         case OZONE_SYSTEM_TAB_VIDEO:
#endif
#ifdef HAVE_IMAGEVIEWER
         case OZONE_SYSTEM_TAB_IMAGES:
#endif
         default:
            is_playlist = true;
            break;
      }
   }

   if (depth)
      return is_playlist && ozone->depth == 1;

   return is_playlist;
}

static bool ozone_want_collapse(ozone_handle_t *ozone,
      bool ozone_collapse_sidebar,
      bool is_playlist)
{
   return (ozone_collapse_sidebar)
         || (is_playlist && (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)));
}

static void ozone_sidebar_update_collapse(ozone_handle_t *ozone,
      bool ozone_collapse_sidebar, bool allow_animation)
{
   /* Collapse sidebar if needed */
   struct gfx_animation_ctx_entry entry;
   bool is_playlist          = ozone_is_playlist(ozone, false);
   bool collapse             = false;
   uintptr_t tag             = (uintptr_t)&ozone->sidebar_collapsed;

   entry.easing_enum         = OZONE_EASING_XY;
   entry.tag                 = tag;
   entry.userdata            = ozone;
   entry.duration            = ANIMATION_CURSOR_DURATION;

   gfx_animation_kill_by_tag(&tag);

   /* Treat Explore lists as playlists */
   if (ozone_get_horizontal_selection_type(ozone) == MENU_EXPLORE_TAB)
      is_playlist = true;

   /* Do not animate sidebar above first depth level */
   if (ozone->depth > 1)
      allow_animation = false;

   /* To collapse or not to collapse */
   collapse = ozone_want_collapse(ozone, ozone_collapse_sidebar, is_playlist);

   /* Skip if already at wanted state */
   if (    ( collapse &&  ozone->sidebar_collapsed)
        || (!collapse && !ozone->sidebar_collapsed))
      goto end;

   /* Collapse it */
   if (collapse)
   {
      if (allow_animation)
      {
         entry.cb             = ozone_sidebar_collapse_end;

         /* Text alpha */
         entry.subject        = &ozone->animations.sidebar_text_alpha;
         entry.target_value   = 0.0f;

         gfx_animation_push(&entry);

         /* Collapse */
         entry.subject        = &ozone->dimensions_sidebar_width;
         entry.target_value   = ozone->dimensions.sidebar_width_collapsed;

         gfx_animation_push(&entry);
      }
      else
      {
         ozone->animations.sidebar_text_alpha = 0.0f;
         ozone->dimensions_sidebar_width      = ozone->dimensions.sidebar_width_collapsed;
         ozone->sidebar_collapsed             = true;
      }
   }
   /* Show it */
   else
   {
      if (allow_animation)
      {
         entry.cb             = NULL;

         /* Text alpha */
         entry.subject        = &ozone->animations.sidebar_text_alpha;
         entry.target_value   = 1.0f;

         gfx_animation_push(&entry);

         /* Collapse */
         entry.subject        = &ozone->dimensions_sidebar_width;
         entry.target_value   = ozone->dimensions.sidebar_width_normal;

         gfx_animation_push(&entry);

         ozone->sidebar_collapsed               = false;
      }
      else
      {
         ozone->animations.sidebar_text_alpha   = 1.0f;
         ozone->dimensions_sidebar_width        = ozone->dimensions.sidebar_width_normal;
         ozone->sidebar_collapsed               = false;
      }
   }

end:
   ozone_entries_update_thumbnail_bar(ozone, config_get_ptr()->bools.savestate_thumbnail_enable,
         is_playlist, allow_animation);
}

static void ozone_go_to_sidebar(ozone_handle_t *ozone,
      bool ozone_collapse_sidebar, uintptr_t tag)
{
   struct gfx_animation_ctx_entry entry;

   ozone->selection_old           = ozone->selection;
   if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
      ozone->flags               |=  OZONE_FLAG_CURSOR_IN_SIDEBAR_OLD;
   else
      ozone->flags               &= ~OZONE_FLAG_CURSOR_IN_SIDEBAR_OLD;
   ozone->flags                  |=  OZONE_FLAG_CURSOR_IN_SIDEBAR;

   /* Remember last selection per tab */
   ozone->tab_selection[ozone->categories_selection_ptr] = ozone->selection;

   /* Cursor animation */
   ozone->animations.cursor_alpha = 0.0f;

   entry.cb                       = NULL;
   entry.duration                 = ANIMATION_CURSOR_DURATION;
   entry.easing_enum              = OZONE_EASING_ALPHA;
   entry.subject                  = &ozone->animations.cursor_alpha;
   entry.tag                      = tag;
   entry.target_value             = 1.0f;
   entry.userdata                 = NULL;

   gfx_animation_push(&entry);

   ozone_sidebar_update_collapse(ozone, ozone_collapse_sidebar, true);

#ifdef HAVE_AUDIOMIXER
   audio_driver_mixer_play_scroll_sound(false);
#endif
}

static void linebreak_after_colon(char (*str)[NAME_MAX_LENGTH])
{
   char *delim = (char*)strchr(*str, ':');
   if (delim)
      *++delim = '\n';
}

static void ozone_update_content_metadata(ozone_handle_t *ozone)
{
   struct menu_state *menu_st        = menu_state_get_ptr();
   menu_list_t *menu_list            = menu_st->entries.list;
   size_t selection                  = menu_st->selection_ptr;
   playlist_t *playlist              = playlist_get_cached();
   settings_t *settings              = config_get_ptr();
   bool scroll_content_metadata      = settings->bools.ozone_scroll_content_metadata;
   bool show_entry_idx               = settings->bools.playlist_show_entry_idx;
   const char *directory_runtime_log = settings->paths.directory_runtime_log;
   const char *directory_playlist    = settings->paths.directory_playlist;
   unsigned runtime_type             = settings->uints.playlist_sublabel_runtime_type;
   enum playlist_sublabel_last_played_style_type
         runtime_last_played_style   =
               (enum playlist_sublabel_last_played_style_type)
                     settings->uints.playlist_sublabel_last_played_style;
   enum playlist_sublabel_last_played_date_separator_type
         runtime_date_separator      =
               (enum playlist_sublabel_last_played_date_separator_type)
                     settings->uints.menu_timedate_date_separator;

   /* Must check whether core corresponds to 'viewer'
    * content even when not using a playlist, otherwise
    * file browser image updates are mishandled */

   if (!string_is_empty(menu_st->thumbnail_path_data->content_core_name))
   {
      if (     string_is_equal(
               menu_st->thumbnail_path_data->content_core_name,
               "imageviewer")
            || string_is_equal(
               menu_st->thumbnail_path_data->content_core_name,
               "musicplayer")
            || string_is_equal(
               menu_st->thumbnail_path_data->content_core_name,
               "movieplayer"))
         ozone->flags2 |=  OZONE_FLAG2_SELECTION_CORE_IS_VIEWER;
      else
         ozone->flags2 &= ~OZONE_FLAG2_SELECTION_CORE_IS_VIEWER;
   }
   else
      ozone->flags2 &= ~OZONE_FLAG2_SELECTION_CORE_IS_VIEWER;

   if (ozone->flags2 & OZONE_FLAG2_SELECTION_CORE_IS_VIEWER)
      ozone->flags2 |=  OZONE_FLAG2_SELECTION_CORE_IS_VIEWER_REAL;
   else
      ozone->flags2 &= ~OZONE_FLAG2_SELECTION_CORE_IS_VIEWER_REAL;

   if ( (playlist
         && (   (ozone->flags   & OZONE_FLAG_IS_PLAYLIST)
            ||  (ozone->flags   & OZONE_FLAG_IS_EXPLORE_LIST)
            || ((ozone->flags2  & OZONE_FLAG2_IS_QUICK_MENU) && !menu_is_running_quick_menu())))
         || (   (ozone->flags   & OZONE_FLAG_IS_DB_MANAGER_LIST) && ozone->depth == 4))
   {
      size_t _len;
      const char *core_label             = NULL;
      const struct playlist_entry *entry = NULL;
      ssize_t playlist_index             = selection;
      size_t list_size                   = MENU_LIST_GET_SELECTION(menu_list, 0)->size;
      file_list_t *list                  = MENU_LIST_GET_SELECTION(menu_list, 0);
      bool content_runtime_log           = settings->bools.content_runtime_log;
      bool content_runtime_log_aggr      = settings->bools.content_runtime_log_aggregate;

      if (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU)
      {
         playlist_index        = ozone->playlist_index;
         list_size             = playlist_get_size(playlist);

         /* Fill play time if applicable */
         if (content_runtime_log || content_runtime_log_aggr)
            playlist_get_index(playlist, playlist_index, &entry);
      }
#if defined(HAVE_LIBRETRODB)
      else if (list
            && (selection < list_size)
            && (ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST))
      {
         playlist_index = menu_explore_get_entry_playlist_index(
               list->list[selection].type, &playlist, &entry, list, &selection, &list_size);

         /* Fill play time if applicable */
         if (content_runtime_log || content_runtime_log_aggr)
            playlist_get_index(playlist, playlist_index, &entry);

         /* Remember playlist index for metadata */
         if (playlist_index >= 0)
            ozone->playlist_index = playlist_index;
      }
#endif
      /* Get playlist index corresponding
       * to the selected entry */
      else if (list
            && (selection < list_size)
            && (list->list[selection].type == FILE_TYPE_RPL_ENTRY))
      {
         playlist_index        = list->list[selection].entry_idx;

         /* Fill play time if applicable */
         if (content_runtime_log || content_runtime_log_aggr)
            playlist_get_index(playlist, playlist_index, &entry);

         /* Remember playlist index for metadata */
         ozone->playlist_index = playlist_index;
      }

      /* Fill entry enumeration */
      if (show_entry_idx)
      {
         unsigned long _entry = menu_entries_search_get_terms()
               ? (unsigned long)(menu_st->selection_ptr + 1)
               : (unsigned long)(playlist_index + 1);

         if (ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST)
            _entry            = (unsigned long)(selection + 1);

         snprintf(ozone->selection_entry_enumeration,
               sizeof(ozone->selection_entry_enumeration),
               msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT_INFO_ENTRY_IDX),
               _entry, (unsigned long)list_size);

         if (!scroll_content_metadata)
            linebreak_after_colon(&ozone->selection_entry_enumeration);
      }
      else
         ozone->selection_entry_enumeration[0] = '\0';

      /* Fill core name */
      if (entry)
      {
         if (!entry->core_name || string_is_equal(entry->core_name, "DETECT"))
            core_label = msg_hash_to_str(MSG_AUTODETECT);
         else
            core_label = entry->core_name;
      }
      else
      {
         if (     string_is_empty(menu_st->thumbnail_path_data->content_core_name)
               || string_is_equal(menu_st->thumbnail_path_data->content_core_name, "DETECT"))
            core_label = msg_hash_to_str(MSG_AUTODETECT);
         else
            core_label = menu_st->thumbnail_path_data->content_core_name;
      }

      _len  = strlcpy(ozone->selection_core_name,
            msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_SUBLABEL_CORE),
            sizeof(ozone->selection_core_name));
      _len += strlcpy(ozone->selection_core_name + _len, " ",
            sizeof(ozone->selection_core_name)   - _len);
      strlcpy(ozone->selection_core_name + _len, core_label,
            sizeof(ozone->selection_core_name) - _len);

      if (!scroll_content_metadata)
         linebreak_after_colon(&ozone->selection_core_name);

      /* Word wrap core name string, if required */
      if (!scroll_content_metadata)
      {
         char tmpstr[256];
         unsigned metadata_len =
               (ozone->dimensions.thumbnail_bar_width
                     - ((ozone->dimensions.sidebar_entry_icon_padding * 2) * 2))
                     / ozone->fonts.footer.glyph_width;
         size_t _len = strlcpy(tmpstr, ozone->selection_core_name, sizeof(tmpstr));
         (ozone->word_wrap)(ozone->selection_core_name,
               sizeof(ozone->selection_core_name),
               tmpstr, _len,
               metadata_len, ozone->fonts.footer.wideglyph_width, 0);
         ozone->selection_core_name_lines = ozone_count_lines(ozone->selection_core_name);
      }
      else
         ozone->selection_core_name_lines = 1;

      if (entry)
      {
         if (     (entry->runtime_status == PLAYLIST_RUNTIME_UNKNOWN)
               || (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU))
            runtime_update_playlist(
                  playlist, playlist_index,
                  directory_runtime_log,
                  directory_playlist,
                  (runtime_type == PLAYLIST_RUNTIME_PER_CORE),
                  runtime_last_played_style,
                  runtime_date_separator);

         if (!string_is_empty(entry->runtime_str))
            strlcpy(ozone->selection_playtime, entry->runtime_str, sizeof(ozone->selection_playtime));
         if (!string_is_empty(entry->last_played_str))
            strlcpy(ozone->selection_lastplayed, entry->last_played_str, sizeof(ozone->selection_lastplayed));
      }
      else
      {
         const char *disabled_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISABLED);
         size_t _len  = strlcpy(ozone->selection_playtime,
               msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_SUBLABEL_RUNTIME),
               sizeof(ozone->selection_playtime));
         _len        += strlcpy(ozone->selection_playtime  + _len, " ",
                  sizeof(ozone->selection_playtime) - _len);
         strlcpy(ozone->selection_playtime + _len, disabled_str, sizeof(ozone->selection_playtime) - _len);

         _len  = strlcpy(ozone->selection_lastplayed,
               msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_SUBLABEL_LAST_PLAYED),
               sizeof(ozone->selection_lastplayed));
         _len += strlcpy(ozone->selection_lastplayed  + _len, " ",
                  sizeof(ozone->selection_lastplayed) - _len);
         strlcpy(ozone->selection_lastplayed + _len, disabled_str, sizeof(ozone->selection_lastplayed) - _len);
      }

      if (!scroll_content_metadata)
      {
         linebreak_after_colon(&ozone->selection_playtime);
         linebreak_after_colon(&ozone->selection_lastplayed);
      }

      /* Word wrap last played string, if required */
      if (!scroll_content_metadata)
      {
         /* Note: Have to use a fixed length of '30' here, to
          * avoid awkward wrapping for certain last played time
          * formats. Last played strings are well defined, however
          * (unlike core names), so this should never overflow the
          * side bar */
         char tmpstr[256];
         size_t _len = strlcpy(tmpstr, ozone->selection_lastplayed, sizeof(tmpstr));
         (ozone->word_wrap)(ozone->selection_lastplayed,
               sizeof(ozone->selection_lastplayed), tmpstr, _len, 30, 100, 0);
         ozone->selection_lastplayed_lines = ozone_count_lines(ozone->selection_lastplayed);
      }
      else
         ozone->selection_lastplayed_lines = 1;
   }
}

static void ozone_tab_set_selection(void *data)
{
   ozone_handle_t *ozone = (ozone_handle_t*)data;

   /* Do not set tab selection when using pointer */
   if (     ozone
         && !(ozone->flags2 & OZONE_FLAG2_LAST_POINTER_IN_SIDEBAR))
   {
      size_t tab_selection = ozone->tab_selection[ozone->categories_selection_ptr];
      if (tab_selection)
      {
         struct menu_state *menu_st = menu_state_get_ptr();
         menu_st->selection_ptr     = tab_selection;
         ozone_selection_changed(ozone, false);
      }
   }
}

static void ozone_leave_sidebar(ozone_handle_t *ozone,
      bool ozone_collapse_sidebar, uintptr_t tag,
      unsigned remember_selection_type)
{
   struct gfx_animation_ctx_entry entry;
   bool ozone_main_tab_selected     = false;

   ozone_update_content_metadata(ozone);

   ozone->categories_active_idx_old = ozone->categories_selection_ptr;

   if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
      ozone->flags                 |=  OZONE_FLAG_CURSOR_IN_SIDEBAR_OLD;
   else
      ozone->flags                 &= ~OZONE_FLAG_CURSOR_IN_SIDEBAR_OLD;
   ozone->flags                    &= ~OZONE_FLAG_CURSOR_IN_SIDEBAR;

   if    ((ozone->tabs[ozone->categories_selection_ptr] == OZONE_SYSTEM_TAB_MAIN)
      || (ozone->tabs[ozone->categories_selection_ptr] == OZONE_SYSTEM_TAB_SETTINGS))
      ozone_main_tab_selected = true;

   /* Restore last selection per tab */
   if    ((remember_selection_type == MENU_REMEMBER_SELECTION_ALWAYS)
      || ((remember_selection_type == MENU_REMEMBER_SELECTION_PLAYLISTS) && (ozone->flags & OZONE_FLAG_IS_PLAYLIST))
      || ((remember_selection_type == MENU_REMEMBER_SELECTION_MAIN) && (ozone_main_tab_selected)))
      ozone_tab_set_selection(ozone);

   /* Cursor animation */
   ozone->animations.cursor_alpha   = 0.0f;

   entry.cb                         = NULL;
   entry.duration                   = ANIMATION_CURSOR_DURATION;
   entry.easing_enum                = OZONE_EASING_ALPHA;
   entry.subject                    = &ozone->animations.cursor_alpha;
   entry.tag                        = tag;
   entry.target_value               = 1.0f;
   entry.userdata                   = NULL;

   gfx_animation_push(&entry);

   ozone_sidebar_update_collapse(ozone, ozone_collapse_sidebar, true);

#ifdef HAVE_AUDIOMIXER
   audio_driver_mixer_play_scroll_sound(true);
#endif
}

static void ozone_free_node(ozone_node_t *node)
{
   if (!node)
      return;

   if (node->console_name)
      free(node->console_name);

   node->console_name = NULL;

   if (node->fullpath)
      free(node->fullpath);

   node->fullpath = NULL;

   free(node);
}

static void ozone_free_list_nodes(file_list_t *list, bool actiondata)
{
   unsigned i, size = list ? (unsigned)list->size : 0;

   for (i = 0; i < size; ++i)
   {
      ozone_free_node((ozone_node_t*)file_list_get_userdata_at_offset(list, i));

      list->list[i].userdata = NULL;

      if (actiondata)
         file_list_free_actiondata(list, i);
   }
}

static ozone_node_t *ozone_copy_node(const ozone_node_t *old_node)
{
   ozone_node_t *new_node = (ozone_node_t*)malloc(sizeof(*new_node));

   *new_node              = *old_node;
   new_node->fullpath     = old_node->fullpath
         ? strdup(old_node->fullpath)
         : NULL;

   return new_node;
}

static void ozone_list_deep_copy(const file_list_t *src,
      file_list_t *dst, size_t first, size_t last)
{
   size_t i, j   = 0;
   uintptr_t tag = (uintptr_t)dst;

   gfx_animation_kill_by_tag(&tag);

   ozone_free_list_nodes(dst, true);

   file_list_clear(dst);
   file_list_reserve(dst, (last + 1) - first);

   for (i = first; i <= last; ++i)
   {
      struct item_file *d = &dst->list[j];
      struct item_file *s = &src->list[i];
      void     *src_udata = s->userdata;
      void     *src_adata = s->actiondata;

      *d       = *s;
      d->alt   = string_is_empty(d->alt)   ? NULL : strdup(d->alt);
      d->path  = string_is_empty(d->path)  ? NULL : strdup(d->path);
      d->label = string_is_empty(d->label) ? NULL : strdup(d->label);

      if (src_udata)
         dst->list[j].userdata = (void*)ozone_copy_node((const ozone_node_t*)src_udata);

      if (src_adata)
      {
         void *data = malloc(sizeof(menu_file_list_cbs_t));
         memcpy(data, src_adata, sizeof(menu_file_list_cbs_t));
         dst->list[j].actiondata = data;
      }

      ++j;
   }

   dst->size = j;
}

static void ozone_list_cache(void *data,
      enum menu_list_type type, unsigned action)
{
   size_t y, entries_end;
   unsigned i;
   unsigned video_info_height;
   float bottom_boundary;
   ozone_node_t *first_node;
   float scale_factor;
   unsigned first             = 0;
   unsigned last              = 0;
   struct menu_state *menu_st = menu_state_get_ptr();
   menu_list_t *menu_list     = menu_st->entries.list;
   file_list_t *selection_buf = NULL;
   ozone_handle_t *ozone      = (ozone_handle_t*)data;

   if (!ozone)
      return;

   scale_factor               = ozone->last_scale_factor;
   ozone->selection_old_list  = ozone->selection;
   ozone->scroll_old          = ozone->animations.scroll_y;

   ozone->flags              |=  OZONE_FLAG_NEED_COMPUTE;
   if (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
      ozone->flags           |=  OZONE_FLAG_IS_PLAYLIST_OLD;
   else
      ozone->flags           &= ~OZONE_FLAG_IS_PLAYLIST_OLD;

   /* Deep copy visible elements */
   video_driver_get_size(NULL, &video_info_height);
   y                          = ozone->dimensions.header_height + ozone->dimensions.entry_padding_vertical;
   entries_end                = MENU_LIST_GET_SELECTION(menu_list, 0)->size;
   selection_buf              = MENU_LIST_GET_SELECTION(menu_list, 0);
   bottom_boundary            = video_info_height - ozone->dimensions.header_height - ozone->dimensions.footer_height;

   if (!selection_buf->size)
      return;

   for (i = 0; i < entries_end; i++)
   {
      ozone_node_t *node = (ozone_node_t*)selection_buf->list[i].userdata;

      if (!node || !node->height)
         continue;

      if (y + ozone->animations.scroll_y + node->height + 20 * scale_factor < ozone->dimensions.header_height + ozone->dimensions.entry_padding_vertical)
      {
         first++;
         goto text_iterate;
      }
      else if (y + ozone->animations.scroll_y - node->height - 20 * scale_factor > bottom_boundary)
         goto text_iterate;

      last++;

text_iterate:
      y += node->height;
   }

   if (last)
   {
      last                 -= 1;
      last                 += first;
   }

   first_node               = (ozone_node_t*)selection_buf->list[first].userdata;
   ozone->old_list_offset_y = (first_node) ? first_node->position_y : 0;

   ozone_list_deep_copy(selection_buf,
         &ozone->selection_buf_old, first, last);
}

static void ozone_change_tab(ozone_handle_t *ozone,
      enum msg_hash_enums tab, enum menu_settings_type type)
{
   struct menu_state *menu_st = menu_state_get_ptr();
   menu_list_t *menu_list     = menu_st->entries.list;
   file_list_t *menu_stack    = MENU_LIST_GET(menu_list, 0);
   file_list_t *selection_buf = MENU_LIST_GET_SELECTION(menu_list, 0);
   size_t stack_size          = menu_stack->size;

   if (menu_stack->list[stack_size - 1].label)
      free(menu_stack->list[stack_size - 1].label);
   menu_stack->list[stack_size - 1].label = NULL;

   menu_stack->list[stack_size - 1].label = strdup(msg_hash_to_str(tab));
   menu_stack->list[stack_size - 1].type  = type;

   ozone_list_cache(ozone, MENU_LIST_HORIZONTAL, MENU_ACTION_LEFT);

   menu_driver_deferred_push_content_list(selection_buf);
}

static void ozone_sidebar_goto(ozone_handle_t *ozone, size_t new_selection)
{
   static const enum msg_hash_enums ozone_system_tabs_idx[OZONE_SYSTEM_TAB_LAST]      = {
      MENU_ENUM_LABEL_MAIN_MENU,
      MENU_ENUM_LABEL_SETTINGS_TAB,
      MENU_ENUM_LABEL_HISTORY_TAB,
      MENU_ENUM_LABEL_FAVORITES_TAB,
#ifdef HAVE_IMAGEVIEWER
      MENU_ENUM_LABEL_IMAGES_TAB,
#endif
      MENU_ENUM_LABEL_MUSIC_TAB,
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
      MENU_ENUM_LABEL_VIDEO_TAB,
#endif
#ifdef HAVE_NETWORKING
      MENU_ENUM_LABEL_NETPLAY_TAB,
#endif
      MENU_ENUM_LABEL_ADD_TAB,
      MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB,
#ifdef HAVE_LIBRETRODB
      MENU_ENUM_LABEL_EXPLORE_TAB
#endif
   };
   static const enum menu_settings_type ozone_system_tabs_type[OZONE_SYSTEM_TAB_LAST] = {
      MENU_SETTINGS,
      MENU_SETTINGS_TAB,
      MENU_HISTORY_TAB,
      MENU_FAVORITES_TAB,
#ifdef HAVE_IMAGEVIEWER
      MENU_IMAGES_TAB,
#endif
      MENU_MUSIC_TAB,
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
      MENU_VIDEO_TAB,
#endif
#ifdef HAVE_NETWORKING
      MENU_NETPLAY_TAB,
#endif
      MENU_ADD_TAB,
      MENU_CONTENTLESS_CORES_TAB,
#ifdef HAVE_LIBRETRODB
      MENU_EXPLORE_TAB
#endif
   };

   unsigned video_info_height;
   struct gfx_animation_ctx_entry entry;
   uintptr_t tag = (uintptr_t)ozone;
   struct menu_state *menu_st = menu_state_get_ptr();
   menu_input_t *menu_input   = &menu_st->input_state;

   video_driver_get_size(NULL, &video_info_height);

   if (ozone->categories_selection_ptr != new_selection)
   {
      ozone->categories_active_idx_old = ozone->categories_selection_ptr;
      ozone->categories_selection_ptr  = new_selection;

      if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         ozone->flags                 |=  OZONE_FLAG_CURSOR_IN_SIDEBAR_OLD;
      else
         ozone->flags                 &= ~OZONE_FLAG_CURSOR_IN_SIDEBAR_OLD;

      gfx_animation_kill_by_tag(&tag);
   }

   /* ozone->animations.scroll_y_sidebar will be modified
    * > Set scroll acceleration to zero to minimise
    *   potential conflicts */
   menu_input->pointer.y_accel    = 0.0f;

   /* Cursor animation */
   ozone->animations.cursor_alpha = 0.0f;

   entry.cb             = NULL;
   entry.duration       = ANIMATION_CURSOR_DURATION;
   entry.easing_enum    = OZONE_EASING_ALPHA;
   entry.subject        = &ozone->animations.cursor_alpha;
   entry.tag            = tag;
   entry.target_value   = 1.0f;
   entry.userdata       = NULL;

   gfx_animation_push(&entry);

   /* Scroll animation */
   entry.cb           = NULL;
   entry.duration     = ANIMATION_CURSOR_DURATION;
   entry.easing_enum  = OZONE_EASING_XY;
   entry.subject      = &ozone->animations.scroll_y_sidebar;
   entry.tag          = tag;
   entry.target_value = ozone_sidebar_get_scroll_y(ozone, video_info_height);
   entry.userdata     = NULL;

   gfx_animation_push(&entry);

   if (new_selection > ozone->system_tab_end)
      ozone_change_tab(ozone, MENU_ENUM_LABEL_HORIZONTAL_MENU, MENU_SETTING_HORIZONTAL_MENU);
   else
      ozone_change_tab(ozone, ozone_system_tabs_idx[ozone->tabs[new_selection]], ozone_system_tabs_type[ozone->tabs[new_selection]]);
}

static void ozone_sidebar_entries_build_scroll_indices(ozone_handle_t *ozone)
{
   size_t i                     = 0;
   const char *path             = ozone->horizontal_list.list[0].alt
                                ? ozone->horizontal_list.list[0].alt
                                : ozone->horizontal_list.list[0].path;
   int ret                      = path ? TOLOWER((int)*path) : 0;
   int current                  = ELEM_GET_FIRST_CHAR(ret);

   ozone->sidebar_index_list[0] = 0;
   ozone->sidebar_index_size    = 1;

   for (i = 1; i < ozone->horizontal_list.size; i++)
   {
      int first;
      path    = ozone->horizontal_list.list[i].alt
              ? ozone->horizontal_list.list[i].alt
              : ozone->horizontal_list.list[i].path;
      ret     = path ? TOLOWER((int)*path) : 0;
      first   = ELEM_GET_FIRST_CHAR(ret);

      if (first != current)
      {
         /* Add scroll index */
         ozone->sidebar_index_list[ozone->sidebar_index_size] = i + ozone->system_tab_end + 1;
         if (!((ozone->sidebar_index_size + 1) >= SCROLL_INDEX_SIZE))
            ozone->sidebar_index_size++;
      }

      current = first;
   }

   /* Add scroll index */
   ozone->sidebar_index_list[ozone->sidebar_index_size] = ozone->horizontal_list.size - 1;
   if (!((ozone->sidebar_index_size + 1) >= SCROLL_INDEX_SIZE))
      ozone->sidebar_index_size++;
}

static void ozone_refresh_sidebars(ozone_handle_t *ozone,
      bool ozone_collapse_sidebar, unsigned video_height)
{
   uintptr_t collapsed_tag              = (uintptr_t)&ozone->sidebar_collapsed;
   uintptr_t offset_tag                 = (uintptr_t)&ozone->sidebar_offset;
   uintptr_t thumbnail_tag              = (uintptr_t)&ozone->show_thumbnail_bar;
   uintptr_t scroll_tag                 = (uintptr_t)ozone;
   bool is_playlist                     = ozone_is_playlist(ozone, false);
   bool collapse                        = false;

   /* Kill any existing animations */
   gfx_animation_kill_by_tag(&collapsed_tag);
   gfx_animation_kill_by_tag(&offset_tag);
   gfx_animation_kill_by_tag(&thumbnail_tag);
   if (ozone->depth == 1)
      gfx_animation_kill_by_tag(&scroll_tag);

   /* Treat Explore lists as playlists */
   if (ozone_get_horizontal_selection_type(ozone) == MENU_EXPLORE_TAB)
      is_playlist = true;

   /* To collapse or not to collapse */
   collapse = ozone_want_collapse(ozone, ozone_collapse_sidebar, is_playlist);

   /* Set sidebar width */
   if (collapse)
   {
      ozone->animations.sidebar_text_alpha = 0.0f;
      ozone->dimensions_sidebar_width      = ozone->dimensions.sidebar_width_collapsed;
      ozone->sidebar_collapsed             = true;
   }
   else
   {
      ozone->animations.sidebar_text_alpha = 1.0f;
      ozone->dimensions_sidebar_width      = ozone->dimensions.sidebar_width_normal;
      ozone->sidebar_collapsed             = false;
   }

   /* Set sidebar offset */
   if (ozone->depth == 1)
   {
      ozone->sidebar_offset = 0.0f;
      ozone->flags         |= OZONE_FLAG_DRAW_SIDEBAR;
   }
   else if (ozone->depth > 1)
   {
      ozone->sidebar_offset = -ozone->dimensions_sidebar_width;
      ozone->flags         &= ~OZONE_FLAG_DRAW_SIDEBAR;
   }

   /* Set thumbnail bar position */
   if (     ( !(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR))
         && ( !(ozone->flags & OZONE_FLAG_WANT_THUMBNAIL_BAR)
            || (ozone->flags & OZONE_FLAG_IS_STATE_SLOT))
      )
   {
      ozone->animations.thumbnail_bar_position = ozone->dimensions.thumbnail_bar_width;
      ozone->show_thumbnail_bar                = true;

      /* Want thumbnails to load instantly when thumbnail
       * sidebar first opens */
      ozone->thumbnails.stream_delay = 0.0f;
      gfx_thumbnail_set_stream_delay(ozone->thumbnails.stream_delay);
   }
   else
   {
      ozone->animations.thumbnail_bar_position = 0.0f;
      ozone->show_thumbnail_bar                = false;
   }
   ozone->flags &= ~OZONE_FLAG_PENDING_HIDE_THUMBNAIL_BAR;

   /* If sidebar is on-screen, update scroll position */
   if (ozone->depth == 1)
   {
      ozone->animations.cursor_alpha     = 1.0f;
      ozone->animations.scroll_y_sidebar = ozone_sidebar_get_scroll_y(ozone, video_height);
   }

   if (ozone->horizontal_list.size)
      ozone_sidebar_entries_build_scroll_indices(ozone);
}

static size_t ozone_list_get_size(void *data, enum menu_list_type type)
{
   ozone_handle_t *ozone = (ozone_handle_t*) data;

   if (ozone)
   {
      switch (type)
      {
         case MENU_LIST_PLAIN:
            {
               struct menu_state   *menu_st   = menu_state_get_ptr();
               menu_list_t *menu_list         = menu_st->entries.list;
               if (menu_list)
                  return MENU_LIST_GET_STACK_SIZE(menu_list, 0);
            }
            break;
         case MENU_LIST_HORIZONTAL:
            return ozone->horizontal_list.size;
         case MENU_LIST_TABS:
            return ozone->system_tab_end;
      }
   }

   return 0;
}

static void ozone_init_horizontal_list(ozone_handle_t *ozone, settings_t *settings)
{
   size_t i, list_size;
   menu_displaylist_info_t info;
   const char *dir_playlist          = settings->paths.directory_playlist;
   bool ozone_truncate_playlist_name = settings->bools.ozone_truncate_playlist_name;
   bool ozone_sort_after_truncate    = settings->bools.ozone_sort_after_truncate_playlist_name;

   menu_displaylist_info_init(&info);

   info.list                    = &ozone->horizontal_list;
   info.path                    = strdup(dir_playlist);
   info.label                   = strdup(msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
   info.exts                    = strldup("lpl", sizeof("lpl"));
   info.type_default            = FILE_TYPE_PLAIN;
   info.enum_idx                = MENU_ENUM_LABEL_PLAYLISTS_TAB;

   if (!string_is_empty(info.path))
   {
      if (menu_displaylist_ctl(DISPLAYLIST_DATABASE_PLAYLISTS_HORIZONTAL, &info, settings))
         menu_displaylist_process(&info);
   }

   menu_displaylist_info_free(&info);

   /* Loop through list and set console names */
   list_size = ozone_list_get_size(ozone, MENU_LIST_HORIZONTAL);

   for (i = 0; i < list_size; i++)
   {
      char playlist_file_noext[NAME_MAX_LENGTH];
      char *console_name        = NULL;
      const char *playlist_file = ozone->horizontal_list.list[i].path;

      if (!playlist_file)
      {
         file_list_set_alt_at_offset(&ozone->horizontal_list, i, NULL);
         continue;
      }

      fill_pathname(playlist_file_noext,
            path_basename(playlist_file), "",
            sizeof(playlist_file_noext));

      console_name = playlist_file_noext;

      /* Truncate playlist names, if required
       * > Format: "Vendor - Console"
           Remove everything before the hyphen
           and the subsequent space */
      if (ozone_truncate_playlist_name)
      {
         bool hyphen_found = false;

         for (;;)
         {
            /* Check for "- " */
            if (*console_name == '\0')
               break;
            else if (*console_name == '-' && *(console_name + 1) == ' ')
            {
               hyphen_found = true;
               break;
            }

            console_name++;
         }

         if (hyphen_found)
            console_name += 2;
         else
            console_name = playlist_file_noext;
      }

      /* Assign console name to list */
      file_list_set_alt_at_offset(&ozone->horizontal_list, i, console_name);
   }

   /* If playlist names were truncated and option is
    * enabled, re-sort list by console name */
   if (     ozone_truncate_playlist_name
         && ozone_sort_after_truncate
         && (list_size > 0))
      file_list_sort_on_alt(&ozone->horizontal_list);
}

static void ozone_context_destroy_horizontal_list(ozone_handle_t *ozone)
{
   unsigned i;
   size_t list_size = ozone_list_get_size(ozone, MENU_LIST_HORIZONTAL);

   for (i = 0; i < list_size; i++)
   {
      const char *path   = NULL;
      ozone_node_t *node = (ozone_node_t*)ozone->horizontal_list.list[i].userdata;

      if (!node)
         continue;

      if (!(path = ozone->horizontal_list.list[i].path))
         continue;

      if (string_ends_with_size(path, ".lpl", strlen(path), STRLEN_CONST(".lpl")))
      {
         video_driver_texture_unload(&node->icon);
         video_driver_texture_unload(&node->content_icon);
      }
   }
}

static ozone_node_t *ozone_alloc_node(void)
{
   ozone_node_t *node   = (ozone_node_t*)malloc(sizeof(*node));

   node->height         = 0;
   node->position_y     = 0;
   node->console_name   = NULL;
   node->icon           = 0;
   node->content_icon   = 0;
   node->fullpath       = NULL;
   node->sublabel_lines = 0;
   node->wrap           = false;

   return node;
}

static void ozone_context_reset_horizontal_list(ozone_handle_t *ozone)
{
   unsigned i;
   size_t list_size = ozone_list_get_size(ozone, MENU_LIST_HORIZONTAL);

   RHMAP_FREE(ozone->playlist_db_node_map);

   for (i = 0; i < list_size; i++)
   {
      const char *path         = NULL;
      const char *console_name = NULL;
      ozone_node_t *node       = (ozone_node_t*)ozone->horizontal_list.list[i].userdata;

      if (!node)
         if (!(node = ozone_alloc_node()))
            continue;

      if (!(path = ozone->horizontal_list.list[i].path))
         continue;

      if (string_ends_with_size(path, ".lpl", strlen(path), STRLEN_CONST(".lpl")))
      {
         size_t __len, syslen;
         struct texture_image ti;
         char sysname[NAME_MAX_LENGTH];
         char texturepath[PATH_MAX_LENGTH];

         /* Add current node to playlist database name map */
         RHMAP_SET_STR(ozone->playlist_db_node_map, path, node);

         syslen   = fill_pathname(sysname, path_basename(path), "", sizeof(sysname));
         __len    = fill_pathname_join_special(texturepath,
               ozone->icons_path, sysname,
               sizeof(texturepath));
         strlcpy(texturepath + __len, ".png", sizeof(texturepath) - __len);

         /* If the playlist icon doesn't exist, return default */
         if (!path_is_valid(texturepath))
            fill_pathname_join_special(
                  texturepath, ozone->icons_path, "default.png",
                  sizeof(texturepath));

         ti.width         = 0;
         ti.height        = 0;
         ti.pixels        = NULL;
         ti.supports_rgba = video_driver_supports_rgba();

         if (image_texture_load(&ti, texturepath))
         {
            if (ti.pixels)
            {
               video_driver_texture_unload(&node->icon);
               video_driver_texture_load(&ti,
                     TEXTURE_FILTER_MIPMAP_LINEAR, &node->icon);
            }

            image_texture_free(&ti);
         }

         strlcpy(sysname + syslen, "-content.png", sizeof(sysname) - syslen);
         /* Assemble new icon path */
         fill_pathname_join_special(
               texturepath, ozone->icons_path, sysname,
               sizeof(texturepath));

         /* If the content icon doesn't exist, return default-content */
         if (!path_is_valid(texturepath))
            fill_pathname_join_delim(texturepath, ozone->icons_path_default,
                  "content.png", '-', sizeof(texturepath));

         if (image_texture_load(&ti, texturepath))
         {
            if (ti.pixels)
            {
               video_driver_texture_unload(&node->content_icon);
               video_driver_texture_load(&ti,
                     TEXTURE_FILTER_MIPMAP_LINEAR, &node->content_icon);
            }

            image_texture_free(&ti);
         }

         /* Console name */
         console_name = ozone->horizontal_list.list[i].alt
               ? ozone->horizontal_list.list[i].alt
               : ozone->horizontal_list.list[i].path;

         if (node->console_name)
            free(node->console_name);
         node->console_name = NULL;

         /* Note: console_name will *always* be valid here,
          * but provide a fallback to prevent NULL pointer
          * dereferencing in case of unknown errors... */
         if (console_name)
            node->console_name = strdup(console_name);
         else
            node->console_name = strdup(path);
      }
      else if (string_ends_with_size(ozone->horizontal_list.list[i].label, ".lvw",
            strlen(ozone->horizontal_list.list[i].label), STRLEN_CONST(".lvw")))
      {
         node->console_name = strdup(path + strlen(msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_VIEW)) + 2);
         node->icon = ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CURSOR];
      }
   }
}

static void ozone_refresh_horizontal_list(ozone_handle_t *ozone,
      settings_t *settings)
{
   struct menu_state *menu_st = menu_state_get_ptr();

   ozone_context_destroy_horizontal_list(ozone);
   ozone_free_list_nodes(&ozone->horizontal_list, false);
   file_list_deinitialize(&ozone->horizontal_list);
   RHMAP_FREE(ozone->playlist_db_node_map);

   menu_st->flags                 |=  MENU_ST_FLAG_PREVENT_POPULATE;

   ozone->horizontal_list.list     = NULL;
   ozone->horizontal_list.capacity = 0;
   ozone->horizontal_list.size     = 0;
   ozone_init_horizontal_list(ozone, settings);

   ozone_context_reset_horizontal_list(ozone);
}

static void ozone_refresh_system_tabs_list(ozone_handle_t * ozone)
{
   settings_t *settings = config_get_ptr();

   ozone->system_tab_end              = 0;
   ozone->tabs[ozone->system_tab_end] = OZONE_SYSTEM_TAB_MAIN;

   if (      settings->bools.menu_content_show_settings
         && !settings->bools.kiosk_mode_enable)
      ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_SETTINGS;

   if (settings->bools.menu_content_show_favorites_first)
   {
      if (settings->bools.menu_content_show_favorites)
         ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_FAVORITES;
      if (settings->bools.menu_content_show_history)
         ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_HISTORY;
   }
   else
   {
      if (settings->bools.menu_content_show_history)
         ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_HISTORY;
      if (settings->bools.menu_content_show_favorites)
         ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_FAVORITES;
   }

#ifdef HAVE_IMAGEVIEWER
   if (settings->bools.menu_content_show_images)
      ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_IMAGES;
#endif

   if (settings->bools.menu_content_show_music)
      ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_MUSIC;

#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
   if (settings->bools.menu_content_show_video)
      ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_VIDEO;
#endif

   if (      settings->uints.menu_content_show_add_entry == MENU_ADD_CONTENT_ENTRY_DISPLAY_PLAYLISTS_TAB
         && !settings->bools.kiosk_mode_enable)
      ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_ADD;

#if defined(HAVE_DYNAMIC)
   if (settings->uints.menu_content_show_contentless_cores !=
         MENU_CONTENTLESS_CORES_DISPLAY_NONE)
      ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_CONTENTLESS_CORES;
#endif

#if defined(HAVE_LIBRETRODB)
   if (settings->bools.menu_content_show_explore)
      ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_EXPLORE;
#endif
}

static int ozone_get_entries_padding_old_list(ozone_handle_t* ozone)
{
   if (ozone->depth == 2) /* false = left to right */
      if (!(ozone->flags & OZONE_FLAG_FADE_DIRECTION))
         return ozone->dimensions.entry_padding_horizontal_half;
   return ozone->dimensions.entry_padding_horizontal_full;
}

static int ozone_get_entries_padding(ozone_handle_t* ozone)
{
   if (ozone->depth == 1)
      return ozone->dimensions.entry_padding_horizontal_half;
   return ozone->dimensions.entry_padding_horizontal_full;
}

static void ozone_draw_entry_value(
      ozone_handle_t *ozone,
      gfx_display_t *p_disp,
      void *userdata,
      unsigned video_width,
      unsigned video_height,
      char *value,
      unsigned x, unsigned y,
      uint32_t alpha_uint32,
      menu_entry_t *entry,
      math_matrix_4x4 *mymat)
{
   bool switch_is_on                 = true;
   bool do_draw_text                 = false;
   float scale_factor                = ozone->last_scale_factor;
   gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;

   /* Check icon */
   if (entry->flags & MENU_ENTRY_FLAG_CHECKED)
   {
      float *col = ozone->theme_dynamic.entries_checkmark;
      if (dispctx && dispctx->blend_begin)
         dispctx->blend_begin(userdata);
      ozone_draw_icon(
            p_disp,
            userdata,
            video_width,
            video_height,
            30 * scale_factor,
            30 * scale_factor,
            ozone->theme->name
                  ? ozone->theme->textures[OZONE_THEME_TEXTURE_CHECK]
                  : ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CHECKMARK],
            x - 20 * scale_factor,
            y - 22 * scale_factor,
            video_width,
            video_height,
            0.0f,
            1.0f,
            col,
            mymat);
      if (dispctx && dispctx->blend_end)
         dispctx->blend_end(userdata);
      return;
   }
   else if (string_is_empty(value))
      return;

   /* Text value */
   if (     string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED))
         || string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)))
      switch_is_on = false;
   else if (string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED))
         || string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON))) { }
   else
   {
      if (!string_is_empty(entry->value))
      {
         if (string_is_equal(entry->value, "..."))
            return;
         if (string_starts_with_size(entry->value, "(", STRLEN_CONST("("))
               && string_ends_with  (entry->value, ")"))
         {
            if (
                     string_is_equal(entry->value, "(PRESET)")
                  || string_is_equal(entry->value, "(SHADER)")
                  || string_is_equal(entry->value, "(COMP)")
                  || string_is_equal(entry->value, "(CORE)")
                  || string_is_equal(entry->value, "(MOVIE)")
                  || string_is_equal(entry->value, "(MUSIC)")
                  || string_is_equal(entry->value, "(DIR)")
                  || string_is_equal(entry->value, "(RDB)")
                  || string_is_equal(entry->value, "(CURSOR)")
                  || string_is_equal(entry->value, "(CFILE)")
                  || string_is_equal(entry->value, "(FILE)")
                  || string_is_equal(entry->value, "(IMAGE)")
               )
               return;
         }
      }

      do_draw_text = true;
   }

   if (do_draw_text)
   {
      gfx_display_draw_text(
            ozone->fonts.entries_label.font,
            value,
            x,
            y,
            video_width,
            video_height,
            !string_is_equal(value, "null")
                  ? COLOR_TEXT_ALPHA(ozone->theme->text_selected_rgba, alpha_uint32)
                  : COLOR_TEXT_ALPHA(ozone->theme->text_selected_rgba, alpha_uint32 >> 1),
            TEXT_ALIGN_RIGHT,
            1.0f,
            false,
            1.0f,
            false);
   }
   else
      gfx_display_draw_text(
            ozone->fonts.entries_label.font,
            (switch_is_on
                  ? msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)
                  : msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)),
            x,
            y,
            video_width,
            video_height,
            switch_is_on
                  ? COLOR_TEXT_ALPHA(ozone->theme->text_selected_rgba, alpha_uint32)
                  : COLOR_TEXT_ALPHA(ozone->theme->text_selected_rgba, alpha_uint32 >> 1),
            TEXT_ALIGN_RIGHT,
            1.0f,
            false,
            1.0f,
            false);
}

static void ozone_draw_no_thumbnail_available(
      ozone_handle_t *ozone,
      gfx_display_t *p_disp,
      void *userdata,
      unsigned video_width,
      unsigned video_height,
      unsigned x_position,
      unsigned y_position,
      unsigned view_width,
      unsigned view_height,
      bool draw_text,
      math_matrix_4x4 *mymat)
{
   gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
   unsigned icon_size                = (unsigned)ozone->dimensions.sidebar_entry_icon_size * 2.0f;
   float                        *col = ozone->theme_dynamic.entries_icon;

   ozone->flags                     |= OZONE_FLAG_NO_THUMBNAIL_AVAILABLE;

   if (draw_text)
      icon_size *= 2;

   if (dispctx)
   {
      gfx_display_set_alpha(col, 0.20f);

      if (dispctx->blend_begin)
         dispctx->blend_begin(userdata);
      if (dispctx->draw)
         ozone_draw_icon(
               p_disp,
               userdata,
               video_width,
               video_height,
               icon_size,
               icon_size,
               ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_IMAGE],
               x_position + ((view_width - icon_size) / 2),
               video_height - y_position - icon_size - ((view_height - icon_size) / 2),
               video_width,
               video_height,
               0.0f,
               1.0f,
               col,
               mymat);
      if (dispctx->blend_end)
         dispctx->blend_end(userdata);
   }

   if (!draw_text)
      return;

   gfx_display_draw_text(
         ozone->fonts.footer.font,
         msg_hash_to_str(MSG_NO_THUMBNAIL_AVAILABLE),
         x_position + (view_width / 2),
         video_height - y_position - ((view_height - icon_size) / 2),
         video_width,
         video_height,
         ozone->theme->text_rgba,
         TEXT_ALIGN_CENTER,
         1.0f,
         false,
         1.0f,
         true);
}

static void ozone_content_metadata_line(
      unsigned video_width,
      unsigned video_height,
      ozone_handle_t *ozone,
      unsigned *y,
      unsigned column_x,
      const char *text,
      uint32_t color,
      uint8_t lines_count)
{
   if (*y + (lines_count * (unsigned)ozone->fonts.footer.line_height)
         > video_height - ozone->dimensions.footer_height)
      return;

   gfx_display_draw_text(
         ozone->fonts.footer.font,
         text,
         column_x,
         *y + ozone->fonts.footer.line_ascender,
         video_width,
         video_height,
         color,
         TEXT_ALIGN_LEFT,
         1.0f,
         false,
         1.0f,
         true);

   if (lines_count > 0)
      *y += (unsigned)(ozone->fonts.footer.line_height * (lines_count - 1))
            + (unsigned)((float)ozone->fonts.footer.line_height * 1.5f);
}


/* Compute new scroll position
 * If the center of the currently selected entry is not in the middle
 * And if we can scroll so that it's in the middle
 * Then scroll
 */
static void ozone_update_scroll(ozone_handle_t *ozone,
      bool allow_animation, ozone_node_t *node)
{
   unsigned video_info_height;
   gfx_animation_ctx_entry_t entry;
   float new_scroll = 0, entries_middle;
   float bottom_boundary, current_selection_middle_onscreen;
   struct menu_state *menu_st = menu_state_get_ptr();
   menu_input_t *menu_input   = &menu_st->input_state;
   menu_list_t *menu_list     = menu_st->entries.list;
   file_list_t *selection_buf = MENU_LIST_GET_SELECTION(menu_list, 0);
   uintptr_t tag              = (uintptr_t)selection_buf;

   video_driver_get_size(NULL, &video_info_height);

   if (!node)
      return;

   current_selection_middle_onscreen    = ozone->dimensions.header_height
         + ozone->dimensions.entry_padding_vertical
         + ozone->animations.scroll_y
         + node->position_y
         + node->height / 2;

   bottom_boundary                      = video_info_height
         - ozone->dimensions.header_height
         - ozone->dimensions.spacer_1px
         - ozone->dimensions.footer_height;
   entries_middle                       = video_info_height / 2;

   new_scroll = ozone->animations.scroll_y - (current_selection_middle_onscreen - entries_middle);

   if (new_scroll + ozone->entries_height < bottom_boundary)
      new_scroll = bottom_boundary - ozone->entries_height - ozone->dimensions.entry_padding_vertical * 2;

   if (new_scroll > 0)
      new_scroll = 0;

   /* Kill any existing scroll animation */
   gfx_animation_kill_by_tag(&tag);

   /* ozone->animations.scroll_y will be modified
    * > Set scroll acceleration to zero to minimise
    *   potential conflicts */
   menu_input->pointer.y_accel    = 0.0f;

   if (allow_animation)
   {
      /* Cursor animation */
      ozone->animations.cursor_alpha = 0.0f;

      entry.cb             = NULL;
      entry.duration       = ANIMATION_CURSOR_DURATION;
      entry.easing_enum    = OZONE_EASING_ALPHA;
      entry.subject        = &ozone->animations.cursor_alpha;
      entry.tag            = tag;
      entry.target_value   = 1.0f;
      entry.userdata       = NULL;

      gfx_animation_push(&entry);

      /* Scroll animation */
      entry.cb             = NULL;
      entry.duration       = ANIMATION_CURSOR_DURATION;
      entry.easing_enum    = OZONE_EASING_ALPHA;
      entry.subject        = &ozone->animations.scroll_y;
      entry.tag            = tag;
      entry.target_value   = new_scroll;
      entry.userdata       = NULL;

      gfx_animation_push(&entry);
   }
   else
   {
      ozone->selection_old       = ozone->selection;
      ozone->animations.scroll_y = new_scroll;
   }
}

static int ozone_get_sublabel_max_width(ozone_handle_t *ozone,
      unsigned video_info_width, unsigned entry_padding)
{
   int sublabel_max_width = video_info_width
         - (entry_padding * 2)
         - (ozone->dimensions.entry_icon_padding * 2);

   if (ozone->depth == 1)
      sublabel_max_width -= (int)ozone->dimensions_sidebar_width;
   if (ozone->show_thumbnail_bar)
      sublabel_max_width -= ozone->dimensions.thumbnail_bar_width;

   return sublabel_max_width;
}

static void ozone_compute_entries_position(ozone_handle_t *ozone,
      bool savestate_thumbnail_enable,
      bool menu_show_sublabels, size_t entries_end)
{
   size_t i;
   /* Compute entries height and adjust scrolling if needed */
   unsigned video_info_height;
   unsigned video_info_width;
   struct menu_state *menu_st    = menu_state_get_ptr();
   menu_list_t *menu_list        = menu_st->entries.list;
   file_list_t *selection_buf    = NULL;
   int entry_padding             = ozone_get_entries_padding(ozone);
   int sublabel_max_width        = 0;
   float scale_factor            = ozone->last_scale_factor;
   bool want_thumbnail_bar       = (ozone->flags & OZONE_FLAG_WANT_THUMBNAIL_BAR) ? true : false;
   bool show_thumbnail_bar       = ozone->show_thumbnail_bar;

   if (show_thumbnail_bar != want_thumbnail_bar)
   {
      if (!(   (ozone->flags & OZONE_FLAG_PENDING_HIDE_THUMBNAIL_BAR)
            && (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU)))
         ozone_entries_update_thumbnail_bar(ozone, savestate_thumbnail_enable,
               false, false);
   }

   if (ozone->show_thumbnail_bar)
      ozone_update_content_metadata(ozone);

   selection_buf                 = MENU_LIST_GET_SELECTION(menu_list, 0);

   if (!selection_buf->size)
      return;

   video_driver_get_size(&video_info_width, &video_info_height);

   if (menu_show_sublabels)
      sublabel_max_width         = ozone_get_sublabel_max_width(ozone, video_info_width, entry_padding);

   ozone->entries_height         = 0;

   for (i = 0; i < entries_end; i++)
   {
      /* Entry */
      menu_entry_t entry;
      ozone_node_t *node       = NULL;

      MENU_ENTRY_INITIALIZE(entry);
      entry.flags |= MENU_ENTRY_FLAG_SUBLABEL_ENABLED;
      menu_entry_get(&entry, 0, (unsigned)i, NULL, true);

      /* Empty playlist detection:
         only one item which icon is
         OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO */
      if (     (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
            && (entries_end == 1))
      {
         uintptr_t         tex = ozone_entries_icon_get_texture(ozone,
               entry.enum_idx, entry.path, entry.label, entry.type, false);
         if (tex == ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO])
            ozone->flags      |=  OZONE_FLAG_EMPTY_PLAYLIST;
         else
            ozone->flags      &= ~OZONE_FLAG_EMPTY_PLAYLIST;
      }
      else
         ozone->flags         &= ~OZONE_FLAG_EMPTY_PLAYLIST;

      /* Cache node */
      if (!(node = (ozone_node_t*)selection_buf->list[i].userdata))
         continue;

      node->height             = ozone->dimensions.entry_height;
      node->wrap               = false;
      node->sublabel_lines     = 0;

      if (menu_show_sublabels)
      {
         if (!string_is_empty(entry.sublabel))
         {
            char wrapped_sublabel_str[MENU_LABEL_MAX_LENGTH];

            wrapped_sublabel_str[0] = '\0';

            (ozone->word_wrap)(wrapped_sublabel_str,
                  sizeof(wrapped_sublabel_str),
                  entry.sublabel,
                  strlen(entry.sublabel),
                  sublabel_max_width / ozone->fonts.entries_sublabel.glyph_width,
                  ozone->fonts.entries_sublabel.wideglyph_width,
                  0);

            node->sublabel_lines = ozone_count_lines(wrapped_sublabel_str);
            node->height        += ozone->dimensions.entry_spacing + 40 * scale_factor;

            if (node->sublabel_lines > 1)
            {
               node->height += (node->sublabel_lines - 1) * ozone->fonts.entries_sublabel.line_height;
               node->wrap    = true;
            }
         }
      }

      node->position_y       = ozone->entries_height;
      ozone->entries_height += node->height;
   }

   /* Update scrolling */
   ozone->selection          = menu_st->selection_ptr;
   ozone_update_scroll(ozone, false, (ozone_node_t*)selection_buf->list[ozone->selection].userdata);
}

static void ozone_draw_entries(
      ozone_handle_t *ozone,
      gfx_display_t *p_disp,
      gfx_animation_t *p_anim,
      settings_t *settings,
      void *userdata,
      unsigned video_width,
      unsigned video_height,
      unsigned selection,
      unsigned selection_old,
      file_list_t *selection_buf,
      float alpha,
      float scroll_y,
      bool is_playlist,
      math_matrix_4x4 *mymat)
{
   size_t i;
   uint32_t alpha_uint32;
   float bottom_boundary;
   unsigned video_info_height, video_info_width;
   bool menu_show_sublabels          = settings->bools.menu_show_sublabels;
   bool use_smooth_ticker            = settings->bools.menu_ticker_smooth;
   unsigned show_history_icons       = settings->uints.playlist_show_history_icons;
   enum gfx_animation_ticker_type
         menu_ticker_type            =
         (enum gfx_animation_ticker_type)settings->uints.menu_ticker_type;
   bool old_list                     = selection_buf == &ozone->selection_buf_old;
   int x_offset                      = 0;
   size_t selection_y                = 0; /* 0 means no selection (we assume that no entry has y = 0) */
   size_t old_selection_y            = 0;
   int entry_padding                 = old_list
         ? ozone_get_entries_padding_old_list(ozone)
         : ozone_get_entries_padding(ozone);
   int sublabel_max_width            = 0;
   float scale_factor                = ozone->last_scale_factor;
   gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
   size_t entries_end                = selection_buf ? selection_buf->size : 0;
   size_t y                          = ozone->dimensions.header_height
         + ozone->dimensions.spacer_1px
         + ozone->dimensions.entry_padding_vertical;
   float sidebar_offset              = ozone->sidebar_offset;
   unsigned entry_width              = video_width
         - (unsigned)ozone->dimensions_sidebar_width
         - ozone->sidebar_offset
         - entry_padding * 2
         - ozone->animations.thumbnail_bar_position;
   unsigned button_height            = ozone->dimensions.entry_height; /* height of the button (entry minus sublabel) */
   float invert                      = (ozone->flags & OZONE_FLAG_FADE_DIRECTION) ? -1 : 1;
   float alpha_anim                  = old_list ? alpha : 1.0f - alpha;

   video_driver_get_size(&video_info_width, &video_info_height);

   bottom_boundary                   = video_info_height
         - ozone->dimensions.header_height
         - ozone->dimensions.footer_height;

   if (menu_show_sublabels)
      sublabel_max_width             = ozone_get_sublabel_max_width(ozone, video_info_width, entry_padding);

   if (old_list)
   {
      alpha = 1.0f - alpha;
      if (alpha != 1.0f)
         x_offset += invert * -(alpha_anim * 120 * scale_factor); /* left */
   }
   else
   {
      if (alpha != 1.0f)
         x_offset += invert *  (alpha_anim * 120 * scale_factor);  /* right */
   }

   x_offset       += (int)sidebar_offset;
   alpha          *= ozone->animations.alpha;
   alpha_uint32    = (uint32_t)(alpha * 255.0f);

   /* Borders layer */
   for (i = 0; i < entries_end; i++)
   {
      bool entry_selected     = selection == i;
      bool entry_old_selected = selection_old == i;

      int border_start_x, border_start_y;

      ozone_node_t *node      = NULL;

      if (entry_selected && selection_y == 0)
         selection_y = y;

      if (entry_old_selected && old_selection_y == 0)
         old_selection_y = y;

      node                    = (ozone_node_t*)selection_buf->list[i].userdata;

      if (!node || (ozone->flags & OZONE_FLAG_EMPTY_PLAYLIST))
         goto border_iterate;

      if (y + scroll_y + node->height + 20 * scale_factor < ozone->dimensions.header_height + ozone->dimensions.entry_padding_vertical)
         goto border_iterate;
      else if (y + scroll_y - node->height - 20 * scale_factor > bottom_boundary)
         goto border_iterate;

      border_start_x = (unsigned)ozone->dimensions_sidebar_width + x_offset + entry_padding;
      border_start_y = y + scroll_y;

      gfx_display_set_alpha(ozone->theme_dynamic.entries_border, alpha);
      gfx_display_set_alpha(ozone->theme_dynamic.entries_checkmark, alpha);

      /* Borders */
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            border_start_x,
            border_start_y,
            entry_width,
            ozone->dimensions.spacer_1px,
            video_width,
            video_height,
            ozone->theme_dynamic.entries_border,
            NULL);
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            border_start_x,
            border_start_y + button_height,
            entry_width,
            ozone->dimensions.spacer_1px,
            video_width,
            video_height,
            ozone->theme_dynamic.entries_border,
            NULL);

border_iterate:
      if (node)
         y += node->height;
   }

   /* Cursor(s) layer - current */
   if (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR))
      ozone_draw_cursor(
            ozone,
            p_disp,
            userdata,
            video_width,
            video_height,
            ozone->dimensions_sidebar_width + x_offset + entry_padding + ozone->dimensions.spacer_3px,
            entry_width - ozone->dimensions.spacer_5px,
            button_height + ozone->dimensions.spacer_1px,
            selection_y + scroll_y,
            ozone->animations.cursor_alpha * alpha,
            mymat);

   /* Old */
   if (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR_OLD))
      ozone_draw_cursor(
            ozone,
            p_disp,
            userdata,
            video_width,
            video_height,
            (unsigned)ozone->dimensions_sidebar_width + x_offset + entry_padding + ozone->dimensions.spacer_3px,
            /* TODO/FIXME - undefined behavior reported by ASAN -
             *-35.2358 is outside the range of representable values
             of type 'unsigned int'
             * */
            entry_width - ozone->dimensions.spacer_5px,
            button_height + ozone->dimensions.spacer_1px,
            old_selection_y + scroll_y,
            (1 - ozone->animations.cursor_alpha) * alpha,
            mymat);

   /* Icons + text */
   y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.entry_padding_vertical;

   if (old_list)
      y += ozone->old_list_offset_y;

   for (i = 0; i < entries_end; i++)
   {
      char rich_label[NAME_MAX_LENGTH];
      char entry_value_ticker[NAME_MAX_LENGTH];
      char wrapped_sublabel_str[MENU_LABEL_MAX_LENGTH];
      uintptr_t texture;
      menu_entry_t entry;
      gfx_animation_ctx_ticker_t ticker;
      gfx_animation_ctx_ticker_smooth_t ticker_smooth;
      unsigned ticker_x_offset     = 0;
      unsigned ticker_str_width    = 0;
      int value_x_offset           = 0;
      const char *sublabel_str     = NULL;
      ozone_node_t *node           = NULL;
      const char *entry_rich_label = NULL;
      const char *entry_value      = NULL;
      bool entry_selected          = false;
      int text_offset              = -ozone->dimensions.entry_icon_padding - ozone->dimensions.entry_icon_size;
      float *icon_color            = NULL;

      /* Initial ticker configuration */
      if (use_smooth_ticker)
      {
         ticker_smooth.idx           = p_anim->ticker_pixel_idx;
         ticker_smooth.font          = ozone->fonts.entries_label.font;
         ticker_smooth.font_scale    = 1.0f;
         ticker_smooth.type_enum     = menu_ticker_type;
         ticker_smooth.spacer        = (ozone->font_unicode) ? OZONE_TICKER_SPACER : NULL;
         ticker_smooth.x_offset      = &ticker_x_offset;
         ticker_smooth.dst_str_width = &ticker_str_width;
      }
      else
      {
         ticker.idx                  = p_anim->ticker_idx;
         ticker.type_enum            = menu_ticker_type;
         ticker.spacer               = (ozone->font_unicode) ? OZONE_TICKER_SPACER : NULL;
      }

      node                           = (ozone_node_t*)selection_buf->list[i].userdata;

      if (!node)
         continue;

      if (y + scroll_y + node->height + 20 * scale_factor < ozone->dimensions.header_height + ozone->dimensions.entry_padding_vertical)
      {
         y += node->height;
         continue;
      }
      else if (y + scroll_y - node->height - 20 * scale_factor > bottom_boundary)
      {
         y += node->height;
         continue;
      }

      entry_selected                 = selection == i;

      MENU_ENTRY_INITIALIZE(entry);
      entry.flags    |= MENU_ENTRY_FLAG_RICH_LABEL_ENABLED
                      | MENU_ENTRY_FLAG_VALUE_ENABLED
                      | MENU_ENTRY_FLAG_SUBLABEL_ENABLED;
      if (ozone->flags & OZONE_FLAG_IS_CONTENTLESS_CORES)
         entry.flags |= MENU_ENTRY_FLAG_LABEL_ENABLED;
      menu_entry_get(&entry, 0, (unsigned)i, selection_buf, true);

      if (entry.enum_idx == MENU_ENUM_LABEL_CHEEVOS_PASSWORD)
         entry_value         = entry.password_value;
      else
         entry_value         = entry.value;

      /* Prepare text */
      if (!string_is_empty(entry.rich_label))
         entry_rich_label  = entry.rich_label;
      else
         entry_rich_label  = entry.path;

      if (use_smooth_ticker)
      {
         ticker_smooth.selected    = entry_selected && (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR));
         ticker_smooth.field_width = entry_width - ozone->dimensions.entry_icon_padding * 6;
         ticker_smooth.src_str     = entry_rich_label;
         ticker_smooth.dst_str     = rich_label;
         ticker_smooth.dst_str_len = sizeof(rich_label);

         gfx_animation_ticker_smooth(&ticker_smooth);
      }
      else
      {
         ticker.s        = rich_label;
         ticker.str      = entry_rich_label;
         ticker.selected = entry_selected && (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR));
         ticker.len      = (entry_width - ozone->dimensions.entry_icon_padding * 6) / ozone->fonts.entries_label.glyph_width;

         gfx_animation_ticker(&ticker);
      }

      if (ozone->flags & OZONE_FLAG_EMPTY_PLAYLIST)
      {
         /* Note: This entry can never be selected, so ticker_x_offset
          * is irrelevant here (i.e. this text will never scroll) */
         unsigned text_width = font_driver_get_message_width(ozone->fonts.entries_label.font, rich_label, strlen(rich_label), 1.0f);
         x_offset            = (video_info_width - (unsigned)ozone->dimensions_sidebar_width - entry_padding * 2) / 2 - (text_width / 2) - (60 * scale_factor);
         y                   = (video_info_height / 2) - (60 * scale_factor);
      }

      sublabel_str = entry.sublabel;

      if (menu_show_sublabels)
      {
         if (node->wrap && !string_is_empty(sublabel_str))
         {
            wrapped_sublabel_str[0] = '\0';

            (ozone->word_wrap)(wrapped_sublabel_str,
                  sizeof(wrapped_sublabel_str),
                  sublabel_str,
                  strlen(sublabel_str),
                  sublabel_max_width / ozone->fonts.entries_sublabel.glyph_width,
                  ozone->fonts.entries_sublabel.wideglyph_width,
                  0);

            sublabel_str = wrapped_sublabel_str;
         }
      }

      /* Icon */
      texture = ozone_entries_icon_get_texture(ozone,
            entry.enum_idx, entry.path, entry.label, entry.type, entry_selected);

      if (texture)
      {
         /* Console specific icons */
         if (     entry.type == FILE_TYPE_RPL_ENTRY
               && ozone->categories_selection_ptr > ozone->system_tab_end)
         {
            ozone_node_t *sidebar_node = (ozone_node_t*)
                  file_list_get_userdata_at_offset(&ozone->horizontal_list,
                        ozone->categories_selection_ptr - ozone->system_tab_end - 1);

            if (sidebar_node && sidebar_node->content_icon)
               texture = sidebar_node->content_icon;
         }
         /* Playlist manager icons */
         else if (ozone->depth == 3 && entry.enum_idx == MENU_ENUM_LABEL_PLAYLIST_MANAGER_SETTINGS)
         {
            if (string_is_equal(entry.rich_label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_HISTORY_TAB)))
               texture = ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_HISTORY];
            else if (string_is_equal(entry.rich_label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES_TAB)))
               texture = ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FAVORITES];
#ifdef HAVE_IMAGEVIEWER
            else if (string_is_equal(entry.rich_label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_IMAGES_TAB)))
               texture = ozone->icons_textures[OZONE_TAB_TEXTURE_IMAGE];
#endif
            else if (string_is_equal(entry.rich_label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MUSIC_TAB)))
               texture = ozone->icons_textures[OZONE_TAB_TEXTURE_MUSIC];
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
            else if (string_is_equal(entry.rich_label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_TAB)))
               texture = ozone->icons_textures[OZONE_TAB_TEXTURE_VIDEO];
#endif
            else if (i < ozone->horizontal_list.size)
            {
               ozone_node_t *sidebar_node = NULL;
               unsigned offset            = 0;

               /* Ignore Explore Views */
               for (offset = 0; offset < ozone->horizontal_list.size; offset++)
               {
                  char playlist_file_noext[NAME_MAX_LENGTH];
                  fill_pathname(playlist_file_noext, ozone->horizontal_list.list[offset].path, "", sizeof(playlist_file_noext));
                  if (string_is_equal(playlist_file_noext, entry.rich_label))
                     break;
               }

               sidebar_node = (ozone_node_t*)file_list_get_userdata_at_offset(&ozone->horizontal_list, offset);
               if (sidebar_node && sidebar_node->icon)
                  texture = sidebar_node->icon;
            }
         }
         /* "Main Menu" playlists */
         else if (ozone->tabs[ozone->categories_selection_ptr] == OZONE_SYSTEM_TAB_MAIN)
         {
            if (ozone_is_main_menu_playlist(ozone))
            {
               const struct playlist_entry *pl_entry = NULL;
               ozone_node_t *db_node                 = NULL;

               playlist_get_index(playlist_get_cached(),
                     entry.entry_idx, &pl_entry);

               if (pl_entry
                     && !string_is_empty(pl_entry->db_name)
                     && (db_node = RHMAP_GET_STR(ozone->playlist_db_node_map, pl_entry->db_name)))
               {
                  switch (show_history_icons)
                  {
                     case PLAYLIST_SHOW_HISTORY_ICONS_MAIN:
                        texture = db_node->icon;
                        break;
                     case PLAYLIST_SHOW_HISTORY_ICONS_CONTENT:
                        texture = db_node->content_icon;
                        break;
                     default:
                        break;
                  }
               }
            }
            else if (ozone->depth == 2 && entry.type == FILE_TYPE_PLAYLIST_COLLECTION)
            {
               ozone_node_t *sidebar_node = (ozone_node_t*)
                     (ozone->horizontal_list.size)
                        ? (ozone_node_t*)file_list_get_userdata_at_offset(
                              &ozone->horizontal_list, selection_buf->list[i].entry_idx)
                        : NULL;

               if (sidebar_node && sidebar_node->icon)
                  texture = sidebar_node->icon;
            }
         }
         /* History/Favorite console specific content icons */
         else if (   entry.type == FILE_TYPE_RPL_ENTRY
                  && show_history_icons != PLAYLIST_SHOW_HISTORY_ICONS_DEFAULT)
         {
            switch (ozone->tabs[ozone->categories_selection_ptr])
            {
               case OZONE_SYSTEM_TAB_MAIN:
               case OZONE_SYSTEM_TAB_HISTORY:
               case OZONE_SYSTEM_TAB_FAVORITES:
                  {
                     const struct playlist_entry *pl_entry = NULL;
                     ozone_node_t *db_node                 = NULL;

                     playlist_get_index(playlist_get_cached(),
                           entry.entry_idx, &pl_entry);

                     if (pl_entry
                           && !string_is_empty(pl_entry->db_name)
                           && (db_node = RHMAP_GET_STR(ozone->playlist_db_node_map, pl_entry->db_name)))
                     {
                        switch (show_history_icons)
                        {
                           case PLAYLIST_SHOW_HISTORY_ICONS_MAIN:
                              texture = db_node->icon;
                              break;
                           case PLAYLIST_SHOW_HISTORY_ICONS_CONTENT:
                              texture = db_node->content_icon;
                              break;
                           default:
                              break;
                        }
                     }
                  }
                  break;
               default:
                  break;
            }
         }

         /* Cheevos badges should not be recolored */
         if (!((entry.type >= MENU_SETTINGS_CHEEVOS_START) && (entry.type < MENU_SETTINGS_NETPLAY_ROOMS_START)))
            icon_color = ozone->theme_dynamic.entries_icon;
         else
            icon_color = ozone->pure_white;

         gfx_display_set_alpha(icon_color, alpha);

         if (dispctx)
         {
            if (dispctx->blend_begin)
               dispctx->blend_begin(userdata);
            if (dispctx->draw)
               ozone_draw_icon(
                     p_disp,
                     userdata,
                     video_width,
                     video_height,
                     ozone->dimensions.entry_icon_size,
                     ozone->dimensions.entry_icon_size,
                     texture,
                     ozone->dimensions_sidebar_width
                           + x_offset
                           + entry_padding
                           + ozone->dimensions.entry_icon_padding,
                     y
                           + scroll_y
                           + ozone->dimensions.entry_height / 2
                           - ozone->dimensions.entry_icon_size / 2,
                     video_width,
                     video_height,
                     0.0f,
                     1.0f,
                     icon_color,
                     mymat);
            if (dispctx->blend_end)
               dispctx->blend_end(userdata);
         }

         if (icon_color == ozone->pure_white)
            gfx_display_set_alpha(icon_color, 1.0f);

         text_offset = 0;
      }

      /* Draw text */
      gfx_display_draw_text(
            ozone->fonts.entries_label.font,
            rich_label,
            ticker_x_offset
                  + text_offset
                  + (unsigned)ozone->dimensions_sidebar_width
                  + x_offset
                  + entry_padding
                  + ozone->dimensions.entry_icon_size
                  + ozone->dimensions.entry_icon_padding * 2,
            y
                  + ozone->dimensions.entry_height / 2.0f
                  + ozone->fonts.entries_label.line_centre_offset
                  + scroll_y,
            video_width,
            video_height,
            COLOR_TEXT_ALPHA(ozone->theme->text_rgba, alpha_uint32),
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

      if (menu_show_sublabels)
      {
         if (!string_is_empty(sublabel_str))
            gfx_display_draw_text(
                  ozone->fonts.entries_sublabel.font,
                  sublabel_str,
                  (unsigned)ozone->dimensions_sidebar_width
                        + x_offset
                        + entry_padding
                        + ozone->dimensions.entry_icon_padding,
                  y
                        + ozone->dimensions.entry_height
                        - ozone->dimensions.spacer_1px
                        + (node->height
                              - ozone->dimensions.entry_height
                              - (node->sublabel_lines * ozone->fonts.entries_sublabel.line_height)) / 2.0f
                        + ozone->fonts.entries_sublabel.line_ascender
                        + scroll_y,
                  video_width,
                  video_height,
                  COLOR_TEXT_ALPHA(ozone->theme->text_sublabel_rgba, alpha_uint32),
                  TEXT_ALIGN_LEFT,
                  1.0f,
                  false,
                  1.0f,
                  false);
      }

      /* Value */
      if (use_smooth_ticker)
      {
         ticker_smooth.selected    = entry_selected && (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR));
         ticker_smooth.field_width = (entry_width
               - ozone->dimensions.entry_icon_size
               - ozone->dimensions.entry_icon_padding * 2
               - ((unsigned)utf8len(entry_rich_label) * ozone->fonts.entries_label.glyph_width));
         ticker_smooth.src_str     = entry_value;
         ticker_smooth.dst_str     = entry_value_ticker;
         ticker_smooth.dst_str_len = sizeof(entry_value_ticker);

         /* Value text is right aligned, so have to offset x
          * by the 'padding' width at the end of the ticker string... */
         if (gfx_animation_ticker_smooth(&ticker_smooth))
            value_x_offset = (ticker_x_offset + ticker_str_width) - ticker_smooth.field_width;
      }
      else
      {
         ticker.s        = entry_value_ticker;
         ticker.str      = entry_value;
         ticker.selected = entry_selected && (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR));
         ticker.len      = (entry_width
               - ozone->dimensions.entry_icon_size
               - ozone->dimensions.entry_icon_padding * 2
               - ((unsigned)utf8len(entry_rich_label) * ozone->fonts.entries_label.glyph_width)) / ozone->fonts.entries_label.glyph_width;

         gfx_animation_ticker(&ticker);
      }

      ozone_draw_entry_value(ozone,
            p_disp,
            userdata,
            video_width,
            video_height,
            entry_value_ticker,
            value_x_offset
                  + (unsigned)ozone->dimensions_sidebar_width
                  + entry_padding
                  + x_offset
                  + entry_width
                  - ozone->dimensions.entry_icon_padding,
            y
                  + ozone->dimensions.entry_height / 2
                  + ozone->fonts.entries_label.line_centre_offset
                  + scroll_y,
            alpha_uint32,
            &entry,
            mymat);

      y += node->height;
   }

   /* Text layer */
   font_flush(video_width, video_height, &ozone->fonts.entries_label);

   if (menu_show_sublabels)
      font_flush(video_width, video_height, &ozone->fonts.entries_sublabel);
}

static void ozone_draw_thumbnail_bar(
      ozone_handle_t *ozone,
      struct menu_state *menu_st,
      gfx_display_t *p_disp,
      gfx_animation_t *p_anim,
      settings_t *settings,
      void *userdata,
      unsigned video_width,
      unsigned video_height,
      bool libretro_running,
      float menu_framebuffer_opacity,
      math_matrix_4x4 *mymat)
{
   enum gfx_thumbnail_alignment right_thumbnail_alignment;
   enum gfx_thumbnail_alignment left_thumbnail_alignment;
   unsigned sidebar_width            = ozone->dimensions.thumbnail_bar_width;
   unsigned thumbnail_width          = sidebar_width - (ozone->dimensions.sidebar_entry_icon_padding * 3);
   int right_thumbnail_y_position    = 0;
   int left_thumbnail_y_position     = 0;
   int bottom_row_y_position         = 0;
   bool show_right_thumbnail         = false;
   bool show_left_thumbnail          = false;
   bool thumbnail_background         = settings->bools.menu_thumbnail_background_enable;
   unsigned sidebar_height           = video_height
         - ozone->dimensions.header_height
         - ozone->dimensions.sidebar_gradient_height * 2
         - ozone->dimensions.footer_height;
   unsigned x_position               = video_width
         - (unsigned)ozone->animations.thumbnail_bar_position;
   int thumbnail_x_position          = x_position
         + ozone->dimensions.sidebar_entry_icon_padding;
   unsigned thumbnail_height         = (video_height
         - ozone->dimensions.header_height
         - ozone->dimensions.spacer_2px
         - ozone->dimensions.footer_height
         - (ozone->dimensions.sidebar_entry_icon_padding * 3)) / 2;
   float scale_factor                = ozone->last_scale_factor;
   gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;

   /* Background */
   if (thumbnail_height)
   {
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            x_position,
            ozone->dimensions.header_height,
            (unsigned)ozone->animations.thumbnail_bar_position,
            ozone->dimensions.sidebar_gradient_height,
            video_width,
            video_height,
            ozone->theme->sidebar_top_gradient,
            NULL);
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            x_position,
            ozone->dimensions.header_height
                  + ozone->dimensions.sidebar_gradient_height,
            (unsigned)ozone->animations.thumbnail_bar_position,
            sidebar_height,
            video_width,
            video_height,
            ozone->theme->sidebar_background,
            NULL);
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            x_position,
            video_height
                  - ozone->dimensions.footer_height
                  - ozone->dimensions.sidebar_gradient_height,
            (unsigned)ozone->animations.thumbnail_bar_position,
            ozone->dimensions.sidebar_gradient_height,
            video_width,
            video_height,
            ozone->theme->sidebar_bottom_gradient,
            NULL);
   }

   /* Thumbnails */
   show_right_thumbnail =
         (ozone->thumbnails.right.status != GFX_THUMBNAIL_STATUS_MISSING)
         && gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT);
   show_left_thumbnail  =
         (ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_MISSING)
         && gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT);

   /* Special "viewer" mode for savestate thumbnails */
   if (     ((ozone->flags & OZONE_FLAG_WANT_THUMBNAIL_BAR) && !string_is_empty(ozone->savestate_thumbnail_file_path))
         || (ozone->flags & OZONE_FLAG_IS_STATE_SLOT))
   {
      ozone->flags2                  |= OZONE_FLAG2_SELECTION_CORE_IS_VIEWER;
      show_left_thumbnail             = false;
      show_right_thumbnail            =
               ozone->thumbnails.savestate.status == GFX_THUMBNAIL_STATUS_AVAILABLE
            || ozone->thumbnails.savestate.status == GFX_THUMBNAIL_STATUS_PENDING;
   }
   else if (!(ozone->flags & OZONE_FLAG_WANT_THUMBNAIL_BAR))
      return;
   else if (ozone->flags & OZONE_FLAG_NO_THUMBNAIL_AVAILABLE)
   {
      /* Remove useless re-blinking of "No thumbnail available" */
      if (show_right_thumbnail && ozone->thumbnails.right.status == GFX_THUMBNAIL_STATUS_UNKNOWN)
         show_right_thumbnail = false;
      if (show_left_thumbnail && ozone->thumbnails.left.status == GFX_THUMBNAIL_STATUS_UNKNOWN)
         show_left_thumbnail  = false;
   }

   /* Top row
    * > Displays one item, with the following order
    *   of preference:
    *   1) Right thumbnail, if available
    *   2) Left thumbnail, if available
    *   3) 'No thumbnail available' message */

   /* > If this entry is associated with the image viewer
    *   core, there can be only one thumbnail and no
    *   content metadata -> centre image vertically */
   if (ozone->flags2 & OZONE_FLAG2_SELECTION_CORE_IS_VIEWER)
   {
      right_thumbnail_y_position = ozone->dimensions.header_height
            + (thumbnail_height / 2)
            + (int)(1.5f * (float)ozone->dimensions.sidebar_entry_icon_padding);

      right_thumbnail_alignment  = GFX_THUMBNAIL_ALIGN_CENTRE;
      show_left_thumbnail        = false;

      if (thumbnail_height > thumbnail_width)
      {
         thumbnail_height /= 2;
         right_thumbnail_y_position += thumbnail_height / 2;
      }
   }
   else
   {
      right_thumbnail_y_position = ozone->dimensions.header_height
            + ozone->dimensions.spacer_1px
            + ozone->dimensions.sidebar_entry_icon_padding;

      right_thumbnail_alignment  = GFX_THUMBNAIL_ALIGN_BOTTOM;
   }

   /* If this entry is associated with the image viewer
    * and no thumbnails are available, show a centred
    * message and return immediately */
   if (     (ozone->flags2 & OZONE_FLAG2_SELECTION_CORE_IS_VIEWER)
         && (!show_right_thumbnail && !show_left_thumbnail))
   {
      float background_color[16]        = {
            0.05f, 0.05f, 0.05f, 1.0f,
            0.05f, 0.05f, 0.05f, 1.0f,
            0.05f, 0.05f, 0.05f, 1.0f,
            0.05f, 0.05f, 0.05f, 1.0f,
      };

      /* Darken background */
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            thumbnail_x_position,
            right_thumbnail_y_position,
            thumbnail_width,
            thumbnail_height,
            video_width,
            video_height,
            background_color,
            NULL);

      ozone_draw_no_thumbnail_available(
            ozone,
            p_disp,
            userdata,
            video_width,
            video_height,
            thumbnail_x_position,
            right_thumbnail_y_position,
            thumbnail_width,
            thumbnail_height,
            false,
            mymat);
      return;
   }

   /* > If we have a right thumbnail, show it */
   if (show_right_thumbnail)
   {
      if (thumbnail_background)
      {
         float background_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,
         };

         right_thumbnail_alignment = GFX_THUMBNAIL_ALIGN_CENTRE;

         gfx_display_draw_quad(
               p_disp,
               userdata,
               video_width,
               video_height,
               thumbnail_x_position,
               right_thumbnail_y_position,
               thumbnail_width,
               thumbnail_height,
               video_width,
               video_height,
               background_color,
               NULL);
      }

      gfx_thumbnail_draw(
            userdata,
            video_width,
            video_height,
            (ozone->thumbnails.savestate.status == GFX_THUMBNAIL_STATUS_AVAILABLE ||
             ozone->thumbnails.savestate.status == GFX_THUMBNAIL_STATUS_PENDING)
                  ? &ozone->thumbnails.savestate
                  : &ozone->thumbnails.right,
            (float)thumbnail_x_position,
            (float)right_thumbnail_y_position,
            thumbnail_width,
            thumbnail_height,
            right_thumbnail_alignment,
            1.0f,
            1.0f,
            NULL);
      ozone->flags &= ~OZONE_FLAG_NO_THUMBNAIL_AVAILABLE;
   }
   /* > If we have neither a right thumbnail nor
    *   a left thumbnail to show in its place,
    *   display 'no thumbnail available' message */
   else if (!show_left_thumbnail)
   {
      ozone_draw_no_thumbnail_available(
            ozone,
            p_disp,
            userdata,
            video_width,
            video_height,
            thumbnail_x_position,
            right_thumbnail_y_position + thumbnail_height,
            thumbnail_width,
            thumbnail_height,
            false,
            mymat);
   }

   /* Bottom row
    * > Displays one item, with the following order
    *   of preference:
    *   1) Left thumbnail, if available
    *      *and*
    *      right thumbnail has been placed in the top row
    *      *and*
    *      content metadata override is not enabled
    *   2) Content metadata */

   /* > Get baseline 'start' position of bottom row */
   bottom_row_y_position = ozone->dimensions.header_height
         + ozone->dimensions.spacer_1px
         + thumbnail_height
         + (ozone->dimensions.sidebar_entry_icon_padding * 2);

   /* > If we have a left thumbnail, show it */
   if (show_left_thumbnail)
   {
      float left_thumbnail_alpha;

      /* Normally a right thumbnail will be shown
       * in the top row - if so, left thumbnail
       * goes at the bottom */
      if (show_right_thumbnail)
      {
         left_thumbnail_y_position = bottom_row_y_position;
         left_thumbnail_alignment  = GFX_THUMBNAIL_ALIGN_TOP;
         /* In this case, thumbnail opacity is dependent
          * upon the content metadata override
          * > i.e. Need to handle fade in/out animations
          *   and set opacity to zero when override
          *   is fully active */
         left_thumbnail_alpha      = ozone->animations.left_thumbnail_alpha;
      }
      /* If right thumbnail is missing, shift left
       * thumbnail up to the top row */
      else
      {
         left_thumbnail_y_position = right_thumbnail_y_position;
         left_thumbnail_alignment  = right_thumbnail_alignment;
         /* In this case, there is no dependence on content
          * metadata - thumbnail is always shown at full
          * opacity */
         left_thumbnail_alpha      = 1.0f;
      }

      if (thumbnail_background)
      {
         float background_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,
         };

         background_color[3]  =
         background_color[7]  =
         background_color[11] =
         background_color[15] = left_thumbnail_alpha;

         left_thumbnail_alignment = GFX_THUMBNAIL_ALIGN_CENTRE;

         gfx_display_draw_quad(
               p_disp,
               userdata,
               video_width,
               video_height,
               thumbnail_x_position,
               left_thumbnail_y_position,
               thumbnail_width,
               thumbnail_height,
               video_width,
               video_height,
               background_color,
               NULL);
      }

      /* Note: This is a NOOP when alpha is zero
       * (i.e. no performance impact when content
       * metadata override is fully active) */
      gfx_thumbnail_draw(
            userdata,
            video_width,
            video_height,
            &ozone->thumbnails.left,
            (float)thumbnail_x_position,
            (float)left_thumbnail_y_position,
            thumbnail_width,
            thumbnail_height,
            left_thumbnail_alignment,
            left_thumbnail_alpha,
            1.0f,
            NULL);
      ozone->flags &= ~OZONE_FLAG_NO_THUMBNAIL_AVAILABLE;
   }

   /* > Display content metadata in the bottom
    *   row if:
    *   - This is *not* image viewer content
    *     *and*
    *   - There is no left thumbnail
    *     *or*
    *     left thumbnail has been shifted to
    *     the top row
    *     *or*
    *     content metadata override is enabled
    *     (i.e. fade in, fade out, or fully
    *     active) */
   if (   (!(ozone->flags2 & OZONE_FLAG2_SELECTION_CORE_IS_VIEWER))
       && (!show_left_thumbnail || !show_right_thumbnail || (ozone->animations.left_thumbnail_alpha < 1.0f)))
   {
      char ticker_buf[NAME_MAX_LENGTH];
      gfx_animation_ctx_ticker_t ticker;
      gfx_animation_ctx_ticker_smooth_t ticker_smooth;
      unsigned ticker_x_offset               = 0;
      bool scroll_content_metadata           = settings->bools.ozone_scroll_content_metadata;
      bool use_smooth_ticker                 = settings->bools.menu_ticker_smooth;
      enum gfx_animation_ticker_type
            menu_ticker_type                 = (enum gfx_animation_ticker_type)settings->uints.menu_ticker_type;
      bool show_entry_idx                    = settings->bools.playlist_show_entry_idx;
      bool show_entry_core                   = (!(ozone->flags & OZONE_FLAG_IS_DB_MANAGER_LIST));
      bool show_entry_playtime               = (!(ozone->flags & OZONE_FLAG_IS_DB_MANAGER_LIST));
      bool show_entry_last_played            = (!(ozone->flags & OZONE_FLAG_IS_DB_MANAGER_LIST));
      unsigned y                             = (unsigned)bottom_row_y_position;
      unsigned separator_padding             = ozone->dimensions.sidebar_entry_icon_padding * 2;
      unsigned column_x                      = x_position + separator_padding;
      bool metadata_override_enabled         =
            show_left_thumbnail && show_right_thumbnail && (ozone->animations.left_thumbnail_alpha < 1.0f);
      float metadata_alpha                   = metadata_override_enabled
            ? (1.0f - ozone->animations.left_thumbnail_alpha)
            : 1.0f;
      uint32_t text_color                    = COLOR_TEXT_ALPHA(
            ozone->theme->text_rgba, (uint32_t)(metadata_alpha * 255.0f));

      if (scroll_content_metadata)
      {
         /* Initial ticker configuration */
         if (use_smooth_ticker)
         {
            ticker_smooth.idx                = p_anim->ticker_pixel_idx;
            ticker_smooth.font_scale         = 1.0f;
            ticker_smooth.type_enum          = menu_ticker_type;
            ticker_smooth.spacer             = (ozone->font_unicode) ? OZONE_TICKER_SPACER : NULL;
            ticker_smooth.x_offset           = &ticker_x_offset;
            ticker_smooth.dst_str_width      = NULL;

            ticker_smooth.font               = ozone->fonts.footer.font;
            ticker_smooth.selected           = true;
            ticker_smooth.field_width        = sidebar_width - (separator_padding * 2);
            ticker_smooth.dst_str            = ticker_buf;
            ticker_smooth.dst_str_len        = sizeof(ticker_buf);
         }
         else
         {
            ticker.idx                       = p_anim->ticker_idx;
            ticker.type_enum                 = menu_ticker_type;
            ticker.spacer                    = (ozone->font_unicode) ? OZONE_TICKER_SPACER : NULL;

            ticker.selected                  = true;
            ticker.len                       = (sidebar_width - (separator_padding * 2)) / ozone->fonts.footer.glyph_width;
            ticker.s                         = ticker_buf;
         }
      }

      /* Content metadata */

      /* Separator */
      gfx_display_set_alpha(ozone->theme_dynamic.entries_border, metadata_alpha);

      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            x_position + separator_padding,
            y,
            sidebar_width - separator_padding * 2,
            ozone->dimensions.spacer_1px,
            video_width,
            video_height,
            ozone->theme_dynamic.entries_border,
            NULL);

      y += 18 * scale_factor;

      if (scroll_content_metadata)
      {
         /* Entry enumeration */
         if (show_entry_idx)
         {
            ticker_buf[0] = '\0';

            if (use_smooth_ticker)
            {
               ticker_smooth.src_str = ozone->selection_entry_enumeration;
               gfx_animation_ticker_smooth(&ticker_smooth);
            }
            else
            {
               ticker.str = ozone->selection_entry_enumeration;
               gfx_animation_ticker(&ticker);
            }

            ozone_content_metadata_line(
                  video_width,
                  video_height,
                  ozone,
                  &y,
                  ticker_x_offset + column_x,
                  ticker_buf,
                  text_color,
                  1);
         }

         /* Core association */
         if (show_entry_core)
         {
            ticker_buf[0] = '\0';

            if (use_smooth_ticker)
            {
               ticker_smooth.src_str = ozone->selection_core_name;
               gfx_animation_ticker_smooth(&ticker_smooth);
            }
            else
            {
               ticker.str = ozone->selection_core_name;
               gfx_animation_ticker(&ticker);
            }

            ozone_content_metadata_line(
                  video_width,
                  video_height,
                  ozone,
                  &y,
                  ticker_x_offset + column_x,
                  ticker_buf,
                  text_color,
                  1);
         }

         /* Playtime
          * Note: It is essentially impossible for this string
          * to exceed the width of the sidebar, but since we
          * are ticker-texting everything else, we include this
          * by default */
         if (show_entry_playtime)
         {
            ticker_buf[0] = '\0';

            if (use_smooth_ticker)
            {
               ticker_smooth.src_str = ozone->selection_playtime;
               gfx_animation_ticker_smooth(&ticker_smooth);
            }
            else
            {
               ticker.str = ozone->selection_playtime;
               gfx_animation_ticker(&ticker);
            }

            ozone_content_metadata_line(
                  video_width,
                  video_height,
                  ozone,
                  &y,
                  ticker_x_offset + column_x,
                  ticker_buf,
                  text_color,
                  1);
         }

         /* Last played */
         if (show_entry_last_played)
         {
            ticker_buf[0] = '\0';

            if (use_smooth_ticker)
            {
               ticker_smooth.src_str = ozone->selection_lastplayed;
               gfx_animation_ticker_smooth(&ticker_smooth);
            }
            else
            {
               ticker.str = ozone->selection_lastplayed;
               gfx_animation_ticker(&ticker);
            }

            ozone_content_metadata_line(
                  video_width,
                  video_height,
                  ozone,
                  &y,
                  ticker_x_offset + column_x,
                  ticker_buf,
                  text_color,
                  1);
         }
      }
      else
      {
         /* Entry enumeration */
         if (show_entry_idx)
            ozone_content_metadata_line(
                  video_width,
                  video_height,
                  ozone,
                  &y,
                  column_x,
                  ozone->selection_entry_enumeration,
                  text_color,
                  ozone_count_lines(ozone->selection_entry_enumeration));

         /* Core association */
         if (show_entry_core)
            ozone_content_metadata_line(
                  video_width,
                  video_height,
                  ozone,
                  &y,
                  column_x,
                  ozone->selection_core_name,
                  text_color,
                  ozone->selection_core_name_lines);

         /* Playtime */
         if (show_entry_playtime)
            ozone_content_metadata_line(
                  video_width,
                  video_height,
                  ozone,
                  &y,
                  column_x,
                  ozone->selection_playtime,
                  text_color,
                  ozone_count_lines(ozone->selection_playtime));

         /* Last played */
         if (show_entry_last_played)
            ozone_content_metadata_line(
                  video_width,
                  video_height,
                  ozone,
                  &y,
                  column_x,
                  ozone->selection_lastplayed,
                  text_color,
                  ozone->selection_lastplayed_lines);
      }

      /* If metadata override is active, display an
       * icon to notify that a left thumbnail image
       * is available */
      if (metadata_override_enabled)
      {
         float         *col = ozone->theme_dynamic.entries_icon;
         /* Icon should be small and unobtrusive
          * > Make it 80% of the normal entry icon size */
         unsigned icon_size = (unsigned)((float)ozone->dimensions.sidebar_entry_icon_size * 0.8f);

         /* > Set its opacity to a maximum of 80% */
         gfx_display_set_alpha(ozone->theme_dynamic.entries_icon, metadata_alpha * 0.8f);

         if (dispctx)
         {
            /* Draw icon in the bottom right corner of
             * the thumbnail bar */
            if (dispctx->blend_begin)
               dispctx->blend_begin(userdata);
            if (dispctx->draw)
               ozone_draw_icon(
                     p_disp,
                     userdata,
                     video_width,
                     video_height,
                     icon_size,
                     icon_size,
                     ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_IMAGE],
                     x_position + sidebar_width - separator_padding - icon_size,
                     video_height - ozone->dimensions.footer_height - ozone->dimensions.sidebar_entry_icon_padding - icon_size,
                     video_width,
                     video_height,
                     0.0f,
                     1.0f,
                     col,
                     mymat);
            if (dispctx->blend_end)
               dispctx->blend_end(userdata);
         }
      }
   }
}

static void ozone_draw_backdrop(
      void *userdata,
      void *disp_data,
      unsigned video_width,
      unsigned video_height,
      float alpha)
{
   static float ozone_backdrop[16] = {
      0.00, 0.00, 0.00, 0.75,
      0.00, 0.00, 0.00, 0.75,
      0.00, 0.00, 0.00, 0.75,
      0.00, 0.00, 0.00, 0.75,
   };
   static float last_alpha         = 0.0f;

   /* TODO: Replace this backdrop by a blur shader
    * on the whole screen if available */
   if (alpha != last_alpha)
   {
      gfx_display_set_alpha(ozone_backdrop, alpha);
      last_alpha = alpha;
   }

   gfx_display_draw_quad(
         (gfx_display_t*)disp_data,
         userdata,
         video_width,
         video_height,
         0,
         0,
         video_width,
         video_height,
         video_width,
         video_height,
         ozone_backdrop,
         NULL);
}

static void ozone_draw_osk(
      ozone_handle_t *ozone,
      void *userdata,
      void *disp_userdata,
      unsigned video_width,
      unsigned video_height,
      const char *label, const char *str)
{
   char message[2048];
   gfx_display_t *p_disp          = (gfx_display_t*)disp_userdata;
   const char *text               = str;
   unsigned text_color            = 0xffffffff;
   static float ozone_osk_backdrop[16] = {
      0.00, 0.00, 0.00, 0.15,
      0.00, 0.00, 0.00, 0.15,
      0.00, 0.00, 0.00, 0.15,
      0.00, 0.00, 0.00, 0.15,
   };
   char *tok, *save               = NULL;
   unsigned i                     = 0;
   static retro_time_t last_time  = 0;
   unsigned list_size             = 0;
   float scale_factor             = ozone->last_scale_factor;
   unsigned margin                = 75 * scale_factor;
   unsigned padding               = 10 * scale_factor;
   unsigned bottom_end            = video_height / 2;
   unsigned y_offset              = 0;
   bool draw_placeholder          = string_is_empty(str);
   retro_time_t current_time      = menu_driver_get_current_time();

   if (current_time - last_time >= INTERVAL_OSK_CURSOR)
   {
      if (ozone->flags & OZONE_FLAG_OSK_CURSOR)
         ozone->flags            &= ~OZONE_FLAG_OSK_CURSOR;
      else
         ozone->flags            |=  OZONE_FLAG_OSK_CURSOR;
      last_time                   = current_time;
   }

   /* Border */
   /* Top */
   gfx_display_draw_quad(
         p_disp,
         userdata,
         video_width,
         video_height,
         margin,
         margin,
         video_width - (margin * 2),
         ozone->dimensions.spacer_1px,
         video_width,
         video_height,
         ozone->theme->entries_border,
         NULL);

   /* Bottom */
   gfx_display_draw_quad(
         p_disp,
         userdata,
         video_width,
         video_height,
         margin,
         bottom_end - margin,
         video_width - (margin * 2),
         ozone->dimensions.spacer_1px,
         video_width,
         video_height,
         ozone->theme->entries_border,
         NULL);

   /* Left */
   gfx_display_draw_quad(
         p_disp,
         userdata,
         video_width,
         video_height,
         margin,
         margin,
         ozone->dimensions.spacer_1px,
         bottom_end - (margin * 2),
         video_width,
         video_height,
         ozone->theme->entries_border,
         NULL);

   /* Right */
   gfx_display_draw_quad(
         p_disp,
         userdata,
         video_width,
         video_height,
         video_width - margin,
         margin,
         ozone->dimensions.spacer_1px,
         bottom_end - (margin * 2),
         video_width,
         video_height,
         ozone->theme->entries_border,
         NULL);

   /* Backdrop */
   /* TODO: Remove the backdrop if blur shader is available */
   gfx_display_draw_quad(
         p_disp,
         userdata,
         video_width,
         video_height,
         margin + ozone->dimensions.spacer_1px,
         margin + ozone->dimensions.spacer_1px,
         video_width - (margin * 2) - ozone->dimensions.spacer_2px,
         bottom_end - (margin * 2) - ozone->dimensions.spacer_2px,
         video_width,
         video_height,
         ozone_osk_backdrop,
         NULL);

   /* Placeholder & text*/
   if (draw_placeholder)
   {
      text        = label;
      text_color  = ozone_theme_light.text_sublabel_rgba;
   }

   (ozone->word_wrap)(message,
         sizeof(message),
         text,
         strlen(text),
         (video_width - (margin * 2) - (padding * 2)) / ozone->fonts.entries_label.glyph_width,
         ozone->fonts.entries_label.wideglyph_width,
         0);

   tok       = strtok_r(message, "\n", &save);
   list_size = string_count_occurrences_single_character(message, '\n');

   while (tok)
   {
      const char *msg = tok;

      gfx_display_draw_text(
            ozone->fonts.entries_label.font,
            msg,
            margin + (padding * 2),
            margin + padding + ozone->fonts.entries_label.line_height + y_offset,
            video_width,
            video_height,
            text_color,
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

      /* Cursor */
      if (i == list_size - 1)
      {
         if (ozone->flags & OZONE_FLAG_OSK_CURSOR)
         {
            unsigned cursor_x = draw_placeholder
                  ? 0
                  : font_driver_get_message_width(ozone->fonts.entries_label.font, msg, strlen(msg), 1.0f);
            gfx_display_draw_quad(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  margin
                        + (padding * 2)
                        + cursor_x,
                  margin
                        + padding
                        + y_offset
                        + ozone->fonts.entries_label.line_height
                        - ozone->fonts.entries_label.line_ascender
                        + ozone->dimensions.spacer_3px,
                  ozone->dimensions.spacer_1px,
                  ozone->fonts.entries_label.line_ascender,
                  video_width,
                  video_height,
                  ozone->pure_white,
                  NULL);
         }
      }
      else
         y_offset += 25 * scale_factor;

      tok = strtok_r(NULL, "\n", &save);
      i++;
   }

   /* Keyboard */
   {
      input_driver_state_t *input_st = input_state_get_ptr();
      gfx_display_draw_keyboard(
            p_disp,
            userdata,
            video_width,
            video_height,
            ozone->theme->name
                  ? ozone->theme->textures[OZONE_THEME_TEXTURE_CURSOR_STATIC]
                  : ozone->textures[OZONE_TEXTURE_CURSOR_BORDER],
            ozone->fonts.entries_label.font,
            input_st->osk_grid,
            input_st->osk_ptr,
            ozone->theme->text_rgba);
   }
}

static void ozone_draw_messagebox(
      ozone_handle_t *ozone,
      gfx_display_t *p_disp,
      void *userdata,
      unsigned video_width,
      unsigned video_height,
      const char *message,
      math_matrix_4x4 *mymat)
{
   size_t x, y;
   char wrapped_message[MENU_LABEL_MAX_LENGTH];
   int longest_width        = 0;
   int usable_width         = 0;
   struct string_list list  = {0};
   float scale_factor       = 0.0f;
   float line_height        = 0;
   unsigned i               = 0;
   unsigned y_position      = 0;
   unsigned width           = video_width;
   unsigned height          = video_height;
   font_data_impl_t font_data
                            = ozone->fonts.entries_label;
   gfx_display_ctx_driver_t
         *dispctx           = p_disp->dispctx;

   wrapped_message[0]       = '\0';

   /* Sanity check */
   if (string_is_empty(message) || !font_data.font)
      return;

   scale_factor             = ozone->last_scale_factor;
   usable_width             = (int)width - (150 * 8 * scale_factor) / ((float)width / (float)height * 2.0f);
   line_height              = font_data.line_height * 1.10f;

   if (usable_width < 1)
      return;

   /* Split message into lines */
   (ozone->word_wrap)(
         wrapped_message,
         sizeof(wrapped_message),
         message,
         strlen(message),
         usable_width / (int)font_data.glyph_width,
         font_data.wideglyph_width,
         0);

   string_list_initialize(&list);
   if (     !string_split_noalloc(&list, wrapped_message, "\n")
         || list.elems == 0)
   {
      string_list_deinitialize(&list);
      return;
   }

   y_position       = height / 2;
   if (menu_input_dialog_get_display_kb())
      y_position    = height / 4;

   x                = width  / 2;
   y                = y_position - (list.size * line_height) / 2;

   /* find the longest line width */
   for (i = 0; i < list.size; i++)
   {
      const char *msg = list.elems[i].data;

      if (!string_is_empty(msg))
      {
         int width = font_driver_get_message_width(font_data.font, msg, strlen(msg), 1.0f);

         if (width > longest_width)
            longest_width = width;
      }
   }

   gfx_display_set_alpha(ozone->theme_dynamic.message_background, ozone->animations.messagebox_alpha);

   if (dispctx && dispctx->blend_begin)
      dispctx->blend_begin(userdata);

   /* Avoid drawing a black box if there's no assets */
   if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS || ozone->flags2 & OZONE_FLAG2_IGNORE_MISSING_ASSETS)
   {
      /* Note: The fact that we use a texture slice here
       * makes things very messy
       * > The actual size and offset of a texture slice
       *   is quite 'loose', and depends upon source image
       *   size, draw size and scale factor... */
      unsigned slice_margin = 50 * scale_factor;
      unsigned slice_new_w  = longest_width + (slice_margin * 2);
      unsigned slice_new_h  = line_height * (list.size + 2) + (slice_margin / 2);
      unsigned slice_w      = 256;
      int slice_x           = x - (longest_width / 2) - slice_margin;
      int slice_y           = y - line_height - (slice_margin / 4)
            + ((slice_new_h >= slice_w)
                  ? (16.0f * scale_factor)
                  : (16.0f * ((float)slice_new_h / (float)slice_w)));

      gfx_display_draw_texture_slice(
            p_disp,
            userdata,
            video_width,
            video_height,
            slice_x,
            slice_y,
            slice_w,
            slice_w,
            (unsigned)slice_new_w,
            (unsigned)slice_new_h,
            width,
            height,
            ozone->theme_dynamic.message_background,
            16,
            scale_factor,
            ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DIALOG_SLICE],
            mymat
            );
   }

   for (i = 0; i < list.size; i++)
   {
      const char *msg = list.elems[i].data;

      if (msg)
         gfx_display_draw_text(
               font_data.font,
               msg,
               x - (longest_width / 2),
               y + (i * line_height) + font_data.line_ascender,
               width,
               height,
               COLOR_TEXT_ALPHA(ozone->theme->text_rgba, (uint32_t)(ozone->animations.messagebox_alpha*255.0f)),
               TEXT_ALIGN_LEFT,
               1.0f,
               false,
               1.0f,
               false);
   }

   string_list_deinitialize(&list);
}

static void ozone_set_thumbnail_delay(ozone_handle_t *ozone, bool on)
{
   if (on)
   {
      ozone->thumbnails.stream_delay = OZONE_THUMBNAIL_STREAM_DELAY;
      gfx_thumbnail_set_stream_delay(ozone->thumbnails.stream_delay);
      gfx_thumbnail_set_fade_duration(-1.0f);
   }
   else
   {
      ozone->thumbnails.stream_delay = 0.0f;
      gfx_thumbnail_set_stream_delay(ozone->thumbnails.stream_delay);
      gfx_thumbnail_set_fade_duration(1);
   }
}

static void ozone_hide_fullscreen_thumbnails(ozone_handle_t *ozone, bool animate)
{
   uintptr_t alpha_tag                = (uintptr_t)
         &ozone->animations.fullscreen_thumbnail_alpha;

   /* Kill any existing fade in/out animations */
   gfx_animation_kill_by_tag(&alpha_tag);

   /* Check whether animations are enabled */
   if (animate && (ozone->animations.fullscreen_thumbnail_alpha > 0.0f))
   {
      gfx_animation_ctx_entry_t animation_entry;

      /* Configure fade out animation */
      animation_entry.easing_enum  = OZONE_EASING_ALPHA;
      animation_entry.duration     = gfx_thumb_get_ptr()->fade_duration;
      animation_entry.subject      = &ozone->animations.fullscreen_thumbnail_alpha;
      animation_entry.tag          = alpha_tag;
      animation_entry.target_value = 0.0f;
      animation_entry.cb           = NULL;
      animation_entry.userdata     = NULL;

      /* Push animation */
      gfx_animation_push(&animation_entry);
   }
   /* No animation - just set thumbnail alpha to zero */
   else
      ozone->animations.fullscreen_thumbnail_alpha = 0.0f;

   /* Disable fullscreen thumbnails */
   ozone->flags2 &= ~OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS;
   ozone_set_thumbnail_delay(ozone, true);
}

static void ozone_show_fullscreen_thumbnails(ozone_handle_t *ozone)
{
   gfx_animation_ctx_entry_t animation_entry;
   struct menu_state *menu_st         = menu_state_get_ptr();
   menu_input_t *menu_input           = &menu_st->input_state;
   menu_list_t *menu_list             = menu_st->entries.list;
   file_list_t *selection_buf         = MENU_LIST_GET_SELECTION(menu_list, 0);
   uintptr_t alpha_tag                = (uintptr_t)&ozone->animations.fullscreen_thumbnail_alpha;
   uintptr_t scroll_tag               = (uintptr_t)selection_buf;

   /* Before showing fullscreen thumbnails, must
    * ensure that any existing fullscreen thumbnail
    * view is disabled... */
   if (!(ozone->flags2 & OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS))
      ozone_hide_fullscreen_thumbnails(ozone, false);

   /* Menu list must be stationary while fullscreen
    * thumbnails are shown
    * > Kill any existing scroll animations and
    *   reset scroll acceleration */
   gfx_animation_kill_by_tag(&scroll_tag);
   menu_input->pointer.y_accel    = 0.0f;

   /* Cache selected entry label
    * (used as title when fullscreen thumbnails
    * are shown) */
   if (menu_update_fullscreen_thumbnail_label(
         ozone->fullscreen_thumbnail_label,
         sizeof(ozone->fullscreen_thumbnail_label),
         (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU) ? true : false,
         ozone->title) == 0)
      ozone->fullscreen_thumbnail_label[0] = '\0';

   /* Configure fade in animation */
   animation_entry.easing_enum  = OZONE_EASING_ALPHA;
   animation_entry.duration     = gfx_thumb_get_ptr()->fade_duration;
   animation_entry.subject      = &ozone->animations.fullscreen_thumbnail_alpha;
   animation_entry.tag          = alpha_tag;
   animation_entry.target_value = 1.0f;
   animation_entry.cb           = NULL;
   animation_entry.userdata     = NULL;

   /* Push animation */
   gfx_animation_push(&animation_entry);

   /* Enable fullscreen thumbnails */
   ozone->fullscreen_thumbnail_selection = (size_t)ozone->selection;
   ozone->flags2 |=  OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS;
   ozone_set_thumbnail_delay(ozone, false);
}

static void ozone_draw_fullscreen_thumbnails(
      ozone_handle_t *ozone,
      void *userdata,
      void *disp_userdata,
      unsigned video_width,
      unsigned video_height)
{
   static float right_thumbnail_draw_width_prev  = 0.0f;
   static float right_thumbnail_draw_height_prev = 0.0f;
   static float left_thumbnail_draw_width_prev   = 0.0f;
   static float left_thumbnail_draw_height_prev  = 0.0f;

   /* Check whether fullscreen thumbnails are visible */
   if (     (ozone->animations.fullscreen_thumbnail_alpha > 0.0f)
         || (ozone->flags2 & OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS))
   {
      gfx_display_t *p_disp             = (gfx_display_t*)disp_userdata;
      gfx_thumbnail_t *right_thumbnail  = &ozone->thumbnails.left;
      gfx_thumbnail_t *left_thumbnail   = &ozone->thumbnails.right;
      float right_thumbnail_draw_width  = 0.0f;
      float right_thumbnail_draw_height = 0.0f;
      float left_thumbnail_draw_width   = 0.0f;
      float left_thumbnail_draw_height  = 0.0f;
      float background_alpha            = 0.95f;
      static float background_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 frame_color[16];
      float separator_color[16];
      int view_width                    = (int)video_width;
      int view_height                   = (int)video_height
            - ozone->dimensions.header_height
            - ozone->dimensions.footer_height
            - ozone->dimensions.spacer_1px;
      int thumbnail_padding             = ozone->dimensions.fullscreen_thumbnail_padding;
      int frame_width                   = (int)((float)thumbnail_padding / 4.0f);
      int thumbnail_box_width           = 0;
      int thumbnail_box_height          = 0;
      int right_thumbnail_x             = 0;
      int left_thumbnail_x              = 0;
      int thumbnail_y                   = 0;
      uint8_t num_thumbnails            = 0;
      bool show_right_thumbnail         = false;
      bool show_left_thumbnail          = false;

      /* Sanity check: Return immediately if the view
       * width/height is < 1 */
      if ((view_width < 1) || (view_height < 1))
         goto error;

      /* Get number of 'active' thumbnails */
      show_right_thumbnail =
            (  right_thumbnail->status == GFX_THUMBNAIL_STATUS_AVAILABLE
            || right_thumbnail->status == GFX_THUMBNAIL_STATUS_PENDING);
      show_left_thumbnail  =
            (  left_thumbnail->status  == GFX_THUMBNAIL_STATUS_AVAILABLE
            || left_thumbnail->status  == GFX_THUMBNAIL_STATUS_PENDING);

      if (((ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU) && !string_is_empty(ozone->savestate_thumbnail_file_path))
            || (ozone->flags & OZONE_FLAG_IS_STATE_SLOT))
      {
         left_thumbnail       = &ozone->thumbnails.savestate;
         show_left_thumbnail  = (left_thumbnail->status == GFX_THUMBNAIL_STATUS_AVAILABLE);
         show_right_thumbnail = false;
      }

      if (show_right_thumbnail)
         num_thumbnails++;

      if (show_left_thumbnail)
         num_thumbnails++;

      /* Get base thumbnail dimensions + draw positions */

      /* > Thumbnail bounding box height + y position
       *   are fixed */
      thumbnail_box_height = view_height - (thumbnail_padding * 2);
      thumbnail_y          = ozone->dimensions.header_height + thumbnail_padding
                           + ozone->dimensions.spacer_1px;

      /* Thumbnail bounding box width and x position
       * depend upon number of active thumbnails */
      if (num_thumbnails == 2)
      {
         thumbnail_box_width = (view_width - (thumbnail_padding * 3) - frame_width) >> 1;
         left_thumbnail_x    = thumbnail_padding;
         right_thumbnail_x   = left_thumbnail_x + thumbnail_box_width + frame_width
                             + thumbnail_padding;
      }
      else
      {
         thumbnail_box_width = view_width - (thumbnail_padding * 2);
         left_thumbnail_x    = thumbnail_padding;
         right_thumbnail_x   = left_thumbnail_x;
      }

      /* Sanity check */
      if (   (thumbnail_box_width  < 1)
          || (thumbnail_box_height < 1))
         goto error;

      /* Get thumbnail draw dimensions
       * > Note: The following code is a bit awkward, since
       *   we have to do things in a very specific order
       *   - i.e. we cannot determine proper thumbnail
       *     layout until we have thumbnail draw dimensions.
       *     and we cannot get draw dimensions until we have
       *     the bounding box dimensions...  */
      if (     show_right_thumbnail
            && right_thumbnail->status == GFX_THUMBNAIL_STATUS_AVAILABLE)
      {
         gfx_thumbnail_get_draw_dimensions(
               right_thumbnail,
               thumbnail_box_width,
               thumbnail_box_height,
               1.0f,
               &right_thumbnail_draw_width,
               &right_thumbnail_draw_height);

         right_thumbnail_draw_width_prev  = right_thumbnail_draw_width;
         right_thumbnail_draw_height_prev = right_thumbnail_draw_height;

         /* Sanity check */
         if (   (right_thumbnail_draw_width  <= 0.0f)
             || (right_thumbnail_draw_height <= 0.0f))
            show_right_thumbnail = false;
      }
      else if (right_thumbnail->status == GFX_THUMBNAIL_STATUS_PENDING)
      {
         right_thumbnail_draw_width  = right_thumbnail_draw_width_prev;
         right_thumbnail_draw_height = right_thumbnail_draw_height_prev;
      }

      if (     show_left_thumbnail
            && left_thumbnail->status == GFX_THUMBNAIL_STATUS_AVAILABLE)
      {
         gfx_thumbnail_get_draw_dimensions(
               left_thumbnail,
               thumbnail_box_width,
               thumbnail_box_height,
               1.0f,
               &left_thumbnail_draw_width,
               &left_thumbnail_draw_height);

         left_thumbnail_draw_width_prev  = left_thumbnail_draw_width;
         left_thumbnail_draw_height_prev = left_thumbnail_draw_height;

         /* Sanity check */
         if (   (left_thumbnail_draw_width  <= 0.0f)
             || (left_thumbnail_draw_height <= 0.0f))
            show_left_thumbnail = false;
      }
      else if (left_thumbnail->status == GFX_THUMBNAIL_STATUS_PENDING)
      {
         left_thumbnail_draw_width  = left_thumbnail_draw_width_prev;
         left_thumbnail_draw_height = left_thumbnail_draw_height_prev;
      }

      /* Adjust thumbnail draw positions to achieve
       * uniform appearance (accounting for actual
       * draw dimensions...) */
      if (num_thumbnails == 2)
      {
         int left_padding  = (thumbnail_box_width - (int)left_thumbnail_draw_width)  >> 1;
         int right_padding = (thumbnail_box_width - (int)right_thumbnail_draw_width) >> 1;

         /* Move thumbnails as close together as possible,
          * and horizontally centre the resultant 'block'
          * of images */
         left_thumbnail_x  += right_padding;
         right_thumbnail_x -= left_padding;
      }

      /* Set colour values */

      /* > Background */
      gfx_display_set_alpha(
            background_color,
            background_alpha * ozone->animations.fullscreen_thumbnail_alpha);

      /* > Separators */
      memcpy(separator_color, ozone->theme->header_footer_separator, sizeof(separator_color));
      gfx_display_set_alpha(
            separator_color,
            ozone->animations.fullscreen_thumbnail_alpha);

      /* > Thumbnail frame */
      memcpy(frame_color, ozone->theme->sidebar_background, sizeof(frame_color));
      gfx_display_set_alpha(
            frame_color,
            ozone->animations.fullscreen_thumbnail_alpha);

      /* Darken background */
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            0,
            ozone->dimensions.header_height + ozone->dimensions.spacer_1px,
            view_width,
            (unsigned)view_height,
            video_width,
            video_height,
            background_color,
            NULL);

      /* Draw full-width separators */
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            0,
            ozone->dimensions.header_height,
            view_width,
            ozone->dimensions.spacer_1px,
            video_width,
            video_height,
            separator_color,
            NULL);

      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            0,
            video_height - ozone->dimensions.footer_height,
            view_width,
            ozone->dimensions.spacer_1px,
            video_width,
            video_height,
            separator_color,
            NULL);

      /* Draw thumbnails */

      /* > Right */
      if (show_right_thumbnail)
      {
         /* Background */
         gfx_display_draw_quad(
               p_disp,
               userdata,
               video_width,
               video_height,
               right_thumbnail_x
                     - frame_width
                     + ((thumbnail_box_width  - (int)right_thumbnail_draw_width)  >> 1),
               thumbnail_y
                     - frame_width
                     + ((thumbnail_box_height - (int)right_thumbnail_draw_height) >> 1),
               (unsigned)right_thumbnail_draw_width  + (frame_width << 1),
               (unsigned)right_thumbnail_draw_height + (frame_width << 1),
               video_width,
               video_height,
               frame_color,
               NULL);

         /* Thumbnail */
         gfx_thumbnail_draw(
               userdata,
               video_width,
               video_height,
               right_thumbnail,
               right_thumbnail_x,
               thumbnail_y,
               (unsigned)thumbnail_box_width,
               (unsigned)thumbnail_box_height,
               GFX_THUMBNAIL_ALIGN_CENTRE,
               ozone->animations.fullscreen_thumbnail_alpha,
               1.0f,
               NULL);
      }

      /* > Left */
      if (show_left_thumbnail)
      {
         /* Background */
         gfx_display_draw_quad(
               p_disp,
               userdata,
               video_width,
               video_height,
               left_thumbnail_x
                     - frame_width
                     + ((thumbnail_box_width  - (int)left_thumbnail_draw_width)  >> 1),
               thumbnail_y
                     - frame_width
                     + ((thumbnail_box_height - (int)left_thumbnail_draw_height) >> 1),
               (unsigned)left_thumbnail_draw_width  + (frame_width << 1),
               (unsigned)left_thumbnail_draw_height + (frame_width << 1),
               video_width,
               video_height,
               frame_color,
               NULL);

         /* Thumbnail */
         gfx_thumbnail_draw(
               userdata,
               video_width,
               video_height,
               left_thumbnail,
               left_thumbnail_x,
               thumbnail_y,
               (unsigned)thumbnail_box_width,
               (unsigned)thumbnail_box_height,
               GFX_THUMBNAIL_ALIGN_CENTRE,
               ozone->animations.fullscreen_thumbnail_alpha,
               1.0f,
               NULL);
      }

      /* Show "No thumbnail available" */
      if (     !show_right_thumbnail
            && !show_left_thumbnail)
      {
         ozone_draw_no_thumbnail_available(
            ozone,
            p_disp,
            userdata,
            video_width,
            video_height,
            0,
            thumbnail_y,
            view_width,
            view_height,
            true,
            NULL);
      }
   }
   return;

error:
   /* If fullscreen thumbnails are enabled at
    * this point, must disable them immediately... */
   if (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
      ozone_hide_fullscreen_thumbnails(ozone, false);
}

static void ozone_set_thumbnail_content(void *data, const char *s)
{
   ozone_handle_t *ozone      = (ozone_handle_t*)data;
   struct menu_state *menu_st = menu_state_get_ptr();
   menu_list_t *menu_list     = menu_st->entries.list;

   if (!ozone)
      return;

   if (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
   {
      /* Playlist content */
      if (string_is_empty(s))
      {
         size_t selection      = menu_st->selection_ptr;
         size_t list_size      = MENU_LIST_GET_SELECTION(menu_list, 0)->size;
         file_list_t *list     = MENU_LIST_GET_SELECTION(menu_list, 0);
         playlist_t *pl        = NULL;

         /* Get playlist index corresponding
          * to the selected entry */
         if (      list
               && (selection < list_size)
               && (list->list[selection].type == FILE_TYPE_RPL_ENTRY))
         {
            selection = list->list[selection].entry_idx;
            pl        = playlist_get_cached();
         }

         gfx_thumbnail_set_content_playlist(menu_st->thumbnail_path_data,
               pl, selection);

         switch (ozone->tabs[ozone->categories_selection_ptr])
         {
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
            case OZONE_SYSTEM_TAB_VIDEO:
#endif
            case OZONE_SYSTEM_TAB_MUSIC:
               if (ozone->categories_selection_ptr <= ozone->system_tab_end)
                  ozone->flags &= ~OZONE_FLAG_WANT_THUMBNAIL_BAR;
               break;

            default:
               if (     string_ends_with(playlist_get_conf_path(pl), FILE_PATH_CONTENT_MUSIC_HISTORY)
                     || string_ends_with(playlist_get_conf_path(pl), FILE_PATH_CONTENT_VIDEO_HISTORY))
                  ozone->flags &= ~OZONE_FLAG_WANT_THUMBNAIL_BAR;
               else
                  ozone->flags |=  OZONE_FLAG_WANT_THUMBNAIL_BAR;
               break;
         }
      }
   }
   else if (ozone->flags & OZONE_FLAG_IS_DB_MANAGER_LIST)
   {
      /* Database list content */
      if (string_is_empty(s))
      {
         menu_entry_t entry;

         MENU_ENTRY_INITIALIZE(entry);
         entry.flags |= MENU_ENTRY_FLAG_PATH_ENABLED;
         menu_entry_get(&entry, 0, menu_st->selection_ptr, NULL, true);

         if (!string_is_empty(entry.path))
            gfx_thumbnail_set_content(menu_st->thumbnail_path_data, entry.path);
      }
   }
#if defined(HAVE_LIBRETRODB)
   else if (ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST)
   {
      /* Explore list */
      if (string_is_empty(s))
      {
         /* Selected entry */
         menu_entry_t entry;

         MENU_ENTRY_INITIALIZE(entry);
         entry.flags |= MENU_ENTRY_FLAG_PATH_ENABLED;
         menu_entry_get(&entry, 0, menu_st->selection_ptr, NULL, true);

         if (menu_explore_set_playlist_thumbnail(entry.type, menu_st->thumbnail_path_data) >= 0)
            ozone->flags |=  OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE;
         else
            ozone->flags &= ~OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE;

         if (ozone->flags & OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE)
            ozone->flags |=  OZONE_FLAG_WANT_THUMBNAIL_BAR;
         else
            ozone->flags &= ~OZONE_FLAG_WANT_THUMBNAIL_BAR;
      }
   }
#endif
   else if (string_is_equal(s, "imageviewer"))
   {
      /* Filebrowser image updates */
      size_t selection           = menu_st->selection_ptr;
      file_list_t *selection_buf = MENU_LIST_GET_SELECTION(menu_list, 0);
      ozone_node_t *node         = (ozone_node_t*)selection_buf->list[selection].userdata;

      if (node)
      {
         menu_entry_t entry;

         MENU_ENTRY_INITIALIZE(entry);
         entry.flags |= MENU_ENTRY_FLAG_PATH_ENABLED;
         menu_entry_get(&entry, 0, selection, NULL, true);

         if (!string_is_empty(entry.path) && !string_is_empty(node->fullpath))
            gfx_thumbnail_set_content_image(menu_st->thumbnail_path_data, node->fullpath, entry.path);
      }
   }
   else if (!string_is_empty(s))
   {
      /* Annoying leftovers...
       * This is required to ensure that thumbnails are
       * updated correctly when navigating deeply through
       * the sublevels of database manager lists.
       * Showing thumbnails on database entries is a
       * pointless nuisance and a waste of CPU cycles, IMHO... */
      gfx_thumbnail_set_content(menu_st->thumbnail_path_data, s);
   }

   ozone_update_content_metadata(ozone);
}

/* Returns true if specified category is currently
 * displayed on screen */
static INLINE bool ozone_category_onscreen(
      ozone_handle_t *ozone,
      size_t idx)
{
   return    (idx >= ozone->first_onscreen_category)
          && (idx <= ozone->last_onscreen_category);
}

/* If current category is on screen, returns its
 * index. If current category is off screen, returns
 * index of centremost on screen category. */
static size_t ozone_get_onscreen_category_selection(
      ozone_handle_t *ozone)
{
   /* Check whether selected category is already on screen */
   if (ozone_category_onscreen(ozone, ozone->categories_selection_ptr))
      return ozone->categories_selection_ptr;

   /* Return index of centremost category */
   return (ozone->first_onscreen_category >> 1) + (ozone->last_onscreen_category >> 1);
}

/* If currently selected entry is off screen,
 * moves selection to specified on screen target
 * > Does nothing if currently selected item is
 *   already on screen */
static void ozone_auto_select_onscreen_entry(
      ozone_handle_t *ozone,
      enum ozone_onscreen_entry_position_type target_entry)
{
   struct menu_state *menu_st = menu_state_get_ptr();
   /* Update selection index */
   switch (target_entry)
   {
      case OZONE_ONSCREEN_ENTRY_FIRST:
         menu_st->selection_ptr = ozone->first_onscreen_entry;
         break;
      case OZONE_ONSCREEN_ENTRY_LAST:
         menu_st->selection_ptr = ozone->last_onscreen_entry;
         break;
      case OZONE_ONSCREEN_ENTRY_CENTRE:
      default:
         menu_st->selection_ptr = (ozone->first_onscreen_entry >> 1) + (ozone->last_onscreen_entry >> 1);
         break;
   }
}

static INLINE bool ozone_metadata_override_available(ozone_handle_t *ozone,
      unsigned menu_left_thumbnails)
{
   /* Ugly construct...
    * Content metadata display override may be
    * toggled if the following are true:
    * - We are viewing playlist thumbnails
    * - This is *not* an image viewer playlist
    * - Both right and left thumbnails are
    *   enabled/available
    * Short circuiting means that in most cases
    * only OZONE_FLAG_IS_PLAYLIST will be evaluated,
    * so this isn't too much of a performance hog... */
   return (
               (ozone->flags   & OZONE_FLAG_IS_PLAYLIST)
            || (ozone->flags   & OZONE_FLAG_IS_EXPLORE_LIST)
         )
         && (ozone->show_thumbnail_bar)
         && (menu_left_thumbnails)
         && (!(ozone->flags2 & OZONE_FLAG2_SELECTION_CORE_IS_VIEWER))
         && (   ozone->thumbnails.left.status == GFX_THUMBNAIL_STATUS_AVAILABLE ||
               (ozone->thumbnails.left.status       < GFX_THUMBNAIL_STATUS_AVAILABLE &&
               (ozone->thumbnails_left_status_prev <= GFX_THUMBNAIL_STATUS_AVAILABLE)))
         && (   ozone->thumbnails.right.status == GFX_THUMBNAIL_STATUS_AVAILABLE ||
               (ozone->thumbnails.right.status       < GFX_THUMBNAIL_STATUS_AVAILABLE &&
               (ozone->thumbnails_right_status_prev <= GFX_THUMBNAIL_STATUS_AVAILABLE)));
}

static INLINE bool ozone_fullscreen_thumbnails_available(ozone_handle_t *ozone,
      struct menu_state *menu_st)
{
   bool ret =
            (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR))
         && (ozone->show_thumbnail_bar)
         && (   gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT)
            ||  gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT));

   if (     !string_is_empty(ozone->savestate_thumbnail_file_path)
         && ozone->thumbnails.savestate.status == GFX_THUMBNAIL_STATUS_AVAILABLE)
      ret = true;

   return ret;
}

static bool ozone_help_available(ozone_handle_t *ozone, size_t current_selection, bool menu_show_sublabels)
{
   menu_entry_t last_entry;
   char help_msg[NAME_MAX_LENGTH];
   help_msg[0] = '\0';

   MENU_ENTRY_INITIALIZE(last_entry);
   last_entry.flags |= MENU_ENTRY_FLAG_SUBLABEL_ENABLED;
   menu_entry_get(&last_entry, 0, current_selection, NULL, true);

   /* If sublabels are not visible, they can be displayed as help. */
   if (!menu_show_sublabels && !string_is_empty(last_entry.sublabel))
      return true;

   /* Otherwise check if actual help is available. */
   msg_hash_get_help_enum(last_entry.enum_idx, help_msg, sizeof(help_msg));
   if (string_is_equal(help_msg, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE)))
      return false;

   return !(
               (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
            || (ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST)
         );
}

static bool ozone_clear_available(ozone_handle_t *ozone, size_t current_selection)
{
   menu_entry_t last_entry;

   MENU_ENTRY_INITIALIZE(last_entry);
   menu_entry_get(&last_entry, 0, current_selection, NULL, true);

   if (last_entry.setting_type == ST_BIND)
   {
      return true;
   }
   else if (last_entry.type >= MENU_SETTINGS_INPUT_DESC_BEGIN
         && last_entry.type <= MENU_SETTINGS_INPUT_DESC_END)
   {
      return true;
   }
   else if (last_entry.type >= MENU_SETTINGS_INPUT_DESC_KBD_BEGIN
         && last_entry.type <= MENU_SETTINGS_INPUT_DESC_KBD_END)
   {
      return true;
   }
   return false;
}

static bool ozone_scan_available(ozone_handle_t *ozone, size_t current_selection)
{
   menu_entry_t last_entry;

   MENU_ENTRY_INITIALIZE(last_entry);
   menu_entry_get(&last_entry, 0, current_selection, NULL, true);

   switch (last_entry.type)
   {
      case FILE_TYPE_DIRECTORY:
      case FILE_TYPE_CARCHIVE:
      case FILE_TYPE_PLAIN:
         return true;
      default:
         break;
   }
   switch (last_entry.enum_idx)
   {
      case MENU_ENUM_LABEL_VIDEO_SHADER_PARAMETERS:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_MANAGER:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_PREPEND:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_APPEND:
      case MENU_ENUM_LABEL_SHADER_APPLY_CHANGES:
      case MENU_ENUM_LABEL_SHADER_PARAMETERS_ENTRY:
         return true;
      default:
         break;
   }
   return false;
}

static bool ozone_manage_available(ozone_handle_t *ozone, size_t current_selection)
{
   menu_entry_t last_entry;

   if (     (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         && (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
         && ozone->categories_selection_ptr > ozone->system_tab_end)
      return true;

   MENU_ENTRY_INITIALIZE(last_entry);
   menu_entry_get(&last_entry, 0, current_selection, NULL, true);

   switch (last_entry.type)
   {
      case FILE_TYPE_PLAYLIST_COLLECTION:
         return true;
      case FILE_TYPE_RPL_ENTRY:
         if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
            return true;
         break;
      default:
         break;
   }
   switch (last_entry.enum_idx)
   {
      case MENU_ENUM_LABEL_LOAD_CONTENT_HISTORY:
      case MENU_ENUM_LABEL_GOTO_FAVORITES:
      case MENU_ENUM_LABEL_GOTO_IMAGES:
      case MENU_ENUM_LABEL_GOTO_MUSIC:
      case MENU_ENUM_LABEL_GOTO_VIDEO:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PARAMETERS:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_MANAGER:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_PREPEND:
      case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_APPEND:
      case MENU_ENUM_LABEL_SHADER_APPLY_CHANGES:
         return true;
      default:
         break;
   }
   return false;
}

static bool ozone_is_current_entry_settings(size_t current_selection)
{
   menu_entry_t last_entry;
   const char *entry_value;

   unsigned entry_type                = 0;
   enum msg_file_type entry_file_type = FILE_TYPE_NONE;

   MENU_ENTRY_INITIALIZE(last_entry);
   last_entry.flags |= MENU_ENTRY_FLAG_VALUE_ENABLED;

   menu_entry_get(&last_entry, 0, current_selection, NULL, true);

   if (last_entry.enum_idx == MENU_ENUM_LABEL_CHEEVOS_PASSWORD)
      entry_value = last_entry.password_value;
   else
      entry_value = last_entry.value;

   entry_file_type = msg_hash_to_file_type(msg_hash_calculate(entry_value));
   entry_type      = last_entry.type;

   /* Logic below taken from materialui_pointer_up_swipe_horz_default */
   if (!string_is_empty(entry_value))
   {
      /* Toggle switch off */
      if (   string_is_equal(entry_value, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED))
          || string_is_equal(entry_value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)))
      {
         return true;
      }
      /* Toggle switch on */
      else if (   string_is_equal(entry_value, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED))
               || string_is_equal(entry_value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)))
      {
         return true;
      }
      /* Normal value text */
      else
      {
         switch (entry_file_type)
         {
            case FILE_TYPE_IN_CARCHIVE:
            case FILE_TYPE_MORE:
            case FILE_TYPE_CORE:
            case FILE_TYPE_DIRECT_LOAD:
            case FILE_TYPE_RDB:
            case FILE_TYPE_PLAIN:
            case FILE_TYPE_DIRECTORY:
            case FILE_TYPE_MUSIC:
            case FILE_TYPE_IMAGE:
            case FILE_TYPE_MOVIE:
               break;
            case FILE_TYPE_COMPRESSED:
               /* Note that we have to perform a backup check here,
                * since the 'manual content scan - file extensions'
                * setting may have a value of 'zip' or '7z' etc, which
                * means it would otherwise get incorrectly identified as
                * an archive file... */
               if (entry_type != FILE_TYPE_CARCHIVE)
                  return true;
               break;
            default:
               return true;
               break;
         }
      }
   }

   return false;
}

static void ozone_toggle_metadata_override(ozone_handle_t *ozone)
{
   gfx_animation_ctx_entry_t animation_entry;
   uintptr_t alpha_tag         = (uintptr_t)&ozone->animations.left_thumbnail_alpha;

   /* Kill any existing fade in/out animations */
   gfx_animation_kill_by_tag(&alpha_tag);

   /* Set common animation parameters */
   animation_entry.easing_enum = OZONE_EASING_ALPHA;
   animation_entry.duration    = gfx_thumb_get_ptr()->fade_duration;
   animation_entry.subject     = &ozone->animations.left_thumbnail_alpha;
   animation_entry.tag         = alpha_tag;
   animation_entry.cb          = NULL;
   animation_entry.userdata    = NULL;

   /* Check whether metadata override is
    * currently enabled */
   if (ozone->flags & OZONE_FLAG_FORCE_METADATA_DISPLAY)
   {
      /* Thumbnail will fade in */
      animation_entry.target_value  = 1.0f;
      ozone->flags                 &= ~OZONE_FLAG_FORCE_METADATA_DISPLAY;
   }
   else
   {
      /* Thumbnail will fade out */
      animation_entry.target_value  = 0.0f;
      ozone->flags                 |=  OZONE_FLAG_FORCE_METADATA_DISPLAY;
   }

   /* Push animation */
   gfx_animation_push(&animation_entry);
}

/**
 * Starts the cursor wiggle animation in the given direction
 * Use ozone_get_cursor_wiggle_offset to read the animation
 * once it has started
 */
static void ozone_start_cursor_wiggle(
      ozone_handle_t* ozone,
      enum menu_action direction)
{
   /* Don't start another wiggle animation on top of another */
   if (!ozone || (ozone->flags2 & OZONE_FLAG2_CURSOR_WIGGLING))
      return;

   /* Don't allow wiggling in invalid directions */
   if (!(
            direction == MENU_ACTION_UP
         || direction == MENU_ACTION_DOWN
         || direction == MENU_ACTION_LEFT
         || direction == MENU_ACTION_RIGHT
        ))
      return;

   /* Start wiggling */
   ozone->cursor_wiggle_state.start_time = menu_driver_get_current_time() / 1000;
   ozone->cursor_wiggle_state.direction  = direction;
   ozone->cursor_wiggle_state.amplitude  = rand() % 15 + 10;
   ozone->flags2                        |= OZONE_FLAG2_CURSOR_WIGGLING;
}

static enum menu_action ozone_parse_menu_entry_action(
      ozone_handle_t *ozone,
      bool menu_navigation_wraparound_enable,
      bool ozone_collapse_sidebar,
      enum menu_action action)
{
   uintptr_t tag;
   size_t new_selection, selection;
   size_t selection_total;
   bool is_current_entry_settings = false;
   struct menu_state *menu_st     = menu_state_get_ptr();
   menu_list_t *menu_list         = menu_st->entries.list;
   size_t menu_stack_size         = MENU_LIST_GET_STACK_SIZE(menu_list, 0);
   settings_t *settings           = config_get_ptr();
   enum menu_action new_action    = action;
   file_list_t *selection_buf     = NULL;
   unsigned horizontal_list_size  = 0;

   /* We have to override the thumbnail stream
    * delay when opening the thumbnail sidebar;
    * ensure that the proper value is restored
    * whenever the user performs regular navigation */
   if (   (action != MENU_ACTION_NOOP)
       && (ozone->thumbnails.stream_delay != OZONE_THUMBNAIL_STREAM_DELAY)
       && !(ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS))
   {
      ozone->thumbnails.stream_delay = OZONE_THUMBNAIL_STREAM_DELAY;
      gfx_thumbnail_set_stream_delay(ozone->thumbnails.stream_delay);
   }

   horizontal_list_size       = (ozone->show_playlist_tabs)
         ? (unsigned)ozone->horizontal_list.size
         : 0;

   if (menu_input_dialog_get_display_kb())
      ozone->flags           |=  OZONE_FLAG_MSGBOX_STATE;
   else
      ozone->flags           &= ~OZONE_FLAG_MSGBOX_STATE;
   selection_buf              = MENU_LIST_GET_SELECTION(menu_list, 0);
   tag                        = (uintptr_t)selection_buf;
   selection                  = menu_st->selection_ptr;
   selection_total            = MENU_LIST_GET_SELECTION(menu_list, 0)->size;

   /* Don't wiggle left or right if the current entry is a setting. This is
      partially wrong because some settings don't use left and right to change their value, such as
      free input fields (passwords...). This is good enough. */
   is_current_entry_settings = ozone_is_current_entry_settings(selection);

   /* Scan user inputs */
   switch (action)
   {
      case MENU_ACTION_START:
         ozone->flags &= ~OZONE_FLAG_CURSOR_MODE;
         if (      (ozone->flags  & OZONE_FLAG_IS_STATE_SLOT)
               || ((ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU) && menu_is_running_quick_menu()))
            break;

         if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         {
            char playlist_path[PATH_MAX_LENGTH];
            ssize_t list_selection;
            /* If cursor is active, ensure we target
             * an on screen category */
            size_t tab_selection       = (ozone->flags & OZONE_FLAG_CURSOR_MODE)
                  ? ozone_get_onscreen_category_selection(ozone)
                  : ozone->categories_selection_ptr;

            playlist_path[0] = '\0';

            if (!ozone_manage_available(ozone, ozone->selection))
               break;

            list_selection = tab_selection - ozone->system_tab_end - 1;
            new_action     = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;

            if (list_selection < 0)
            {
               enum msg_hash_enums value_idx = ozone_system_tabs_label[ozone->tabs[tab_selection]];

               switch (value_idx)
               {
                  case MENU_ENUM_LABEL_HISTORY_TAB:
                     strlcpy(playlist_path, playlist_get_conf_path(g_defaults.content_history), sizeof(playlist_path));
                     break;
                  case MENU_ENUM_LABEL_FAVORITES_TAB:
                     strlcpy(playlist_path, playlist_get_conf_path(g_defaults.content_favorites), sizeof(playlist_path));
                     break;
                  case MENU_ENUM_LABEL_IMAGES_TAB:
#ifdef HAVE_IMAGEVIEWER
                     strlcpy(playlist_path, playlist_get_conf_path(g_defaults.image_history), sizeof(playlist_path));
#endif
                     break;
                  case MENU_ENUM_LABEL_MUSIC_TAB:
                     strlcpy(playlist_path, playlist_get_conf_path(g_defaults.music_history), sizeof(playlist_path));
                     break;
                  case MENU_ENUM_LABEL_VIDEO_TAB:
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
                     strlcpy(playlist_path, playlist_get_conf_path(g_defaults.video_history), sizeof(playlist_path));
#endif
                     break;
                  default:
                     break;
               }

               if (string_is_empty(playlist_path))
                  break;
            }
            else
               fill_pathname_join(playlist_path,
                     settings->paths.directory_playlist,
                     ozone->horizontal_list.list[list_selection].path,
                     sizeof(playlist_path));

            generic_action_ok_displaylist_push(
                  playlist_path,
                  NULL,
                  msg_hash_to_str(MENU_ENUM_LABEL_PLAYLIST_MANAGER_SETTINGS),
                  MENU_SETTING_ACTION,
                  ozone->tab_selection[ozone->categories_selection_ptr],
                  0,
                  ACTION_OK_DL_PLAYLIST_MANAGER_SETTINGS);

            ozone->flags  &= ~(OZONE_FLAG_CURSOR_IN_SIDEBAR
                             | OZONE_FLAG_WANT_THUMBNAIL_BAR);
            ozone->flags2 |=  OZONE_FLAG2_PENDING_CURSOR_IN_SIDEBAR;

            ozone_refresh_sidebars(ozone, ozone_collapse_sidebar, ozone->last_height);
            if (!(ozone->flags & OZONE_FLAG_EMPTY_PLAYLIST))
               ozone_leave_sidebar(ozone, ozone_collapse_sidebar, tag,
                     settings->uints.menu_remember_selection);

            menu_st->selection_ptr = 0;
            ozone_selection_changed(ozone, false);
            break;
         }

         /* If this is a menu with thumbnails and cursor
          * is not in the sidebar, attempt to show
          * fullscreen thumbnail view */
         if (        ozone_fullscreen_thumbnails_available(ozone, menu_st)
               && (!(ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS))
               && (!(ozone->flags  & OZONE_FLAG_CURSOR_IN_SIDEBAR)))
         {
            ozone->flags2 |= OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS;
            ozone_show_fullscreen_thumbnails(ozone);
            new_action     = MENU_ACTION_NOOP;
         }
         else if ((ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
               || (ozone->flags2 & OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS))
         {
            ozone->flags2 &= ~OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS;
            ozone_hide_fullscreen_thumbnails(ozone, true);
            new_action     = MENU_ACTION_NOOP;
         }
         break;
      case MENU_ACTION_SCAN:
         if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         {
            /* If cursor is active, ensure we target
             * an on screen category */
            size_t tab_selection  = (ozone->flags & OZONE_FLAG_CURSOR_MODE)
                  ? ozone_get_onscreen_category_selection(ozone)
                  : ozone->categories_selection_ptr;

            new_selection = random_range((unsigned)(ozone->system_tab_end + 1), (unsigned)(ozone->system_tab_end + horizontal_list_size));
            while (new_selection == tab_selection)
               new_selection = random_range((unsigned)(ozone->system_tab_end + 1), (unsigned)(ozone->system_tab_end + horizontal_list_size));

            if (new_selection != tab_selection)
               ozone_sidebar_goto(ozone, new_selection);

            ozone->flags &= ~OZONE_FLAG_CURSOR_MODE;
            new_action    = MENU_ACTION_NOOP;

#ifdef HAVE_AUDIOMIXER
            if (new_selection != selection)
               audio_driver_mixer_play_scroll_sound(true);
#endif
            break;
         }
         else if (ozone->flags2 & OZONE_FLAG2_IS_PLAYLISTS_TAB)
         {
            struct menu_state *menu_st = menu_state_get_ptr();
            size_t selection_total     = menu_st->entries.list ? MENU_LIST_GET_SELECTION(menu_st->entries.list, 0)->size : 0;
            size_t selection           = menu_st->selection_ptr;
            size_t new_selection       = random_range(0, (unsigned)(selection_total - 1));
            menu_entry_t entry_new;

            MENU_ENTRY_INITIALIZE(entry_new);
            menu_entry_get(&entry_new, 0, new_selection, NULL, false);
            /* Keep randomizing until selection is a fresh playlist */
            while (new_selection == selection || entry_new.type != FILE_TYPE_PLAYLIST_COLLECTION)
            {
               new_selection = random_range(0, (unsigned)(selection_total - 1));
               menu_entry_get(&entry_new, 0, new_selection, NULL, false);
            }

            if (new_selection != selection)
            {
               menu_st->selection_ptr = new_selection;
               if (menu_st->driver_ctx->navigation_set)
                  menu_st->driver_ctx->navigation_set(menu_st->userdata, true);
            }

            new_action = MENU_ACTION_NOOP;

#ifdef HAVE_AUDIOMIXER
            if (new_selection != selection)
               audio_driver_mixer_play_scroll_sound(true);
#endif
         }
         else if ((ozone->flags & OZONE_FLAG_IS_PLAYLIST)
               || (ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST))
         {
            size_t new_selection = menu_playlist_random_selection(selection, ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST);

            if (new_selection != selection)
            {
               menu_st->selection_ptr = new_selection;
               ozone_selection_changed(ozone, false);
            }

            ozone->flags &= ~OZONE_FLAG_CURSOR_MODE;
            new_action    = MENU_ACTION_NOOP;

#ifdef HAVE_AUDIOMIXER
            if (new_selection != selection)
               audio_driver_mixer_play_scroll_sound(true);
#endif
            break;
         }

         ozone->flags      &= ~(OZONE_FLAG_SKIP_THUMBNAIL_RESET
                              | OZONE_FLAG_CURSOR_MODE);

         if (     (!(ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS))
               && (  (ozone->flags & OZONE_FLAG_IS_STATE_SLOT)
                  || ((ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU) && !string_is_empty(ozone->savestate_thumbnail_file_path))))
         {
            ozone->flags2 |= OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS;
            ozone_show_fullscreen_thumbnails(ozone);
            new_action     = MENU_ACTION_NOOP;
         }
         else if ((ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
               && (  (ozone->flags & OZONE_FLAG_IS_STATE_SLOT)
                  || ((ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU) && menu_is_running_quick_menu())))
         {
            ozone->flags2 &= ~OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS;
            ozone_hide_fullscreen_thumbnails(ozone, true);
            new_action     = MENU_ACTION_NOOP;
         }
         break;
      case MENU_ACTION_SEARCH:
         /* Playlist thumbnail cycle */
         if (     (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
               || ((ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU) && !menu_is_running_quick_menu()))
         {
            ozone->flags      &= ~OZONE_FLAG_SKIP_THUMBNAIL_RESET;
            action_switch_thumbnail(NULL, NULL, 0, 0);
            new_action = MENU_ACTION_NOOP;
         }
         break;
      case MENU_ACTION_DOWN:
         if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         {
            /* If cursor is active, ensure we target
             * an on screen category */
            size_t selection   = (ozone->flags & OZONE_FLAG_CURSOR_MODE)
                  ? ozone_get_onscreen_category_selection(ozone)
                  : ozone->categories_selection_ptr;

            new_selection      = selection + 1;

            if (new_selection >= ozone->system_tab_end + horizontal_list_size + 1)
               new_selection   = 0;

            if (     !menu_navigation_wraparound_enable
                  && selection == ozone->system_tab_end + horizontal_list_size)
               new_selection   = selection;

            if (new_selection != selection)
            {
               ozone_sidebar_goto(ozone, new_selection);
               new_action      = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
            }
            else
            {
               ozone_start_cursor_wiggle(ozone, MENU_ACTION_DOWN);
               new_action      = MENU_ACTION_NOOP;
            }
            ozone->flags      &= ~OZONE_FLAG_CURSOR_MODE;

#ifdef HAVE_AUDIOMIXER
            if (new_selection != selection)
               audio_driver_mixer_play_scroll_sound(false);
#endif
            break;
         }
         else if (!menu_navigation_wraparound_enable && selection == selection_total - 1)
               ozone_start_cursor_wiggle(ozone, MENU_ACTION_DOWN);

         if (     (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
               && (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU))
            return MENU_ACTION_NOOP;

         /* If pointer is active and current selection
          * is off screen, auto select *centre* item */
         if (     (ozone->flags & OZONE_FLAG_CURSOR_MODE)
               && (!OZONE_ENTRY_ONSCREEN(ozone, selection)))
            ozone_auto_select_onscreen_entry(ozone, OZONE_ONSCREEN_ENTRY_CENTRE);
         ozone->flags &= ~OZONE_FLAG_CURSOR_MODE;
         break;
      case MENU_ACTION_UP:
         if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         {
            /* If cursor is active, ensure we target
             * an on screen category */
            size_t selection   = (ozone->flags & OZONE_FLAG_CURSOR_MODE)
                  ? ozone_get_onscreen_category_selection(ozone)
                  : ozone->categories_selection_ptr;

            if (selection > 0)
               new_selection = selection - 1;
            else if (menu_navigation_wraparound_enable)
               new_selection = horizontal_list_size + ozone->system_tab_end;
            else
               new_selection = selection;

            if (new_selection != selection)
            {
               ozone_sidebar_goto(ozone, new_selection);
               new_action      = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
            }
            else
            {
               ozone_start_cursor_wiggle(ozone, MENU_ACTION_UP);
               new_action      = MENU_ACTION_NOOP;
            }
            ozone->flags      &= ~OZONE_FLAG_CURSOR_MODE;

#ifdef HAVE_AUDIOMIXER
            if (new_selection != selection)
               audio_driver_mixer_play_scroll_sound(true);
#endif
            break;
         }
         else if (!menu_navigation_wraparound_enable && selection == 0)
               ozone_start_cursor_wiggle(ozone, MENU_ACTION_UP);

         if (     (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
               && (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU))
            return MENU_ACTION_NOOP;

         /* If pointer is active and current selection
          * is off screen, auto select *centre* item */
         if (     (ozone->flags & OZONE_FLAG_CURSOR_MODE)
               && (!OZONE_ENTRY_ONSCREEN(ozone, selection)))
            ozone_auto_select_onscreen_entry(ozone, OZONE_ONSCREEN_ENTRY_CENTRE);
         ozone->flags &= ~OZONE_FLAG_CURSOR_MODE;
         break;
      case MENU_ACTION_LEFT:
         ozone->flags &= ~OZONE_FLAG_CURSOR_MODE;
         if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         {
            new_action      = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
            ozone_start_cursor_wiggle(ozone, MENU_ACTION_LEFT);
            break;
         }
         else if (ozone->depth > 1)
         {
            /* Pressing left goes up but faster, so
               wiggle up to say that there is nothing more upwards
               even though the user pressed the left button */
            if (     !menu_navigation_wraparound_enable
                  && (selection == 0)
                  && !is_current_entry_settings)
               ozone_start_cursor_wiggle(ozone, MENU_ACTION_DOWN);

            if (      (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
                  && ((ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU)
                  && !menu_is_running_quick_menu()))
               return MENU_ACTION_NOOP;

            break;
         }
         else if ((ozone->depth == 1)
               && (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
               && (ozone->flags  & OZONE_FLAG_IS_PLAYLIST))
         {
            ozone_hide_fullscreen_thumbnails(ozone, true);
            ozone->flags2 &= ~OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS;
         }

         ozone_go_to_sidebar(ozone, ozone_collapse_sidebar, tag);
         new_action    = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
         break;
      case MENU_ACTION_RIGHT:
         ozone->flags &= ~OZONE_FLAG_CURSOR_MODE;
         if (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR))
         {
            if (ozone->depth == 1)
            {
               new_action = MENU_ACTION_NOOP;
               ozone_start_cursor_wiggle(ozone, MENU_ACTION_RIGHT);
            }
            /* Pressing right goes down but faster, so
               wiggle down to say that there is nothing more downwards
               even though the user pressed the right button */
            else if (!menu_navigation_wraparound_enable
                  && selection == (selection_total - 1)
                  && !is_current_entry_settings)
               ozone_start_cursor_wiggle(ozone, MENU_ACTION_DOWN);

            if (     (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
                  && (    (ozone->flags  & OZONE_FLAG_IS_PLAYLIST)
                      || ((ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU)
                  && !menu_is_running_quick_menu())))
               return MENU_ACTION_NOOP;

            break;
         }

         if (!(ozone->flags & OZONE_FLAG_EMPTY_PLAYLIST))
            ozone_leave_sidebar(ozone, ozone_collapse_sidebar, tag,
                  settings->uints.menu_remember_selection);

         new_action    = MENU_ACTION_ACCESSIBILITY_SPEAK_LABEL;
         break;
      case MENU_ACTION_OK:
         ozone->flags &= ~OZONE_FLAG_CURSOR_MODE;

         if (ozone->flags & OZONE_FLAG_IS_STATE_SLOT)
            ozone->flags |= OZONE_FLAG_SKIP_THUMBNAIL_RESET;

         /* Open fullscreen thumbnail with Ok when core is running
            to prevent accidental imageviewer core launch */
         if (     (ozone->flags & OZONE_FLAG_LIBRETRO_RUNNING)
               && (ozone->flags & OZONE_FLAG_IS_FILE_LIST)
               && (ozone->flags & OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE)
               && (ozone->show_thumbnail_bar))
         {
            /* Allow launch if already using "imageviewer" core */
            if (string_is_equal(runloop_state_get_ptr()->system.info.library_name, "image display"))
               break;

            if (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
               ozone_hide_fullscreen_thumbnails(ozone, true);
            else
               ozone_show_fullscreen_thumbnails(ozone);
            new_action     = MENU_ACTION_NOOP;
            break;
         }

         if (     (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
               || (ozone->flags2 & OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS))
         {
            ozone->flags2 &= ~OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS;
            ozone_hide_fullscreen_thumbnails(ozone, true);
            if (     (!(ozone->flags & OZONE_FLAG_IS_STATE_SLOT))
                  && (!(ozone->flags & OZONE_FLAG_IS_PLAYLIST))
                  && (!(ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST)))
               new_action = MENU_ACTION_NOOP;
            break;
         }

         /* Configuration load must reset depth according to new menu
          * location, otherwise sidebar gets stuck in wrong position */
         {
            const file_list_t *list = menu_st->entries.list ? MENU_LIST_GET(menu_st->entries.list, 0) : NULL;
            if (list->size)
            {
               if (string_is_equal(list->list[list->size - 1].label, msg_hash_to_str(MENU_ENUM_LABEL_CONFIGURATIONS)))
                  ozone->flags2 |= OZONE_FLAG2_RESET_DEPTH;
            }
         }

         if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         {
            ozone_refresh_sidebars(ozone, ozone_collapse_sidebar, ozone->last_height);
            if (!(ozone->flags & OZONE_FLAG_EMPTY_PLAYLIST))
               ozone_leave_sidebar(ozone, ozone_collapse_sidebar, tag,
                     settings->uints.menu_remember_selection);
            new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_LABEL;
            break;
         }

         /* Make transition smoother for single-click playlist launching */
         if (     settings->bools.input_menu_singleclick_playlists
               && (  ozone->flags & OZONE_FLAG_IS_PLAYLIST
                  || ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST))
         {
            if (ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST)
            {
#if defined(HAVE_LIBRETRODB)
               menu_entry_t entry;
               MENU_ENTRY_INITIALIZE(entry);
               menu_entry_get(&entry, 0, menu_st->selection_ptr, NULL, true);
               if (     entry.type == FILE_TYPE_RDB
                     || entry.type == FILE_TYPE_PLAIN
                     || !menu_explore_is_content_list())
                  break;
#endif
            }
            ozone->animations.list_alpha = 0.0f;
            ozone->draw_entry_delay = MENU_DRAW_ENTRY_DELAY;
         }
         break;
      case MENU_ACTION_CANCEL:
         ozone->flags &= ~OZONE_FLAG_CURSOR_MODE;

         if (ozone->flags & OZONE_FLAG_IS_STATE_SLOT)
            ozone->flags |= OZONE_FLAG_SKIP_THUMBNAIL_RESET;

         /* If this is a playlist, handle 'backing out'
          * of a search, if required */
         if (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
            if (menu_entries_search_get_terms())
               break;

         /* Limit previous sidebar selection if playlist is deleted */
         if (ozone->categories_selection_ptr > ozone->system_tab_end + ozone->horizontal_list.size)
            ozone->categories_selection_ptr = ozone->system_tab_end + ozone->horizontal_list.size;

         if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         {
            /* Go back to main menu tab */
            if (     ozone->categories_selection_ptr != 0
                  && settings->bools.input_menu_allow_tabs_back)
            {
               ozone_sidebar_goto(ozone, 0);
#ifdef HAVE_AUDIOMIXER
               audio_driver_mixer_play_scroll_sound(true);
#endif
            }
            else
            {
               /* Jump to first item on Main Menu */
               ozone->tab_selection[ozone->categories_selection_ptr] = 0;
               menu_st->selection_ptr = 0;
            }

            new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
            break;
         }

         if (     (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
               || (ozone->flags2 & OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS))
         {
            ozone->flags  &= ~OZONE_FLAG_SKIP_THUMBNAIL_RESET;
            ozone->flags2 &= ~OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS;
            ozone_hide_fullscreen_thumbnails(ozone, true);
            new_action     = MENU_ACTION_NOOP;
            break;
         }

         if (menu_stack_size == 1)
         {
            ozone_go_to_sidebar(ozone, ozone_collapse_sidebar, tag);
            new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
         }

         /* Return from manage playlist quick access back to sidebar */
         if (    (ozone->flags2 & OZONE_FLAG2_PENDING_CURSOR_IN_SIDEBAR)
               && ozone->depth == 2)
         {
            ozone->flags2 &= ~OZONE_FLAG2_PENDING_CURSOR_IN_SIDEBAR;
            ozone->flags  |=  OZONE_FLAG_CURSOR_IN_SIDEBAR;
            ozone_sidebar_goto(ozone, ozone->categories_selection_ptr);
         }
         break;
      case MENU_ACTION_SCROLL_UP:
         /* Descend 10 items or to previous alphabet (Z towards A) */

         /* Scroll sidebar tabs */
         if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         {
            /* If cursor is active, ensure we target
             * an on screen category */
            size_t tab_selection       = (ozone->flags & OZONE_FLAG_CURSOR_MODE)
                  ? ozone_get_onscreen_category_selection(ozone)
                  : ozone->categories_selection_ptr;

            new_selection              = tab_selection;

            if (menu_st->scroll.mode == MENU_SCROLL_PAGE)
            {
               if (tab_selection >= (size_t)ozone->system_tab_end + 11)
                  new_selection        = tab_selection - 10;
               else
                  new_selection        = ozone->system_tab_end + 1;
            }
            else if (ozone->sidebar_index_size)
            {
               /* Alphabetical scroll */
               size_t l                = ozone->sidebar_index_size - 1;

               while (  l
                     && ozone->sidebar_index_list[l - 1] >= tab_selection)
                  l--;

               if (l > 0)
                  new_selection        = ozone->sidebar_index_list[l - 1];
            }

            if (tab_selection < (size_t)(ozone->system_tab_end + 1))
               new_selection           = 0;
            else if (tab_selection > ozone->system_tab_end - new_selection)
               new_selection           = ozone->system_tab_end + 1;

            if (new_selection != tab_selection)
               ozone_sidebar_goto(ozone, new_selection);

            new_action         = MENU_ACTION_NOOP;
            ozone->flags      &= ~OZONE_FLAG_CURSOR_MODE;

#ifdef HAVE_AUDIOMIXER
            if (new_selection != selection)
               audio_driver_mixer_play_scroll_sound(true);
#endif
            break;
         }

         if (     (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
               && (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU))
            return MENU_ACTION_NOOP;

         /* If pointer is active and current selection
          * is off screen, auto select *last* item */
         if (ozone->flags & OZONE_FLAG_CURSOR_MODE)
            if (!OZONE_ENTRY_ONSCREEN(ozone, selection))
               ozone_auto_select_onscreen_entry(ozone,
                     OZONE_ONSCREEN_ENTRY_LAST);
         ozone->flags &= ~OZONE_FLAG_CURSOR_MODE;
         break;
      case MENU_ACTION_SCROLL_DOWN:
         /* Ascend 10 items or to next alphabet (A towards Z) */

         /* Scroll sidebar tabs */
         if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         {
            /* If cursor is active, ensure we target
             * an on screen category */
            size_t tab_selection       = (ozone->flags & OZONE_FLAG_CURSOR_MODE)
                  ? ozone_get_onscreen_category_selection(ozone)
                  : ozone->categories_selection_ptr;

            new_selection              = (int)tab_selection;

            if (menu_st->scroll.mode == MENU_SCROLL_PAGE)
               new_selection           = (int)(tab_selection + 10);
            else
            {
               /* Alphabetical scroll */
               size_t l                = 0;

               while (  (l < (size_t)(ozone->sidebar_index_size - 1))
                     && (ozone->sidebar_index_list[l + 1] <= tab_selection))
                  l++;

               if (l < (size_t)(ozone->sidebar_index_size - 1))
                  new_selection        = ozone->sidebar_index_list[l + 1];
               else if (l == (size_t)(ozone->sidebar_index_size - 1))
                  new_selection        = ozone->system_tab_end + horizontal_list_size;

               if (tab_selection < (size_t)(ozone->system_tab_end + 1))
                  new_selection        = ozone->system_tab_end + 1;
            }

            if (new_selection > ozone->system_tab_end + horizontal_list_size)
               new_selection           = ozone->system_tab_end + horizontal_list_size;

            if (new_selection != tab_selection)
               ozone_sidebar_goto(ozone, new_selection);

            new_action         = MENU_ACTION_NOOP;
            ozone->flags      &= ~OZONE_FLAG_CURSOR_MODE;

#ifdef HAVE_AUDIOMIXER
            if (new_selection != selection)
               audio_driver_mixer_play_scroll_sound(false);
#endif
            break;
         }

         if (     (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
               && (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU))
            return MENU_ACTION_NOOP;

         /* If pointer is active and current selection
          * is off screen, auto select *first* item */
         if (     (ozone->flags & OZONE_FLAG_CURSOR_MODE)
               && (!OZONE_ENTRY_ONSCREEN(ozone, selection)))
            ozone_auto_select_onscreen_entry(ozone, OZONE_ONSCREEN_ENTRY_FIRST);
         ozone->flags &= ~OZONE_FLAG_CURSOR_MODE;
         break;
      case MENU_ACTION_SCROLL_HOME:
         if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         {
            /* If cursor is active, ensure we target
             * an on screen category */
            size_t tab_selection       = (ozone->flags & OZONE_FLAG_CURSOR_MODE)
                  ? ozone_get_onscreen_category_selection(ozone)
                  : ozone->categories_selection_ptr;

            new_selection              = 0;

            if (tab_selection > ozone->system_tab_end)
               new_selection           = ozone->system_tab_end + 1;

            if (new_selection != tab_selection)
               ozone_sidebar_goto(ozone, new_selection);

            new_action         = MENU_ACTION_NOOP;
            ozone->flags      &= ~OZONE_FLAG_CURSOR_MODE;

#ifdef HAVE_AUDIOMIXER
            if (new_selection != selection)
               audio_driver_mixer_play_scroll_sound(true);
#endif
            break;
         }
         break;
      case MENU_ACTION_SCROLL_END:
         if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         {
            /* If cursor is active, ensure we target
             * an on screen category */
            size_t tab_selection       = (ozone->flags & OZONE_FLAG_CURSOR_MODE)
                  ? ozone_get_onscreen_category_selection(ozone)
                  : ozone->categories_selection_ptr;

            new_selection              = ozone->system_tab_end + horizontal_list_size;

            if (new_selection != tab_selection)
               ozone_sidebar_goto(ozone, new_selection);

            new_action         = MENU_ACTION_NOOP;
            ozone->flags      &= ~OZONE_FLAG_CURSOR_MODE;

#ifdef HAVE_AUDIOMIXER
            if (new_selection != selection)
               audio_driver_mixer_play_scroll_sound(false);
#endif
            break;
         }
         break;
      case MENU_ACTION_INFO:
         /* If we currently viewing a playlist with
          * dual thumbnails, toggle the content metadata
          * override */
         if (ozone_metadata_override_available(ozone,
                  settings->uints.menu_left_thumbnails))
         {
            ozone_toggle_metadata_override(ozone);
            new_action = MENU_ACTION_NOOP;
         }
         /* ...and since the user is likely to trigger
          * 'INFO' actions on invalid playlist entries,
          * suppress this action entirely when viewing
          * playlists under all other conditions
          * > Playlists have no 'INFO' entries - the
          *   user is just greeted with a useless
          *   'no information available' message
          * > It is incredibly annoying to inadvertently
          *   trigger this message when you just want to
          *   toggle metadata... */
         else if ((ozone->flags & OZONE_FLAG_IS_PLAYLIST)
               && (ozone->show_thumbnail_bar))
            new_action = MENU_ACTION_NOOP;

         ozone->flags &= ~OZONE_FLAG_CURSOR_MODE;
         break;
      default:
         /* In all other cases, pass through input
          * menu action without intervention */
         break;
   }

   switch (new_action)
   {
      case MENU_ACTION_UP:
      case MENU_ACTION_DOWN:
      case MENU_ACTION_LEFT:
      case MENU_ACTION_RIGHT:
      case MENU_ACTION_CANCEL:
         if (     settings->bools.input_menu_singleclick_playlists
               && ozone->animations.list_alpha < 1.0f)
         {
            ozone_animation_list_alpha(ozone, true);
            new_action = MENU_ACTION_NOOP;
         }
         break;
      default:
         break;
   }

   return new_action;
}

/* Menu entry action callback */
static int ozone_menu_entry_action(
      void *userdata,
      menu_entry_t *entry,
      size_t i,
      enum menu_action action)
{
   menu_entry_t new_entry;
   ozone_handle_t *ozone       = (ozone_handle_t*)userdata;
   struct menu_state *menu_st  = menu_state_get_ptr();
   settings_t *settings        = config_get_ptr();
   menu_entry_t *entry_ptr     = entry;
   size_t selection            = i;
   /* Process input action */
   enum menu_action new_action = ozone_parse_menu_entry_action(ozone,
         settings->bools.menu_navigation_wraparound_enable,
         settings->bools.ozone_collapse_sidebar,
         action);
   /* Check whether current selection has changed
    * (due to automatic on screen entry selection...) */
   size_t new_selection        = menu_st->selection_ptr;

   if (new_selection != selection)
   {
      /* Selection has changed - must update
       * entry pointer */
      MENU_ENTRY_INITIALIZE(new_entry);
      menu_entry_get(&new_entry, 0, new_selection, NULL, true);
      entry_ptr                = &new_entry;
   }

   /* Call standard generic_menu_entry_action() function */
   return generic_menu_entry_action(userdata,
         entry_ptr,
         new_selection,
         new_action);
}

static void ozone_menu_animation_update_time(
      float *ticker_pixel_increment,
      unsigned video_width,
      unsigned video_height)
{
   gfx_display_t *p_disp      = disp_get_ptr();
   settings_t *settings       = config_get_ptr();
   /* Ozone uses DPI scaling
    * > Smooth ticker scaling multiplier is
    *   gfx_display_get_dpi_scale() multiplied by
    *   a small correction factor to achieve a
    *   default scroll speed equal to that of the
    *   non-smooth ticker */
   *(ticker_pixel_increment) *= gfx_display_get_dpi_scale(p_disp,
         settings, video_width, video_height, false, false) * 0.5f;
}

static void *ozone_init(void **userdata, bool video_is_threaded)
{
   unsigned i;
   bool fallback_color_theme           = false;
   unsigned width, height, color_theme = 0;
   ozone_handle_t *ozone               = NULL;
   settings_t *settings                = config_get_ptr();
   gfx_animation_t *p_anim             = anim_get_ptr();
   gfx_display_t *p_disp               = disp_get_ptr();
   struct menu_state *menu_st          = menu_state_get_ptr();
   menu_handle_t *menu                 = (menu_handle_t*)calloc(1, sizeof(*menu));
   const char *directory_assets        = settings->paths.directory_assets;

   if (!menu)
      return NULL;
   if (!(ozone = (ozone_handle_t*)calloc(1, sizeof(ozone_handle_t))))
      goto error;

   *userdata = ozone;

   for (i = 0; i < 15; i++)
      ozone->pure_white[i]                      = 1.00f;

   video_driver_get_size(&width, &height);

   ozone->last_width                            = width;
   ozone->last_height                           = height;
   ozone->last_scale_factor                     = gfx_display_get_dpi_scale(p_disp,
         settings, width, height, false, false);
   ozone->last_thumbnail_scale_factor           = settings->floats.ozone_thumbnail_scale_factor;

   ozone->selection_buf_old.list                = NULL;
   ozone->selection_buf_old.capacity            = 0;
   ozone->selection_buf_old.size                = 0;

   ozone->flags                                |= OZONE_FLAG_DRAW_SIDEBAR;
   ozone->sidebar_offset                        = 0;
   ozone->pending_message                       = NULL;
   ozone->categories_selection_ptr              = 0;
   ozone->pending_message                       = NULL;

   ozone->flags                                |= OZONE_FLAG_FIRST_FRAME;

   ozone->animations.sidebar_text_alpha         = 1.0f;
   ozone->animations.thumbnail_bar_position     = 0.0f;
   ozone->dimensions_sidebar_width              = 0.0f;

   ozone->num_search_terms_old                  = 0;

   ozone->flags2                               &= ~OZONE_FLAG2_CURSOR_WIGGLING;
   ozone->flags2                               &= ~OZONE_FLAG2_BLOCK_ANIMATION;

   if (!(ozone->screensaver = menu_screensaver_init()))
      goto error;

   ozone->savestate_thumbnail_file_path[0]      = '\0';
   ozone->prev_savestate_thumbnail_file_path[0] = '\0';

   ozone->animations.fullscreen_thumbnail_alpha = 0.0f;
   ozone->fullscreen_thumbnail_selection        = 0;
   ozone->fullscreen_thumbnail_label[0]         = '\0';

   ozone->animations.left_thumbnail_alpha       = 1.0f;

   ozone->thumbnails.pending                    = OZONE_PENDING_THUMBNAIL_NONE;
   ozone->thumbnails.stream_delay               = OZONE_THUMBNAIL_STREAM_DELAY;
   gfx_thumbnail_set_stream_delay(ozone->thumbnails.stream_delay);
   gfx_thumbnail_set_fade_duration(-1.0f);
   gfx_thumbnail_set_fade_missing(false);

   ozone_sidebar_update_collapse(ozone, settings->bools.ozone_collapse_sidebar, false);
   ozone_refresh_system_tabs_list(ozone);

   for (i = 0; i < OZONE_TAB_MAX_LENGTH; i++)
      ozone->tab_selection[i]                   = 0;

   menu_st->flags                 &=  ~MENU_ST_FLAG_PREVENT_POPULATE;

   /* TODO/FIXME - we don't use framebuffer at all
    * for Ozone, we should refactor this dependency
    * away. */
   p_disp->framebuf_width  = width;
   p_disp->framebuf_height = height;

   gfx_display_init_white_texture();

   ozone->horizontal_list.list                  = NULL;
   ozone->horizontal_list.capacity              = 0;
   ozone->horizontal_list.size                  = 0;

   ozone_init_horizontal_list(ozone, settings);

   /* Theme */
   if (settings->bools.menu_use_preferred_system_color_theme)
   {
#ifdef HAVE_LIBNX
      if (R_SUCCEEDED(setsysInitialize()))
      {
         ColorSetId theme;
         setsysGetColorSetId(&theme);
         color_theme = (theme == ColorSetId_Dark) ? 1 : 0;
         ozone_set_color_theme(ozone, color_theme);
         configuration_set_uint(settings,
               settings->uints.menu_ozone_color_theme, color_theme);
         configuration_set_bool(settings,
               settings->bools.menu_preferred_system_color_theme_set, true);
         setsysExit();
      }
      else
#endif
         fallback_color_theme                   = true;
   }
   else
      fallback_color_theme                      = true;

   if (fallback_color_theme)
   {
      color_theme                               = settings->uints.menu_ozone_color_theme;
      ozone_set_color_theme(ozone, color_theme);
   }

   ozone->flags                                &= ~OZONE_FLAG_NEED_COMPUTE;
   ozone->animations.scroll_y                   = 0.0f;
   ozone->animations.scroll_y_sidebar           = 0.0f;

   ozone->first_onscreen_entry                  = 0;
   ozone->last_onscreen_entry                   = 0;
   ozone->first_onscreen_category               = 0;
   ozone->last_onscreen_category                = 0;

   /* Assets path */
   fill_pathname_join_special(
         ozone->assets_path,
         directory_assets,
         "ozone",
         sizeof(ozone->assets_path));

   /* PNG path */
   fill_pathname_join_special(
         ozone->png_path,
         ozone->assets_path,
         "png",
         sizeof(ozone->png_path));

   /* Sidebar path */
   fill_pathname_join_special(
         ozone->tab_path,
         ozone->png_path,
         "sidebar",
         sizeof(ozone->tab_path));

   /* Icons path */
   fill_pathname_application_special(ozone->icons_path,
         sizeof(ozone->icons_path),
         APPLICATION_SPECIAL_DIRECTORY_ASSETS_OZONE_ICONS);
   fill_pathname_join_special(ozone->icons_path_default,
         ozone->icons_path,
         "default",
         sizeof(ozone->icons_path_default));

   if (settings->bools.menu_use_preferred_system_color_theme)
      ozone->flags2 |=  OZONE_FLAG2_LAST_USE_PREFERRED_SYSTEM_COLOR_THEME;
   else
      ozone->flags2 &= ~OZONE_FLAG2_LAST_USE_PREFERRED_SYSTEM_COLOR_THEME;

   p_anim->updatetime_cb                        = ozone_menu_animation_update_time;

   /* set word_wrap function pointer */
   ozone->word_wrap                             = msg_hash_get_wideglyph_str()
         ? word_wrap_wideglyph
         : word_wrap;

   return menu;

error:
   if (ozone)
   {
      ozone_free_list_nodes(&ozone->horizontal_list, false);
      ozone_free_list_nodes(&ozone->selection_buf_old, false);
      file_list_deinitialize(&ozone->horizontal_list);
      file_list_deinitialize(&ozone->selection_buf_old);
      RHMAP_FREE(ozone->playlist_db_node_map);
   }

   if (menu)
      free(menu);

   return NULL;
}

static void ozone_free(void *data)
{
   ozone_handle_t *ozone   = (ozone_handle_t*) data;

   if (ozone)
   {
      video_coord_array_free(&ozone->fonts.footer.raster_block.carr);
      video_coord_array_free(&ozone->fonts.title.raster_block.carr);
      video_coord_array_free(&ozone->fonts.time.raster_block.carr);
      video_coord_array_free(&ozone->fonts.entries_label.raster_block.carr);
      video_coord_array_free(&ozone->fonts.entries_sublabel.raster_block.carr);
      video_coord_array_free(&ozone->fonts.sidebar.raster_block.carr);

      ozone_free_list_nodes(&ozone->selection_buf_old, false);
      ozone_free_list_nodes(&ozone->horizontal_list, false);
      file_list_deinitialize(&ozone->selection_buf_old);
      file_list_deinitialize(&ozone->horizontal_list);
      RHMAP_FREE(ozone->playlist_db_node_map);

      if (!string_is_empty(ozone->pending_message))
         free(ozone->pending_message);

      menu_screensaver_free(ozone->screensaver);
   }

   gfx_display_deinit_white_texture();

   font_driver_bind_block(NULL, NULL);
}

static void ozone_update_thumbnail_image(void *data)
{
   bool show_thumbnail_bar, want_thumbnail_bar;
   ozone_handle_t *ozone      = (ozone_handle_t*)data;
   struct menu_state *menu_st = menu_state_get_ptr();

   if (!ozone)
      return;

   /* Cache previous status to remove footer metadata indicator blinking */
   ozone->thumbnails_left_status_prev  = ozone->thumbnails.left.status;
   ozone->thumbnails_right_status_prev = ozone->thumbnails.right.status;
   ozone->thumbnails.pending           = OZONE_PENDING_THUMBNAIL_NONE;
   gfx_thumbnail_cancel_pending_requests();

   if (!(ozone->flags & OZONE_FLAG_SKIP_THUMBNAIL_RESET))
   {
      gfx_thumbnail_reset(&ozone->thumbnails.right);
      gfx_thumbnail_reset(&ozone->thumbnails.left);
   }

   /* Right thumbnail */
   if (gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT))
      ozone->thumbnails.pending = OZONE_PENDING_THUMBNAIL_RIGHT;

   /* Left thumbnail
    * > Disabled for image (and video/music) content */
   if (     (!(ozone->flags2 & OZONE_FLAG2_SELECTION_CORE_IS_VIEWER))
         && (gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT)))
      ozone->thumbnails.pending = (ozone->thumbnails.pending == OZONE_PENDING_THUMBNAIL_RIGHT)
            ? OZONE_PENDING_THUMBNAIL_BOTH
            : OZONE_PENDING_THUMBNAIL_LEFT;

   /* Use left thumbnail as imageviewer failsafe if right is not enabled */
   if (     (ozone->flags2 & OZONE_FLAG2_SELECTION_CORE_IS_VIEWER)
         && ( gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
         && (!gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT)))
      ozone->thumbnails.pending = OZONE_PENDING_THUMBNAIL_LEFT;

   show_thumbnail_bar = ozone->show_thumbnail_bar;
   want_thumbnail_bar = (ozone->flags & OZONE_FLAG_WANT_THUMBNAIL_BAR) ? true : false;
   if (show_thumbnail_bar != want_thumbnail_bar)
      ozone->flags |= OZONE_FLAG_NEED_COMPUTE;

   if (ozone->flags2 & OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS)
      menu_update_fullscreen_thumbnail_label(
            ozone->fullscreen_thumbnail_label,
            sizeof(ozone->fullscreen_thumbnail_label),
            (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU) ? true : false,
            ozone->title);
}

static void ozone_refresh_thumbnail_image(void *data, size_t i)
{
   ozone_handle_t *ozone        = (ozone_handle_t*)data;
   struct menu_state   *menu_st = menu_state_get_ptr();

   if (!ozone)
      return;

   ozone->flags &= ~OZONE_FLAG_SKIP_THUMBNAIL_RESET;
   ozone_unload_thumbnail_textures(ozone);

   ozone->flags2 |= OZONE_FLAG2_BLOCK_ANIMATION;

   /* Refresh metadata */
   if (!i)
      ozone_update_content_metadata(ozone);

   /* Only refresh thumbnails if thumbnails are enabled */
   if (     (  gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT)
            || gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
         && (ozone->flags & OZONE_FLAG_WANT_THUMBNAIL_BAR)
         && (  (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU)
            || (ozone->flags  & OZONE_FLAG_IS_PLAYLIST)
            || (ozone->flags  & OZONE_FLAG_IS_EXPLORE_LIST)))
      ozone_update_thumbnail_image(ozone);
}

static bool ozone_init_font(
      font_data_impl_t *font_data,
      bool is_threaded,
      char *font_path,
      float font_size)
{
   int glyph_width               = 0;
   gfx_display_t *p_disp         = disp_get_ptr();
   const char *wideglyph_str     = msg_hash_get_wideglyph_str();

   /* Free existing */
   if (font_data->font)
   {
      font_driver_free(font_data->font);
      font_data->font            = NULL;
   }

   /* Enforce minimum readable font size for small screens */
   if (font_size < 9)
      font_size = 9;

   /* Cache approximate dimensions */
   font_data->line_height        = (int)(font_size + 0.5f);
   font_data->glyph_width        = (int)((font_size * (3.0f / 4.0f)) + 0.5f);

   /* Create font */
   if (!(font_data->font = gfx_display_font_file(p_disp, font_path, font_size, is_threaded)))
      return false;

   /* Get font metadata */
   if ((glyph_width = font_driver_get_message_width(font_data->font, "a", 1, 1.0f)) > 0)
      font_data->glyph_width     = glyph_width;

   font_data->wideglyph_width    = 100;

   if (wideglyph_str)
   {
      int wideglyph_width        =
            font_driver_get_message_width(font_data->font,
                  wideglyph_str, strlen(wideglyph_str), 1.0f);
      if (wideglyph_width > 0 && glyph_width > 0)
         font_data->wideglyph_width = wideglyph_width * 100 / glyph_width;
   }

   font_data->line_height        = font_driver_get_line_height(font_data->font, 1.0f);
   font_data->line_ascender      = font_driver_get_line_ascender(font_data->font, 1.0f);
   font_data->line_centre_offset = font_driver_get_line_centre_offset(font_data->font, 1.0f);

   return true;
}

static void ozone_cache_footer_label(
      ozone_handle_t *ozone,
      ozone_footer_label_t *label,
      enum msg_hash_enums enum_idx)
{
   const char *str = msg_hash_to_str(enum_idx);
   /* Determine pixel width */
   size_t _len     = strlen(str);

   /* Assign string */
   label->str      = str;
   label->width    = font_driver_get_message_width(ozone->fonts.footer.font, label->str, _len, 1.0f);

   /* If font_driver_get_message_width() fails,
    * use predetermined glyph_width as a fallback */
   if (label->width < 0)
      label->width = (int)(_len * ozone->fonts.footer.glyph_width);
}

/* Assigns footer label strings (based on current
 * menu language) and calculates pixel widths */
static void ozone_cache_footer_labels(ozone_handle_t *ozone)
{
   /* Just the resume icon or text too?
    * The former seems better to me. */
   /* ozone_cache_footer_label(ozone,
         &ozone->footer_labels.resume,
         MENU_ENUM_SUBLABEL_RESUME_CONTENT);*/

   ozone_cache_footer_label(ozone,
         &ozone->footer_labels.ok,
         MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_OK);

   ozone_cache_footer_label(ozone,
         &ozone->footer_labels.back,
         MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_BACK);

   ozone_cache_footer_label(ozone,
         &ozone->footer_labels.search,
         MENU_ENUM_LABEL_VALUE_SEARCH);

   ozone_cache_footer_label(ozone,
         &ozone->footer_labels.reset_to_default,
         MENU_ENUM_LABEL_VALUE_RESET_TO_DEFAULT_CONFIG);

   ozone_cache_footer_label(ozone,
         &ozone->footer_labels.random_select,
         MENU_ENUM_LABEL_VALUE_RANDOM_SELECT);

   ozone_cache_footer_label(ozone,
         &ozone->footer_labels.cycle_thumbnails,
         MENU_ENUM_LABEL_VALUE_CYCLE_THUMBNAILS);

   ozone_cache_footer_label(ozone,
         &ozone->footer_labels.fullscreen_thumbnails,
         MSG_TOGGLE_FULLSCREEN_THUMBNAILS);

   ozone_cache_footer_label(ozone,
         &ozone->footer_labels.metadata_override,
         MSG_TOGGLE_CONTENT_METADATA);

   ozone_cache_footer_label(ozone,
         &ozone->footer_labels.help,
         MENU_ENUM_LABEL_VALUE_HELP);

   ozone_cache_footer_label(ozone,
         &ozone->footer_labels.clear_setting,
         MENU_ENUM_LABEL_VALUE_CLEAR_SETTING);

   ozone_cache_footer_label(ozone,
         &ozone->footer_labels.scan,
         MENU_ENUM_LABEL_VALUE_SCAN_ENTRY);

   ozone_cache_footer_label(ozone,
         &ozone->footer_labels.manage,
         MENU_ENUM_LABEL_VALUE_MANAGE);

   /* Record current language setting */
   ozone->footer_labels_language = *msg_hash_get_uint(MSG_HASH_USER_LANGUAGE);
}

/* Determines the size of all menu elements */
static void ozone_set_layout(
      ozone_handle_t *ozone,
      const char *path_directory_assets,
      bool ozone_collapse_sidebar,
      bool is_threaded)
{
   settings_t *settings                             = config_get_ptr();
   const char *path_menu_font                       = settings->paths.path_menu_ozone_font;
   char tmp_dir[DIR_MAX_LENGTH];
   char font_path[PATH_MAX_LENGTH];
   bool font_inited                                 = false;
   float scale_factor                               = ozone->last_scale_factor;
   float padding_factor                             = settings->floats.ozone_padding_factor;
   unsigned font_scale                              = settings->uints.menu_ozone_font_scale;
   float font_scale_factor_global                   = (font_scale == 1) ? (settings->floats.ozone_font_scale_factor_global) : 1.0f;
   float font_scale_factor_title                    = (font_scale == 2) ? (settings->floats.ozone_font_scale_factor_title) : 1.0f;
   float font_scale_factor_sidebar                  = (font_scale == 2) ? (settings->floats.ozone_font_scale_factor_sidebar) : 1.0f;
   float font_scale_factor_label                    = (font_scale == 2) ? (settings->floats.ozone_font_scale_factor_label) : 1.0f;
   float font_scale_factor_sublabel                 = (font_scale == 2) ? (settings->floats.ozone_font_scale_factor_sublabel) : 1.0f;
   float font_scale_factor_time                     = (font_scale == 2) ? (settings->floats.ozone_font_scale_factor_time) : 1.0f;
   float font_scale_factor_footer                   = (font_scale == 2) ? (settings->floats.ozone_font_scale_factor_footer) : 1.0f;

   /* Calculate dimensions */
   ozone->dimensions.header_height                  = HEADER_HEIGHT * scale_factor;
   ozone->dimensions.footer_height                  = FOOTER_HEIGHT * scale_factor;

   ozone->dimensions.entry_padding_horizontal_half  = ENTRY_PADDING_HORIZONTAL_HALF * scale_factor * padding_factor;
   ozone->dimensions.entry_padding_horizontal_full  = ENTRY_PADDING_HORIZONTAL_FULL * scale_factor * padding_factor;
   ozone->dimensions.entry_padding_vertical         = ENTRY_PADDING_VERTICAL * scale_factor;
   ozone->dimensions.entry_height                   = ENTRY_HEIGHT * scale_factor;
   ozone->dimensions.entry_spacing                  = ENTRY_SPACING * scale_factor;
   ozone->dimensions.entry_icon_size                = ENTRY_ICON_SIZE * scale_factor;
   ozone->dimensions.entry_icon_padding             = ENTRY_ICON_PADDING * scale_factor;

   ozone->dimensions.sidebar_entry_height           = SIDEBAR_ENTRY_HEIGHT * scale_factor;
   ozone->dimensions.sidebar_padding_horizontal     = SIDEBAR_X_PADDING * scale_factor * padding_factor;
   ozone->dimensions.sidebar_padding_vertical       = SIDEBAR_Y_PADDING * scale_factor;
   ozone->dimensions.sidebar_entry_padding_vertical = SIDEBAR_ENTRY_Y_PADDING * scale_factor;
   ozone->dimensions.sidebar_entry_icon_size        = SIDEBAR_ENTRY_ICON_SIZE * scale_factor;
   ozone->dimensions.sidebar_entry_icon_padding     = SIDEBAR_ENTRY_ICON_PADDING * scale_factor;
   ozone->dimensions.sidebar_gradient_height        = SIDEBAR_GRADIENT_HEIGHT * scale_factor;

   ozone->dimensions.sidebar_width_normal           = SIDEBAR_WIDTH * scale_factor;
   ozone->dimensions.sidebar_width_collapsed        = ozone->dimensions.sidebar_entry_icon_size
         + ozone->dimensions.sidebar_entry_icon_padding * 2
         + ozone->dimensions.sidebar_padding_horizontal * 2;

   if (ozone->dimensions_sidebar_width == 0)
      ozone->dimensions_sidebar_width               = (float)ozone->dimensions.sidebar_width_normal;

   ozone->dimensions.thumbnail_bar_width            = ozone->last_thumbnail_scale_factor *
         (ozone->dimensions.sidebar_width_normal -
          ozone->dimensions.sidebar_entry_icon_size +
          ozone->dimensions.sidebar_entry_icon_padding);

   /* Prevent thumbnail sidebar from growing too much and making the UI unusable. */
   if (ozone->dimensions.thumbnail_bar_width > ozone->last_width / 3.0f)
      ozone->dimensions.thumbnail_bar_width         = ozone->last_width / 3.0f;

   ozone->dimensions.cursor_size                    = CURSOR_SIZE * scale_factor;

   ozone->dimensions.fullscreen_thumbnail_padding   = FULLSCREEN_THUMBNAIL_PADDING * scale_factor;

   /* Common spacers */
   ozone->dimensions.spacer_1px = (scale_factor > 1.0f) ? (unsigned)(scale_factor + 0.5f) : 1;
   ozone->dimensions.spacer_2px = ozone->dimensions.spacer_1px * 2;
   ozone->dimensions.spacer_3px = (unsigned)((scale_factor * 3.0f) + 0.5f);
   ozone->dimensions.spacer_5px = (unsigned)((scale_factor * 5.0f) + 0.5f);

   /* Determine movement delta size for activating
    * pointer input (note: not a dimension as such,
    * so not included in the 'dimensions' struct) */
   ozone->pointer_active_delta = CURSOR_ACTIVE_DELTA * scale_factor;

   /* Initialise fonts */
   switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE))
   {
      case RETRO_LANGUAGE_ARABIC:
      case RETRO_LANGUAGE_PERSIAN:
         fill_pathname_join_special(tmp_dir, path_directory_assets, "pkg", sizeof(tmp_dir));
         fill_pathname_join_special(font_path, tmp_dir, "fallback-font.ttf", sizeof(font_path));
         break;
      case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
      case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
         fill_pathname_join_special(tmp_dir, path_directory_assets, "pkg", sizeof(tmp_dir));
         fill_pathname_join_special(font_path, tmp_dir, "chinese-fallback-font.ttf", sizeof(font_path));
         break;
      case RETRO_LANGUAGE_KOREAN:
         fill_pathname_join_special(tmp_dir, path_directory_assets, "pkg", sizeof(tmp_dir));
         fill_pathname_join_special(font_path, tmp_dir, "korean-fallback-font.ttf", sizeof(font_path));
         break;
      default:
         fill_pathname_join_special(font_path, ozone->assets_path, "bold.ttf", sizeof(font_path));
         break;
   }

   if (!string_is_empty(path_menu_font))
      strlcpy(font_path, path_menu_font, sizeof(font_path));

   font_inited = ozone_init_font(&ozone->fonts.title,
         is_threaded, font_path, FONT_SIZE_TITLE * scale_factor * font_scale_factor_global * font_scale_factor_title);
   if (!(((ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS) > 0) && font_inited))
      ozone->flags &= ~OZONE_FLAG_HAS_ALL_ASSETS;

   switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE))
   {
      case RETRO_LANGUAGE_ARABIC:
      case RETRO_LANGUAGE_PERSIAN:
         fill_pathname_join_special(tmp_dir, path_directory_assets, "pkg", sizeof(tmp_dir));
         fill_pathname_join_special(font_path, tmp_dir, "fallback-font.ttf", sizeof(font_path));
         break;
      case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
      case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
         fill_pathname_join_special(tmp_dir, path_directory_assets, "pkg", sizeof(tmp_dir));
         fill_pathname_join_special(font_path, tmp_dir, "chinese-fallback-font.ttf", sizeof(font_path));
         break;
      case RETRO_LANGUAGE_KOREAN:
         fill_pathname_join_special(tmp_dir, path_directory_assets, "pkg", sizeof(tmp_dir));
         fill_pathname_join_special(font_path, tmp_dir, "korean-fallback-font.ttf", sizeof(font_path));
         break;
      default:
         fill_pathname_join_special(font_path, ozone->assets_path, "regular.ttf", sizeof(font_path));
         break;
   }

   if (!string_is_empty(path_menu_font))
      strlcpy(font_path, path_menu_font, sizeof(font_path));

   ozone->font_unicode = !string_is_equal(path_menu_font, FILE_PATH_UNKNOWN) ? true : false;

   /* Sidebar */
   font_inited = ozone_init_font(&ozone->fonts.sidebar,
         is_threaded, font_path, FONT_SIZE_SIDEBAR * scale_factor * font_scale_factor_global * font_scale_factor_sidebar);
   if (!(((ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS) > 0) && font_inited))
      ozone->flags &= ~OZONE_FLAG_HAS_ALL_ASSETS;

   /* Entries */
   font_inited = ozone_init_font(&ozone->fonts.entries_label,
         is_threaded, font_path, FONT_SIZE_ENTRIES_LABEL * scale_factor * font_scale_factor_global * font_scale_factor_label);
   if (!(((ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS) > 0) && font_inited))
      ozone->flags &= ~OZONE_FLAG_HAS_ALL_ASSETS;

   /* Sublabels */
   font_inited = ozone_init_font(&ozone->fonts.entries_sublabel,
         is_threaded, font_path, FONT_SIZE_ENTRIES_SUBLABEL * scale_factor * font_scale_factor_global * font_scale_factor_sublabel);
   if (!(((ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS) > 0) && font_inited))
      ozone->flags &= ~OZONE_FLAG_HAS_ALL_ASSETS;

   /* Time */
   font_inited = ozone_init_font(&ozone->fonts.time,
         is_threaded, font_path, FONT_SIZE_TIME * scale_factor * font_scale_factor_global * font_scale_factor_time);
   if (!(((ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS) > 0) && font_inited))
      ozone->flags &= ~OZONE_FLAG_HAS_ALL_ASSETS;

   /* Footer */
   font_inited = ozone_init_font(&ozone->fonts.footer,
         is_threaded, font_path, FONT_SIZE_FOOTER * scale_factor * font_scale_factor_global * font_scale_factor_footer);
   if (!(((ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS) > 0) && font_inited))
      ozone->flags &= ~OZONE_FLAG_HAS_ALL_ASSETS;

   /* Cache footer text labels
    * > Fonts have been (re)initialised, so need
    *   to recalculate label widths */
   ozone_cache_footer_labels(ozone);

   /* Multiple sidebar parameters are set via animations
    * > ozone_refresh_sidebars() cancels any existing
    *   animations and 'force updates' the affected
    *   variables with newly scaled values */
   ozone_refresh_sidebars(ozone, ozone_collapse_sidebar, ozone->last_height);

   /* Entry dimensions must be recalculated after
    * updating menu layout */
   ozone->flags |= OZONE_FLAG_NEED_COMPUTE;
}

static void ozone_context_reset(void *data, bool is_threaded)
{
   static const char *OZONE_TAB_TEXTURES_FILES[OZONE_TAB_TEXTURE_LAST] = {
      "retroarch.png", /* MAIN_MENU */
      "settings.png",  /* SETTINGS_TAB */
      "history.png",   /* HISTORY_TAB */
      "favorites.png", /* FAVORITES_TAB */
      "image.png",     /* IMAGES_TAB */
      "music.png",     /* MUSIC_TAB */
      "video.png",     /* VIDEO_TAB */
      "netplay.png",   /* NETPLAY_TAB */
      "add.png",       /* ADD_TAB */
      "core.png",      /* CONTENTLESS_CORES_TAB */
      "database.png"   /* EXPLORE_TAB */
   };
   static const char *OZONE_TEXTURES_FILES[OZONE_TEXTURE_LAST]         = {
      "retroarch.png",
      "cursor_border.png"
   };
   unsigned i;
   ozone_handle_t *ozone      = (ozone_handle_t*) data;

   if (ozone)
   {
      settings_t *settings = config_get_ptr();

      ozone->flags |= OZONE_FLAG_HAS_ALL_ASSETS;

      if (settings->bools.menu_ignore_missing_assets)
         ozone->flags2 |= OZONE_FLAG2_IGNORE_MISSING_ASSETS;

      ozone_set_layout(ozone, settings->paths.directory_assets,
            settings->bools.ozone_collapse_sidebar, is_threaded);

      /* Textures init */
      for (i = 0; i < OZONE_TEXTURE_LAST; i++)
      {
         char filename[64];
         strlcpy(filename, OZONE_TEXTURES_FILES[i], sizeof(filename));

         if (!gfx_display_reset_textures_list(filename,
               ozone->png_path, &ozone->textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
            ozone->flags &= ~OZONE_FLAG_HAS_ALL_ASSETS;
      }

      /* Sidebar textures */
      for (i = 0; i < OZONE_TAB_TEXTURE_LAST; i++)
      {
         switch (i)
         {
            /* Exceptions for icons that don't exist in 'png/sidebar/' */
            case OZONE_TAB_TEXTURE_CONTENTLESS_CORES:
            case OZONE_TAB_TEXTURE_EXPLORE:
               if (!gfx_display_reset_textures_list(OZONE_TAB_TEXTURES_FILES[i],
                     ozone->icons_path, &ozone->tab_textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
                  ozone->flags &= ~OZONE_FLAG_HAS_ALL_ASSETS;
               break;
            default:
               if (!gfx_display_reset_textures_list(OZONE_TAB_TEXTURES_FILES[i],
                     ozone->tab_path, &ozone->tab_textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
                  ozone->flags &= ~OZONE_FLAG_HAS_ALL_ASSETS;
               break;
         }
      }

      /* Theme textures */
      if (!ozone_reset_theme_textures(ozone))
         ozone->flags &= ~OZONE_FLAG_HAS_ALL_ASSETS;

      /* Icons textures init */
      for (i = 0; i < OZONE_ENTRIES_ICONS_TEXTURE_LAST; i++)
      {
         if (!gfx_display_reset_textures_list(ozone_entries_icon_texture_path(i),
               ozone->icons_path, &ozone->icons_textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
            ozone->flags &= ~OZONE_FLAG_HAS_ALL_ASSETS;
      }

      gfx_display_deinit_white_texture();
      gfx_display_init_white_texture();

      /* Horizontal list */
      ozone_context_reset_horizontal_list(ozone);

      /* State reset */
      ozone->flags                       &= ~(OZONE_FLAG_DRAW_OLD_LIST
                                            | OZONE_FLAG_FADE_DIRECTION
                                            | OZONE_FLAG_CURSOR_IN_SIDEBAR
                                            | OZONE_FLAG_CURSOR_IN_SIDEBAR_OLD
                                            | OZONE_FLAG_MSGBOX_STATE
                                            | OZONE_FLAG_MSGBOX_STATE_OLD
                                             );

      ozone->flags2                      &= ~OZONE_FLAG2_CURSOR_WIGGLING;

      /* Animations */
      ozone->animations.cursor_alpha      = 1.0f;
      ozone->animations.scroll_y          = 0.0f;
      ozone->animations.list_alpha        = 1.0f;
      ozone->animations.alpha             = 1.0f;

      /* Thumbnails */
      ozone_update_thumbnail_image(ozone);
      ozone_update_savestate_thumbnail_image(ozone);

      if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS || ozone->flags2 & OZONE_FLAG2_IGNORE_MISSING_ASSETS)
         ozone_restart_cursor_animation(ozone);

      /* Screensaver */
      menu_screensaver_context_destroy(ozone->screensaver);
   }

   video_driver_monitor_reset();
}

static void ozone_collapse_end(void *userdata)
{
   ozone_handle_t *ozone = (ozone_handle_t*)userdata;
   ozone->flags         &= ~OZONE_FLAG_DRAW_SIDEBAR;
}

static void ozone_unload_thumbnail_textures(void *data)
{
   ozone_handle_t *ozone = (ozone_handle_t*)data;
   if (!ozone)
      return;

   ozone->thumbnails.pending = OZONE_PENDING_THUMBNAIL_NONE;
   gfx_thumbnail_cancel_pending_requests();
   gfx_thumbnail_reset(&ozone->thumbnails.right);
   gfx_thumbnail_reset(&ozone->thumbnails.left);
   gfx_thumbnail_reset(&ozone->thumbnails.savestate);
}

static INLINE void ozone_font_free(font_data_impl_t *font_data)
{
   if (font_data->font)
      font_driver_free(font_data->font);

   font_data->font = NULL;
}

static void ozone_context_destroy(void *data)
{
   unsigned i;
   uintptr_t tag;
   ozone_handle_t *ozone = (ozone_handle_t*) data;

   if (!ozone)
      return;

   /* Theme */
   ozone_unload_theme_textures(ozone);

   /* Icons */
   for (i = 0; i < OZONE_ENTRIES_ICONS_TEXTURE_LAST; i++)
      video_driver_texture_unload(&ozone->icons_textures[i]);

   /* Textures */
   for (i = 0; i < OZONE_TEXTURE_LAST; i++)
      video_driver_texture_unload(&ozone->textures[i]);

   /* Icons */
   for (i = 0; i < OZONE_TAB_TEXTURE_LAST; i++)
      video_driver_texture_unload(&ozone->tab_textures[i]);

   /* Thumbnails */
   ozone_unload_thumbnail_textures(ozone);

   gfx_display_deinit_white_texture();

   /* Fonts */
   ozone_font_free(&ozone->fonts.footer);
   ozone_font_free(&ozone->fonts.title);
   ozone_font_free(&ozone->fonts.time);
   ozone_font_free(&ozone->fonts.entries_label);
   ozone_font_free(&ozone->fonts.entries_sublabel);
   ozone_font_free(&ozone->fonts.sidebar);

   tag = (uintptr_t) &ozone_default_theme;
   gfx_animation_kill_by_tag(&tag);

   /* Horizontal list */
   ozone_context_destroy_horizontal_list(ozone);

   /* Screensaver */
   menu_screensaver_context_destroy(ozone->screensaver);

   /* Forced depth reset due to menu position reset */
   if (ozone->flags2 & OZONE_FLAG2_RESET_DEPTH)
   {
      ozone->flags2 &= ~OZONE_FLAG2_RESET_DEPTH;
      ozone->depth = 1;
   }
}

static void *ozone_list_get_entry(void *data,
      enum menu_list_type type, unsigned i)
{
   ozone_handle_t* ozone   = (ozone_handle_t*) data;

   switch (type)
   {
      case MENU_LIST_PLAIN:
         {
            struct menu_state   *menu_st = menu_state_get_ptr();
            menu_list_t *menu_list       = menu_st->entries.list;
            file_list_t *menu_stack      = MENU_LIST_GET(menu_list, 0);
            size_t list_size             = MENU_LIST_GET_STACK_SIZE(menu_list, 0);
            if (i < list_size)
               return (void*)&menu_stack->list[i];
         }
         break;
      case MENU_LIST_HORIZONTAL:
         {
            size_t list_size = ozone->horizontal_list.size;
            if (i < list_size)
               return (void*)&ozone->horizontal_list.list[i];
         }
         break;
      default:
         break;
   }

   return NULL;
}

static int ozone_list_push(void *data, void *userdata,
      menu_displaylist_info_t *info, unsigned type)
{
   int ret                = -1;

   /* Use common lists for all drivers */
   return ret;

   switch (type)
   {
      case DISPLAYLIST_LOAD_CONTENT_LIST:
         {
            settings_t             *settings = config_get_ptr();
            bool menu_content_show_playlists = settings->bools.menu_content_show_playlists;
            bool kiosk_mode_enable           = settings->bools.kiosk_mode_enable;
            core_info_list_t *list           = NULL;

            core_info_get_list(&list);

            menu_entries_clear(info->list);

            menu_entries_append(info->list,
                  msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES),
                  msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES),
                  MENU_ENUM_LABEL_FAVORITES,
                  MENU_SETTING_ACTION_FAVORITES_DIR, 0, 0, NULL);

            if (menu_content_show_playlists)
               menu_entries_append(info->list,
                     msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLISTS_TAB),
                     msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB),
                     MENU_ENUM_LABEL_PLAYLISTS_TAB,
                     MENU_SETTING_ACTION, 0, 0, NULL);

            if (list && list->info_count > 0)
               menu_entries_append(info->list,
                     msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DOWNLOADED_FILE_DETECT_CORE_LIST),
                     msg_hash_to_str(MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST),
                     MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST,
                     MENU_SETTING_ACTION, 0, 0, NULL);

            if (frontend_driver_parse_drive_list(info->list, true) != 0)
               menu_entries_append(info->list, "/",
                     msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
                     MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR,
                     MENU_SETTING_ACTION, 0, 0, NULL);

            if (!kiosk_mode_enable)
               menu_entries_append(info->list,
                     msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MENU_FILE_BROWSER_SETTINGS),
                     msg_hash_to_str(MENU_ENUM_LABEL_MENU_FILE_BROWSER_SETTINGS),
                     MENU_ENUM_LABEL_MENU_FILE_BROWSER_SETTINGS,
                     MENU_SETTING_ACTION, 0, 0, NULL);

            info->flags |= MD_FLAG_NEED_PUSH | MD_FLAG_NEED_REFRESH;
            ret          = 0;
         }
         break;
      case DISPLAYLIST_MAIN_MENU:
         {
            settings_t   *settings      = config_get_ptr();
            uint32_t flags              = runloop_get_flags();

            menu_entries_clear(info->list);

            if (flags & RUNLOOP_FLAG_CORE_RUNNING)
            {
               if (!retroarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL))
               {
                  MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                        info->list,
                        MENU_ENUM_LABEL_CONTENT_SETTINGS,
                        PARSE_ACTION,
                        false);
               }
            }
            else
            {
               rarch_system_info_t *sys_info = &runloop_state_get_ptr()->system;
               if (     sys_info
                     && sys_info->load_no_content)
               {
                  MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                        info->list,
                        MENU_ENUM_LABEL_START_CORE,
                        PARSE_ACTION,
                        false);
               }

#ifndef HAVE_DYNAMIC
               if (frontend_driver_has_fork())
#endif
               {
                  if (settings->bools.menu_show_load_core)
                  {
                     MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                           info->list,
                           MENU_ENUM_LABEL_CORE_LIST,
                           PARSE_ACTION,
                           false);
                  }
               }
            }

            if (settings->bools.menu_show_load_content)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_LOAD_CONTENT_LIST,
                     PARSE_ACTION,
                     false);

               if (menu_displaylist_has_subsystems())
               {
                  MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                        info->list,
                        MENU_ENUM_LABEL_SUBSYSTEM_SETTINGS,
                        PARSE_ACTION,
                        false);
               }
            }

            if (settings->bools.menu_show_load_disc)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_LOAD_DISC,
                     PARSE_ACTION,
                     false);
            }

            if (settings->bools.menu_show_dump_disc)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_DUMP_DISC,
                     PARSE_ACTION,
                     false);
            }

#ifdef HAVE_LAKKA
            if (settings->bools.menu_show_eject_disc)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_EJECT_DISC,
                     PARSE_ACTION,
                     false);
            }
#endif

            MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                  info->list,
                  MENU_ENUM_LABEL_ADD_CONTENT_LIST,
                  PARSE_ACTION,
                  false);
#ifdef HAVE_QT
            if (settings->bools.desktop_menu_enable)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_SHOW_WIMP,
                     PARSE_ACTION,
                     false);
            }
#endif
#if defined(HAVE_NETWORKING)
#if defined(HAVE_ONLINE_UPDATER)
            if (      settings->bools.menu_show_online_updater
                  && !settings->bools.kiosk_mode_enable)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_ONLINE_UPDATER,
                     PARSE_ACTION,
                     false);
            }
#endif
#endif
#ifdef HAVE_MIST
            if (      settings->bools.menu_show_core_manager_steam
                  && !settings->bools.kiosk_mode_enable)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                  info->list,
                  MENU_ENUM_LABEL_CORE_MANAGER_STEAM_LIST,
                  PARSE_ACTION,
                  false);
            }
#endif
            if (     !settings->bools.menu_content_show_settings
                  && !string_is_empty(settings->paths.menu_content_show_settings_password))
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_XMB_MAIN_MENU_ENABLE_SETTINGS,
                     PARSE_ACTION,
                     false);
            }

            if (      settings->bools.kiosk_mode_enable
                  && !string_is_empty(settings->paths.kiosk_mode_password))
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_MENU_DISABLE_KIOSK_MODE,
                     PARSE_ACTION,
                     false);
            }

            if (settings->bools.menu_show_information)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_INFORMATION_LIST,
                     PARSE_ACTION,
                     false);
            }

#if defined(HAVE_LIBNX)
            MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                  info->list,
                  MENU_ENUM_LABEL_SWITCH_CPU_PROFILE,
                  PARSE_ACTION,
                  false);
#endif

            if (      settings->bools.menu_show_configurations
                  && !settings->bools.kiosk_mode_enable)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_CONFIGURATIONS_LIST,
                     PARSE_ACTION,
                     false);
            }

#if !defined(IOS)
            if (settings->bools.menu_show_restart_retroarch)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_RESTART_RETROARCH,
                     PARSE_ACTION,
                     false);
            }

            if (settings->bools.menu_show_quit_retroarch)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_QUIT_RETROARCH,
                     PARSE_ACTION,
                     false);
            }
#endif

            if (settings->bools.menu_show_reboot)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_REBOOT,
                     PARSE_ACTION,
                     false);
            }

            if (settings->bools.menu_show_shutdown)
            {
               MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
                     info->list,
                     MENU_ENUM_LABEL_SHUTDOWN,
                     PARSE_ACTION,
                     false);
            }

            info->flags |= MD_FLAG_NEED_PUSH;
            ret          = 0;
         }
         break;
   }
   return ret;
}

static size_t ozone_list_get_selection(void *data)
{
   ozone_handle_t *ozone      = (ozone_handle_t*)data;
   if (ozone)
      return ozone->categories_selection_ptr;
   return 0;
}

static void ozone_list_clear(file_list_t *list)
{
   uintptr_t tag = (uintptr_t)list;
   gfx_animation_kill_by_tag(&tag);

   ozone_free_list_nodes(list, false);
}

static void ozone_list_free(file_list_t *list, size_t a, size_t b)
{
   ozone_list_clear(list);
}

static void ozone_render(void *data,
      unsigned width,
      unsigned height,
      bool is_idle)
{
   size_t i;
   /* 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;
   volatile float thumbnail_scale_factor;
   volatile float padding_factor;
   volatile float font_scale_factor_global;
   volatile float font_scale_factor_title;
   volatile float font_scale_factor_sidebar;
   volatile float font_scale_factor_label;
   volatile float font_scale_factor_sublabel;
   volatile float font_scale_factor_time;
   volatile float font_scale_factor_footer;
   struct menu_state *menu_st         = menu_state_get_ptr();
   menu_input_t *menu_input           = &menu_st->input_state;
   menu_list_t *menu_list             = menu_st->entries.list;
   unsigned entries_end               = (unsigned)MENU_LIST_GET_SELECTION(menu_list, 0)->size;
   bool pointer_enabled               = false;
   unsigned language                  = *msg_hash_get_uint(MSG_HASH_USER_LANGUAGE);
   ozone_handle_t *ozone              = (ozone_handle_t*)data;
   gfx_display_t *p_disp              = disp_get_ptr();
   gfx_animation_t          *p_anim   = anim_get_ptr();
   settings_t             *settings   = config_get_ptr();
   bool ozone_collapse_sidebar        = settings->bools.ozone_collapse_sidebar;
   unsigned font_scale                = settings->uints.menu_ozone_font_scale;

   if (!ozone)
      return;

   /* Check whether screen dimensions or menu scale
    * factor have changed */
   scale_factor               = gfx_display_get_dpi_scale(p_disp, settings,
            width, height, false, false);
   thumbnail_scale_factor     = settings->floats.ozone_thumbnail_scale_factor;
   padding_factor             = settings->floats.ozone_padding_factor;
   font_scale_factor_global   = (font_scale == 1) ? (settings->floats.ozone_font_scale_factor_global) : 1.0f;
   font_scale_factor_title    = (font_scale == 2) ? (settings->floats.ozone_font_scale_factor_title) : 1.0f;
   font_scale_factor_sidebar  = (font_scale == 2) ? (settings->floats.ozone_font_scale_factor_sidebar) : 1.0f;
   font_scale_factor_label    = (font_scale == 2) ? (settings->floats.ozone_font_scale_factor_label) : 1.0f;
   font_scale_factor_sublabel = (font_scale == 2) ? (settings->floats.ozone_font_scale_factor_sublabel) : 1.0f;
   font_scale_factor_time     = (font_scale == 2) ? (settings->floats.ozone_font_scale_factor_time) : 1.0f;
   font_scale_factor_footer   = (font_scale == 2) ? (settings->floats.ozone_font_scale_factor_footer) : 1.0f;

   if (     (scale_factor != ozone->last_scale_factor)
         || (thumbnail_scale_factor != ozone->last_thumbnail_scale_factor)
         || (padding_factor != ozone->last_padding_factor)
         || (font_scale_factor_global != ozone->last_font_scale_factor_global)
         || (font_scale_factor_title != ozone->last_font_scale_factor_title)
         || (font_scale_factor_sidebar != ozone->last_font_scale_factor_sidebar)
         || (font_scale_factor_label != ozone->last_font_scale_factor_label)
         || (font_scale_factor_sublabel != ozone->last_font_scale_factor_sublabel)
         || (font_scale_factor_time != ozone->last_font_scale_factor_time)
         || (font_scale_factor_footer != ozone->last_font_scale_factor_footer)
         || (width != ozone->last_width)
         || (height != ozone->last_height))
   {
      ozone->last_scale_factor               = scale_factor;
      ozone->last_thumbnail_scale_factor     = thumbnail_scale_factor;
      ozone->last_padding_factor             = padding_factor;
      ozone->last_font_scale_factor_global   = font_scale_factor_global;
      ozone->last_font_scale_factor_title    = font_scale_factor_title;
      ozone->last_font_scale_factor_sidebar  = font_scale_factor_sidebar;
      ozone->last_font_scale_factor_label    = font_scale_factor_label;
      ozone->last_font_scale_factor_sublabel = font_scale_factor_sublabel;
      ozone->last_font_scale_factor_time     = font_scale_factor_time;
      ozone->last_font_scale_factor_footer   = font_scale_factor_footer;
      ozone->last_width                      = width;
      ozone->last_height                     = height;

      /* Note: We don't need a full context reset here
       * > Just rescale layout, and reset frame time counter */
      ozone_set_layout(ozone, settings->paths.directory_assets,
            ozone_collapse_sidebar,
            video_driver_is_threaded());
      video_driver_monitor_reset();
   }

   if (ozone->flags & OZONE_FLAG_NEED_COMPUTE)
   {
      ozone_compute_entries_position(ozone,
            settings->bools.savestate_thumbnail_enable,
            settings->bools.menu_show_sublabels, entries_end);
      ozone->flags &= ~OZONE_FLAG_NEED_COMPUTE;
   }

   /* Check whether menu language has changed
    * > If so, need to re-cache footer text labels */
   if (ozone->footer_labels_language != language)
   {
      ozone->footer_labels_language = language;
      ozone_cache_footer_labels(ozone);
   }

   ozone->selection        = menu_st->selection_ptr;

   /* Need to update this each frame, otherwise touchscreen
    * input breaks when changing orientation */
   p_disp->framebuf_width  = width;
   p_disp->framebuf_height = height;

   /* Read pointer state */
   menu_input_get_pointer_state(&ozone->pointer);

   /* If menu screensaver is active, update
    * screensaver and return */
   if (ozone->flags & OZONE_FLAG_SHOW_SCREENSAVER)
   {
      menu_screensaver_iterate(
            ozone->screensaver,
            p_disp, p_anim,
            (enum menu_screensaver_effect)settings->uints.menu_screensaver_animation,
            settings->floats.menu_screensaver_animation_speed,
            ozone->theme->screensaver_tint,
            width, height,
            settings->paths.directory_assets);
      GFX_ANIMATION_CLEAR_ACTIVE(p_anim);
      return;
   }

   /* Check whether pointer is enabled */
   if (ozone->pointer.type != MENU_POINTER_DISABLED)
   {
      /* When using a mouse, entry under pointer is
       * automatically selected
       * > Must therefore filter out small movements,
       *   otherwise scrolling with the mouse wheel
       *   becomes impossible... */
      if (ozone->pointer.type == MENU_POINTER_MOUSE)
      {
         int16_t cursor_x_delta = ozone->pointer.x - ozone->cursor_x_old;
         int16_t cursor_y_delta = ozone->pointer.y - ozone->cursor_y_old;

         if (   (cursor_x_delta >  ozone->pointer_active_delta)
             || (cursor_x_delta < -ozone->pointer_active_delta)
             || (cursor_y_delta >  ozone->pointer_active_delta)
             || (cursor_y_delta < -ozone->pointer_active_delta))
            ozone->flags |=  OZONE_FLAG_CURSOR_MODE;
      }
      /* On touchscreens, just check for any movement */
      else
      {
         if (   (ozone->pointer.x != ozone->cursor_x_old)
             || (ozone->pointer.y != ozone->cursor_y_old))
            ozone->flags |=  OZONE_FLAG_CURSOR_MODE;
      }
   }

   ozone->cursor_x_old = ozone->pointer.x;
   ozone->cursor_y_old = ozone->pointer.y;

   /* Pointer is disabled when:
    * - Showing fullscreen thumbnails
    * - On-screen keyboard is active
    * - A message box is being displayed */
   pointer_enabled =
              (ozone->flags  & OZONE_FLAG_CURSOR_MODE)
         && (!(ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS))
         && !menu_input_dialog_get_display_kb()
         && (!(ozone->flags & OZONE_FLAG_SHOULD_DRAW_MSGBOX));

   /* Process pointer input, if required */
   if (pointer_enabled)
   {
      bool pointer_in_sidebar, last_pointer_in_sidebar;
      file_list_t *selection_buf    = MENU_LIST_GET_SELECTION(menu_list, 0);
      uintptr_t     animation_tag   = (uintptr_t)selection_buf;

      int entry_padding             = (ozone->depth == 1)
            ? ozone->dimensions.entry_padding_horizontal_half
            : ozone->dimensions.entry_padding_horizontal_full;
      float entry_x                 = ozone->dimensions_sidebar_width
            + ozone->sidebar_offset
            + entry_padding;
      float entry_width             = width
            - ozone->dimensions_sidebar_width
            - ozone->sidebar_offset
            - entry_padding * 2
            - ozone->animations.thumbnail_bar_position;
      bool first_entry_found        = false;
      bool last_entry_found         = false;

      unsigned horizontal_list_size = (ozone->show_playlist_tabs)
            ? (unsigned)ozone->horizontal_list.size
            : 0;
      float category_height         = ozone->dimensions.sidebar_entry_height
            + ozone->dimensions.sidebar_entry_padding_vertical;
      bool first_category_found     = false;
      bool last_category_found      = false;

      /* Check whether pointer is operating on entries
       * or sidebar
       * > Note 1: Since touchscreens effectively 'lose their
       *   place' when a touch is released, we can only perform
       *   this this check if the pointer is currently
       *   pressed - i.e. we must preserve the values set the
       *   last time the screen was touched.
       *   With mouse input we have a permanent cursor, so this
       *   is not an issue
       * > Note 2: Windows seems to report negative pointer
       *   coordinates when the cursor goes off the left hand
       *   side of the screen/window, so checking whether
       *   pointer.x is less than the effective sidebar width
       *   generates a false positive when ozone->depth > 1.
       *   We therefore must also check whether the sidebar
       *   is currently being drawn */
      if (ozone->flags2 & OZONE_FLAG2_POINTER_IN_SIDEBAR)
         ozone->flags2 |=  OZONE_FLAG2_LAST_POINTER_IN_SIDEBAR;
      else
         ozone->flags2 &= ~OZONE_FLAG2_LAST_POINTER_IN_SIDEBAR;
      if (    (ozone->pointer.type == MENU_POINTER_MOUSE)
           || (ozone->pointer.flags & MENU_INP_PTR_FLG_PRESSED))
      {
         if ((ozone->flags & OZONE_FLAG_DRAW_SIDEBAR)
               && (ozone->pointer.x < ozone->dimensions_sidebar_width
                +  ozone->sidebar_offset))
            ozone->flags2 |=  OZONE_FLAG2_POINTER_IN_SIDEBAR;
         else
            ozone->flags2 &= ~OZONE_FLAG2_POINTER_IN_SIDEBAR;
      }

      pointer_in_sidebar      = ozone->flags2 &
         OZONE_FLAG2_POINTER_IN_SIDEBAR;
      last_pointer_in_sidebar = ozone->flags2 &
         OZONE_FLAG2_LAST_POINTER_IN_SIDEBAR;
      /* If pointer has switched from entries to sidebar
       * or vice versa, must reset pointer acceleration */
      if (     (     pointer_in_sidebar)
            != (last_pointer_in_sidebar))
      {
         menu_input->pointer.y_accel = 0.0f;
         ozone->pointer.y_accel      = 0.0f;
      }

      /* If pointer is a mouse, then automatically follow
       * mouse focus from entries to sidebar (and vice versa) */
      if (ozone->pointer.type == MENU_POINTER_MOUSE)
      {
         if (       (pointer_in_sidebar)
               && (!(last_pointer_in_sidebar))
               && (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)))
            ozone_go_to_sidebar(ozone, ozone_collapse_sidebar, animation_tag);
         else if (   (!pointer_in_sidebar)
                  && (last_pointer_in_sidebar)
                  && (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR))
            if (!(ozone->flags & OZONE_FLAG_EMPTY_PLAYLIST))
               ozone_leave_sidebar(ozone, ozone_collapse_sidebar, animation_tag,
                     settings->uints.menu_remember_selection);
      }
      else if (ozone->pointer.type == MENU_POINTER_TOUCHSCREEN)
      {
         if (       (pointer_in_sidebar)
               && (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)))
            ozone_go_to_sidebar(ozone, ozone_collapse_sidebar, animation_tag);
      }

      /* Update scrolling - must be done first, otherwise
       * cannot determine entry/category positions
       * > Entries */
      if (!(ozone->flags2 & OZONE_FLAG2_POINTER_IN_SIDEBAR))
      {
         float entry_bottom_boundary = height
               - ozone->dimensions.header_height
               - ozone->dimensions.spacer_1px
               - ozone->dimensions.footer_height
               - ozone->dimensions.entry_padding_vertical * 2;

         ozone->animations.scroll_y += ozone->pointer.y_accel;

         if (ozone->animations.scroll_y + ozone->entries_height < entry_bottom_boundary)
            ozone->animations.scroll_y = entry_bottom_boundary - ozone->entries_height;

         if (ozone->animations.scroll_y > 0.0f)
            ozone->animations.scroll_y = 0.0f;
      }
      /* > Sidebar
       * Only process sidebar input here if the
       * cursor is currently *in* the sidebar */
      else if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
      {
         float sidebar_bottom_boundary = height -
               (ozone->dimensions.header_height + ozone->dimensions.spacer_1px) -
               ozone->dimensions.footer_height -
               ozone->dimensions.sidebar_padding_vertical;
         float sidebar_height          = ozone_get_sidebar_height(ozone);

         ozone->animations.scroll_y_sidebar += ozone->pointer.y_accel;

         if (ozone->animations.scroll_y_sidebar + sidebar_height < sidebar_bottom_boundary)
            ozone->animations.scroll_y_sidebar = sidebar_bottom_boundary - sidebar_height;

         if (ozone->animations.scroll_y_sidebar > 0.0f)
            ozone->animations.scroll_y_sidebar = 0.0f;
      }

      /* Regardless of pointer location, have to process
       * all entries/categories in order to determine
       * the indices of the first and last entries/categories
       * displayed on screen
       * > Needed so we can determine proper cursor positions
       *   when mixing pointer + gamepad/keyboard input */

      /* >> Loop over all entries */
      ozone->first_onscreen_entry   = 0;
      ozone->last_onscreen_entry    = 0;
      if (entries_end > 0)
         ozone->last_onscreen_entry = entries_end - 1;

      for (i = 0; i < entries_end; i++)
      {
         float entry_y;
         ozone_node_t *node = (ozone_node_t*)selection_buf->list[i].userdata;

         /* Sanity check */
         if (!node)
            break;

         /* Get current entry y position */
         entry_y = ozone->dimensions.header_height
               + ozone->dimensions.spacer_1px
               + ozone->dimensions.entry_padding_vertical
               + ozone->animations.scroll_y
               + node->position_y;

         /* Check whether this is the first on screen entry */
         if (!first_entry_found)
         {
            if ((entry_y + node->height) > ozone->dimensions.header_height)
            {
               ozone->first_onscreen_entry = i;
               first_entry_found = true;
            }
         }
         /* Check whether this is the last on screen entry */
         else if (!last_entry_found)
         {
            if (entry_y > (height - ozone->dimensions.footer_height))
            {
               /* Current entry is off screen - get index
                * of previous entry */
               if (i > 0)
               {
                  ozone->last_onscreen_entry = i - 1;
                  last_entry_found = true;
               }
            }
         }

         /* Track pointer input, if required */
         if (     (!(ozone->flags2 & OZONE_FLAG2_POINTER_IN_SIDEBAR))
               && first_entry_found
               && !last_entry_found)
         {
            /* Check whether pointer is within the bounds
             * of the current entry */
            if (     (ozone->pointer.x > entry_x)
                  && (ozone->pointer.x < entry_x + entry_width)
                  && (ozone->pointer.y > entry_y)
                  && (ozone->pointer.y < entry_y + node->height))
            {
               /* Pointer selection is always updated */
               menu_input->ptr = (unsigned)i;

               /* If pointer is a mouse, then automatically
                * select entry under cursor */
               if (ozone->pointer.type == MENU_POINTER_MOUSE)
               {
                  /* Note the fudge factor - cannot auto select
                   * items while drag-scrolling the entry list,
                   * so have to wait until pointer acceleration
                   * drops below a 'sensible' level... */
                  if (     (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR))
                        && (i != ozone->selection)
                        && (ozone->pointer.y_accel < ozone->last_scale_factor)
                        && (ozone->pointer.y_accel > -ozone->last_scale_factor))
                  {
                     menu_st->selection_ptr = i;

                     /* If this is a playlist, must update thumbnails */
                     if (     (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
                           || (ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST))
                     {
                        ozone->flags &= ~OZONE_FLAG_SKIP_THUMBNAIL_RESET;
                        ozone_set_thumbnail_content(ozone, "");
                        ozone_update_thumbnail_image(ozone);
                     }
                     /* Also savestate thumbnails need updating */
                     else if (((ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU)
                           && ozone->depth >= 2)
                           || (ozone->flags & OZONE_FLAG_IS_STATE_SLOT))
                     {
                        ozone_update_savestate_thumbnail_path(ozone, (unsigned)i);
                        ozone_update_savestate_thumbnail_image(ozone);
                     }
                  }
               }

               /* If pointer is pressed and stationary, and
                * if pointer has been held for at least
                * MENU_INPUT_PRESS_TIME_SHORT ms, automatically
                * select current entry */
               if (       (ozone->pointer.flags & MENU_INP_PTR_FLG_PRESSED)
                     && (!(ozone->pointer.flags & MENU_INP_PTR_FLG_DRAGGED))
                     && (ozone->pointer.press_duration >= MENU_INPUT_PRESS_TIME_SHORT)
                     && (i != ozone->selection))
               {
                  menu_st->selection_ptr = i;

                  /* If we are currently in the sidebar, leave it */
                  if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
                  {
                     if (!(ozone->flags & OZONE_FLAG_EMPTY_PLAYLIST))
                        ozone_leave_sidebar(ozone, ozone_collapse_sidebar, animation_tag,
                              settings->uints.menu_remember_selection);
                  }
                  /* If this is a playlist, must update thumbnails */
                  else if (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
                  {
                     ozone_set_thumbnail_content(ozone, "");
                     ozone_update_thumbnail_image(ozone);
                  }
               }
            }
         }

         if (last_entry_found)
            break;
      }

      /* >> Loop over all categories */
      ozone->first_onscreen_category = 0;
      ozone->last_onscreen_category  = ozone->system_tab_end + horizontal_list_size;

      for (i = 0; i < ozone->system_tab_end + horizontal_list_size + 1; i++)
      {
         /* Get current category y position */
         float category_y = ozone->dimensions.header_height
               + ozone->dimensions.spacer_1px
               + ozone->dimensions.sidebar_padding_vertical
               + (category_height * i)
               + ((i > ozone->system_tab_end)
                     ? (ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px)
                     : 0)
               + ozone->animations.scroll_y_sidebar;

         /* Check whether this is the first on screen category */
         if (!first_category_found)
         {
            if ((category_y + category_height) > ozone->dimensions.header_height)
            {
               ozone->first_onscreen_category = i;
               first_category_found = true;
            }
         }
         /* Check whether this is the last on screen category */
         else if (!last_category_found)
         {
            if (category_y > (height - ozone->dimensions.footer_height))
            {
               /* Current category is off screen - get index
                * of previous category */
               if (i > 0)
               {
                  ozone->last_onscreen_category = i - 1;
                  last_category_found = true;
               }
            }
         }

         /* Track pointer input, if required */
         if (     (ozone->flags2 & OZONE_FLAG2_POINTER_IN_SIDEBAR)
               && (ozone->flags  & OZONE_FLAG_CURSOR_IN_SIDEBAR)
               && first_category_found
               && !last_category_found)
         {
            /* If pointer is within the bounds of the
             * current category, cache category index
             * (for use in next 'pointer up' event) */
            if (     (ozone->pointer.y > category_y)
                  && (ozone->pointer.y < category_y + category_height))
               ozone->pointer_categories_selection = i;
         }

         if (last_category_found)
            break;
      }
   }

   /* Handle any pending thumbnail load requests */
   if (      ozone->show_thumbnail_bar
         && (ozone->thumbnails.pending != OZONE_PENDING_THUMBNAIL_NONE))
   {
      size_t selection                         = menu_st->selection_ptr;
      playlist_t *playlist                     = playlist_get_cached();
      unsigned gfx_thumbnail_upscale_threshold = settings->uints.gfx_thumbnail_upscale_threshold;
      bool network_on_demand_thumbnails        = settings->bools.network_on_demand_thumbnails;

      /* Explore list needs cached selection index */
      if (ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST)
         selection = menu_st->thumbnail_path_data
            ? menu_st->thumbnail_path_data->playlist_index
            : 0;

      switch (ozone->thumbnails.pending)
      {
         case OZONE_PENDING_THUMBNAIL_BOTH:
            gfx_thumbnail_request_streams(
                  menu_st->thumbnail_path_data,
                  p_anim,
                  playlist, selection,
                  &ozone->thumbnails.right,
                  &ozone->thumbnails.left,
                  gfx_thumbnail_upscale_threshold,
                  network_on_demand_thumbnails);

            if (     (ozone->thumbnails.right.status != GFX_THUMBNAIL_STATUS_UNKNOWN)
                  && (ozone->thumbnails.left.status  != GFX_THUMBNAIL_STATUS_UNKNOWN))
               ozone->thumbnails.pending = OZONE_PENDING_THUMBNAIL_NONE;
            break;
         case OZONE_PENDING_THUMBNAIL_RIGHT:
            gfx_thumbnail_request_stream(
                  menu_st->thumbnail_path_data,
                  p_anim,
                  GFX_THUMBNAIL_RIGHT,
                  playlist, selection,
                  &ozone->thumbnails.right,
                  gfx_thumbnail_upscale_threshold,
                  network_on_demand_thumbnails);

            if (ozone->thumbnails.right.status != GFX_THUMBNAIL_STATUS_UNKNOWN)
               ozone->thumbnails.pending = OZONE_PENDING_THUMBNAIL_NONE;
            break;
         case OZONE_PENDING_THUMBNAIL_LEFT:
            gfx_thumbnail_request_stream(
                  menu_st->thumbnail_path_data,
                  p_anim,
                  GFX_THUMBNAIL_LEFT,
                  playlist, selection,
                  &ozone->thumbnails.left,
                  gfx_thumbnail_upscale_threshold,
                  network_on_demand_thumbnails);

            if (ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_UNKNOWN)
               ozone->thumbnails.pending = OZONE_PENDING_THUMBNAIL_NONE;
            break;
         default:
            break;
      }

      if (ozone->thumbnails.left.status  != GFX_THUMBNAIL_STATUS_UNKNOWN)
         ozone->thumbnails_left_status_prev  = ozone->thumbnails.left.status;
      if (ozone->thumbnails.right.status != GFX_THUMBNAIL_STATUS_UNKNOWN)
         ozone->thumbnails_right_status_prev = ozone->thumbnails.right.status;
   }

   i = menu_st->entries.begin;

   if (i >= entries_end)
      menu_st->entries.begin = 0;

   GFX_ANIMATION_CLEAR_ACTIVE(p_anim);
}

static void ozone_draw_header(
      ozone_handle_t *ozone,
      gfx_display_t *p_disp,
      gfx_animation_t *p_anim,
      settings_t *settings,
      void *userdata,
      unsigned video_width,
      unsigned video_height,
      bool battery_level_enable,
      bool timedate_enable,
      math_matrix_4x4 *mymat)
{
   char title[NAME_MAX_LENGTH];
   gfx_animation_ctx_ticker_t ticker;
   gfx_animation_ctx_ticker_smooth_t ticker_smooth;
   unsigned ticker_x_offset                 = 0;
   unsigned timedate_offset                 = 0;
   bool use_smooth_ticker                   = settings->bools.menu_ticker_smooth;
   float *col                               = ozone->theme->entries_icon;
   float scale_factor                       = ozone->last_scale_factor;
   float header_margin                      = 40 * scale_factor;
   float header_margin_min                  = ozone->dimensions.sidebar_entry_icon_padding;
   unsigned logo_icon_size                  = 60 * scale_factor;
   unsigned status_icon_size                = 80 * scale_factor;
   unsigned status_row_size                 = 160 * scale_factor;
   unsigned separator_margin                = 30 * scale_factor;
   unsigned separator                       = settings->uints.menu_ozone_header_separator;
   enum gfx_animation_ticker_type
         menu_ticker_type                   = (enum gfx_animation_ticker_type)settings->uints.menu_ticker_type;
   gfx_display_ctx_driver_t *dispctx        = p_disp->dispctx;

   header_margin *= ozone->last_padding_factor;
   if (header_margin < header_margin_min)
      header_margin = header_margin_min;

   gfx_display_set_alpha(col, ozone->animations.alpha);

   /* Initial ticker configuration */
   if (use_smooth_ticker)
   {
      ticker_smooth.idx           = p_anim->ticker_pixel_idx;
      ticker_smooth.font_scale    = 1.0f;
      ticker_smooth.type_enum     = menu_ticker_type;
      ticker_smooth.spacer        = (ozone->font_unicode) ? OZONE_TICKER_SPACER : NULL;
      ticker_smooth.x_offset      = &ticker_x_offset;
      ticker_smooth.dst_str_width = NULL;
   }
   else
   {
      ticker.idx                  = p_anim->ticker_idx;
      ticker.type_enum            = menu_ticker_type;
      ticker.spacer               = (ozone->font_unicode) ? OZONE_TICKER_SPACER : NULL;
   }

   /* Separator */
   if (separator)
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            ((separator == OZONE_HEADER_SEPARATOR_NORMAL) ? separator_margin : 0),
            ozone->dimensions.header_height,
            video_width - ((separator == OZONE_HEADER_SEPARATOR_NORMAL) ? (separator_margin * 2) : 0),
            ozone->dimensions.spacer_1px,
            video_width,
            video_height,
            ozone->theme->header_footer_separator,
            NULL);

   /* Icon */
   if (dispctx)
   {
      if (dispctx->blend_begin)
         dispctx->blend_begin(userdata);
      if (dispctx->draw)
      {
         ozone_draw_icon(
               p_disp,
               userdata,
               video_width,
               video_height,
               logo_icon_size,
               logo_icon_size,
               ozone->textures[OZONE_TEXTURE_RETROARCH],
               header_margin,
               (ozone->dimensions.header_height - logo_icon_size) / 2,
               video_width,
               video_height,
               0.0f,
               1.0f,
               col,
               mymat);
      }
      if (dispctx->blend_end)
         dispctx->blend_end(userdata);
   }

   /* Battery */
   if (battery_level_enable)
   {
      gfx_display_ctx_powerstate_t powerstate;
      char msg[12];

      msg[0] = '\0';
      menu_display_powerstate(&powerstate, msg, sizeof(msg));

      if (powerstate.battery_enabled)
      {
         float icon_width = 35 * scale_factor;
         float text_offset = ozone->dimensions.entry_spacing * 2.5f;
         timedate_offset  = header_margin + icon_width + text_offset + ozone->dimensions.entry_spacing * 3;
         status_row_size += timedate_offset;

         gfx_display_draw_text(
               ozone->fonts.time.font,
               msg,
               video_width - (header_margin + icon_width - text_offset),
               ozone->dimensions.header_height / 2 + ozone->fonts.time.line_centre_offset,
               video_width,
               video_height,
               ozone->theme->text_rgba,
               TEXT_ALIGN_RIGHT,
               1.0f,
               false,
               1.0f,
               false);

         if (dispctx)
         {
            if (dispctx->blend_begin)
               dispctx->blend_begin(userdata);
            if (dispctx->draw)
               ozone_draw_icon(
                     p_disp,
                     userdata,
                     video_width,
                     video_height,
                     status_icon_size,
                     status_icon_size,
                     ozone->icons_textures[powerstate.charging
                           ? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_CHARGING
                           : (powerstate.percent > 80) ? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_FULL
                           : (powerstate.percent > 60) ? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_80
                           : (powerstate.percent > 40) ? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_60
                           : (powerstate.percent > 20) ? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_40
                           : OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_20],
                     video_width - (header_margin + status_icon_size - icon_width),
                     (6 * scale_factor),
                     video_width,
                     video_height,
                     0.0f,
                     1.0f,
                     col,
                     mymat);
            if (dispctx->blend_end)
               dispctx->blend_end(userdata);
         }
      }
   }

   /* Timedate */
   if (timedate_enable)
   {
      gfx_display_ctx_datetime_t datetime;
      char timedate[256];
      float icon_width  = 35 * scale_factor;
      float text_offset = ozone->dimensions.entry_spacing * 3.0f;

      if (!timedate_offset)
         timedate_offset = header_margin;

      datetime.time_mode      = settings->uints.menu_timedate_style;
      datetime.date_separator = settings->uints.menu_timedate_date_separator;

      menu_display_timedate(&datetime, timedate, sizeof(timedate));

      gfx_display_draw_text(
            ozone->fonts.time.font,
            timedate,
            video_width - (timedate_offset + status_icon_size - icon_width - text_offset),
            ozone->dimensions.header_height / 2 + ozone->fonts.time.line_centre_offset,
            video_width,
            video_height,
            ozone->theme->text_rgba,
            TEXT_ALIGN_RIGHT,
            1.0f,
            false,
            1.0f,
            false);

      if (dispctx)
      {
         if (dispctx->blend_begin)
            dispctx->blend_begin(userdata);
         if (dispctx->draw)
            ozone_draw_icon(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  status_icon_size,
                  status_icon_size,
                  ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOCK],
                  video_width - (timedate_offset + status_icon_size - icon_width),
                  (6 * scale_factor),
                  video_width,
                  video_height,
                  0.0f,
                  1.0f,
                  col,
                  mymat);
         if (dispctx->blend_end)
            dispctx->blend_end(userdata);
      }

      status_row_size += 240 * scale_factor;
   }

   /* Title */
   if (use_smooth_ticker)
   {
      ticker_smooth.font        = ozone->fonts.title.font;
      ticker_smooth.selected    = true;
      ticker_smooth.field_width = video_width - status_row_size;
      ticker_smooth.src_str     = (ozone->flags2 & OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS)
            ? ozone->fullscreen_thumbnail_label
            : ozone->title;
      ticker_smooth.dst_str     = title;
      ticker_smooth.dst_str_len = sizeof(title);

      gfx_animation_ticker_smooth(&ticker_smooth);
   }
   else
   {
      ticker.s        = title;
      ticker.len      = video_width - status_row_size / ozone->fonts.title.glyph_width;
      ticker.str      = (ozone->flags2 & OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS)
            ? ozone->fullscreen_thumbnail_label
            : ozone->title;
      ticker.selected = true;

      gfx_animation_ticker(&ticker);
   }

   gfx_display_draw_text(
         ozone->fonts.title.font,
         title,
         ticker_x_offset + header_margin + logo_icon_size + ozone->dimensions.entry_icon_padding,
         ozone->dimensions.header_height / 2 + ozone->fonts.title.line_centre_offset,
         video_width,
         video_height,
         ozone->theme->text_rgba,
         TEXT_ALIGN_LEFT,
         1.0f,
         false,
         1.0f,
         false);
}

static void ozone_draw_footer(
      ozone_handle_t *ozone,
      struct menu_state *menu_st,
      gfx_display_t *p_disp,
      gfx_animation_t *p_anim,
      void *userdata,
      unsigned video_width,
      unsigned video_height,
      settings_t *settings,
      math_matrix_4x4 *mymat)
{
   gfx_display_ctx_driver_t *dispctx      = p_disp->dispctx;
   bool menu_core_enable                  = settings->bools.menu_core_enable;
   bool input_menu_swap_ok_cancel_buttons = settings->bools.input_menu_swap_ok_cancel_buttons;
   size_t selection                       = ozone->selection;
   float *col                             = ozone->theme_dynamic.entries_icon;
   float scale_factor                     = ozone->last_scale_factor;
   float footer_margin                    = 40 * scale_factor;
   float footer_margin_min                = ozone->dimensions.sidebar_entry_icon_padding;
   float footer_text_y                    = (float)video_height
         - (ozone->dimensions.footer_height / 2.0f)
         + ozone->fonts.footer.line_centre_offset;
   float icon_size                        = 35 * scale_factor;
   float icon_padding                     = 8 * scale_factor;
   float icon_padding_small               = icon_padding / 5;
   float icon_spacer                      = icon_size + (2.0f * icon_padding);
   float icon_y                           = (float)video_height
         - (ozone->dimensions.footer_height / 2.0f)
         - (icon_size / 2.0f);
   unsigned separator_margin              = 30 * scale_factor;
   unsigned separator                     = settings->uints.menu_ozone_header_separator;

   footer_margin *= ozone->last_padding_factor;
   if (footer_margin < footer_margin_min)
      footer_margin = footer_margin_min;

   /* Button enable states
    * > Note: Only show 'metadata_override' if
    *   'fullscreen_thumbnails' is shown. This condition
    *   should be guaranteed anyway, but enforce it
    *   here to prevent 'gaps' in the button list in
    *   the event of unknown errors */

   ozone->footer_labels.fullscreen_thumbnails.show =
         ozone_fullscreen_thumbnails_available(ozone, menu_st);

   ozone->footer_labels.metadata_override.show     =
            ozone->footer_labels.fullscreen_thumbnails.show
         && ozone_metadata_override_available(ozone,
                  settings->uints.menu_left_thumbnails);

   ozone->footer_labels.random_select.show         =
            (ozone->flags  & OZONE_FLAG_IS_PLAYLIST)
         || (ozone->flags  & OZONE_FLAG_IS_EXPLORE_LIST)
         || (ozone->flags  & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         || (ozone->flags2 & OZONE_FLAG2_IS_PLAYLISTS_TAB);

   ozone->footer_labels.cycle_thumbnails.show      =
            ozone->footer_labels.fullscreen_thumbnails.show
         && ((ozone->flags2 & OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS) || (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS))
         && !(ozone->flags & OZONE_FLAG_IS_FILE_LIST)
         && string_is_empty(ozone->savestate_thumbnail_file_path);

   ozone->footer_labels.clear_setting.show         =
            !ozone->footer_labels.cycle_thumbnails.show
         && ozone_clear_available(ozone, selection);

   ozone->footer_labels.scan.show                  =
            !ozone->footer_labels.cycle_thumbnails.show
         && !ozone->footer_labels.clear_setting.show
         && ozone_scan_available(ozone, selection);

   ozone->footer_labels.reset_to_default.show      =
            !ozone->footer_labels.fullscreen_thumbnails.show
         && ozone_is_current_entry_settings(selection);

   ozone->footer_labels.help.show                  =
            !ozone->footer_labels.metadata_override.show
         && ozone_help_available(ozone, selection, settings->bools.menu_show_sublabels);

   ozone->footer_labels.manage.show                =
         ozone_manage_available(ozone, selection);

   ozone->footer_labels.search.show                =
            !settings->bools.menu_disable_search_button
         && !((ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU)
         && !menu_is_running_quick_menu())
         &&  !(ozone->flags2 & OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS)
         &&  !(ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS);

   ozone->footer_labels.resume.show                =
         !retroarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL);

   /* Determine x origin positions of each
    * button
    * > From right to left, these are ordered:
    *   - resume
    *   - ok
    *   - back
    *   - (X) search
    *   - (X) cycle thumbnails (fullscreen thumbnail + quick menu only
    *   - (Y) random selector (playlists only)
    *   - (Y) clear settings (keybinds only)
    *   - (Y) scan entry (certain file types only)
    *   - (Start) toggle fullscreen thumbs (playlists only)
    *   - (Start) reset to default (settings only)
    *   - (Start) manage playlist (sidebar only)
    *   - (Select) toggle metadata (playlists only)
    *   - (Select) help (non-playlist only)*/

   ozone->footer_labels.resume.x                = (ozone->footer_labels.resume.show)
         ? (float)video_width - footer_margin - ozone->footer_labels.resume.width - icon_size
         : (float)video_width - footer_margin;

   if (input_menu_swap_ok_cancel_buttons)
   {
      ozone->footer_labels.back.x               = ozone->footer_labels.resume.x
                                                - ozone->footer_labels.back.width
                                                - icon_spacer;
      ozone->footer_labels.ok.x                 = ozone->footer_labels.back.x
                                                - ozone->footer_labels.ok.width
                                                - icon_spacer;
   }
   else
   {
      ozone->footer_labels.ok.x                 = ozone->footer_labels.resume.x
                                                - ozone->footer_labels.ok.width
                                                - icon_spacer;
      ozone->footer_labels.back.x               = ozone->footer_labels.ok.x
                                                - ozone->footer_labels.back.width
                                                - icon_spacer;
   }

   ozone->footer_labels.search.x                = (ozone->footer_labels.search.show)
         ? ((input_menu_swap_ok_cancel_buttons)
               ? ozone->footer_labels.ok.x : ozone->footer_labels.back.x)
         - ozone->footer_labels.search.width
         - icon_spacer
         : ((input_menu_swap_ok_cancel_buttons)
               ? ozone->footer_labels.ok.x : ozone->footer_labels.back.x);

   ozone->footer_labels.cycle_thumbnails.x      = (ozone->footer_labels.cycle_thumbnails.show)
         ? ozone->footer_labels.search.x - ozone->footer_labels.cycle_thumbnails.width - icon_spacer
         : ozone->footer_labels.search.x;

   ozone->footer_labels.random_select.x         = (ozone->footer_labels.random_select.show)
         ? ozone->footer_labels.cycle_thumbnails.x - ozone->footer_labels.random_select.width - icon_spacer
         : ozone->footer_labels.cycle_thumbnails.x;

   ozone->footer_labels.clear_setting.x         = (ozone->footer_labels.clear_setting.show)
         ? ozone->footer_labels.random_select.x - ozone->footer_labels.clear_setting.width - icon_spacer
         : ozone->footer_labels.random_select.x;

   ozone->footer_labels.scan.x                  = (ozone->footer_labels.scan.show)
         ? ozone->footer_labels.clear_setting.x - ozone->footer_labels.scan.width - icon_spacer
         : ozone->footer_labels.clear_setting.x;

   ozone->footer_labels.reset_to_default.x      = (ozone->footer_labels.reset_to_default.show)
         ? ozone->footer_labels.scan.x - ozone->footer_labels.reset_to_default.width - icon_spacer
         : ozone->footer_labels.scan.x;

   ozone->footer_labels.help.x                  = (ozone->footer_labels.help.show)
         ? ozone->footer_labels.reset_to_default.x - ozone->footer_labels.help.width - icon_spacer
         : ozone->footer_labels.reset_to_default.x;

   ozone->footer_labels.fullscreen_thumbnails.x = (ozone->footer_labels.fullscreen_thumbnails.show)
         ? ozone->footer_labels.help.x - ozone->footer_labels.fullscreen_thumbnails.width - icon_spacer
         : ozone->footer_labels.help.x;

   ozone->footer_labels.manage.x                = (ozone->footer_labels.manage.show)
         ? ozone->footer_labels.fullscreen_thumbnails.x - ozone->footer_labels.manage.width - icon_spacer
         : ozone->footer_labels.fullscreen_thumbnails.x;

   ozone->footer_labels.metadata_override.x     = ozone->footer_labels.manage.x
         - ozone->footer_labels.metadata_override.width - icon_spacer;

   /* Separator */
   if (separator)
      gfx_display_draw_quad(
            p_disp,
            userdata,
            video_width,
            video_height,
            ((separator == OZONE_HEADER_SEPARATOR_NORMAL) ? separator_margin : 0),
            video_height - ozone->dimensions.footer_height,
            video_width - ((separator == OZONE_HEADER_SEPARATOR_NORMAL) ? (separator_margin * 2) : 0),
            ozone->dimensions.spacer_1px,
            video_width,
            video_height,
            ozone->theme->header_footer_separator,
            NULL);

   /* Buttons */

   /* Draw icons */
   if (dispctx)
   {
      if (dispctx->blend_begin)
         dispctx->blend_begin(userdata);
      gfx_display_set_alpha(ozone->theme_dynamic.entries_icon, 1.0f);

      if (dispctx->draw)
      {
         /* > Resume */
         if (ozone->footer_labels.resume.show)
            ozone_draw_icon(
               p_disp,
               userdata,
               video_width,
               video_height,
               icon_size,
               icon_size,
               ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RESUME],
               ozone->footer_labels.resume.x,
               icon_y,
               video_width,
               video_height,
               0.0f,
               1.0f,
               col,
               mymat);

         /* > Ok */
         ozone_draw_icon(
               p_disp,
               userdata,
               video_width,
               video_height,
               icon_size,
               icon_size,
               input_menu_swap_ok_cancel_buttons
                     ? ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_D]
                     : ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_R],
               ozone->footer_labels.ok.x,
               icon_y,
               video_width,
               video_height,
               0.0f,
               1.0f,
               col,
               mymat);

         /* > Back */
         ozone_draw_icon(
               p_disp,
               userdata,
               video_width,
               video_height,
               icon_size,
               icon_size,
               input_menu_swap_ok_cancel_buttons
                     ? ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_R]
                     : ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_D],
               ozone->footer_labels.back.x,
               icon_y,
               video_width,
               video_height,
               0.0f,
               1.0f,
               col,
               mymat);

         /* > Search */
         if (ozone->footer_labels.search.show)
            ozone_draw_icon(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  icon_size,
                  icon_size,
                  ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_U],
                  ozone->footer_labels.search.x,
                  icon_y,
                  video_width,
                  video_height,
                  0.0f,
                  1.0f,
                  col,
                  mymat);

         /* > Thumbnail cycle */
         if (ozone->footer_labels.cycle_thumbnails.show)
            ozone_draw_icon(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  icon_size,
                  icon_size,
                  ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_U],
                  ozone->footer_labels.cycle_thumbnails.x,
                  icon_y,
                  video_width,
                  video_height,
                  0.0f,
                  1.0f,
                  col,
                  mymat);

         /* > Random select */
         if (ozone->footer_labels.random_select.show)
            ozone_draw_icon(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  icon_size,
                  icon_size,
                  ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_L],
                  ozone->footer_labels.random_select.x,
                  icon_y,
                  video_width,
                  video_height,
                  0.0f,
                  1.0f,
                  col,
                  mymat);

         /* > Fullscreen thumbnais */
         if (ozone->footer_labels.fullscreen_thumbnails.show)
            ozone_draw_icon(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  icon_size,
                  icon_size,
                  (!string_is_empty(ozone->savestate_thumbnail_file_path))
                        ? ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_L]
                        : ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_START],
                  ozone->footer_labels.fullscreen_thumbnails.x,
                  icon_y,
                  video_width,
                  video_height,
                  0.0f,
                  1.0f,
                  col,
                  mymat);

         /* > Metadata toggle */
         if (ozone->footer_labels.metadata_override.show)
            ozone_draw_icon(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  icon_size,
                  icon_size,
                  ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SELECT],
                  ozone->footer_labels.metadata_override.x,
                  icon_y,
                  video_width,
                  video_height,
                  0.0f,
                  1.0f,
                  col,
                  mymat);

         /* > Reset to default */
         if (ozone->footer_labels.reset_to_default.show)
            ozone_draw_icon(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  icon_size,
                  icon_size,
                  ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_START],
                  ozone->footer_labels.reset_to_default.x,
                  icon_y,
                  video_width,
                  video_height,
                  0.0f,
                  1.0f,
                  col,
                  mymat);

         /* > Help */
         if (ozone->footer_labels.help.show)
            ozone_draw_icon(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  icon_size,
                  icon_size,
                  ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SELECT],
                  ozone->footer_labels.help.x,
                  icon_y,
                  video_width,
                  video_height,
                  0.0f,
                  1.0f,
                  col,
                  mymat);

         /* > Clear setting */
         if (ozone->footer_labels.clear_setting.show)
            ozone_draw_icon(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  icon_size,
                  icon_size,
                  ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_L],
                  ozone->footer_labels.clear_setting.x,
                  icon_y,
                  video_width,
                  video_height,
                  0.0f,
                  1.0f,
                  col,
                  mymat);

         /* > Scan entry */
         if (ozone->footer_labels.scan.show)
            ozone_draw_icon(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  icon_size,
                  icon_size,
                  ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_L],
                  ozone->footer_labels.scan.x,
                  icon_y,
                  video_width,
                  video_height,
                  0.0f,
                  1.0f,
                  col,
                  mymat);

         /* > Manage */
         if (ozone->footer_labels.manage.show)
            ozone_draw_icon(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  icon_size,
                  icon_size,
                  ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_START],
                  ozone->footer_labels.manage.x,
                  icon_y,
                  video_width,
                  video_height,
                  0.0f,
                  1.0f,
                  col,
                  mymat);
      }

      if (dispctx->blend_end)
         dispctx->blend_end(userdata);
   }

   /* Draw labels */

   /* > Resume */
   if (ozone->footer_labels.resume.show)
      gfx_display_draw_text(
            ozone->fonts.footer.font,
            ozone->footer_labels.resume.str,
            ozone->footer_labels.resume.x + icon_size + icon_padding_small,
            footer_text_y,
            video_width,
            video_height,
            ozone->theme->text_rgba,
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

   /* > Ok */
   gfx_display_draw_text(
         ozone->fonts.footer.font,
         ozone->footer_labels.ok.str,
         ozone->footer_labels.ok.x + icon_size + icon_padding_small,
         footer_text_y,
         video_width,
         video_height,
         ozone->theme->text_rgba,
         TEXT_ALIGN_LEFT,
         1.0f,
         false,
         1.0f,
         false);

   /* > Back */
   gfx_display_draw_text(
         ozone->fonts.footer.font,
         ozone->footer_labels.back.str,
         ozone->footer_labels.back.x + icon_size + icon_padding_small,
         footer_text_y,
         video_width,
         video_height,
         ozone->theme->text_rgba,
         TEXT_ALIGN_LEFT,
         1.0f,
         false,
         1.0f,
         false);

   /* > Search */
   if (ozone->footer_labels.search.show)
      gfx_display_draw_text(
            ozone->fonts.footer.font,
            ozone->footer_labels.search.str,
            ozone->footer_labels.search.x + icon_size + icon_padding_small,
            footer_text_y,
            video_width,
            video_height,
            ozone->theme->text_rgba,
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

   /* > Thumbnail cycle */
   if (ozone->footer_labels.cycle_thumbnails.show)
      gfx_display_draw_text(
            ozone->fonts.footer.font,
            ozone->footer_labels.cycle_thumbnails.str,
            ozone->footer_labels.cycle_thumbnails.x + icon_size + icon_padding_small,
            footer_text_y,
            video_width,
            video_height,
            ozone->theme->text_rgba,
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

   /* > Random select */
   if (ozone->footer_labels.random_select.show)
      gfx_display_draw_text(
            ozone->fonts.footer.font,
            ozone->footer_labels.random_select.str,
            ozone->footer_labels.random_select.x + icon_size + icon_padding_small,
            footer_text_y,
            video_width,
            video_height,
            ozone->theme->text_rgba,
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

   /* > Fullscreen thumbnails */
   if (ozone->footer_labels.fullscreen_thumbnails.show)
      gfx_display_draw_text(
            ozone->fonts.footer.font,
            ozone->footer_labels.fullscreen_thumbnails.str,
            ozone->footer_labels.fullscreen_thumbnails.x + icon_size + icon_padding_small,
            footer_text_y,
            video_width,
            video_height,
            ozone->theme->text_rgba,
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

   /* > Metadata toggle */
   if (ozone->footer_labels.metadata_override.show)
      gfx_display_draw_text(
            ozone->fonts.footer.font,
            ozone->footer_labels.metadata_override.str,
            ozone->footer_labels.metadata_override.x + icon_size + icon_padding_small,
            footer_text_y,
            video_width,
            video_height,
            ozone->theme->text_rgba,
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

   /* > Reset to default */
   if (ozone->footer_labels.reset_to_default.show)
      gfx_display_draw_text(
            ozone->fonts.footer.font,
            ozone->footer_labels.reset_to_default.str,
            ozone->footer_labels.reset_to_default.x + icon_size + icon_padding_small,
            footer_text_y,
            video_width,
            video_height,
            ozone->theme->text_rgba,
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

   /* > Help */
   if (ozone->footer_labels.help.show)
      gfx_display_draw_text(
            ozone->fonts.footer.font,
            ozone->footer_labels.help.str,
            ozone->footer_labels.help.x + icon_size + icon_padding_small,
            footer_text_y,
            video_width,
            video_height,
            ozone->theme->text_rgba,
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

   /* > Clear settings */
   if (ozone->footer_labels.clear_setting.show)
      gfx_display_draw_text(
            ozone->fonts.footer.font,
            ozone->footer_labels.clear_setting.str,
            ozone->footer_labels.clear_setting.x + icon_size + icon_padding_small,
            footer_text_y,
            video_width,
            video_height,
            ozone->theme->text_rgba,
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

   /* > Scan entry */
   if (ozone->footer_labels.scan.show)
      gfx_display_draw_text(
            ozone->fonts.footer.font,
            ozone->footer_labels.scan.str,
            ozone->footer_labels.scan.x + icon_size + icon_padding_small,
            footer_text_y,
            video_width,
            video_height,
            ozone->theme->text_rgba,
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

   /* > Manage */
   if (ozone->footer_labels.manage.show)
      gfx_display_draw_text(
            ozone->fonts.footer.font,
            ozone->footer_labels.manage.str,
            ozone->footer_labels.manage.x + icon_size + icon_padding_small,
            footer_text_y,
            video_width,
            video_height,
            ozone->theme->text_rgba,
            TEXT_ALIGN_LEFT,
            1.0f,
            false,
            1.0f,
            false);

   /* Core title or Switch icon */
   if (menu_core_enable)
   {
      gfx_animation_ctx_ticker_t ticker;
      gfx_animation_ctx_ticker_smooth_t ticker_smooth;
      char core_title[NAME_MAX_LENGTH];
      char core_title_buf[NAME_MAX_LENGTH];
      int usable_width;
      unsigned ticker_x_offset                        = 0;
      bool use_smooth_ticker                          =
            settings->bools.menu_ticker_smooth;
      enum gfx_animation_ticker_type menu_ticker_type =
            (enum gfx_animation_ticker_type)settings->uints.menu_ticker_type;

      core_title[0]     = '\0';
      core_title_buf[0] = '\0';

      /* Determine available width for core
       * title string */
      usable_width = ozone->footer_labels.metadata_override.show
            ? ozone->footer_labels.metadata_override.x
            : ozone->footer_labels.fullscreen_thumbnails.show
            ? ozone->footer_labels.fullscreen_thumbnails.x
            : ozone->footer_labels.search.x;
      usable_width -= footer_margin + (icon_padding * 2);

      if (usable_width > 0)
      {
         /* Get core title */
         menu_entries_get_core_title(core_title, sizeof(core_title));

         /* Configure and run ticker */
         if (use_smooth_ticker)
         {
            ticker_smooth.idx           = p_anim->ticker_pixel_idx;
            ticker_smooth.font_scale    = 1.0f;
            ticker_smooth.type_enum     = menu_ticker_type;
            ticker_smooth.spacer        = (ozone->font_unicode) ? OZONE_TICKER_SPACER : NULL;
            ticker_smooth.x_offset      = &ticker_x_offset;
            ticker_smooth.dst_str_width = NULL;

            ticker_smooth.font          = ozone->fonts.footer.font;
            ticker_smooth.selected      = true;
            ticker_smooth.field_width   = usable_width;
            ticker_smooth.src_str       = core_title;
            ticker_smooth.dst_str       = core_title_buf;
            ticker_smooth.dst_str_len   = sizeof(core_title_buf);

            gfx_animation_ticker_smooth(&ticker_smooth);
         }
         else
         {
            ticker.idx                  = p_anim->ticker_idx;
            ticker.type_enum            = menu_ticker_type;
            ticker.spacer               = (ozone->font_unicode) ? OZONE_TICKER_SPACER : NULL;

            ticker.s                    = core_title_buf;
            ticker.len                  = usable_width / ozone->fonts.footer.glyph_width;
            ticker.str                  = core_title;
            ticker.selected             = true;

            gfx_animation_ticker(&ticker);
         }

         /* Draw text */
         gfx_display_draw_text(
               ozone->fonts.footer.font,
               core_title_buf,
               ticker_x_offset + footer_margin,
               footer_text_y,
               video_width,
               video_height,
               ozone->theme->text_rgba,
               TEXT_ALIGN_LEFT,
               1.0f,
               false,
               1.0f,
               false);
      }
   }
#ifdef HAVE_LIBNX
   else if (ozone->theme->name)
   {
      if (dispctx)
      {
         if (dispctx->blend_begin)
            dispctx->blend_begin(userdata);
         if (dispctx->draw)
            ozone_draw_icon(
                  p_disp,
                  userdata,
                  video_width,
                  video_height,
                  69 * scale_factor,
                  30 * scale_factor,
                  ozone->theme->textures[OZONE_THEME_TEXTURE_SWITCH],
                  footer_margin,
                  video_height - ozone->dimensions.footer_height / 2 - 15 * scale_factor,
                  video_width,
                  video_height,
                  0.0f,
                  1.0f,
                  ozone->pure_white,
                  mymat);
         if (dispctx->blend_end)
            dispctx->blend_end(userdata);
      }
   }
#endif
}

static void ozone_selection_changed(ozone_handle_t *ozone, bool allow_animation)
{
   struct menu_state *menu_st = menu_state_get_ptr();
   menu_list_t *menu_list     = menu_st->entries.list;
   file_list_t *selection_buf = MENU_LIST_GET_SELECTION(menu_list, 0);
   size_t new_selection       = menu_st->selection_ptr;
   ozone_node_t *node         = (ozone_node_t*)selection_buf->list[new_selection].userdata;

   if (!node)
      return;

   if (ozone->selection != new_selection)
   {
      uintptr_t tag                = (uintptr_t)selection_buf;
      size_t selection             = new_selection;


      ozone->selection_old         = ozone->selection;
      ozone->selection             = new_selection;

      if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
         ozone->flags             |=  OZONE_FLAG_CURSOR_IN_SIDEBAR_OLD;
      else
         ozone->flags             &= ~OZONE_FLAG_CURSOR_IN_SIDEBAR_OLD;

      gfx_animation_kill_by_tag(&tag);
      ozone_update_scroll(ozone, allow_animation, node);

      /* Update thumbnail */
      if (   gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT)
          || gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
      {
         bool update_thumbnails = false;

         /* Playlist updates */
         if (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
         {
            ozone_set_thumbnail_content(ozone, "");
            update_thumbnails = true;
            ozone->flags &= ~OZONE_FLAG_SKIP_THUMBNAIL_RESET;
         }
         /* Database + Explore list updates */
         else if (  ((ozone->flags & OZONE_FLAG_IS_DB_MANAGER_LIST) && (ozone->depth == 4))
               ||    (ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST))
         {
            ozone_set_thumbnail_content(ozone, "");
            update_thumbnails = true;
            ozone->flags &= ~OZONE_FLAG_SKIP_THUMBNAIL_RESET;
         }
         /* Filebrowser image updates */
         else if (ozone->flags & OZONE_FLAG_IS_FILE_LIST)
         {
            menu_entry_t entry;
            MENU_ENTRY_INITIALIZE(entry);
            menu_entry_get(&entry, 0, selection, NULL, true);

            if (   (entry.type == FILE_TYPE_IMAGEVIEWER)
                || (entry.type == FILE_TYPE_IMAGE))
            {
               ozone_set_thumbnail_content(ozone, "imageviewer");
               update_thumbnails = true;
               ozone->flags |= OZONE_FLAG_WANT_THUMBNAIL_BAR
                             | OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE;
            }
            else
            {
               /* If this is a file list and current
                * entry is not an image, have to 'reset'
                * content + right/left thumbnails
                * (otherwise last loaded thumbnail will
                * persist, and be shown on the wrong entry) */
               gfx_thumbnail_set_content(menu_st->thumbnail_path_data, NULL);
               ozone_unload_thumbnail_textures(ozone);
               update_thumbnails = true;
               ozone->flags &= ~(OZONE_FLAG_WANT_THUMBNAIL_BAR
                               | OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE);
            }
         }

         if (update_thumbnails)
            ozone_update_thumbnail_image(ozone);
      }

      ozone_update_savestate_thumbnail_path(ozone, (unsigned)ozone->selection);
      ozone_update_savestate_thumbnail_image(ozone);
   }
}

static void ozone_navigation_clear(void *data, bool pending_push)
{
   ozone_handle_t *ozone = (ozone_handle_t*)data;
   if (!pending_push)
      ozone_selection_changed(ozone, true);
}

static void ozone_navigation_pointer_changed(void *data)
{
   ozone_handle_t *ozone = (ozone_handle_t*)data;
   ozone_selection_changed(ozone, true);
}

static void ozone_navigation_set(void *data, bool scroll)
{
   ozone_handle_t *ozone = (ozone_handle_t*)data;
   ozone_selection_changed(ozone, true);
}

static void ozone_navigation_alphabet(void *data, size_t *unused)
{
   ozone_handle_t *ozone = (ozone_handle_t*)data;
   ozone_selection_changed(ozone, true);
}

static void ozone_messagebox_fadeout_cb(void *userdata)
{
   ozone_handle_t *ozone = (ozone_handle_t*)userdata;

   free(ozone->pending_message);
   ozone->pending_message        = NULL;
   ozone->flags                 &= ~OZONE_FLAG_SHOULD_DRAW_MSGBOX;
}

static void ozone_frame(void *data, video_frame_info_t *video_info)
{
   math_matrix_4x4 mymat;
   gfx_animation_ctx_entry_t entry;
   bool ozone_last_use_preferred_system_color_theme;
   ozone_handle_t* ozone                  = (ozone_handle_t*)data;
   settings_t  *settings                  = config_get_ptr();
   unsigned color_theme                   = settings->uints.menu_ozone_color_theme;
   bool use_preferred_system_color_theme  = settings->bools.menu_use_preferred_system_color_theme;
   uintptr_t messagebox_tag               = (uintptr_t)ozone->pending_message;
   bool draw_osk                          = menu_input_dialog_get_display_kb();
   static bool draw_osk_old               = false;
   float *background_color                = NULL;
   void *userdata                         = video_info->userdata;
   unsigned video_width                   = video_info->width;
   unsigned video_height                  = video_info->height;
   float menu_framebuffer_opacity         = video_info->menu_framebuffer_opacity;
   bool libretro_running                  = video_info->libretro_running;
   bool video_fullscreen                  = video_info->fullscreen;
   bool mouse_grabbed                     = video_info->input_driver_grab_mouse_state;
   bool menu_mouse_enable                 = video_info->menu_mouse_enable;
   bool battery_level_enable              = video_info->battery_level_enable;
   bool timedate_enable                   = video_info->timedate_enable;
   gfx_display_t            *p_disp       = (gfx_display_t*)video_info->disp_userdata;
   gfx_animation_t *p_anim                = anim_get_ptr();
   gfx_display_ctx_driver_t *dispctx      = p_disp->dispctx;
   video_driver_state_t *video_st         = video_state_get_ptr();
   struct menu_state *menu_st             = menu_state_get_ptr();
   menu_list_t *menu_list                 = menu_st->entries.list;

   if (!ozone)
      return;

   /* Reset thumbnail bar when starting/closing content */
   if (((ozone->flags & OZONE_FLAG_LIBRETRO_RUNNING) > 0) != libretro_running)
   {
      if (libretro_running)
         ozone->flags          |=  OZONE_FLAG_LIBRETRO_RUNNING;
      else
         ozone->flags          &= ~OZONE_FLAG_LIBRETRO_RUNNING;

      ozone->flags             |=  OZONE_FLAG_NEED_COMPUTE;

      if (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU)
      {
         if (libretro_running)
            ozone->flags       &= ~(OZONE_FLAG_WANT_THUMBNAIL_BAR
                               |    OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE);
         else
            ozone->flags       |=  (OZONE_FLAG_WANT_THUMBNAIL_BAR
                               |    OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE);
      }
   }

   if (ozone->flags & OZONE_FLAG_FIRST_FRAME)
   {
      menu_input_get_pointer_state(&ozone->pointer);

      ozone->cursor_x_old = ozone->pointer.x;
      ozone->cursor_y_old = ozone->pointer.y;
      ozone->flags       &= ~OZONE_FLAG_FIRST_FRAME;
   }

   /* OSK Fade detection */
   if (draw_osk != draw_osk_old)
   {
      draw_osk_old = draw_osk;
      if (!draw_osk)
      {
         ozone->flags                       &= ~(OZONE_FLAG_SHOULD_DRAW_MSGBOX
                                               | OZONE_FLAG_MSGBOX_STATE
                                               | OZONE_FLAG_MSGBOX_STATE_OLD);
         ozone->animations.messagebox_alpha  = 0.0f;
      }
   }

   /* Change theme on the fly */
   ozone_last_use_preferred_system_color_theme =
         ozone->flags2 & OZONE_FLAG2_LAST_USE_PREFERRED_SYSTEM_COLOR_THEME;

   if (   (color_theme != ozone_last_color_theme)
       || (ozone_last_use_preferred_system_color_theme != use_preferred_system_color_theme))
   {
      if (use_preferred_system_color_theme)
      {
         color_theme                           = ozone_get_system_theme();
         configuration_set_uint(settings,
               settings->uints.menu_ozone_color_theme, color_theme);
      }

      ozone_set_color_theme(ozone, color_theme);
      if (ozone->theme->background_libretro_running)
         ozone_set_background_running_opacity(ozone, menu_framebuffer_opacity);

      if (use_preferred_system_color_theme)
         ozone->flags2 |=  OZONE_FLAG2_LAST_USE_PREFERRED_SYSTEM_COLOR_THEME;
      else
         ozone->flags2 &= ~OZONE_FLAG2_LAST_USE_PREFERRED_SYSTEM_COLOR_THEME;
   }

   /* If menu screensaver is active, draw
    * screensaver and return */
   if (ozone->flags & OZONE_FLAG_SHOW_SCREENSAVER)
   {
      menu_screensaver_frame(ozone->screensaver,
            video_info, p_disp);
      return;
   }

   if (video_st->current_video && video_st->current_video->set_viewport)
      video_st->current_video->set_viewport(
            video_st->data, video_width, video_height, true, false);

   /* Clear text */
   font_bind(&ozone->fonts.footer);
   font_bind(&ozone->fonts.title);
   font_bind(&ozone->fonts.time);
   font_bind(&ozone->fonts.entries_label);
   font_bind(&ozone->fonts.entries_sublabel);
   font_bind(&ozone->fonts.sidebar);

   /* Single-click playlist button hold delay */
   if (ozone->animations.list_alpha == 0.0f && ozone->draw_entry_delay)
   {
      ozone->draw_entry_delay--;
      if (!ozone->draw_entry_delay)
         ozone_animation_list_alpha(ozone, true);
   }

   /* Blank dummy core output */
   if (!libretro_running)
   {
      float pure_black[16] = COLOR_HEX_TO_FLOAT(0x000000, 1.00f);

      gfx_display_draw_quad(p_disp,
            userdata,
            video_width,
            video_height,
            0,
            0,
            video_width,
            video_height,
            video_width,
            video_height,
            pure_black,
            NULL);
   }

   /* Background (Always use running background due to overlays) */
   if (menu_framebuffer_opacity < 1.0f)
   {
      if (menu_framebuffer_opacity != ozone_last_framebuffer_opacity)
         if (ozone->theme->background_libretro_running)
            ozone_set_background_running_opacity(ozone, menu_framebuffer_opacity);

      background_color = ozone->theme->background_libretro_running;
   }
   else
      background_color = ozone->theme->background;

   gfx_display_set_alpha(background_color, menu_framebuffer_opacity * ozone->animations.alpha);

   gfx_display_draw_quad(p_disp,
         userdata,
         video_width,
         video_height,
         0,
         0,
         video_width,
         video_height,
         video_width,
         video_height,
         background_color,
         NULL);

   if (!p_disp->dispctx->handles_transform)
   {
      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);
   }

   /* Header, footer */
   ozone_draw_header(ozone,
         p_disp,
         p_anim,
         settings,
         userdata,
         video_width,
         video_height,
         battery_level_enable,
         timedate_enable,
         &mymat);

   ozone_draw_footer(ozone,
         menu_st,
         p_disp,
         p_anim,
         userdata,
         video_width,
         video_height,
         settings,
         &mymat);

   /* Sidebar */
   if (ozone->flags & OZONE_FLAG_DRAW_SIDEBAR)
      ozone_draw_sidebar(ozone,
            p_disp,
            p_anim,
            settings->bools.menu_ticker_smooth,
            (enum gfx_animation_ticker_type)settings->uints.menu_ticker_type,
            userdata,
            video_width,
            video_height,
            libretro_running,
            menu_framebuffer_opacity,
            &mymat);

   /* Menu entries */
   if (p_disp->dispctx && p_disp->dispctx->scissor_begin)
      gfx_display_scissor_begin(p_disp,
            userdata,
            video_width,
            video_height,
            ozone->sidebar_offset + (unsigned)ozone->dimensions_sidebar_width,
            ozone->dimensions.header_height + ozone->dimensions.spacer_1px,
            video_width - (unsigned)ozone->dimensions_sidebar_width + (-ozone->sidebar_offset),
            video_height
                  - ozone->dimensions.header_height
                  - ozone->dimensions.footer_height
                  - ozone->dimensions.spacer_1px);

   /* Current list */
   ozone_draw_entries(ozone,
         p_disp,
         p_anim,
         settings,
         userdata,
         video_width,
         video_height,
         (unsigned)ozone->selection,
         (unsigned)ozone->selection_old,
         MENU_LIST_GET_SELECTION(menu_list, 0),
         ozone->animations.list_alpha,
         ozone->animations.scroll_y,
         (ozone->flags & OZONE_FLAG_IS_PLAYLIST) ? true : false,
         &mymat
         );

   /* Old list */
   if (ozone->flags & OZONE_FLAG_DRAW_OLD_LIST)
      ozone_draw_entries(ozone,
            p_disp,
            p_anim,
            settings,
            userdata,
            video_width,
            video_height,
            (unsigned)ozone->selection_old_list,
            (unsigned)ozone->selection_old_list,
            &ozone->selection_buf_old,
            ozone->animations.list_alpha,
            ozone->scroll_old,
            (ozone->flags & OZONE_FLAG_IS_PLAYLIST_OLD) ? true : false,
            &mymat
      );

   /* Thumbnail bar */
   if (     ozone->show_thumbnail_bar
         && !(ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS))
      ozone_draw_thumbnail_bar(ozone,
            menu_st,
            p_disp,
            p_anim,
            settings,
            userdata,
            video_width,
            video_height,
            libretro_running,
            menu_framebuffer_opacity,
            &mymat);

   if (dispctx && dispctx->scissor_end)
      dispctx->scissor_end(userdata, video_width, video_height);

   /* Flush first layer of text */
   font_flush(video_width, video_height, &ozone->fonts.footer);
   font_flush(video_width, video_height, &ozone->fonts.title);
   font_flush(video_width, video_height, &ozone->fonts.time);
   font_flush(video_width, video_height, &ozone->fonts.entries_label);
   font_flush(video_width, video_height, &ozone->fonts.entries_sublabel);
   font_flush(video_width, video_height, &ozone->fonts.sidebar);

   /* Draw fullscreen thumbnails, if required */
   ozone_draw_fullscreen_thumbnails(ozone,
         userdata,
         video_info->disp_userdata,
         video_width,
         video_height);

   /* Message box & OSK - second layer of text */
   if (    (ozone->flags & OZONE_FLAG_SHOULD_DRAW_MSGBOX)
         || draw_osk)
   {
      /* Fade in animation */
      if (     (((ozone->flags & OZONE_FLAG_MSGBOX_STATE_OLD) > 0) != ((ozone->flags & OZONE_FLAG_MSGBOX_STATE) > 0))
            && (((ozone->flags & OZONE_FLAG_MSGBOX_STATE) > 0)))
      {
         if (ozone->flags & OZONE_FLAG_MSGBOX_STATE)
            ozone->flags                   |=  OZONE_FLAG_MSGBOX_STATE_OLD;
         else
            ozone->flags                   &= ~OZONE_FLAG_MSGBOX_STATE_OLD;

         gfx_animation_kill_by_tag(&messagebox_tag);
         ozone->animations.messagebox_alpha = 0.0f;

         entry.cb                           = NULL;
         entry.duration                     = ANIMATION_PUSH_ENTRY_DURATION;
         entry.easing_enum                  = OZONE_EASING_ALPHA;
         entry.subject                      = &ozone->animations.messagebox_alpha;
         entry.tag                          = messagebox_tag;
         entry.target_value                 = 1.0f;
         entry.userdata                     = NULL;

         gfx_animation_push(&entry);
      }
      /* Fade out animation */
      else if   (((ozone->flags & OZONE_FLAG_MSGBOX_STATE_OLD) > 0) != ((ozone->flags & OZONE_FLAG_MSGBOX_STATE) > 0)
            &&  (!(ozone->flags & OZONE_FLAG_MSGBOX_STATE)))
      {
         if (ozone->flags & OZONE_FLAG_MSGBOX_STATE)
            ozone->flags                   &= ~OZONE_FLAG_MSGBOX_STATE_OLD;
         else
            ozone->flags                   &= ~OZONE_FLAG_MSGBOX_STATE_OLD;
         ozone->flags                      &= ~OZONE_FLAG_MSGBOX_STATE;

         gfx_animation_kill_by_tag(&messagebox_tag);
         ozone->animations.messagebox_alpha = 1.0f;

         entry.cb                           = ozone_messagebox_fadeout_cb;
         entry.duration                     = ANIMATION_PUSH_ENTRY_DURATION;
         entry.easing_enum                  = OZONE_EASING_ALPHA;
         entry.subject                      = &ozone->animations.messagebox_alpha;
         entry.tag                          = messagebox_tag;
         entry.target_value                 = 0.0f;
         entry.userdata                     = ozone;

         gfx_animation_push(&entry);
      }

      ozone_draw_backdrop(userdata,
            video_info->disp_userdata,
            video_width,
            video_height,
            float_min(ozone->animations.messagebox_alpha,
            (draw_osk) ? 0.95f : 0.75f));

      if (draw_osk)
      {
         struct menu_state *menu_st  = menu_state_get_ptr();
         const char *label           = menu_st->input_dialog_kb_label;
         const char *str             = menu_input_dialog_get_buffer();

         ozone_draw_osk(ozone,
               userdata,
               video_info->disp_userdata,
               video_width,
               video_height,
               label,
               str);
      }
      else
         ozone_draw_messagebox(ozone,
               p_disp,
               userdata,
               video_width,
               video_height,
               ozone->pending_message,
               &mymat);

      /* Flush second layer of text */
      font_flush(video_width, video_height, &ozone->fonts.footer);
      font_flush(video_width, video_height, &ozone->fonts.entries_label);
   }

   /* Cursor */
   if (     (ozone->flags & OZONE_FLAG_SHOW_CURSOR)
         && (ozone->pointer.type != MENU_POINTER_DISABLED))
   {
      bool cursor_visible = (video_fullscreen || mouse_grabbed) && menu_mouse_enable;

      gfx_display_set_alpha(ozone->pure_white, 1.0f);
      if (cursor_visible)
         gfx_display_draw_cursor(p_disp,
               userdata,
               video_width,
               video_height,
               cursor_visible,
               ozone->pure_white,
               ozone->dimensions.cursor_size,
               ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_POINTER],
               ozone->pointer.x,
               ozone->pointer.y,
               video_width,
               video_height);
   }

   /* Unbind fonts */
   font_unbind(&ozone->fonts.footer);
   font_unbind(&ozone->fonts.title);
   font_unbind(&ozone->fonts.time);
   font_unbind(&ozone->fonts.entries_label);
   font_unbind(&ozone->fonts.entries_sublabel);
   font_unbind(&ozone->fonts.sidebar);

   if (video_st->current_video && video_st->current_video->set_viewport)
      video_st->current_video->set_viewport(
            video_st->data, video_width, video_height, false, true);
}

static void ozone_set_header(ozone_handle_t *ozone)
{
   if (     (ozone->categories_selection_ptr <= ozone->system_tab_end)
         || ((ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU) && !menu_is_running_quick_menu())
         || (ozone->depth > 1))
      menu_entries_get_title(ozone->title, sizeof(ozone->title));
   else if (ozone->horizontal_list.size)
   {
      ozone_node_t *node = (ozone_node_t*)file_list_get_userdata_at_offset(
            &ozone->horizontal_list,
            ozone->categories_selection_ptr - ozone->system_tab_end - 1);

      if (node && node->console_name)
      {
         strlcpy(ozone->title, node->console_name, sizeof(ozone->title));

         /* Add current search terms */
         menu_entries_search_append_terms_string(ozone->title, sizeof(ozone->title));
      }
   }
}

static void ozone_animation_end(void *userdata)
{
   ozone_handle_t *ozone            = (ozone_handle_t*)userdata;
   ozone->flags                    &= ~OZONE_FLAG_DRAW_OLD_LIST;
   ozone->animations.cursor_alpha   = 1.0f;
}

static void ozone_animation_list_alpha(ozone_handle_t *ozone, bool fade_in)
{
   struct gfx_animation_ctx_entry entry;

   entry.cb                     = ozone_animation_end;
   entry.duration               = ANIMATION_PUSH_ENTRY_DURATION;
   entry.easing_enum            = (fade_in) ? OZONE_EASING_ALPHA_IN : OZONE_EASING_ALPHA;
   entry.subject                = &ozone->animations.list_alpha;
   entry.tag                    = (uintptr_t)NULL;
   entry.target_value           = (fade_in) ? 1.0f : 0.0f;
   entry.userdata               = ozone;

   gfx_animation_push(&entry);
}

static void ozone_list_open(
      ozone_handle_t *ozone,
      bool ozone_collapse_sidebar,
      bool animate)
{
   struct gfx_animation_ctx_entry entry;
   uintptr_t sidebar_tag           = (uintptr_t)&ozone->sidebar_offset;

   ozone->flags                   |= OZONE_FLAG_DRAW_OLD_LIST;

   /* Sidebar animation */
   ozone_sidebar_update_collapse(ozone, ozone_collapse_sidebar, animate);

   /* Kill any existing sidebar slide-in/out animations
    * before pushing a new one
    * > This is required since the 'ozone_collapse_end'
    *   callback from an unfinished slide-out animation
    *   may subsequently override the OZONE_FLAG_DRAW_SIDEBAR
    *   value set at the beginning of the next slide-in
    *   animation... */
   gfx_animation_kill_by_tag(&sidebar_tag);

   if (ozone->depth == 1)
   {
      ozone->flags            |= OZONE_FLAG_DRAW_SIDEBAR;

      if (animate)
      {
         entry.cb              = NULL;
         entry.duration        = ANIMATION_PUSH_ENTRY_DURATION;
         entry.easing_enum     = OZONE_EASING_XY;
         entry.subject         = &ozone->sidebar_offset;
         entry.tag             = sidebar_tag;
         entry.target_value    = 0.0f;
         entry.userdata        = NULL;

         gfx_animation_push(&entry);

         /* Skip "left/right" animation if animating already */
         if (ozone->sidebar_offset != entry.target_value)
            animate = false;
      }
      else
         ozone->sidebar_offset = 0.0f;
   }
   else if (ozone->depth > 1)
   {
      if (animate)
      {
         entry.cb              = ozone_collapse_end;
         entry.duration        = ANIMATION_PUSH_ENTRY_DURATION;
         entry.easing_enum     = OZONE_EASING_XY;
         entry.subject         = &ozone->sidebar_offset;
         entry.tag             = sidebar_tag;
         entry.target_value    = -ozone->dimensions_sidebar_width;
         entry.userdata        = ozone;

         gfx_animation_push(&entry);

         /* Skip "left/right" animation if animating already */
         if (ozone->sidebar_offset != entry.target_value)
            animate = false;
      }
      else
         ozone->sidebar_offset = -ozone->dimensions_sidebar_width;
   }

   if (animate && ozone->animations.list_alpha != 0.0f)
   {
      ozone->animations.list_alpha = 0.0f;

      entry.cb                     = ozone_animation_end;
      entry.duration               = ANIMATION_PUSH_ENTRY_DURATION / 2;
      entry.easing_enum            = OZONE_EASING_ALPHA_IN;
      entry.subject                = &ozone->animations.list_alpha;
      entry.tag                    = (uintptr_t)NULL;
      entry.target_value           = 1.0f;
      entry.userdata               = ozone;

      gfx_animation_push(&entry);
   }
   else
      ozone_animation_end(ozone);
}

static void ozone_populate_entries(
      void *data,
      const char *path,
      const char *label,
      unsigned k)
{
   settings_t *settings                 = config_get_ptr();
   int new_depth                        = 0;
   bool was_db_manager_list             = false;
   bool want_thumbnail_bar              = false;
   bool fs_thumbnails_available         = false;
   bool animate                         = false;
   struct menu_state *menu_st           = menu_state_get_ptr();
   menu_list_t *menu_list               = menu_st->entries.list;
   bool ozone_collapse_sidebar          = settings->bools.ozone_collapse_sidebar;
   ozone_handle_t *ozone                = (ozone_handle_t*) data;

   if (!ozone)
      return;

   ozone->show_playlist_tabs            = settings->bools.menu_content_show_playlist_tabs;

   if ((menu_st->flags & MENU_ST_FLAG_PREVENT_POPULATE) > 0)
   {
      menu_st->flags                 &=  ~MENU_ST_FLAG_PREVENT_POPULATE;
      ozone_selection_changed(ozone, false);

      /* Refresh title for search terms */
      ozone_set_header(ozone);

      /* Handle playlist searches
       * (Ozone is a fickle beast...) */
      if (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
      {
         menu_search_terms_t *menu_search_terms = menu_entries_search_get_terms();
         size_t num_search_terms                = menu_search_terms
               ? menu_search_terms->size
               : 0;

         if (ozone->num_search_terms_old != num_search_terms)
         {
            /* Refresh thumbnails */
            ozone_unload_thumbnail_textures(ozone);

            if (   gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT)
                || gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
            {
               ozone_set_thumbnail_content(ozone, "");
               ozone_update_thumbnail_image(ozone);
            }

            /* If we are currently inside an empty
             * playlist, return to the sidebar */
            if (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR))
            {
               file_list_t *list       = MENU_LIST_GET_SELECTION(menu_list, 0);
               uintptr_t animation_tag = (uintptr_t)&ozone->animations.cursor_alpha;
               bool goto_sidebar       = false;

               if (!list || (list->size < 1))
                  goto_sidebar         = true;
               else if ((list->list[0].type != FILE_TYPE_RPL_ENTRY))
                  goto_sidebar         = true;

               if (goto_sidebar)
               {
                  gfx_animation_kill_by_tag(&animation_tag);
                  ozone->flags |= OZONE_FLAG_EMPTY_PLAYLIST;
                  ozone_go_to_sidebar(ozone, ozone_collapse_sidebar, animation_tag);
               }
            }

            ozone->num_search_terms_old = num_search_terms;
         }
      }
      return;
   }

   ozone->flags               |=  OZONE_FLAG_NEED_COMPUTE;
   ozone->flags               &= ~OZONE_FLAG_SKIP_THUMBNAIL_RESET;

   if (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU)
      ozone->flags            |=  OZONE_FLAG_WAS_QUICK_MENU;
   else
      ozone->flags            &= ~OZONE_FLAG_WAS_QUICK_MENU;

   ozone->first_onscreen_entry = 0;
   ozone->last_onscreen_entry  = 0;

   new_depth                   = (int)ozone_list_get_size(ozone, MENU_LIST_PLAIN);
   was_db_manager_list         = (ozone->flags & OZONE_FLAG_IS_DB_MANAGER_LIST)
                                 && new_depth > ozone->depth;

   animate                     = new_depth != ozone->depth;

   if (new_depth <= ozone->depth)
      ozone->flags            |=  OZONE_FLAG_FADE_DIRECTION;
   else
      ozone->flags            &= ~OZONE_FLAG_FADE_DIRECTION;
   ozone->depth                = new_depth;

   if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DATABASE_MANAGER_LIST)))
      ozone->flags            |=  OZONE_FLAG_IS_DB_MANAGER_LIST;
   else
      ozone->flags            &= ~OZONE_FLAG_IS_DB_MANAGER_LIST;

#if defined(HAVE_LIBRETRODB)
   if (     string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_EXPLORE_LIST))
         || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB))
         || ozone_get_horizontal_selection_type(ozone) == MENU_EXPLORE_TAB)
      ozone->flags |=  OZONE_FLAG_IS_EXPLORE_LIST;
   else
      ozone->flags &= ~OZONE_FLAG_IS_EXPLORE_LIST;
#endif

   if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES)))
      ozone->flags |=  OZONE_FLAG_IS_FILE_LIST;
   else
      ozone->flags &= ~OZONE_FLAG_IS_FILE_LIST;

   if (     string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_RPL_ENTRY_ACTIONS))
         || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_SETTINGS))
         || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SAVESTATE_LIST)))
      ozone->flags2 |=  OZONE_FLAG2_IS_QUICK_MENU;
   else
      ozone->flags2 &= ~OZONE_FLAG2_IS_QUICK_MENU;

   if (     string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB))
         || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_CONTENTLESS_CORES_LIST)))
      ozone->flags |=  OZONE_FLAG_IS_CONTENTLESS_CORES;
   else
      ozone->flags &= ~OZONE_FLAG_IS_CONTENTLESS_CORES;

   if (string_to_unsigned(path) == MENU_ENUM_LABEL_STATE_SLOT)
      ozone->flags |=  OZONE_FLAG_IS_STATE_SLOT;
   else
      ozone->flags &= ~OZONE_FLAG_IS_STATE_SLOT;

   if (ozone_is_playlist(ozone, true))
      ozone->flags |=  OZONE_FLAG_IS_PLAYLIST;
   else
      ozone->flags &= ~OZONE_FLAG_IS_PLAYLIST;

   if (     !(ozone->flags & OZONE_FLAG_IS_PLAYLIST)
         && string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB)))
      ozone->flags2 |=  OZONE_FLAG2_IS_PLAYLISTS_TAB;
   else
      ozone->flags2 &= ~OZONE_FLAG2_IS_PLAYLISTS_TAB;

   ozone_set_header(ozone);

   if (was_db_manager_list)
      ozone->flags |= OZONE_FLAG_IS_DB_MANAGER_LIST
                    | OZONE_FLAG_SKIP_THUMBNAIL_RESET;

#if defined(HAVE_LIBRETRODB)
   if (ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST)
   {
      menu_entry_t entry;
      MENU_ENTRY_INITIALIZE(entry);
      entry.flags |= MENU_ENTRY_FLAG_LABEL_ENABLED;
      menu_entry_get(&entry, 0, 0, NULL, true);

      /* Quick Menu under Explore list must also be Quick Menu */
      if (     string_is_equal(entry.label, msg_hash_to_str(MENU_ENUM_LABEL_RUN))
            || string_is_equal(entry.label, msg_hash_to_str(MENU_ENUM_LABEL_RESUME_CONTENT))
            || string_is_equal(entry.label, msg_hash_to_str(MENU_ENUM_LABEL_STATE_SLOT))
         )
      {
         ozone->flags2 |=  OZONE_FLAG2_IS_QUICK_MENU;
         ozone->flags  &= ~OZONE_FLAG_IS_EXPLORE_LIST;
      }
   }
#endif

   if (animate)
      if (ozone->categories_selection_ptr == ozone->categories_active_idx_old)
         ozone_list_open(ozone, ozone_collapse_sidebar,
               (  !(ozone->flags & OZONE_FLAG_FIRST_FRAME)
               && !(ozone->flags2 & OZONE_FLAG2_BLOCK_ANIMATION)));

   ozone->flags2 &= ~OZONE_FLAG2_BLOCK_ANIMATION;

   /* Reset savestate thumbnails always */
   ozone_update_savestate_thumbnail_path(ozone, (unsigned)menu_st->selection_ptr);
   ozone_update_savestate_thumbnail_image(ozone);

   /* Thumbnails
    * > Note: Leave current thumbnails loaded when
    *   opening the quick menu - allows proper fade
    *   out of the fullscreen thumbnail viewer
    * > Do not reset thumbnail when returning from quick menu */
   if (     (!(ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU))
         &&   (ozone->flags  & OZONE_FLAG_WAS_QUICK_MENU)
         && (!(ozone->flags & OZONE_FLAG_IS_STATE_SLOT))
         && (  gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT)
         ||    gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
         && (  ozone->thumbnails.left.status  == GFX_THUMBNAIL_STATUS_AVAILABLE
         ||    ozone->thumbnails.right.status == GFX_THUMBNAIL_STATUS_AVAILABLE
         ||    ozone->thumbnails.left.status  == GFX_THUMBNAIL_STATUS_MISSING
         ||    ozone->thumbnails.right.status == GFX_THUMBNAIL_STATUS_MISSING))
      ozone->flags |= OZONE_FLAG_SKIP_THUMBNAIL_RESET;

   /* Always allow thumbnail reset in Information page */
   if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_INFORMATION)))
      ozone->flags &= ~OZONE_FLAG_SKIP_THUMBNAIL_RESET;

   if (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU)
   {
      if (menu_is_running_quick_menu())
      {
         ozone->flags &= ~(OZONE_FLAG_WANT_THUMBNAIL_BAR
                         | OZONE_FLAG_SKIP_THUMBNAIL_RESET);
         ozone_update_savestate_thumbnail_path(ozone, (unsigned)menu_st->selection_ptr);
         ozone_update_savestate_thumbnail_image(ozone);
      }
      else if (   gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT)
               || gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
      {
         ozone->flags |=  (OZONE_FLAG_WANT_THUMBNAIL_BAR
                         | OZONE_FLAG_SKIP_THUMBNAIL_RESET);
         ozone_update_thumbnail_image(ozone);
      }
   }
   else if (ozone->flags & OZONE_FLAG_IS_STATE_SLOT)
      ozone->flags |= OZONE_FLAG_WANT_THUMBNAIL_BAR;
   else if (ozone->flags & OZONE_FLAG_IS_EXPLORE_LIST)
   {
      ozone->flags &= ~OZONE_FLAG_WANT_THUMBNAIL_BAR;
      ozone_set_thumbnail_content(ozone, "");
      ozone_update_thumbnail_image(ozone);
   }
   else if ((!(ozone->flags & OZONE_FLAG_IS_STATE_SLOT))
         && (!(ozone->flags & OZONE_FLAG_SKIP_THUMBNAIL_RESET)))
   {
      if ((ozone->flags & OZONE_FLAG_IS_DB_MANAGER_LIST) && (ozone->depth == 4))
         ozone->flags |= OZONE_FLAG_SKIP_THUMBNAIL_RESET;

      if (!(ozone->flags & OZONE_FLAG_SKIP_THUMBNAIL_RESET))
         ozone_unload_thumbnail_textures(ozone);

      if (   gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT)
          || gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
      {
         /* Only auto-load thumbnails if we are viewing
          * a playlist or a database manager list
          * > Note that we can ignore file browser lists,
          *   since the first selected item on such a list
          *   can never have a thumbnail */
         if (    (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
             || ((ozone->flags & OZONE_FLAG_IS_DB_MANAGER_LIST) && (ozone->depth >= 4)))
         {
            ozone_set_thumbnail_content(ozone, "");
            ozone_update_thumbnail_image(ozone);
         }
         else if (ozone->flags & OZONE_FLAG_IS_FILE_LIST)
            ozone->flags &= ~OZONE_FLAG_WANT_THUMBNAIL_BAR;
      }
   }

   if (     (ozone->flags & OZONE_FLAG_SKIP_THUMBNAIL_RESET)
         && (     gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT)
               || gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT)))
      ozone->flags |= OZONE_FLAG_WANT_THUMBNAIL_BAR;

   switch (ozone->tabs[ozone->categories_selection_ptr])
   {
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
      case OZONE_SYSTEM_TAB_VIDEO:
#endif
      case OZONE_SYSTEM_TAB_MUSIC:
         if (ozone->categories_selection_ptr <= ozone->system_tab_end)
            ozone->flags &= ~(OZONE_FLAG_WANT_THUMBNAIL_BAR
                            | OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE);
         break;
   }

   /* Fullscreen thumbnails are only available on
    * playlists, database manager lists, file lists
    * and savestate slots */
   if (
            (   (ozone->flags  & OZONE_FLAG_WANT_THUMBNAIL_BAR))
         && (   (ozone->flags  & OZONE_FLAG_IS_PLAYLIST)
            || ((ozone->flags  & OZONE_FLAG_IS_DB_MANAGER_LIST) && (ozone->depth >= 4))
            ||  (ozone->flags  & OZONE_FLAG_IS_EXPLORE_LIST)
            ||  (ozone->flags  & OZONE_FLAG_IS_FILE_LIST)
            ||  (ozone->flags  & OZONE_FLAG_IS_STATE_SLOT)
            ||  (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU)
            )
      )
      ozone->flags |=  OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE;
   else
      ozone->flags &= ~OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE;

   fs_thumbnails_available = (ozone->flags & OZONE_FLAG_FULLSCREEN_THUMBNAILS_AVAILABLE) ? true : false;
   want_thumbnail_bar      = (ozone->flags & OZONE_FLAG_WANT_THUMBNAIL_BAR) ? true : false;

   if (fs_thumbnails_available != want_thumbnail_bar)
   {
      if (fs_thumbnails_available)
         ozone->flags |=  OZONE_FLAG_WANT_THUMBNAIL_BAR;
      else
         ozone->flags &= ~OZONE_FLAG_WANT_THUMBNAIL_BAR;
   }
}

static void ozone_toggle(void *userdata, bool menu_on)
{
   bool ozone_collapse_sidebar = config_get_ptr()->bools.ozone_collapse_sidebar;
   struct menu_state *menu_st  = menu_state_get_ptr();
   ozone_handle_t *ozone       = (ozone_handle_t*)userdata;

   if (!ozone)
      return;

   /* Fade in animation */
   if (menu_on)
   {
      struct gfx_animation_ctx_entry entry;

      ozone->animations.alpha      = 0.0f;

      entry.cb                     = NULL;
      entry.duration               = ANIMATION_PUSH_ENTRY_DURATION * 1.25f;
      entry.easing_enum            = OZONE_EASING_ALPHA;
      entry.subject                = &ozone->animations.alpha;
      entry.tag                    = (uintptr_t)NULL;
      entry.target_value           = 1.0f;

      gfx_animation_push(&entry);
   }

   /* Reset */
   ozone->animations.list_alpha    = 1.0f;

   /* Have to reset this, otherwise savestate
    * thumbnail won't update after selecting
    * 'save state' option */
   if (ozone->flags2 & OZONE_FLAG2_IS_QUICK_MENU)
   {
      ozone->flags              &= ~(OZONE_FLAG_WANT_THUMBNAIL_BAR
                                   | OZONE_FLAG_SKIP_THUMBNAIL_RESET);
      gfx_thumbnail_reset(&ozone->thumbnails.savestate);
      ozone_update_savestate_thumbnail_path(ozone, (unsigned)menu_st->selection_ptr);
      ozone_update_savestate_thumbnail_image(ozone);
   }

   if (MENU_ENTRIES_NEEDS_REFRESH(menu_st))
      menu_st->flags                 &=  ~MENU_ST_FLAG_PREVENT_POPULATE;
   else
      menu_st->flags                 |=   MENU_ST_FLAG_PREVENT_POPULATE;

   if (ozone->depth == 1)
   {
      ozone->flags                   |= OZONE_FLAG_DRAW_SIDEBAR;
      ozone->sidebar_offset           = 0.0f;
   }

   ozone_sidebar_update_collapse(ozone, ozone_collapse_sidebar, false);
}

static bool ozone_menu_init_list(void *data)
{
   menu_displaylist_info_t info;

   settings_t *settings         = config_get_ptr();
   struct menu_state *menu_st   = menu_state_get_ptr();
   menu_list_t *menu_list       = menu_st->entries.list;
   file_list_t *menu_stack      = MENU_LIST_GET(menu_list, 0);
   file_list_t *selection_buf   = MENU_LIST_GET_SELECTION(menu_list, 0);

   menu_displaylist_info_init(&info);

   info.label                   = strdup(msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU));
   info.exts                    = strldup("lpl", sizeof("lpl"));
   info.type_default            = FILE_TYPE_PLAIN;
   info.enum_idx                = MENU_ENUM_LABEL_MAIN_MENU;

   menu_entries_append(menu_stack,
         info.path,
         info.label,
         MENU_ENUM_LABEL_MAIN_MENU,
         info.type,
         info.flags,
         0,
         NULL);

   info.list                    = selection_buf;

   if (!menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info, settings))
      goto error;

   info.flags |= MD_FLAG_NEED_PUSH;

   if (!menu_displaylist_process(&info))
      goto error;

   menu_displaylist_info_free(&info);
   return true;

error:
   menu_displaylist_info_free(&info);
   return false;
}

static void ozone_list_insert(void *userdata,
      file_list_t *list,
      const char *path,
      const char *fullpath,
      const char *label,
      size_t list_size,
      unsigned type)
{
   ozone_handle_t *ozone = (ozone_handle_t*)userdata;
   ozone_node_t *node    = NULL;
   int i                 = (int)list_size;

   if (!ozone || !list)
      return;

   ozone->flags         |= OZONE_FLAG_NEED_COMPUTE;

   if (!(node = (ozone_node_t*)list->list[i].userdata))
   {
      if (!(node = ozone_alloc_node()))
         return;
   }

   if (!string_is_empty(fullpath))
   {
      if (node->fullpath)
         free(node->fullpath);

      node->fullpath      = strdup(fullpath);
   }

   list->list[i].userdata = node;
}

static int ozone_environ_cb(enum menu_environ_cb type, void *data, void *userdata)
{
   ozone_handle_t *ozone = (ozone_handle_t*)userdata;

   if (!ozone)
      return -1;

   switch (type)
   {
      case MENU_ENVIRON_ENABLE_MOUSE_CURSOR:
         ozone->flags |=  OZONE_FLAG_SHOW_CURSOR;
         break;
      case MENU_ENVIRON_DISABLE_MOUSE_CURSOR:
         ozone->flags &= ~OZONE_FLAG_SHOW_CURSOR;
         break;
      case MENU_ENVIRON_RESET_HORIZONTAL_LIST:
         ozone_refresh_system_tabs_list(ozone);
         ozone_refresh_horizontal_list(ozone, config_get_ptr());
         break;
      case MENU_ENVIRON_ENABLE_SCREENSAVER:
         ozone->flags |=  OZONE_FLAG_SHOW_SCREENSAVER;
         break;
      case MENU_ENVIRON_DISABLE_SCREENSAVER:
         ozone->flags &= ~OZONE_FLAG_SHOW_SCREENSAVER;
         break;
      default:
         return -1;
   }

   return 0;
}

static void ozone_messagebox(void *data, const char *message)
{
   ozone_handle_t *ozone = (ozone_handle_t*) data;

   if (!ozone || string_is_empty(message))
      return;

   if (ozone->pending_message)
   {
      free(ozone->pending_message);
      ozone->pending_message     = NULL;
   }

   ozone->pending_message        = strdup(message);
   ozone->flags                 |= OZONE_FLAG_SHOULD_DRAW_MSGBOX
                                 | OZONE_FLAG_MSGBOX_STATE;
}

static int ozone_deferred_push_content_actions(menu_displaylist_info_t *info)
{
   settings_t *settings         = config_get_ptr();
   if (!menu_displaylist_ctl(DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info, settings))
      return -1;
   menu_displaylist_process(info);
   menu_displaylist_info_free(info);
   return 0;
}

static int ozone_list_bind_init_compare_label(menu_file_list_cbs_t *cbs)
{
   if (cbs && cbs->enum_idx != MSG_UNKNOWN)
   {
      switch (cbs->enum_idx)
      {
         case MENU_ENUM_LABEL_CONTENT_ACTIONS:
            cbs->action_deferred_push = ozone_deferred_push_content_actions;
            break;
         default:
            return -1;
      }
   }

   return 0;
}

static int ozone_list_bind_init(menu_file_list_cbs_t *cbs,
      const char *path,
      const char *label,
      unsigned type,
      size_t idx)
{
   if (ozone_list_bind_init_compare_label(cbs) == 0)
      return 0;

   return -1;
}

static int ozone_tap_footer(
      ozone_handle_t *ozone,
      unsigned x,
      unsigned video_width,
      menu_entry_t *entry)
{
   struct menu_state *menu_st             = menu_state_get_ptr();
   size_t selection                       = menu_st->selection_ptr;
   settings_t *settings                   = config_get_ptr();
   bool input_menu_swap_ok_cancel_buttons = settings->bools.input_menu_swap_ok_cancel_buttons;

   /* values below should all be kept in sync with ozone_draw_footer */
   float scale_factor                     = ozone->last_scale_factor;
   float footer_margin                    = 42 * scale_factor;

   if (x > (float)video_width - footer_margin)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_CANCEL);
   else if (ozone->footer_labels.resume.show && x > ozone->footer_labels.resume.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_RESUME);
   else if (input_menu_swap_ok_cancel_buttons && x > ozone->footer_labels.back.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_CANCEL);
   else if (input_menu_swap_ok_cancel_buttons && x > ozone->footer_labels.ok.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_OK);
   else if (!input_menu_swap_ok_cancel_buttons && x > ozone->footer_labels.ok.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_OK);
   else if (!input_menu_swap_ok_cancel_buttons && x > ozone->footer_labels.back.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_CANCEL);
   else if (ozone->footer_labels.search.show && x > ozone->footer_labels.search.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_SEARCH);
   else if (ozone->footer_labels.cycle_thumbnails.show && x > ozone->footer_labels.cycle_thumbnails.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_SEARCH);
   else if (ozone->footer_labels.random_select.show && x > ozone->footer_labels.random_select.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_SCAN);
   else if (ozone->footer_labels.clear_setting.show && x > ozone->footer_labels.clear_setting.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_SCAN);
   else if (ozone->footer_labels.scan.show && x > ozone->footer_labels.scan.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_SCAN);
   else if (ozone->footer_labels.reset_to_default.show && x > ozone->footer_labels.reset_to_default.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_START);
   else if (ozone->footer_labels.help.show && x > ozone->footer_labels.help.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_INFO);
   else if (ozone->footer_labels.fullscreen_thumbnails.show && x > ozone->footer_labels.fullscreen_thumbnails.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_START);
   else if (ozone->footer_labels.manage.show && x > ozone->footer_labels.manage.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_START);
   else if (ozone->footer_labels.fullscreen_thumbnails.show && x > ozone->footer_labels.metadata_override.x)
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_INFO);
   else
      return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_CANCEL);
}

static int ozone_pointer_up(void *userdata,
      unsigned x,
      unsigned y,
      unsigned ptr,
      enum menu_input_pointer_gesture gesture,
      menu_file_list_cbs_t *cbs,
      menu_entry_t *entry,
      unsigned action)
{
   unsigned int width, height;
   ozone_handle_t *ozone             = (ozone_handle_t*)userdata;
   struct menu_state *menu_st        = menu_state_get_ptr();
   menu_input_t *menu_input          = &menu_st->input_state;
   menu_list_t *menu_list            = menu_st->entries.list;
   file_list_t *selection_buf        = MENU_LIST_GET_SELECTION(menu_list, 0);
   uintptr_t sidebar_tag             = (uintptr_t)selection_buf;
   size_t selection                  = menu_st->selection_ptr;
   size_t entries_end                = MENU_LIST_GET_SELECTION(menu_list, 0)->size;
   settings_t *settings              = config_get_ptr();
   bool ozone_collapse_sidebar       = settings->bools.ozone_collapse_sidebar;

   if (!ozone)
      return -1;

   /* If fullscreen thumbnail view is enabled,
    * all input will disable it and otherwise
    * be ignored */
   if (ozone->flags2 & OZONE_FLAG2_SHOW_FULLSCREEN_THUMBNAILS)
   {
      /* Must reset scroll acceleration, in case
       * user performed a swipe (don't want menu
       * list to 'drift' after hiding fullscreen
       * thumbnails...) */
      menu_input->pointer.y_accel    = 0.0f;

      ozone_hide_fullscreen_thumbnails(ozone, true);
      ozone->flags2 &= ~OZONE_FLAG2_WANT_FULLSCREEN_THUMBNAILS;
      return 0;
   }

   video_driver_get_size(&width, &height);

   switch (gesture)
   {
      case MENU_INPUT_GESTURE_TAP:
      case MENU_INPUT_GESTURE_SHORT_PRESS:
         /* Tap/press header or footer: Menu back/cancel */
         if ((int)y < ozone->dimensions.header_height)
            return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_CANCEL);
         else if ((int)y > (int)height - ozone->dimensions.footer_height)
            return ozone_tap_footer(ozone, x, width, entry);
         /* Tap/press entries: Activate and/or select item */
         else if ((ptr < entries_end)
               && ((int)x > (int)(ozone->dimensions_sidebar_width + ozone->sidebar_offset))
               && ((int)x < (int)((float)width - ozone->animations.thumbnail_bar_position)))
         {
            if (gesture == MENU_INPUT_GESTURE_TAP)
            {
               /* A 'tap' always produces a menu action */

               /* If current 'pointer' item is not active,
                * activate it immediately */
               if (ptr != selection)
                  menu_st->selection_ptr = ptr;

               /* If we are not currently in the sidebar,
                * perform a MENU_ACTION_SELECT on currently
                * active item
                * > NOTE 1: Cannot perform a 'leave sidebar' operation
                *   and a MENU_ACTION_SELECT at the same time...
                * > NOTE 2: We still use 'selection' (i.e. old selection
                *   value) here. This ensures that ozone_menu_entry_action()
                *   registers any change due to the above automatic
                *   'pointer item' activation, and thus operates
                *   on the correct target entry */
               if (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR))
                  return ozone_menu_entry_action(ozone, entry,
                        selection, MENU_ACTION_SELECT);

               /* If we currently in the sidebar, leave it */
               if (!(ozone->flags & OZONE_FLAG_EMPTY_PLAYLIST))
               {
                  ozone_leave_sidebar(ozone, ozone_collapse_sidebar, sidebar_tag,
                        settings->uints.menu_remember_selection);
                  ozone_set_thumbnail_content(ozone, "");
                  ozone_update_thumbnail_image(ozone);
               }
            }
            else
            {
               /* A 'short' press is used only to activate (highlight)
                * an item - it does not invoke a MENU_ACTION_SELECT
                * action */
               menu_input->pointer.y_accel    = 0.0f;

               if (ptr != selection)
                  menu_st->selection_ptr = ptr;

               /* If we are currently in the sidebar, leave it */
               if (ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR)
               {
                  if (!(ozone->flags & OZONE_FLAG_EMPTY_PLAYLIST))
                     ozone_leave_sidebar(ozone, ozone_collapse_sidebar, sidebar_tag,
                           settings->uints.menu_remember_selection);
               }
               /* If this is a playlist and the selection
                * has changed, must update thumbnails */
               else if (   (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
                        && (ptr != selection))
               {
                  ozone_set_thumbnail_content(ozone, "");
                  ozone_update_thumbnail_image(ozone);
               }
            }
         }
         /* Tap/press thumbnail bar: toggle content metadata
          * override */
         else if (x > width - ozone->animations.thumbnail_bar_position)
         {
            /* Want to capture all input here, but only act
             * upon it if the content metadata toggle is
             * available (i.e. viewing a playlist with dual
             * thumbnails) */
            if (ozone_metadata_override_available(ozone,
                     settings->uints.menu_left_thumbnails))
               return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_INFO);
         }
         /* Tap/press sidebar: return to sidebar or select
          * category */
         else if (ozone->flags2 & OZONE_FLAG2_POINTER_IN_SIDEBAR)
         {
            /* If cursor is not in sidebar, return to sidebar */
            if (!(ozone->flags & OZONE_FLAG_CURSOR_IN_SIDEBAR))
               ozone_go_to_sidebar(ozone, ozone_collapse_sidebar, sidebar_tag);
            /* Otherwise, select current category */
            else if (
                     ozone->pointer_categories_selection
                  != ozone->categories_selection_ptr)
            {
               unsigned horizontal_list_size = (unsigned)ozone->horizontal_list.size;

               /* Ensure that current category is valid */
               if (ozone->pointer_categories_selection <= ozone->system_tab_end + horizontal_list_size)
                  ozone_sidebar_goto(ozone, ozone->pointer_categories_selection);
            }
         }
         break;
      case MENU_INPUT_GESTURE_LONG_PRESS:
         /* 'Reset to default' action */
         if (     ((int)y > ozone->dimensions.header_height)
               && ((int)y < (int)(height - ozone->dimensions.footer_height))
               && (ptr < entries_end)
               && (ptr == selection)
               && ((int)x > ozone->dimensions_sidebar_width + ozone->sidebar_offset)
               && ((int)x < width - ozone->animations.thumbnail_bar_position))
            return ozone_menu_entry_action(ozone,
                  entry, selection, MENU_ACTION_START);
         break;
      case MENU_INPUT_GESTURE_SWIPE_LEFT:
         /* If this is a playlist, descend alphabet
          * > Note: Can only do this if we are not using
          *   a mouse, since it conflicts with auto selection
          *   of entry under cursor */
         if (     (ozone->pointer.type != MENU_POINTER_MOUSE)
               && (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
               && (ozone->depth == 1))
            return ozone_menu_entry_action(ozone, entry, (size_t)ptr, MENU_ACTION_SCROLL_UP);
         break;
      case MENU_INPUT_GESTURE_SWIPE_RIGHT:
         /* If this is a playlist, ascend alphabet
          * > Note: Can only do this if we are not using
          *   a mouse, since it conflicts with auto selection
          *   of entry under cursor */
         if (     (ozone->pointer.type != MENU_POINTER_MOUSE)
               && (ozone->flags & OZONE_FLAG_IS_PLAYLIST)
               && (ozone->depth == 1))
            return ozone_menu_entry_action(ozone, entry, (size_t)ptr, MENU_ACTION_SCROLL_DOWN);
         break;
      default:
         /* Ignore input */
         break;
   }

   return 0;
}

menu_ctx_driver_t menu_ctx_ozone = {
   NULL,                         /* set_texture */
   ozone_messagebox,
   ozone_render,
   ozone_frame,
   ozone_init,
   ozone_free,
   ozone_context_reset,
   ozone_context_destroy,
   ozone_populate_entries,
   ozone_toggle,
   ozone_navigation_clear,
   NULL,
   NULL,
   ozone_navigation_set,
   ozone_navigation_pointer_changed,
   ozone_navigation_alphabet,
   ozone_navigation_alphabet,
   ozone_menu_init_list,
   ozone_list_insert,
   NULL,                         /* list_prepend */
   ozone_list_free,
   ozone_list_clear,
   ozone_list_cache,
   ozone_list_push,
   ozone_list_get_selection,
   ozone_list_get_size,
   ozone_list_get_entry,
   NULL,                         /* list_set_selection */
   ozone_list_bind_init,
   NULL,
   "ozone",
   ozone_environ_cb,
   NULL,
   ozone_update_thumbnail_image,
   ozone_refresh_thumbnail_image,
   ozone_set_thumbnail_content,
   gfx_display_osk_ptr_at_pos,
   ozone_update_savestate_thumbnail_path,
   ozone_update_savestate_thumbnail_image,
   NULL,                         /* pointer_down */
   ozone_pointer_up,
   ozone_menu_entry_action
};
