/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2013 - Hans-Kristian Arntzen
* Copyright (C) 2011-2013 - Daniel De Matteis
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see .
*/
#include "rmenu.h"
#include "utils/file_browser.h"
#if defined(__CELLOS_LV2__)
#include
#if (CELL_SDK_VERSION > 0x340000)
#include
#endif
#endif
#include "../../console/rarch_console.h"
#include "../../gfx/image.h"
#include "../../gfx/gfx_common.h"
#include "../../gfx/gfx_context.h"
#include "../../file.h"
#include "../../driver.h"
#include "../../general.h"
#ifdef HAVE_SHADER_MANAGER
#include "../../gfx/shader_parse.h"
#define EXT_SHADERS "cg"
#define EXT_CGP_PRESETS "cgp"
#endif
#ifdef _XBOX1
#define JPEG_FORMATS ""
#else
#define JPEG_FORMATS "|jpg|JPG|JPEG|jpeg"
#endif
#define EXT_IMAGES "png|PNG"JPEG_FORMATS
struct texture_image *menu_texture;
#ifdef HAVE_MENU_PANEL
struct texture_image *menu_panel;
#endif
#if defined(_XBOX1)
const char drive_mappings[][32] = {
"C:\\",
"D:\\",
"E:\\",
"F:\\",
"G:\\"
};
size_t drive_mapping_idx = 2;
#define TICKER_LABEL_CHARS_MAX_PER_LINE 16
#elif defined(__CELLOS_LV2__)
const char drive_mappings[][32] = {
"/app_home/",
"/dev_hdd0/",
"/dev_hdd1/",
"/host_root/"
};
size_t drive_mapping_idx = 1;
#define TICKER_LABEL_CHARS_MAX_PER_LINE 25
#endif
unsigned settings_lut[SETTING_LAST_LAST] = {0};
static const char *menu_drive_mapping_previous(void)
{
if (drive_mapping_idx > 0)
drive_mapping_idx--;
return drive_mappings[drive_mapping_idx];
}
static const char *menu_drive_mapping_next(void)
{
size_t arr_size = sizeof(drive_mappings) / sizeof(drive_mappings[0]);
if ((drive_mapping_idx + 1) < arr_size)
drive_mapping_idx++;
return drive_mappings[drive_mapping_idx];
}
#if defined(_XBOX1)
#define HARDCODE_FONT_SIZE 21
#define FONT_SIZE_VARIABLE FONT_SIZE
#define POSITION_X 60
#define POSITION_X_CENTER (POSITION_X + 350)
#define POSITION_Y_START 80
#define Y_POSITION 430
#define POSITION_Y_BEGIN (POSITION_Y_START + POSITION_Y_INCREMENT)
#define POSITION_Y_INCREMENT 20
#define COMMENT_POSITION_Y (Y_POSITION - ((POSITION_Y_INCREMENT/2) * 3))
#define CORE_MSG_POSITION_X FONT_SIZE
#define CORE_MSG_POSITION_Y (MSG_PREV_NEXT_Y_POSITION + 0.01f)
#define CORE_MSG_FONT_SIZE FONT_SIZE
#define MSG_QUEUE_X_POSITION POSITION_X
#define MSG_QUEUE_Y_POSITION (Y_POSITION - ((POSITION_Y_INCREMENT/2) * 7) + 10)
#define MSG_QUEUE_FONT_SIZE HARDCODE_FONT_SIZE
#define MSG_PREV_NEXT_Y_POSITION 24
#define CURRENT_PATH_Y_POSITION (POSITION_Y_START - ((POSITION_Y_INCREMENT/2)))
#define CURRENT_PATH_FONT_SIZE 21
#define FONT_SIZE 21
#define NUM_ENTRY_PER_PAGE 15
#elif defined(__CELLOS_LV2__)
#define HARDCODE_FONT_SIZE 0.91f
#define FONT_SIZE_VARIABLE g_settings.video.font_size
#define POSITION_X 0.09f
#define POSITION_X_CENTER 0.5f
#define POSITION_Y_START 0.17f
#define POSITION_Y_INCREMENT 0.035f
#define POSITION_Y_BEGIN (POSITION_Y_START + POSITION_Y_INCREMENT)
#define COMMENT_POSITION_Y 0.82f
#define CORE_MSG_POSITION_X 0.3f
#define CORE_MSG_POSITION_Y 0.06f
#define CORE_MSG_FONT_SIZE COMMENT_POSITION_Y
#define MSG_QUEUE_X_POSITION g_settings.video.msg_pos_x
#define MSG_QUEUE_Y_POSITION 0.90f
#define MSG_QUEUE_FONT_SIZE 1.03f
#define MSG_PREV_NEXT_Y_POSITION 0.03f
#define CURRENT_PATH_Y_POSITION 0.15f
#define CURRENT_PATH_FONT_SIZE (g_settings.video.font_size)
#define NUM_ENTRY_PER_PAGE 18
#endif
#ifdef HAVE_SHADER_MANAGER
static void shader_manager_get_str_filter(char *type_str,
size_t sizeof_type_str, unsigned pass)
{
switch (rgui->shader.pass[pass].filter)
{
case RARCH_FILTER_LINEAR:
strlcpy(type_str, "Linear", sizeof_type_str);
break;
case RARCH_FILTER_NEAREST:
strlcpy(type_str, "Nearest", sizeof_type_str);
break;
case RARCH_FILTER_UNSPEC:
strlcpy(type_str, "Don't care", sizeof_type_str);
break;
}
}
#endif
/*============================================================
MENU STACK
============================================================ */
static unsigned char menu_stack_enum_array[10];
static unsigned stack_idx = 0;
static unsigned shader_choice_set_shader_slot = 0;
static unsigned setting_page_number = 0;
static void menu_stack_pop(unsigned menu_type)
{
switch(menu_type)
{
case LIBRETRO_CHOICE:
case INGAME_MENU_CORE_OPTIONS:
case INGAME_MENU_LOAD_GAME_HISTORY:
case INGAME_MENU_CUSTOM_RATIO:
case INGAME_MENU_SCREENSHOT:
rgui->frame_buf_show = true;
break;
case INGAME_MENU_SETTINGS:
case INGAME_MENU_VIDEO_OPTIONS:
case INGAME_MENU_AUDIO_OPTIONS:
case INGAME_MENU_INPUT_OPTIONS:
case INGAME_MENU_PATH_OPTIONS:
rgui->selection_ptr = FIRST_INGAME_MENU_SETTING;
rgui->frame_buf_show = true;
break;
case INGAME_MENU_SHADER_OPTIONS:
rgui->selection_ptr = FIRST_VIDEO_SETTING;
break;
#ifdef HAVE_SHADER_MANAGER
case CGP_CHOICE:
rgui->selection_ptr = FIRST_SHADERMAN_SETTING;
break;
#endif
default:
break;
}
setting_page_number = 0;
if (rgui->browser->prev_dir.directory_path[0] != '\0')
{
memcpy(&rgui->browser->current_dir, &rgui->browser->prev_dir, sizeof(*(&rgui->browser->current_dir)));
filebrowser_iterate(rgui->browser, FILEBROWSER_ACTION_RESET_CURRENT_DIR);
rgui->browser->current_dir.ptr = rgui->browser->prev_dir.ptr;
strlcpy(rgui->browser->current_dir.path, rgui->browser->prev_dir.path,
sizeof(rgui->browser->current_dir.path));
memset(&rgui->browser->prev_dir, 0, sizeof(*(&rgui->browser->prev_dir)));
}
if (stack_idx > 1)
stack_idx--;
}
static void menu_stack_push(unsigned menu_type, bool prev_dir)
{
switch (menu_type)
{
case INGAME_MENU:
rgui->selection_ptr = FIRST_INGAME_MENU_SETTING;
break;
case INGAME_MENU_VIDEO_OPTIONS:
rgui->selection_ptr = FIRST_VIDEO_SETTING;
break;
#ifdef HAVE_SHADER_MANAGER
case INGAME_MENU_SHADER_OPTIONS:
rgui->selection_ptr = FIRST_SHADERMAN_SETTING;
break;
#endif
case INGAME_MENU_AUDIO_OPTIONS:
rgui->selection_ptr = FIRST_AUDIO_SETTING;
break;
case INGAME_MENU_INPUT_OPTIONS:
rgui->selection_ptr = FIRST_CONTROLS_SETTING_PAGE_1;
break;
case INGAME_MENU_PATH_OPTIONS:
rgui->selection_ptr = FIRST_PATH_SETTING;
break;
case INGAME_MENU_SETTINGS:
rgui->selection_ptr = FIRST_SETTING;
break;
default:
break;
}
setting_page_number = 0;
if (prev_dir)
{
memcpy(&rgui->browser->prev_dir, &rgui->browser->current_dir, sizeof(*(&rgui->browser->prev_dir)));
rgui->browser->prev_dir.ptr = rgui->browser->current_dir.ptr;
}
menu_stack_enum_array[stack_idx] = menu_type;
stack_idx++;
}
/*============================================================
EVENT CALLBACKS (AND RELATED)
============================================================ */
static void render_text(void *data)
{
rgui_handle_t *rgui = (rgui_handle_t*)data;
font_params_t font_parms = {0};
char msg[128];
char label[64];
font_parms.x = POSITION_X;
font_parms.y = CURRENT_PATH_Y_POSITION;
font_parms.scale = CURRENT_PATH_FONT_SIZE;
font_parms.color = WHITE;
switch(rgui->menu_type)
{
#ifdef HAVE_SHADER_MANAGER
case SHADER_CHOICE:
case CGP_CHOICE:
#endif
case BORDER_CHOICE:
case LIBRETRO_CHOICE:
case PATH_SAVESTATES_DIR_CHOICE:
case PATH_DEFAULT_ROM_DIR_CHOICE:
#ifdef HAVE_XML
case PATH_CHEATS_DIR_CHOICE:
#endif
case PATH_SRAM_DIR_CHOICE:
case PATH_SYSTEM_DIR_CHOICE:
case FILE_BROWSER_MENU:
if (rgui->menu_type == LIBRETRO_CHOICE)
strlcpy(label, "CORE SELECTION", sizeof(label));
else
strlcpy(label, "PATH", sizeof(label));
snprintf(msg, sizeof(msg), "%s %s", label, rgui->browser->current_dir.directory_path);
break;
case INGAME_MENU_LOAD_GAME_HISTORY:
strlcpy(msg, "LOAD HISTORY", sizeof(msg));
break;
case INGAME_MENU:
strlcpy(msg, "MENU", sizeof(msg));
break;
case INGAME_MENU_CORE_OPTIONS:
strlcpy(msg, "CORE OPTIONS", sizeof(msg));
break;
case INGAME_MENU_VIDEO_OPTIONS:
case INGAME_MENU_VIDEO_OPTIONS_MODE:
strlcpy(msg, "VIDEO OPTIONS", sizeof(msg));
break;
#ifdef HAVE_SHADER_MANAGER
case INGAME_MENU_SHADER_OPTIONS:
case INGAME_MENU_SHADER_OPTIONS_MODE:
strlcpy(msg, "SHADER OPTIONS", sizeof(msg));
break;
#endif
case INGAME_MENU_INPUT_OPTIONS:
case INGAME_MENU_INPUT_OPTIONS_MODE:
strlcpy(msg, "INPUT OPTIONS", sizeof(msg));
break;
case INGAME_MENU_CUSTOM_RATIO:
strlcpy(msg, "CUSTOM RATIO", sizeof(msg));
break;
case INGAME_MENU_SETTINGS:
case INGAME_MENU_SETTINGS_MODE:
strlcpy(msg, "MENU SETTINGS", sizeof(msg));
break;
case INGAME_MENU_AUDIO_OPTIONS:
case INGAME_MENU_AUDIO_OPTIONS_MODE:
strlcpy(msg, "AUDIO OPTIONS", sizeof(msg));
break;
case INGAME_MENU_PATH_OPTIONS:
case INGAME_MENU_PATH_OPTIONS_MODE:
strlcpy(msg, "PATH OPTIONS", sizeof(msg));
break;
}
if (driver.video_poke->set_osd_msg && msg[0] != '\0')
driver.video_poke->set_osd_msg(driver.video_data, msg, &font_parms);
font_parms.x = POSITION_X;
font_parms.y = CORE_MSG_POSITION_Y;
font_parms.scale = CORE_MSG_FONT_SIZE;
font_parms.color = WHITE;
snprintf(msg, sizeof(msg), "%s - %s %s", PACKAGE_VERSION, rgui->info.library_name, rgui->info.library_version);
if (driver.video_poke->set_osd_msg)
driver.video_poke->set_osd_msg(driver.video_data, msg, &font_parms);
bool render_browser = false;
bool render_ingame_menu_resize = false;
switch(rgui->menu_type)
{
case FILE_BROWSER_MENU:
case LIBRETRO_CHOICE:
#ifdef HAVE_SHADER_MANAGER
case CGP_CHOICE:
case SHADER_CHOICE:
#endif
case BORDER_CHOICE:
case PATH_DEFAULT_ROM_DIR_CHOICE:
case PATH_SAVESTATES_DIR_CHOICE:
case PATH_SRAM_DIR_CHOICE:
#ifdef HAVE_XML
case PATH_CHEATS_DIR_CHOICE:
#endif
case PATH_SYSTEM_DIR_CHOICE:
render_browser = true;
break;
case INGAME_MENU_CUSTOM_RATIO:
render_ingame_menu_resize = true;
break;
}
if (render_browser)
{
font_params_t font_parms = {0};
font_parms.scale = FONT_SIZE_VARIABLE;
if (rgui->browser->list->size)
{
unsigned file_count = rgui->browser->list->size;
unsigned current_index = 0;
unsigned page_number = 0;
unsigned page_base = 0;
unsigned i;
float y_increment = POSITION_Y_START;
current_index = rgui->browser->current_dir.ptr;
page_number = current_index / NUM_ENTRY_PER_PAGE;
page_base = page_number * NUM_ENTRY_PER_PAGE;
for (i = page_base; i < file_count && i < page_base + NUM_ENTRY_PER_PAGE; ++i)
{
char fname_tmp[128];
fill_pathname_base(fname_tmp, rgui->browser->list->elems[i].data, sizeof(fname_tmp));
y_increment += POSITION_Y_INCREMENT;
#ifdef HAVE_MENU_PANEL
//check if this is the currently selected file
if (strcmp(rgui->browser->current_dir.path, rgui->browser->list->elems[i].data) == 0)
menu_panel->y = y_increment;
#endif
font_parms.x = POSITION_X;
font_parms.y = y_increment;
font_parms.color = i == current_index ? YELLOW : rgui->browser->list->elems[i].attr.b ? GREEN : WHITE;
if (driver.video_poke->set_osd_msg)
driver.video_poke->set_osd_msg(driver.video_data, fname_tmp, &font_parms);
}
}
else
{
char entry[128];
font_parms.x = POSITION_X;
font_parms.y = POSITION_Y_START + POSITION_Y_INCREMENT;
font_parms.color = WHITE;
strlcpy(entry, "No entries available.", sizeof(entry));
if (driver.video_poke->set_osd_msg)
driver.video_poke->set_osd_msg(driver.video_data, entry, &font_parms);
}
}
if (render_ingame_menu_resize && rgui->frame_buf_show)
{
char viewport[32];
snprintf(viewport, sizeof(viewport), "Viewport X: #%d Y: %d (%dx%d)", g_extern.console.screen.viewports.custom_vp.x, g_extern.console.screen.viewports.custom_vp.y, g_extern.console.screen.viewports.custom_vp.width,
g_extern.console.screen.viewports.custom_vp.height);
font_parms.x = POSITION_X;
font_parms.y = POSITION_Y_BEGIN;
font_parms.scale = HARDCODE_FONT_SIZE;
font_parms.color = WHITE;
if (driver.video_poke->set_osd_msg)
driver.video_poke->set_osd_msg(driver.video_data, viewport, &font_parms);
}
}
static int select_file(void *data, uint64_t action)
{
rgui_handle_t *rgui = (rgui_handle_t*)data;
char path[PATH_MAX];
bool pop_menu_stack = false;
switch (action)
{
case RGUI_ACTION_OK:
if (filebrowser_iterate(rgui->browser, FILEBROWSER_ACTION_PATH_ISDIR))
{
if (!filebrowser_iterate(rgui->browser, FILEBROWSER_ACTION_OK))
{
RARCH_ERR("Failed to open directory.\n");
msg_queue_push(g_extern.msg_queue, "ERROR - Failed to open directory.", 1, 180);
}
}
else
{
strlcpy(path, rgui->browser->current_dir.path, sizeof(path));
switch(rgui->menu_type)
{
#ifdef HAVE_SHADER_MANAGER
case SHADER_CHOICE:
strlcpy(rgui->shader.pass[shader_choice_set_shader_slot].source.cg, path,
sizeof(rgui->shader.pass[shader_choice_set_shader_slot].source.cg));
break;
case CGP_CHOICE:
{
config_file_t *conf = NULL;
strlcpy(g_settings.video.shader_path, path, sizeof(g_settings.video.shader_path));
conf = config_file_new(path);
if (conf)
gfx_shader_read_conf_cgp(conf, &rgui->shader);
config_file_free(conf);
if (video_set_shader_func(RARCH_SHADER_CG, path))
g_settings.video.shader_enable = true;
else
{
RARCH_ERR("Setting CGP failed.\n");
g_settings.video.shader_enable = false;
}
}
break;
#endif
case BORDER_CHOICE:
if (menu_texture)
{
#ifdef _XBOX
if (menu_texture->vertex_buf)
{
menu_texture->vertex_buf->Release();
menu_texture->vertex_buf = NULL;
}
if (menu_texture->pixels)
{
menu_texture->pixels->Release();
menu_texture->pixels = NULL;
}
#else
free(menu_texture->pixels);
menu_texture->pixels = NULL;
#endif
menu_texture = (struct texture_image*)calloc(1, sizeof(*menu_texture));
}
texture_image_load(path, menu_texture);
strlcpy(g_extern.menu_texture_path, path, sizeof(g_extern.menu_texture_path));
if (driver.video_poke && driver.video_poke->set_texture_frame)
driver.video_poke->set_texture_frame(driver.video_data, menu_texture->pixels,
true, menu_texture->width, menu_texture->height, 1.0f);
break;
case LIBRETRO_CHOICE:
rarch_environment_cb(RETRO_ENVIRONMENT_SET_LIBRETRO_PATH, (void*)path);
g_extern.lifecycle_mode_state |= (1ULL << MODE_EXITSPAWN);
return -1;
case FILE_BROWSER_MENU:
strlcpy(g_extern.fullpath, path, sizeof(g_extern.fullpath));
g_extern.lifecycle_mode_state |= (1ULL << MODE_LOAD_GAME);
return -1;
}
pop_menu_stack = true;
}
break;
case RGUI_ACTION_CANCEL:
if (rgui->menu_type == LIBRETRO_CHOICE)
pop_menu_stack = true;
else
{
char tmp_str[PATH_MAX];
fill_pathname_parent_dir(tmp_str, rgui->browser->current_dir.directory_path, sizeof(tmp_str));
if (tmp_str[0] == '\0')
pop_menu_stack = true;
}
break;
case RGUI_ACTION_MAPPING_PREVIOUS:
if (rgui->menu_type == FILE_BROWSER_MENU)
{
const char * drive_map = menu_drive_mapping_previous();
if (drive_map != NULL)
filebrowser_set_root_and_ext(rgui->browser, rgui->browser->current_dir.extensions, drive_map);
}
break;
case RGUI_ACTION_MAPPING_NEXT:
if (rgui->menu_type == FILE_BROWSER_MENU)
{
const char * drive_map = menu_drive_mapping_next();
if (drive_map != NULL)
filebrowser_set_root_and_ext(rgui->browser, rgui->browser->current_dir.extensions, drive_map);
}
break;
}
if (pop_menu_stack)
menu_stack_pop(rgui->menu_type);
return 0;
}
static int select_directory(void *data, uint64_t action)
{
rgui_handle_t *rgui = (rgui_handle_t*)data;
char path[PATH_MAX];
(void)path;
bool ret = true;
bool is_dir = filebrowser_iterate(rgui->browser, FILEBROWSER_ACTION_PATH_ISDIR);
bool pop_menu_stack = false;
switch (action)
{
case RGUI_ACTION_OK:
#if 1
if (is_dir)
ret = filebrowser_iterate(rgui->browser, FILEBROWSER_ACTION_OK);
#else
/* TODO - extra conditional needed here to recognize if user pressed