From a2e1d622e1245c5935f2db98fecf4febddea39c4 Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Fri, 17 Jan 2020 16:32:54 +0000 Subject: [PATCH] Add disk control interface API extension --- core.h | 4 +- libretro-common/include/libretro.h | 132 ++++++++++++++++++++++++++--- menu/cbs/menu_cbs_get_value.c | 6 +- menu/cbs/menu_cbs_ok.c | 4 +- menu/menu_displaylist.c | 55 +++++++++--- retroarch.c | 81 ++++++++++++------ 6 files changed, 228 insertions(+), 54 deletions(-) diff --git a/core.h b/core.h index 29fc95767d..c2c090b72e 100644 --- a/core.h +++ b/core.h @@ -67,8 +67,8 @@ typedef struct rarch_system_info bool supports_vfs; - struct retro_disk_control_callback disk_control_cb; - struct retro_location_callback location_cb; + struct retro_disk_control_ext_callback disk_control_cb; + struct retro_location_callback location_cb; struct { diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 2b7fc95a76..4ddb6e89ea 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -1117,7 +1117,7 @@ enum retro_mod * This may be still be done regardless of the core options * interface version. * - * If version is 1 however, core options may instead be set by + * If version is >= 1 however, core options may instead be set by * passing an array of retro_core_option_definition structs to * RETRO_ENVIRONMENT_SET_CORE_OPTIONS, or a 2D array of * retro_core_option_definition structs to RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL. @@ -1132,8 +1132,8 @@ enum retro_mod * GET_VARIABLE. * This allows the frontend to present these variables to * a user dynamically. - * This should only be called if RETRO_ENVIRONMENT_GET_ENHANCED_CORE_OPTIONS - * returns an API version of 1. + * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION + * returns an API version of >= 1. * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. * This should be called the first time as early as * possible (ideally in retro_set_environment). @@ -1169,8 +1169,6 @@ enum retro_mod * i.e. it should be feasible to cycle through options * without a keyboard. * - * First entry should be treated as a default. - * * Example entry: * { * "foo_option", @@ -1196,8 +1194,8 @@ enum retro_mod * GET_VARIABLE. * This allows the frontend to present these variables to * a user dynamically. - * This should only be called if RETRO_ENVIRONMENT_GET_ENHANCED_CORE_OPTIONS - * returns an API version of 1. + * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION + * returns an API version of >= 1. * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. * This should be called the first time as early as * possible (ideally in retro_set_environment). @@ -1257,7 +1255,38 @@ enum retro_mod * * 'data' points to an unsigned variable */ - + +#define RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION 57 + /* unsigned * -- + * Unsigned value is the API version number of the disk control + * interface supported by the frontend. If callback return false, + * API version is assumed to be 0. + * + * In legacy code, the disk control interface is defined by passing + * a struct of type retro_disk_control_callback to + * RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE. + * This may be still be done regardless of the disk control + * interface version. + * + * If version is >= 1 however, the disk control interface may + * instead be defined by passing a struct of type + * retro_disk_control_ext_callback to + * RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE. + * This allows the core to provide additional information about + * disk images to the frontend and/or enables extra + * disk control functionality by the frontend. + */ + +#define RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE 58 + /* const struct retro_disk_control_ext_callback * -- + * Sets an interface which frontend can use to eject and insert + * disk images, and also obtain information about individual + * disk image files registered by the core. + * This is used for games which consist of multiple images and + * must be manually swapped out by the user (e.g. PSX, floppy disk + * based systems). + */ + /* VFS functionality */ /* File paths: @@ -2307,7 +2336,8 @@ struct retro_keyboard_callback retro_keyboard_event_t callback; }; -/* Callbacks for RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE. +/* Callbacks for RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE & + * RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE. * Should be set for implementations which can swap out multiple disk * images in runtime. * @@ -2365,6 +2395,53 @@ typedef bool (RETRO_CALLCONV *retro_replace_image_index_t)(unsigned index, * with replace_image_index. */ typedef bool (RETRO_CALLCONV *retro_add_image_index_t)(void); +/* Sets initial image to insert in drive when calling + * core_load_game(). + * Since we cannot pass the initial index when loading + * content (this would require a major API change), this + * is set by the frontend *before* calling the core's + * retro_load_game()/retro_load_game_special() implementation. + * A core should therefore cache the index/path values and handle + * them inside retro_load_game()/retro_load_game_special(). + * - If 'index' is invalid (index >= get_num_images()), the + * core should ignore the set value and instead use 0 + * - 'path' is used purely for error checking - i.e. when + * content is loaded, the core should verify that the + * disk specified by 'index' has the specified file path. + * This is to guard against auto selecting the wrong image + * if (for example) the user should modify an existing M3U + * playlist. We have to let the core handle this because + * set_initial_image() must be called before loading content, + * i.e. the frontend cannot access image paths in advance + * and thus cannot perform the error check itself. + * If set path and content path do not match, the core should + * ignore the set 'index' value and instead use 0 + * Returns 'false' if index or 'path' are invalid, or core + * does not support this functionality + */ +typedef bool (RETRO_CALLCONV *retro_set_initial_image_t)(unsigned index, const char *path); + +/* Fetches the path of the specified disk image file. + * Returns 'false' if index is invalid (index >= get_num_images()) + * or path is otherwise unavailable. + */ +typedef bool (RETRO_CALLCONV *retro_get_image_path_t)(unsigned index, char *path, size_t len); + +/* Fetches a core-provided 'label' for the specified disk + * image file. In the simplest case this may be a file name + * (without extension), but for cores with more complex + * content requirements information may be provided to + * facilitate user disk swapping - for example, a core + * running floppy-disk-based content may uniquely label + * save disks, data disks, level disks, etc. with names + * corresponding to in-game disk change prompts (so the + * frontend can provide better user guidance than a 'dumb' + * disk index value). + * Returns 'false' if index is invalid (index >= get_num_images()) + * or label is otherwise unavailable. + */ +typedef bool (RETRO_CALLCONV *retro_get_image_label_t)(unsigned index, char *label, size_t len); + struct retro_disk_control_callback { retro_set_eject_state_t set_eject_state; @@ -2378,6 +2455,27 @@ struct retro_disk_control_callback retro_add_image_index_t add_image_index; }; +struct retro_disk_control_ext_callback +{ + retro_set_eject_state_t set_eject_state; + retro_get_eject_state_t get_eject_state; + + retro_get_image_index_t get_image_index; + retro_set_image_index_t set_image_index; + retro_get_num_images_t get_num_images; + + retro_replace_image_index_t replace_image_index; + retro_add_image_index_t add_image_index; + + /* NOTE: Frontend will only attempt to record/restore + * last used disk index if both set_initial_image() + * and get_image_path() are implemented */ + retro_set_initial_image_t set_initial_image; /* Optional - may be NULL */ + + retro_get_image_path_t get_image_path; /* Optional - may be NULL */ + retro_get_image_label_t get_image_label; /* Optional - may be NULL */ +}; + enum retro_pixel_format { /* 0RGB1555, native endian. @@ -2522,8 +2620,20 @@ struct retro_core_option_display }; /* Maximum number of values permitted for a core option - * NOTE: This may be increased on a core-by-core basis - * if required (doing so has no effect on the frontend) */ + * > Note: We have to set a maximum value due the limitations + * of the C language - i.e. it is not possible to create an + * array of structs each containing a variable sized array, + * so the retro_core_option_definition values array must + * have a fixed size. The size limit of 128 is a balancing + * act - it needs to be large enough to support all 'sane' + * core options, but setting it too large may impact low memory + * platforms. In practise, if a core option has more than + * 128 values then the implementation is likely flawed. + * To quote the above API reference: + * "The number of possible options should be very limited + * i.e. it should be feasible to cycle through options + * without a keyboard." + */ #define RETRO_NUM_CORE_OPTION_VALUES_MAX 128 struct retro_core_option_value diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 139f8316f9..fea114f782 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -762,9 +762,9 @@ static void menu_action_setting_disp_set_label_menu_disk_index( const char *path, char *s2, size_t len2) { - unsigned images = 0, current = 0; - struct retro_disk_control_callback *control = NULL; - rarch_system_info_t *system = runloop_get_system_info(); + unsigned images = 0, current = 0; + struct retro_disk_control_ext_callback *control = NULL; + rarch_system_info_t *system = runloop_get_system_info(); if (!system) return; diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 2486a83280..a00f1049c9 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -6115,8 +6115,8 @@ static int action_ok_disk_cycle_tray_status(const char *path, /* Get disk eject state *before* toggling drive status */ if (sys_info) { - const struct retro_disk_control_callback *control = - (const struct retro_disk_control_callback*) + const struct retro_disk_control_ext_callback *control = + (const struct retro_disk_control_ext_callback*) &sys_info->disk_control_cb; if (control) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 940ea71a65..c914248e23 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3242,17 +3242,17 @@ static unsigned menu_displaylist_parse_content_information( static unsigned menu_displaylist_parse_disk_options( file_list_t *list) { - unsigned count = 0; - rarch_system_info_t *sys_info = + unsigned count = 0; + rarch_system_info_t *sys_info = runloop_get_system_info(); - const struct retro_disk_control_callback *control = NULL; + const struct retro_disk_control_ext_callback *control = NULL; bool disk_ejected; /* Sanity Check */ if (!sys_info) return count; - control = (const struct retro_disk_control_callback*) + control = (const struct retro_disk_control_ext_callback*) &sys_info->disk_control_cb; if (!control || @@ -5356,8 +5356,8 @@ unsigned menu_displaylist_build_list(file_list_t *list, enum menu_displaylist_ct if (sys_info) { - const struct retro_disk_control_callback *control = - (const struct retro_disk_control_callback*) + const struct retro_disk_control_ext_callback *control = + (const struct retro_disk_control_ext_callback*) &sys_info->disk_control_cb; /* Check that the required disk control interface @@ -5368,21 +5368,56 @@ unsigned menu_displaylist_build_list(file_list_t *list, enum menu_displaylist_ct { unsigned num_images = control->get_num_images(); unsigned current_image = control->get_image_index(); + unsigned num_digits = 0; unsigned i; + /* If core supports labels, index value string + * should be padded to maximum width (otherwise + * labels will be misaligned/ugly) */ + if (control->get_image_label) + { + unsigned digit_counter = num_images; + do + { + num_digits++; + digit_counter = digit_counter / 10; + } + while (digit_counter > 0); + } + /* Loop through disk images */ for (i = 0; i < num_images; i++) { - char current_image_str[256]; + char current_image_str[PATH_MAX_LENGTH]; + char image_label[PATH_MAX_LENGTH]; current_image_str[0] = '\0'; + image_label[0] = '\0'; + + /* Get image label, if supported by core */ + if (control->get_image_label) + if (!control->get_image_label(i, image_label, sizeof(image_label))) + image_label[0] = '\0'; /* Get string representation of disk index * > Note that displayed index starts at '1', * not '0' */ - snprintf( - current_image_str, sizeof(current_image_str), - "%u", i + 1); + if (!string_is_empty(image_label)) + { + /* Note: 2-space gap is intentional + * (for clarity) */ + int n = snprintf( + current_image_str, sizeof(current_image_str), + "%0*u: %s", num_digits, i + 1, image_label); + + /* Suppress GCC warnings... */ + if ((n < 0) || (n >= PATH_MAX_LENGTH)) + n = 0; + } + else + snprintf( + current_image_str, sizeof(current_image_str), + "%0*u", num_digits, i + 1); /* Add menu entry */ if (menu_entries_append_enum(list, diff --git a/retroarch.c b/retroarch.c index b89de3520f..bc98ec6d2c 100644 --- a/retroarch.c +++ b/retroarch.c @@ -5231,14 +5231,14 @@ finish: static void command_event_disk_control_set_eject(bool new_state, bool print_log) { char msg[128]; - bool error = false; - const struct retro_disk_control_callback *control = NULL; - rarch_system_info_t *info = &runloop_system; + bool error = false; + const struct retro_disk_control_ext_callback *control = NULL; + rarch_system_info_t *info = &runloop_system; msg[0] = '\0'; if (info) - control = (const struct retro_disk_control_callback*)&info->disk_control_cb; + control = (const struct retro_disk_control_ext_callback*)&info->disk_control_cb; if (!control || !control->get_num_images) return; @@ -5285,14 +5285,14 @@ static void command_event_disk_control_set_index(unsigned idx, bool print_log) { unsigned num_disks; char msg[128]; - bool error = false; - const struct retro_disk_control_callback *control = NULL; - rarch_system_info_t *info = &runloop_system; + bool error = false; + const struct retro_disk_control_ext_callback *control = NULL; + rarch_system_info_t *info = &runloop_system; msg[0] = '\0'; if (info) - control = (const struct retro_disk_control_callback*)&info->disk_control_cb; + control = (const struct retro_disk_control_ext_callback*)&info->disk_control_cb; if (!control || !control->get_num_images) return; @@ -5349,10 +5349,10 @@ static bool command_event_disk_control_append_image(const char *path) { unsigned new_idx; char msg[128]; - struct retro_game_info info = {0}; - const struct retro_disk_control_callback *control = NULL; - rarch_system_info_t *sysinfo = &runloop_system; - const char *disk_filename = NULL; + struct retro_game_info info = {0}; + const struct retro_disk_control_ext_callback *control = NULL; + rarch_system_info_t *sysinfo = &runloop_system; + const char *disk_filename = NULL; msg[0] = '\0'; @@ -5365,7 +5365,7 @@ static bool command_event_disk_control_append_image(const char *path) return false; if (sysinfo) - control = (const struct retro_disk_control_callback*) + control = (const struct retro_disk_control_ext_callback*) &sysinfo->disk_control_cb; if (!control) @@ -5419,7 +5419,7 @@ static bool command_event_disk_control_append_image(const char *path) * Perform disk cycle to previous index action (Core Disk Options). **/ static void command_event_check_disk_prev( - const struct retro_disk_control_callback *control, bool print_log) + const struct retro_disk_control_ext_callback *control, bool print_log) { unsigned num_disks = 0; unsigned current = 0; @@ -5453,7 +5453,7 @@ static void command_event_check_disk_prev( * Perform disk cycle to next index action (Core Disk Options). **/ static void command_event_check_disk_next( - const struct retro_disk_control_callback *control, bool print_log) + const struct retro_disk_control_ext_callback *control, bool print_log) { unsigned num_disks = 0; unsigned current = 0; @@ -7587,8 +7587,8 @@ TODO: Add a setting for these tweaks */ if (info && info->disk_control_cb.get_num_images) { - const struct retro_disk_control_callback *control = - (const struct retro_disk_control_callback*) + const struct retro_disk_control_ext_callback *control = + (const struct retro_disk_control_ext_callback*) &info->disk_control_cb; if (control) @@ -7624,8 +7624,8 @@ TODO: Add a setting for these tweaks */ if (info && info->disk_control_cb.get_num_images) { - const struct retro_disk_control_callback *control = - (const struct retro_disk_control_callback*) + const struct retro_disk_control_ext_callback *control = + (const struct retro_disk_control_ext_callback*) &info->disk_control_cb; bool print_log = true; @@ -7654,8 +7654,8 @@ TODO: Add a setting for these tweaks */ if (info && info->disk_control_cb.get_num_images) { - const struct retro_disk_control_callback *control = - (const struct retro_disk_control_callback*) + const struct retro_disk_control_ext_callback *control = + (const struct retro_disk_control_ext_callback*) &info->disk_control_cb; bool print_log = true; @@ -7687,8 +7687,8 @@ TODO: Add a setting for these tweaks */ if (info && info->disk_control_cb.get_num_images) { - const struct retro_disk_control_callback *control = - (const struct retro_disk_control_callback*) + const struct retro_disk_control_ext_callback *control = + (const struct retro_disk_control_ext_callback*) &info->disk_control_cb; if (!control) @@ -9803,13 +9803,42 @@ static bool rarch_environment_cb(unsigned cmd, void *data) break; } + case RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION: + /* Current API version is 1 */ + *(unsigned *)data = 1; + break; + case RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE: - RARCH_LOG("[Environ]: SET_DISK_CONTROL_INTERFACE.\n"); + { + const struct retro_disk_control_callback *control_cb = + (const struct retro_disk_control_callback*)data; + + if (control_cb && system) + { + RARCH_LOG("[Environ]: SET_DISK_CONTROL_INTERFACE.\n"); + + system->disk_control_cb.set_eject_state = control_cb->set_eject_state; + system->disk_control_cb.get_eject_state = control_cb->get_eject_state; + system->disk_control_cb.get_image_index = control_cb->get_image_index; + system->disk_control_cb.set_image_index = control_cb->set_image_index; + system->disk_control_cb.get_num_images = control_cb->get_num_images; + system->disk_control_cb.replace_image_index = control_cb->replace_image_index; + system->disk_control_cb.add_image_index = control_cb->add_image_index; + + system->disk_control_cb.set_initial_image = NULL; + system->disk_control_cb.get_image_path = NULL; + system->disk_control_cb.get_image_label = NULL; + } + } + break; + + case RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE: + RARCH_LOG("[Environ]: SET_DISK_CONTROL_EXT_INTERFACE.\n"); if (system) system->disk_control_cb = - *(const struct retro_disk_control_callback*)data; + *(const struct retro_disk_control_ext_callback*)data; break; - + case RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER: { unsigned *cb = (unsigned*)data;