diff --git a/libretro/libretro-common/include/libretro.h b/libretro/libretro-common/include/libretro.h index c2cbba6..076dd25 100644 --- a/libretro/libretro-common/include/libretro.h +++ b/libretro/libretro-common/include/libretro.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2018 The RetroArch team +/* Copyright (C) 2010-2020 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro API header (libretro.h). @@ -69,7 +69,7 @@ extern "C" { # endif # endif # else -# if defined(__GNUC__) && __GNUC__ >= 4 && !defined(__CELLOS_LV2__) +# if defined(__GNUC__) && __GNUC__ >= 4 # define RETRO_API RETRO_CALLCONV __attribute__((__visibility__("default"))) # else # define RETRO_API RETRO_CALLCONV @@ -128,7 +128,8 @@ extern "C" { /* LIGHTGUN device is similar to Guncon-2 for PlayStation 2. * It reports X/Y coordinates in screen space (similar to the pointer) - * in the range [-0x8000, 0x7fff] in both axes, with zero being center. + * in the range [-0x8000, 0x7fff] in both axes, with zero being center and + * -0x8000 being out of bounds. * As well as reporting on/off screen state. It features a trigger, * start/select buttons, auxiliary action buttons and a * directional pad. A forced off-screen shot can be requested for @@ -139,7 +140,8 @@ extern "C" { /* The ANALOG device is an extension to JOYPAD (RetroPad). * Similar to DualShock2 it adds two analog sticks and all buttons can * be analog. This is treated as a separate device type as it returns - * axis values in the full analog range of [-0x8000, 0x7fff]. + * axis values in the full analog range of [-0x7fff, 0x7fff], + * although some devices may return -0x8000. * Positive X axis is right. Positive Y axis is down. * Buttons are returned in the range [0, 0x7fff]. * Only use ANALOG type when polling for analog values. @@ -200,6 +202,8 @@ extern "C" { #define RETRO_DEVICE_ID_JOYPAD_L3 14 #define RETRO_DEVICE_ID_JOYPAD_R3 15 +#define RETRO_DEVICE_ID_JOYPAD_MASK 256 + /* Index / Id values for ANALOG device. */ #define RETRO_DEVICE_INDEX_ANALOG_LEFT 0 #define RETRO_DEVICE_INDEX_ANALOG_RIGHT 1 @@ -246,6 +250,7 @@ extern "C" { #define RETRO_DEVICE_ID_POINTER_X 0 #define RETRO_DEVICE_ID_POINTER_Y 1 #define RETRO_DEVICE_ID_POINTER_PRESSED 2 +#define RETRO_DEVICE_ID_POINTER_COUNT 3 /* Returned from retro_get_region(). */ #define RETRO_REGION_NTSC 0 @@ -271,6 +276,13 @@ enum retro_language RETRO_LANGUAGE_POLISH = 14, RETRO_LANGUAGE_VIETNAMESE = 15, RETRO_LANGUAGE_ARABIC = 16, + RETRO_LANGUAGE_GREEK = 17, + RETRO_LANGUAGE_TURKISH = 18, + RETRO_LANGUAGE_SLOVAK = 19, + RETRO_LANGUAGE_PERSIAN = 20, + RETRO_LANGUAGE_HEBREW = 21, + RETRO_LANGUAGE_ASTURIAN = 22, + RETRO_LANGUAGE_FINNISH = 23, RETRO_LANGUAGE_LAST, /* Ensure sizeof(enum) == sizeof(int) */ @@ -482,11 +494,13 @@ enum retro_mod /* Environment commands. */ #define RETRO_ENVIRONMENT_SET_ROTATION 1 /* const unsigned * -- * Sets screen rotation of graphics. - * Is only implemented if rotation can be accelerated by hardware. * Valid values are 0, 1, 2, 3, which rotates screen by 0, 90, 180, * 270 degrees counter-clockwise respectively. */ #define RETRO_ENVIRONMENT_GET_OVERSCAN 2 /* bool * -- + * NOTE: As of 2019 this callback is considered deprecated in favor of + * using core options to manage overscan in a more nuanced, core-specific way. + * * Boolean value whether or not the implementation should use overscan, * or crop away overscan. */ @@ -605,7 +619,7 @@ enum retro_mod * Afterward it may be called again for the core to communicate * updated options to the frontend, but the number of core * options must not change from the number in the initial call. - * + * * 'data' points to an array of retro_variable structs * terminated by a { NULL, NULL } element. * retro_variable::key should be namespaced to not collide @@ -699,6 +713,9 @@ enum retro_mod * state of rumble motors in controllers. * A strong and weak motor is supported, and they can be * controlled indepedently. + * Should be called from either retro_init() or retro_load_game(). + * Should not be called from retro_set_environment(). + * Returns false if rumble functionality is unavailable. */ #define RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES 24 /* uint64_t * -- @@ -872,12 +889,12 @@ enum retro_mod * types already defined in the libretro API. * * The core must pass an array of const struct retro_controller_info which - * is terminated with a blanked out struct. Each element of the + * is terminated with a blanked out struct. Each element of the * retro_controller_info struct corresponds to the ascending port index * that is passed to retro_set_controller_port_device() when that function * is called to indicate to the core that the frontend has changed the * active device subclass. SEE ALSO: retro_set_controller_port_device() - * + * * The ascending input port indexes provided by the core in the struct * are generally presented by frontends as ascending User # or Player #, * such as Player 1, Player 2, Player 3, etc. Which device subclasses are @@ -889,7 +906,7 @@ enum retro_mod * User or Player, beginning with the generic Libretro device that the * subclasses are derived from. The second inner element of each entry is the * total number of subclasses that are listed in the retro_controller_description. - * + * * NOTE: Even if special device types are set in the libretro core, * libretro should only poll input based on the base input device types. */ @@ -1076,10 +1093,629 @@ enum retro_mod * fastforwarding mode. */ +#define RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE (50 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* float * -- + * Float value that lets us know what target refresh rate + * is curently in use by the frontend. + * + * The core can use the returned value to set an ideal + * refresh rate/framerate. + */ + +#define RETRO_ENVIRONMENT_GET_INPUT_BITMASKS (51 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* bool * -- + * Boolean value that indicates whether or not the frontend supports + * input bitmasks being returned by retro_input_state_t. The advantage + * of this is that retro_input_state_t has to be only called once to + * grab all button states instead of multiple times. + * + * If it returns true, you can pass RETRO_DEVICE_ID_JOYPAD_MASK as 'id' + * to retro_input_state_t (make sure 'device' is set to RETRO_DEVICE_JOYPAD). + * It will return a bitmask of all the digital buttons. + */ + +#define RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION 52 + /* unsigned * -- + * Unsigned value is the API version number of the core options + * interface supported by the frontend. If callback return false, + * API version is assumed to be 0. + * + * In legacy code, core options are set by passing an array of + * retro_variable structs to RETRO_ENVIRONMENT_SET_VARIABLES. + * 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 + * 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. + * This allows the core to additionally set option sublabel information + * and/or provide localisation support. + * + * If version is >= 2, core options may instead be set by passing + * a retro_core_options_v2 struct to RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2, + * or an array of retro_core_options_v2 structs to + * RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL. This allows the core + * to additionally set optional core option category information + * for frontends with core option category support. + */ + +#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS 53 + /* const struct retro_core_option_definition ** -- + * Allows an implementation to signal the environment + * which variables it might want to check for later using + * GET_VARIABLE. + * This allows the frontend to present these variables to + * a user dynamically. + * 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). + * Afterwards it may be called again for the core to communicate + * updated options to the frontend, but the number of core + * options must not change from the number in the initial call. + * + * 'data' points to an array of retro_core_option_definition structs + * terminated by a { NULL, NULL, NULL, {{0}}, NULL } element. + * retro_core_option_definition::key should be namespaced to not collide + * with other implementations' keys. e.g. A core called + * 'foo' should use keys named as 'foo_option'. + * retro_core_option_definition::desc should contain a human readable + * description of the key. + * retro_core_option_definition::info should contain any additional human + * readable information text that a typical user may need to + * understand the functionality of the option. + * retro_core_option_definition::values is an array of retro_core_option_value + * structs terminated by a { NULL, NULL } element. + * > retro_core_option_definition::values[index].value is an expected option + * value. + * > retro_core_option_definition::values[index].label is a human readable + * label used when displaying the value on screen. If NULL, + * the value itself is used. + * retro_core_option_definition::default_value is the default core option + * setting. It must match one of the expected option values in the + * retro_core_option_definition::values array. If it does not, or the + * default value is NULL, the first entry in the + * retro_core_option_definition::values array is treated as the default. + * + * The number of possible option values should be very limited, + * and must be less than RETRO_NUM_CORE_OPTION_VALUES_MAX. + * i.e. it should be feasible to cycle through options + * without a keyboard. + * + * Example entry: + * { + * "foo_option", + * "Speed hack coprocessor X", + * "Provides increased performance at the expense of reduced accuracy", + * { + * { "false", NULL }, + * { "true", NULL }, + * { "unstable", "Turbo (Unstable)" }, + * { NULL, NULL }, + * }, + * "false" + * } + * + * Only strings are operated on. The possible values will + * generally be displayed and stored as-is by the frontend. + */ + +#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL 54 + /* const struct retro_core_options_intl * -- + * Allows an implementation to signal the environment + * which variables it might want to check for later using + * GET_VARIABLE. + * This allows the frontend to present these variables to + * a user dynamically. + * 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 instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS. + * This should be called the first time as early as + * possible (ideally in retro_set_environment). + * Afterwards it may be called again for the core to communicate + * updated options to the frontend, but the number of core + * options must not change from the number in the initial call. + * + * This is fundamentally the same as RETRO_ENVIRONMENT_SET_CORE_OPTIONS, + * with the addition of localisation support. The description of the + * RETRO_ENVIRONMENT_SET_CORE_OPTIONS callback should be consulted + * for further details. + * + * 'data' points to a retro_core_options_intl struct. + * + * retro_core_options_intl::us is a pointer to an array of + * retro_core_option_definition structs defining the US English + * core options implementation. It must point to a valid array. + * + * retro_core_options_intl::local is a pointer to an array of + * retro_core_option_definition structs defining core options for + * the current frontend language. It may be NULL (in which case + * retro_core_options_intl::us is used by the frontend). Any items + * missing from this array will be read from retro_core_options_intl::us + * instead. + * + * NOTE: Default core option values are always taken from the + * retro_core_options_intl::us array. Any default values in + * retro_core_options_intl::local array will be ignored. + */ + +#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY 55 + /* struct retro_core_option_display * -- + * + * Allows an implementation to signal the environment to show + * or hide a variable when displaying core options. This is + * considered a *suggestion*. The frontend is free to ignore + * this callback, and its implementation not considered mandatory. + * + * 'data' points to a retro_core_option_display struct + * + * retro_core_option_display::key is a variable identifier + * which has already been set by SET_VARIABLES/SET_CORE_OPTIONS. + * + * retro_core_option_display::visible is a boolean, specifying + * whether variable should be displayed + * + * Note that all core option variables will be set visible by + * default when calling SET_VARIABLES/SET_CORE_OPTIONS. + */ + +#define RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER 56 + /* unsigned * -- + * + * Allows an implementation to ask frontend preferred hardware + * context to use. Core should use this information to deal + * with what specific context to request with SET_HW_RENDER. + * + * '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). + */ + +#define RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION 59 + /* unsigned * -- + * Unsigned value is the API version number of the message + * interface supported by the frontend. If callback returns + * false, API version is assumed to be 0. + * + * In legacy code, messages may be displayed in an + * implementation-specific manner by passing a struct + * of type retro_message to RETRO_ENVIRONMENT_SET_MESSAGE. + * This may be still be done regardless of the message + * interface version. + * + * If version is >= 1 however, messages may instead be + * displayed by passing a struct of type retro_message_ext + * to RETRO_ENVIRONMENT_SET_MESSAGE_EXT. This allows the + * core to specify message logging level, priority and + * destination (OSD, logging interface or both). + */ + +#define RETRO_ENVIRONMENT_SET_MESSAGE_EXT 60 + /* const struct retro_message_ext * -- + * Sets a message to be displayed in an implementation-specific + * manner for a certain amount of 'frames'. Additionally allows + * the core to specify message logging level, priority and + * destination (OSD, logging interface or both). + * Should not be used for trivial messages, which should simply be + * logged via RETRO_ENVIRONMENT_GET_LOG_INTERFACE (or as a + * fallback, stderr). + */ + +#define RETRO_ENVIRONMENT_GET_INPUT_MAX_USERS 61 + /* unsigned * -- + * Unsigned value is the number of active input devices + * provided by the frontend. This may change between + * frames, but will remain constant for the duration + * of each frame. + * If callback returns true, a core need not poll any + * input device with an index greater than or equal to + * the number of active devices. + * If callback returns false, the number of active input + * devices is unknown. In this case, all input devices + * should be considered active. + */ + +#define RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK 62 + /* const struct retro_audio_buffer_status_callback * -- + * Lets the core know the occupancy level of the frontend + * audio buffer. Can be used by a core to attempt frame + * skipping in order to avoid buffer under-runs. + * A core may pass NULL to disable buffer status reporting + * in the frontend. + */ + +#define RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY 63 + /* const unsigned * -- + * Sets minimum frontend audio latency in milliseconds. + * Resultant audio latency may be larger than set value, + * or smaller if a hardware limit is encountered. A frontend + * is expected to honour requests up to 512 ms. + * + * - If value is less than current frontend + * audio latency, callback has no effect + * - If value is zero, default frontend audio + * latency is set + * + * May be used by a core to increase audio latency and + * therefore decrease the probability of buffer under-runs + * (crackling) when performing 'intensive' operations. + * A core utilising RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK + * to implement audio-buffer-based frame skipping may achieve + * optimal results by setting the audio latency to a 'high' + * (typically 6x or 8x) integer multiple of the expected + * frame time. + * + * WARNING: This can only be called from within retro_run(). + * Calling this can require a full reinitialization of audio + * drivers in the frontend, so it is important to call it very + * sparingly, and usually only with the users explicit consent. + * An eventual driver reinitialize will happen so that audio + * callbacks happening after this call within the same retro_run() + * call will target the newly initialized driver. + */ + +#define RETRO_ENVIRONMENT_SET_FASTFORWARDING_OVERRIDE 64 + /* const struct retro_fastforwarding_override * -- + * Used by a libretro core to override the current + * fastforwarding mode of the frontend. + * If NULL is passed to this function, the frontend + * will return true if fastforwarding override + * functionality is supported (no change in + * fastforwarding state will occur in this case). + */ + +#define RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE 65 + /* const struct retro_system_content_info_override * -- + * Allows an implementation to override 'global' content + * info parameters reported by retro_get_system_info(). + * Overrides also affect subsystem content info parameters + * set via RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO. + * This function must be called inside retro_set_environment(). + * If callback returns false, content info overrides + * are unsupported by the frontend, and will be ignored. + * If callback returns true, extended game info may be + * retrieved by calling RETRO_ENVIRONMENT_GET_GAME_INFO_EXT + * in retro_load_game() or retro_load_game_special(). + * + * 'data' points to an array of retro_system_content_info_override + * structs terminated by a { NULL, false, false } element. + * If 'data' is NULL, no changes will be made to the frontend; + * a core may therefore pass NULL in order to test whether + * the RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE and + * RETRO_ENVIRONMENT_GET_GAME_INFO_EXT callbacks are supported + * by the frontend. + * + * For struct member descriptions, see the definition of + * struct retro_system_content_info_override. + * + * Example: + * + * - struct retro_system_info: + * { + * "My Core", // library_name + * "v1.0", // library_version + * "m3u|md|cue|iso|chd|sms|gg|sg", // valid_extensions + * true, // need_fullpath + * false // block_extract + * } + * + * - Array of struct retro_system_content_info_override: + * { + * { + * "md|sms|gg", // extensions + * false, // need_fullpath + * true // persistent_data + * }, + * { + * "sg", // extensions + * false, // need_fullpath + * false // persistent_data + * }, + * { NULL, false, false } + * } + * + * Result: + * - Files of type m3u, cue, iso, chd will not be + * loaded by the frontend. Frontend will pass a + * valid path to the core, and core will handle + * loading internally + * - Files of type md, sms, gg will be loaded by + * the frontend. A valid memory buffer will be + * passed to the core. This memory buffer will + * remain valid until retro_deinit() returns + * - Files of type sg will be loaded by the frontend. + * A valid memory buffer will be passed to the core. + * This memory buffer will remain valid until + * retro_load_game() (or retro_load_game_special()) + * returns + * + * NOTE: If an extension is listed multiple times in + * an array of retro_system_content_info_override + * structs, only the first instance will be registered + */ + +#define RETRO_ENVIRONMENT_GET_GAME_INFO_EXT 66 + /* const struct retro_game_info_ext ** -- + * Allows an implementation to fetch extended game + * information, providing additional content path + * and memory buffer status details. + * This function may only be called inside + * retro_load_game() or retro_load_game_special(). + * If callback returns false, extended game information + * is unsupported by the frontend. In this case, only + * regular retro_game_info will be available. + * RETRO_ENVIRONMENT_GET_GAME_INFO_EXT is guaranteed + * to return true if RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE + * returns true. + * + * 'data' points to an array of retro_game_info_ext structs. + * + * For struct member descriptions, see the definition of + * struct retro_game_info_ext. + * + * - If function is called inside retro_load_game(), + * the retro_game_info_ext array is guaranteed to + * have a size of 1 - i.e. the returned pointer may + * be used to access directly the members of the + * first retro_game_info_ext struct, for example: + * + * struct retro_game_info_ext *game_info_ext; + * if (environ_cb(RETRO_ENVIRONMENT_GET_GAME_INFO_EXT, &game_info_ext)) + * printf("Content Directory: %s\n", game_info_ext->dir); + * + * - If the function is called inside retro_load_game_special(), + * the retro_game_info_ext array is guaranteed to have a + * size equal to the num_info argument passed to + * retro_load_game_special() + */ + +#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2 67 + /* const struct retro_core_options_v2 * -- + * Allows an implementation to signal the environment + * which variables it might want to check for later using + * GET_VARIABLE. + * This allows the frontend to present these variables to + * a user dynamically. + * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION + * returns an API version of >= 2. + * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. + * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS. + * This should be called the first time as early as + * possible (ideally in retro_set_environment). + * Afterwards it may be called again for the core to communicate + * updated options to the frontend, but the number of core + * options must not change from the number in the initial call. + * If RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION returns an API + * version of >= 2, this callback is guaranteed to succeed + * (i.e. callback return value does not indicate success) + * If callback returns true, frontend has core option category + * support. + * If callback returns false, frontend does not have core option + * category support. + * + * 'data' points to a retro_core_options_v2 struct, containing + * of two pointers: + * - retro_core_options_v2::categories is an array of + * retro_core_option_v2_category structs terminated by a + * { NULL, NULL, NULL } element. If retro_core_options_v2::categories + * is NULL, all core options will have no category and will be shown + * at the top level of the frontend core option interface. If frontend + * does not have core option category support, categories array will + * be ignored. + * - retro_core_options_v2::definitions is an array of + * retro_core_option_v2_definition structs terminated by a + * { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL } + * element. + * + * >> retro_core_option_v2_category notes: + * + * - retro_core_option_v2_category::key should contain string + * that uniquely identifies the core option category. Valid + * key characters are [a-z, A-Z, 0-9, _, -] + * Namespace collisions with other implementations' category + * keys are permitted. + * - retro_core_option_v2_category::desc should contain a human + * readable description of the category key. + * - retro_core_option_v2_category::info should contain any + * additional human readable information text that a typical + * user may need to understand the nature of the core option + * category. + * + * Example entry: + * { + * "advanced_settings", + * "Advanced", + * "Options affecting low-level emulation performance and accuracy." + * } + * + * >> retro_core_option_v2_definition notes: + * + * - retro_core_option_v2_definition::key should be namespaced to not + * collide with other implementations' keys. e.g. A core called + * 'foo' should use keys named as 'foo_option'. Valid key characters + * are [a-z, A-Z, 0-9, _, -]. + * - retro_core_option_v2_definition::desc should contain a human readable + * description of the key. Will be used when the frontend does not + * have core option category support. Examples: "Aspect Ratio" or + * "Video > Aspect Ratio". + * - retro_core_option_v2_definition::desc_categorized should contain a + * human readable description of the key, which will be used when + * frontend has core option category support. Example: "Aspect Ratio", + * where associated retro_core_option_v2_category::desc is "Video". + * If empty or NULL, the string specified by + * retro_core_option_v2_definition::desc will be used instead. + * retro_core_option_v2_definition::desc_categorized will be ignored + * if retro_core_option_v2_definition::category_key is empty or NULL. + * - retro_core_option_v2_definition::info should contain any additional + * human readable information text that a typical user may need to + * understand the functionality of the option. + * - retro_core_option_v2_definition::info_categorized should contain + * any additional human readable information text that a typical user + * may need to understand the functionality of the option, and will be + * used when frontend has core option category support. This is provided + * to accommodate the case where info text references an option by + * name/desc, and the desc/desc_categorized text for that option differ. + * If empty or NULL, the string specified by + * retro_core_option_v2_definition::info will be used instead. + * retro_core_option_v2_definition::info_categorized will be ignored + * if retro_core_option_v2_definition::category_key is empty or NULL. + * - retro_core_option_v2_definition::category_key should contain a + * category identifier (e.g. "video" or "audio") that will be + * assigned to the core option if frontend has core option category + * support. A categorized option will be shown in a subsection/ + * submenu of the frontend core option interface. If key is empty + * or NULL, or if key does not match one of the + * retro_core_option_v2_category::key values in the associated + * retro_core_option_v2_category array, option will have no category + * and will be shown at the top level of the frontend core option + * interface. + * - retro_core_option_v2_definition::values is an array of + * retro_core_option_value structs terminated by a { NULL, NULL } + * element. + * --> retro_core_option_v2_definition::values[index].value is an + * expected option value. + * --> retro_core_option_v2_definition::values[index].label is a + * human readable label used when displaying the value on screen. + * If NULL, the value itself is used. + * - retro_core_option_v2_definition::default_value is the default + * core option setting. It must match one of the expected option + * values in the retro_core_option_v2_definition::values array. If + * it does not, or the default value is NULL, the first entry in the + * retro_core_option_v2_definition::values array is treated as the + * default. + * + * The number of possible option values should be very limited, + * and must be less than RETRO_NUM_CORE_OPTION_VALUES_MAX. + * i.e. it should be feasible to cycle through options + * without a keyboard. + * + * Example entries: + * + * - Uncategorized: + * + * { + * "foo_option", + * "Speed hack coprocessor X", + * NULL, + * "Provides increased performance at the expense of reduced accuracy.", + * NULL, + * NULL, + * { + * { "false", NULL }, + * { "true", NULL }, + * { "unstable", "Turbo (Unstable)" }, + * { NULL, NULL }, + * }, + * "false" + * } + * + * - Categorized: + * + * { + * "foo_option", + * "Advanced > Speed hack coprocessor X", + * "Speed hack coprocessor X", + * "Setting 'Advanced > Speed hack coprocessor X' to 'true' or 'Turbo' provides increased performance at the expense of reduced accuracy", + * "Setting 'Speed hack coprocessor X' to 'true' or 'Turbo' provides increased performance at the expense of reduced accuracy", + * "advanced_settings", + * { + * { "false", NULL }, + * { "true", NULL }, + * { "unstable", "Turbo (Unstable)" }, + * { NULL, NULL }, + * }, + * "false" + * } + * + * Only strings are operated on. The possible values will + * generally be displayed and stored as-is by the frontend. + */ + +#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL 68 + /* const struct retro_core_options_v2_intl * -- + * Allows an implementation to signal the environment + * which variables it might want to check for later using + * GET_VARIABLE. + * This allows the frontend to present these variables to + * a user dynamically. + * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION + * returns an API version of >= 2. + * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. + * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS. + * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL. + * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2. + * This should be called the first time as early as + * possible (ideally in retro_set_environment). + * Afterwards it may be called again for the core to communicate + * updated options to the frontend, but the number of core + * options must not change from the number in the initial call. + * If RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION returns an API + * version of >= 2, this callback is guaranteed to succeed + * (i.e. callback return value does not indicate success) + * If callback returns true, frontend has core option category + * support. + * If callback returns false, frontend does not have core option + * category support. + * + * This is fundamentally the same as RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2, + * with the addition of localisation support. The description of the + * RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2 callback should be consulted + * for further details. + * + * 'data' points to a retro_core_options_v2_intl struct. + * + * - retro_core_options_v2_intl::us is a pointer to a + * retro_core_options_v2 struct defining the US English + * core options implementation. It must point to a valid struct. + * + * - retro_core_options_v2_intl::local is a pointer to a + * retro_core_options_v2 struct defining core options for + * the current frontend language. It may be NULL (in which case + * retro_core_options_v2_intl::us is used by the frontend). Any items + * missing from this struct will be read from + * retro_core_options_v2_intl::us instead. + * + * NOTE: Default core option values are always taken from the + * retro_core_options_v2_intl::us struct. Any default values in + * the retro_core_options_v2_intl::local struct will be ignored. + */ + /* VFS functionality */ /* File paths: - * File paths passed as parameters when using this api shall be well formed unix-style, + * File paths passed as parameters when using this API shall be well formed UNIX-style, * using "/" (unquoted forward slash) as directory separator regardless of the platform's native separator. * Paths shall also include at least one forward slash ("game.bin" is an invalid path, use "./game.bin" instead). * Other than the directory separator, cores shall not make assumptions about path format: @@ -1097,6 +1733,10 @@ enum retro_mod * Introduced in VFS API v1 */ struct retro_vfs_file_handle; +/* Opaque directory handle + * Introduced in VFS API v3 */ +struct retro_vfs_dir_handle; + /* File open flags * Introduced in VFS API v1 */ #define RETRO_VFS_FILE_ACCESS_READ (1 << 0) /* Read only mode */ @@ -1116,6 +1756,12 @@ struct retro_vfs_file_handle; #define RETRO_VFS_SEEK_POSITION_CURRENT 1 #define RETRO_VFS_SEEK_POSITION_END 2 +/* stat() result flags + * Introduced in VFS API v3 */ +#define RETRO_VFS_STAT_IS_VALID (1 << 0) +#define RETRO_VFS_STAT_IS_DIRECTORY (1 << 1) +#define RETRO_VFS_STAT_IS_CHARACTER_SPECIAL (1 << 2) + /* Get path from opaque handle. Returns the exact same path passed to file_open when getting the handle * Introduced in VFS API v1 */ typedef const char *(RETRO_CALLCONV *retro_vfs_get_path_t)(struct retro_vfs_file_handle *stream); @@ -1125,7 +1771,7 @@ typedef const char *(RETRO_CALLCONV *retro_vfs_get_path_t)(struct retro_vfs_file * Introduced in VFS API v1 */ typedef struct retro_vfs_file_handle *(RETRO_CALLCONV *retro_vfs_open_t)(const char *path, unsigned mode, unsigned hints); -/* Close the file and release its resources. Must be called if open_file returns non-NULL. Returns 0 on succes, -1 on failure. +/* Close the file and release its resources. Must be called if open_file returns non-NULL. Returns 0 on success, -1 on failure. * Whether the call succeeds ot not, the handle passed as parameter becomes invalid and should no longer be used. * Introduced in VFS API v1 */ typedef int (RETRO_CALLCONV *retro_vfs_close_t)(struct retro_vfs_file_handle *stream); @@ -1138,7 +1784,7 @@ typedef int64_t (RETRO_CALLCONV *retro_vfs_size_t)(struct retro_vfs_file_handle * Introduced in VFS API v2 */ typedef int64_t (RETRO_CALLCONV *retro_vfs_truncate_t)(struct retro_vfs_file_handle *stream, int64_t length); -/* Get the current read / write position for the file. Returns - 1 for error. +/* Get the current read / write position for the file. Returns -1 for error. * Introduced in VFS API v1 */ typedef int64_t (RETRO_CALLCONV *retro_vfs_tell_t)(struct retro_vfs_file_handle *stream); @@ -1166,6 +1812,39 @@ typedef int (RETRO_CALLCONV *retro_vfs_remove_t)(const char *path); * Introduced in VFS API v1 */ typedef int (RETRO_CALLCONV *retro_vfs_rename_t)(const char *old_path, const char *new_path); +/* Stat the specified file. Retruns a bitmask of RETRO_VFS_STAT_* flags, none are set if path was not valid. + * Additionally stores file size in given variable, unless NULL is given. + * Introduced in VFS API v3 */ +typedef int (RETRO_CALLCONV *retro_vfs_stat_t)(const char *path, int32_t *size); + +/* Create the specified directory. Returns 0 on success, -1 on unknown failure, -2 if already exists. + * Introduced in VFS API v3 */ +typedef int (RETRO_CALLCONV *retro_vfs_mkdir_t)(const char *dir); + +/* Open the specified directory for listing. Returns the opaque dir handle, or NULL for error. + * Support for the include_hidden argument may vary depending on the platform. + * Introduced in VFS API v3 */ +typedef struct retro_vfs_dir_handle *(RETRO_CALLCONV *retro_vfs_opendir_t)(const char *dir, bool include_hidden); + +/* Read the directory entry at the current position, and move the read pointer to the next position. + * Returns true on success, false if already on the last entry. + * Introduced in VFS API v3 */ +typedef bool (RETRO_CALLCONV *retro_vfs_readdir_t)(struct retro_vfs_dir_handle *dirstream); + +/* Get the name of the last entry read. Returns a string on success, or NULL for error. + * The returned string pointer is valid until the next call to readdir or closedir. + * Introduced in VFS API v3 */ +typedef const char *(RETRO_CALLCONV *retro_vfs_dirent_get_name_t)(struct retro_vfs_dir_handle *dirstream); + +/* Check if the last entry read was a directory. Returns true if it was, false otherwise (or on error). + * Introduced in VFS API v3 */ +typedef bool (RETRO_CALLCONV *retro_vfs_dirent_is_dir_t)(struct retro_vfs_dir_handle *dirstream); + +/* Close the directory and release its resources. Must be called if opendir returns non-NULL. Returns 0 on success, -1 on failure. + * Whether the call succeeds ot not, the handle passed as parameter becomes invalid and should no longer be used. + * Introduced in VFS API v3 */ +typedef int (RETRO_CALLCONV *retro_vfs_closedir_t)(struct retro_vfs_dir_handle *dirstream); + struct retro_vfs_interface { /* VFS API v1 */ @@ -1182,6 +1861,14 @@ struct retro_vfs_interface retro_vfs_rename_t rename; /* VFS API v2 */ retro_vfs_truncate_t truncate; + /* VFS API v3 */ + retro_vfs_stat_t stat; + retro_vfs_mkdir_t mkdir; + retro_vfs_opendir_t opendir; + retro_vfs_readdir_t readdir; + retro_vfs_dirent_get_name_t dirent_get_name; + retro_vfs_dirent_is_dir_t dirent_is_dir; + retro_vfs_closedir_t closedir; }; struct retro_vfs_interface_info @@ -1204,6 +1891,7 @@ enum retro_hw_render_interface_type RETRO_HW_RENDER_INTERFACE_D3D10 = 2, RETRO_HW_RENDER_INTERFACE_D3D11 = 3, RETRO_HW_RENDER_INTERFACE_D3D12 = 4, + RETRO_HW_RENDER_INTERFACE_GSKIT_PS2 = 5, RETRO_HW_RENDER_INTERFACE_DUMMY = INT_MAX }; @@ -1289,14 +1977,17 @@ struct retro_hw_render_context_negotiation_interface * dependence */ #define RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT (1 << 6) -#define RETRO_MEMDESC_CONST (1 << 0) /* The frontend will never change this memory area once retro_load_game has returned. */ -#define RETRO_MEMDESC_BIGENDIAN (1 << 1) /* The memory area contains big endian data. Default is little endian. */ -#define RETRO_MEMDESC_ALIGN_2 (1 << 16) /* All memory access in this area is aligned to their own size, or 2, whichever is smaller. */ -#define RETRO_MEMDESC_ALIGN_4 (2 << 16) -#define RETRO_MEMDESC_ALIGN_8 (3 << 16) -#define RETRO_MEMDESC_MINSIZE_2 (1 << 24) /* All memory in this region is accessed at least 2 bytes at the time. */ -#define RETRO_MEMDESC_MINSIZE_4 (2 << 24) -#define RETRO_MEMDESC_MINSIZE_8 (3 << 24) +#define RETRO_MEMDESC_CONST (1 << 0) /* The frontend will never change this memory area once retro_load_game has returned. */ +#define RETRO_MEMDESC_BIGENDIAN (1 << 1) /* The memory area contains big endian data. Default is little endian. */ +#define RETRO_MEMDESC_SYSTEM_RAM (1 << 2) /* The memory area is system RAM. This is main RAM of the gaming system. */ +#define RETRO_MEMDESC_SAVE_RAM (1 << 3) /* The memory area is save RAM. This RAM is usually found on a game cartridge, backed up by a battery. */ +#define RETRO_MEMDESC_VIDEO_RAM (1 << 4) /* The memory area is video RAM (VRAM) */ +#define RETRO_MEMDESC_ALIGN_2 (1 << 16) /* All memory access in this area is aligned to their own size, or 2, whichever is smaller. */ +#define RETRO_MEMDESC_ALIGN_4 (2 << 16) +#define RETRO_MEMDESC_ALIGN_8 (3 << 16) +#define RETRO_MEMDESC_MINSIZE_2 (1 << 24) /* All memory in this region is accessed at least 2 bytes at the time. */ +#define RETRO_MEMDESC_MINSIZE_4 (2 << 24) +#define RETRO_MEMDESC_MINSIZE_8 (3 << 24) struct retro_memory_descriptor { uint64_t flags; @@ -1697,6 +2388,10 @@ enum retro_sensor_action { RETRO_SENSOR_ACCELEROMETER_ENABLE = 0, RETRO_SENSOR_ACCELEROMETER_DISABLE, + RETRO_SENSOR_GYROSCOPE_ENABLE, + RETRO_SENSOR_GYROSCOPE_DISABLE, + RETRO_SENSOR_ILLUMINANCE_ENABLE, + RETRO_SENSOR_ILLUMINANCE_DISABLE, RETRO_SENSOR_DUMMY = INT_MAX }; @@ -1705,6 +2400,10 @@ enum retro_sensor_action #define RETRO_SENSOR_ACCELEROMETER_X 0 #define RETRO_SENSOR_ACCELEROMETER_Y 1 #define RETRO_SENSOR_ACCELEROMETER_Z 2 +#define RETRO_SENSOR_GYROSCOPE_X 3 +#define RETRO_SENSOR_GYROSCOPE_Y 4 +#define RETRO_SENSOR_GYROSCOPE_Z 5 +#define RETRO_SENSOR_ILLUMINANCE 6 typedef bool (RETRO_CALLCONV *retro_set_sensor_state_t)(unsigned port, enum retro_sensor_action action, unsigned rate); @@ -1902,6 +2601,30 @@ struct retro_frame_time_callback retro_usec_t reference; }; +/* Notifies a libretro core of the current occupancy + * level of the frontend audio buffer. + * + * - active: 'true' if audio buffer is currently + * in use. Will be 'false' if audio is + * disabled in the frontend + * + * - occupancy: Given as a value in the range [0,100], + * corresponding to the occupancy percentage + * of the audio buffer + * + * - underrun_likely: 'true' if the frontend expects an + * audio buffer underrun during the + * next frame (indicates that a core + * should attempt frame skipping) + * + * It will be called right before retro_run() every frame. */ +typedef void (RETRO_CALLCONV *retro_audio_buffer_status_callback_t)( + bool active, unsigned occupancy, bool underrun_likely); +struct retro_audio_buffer_status_callback +{ + retro_audio_buffer_status_callback_t callback; +}; + /* Pass this to retro_video_refresh_t if rendering to hardware. * Passing NULL to retro_video_refresh_t is still a frame dupe as normal. * */ @@ -2062,7 +2785,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. * @@ -2120,6 +2844,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; @@ -2133,6 +2904,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. @@ -2163,6 +2955,104 @@ struct retro_message unsigned frames; /* Duration in frames of message. */ }; +enum retro_message_target +{ + RETRO_MESSAGE_TARGET_ALL = 0, + RETRO_MESSAGE_TARGET_OSD, + RETRO_MESSAGE_TARGET_LOG +}; + +enum retro_message_type +{ + RETRO_MESSAGE_TYPE_NOTIFICATION = 0, + RETRO_MESSAGE_TYPE_NOTIFICATION_ALT, + RETRO_MESSAGE_TYPE_STATUS, + RETRO_MESSAGE_TYPE_PROGRESS +}; + +struct retro_message_ext +{ + /* Message string to be displayed/logged */ + const char *msg; + /* Duration (in ms) of message when targeting the OSD */ + unsigned duration; + /* Message priority when targeting the OSD + * > When multiple concurrent messages are sent to + * the frontend and the frontend does not have the + * capacity to display them all, messages with the + * *highest* priority value should be shown + * > There is no upper limit to a message priority + * value (within the bounds of the unsigned data type) + * > In the reference frontend (RetroArch), the same + * priority values are used for frontend-generated + * notifications, which are typically assigned values + * between 0 and 3 depending upon importance */ + unsigned priority; + /* Message logging level (info, warn, error, etc.) */ + enum retro_log_level level; + /* Message destination: OSD, logging interface or both */ + enum retro_message_target target; + /* Message 'type' when targeting the OSD + * > RETRO_MESSAGE_TYPE_NOTIFICATION: Specifies that a + * message should be handled in identical fashion to + * a standard frontend-generated notification + * > RETRO_MESSAGE_TYPE_NOTIFICATION_ALT: Specifies that + * message is a notification that requires user attention + * or action, but that it should be displayed in a manner + * that differs from standard frontend-generated notifications. + * This would typically correspond to messages that should be + * displayed immediately (independently from any internal + * frontend message queue), and/or which should be visually + * distinguishable from frontend-generated notifications. + * For example, a core may wish to inform the user of + * information related to a disk-change event. It is + * expected that the frontend itself may provide a + * notification in this case; if the core sends a + * message of type RETRO_MESSAGE_TYPE_NOTIFICATION, an + * uncomfortable 'double-notification' may occur. A message + * of RETRO_MESSAGE_TYPE_NOTIFICATION_ALT should therefore + * be presented such that visual conflict with regular + * notifications does not occur + * > RETRO_MESSAGE_TYPE_STATUS: Indicates that message + * is not a standard notification. This typically + * corresponds to 'status' indicators, such as a core's + * internal FPS, which are intended to be displayed + * either permanently while a core is running, or in + * a manner that does not suggest user attention or action + * is required. 'Status' type messages should therefore be + * displayed in a different on-screen location and in a manner + * easily distinguishable from both standard frontend-generated + * notifications and messages of type RETRO_MESSAGE_TYPE_NOTIFICATION_ALT + * > RETRO_MESSAGE_TYPE_PROGRESS: Indicates that message reports + * the progress of an internal core task. For example, in cases + * where a core itself handles the loading of content from a file, + * this may correspond to the percentage of the file that has been + * read. Alternatively, an audio/video playback core may use a + * message of type RETRO_MESSAGE_TYPE_PROGRESS to display the current + * playback position as a percentage of the runtime. 'Progress' type + * messages should therefore be displayed as a literal progress bar, + * where: + * - 'retro_message_ext.msg' is the progress bar title/label + * - 'retro_message_ext.progress' determines the length of + * the progress bar + * NOTE: Message type is a *hint*, and may be ignored + * by the frontend. If a frontend lacks support for + * displaying messages via alternate means than standard + * frontend-generated notifications, it will treat *all* + * messages as having the type RETRO_MESSAGE_TYPE_NOTIFICATION */ + enum retro_message_type type; + /* Task progress when targeting the OSD and message is + * of type RETRO_MESSAGE_TYPE_PROGRESS + * > -1: Unmetered/indeterminate + * > 0-100: Current progress percentage + * NOTE: Since message type is a hint, a frontend may ignore + * progress values. Where relevant, a core should therefore + * include progress percentage within the message string, + * such that the message intent remains clear when displayed + * as a standard frontend-generated notification */ + int8_t progress; +}; + /* Describes how the libretro implementation maps a libretro input bind * to its internal input system through a human readable string. * This string can be used to better let a user configure input. */ @@ -2183,7 +3073,7 @@ struct retro_input_descriptor struct retro_system_info { /* All pointers are owned by libretro implementation, and pointers must - * remain valid until retro_deinit() is called. */ + * remain valid until it is unloaded. */ const char *library_name; /* Descriptive name of library. Should not * contain any version numbers, etc. */ @@ -2225,6 +3115,213 @@ struct retro_system_info bool block_extract; }; +/* Defines overrides which modify frontend handling of + * specific content file types. + * An array of retro_system_content_info_override is + * passed to RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE + * NOTE: In the following descriptions, references to + * retro_load_game() may be replaced with + * retro_load_game_special() */ +struct retro_system_content_info_override +{ + /* A list of file extensions for which the override + * should apply, delimited by a 'pipe' character + * (e.g. "md|sms|gg") + * Permitted file extensions are limited to those + * included in retro_system_info::valid_extensions + * and/or retro_subsystem_rom_info::valid_extensions */ + const char *extensions; + + /* Overrides the need_fullpath value set in + * retro_system_info and/or retro_subsystem_rom_info. + * To reiterate: + * + * If need_fullpath is true and retro_load_game() is called: + * - retro_game_info::path is guaranteed to contain a valid + * path to an existent file + * - retro_game_info::data and retro_game_info::size are invalid + * + * If need_fullpath is false and retro_load_game() is called: + * - retro_game_info::path may be NULL + * - retro_game_info::data and retro_game_info::size are guaranteed + * to be valid + * + * In addition: + * + * If need_fullpath is true and retro_load_game() is called: + * - retro_game_info_ext::full_path is guaranteed to contain a valid + * path to an existent file + * - retro_game_info_ext::archive_path may be NULL + * - retro_game_info_ext::archive_file may be NULL + * - retro_game_info_ext::dir is guaranteed to contain a valid path + * to the directory in which the content file exists + * - retro_game_info_ext::name is guaranteed to contain the + * basename of the content file, without extension + * - retro_game_info_ext::ext is guaranteed to contain the + * extension of the content file in lower case format + * - retro_game_info_ext::data and retro_game_info_ext::size + * are invalid + * + * If need_fullpath is false and retro_load_game() is called: + * - If retro_game_info_ext::file_in_archive is false: + * - retro_game_info_ext::full_path is guaranteed to contain + * a valid path to an existent file + * - retro_game_info_ext::archive_path may be NULL + * - retro_game_info_ext::archive_file may be NULL + * - retro_game_info_ext::dir is guaranteed to contain a + * valid path to the directory in which the content file exists + * - retro_game_info_ext::name is guaranteed to contain the + * basename of the content file, without extension + * - retro_game_info_ext::ext is guaranteed to contain the + * extension of the content file in lower case format + * - If retro_game_info_ext::file_in_archive is true: + * - retro_game_info_ext::full_path may be NULL + * - retro_game_info_ext::archive_path is guaranteed to + * contain a valid path to an existent compressed file + * inside which the content file is located + * - retro_game_info_ext::archive_file is guaranteed to + * contain a valid path to an existent content file + * inside the compressed file referred to by + * retro_game_info_ext::archive_path + * e.g. for a compressed file '/path/to/foo.zip' + * containing 'bar.sfc' + * > retro_game_info_ext::archive_path will be '/path/to/foo.zip' + * > retro_game_info_ext::archive_file will be 'bar.sfc' + * - retro_game_info_ext::dir is guaranteed to contain a + * valid path to the directory in which the compressed file + * (containing the content file) exists + * - retro_game_info_ext::name is guaranteed to contain + * EITHER + * 1) the basename of the compressed file (containing + * the content file), without extension + * OR + * 2) the basename of the content file inside the + * compressed file, without extension + * In either case, a core should consider 'name' to + * be the canonical name/ID of the the content file + * - retro_game_info_ext::ext is guaranteed to contain the + * extension of the content file inside the compressed file, + * in lower case format + * - retro_game_info_ext::data and retro_game_info_ext::size are + * guaranteed to be valid */ + bool need_fullpath; + + /* If need_fullpath is false, specifies whether the content + * data buffer available in retro_load_game() is 'persistent' + * + * If persistent_data is false and retro_load_game() is called: + * - retro_game_info::data and retro_game_info::size + * are valid only until retro_load_game() returns + * - retro_game_info_ext::data and retro_game_info_ext::size + * are valid only until retro_load_game() returns + * + * If persistent_data is true and retro_load_game() is called: + * - retro_game_info::data and retro_game_info::size + * are valid until retro_deinit() returns + * - retro_game_info_ext::data and retro_game_info_ext::size + * are valid until retro_deinit() returns */ + bool persistent_data; +}; + +/* Similar to retro_game_info, but provides extended + * information about the source content file and + * game memory buffer status. + * And array of retro_game_info_ext is returned by + * RETRO_ENVIRONMENT_GET_GAME_INFO_EXT + * NOTE: In the following descriptions, references to + * retro_load_game() may be replaced with + * retro_load_game_special() */ +struct retro_game_info_ext +{ + /* - If file_in_archive is false, contains a valid + * path to an existent content file (UTF-8 encoded) + * - If file_in_archive is true, may be NULL */ + const char *full_path; + + /* - If file_in_archive is false, may be NULL + * - If file_in_archive is true, contains a valid path + * to an existent compressed file inside which the + * content file is located (UTF-8 encoded) */ + const char *archive_path; + + /* - If file_in_archive is false, may be NULL + * - If file_in_archive is true, contain a valid path + * to an existent content file inside the compressed + * file referred to by archive_path (UTF-8 encoded) + * e.g. for a compressed file '/path/to/foo.zip' + * containing 'bar.sfc' + * > archive_path will be '/path/to/foo.zip' + * > archive_file will be 'bar.sfc' */ + const char *archive_file; + + /* - If file_in_archive is false, contains a valid path + * to the directory in which the content file exists + * (UTF-8 encoded) + * - If file_in_archive is true, contains a valid path + * to the directory in which the compressed file + * (containing the content file) exists (UTF-8 encoded) */ + const char *dir; + + /* Contains the canonical name/ID of the content file + * (UTF-8 encoded). Intended for use when identifying + * 'complementary' content named after the loaded file - + * i.e. companion data of a different format (a CD image + * required by a ROM), texture packs, internally handled + * save files, etc. + * - If file_in_archive is false, contains the basename + * of the content file, without extension + * - If file_in_archive is true, then string is + * implementation specific. A frontend may choose to + * set a name value of: + * EITHER + * 1) the basename of the compressed file (containing + * the content file), without extension + * OR + * 2) the basename of the content file inside the + * compressed file, without extension + * RetroArch sets the 'name' value according to (1). + * A frontend that supports routine loading of + * content from archives containing multiple unrelated + * content files may set the 'name' value according + * to (2). */ + const char *name; + + /* - If file_in_archive is false, contains the extension + * of the content file in lower case format + * - If file_in_archive is true, contains the extension + * of the content file inside the compressed file, + * in lower case format */ + const char *ext; + + /* String of implementation specific meta-data. */ + const char *meta; + + /* Memory buffer of loaded game content. Will be NULL: + * IF + * - retro_system_info::need_fullpath is true and + * retro_system_content_info_override::need_fullpath + * is unset + * OR + * - retro_system_content_info_override::need_fullpath + * is true */ + const void *data; + + /* Size of game content memory buffer, in bytes */ + size_t size; + + /* True if loaded content file is inside a compressed + * archive */ + bool file_in_archive; + + /* - If data is NULL, value is unset/ignored + * - If data is non-NULL: + * - If persistent_data is false, data and size are + * valid only until retro_load_game() returns + * - If persistent_data is true, data and size are + * are valid until retro_deinit() returns */ + bool persistent_data; +}; + struct retro_game_geometry { unsigned base_width; /* Nominal video width of game. */ @@ -2266,6 +3363,194 @@ struct retro_variable const char *value; }; +struct retro_core_option_display +{ + /* Variable to configure in RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY */ + const char *key; + + /* Specifies whether variable should be displayed + * when presenting core options to the user */ + bool visible; +}; + +/* Maximum number of values permitted for a core option + * > 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 +{ + /* Expected option value */ + const char *value; + + /* Human-readable value label. If NULL, value itself + * will be displayed by the frontend */ + const char *label; +}; + +struct retro_core_option_definition +{ + /* Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. */ + const char *key; + + /* Human-readable core option description (used as menu label) */ + const char *desc; + + /* Human-readable core option information (used as menu sublabel) */ + const char *info; + + /* Array of retro_core_option_value structs, terminated by NULL */ + struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX]; + + /* Default core option value. Must match one of the values + * in the retro_core_option_value array, otherwise will be + * ignored */ + const char *default_value; +}; + +struct retro_core_options_intl +{ + /* Pointer to an array of retro_core_option_definition structs + * - US English implementation + * - Must point to a valid array */ + struct retro_core_option_definition *us; + + /* Pointer to an array of retro_core_option_definition structs + * - Implementation for current frontend language + * - May be NULL */ + struct retro_core_option_definition *local; +}; + +struct retro_core_option_v2_category +{ + /* Variable uniquely identifying the + * option category. Valid key characters + * are [a-z, A-Z, 0-9, _, -] */ + const char *key; + + /* Human-readable category description + * > Used as category menu label when + * frontend has core option category + * support */ + const char *desc; + + /* Human-readable category information + * > Used as category menu sublabel when + * frontend has core option category + * support + * > Optional (may be NULL or an empty + * string) */ + const char *info; +}; + +struct retro_core_option_v2_definition +{ + /* Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. + * Valid key characters are [a-z, A-Z, 0-9, _, -] */ + const char *key; + + /* Human-readable core option description + * > Used as menu label when frontend does + * not have core option category support + * e.g. "Video > Aspect Ratio" */ + const char *desc; + + /* Human-readable core option description + * > Used as menu label when frontend has + * core option category support + * e.g. "Aspect Ratio", where associated + * retro_core_option_v2_category::desc + * is "Video" + * > If empty or NULL, the string specified by + * desc will be used as the menu label + * > Will be ignored (and may be set to NULL) + * if category_key is empty or NULL */ + const char *desc_categorized; + + /* Human-readable core option information + * > Used as menu sublabel */ + const char *info; + + /* Human-readable core option information + * > Used as menu sublabel when frontend + * has core option category support + * (e.g. may be required when info text + * references an option by name/desc, + * and the desc/desc_categorized text + * for that option differ) + * > If empty or NULL, the string specified by + * info will be used as the menu sublabel + * > Will be ignored (and may be set to NULL) + * if category_key is empty or NULL */ + const char *info_categorized; + + /* Variable specifying category (e.g. "video", + * "audio") that will be assigned to the option + * if frontend has core option category support. + * > Categorized options will be displayed in a + * subsection/submenu of the frontend core + * option interface + * > Specified string must match one of the + * retro_core_option_v2_category::key values + * in the associated retro_core_option_v2_category + * array; If no match is not found, specified + * string will be considered as NULL + * > If specified string is empty or NULL, option will + * have no category and will be shown at the top + * level of the frontend core option interface */ + const char *category_key; + + /* Array of retro_core_option_value structs, terminated by NULL */ + struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX]; + + /* Default core option value. Must match one of the values + * in the retro_core_option_value array, otherwise will be + * ignored */ + const char *default_value; +}; + +struct retro_core_options_v2 +{ + /* Array of retro_core_option_v2_category structs, + * terminated by NULL + * > If NULL, all entries in definitions array + * will have no category and will be shown at + * the top level of the frontend core option + * interface + * > Will be ignored if frontend does not have + * core option category support */ + struct retro_core_option_v2_category *categories; + + /* Array of retro_core_option_v2_definition structs, + * terminated by NULL */ + struct retro_core_option_v2_definition *definitions; +}; + +struct retro_core_options_v2_intl +{ + /* Pointer to a retro_core_options_v2 struct + * > US English implementation + * > Must point to a valid struct */ + struct retro_core_options_v2 *us; + + /* Pointer to a retro_core_options_v2 struct + * - Implementation for current frontend language + * - May be NULL */ + struct retro_core_options_v2 *local; +}; + struct retro_game_info { const char *path; /* Path to game, UTF-8 encoded. @@ -2312,6 +3597,47 @@ struct retro_framebuffer Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. */ }; +/* Used by a libretro core to override the current + * fastforwarding mode of the frontend */ +struct retro_fastforwarding_override +{ + /* Specifies the runtime speed multiplier that + * will be applied when 'fastforward' is true. + * For example, a value of 5.0 when running 60 FPS + * content will cap the fast-forward rate at 300 FPS. + * Note that the target multiplier may not be achieved + * if the host hardware has insufficient processing + * power. + * Setting a value of 0.0 (or greater than 0.0 but + * less than 1.0) will result in an uncapped + * fast-forward rate (limited only by hardware + * capacity). + * If the value is negative, it will be ignored + * (i.e. the frontend will use a runtime speed + * multiplier of its own choosing) */ + float ratio; + + /* If true, fastforwarding mode will be enabled. + * If false, fastforwarding mode will be disabled. */ + bool fastforward; + + /* If true, and if supported by the frontend, an + * on-screen notification will be displayed while + * 'fastforward' is true. + * If false, and if supported by the frontend, any + * on-screen fast-forward notifications will be + * suppressed */ + bool notification; + + /* If true, the core will have sole control over + * when fastforwarding mode is enabled/disabled; + * the frontend will not be able to change the + * state set by 'fastforward' until either + * 'inhibit_toggle' is set to false, or the core + * is unloaded */ + bool inhibit_toggle; +}; + /* Callbacks */ /* Environment callback. Gives implementations a way of performing diff --git a/libretro/libretro.c b/libretro/libretro.c index 546b266..b81cada 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -73,6 +73,8 @@ #include #include +#include "libretro_core_options.h" + #include "shared.h" #include "md_ntsc.h" #include "sms_ntsc.h" @@ -83,7 +85,9 @@ #define OVERCLOCK_FRAME_DELAY 100 #ifdef M68K_OVERCLOCK_SHIFT +#ifndef HAVE_OVERCLOCK #define HAVE_OVERCLOCK +#endif STATIC_ASSERT(m68k_overflow, M68K_MAX_CYCLES <= UINT_MAX >> (M68K_OVERCLOCK_SHIFT + 1)); #endif @@ -96,11 +100,11 @@ STATIC_ASSERT(z80_overflow, Z80_MAX_CYCLES <= UINT_MAX >> (Z80_OVERCLOCK_SHIFT + 1)); #endif +t_config config; + sms_ntsc_t *sms_ntsc; md_ntsc_t *md_ntsc; -t_config config; - char GG_ROM[256]; char AR_ROM[256]; char SK_ROM[256]; @@ -140,9 +144,9 @@ static bool restart_eq = false; static char g_rom_dir[256]; static char g_rom_name[256]; -static void *g_rom_data; -static size_t g_rom_size; -static char *save_dir; +static const void *g_rom_data = NULL; +static size_t g_rom_size = 0; +static char *save_dir = NULL; static retro_log_printf_t log_cb; static retro_video_refresh_t video_cb; @@ -151,6 +155,9 @@ static retro_input_state_t input_state_cb; static retro_environment_t environ_cb; static retro_audio_sample_batch_t audio_cb; +enum RetroLightgunInputModes{RetroLightgun, RetroPointer}; +static enum RetroLightgunInputModes retro_gun_mode = RetroLightgun; + /* Cheat Support */ #define MAX_CHEATS (150) @@ -182,11 +189,95 @@ static char arvalidchars[] = "0123456789ABCDEF"; static uint32_t overclock_delay; #endif +static bool libretro_supports_option_categories = false; +static bool libretro_supports_bitmasks = false; + #define SOUND_FREQUENCY 44100 /* Hide the EQ settings for now */ /*#define HAVE_EQ*/ +/* Frameskipping Support */ + +static unsigned frameskip_type = 0; +static unsigned frameskip_threshold = 0; +static uint16_t frameskip_counter = 0; + +static bool retro_audio_buff_active = false; +static unsigned retro_audio_buff_occupancy = 0; +static bool retro_audio_buff_underrun = false; +/* Maximum number of consecutive frames that + * can be skipped */ +#define FRAMESKIP_MAX 60 + +static unsigned audio_latency = 0; +static bool update_audio_latency = false; + +#ifdef USE_PER_SOUND_CHANNELS_CONFIG +static bool show_advanced_av_settings = true; +#endif + +static void retro_audio_buff_status_cb( + bool active, unsigned occupancy, bool underrun_likely) +{ + retro_audio_buff_active = active; + retro_audio_buff_occupancy = occupancy; + retro_audio_buff_underrun = underrun_likely; +} + +static void init_frameskip(void) +{ + if (frameskip_type > 0) + { + struct retro_audio_buffer_status_callback buf_status_cb; + + buf_status_cb.callback = retro_audio_buff_status_cb; + if (!environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, + &buf_status_cb)) + { + if (log_cb) + log_cb(RETRO_LOG_WARN, "Frameskip disabled - frontend does not support audio buffer status monitoring.\n"); + + retro_audio_buff_active = false; + retro_audio_buff_occupancy = 0; + retro_audio_buff_underrun = false; + audio_latency = 0; + } + else + { + /* Frameskip is enabled - increase frontend + * audio latency to minimise potential + * buffer underruns */ + float frames_per_sec; + float frame_time_msec; + + /* While system_clock and lines_per_frame will + * always be valid when this function is called, + * it is not guaranteed. Just add a fallback to + * prevent any possible divide-by-zero errors */ + if ((system_clock <= 0) || (lines_per_frame <= 0)) + frames_per_sec = 60.0f; + else + frames_per_sec = (float)system_clock / (float)lines_per_frame / (float)MCYCLES_PER_LINE; + + frame_time_msec = 1000.0f / frames_per_sec; + + /* Set latency to 6x current frame time... */ + audio_latency = (unsigned)((6.0f * frame_time_msec) + 0.5f); + + /* ...then round up to nearest multiple of 32 */ + audio_latency = (audio_latency + 0x1F) & ~0x1F; + } + } + else + { + environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL); + audio_latency = 0; + } + + update_audio_latency = true; +} + /************************************ * Genesis Plus GX implementation ************************************/ @@ -295,243 +386,481 @@ int load_archive(char *filename, unsigned char *buffer, int maxsize, char *exten static void RAMCheatUpdate(void); +static void osd_input_update_internal_bitmasks(void) +{ + int i, player = 0; + unsigned int temp; + int16_t ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); + + for (i = 0; i < MAX_INPUTS; i++) + { + temp = 0; + switch (input.dev[i]) + { + case DEVICE_PAD6B: + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_L)) + temp |= INPUT_X; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_X)) + temp |= INPUT_Y; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_R)) + temp |= INPUT_Z; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_SELECT)) + temp |= INPUT_MODE; + + case DEVICE_PAD3B: + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_Y)) + temp |= INPUT_A; + + case DEVICE_PAD2B: + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_B)) + temp |= INPUT_B; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_A)) + temp |= INPUT_C; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_START)) + temp |= INPUT_START; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_UP)) + temp |= INPUT_UP; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN)) + temp |= INPUT_DOWN; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT)) + temp |= INPUT_LEFT; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT)) + temp |= INPUT_RIGHT; + + player++; + ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); + break; + + case DEVICE_MOUSE: + input.analog[i][0] = input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X); + if (config.invert_mouse) + input.analog[i][1] = input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y); + else + input.analog[i][1] = -input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y); + + if (input.analog[i][0] < -255) + input.analog[i][0] = -255; + else if (input.analog[i][0] > 255) + input.analog[i][0] = 255; + if (input.analog[i][1] < -255) + input.analog[i][1] = -255; + else if (input.analog[i][1] > 255) + input.analog[i][1] = 255; + + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) + temp |= INPUT_MOUSE_LEFT; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) + temp |= INPUT_MOUSE_RIGHT; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN)) + temp |= INPUT_MOUSE_CENTER; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) + temp |= INPUT_START; + + player++; + ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); + break; + + case DEVICE_LIGHTGUN: + if ( retro_gun_mode == RetroPointer ) + { + int touch_count; + input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * bitmap.viewport.w) / 0xfffe; + input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * bitmap.viewport.h) / 0xfffe; + if (input_state_cb(player, RETRO_DEVICE_POINTER, 0, + RETRO_DEVICE_ID_POINTER_PRESSED)) + temp |= INPUT_A; + touch_count = input_state_cb(player, + RETRO_DEVICE_POINTER, 0, + RETRO_DEVICE_ID_POINTER_COUNT); + + if (touch_count == 2) + temp |= INPUT_B; + else if (touch_count == 3) + temp |= INPUT_START; + else if (touch_count == 4) + temp |= INPUT_C; + } + else + { + /* RetroLightgun is default */ + if ( input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) ) + { + input.analog[i][0] = -1000; + input.analog[i][1] = -1000; + } + else + { + input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) + 0x7fff) * bitmap.viewport.w) / 0xfffe; + input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y) + 0x7fff) * bitmap.viewport.h) / 0xfffe; + } + + if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER)) + temp |= INPUT_A; + if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A)) + temp |= INPUT_B; + if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_B)) + temp |= INPUT_C; + if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_START)) + temp |= INPUT_START; + } + + player++; + ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); + break; + + case DEVICE_PADDLE: + input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; + + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_B)) + temp |= INPUT_BUTTON1; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_START)) + temp |= INPUT_START; + + player++; + ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); + break; + + case DEVICE_SPORTSPAD: + input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; + input.analog[i][1] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_Y) + 0x8000) >> 8; + + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_B)) + temp |= INPUT_BUTTON1; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_A)) + temp |= INPUT_BUTTON2; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_START)) + temp |= INPUT_START; + + player++; + ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); + break; + + case DEVICE_PICO: + input.analog[i][0] = 0x03c + ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * (0x17c-0x03c)) / 0xfffe; + input.analog[i][1] = 0x1fc + ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * (0x2f7-0x1fc)) / 0xfffe; + + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) + temp |= INPUT_PICO_PEN; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) + temp |= INPUT_PICO_RED; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELUP)) + pico_current = (pico_current - 1) & 7; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN)) + pico_current = (pico_current + 1) & 7; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_UP)) + temp |= INPUT_UP; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN)) + temp |= INPUT_DOWN; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT)) + temp |= INPUT_LEFT; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT)) + temp |= INPUT_RIGHT; + + player++; + ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); + break; + + case DEVICE_TEREBI: + input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * 250) / 0xfffe; + input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * 250) / 0xfffe; + + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) + temp |= INPUT_BUTTON1; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) + temp |= INPUT_START; + + player++; + ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); + break; + + case DEVICE_GRAPHIC_BOARD: + input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * 255) / 0xfffe; + input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * 255) / 0xfffe; + + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) + temp |= INPUT_GRAPHIC_PEN; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) + temp |= INPUT_GRAPHIC_DO; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) + temp |= INPUT_GRAPHIC_MENU; + + player++; + ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); + break; + + case DEVICE_XE_1AP: + { + int rx = input.analog[i][0] = input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X); + int ry = input.analog[i][1] = input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y); + if (abs(rx) > abs(ry)) + input.analog[i+1][0] = (rx + 0x8000) >> 8; + else + input.analog[i+1][0] = (0x7fff - ry) >> 8; + input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; + input.analog[i][1] = (input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) + 0x8000) >> 8; + + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_R)) + temp |= INPUT_XE_A; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_R2)) + temp |= INPUT_XE_B; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_L)) + temp |= INPUT_XE_C; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_L2)) + temp |= INPUT_XE_D; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_Y)) + temp |= INPUT_XE_E1; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_B)) + temp |= INPUT_XE_E2; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_SELECT)) + temp |= INPUT_XE_SELECT; + if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_START)) + temp |= INPUT_XE_START; + + player++; + ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); + break; + } + + default: + break; + } + + input.pad[i] = temp; + } +} + +static void osd_input_update_internal(void) +{ + int i, player = 0; + unsigned int temp; + + for (i = 0; i < MAX_INPUTS; i++) + { + temp = 0; + switch (input.dev[i]) + { + case DEVICE_PAD6B: + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L)) + temp |= INPUT_X; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X)) + temp |= INPUT_Y; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R)) + temp |= INPUT_Z; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) + temp |= INPUT_MODE; + + case DEVICE_PAD3B: + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y)) + temp |= INPUT_A; + + case DEVICE_PAD2B: + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) + temp |= INPUT_B; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A)) + temp |= INPUT_C; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) + temp |= INPUT_START; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP)) + temp |= INPUT_UP; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)) + temp |= INPUT_DOWN; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT)) + temp |= INPUT_LEFT; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT)) + temp |= INPUT_RIGHT; + + player++; + break; + + case DEVICE_MOUSE: + input.analog[i][0] = input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X); + if (config.invert_mouse) + input.analog[i][1] = input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y); + else + input.analog[i][1] = -input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y); + + if (input.analog[i][0] < -255) + input.analog[i][0] = -255; + else if (input.analog[i][0] > 255) + input.analog[i][0] = 255; + if (input.analog[i][1] < -255) + input.analog[i][1] = -255; + else if (input.analog[i][1] > 255) + input.analog[i][1] = 255; + + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) + temp |= INPUT_MOUSE_LEFT; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) + temp |= INPUT_MOUSE_RIGHT; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN)) + temp |= INPUT_MOUSE_CENTER; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) + temp |= INPUT_START; + + player++; + break; + + case DEVICE_LIGHTGUN: + if ( retro_gun_mode == RetroPointer ) + { + input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * bitmap.viewport.w) / 0xfffe; + input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * bitmap.viewport.h) / 0xfffe; + if (input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED)) + temp |= INPUT_A; + } + else + { + /* RetroLightgun is default */ + if ( input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) ) + { + input.analog[i][0] = -1000; + input.analog[i][1] = -1000; + } + else + { + input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) + 0x7fff) * bitmap.viewport.w) / 0xfffe; + input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y) + 0x7fff) * bitmap.viewport.h) / 0xfffe; + } + + if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER)) + temp |= INPUT_A; + if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A)) + temp |= INPUT_B; + if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_B)) + temp |= INPUT_C; + if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_START)) + temp |= INPUT_START; + } + + player++; + break; + + case DEVICE_PADDLE: + input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; + + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) + temp |= INPUT_BUTTON1; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) + temp |= INPUT_START; + + player++; + break; + + case DEVICE_SPORTSPAD: + input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; + input.analog[i][1] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_Y) + 0x8000) >> 8; + + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) + temp |= INPUT_BUTTON1; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A)) + temp |= INPUT_BUTTON2; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) + temp |= INPUT_START; + + player++; + break; + + case DEVICE_PICO: + input.analog[i][0] = 0x03c + ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * (0x17c-0x03c)) / 0xfffe; + input.analog[i][1] = 0x1fc + ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * (0x2f7-0x1fc)) / 0xfffe; + + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) + temp |= INPUT_PICO_PEN; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) + temp |= INPUT_PICO_RED; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELUP)) + pico_current = (pico_current - 1) & 7; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN)) + pico_current = (pico_current + 1) & 7; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP)) + temp |= INPUT_UP; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)) + temp |= INPUT_DOWN; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT)) + temp |= INPUT_LEFT; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT)) + temp |= INPUT_RIGHT; + + player++; + break; + + case DEVICE_TEREBI: + input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * 250) / 0xfffe; + input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * 250) / 0xfffe; + + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) + temp |= INPUT_BUTTON1; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) + temp |= INPUT_START; + + player++; + break; + + case DEVICE_GRAPHIC_BOARD: + input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * 255) / 0xfffe; + input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * 255) / 0xfffe; + + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) + temp |= INPUT_GRAPHIC_PEN; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) + temp |= INPUT_GRAPHIC_DO; + if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) + temp |= INPUT_GRAPHIC_MENU; + + player++; + break; + + case DEVICE_XE_1AP: + { + int rx = input.analog[i][0] = input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X); + int ry = input.analog[i][1] = input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y); + if (abs(rx) > abs(ry)) + input.analog[i+1][0] = (rx + 0x8000) >> 8; + else + input.analog[i+1][0] = (0x7fff - ry) >> 8; + input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; + input.analog[i][1] = (input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) + 0x8000) >> 8; + + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R)) + temp |= INPUT_XE_A; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2)) + temp |= INPUT_XE_B; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L)) + temp |= INPUT_XE_C; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2)) + temp |= INPUT_XE_D; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y)) + temp |= INPUT_XE_E1; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) + temp |= INPUT_XE_E2; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) + temp |= INPUT_XE_SELECT; + if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) + temp |= INPUT_XE_START; + + player++; + break; + } + + default: + break; + } + + input.pad[i] = temp; + } +} + void osd_input_update(void) { - int i, player = 0; - unsigned int temp; - input_poll_cb(); /* Update RAM patches */ RAMCheatUpdate(); - for (i = 0; i < MAX_INPUTS; i++) - { - temp = 0; - switch (input.dev[i]) - { - case DEVICE_PAD6B: - { - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L)) - temp |= INPUT_X; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X)) - temp |= INPUT_Y; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R)) - temp |= INPUT_Z; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) - temp |= INPUT_MODE; - } - - case DEVICE_PAD3B: - { - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y)) - temp |= INPUT_A; - } - - case DEVICE_PAD2B: - { - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) - temp |= INPUT_B; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A)) - temp |= INPUT_C; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) - temp |= INPUT_START; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP)) - temp |= INPUT_UP; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)) - temp |= INPUT_DOWN; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT)) - temp |= INPUT_LEFT; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT)) - temp |= INPUT_RIGHT; - - player++; - break; - } - - case DEVICE_MOUSE: - { - input.analog[i][0] = input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X); - if (config.invert_mouse) - input.analog[i][1] = input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y); - else - input.analog[i][1] = -input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y); - - if (input.analog[i][0] < -255) - input.analog[i][0] = -255; - else if (input.analog[i][0] > 255) - input.analog[i][0] = 255; - if (input.analog[i][1] < -255) - input.analog[i][1] = -255; - else if (input.analog[i][1] > 255) - input.analog[i][1] = 255; - - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) - temp |= INPUT_MOUSE_LEFT; - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) - temp |= INPUT_MOUSE_RIGHT; - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN)) - temp |= INPUT_MOUSE_CENTER; - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) - temp |= INPUT_START; - - player++; - break; - } - - case DEVICE_LIGHTGUN: - { - if ( input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) ) - { - input.analog[i][0] = -1000; - input.analog[i][1] = -1000; - } - else - { - input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) + 0x7fff) * bitmap.viewport.w) / 0xfffe; - input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y) + 0x7fff) * bitmap.viewport.h) / 0xfffe; - } - - if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER)) - temp |= INPUT_A; - if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A)) - temp |= INPUT_B; - if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_B)) - temp |= INPUT_C; - if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_START)) - temp |= INPUT_START; - - player++; - break; - } - - case DEVICE_PADDLE: - { - input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; - - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) - temp |= INPUT_BUTTON1; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) - temp |= INPUT_START; - - player++; - break; - } - - case DEVICE_SPORTSPAD: - { - input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; - input.analog[i][1] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_Y) + 0x8000) >> 8; - - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) - temp |= INPUT_BUTTON1; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A)) - temp |= INPUT_BUTTON2; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) - temp |= INPUT_START; - - player++; - break; - } - - case DEVICE_PICO: - { - input.analog[i][0] = 0x03c + ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * (0x17c-0x03c)) / 0xfffe; - input.analog[i][1] = 0x1fc + ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * (0x2f7-0x1fc)) / 0xfffe; - - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) - temp |= INPUT_PICO_PEN; - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) - temp |= INPUT_PICO_RED; - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELUP)) - pico_current = (pico_current - 1) & 7; - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN)) - pico_current = (pico_current + 1) & 7; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP)) - temp |= INPUT_UP; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)) - temp |= INPUT_DOWN; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT)) - temp |= INPUT_LEFT; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT)) - temp |= INPUT_RIGHT; - - player++; - break; - } - - case DEVICE_TEREBI: - { - input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * 250) / 0xfffe; - input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * 250) / 0xfffe; - - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) - temp |= INPUT_BUTTON1; - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) - temp |= INPUT_START; - - player++; - break; - } - - case DEVICE_GRAPHIC_BOARD: - { - input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * 255) / 0xfffe; - input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * 255) / 0xfffe; - - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) - temp |= INPUT_GRAPHIC_PEN; - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) - temp |= INPUT_GRAPHIC_DO; - if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) - temp |= INPUT_GRAPHIC_MENU; - - player++; - break; - } - - case DEVICE_XE_1AP: - { - int rx = input.analog[i][0] = input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X); - int ry = input.analog[i][1] = input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y); - if (abs(rx) > abs(ry)) - { - input.analog[i+1][0] = (rx + 0x8000) >> 8; - } - else - { - input.analog[i+1][0] = (0x7fff - ry) >> 8; - } - input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; - input.analog[i][1] = (input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) + 0x8000) >> 8; - - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R)) - temp |= INPUT_XE_A; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2)) - temp |= INPUT_XE_B; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L)) - temp |= INPUT_XE_C; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2)) - temp |= INPUT_XE_D; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y)) - temp |= INPUT_XE_E1; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) - temp |= INPUT_XE_E2; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) - temp |= INPUT_XE_SELECT; - if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) - temp |= INPUT_XE_START; - - player++; - break; - } - - default: - break; - } - - input.pad[i] = temp; - } + if (libretro_supports_bitmasks) + osd_input_update_internal_bitmasks(); + else + osd_input_update_internal(); } static void draw_cursor(int16_t x, int16_t y, uint16_t color) @@ -592,6 +921,11 @@ static void config_default(void) config.ym2612 = YM2612_DISCRETE; config.ym2413 = 2; /* AUTO */ config.mono = 0; /* STEREO output */ +#ifdef USE_PER_SOUND_CHANNELS_CONFIG + for (i = 0; i < 4; i++) config.psg_ch_volumes[i] = 100; /* individual channel volumes */ + for (i = 0; i < 6; i++) config.md_ch_volumes[i] = 100; /* individual channel volumes */ + for (i = 0; i < 9; i++) config.sms_fm_ch_volumes[i] = 100; /* individual channel volumes */ +#endif #ifdef HAVE_YM3438_CORE config.ym3438 = 0; #endif @@ -908,12 +1242,19 @@ static void update_overclock(void) } #endif -static void check_variables(void) +static void check_variables(bool first_run) { unsigned orig_value; struct retro_system_av_info info; - bool update_viewports = false; - bool reinit = false; +#ifdef USE_PER_SOUND_CHANNELS_CONFIG + unsigned c; + char md_fm_channel_volume_base_str[] = "genesis_plus_gx_md_channel_0_volume"; + char sms_fm_channel_volume_base_str[] = "genesis_plus_gx_sms_fm_channel_0_volume"; + char psg_channel_volume_base_str[] = "genesis_plus_gx_psg_channel_0_volume"; +#endif + bool update_viewports = false; + bool reinit = false; + bool update_frameskip = false; struct retro_variable var = {0}; var.key = "genesis_plus_gx_bram"; @@ -1083,6 +1424,8 @@ static void check_variables(void) update_viewports = true; } + + update_frameskip = true; } } } @@ -1193,6 +1536,27 @@ static void check_variables(void) config.mono = 0; } + + var.key = "genesis_plus_gx_psg_preamp"; + environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); + { + config.psg_preamp = (!var.value) ? 150: atoi(var.value); + if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) + { + psg_config(0, config.psg_preamp, 0xff); + } + else + { + psg_config(0, config.psg_preamp, io_reg[6]); + } + } + + var.key = "genesis_plus_gx_fm_preamp"; + environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); + { + config.fm_preamp = (!var.value) ? 100: atoi(var.value); + } + var.key = "genesis_plus_gx_audio_filter"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { @@ -1285,6 +1649,28 @@ static void check_variables(void) } } + var.key = "genesis_plus_gx_frameskip"; + var.value = NULL; + orig_value = frameskip_type; + frameskip_type = 0; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + if (strcmp(var.value, "auto") == 0) + frameskip_type = 1; + else if (strcmp(var.value, "manual") == 0) + frameskip_type = 2; + } + + update_frameskip = update_frameskip || (frameskip_type != orig_value); + + var.key = "genesis_plus_gx_frameskip_threshold"; + var.value = NULL; + frameskip_threshold = 33; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + frameskip_threshold = strtol(var.value, NULL, 10); + var.key = "genesis_plus_gx_blargg_ntsc_filter"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { @@ -1393,6 +1779,15 @@ static void check_variables(void) config.gun_cursor = 1; } + var.key = "genesis_plus_gx_gun_input"; + environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); + { + if (!var.value || !strcmp(var.value, "touchscreen")) + retro_gun_mode = RetroPointer; + else + retro_gun_mode = RetroLightgun; + } + var.key = "genesis_plus_gx_invert_mouse"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { @@ -1401,7 +1796,7 @@ static void check_variables(void) else config.invert_mouse = 1; } - + var.key = "genesis_plus_gx_left_border"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { @@ -1445,6 +1840,91 @@ static void check_variables(void) config.no_sprite_limit = 1; } +#ifdef USE_PER_SOUND_CHANNELS_CONFIG + var.key = psg_channel_volume_base_str; + for (c = 0; c < 4; c++) + { + psg_channel_volume_base_str[28] = c+'0'; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var)) + { + config.psg_ch_volumes[c] = atoi(var.value); + /* need to recall config to have the settings applied */ + if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) + psg_config(0, config.psg_preamp, 0xff); + else + psg_config(0, config.psg_preamp, io_reg[6]); + } + } + + var.key = md_fm_channel_volume_base_str; + for (c = 0; c < 6; c++) + { + md_fm_channel_volume_base_str[27] = c+'0'; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var)) + config.md_ch_volumes[c] = atoi(var.value); + } + + var.key = sms_fm_channel_volume_base_str; + for (c = 0; c < 9; c++) + { + sms_fm_channel_volume_base_str[31] = c+'0'; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var)) + config.sms_fm_ch_volumes[c] = atoi(var.value); + } + + var.key = "genesis_plus_gx_show_advanced_audio_settings"; + var.value = NULL; + + /* If frontend supports core option categories, + * then genesis_plus_gx_show_advanced_audio_settings + * is ignored and no options should be hidden */ + + if (!libretro_supports_option_categories && + environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + bool show_advanced_av_settings_prev = show_advanced_av_settings; + + show_advanced_av_settings = true; + if (strcmp(var.value, "disabled") == 0) + show_advanced_av_settings = false; + + if (show_advanced_av_settings != show_advanced_av_settings_prev) + { + size_t i; + struct retro_core_option_display option_display; + char av_keys[19][40] = { + "genesis_plus_gx_psg_channel_0_volume", + "genesis_plus_gx_psg_channel_1_volume", + "genesis_plus_gx_psg_channel_2_volume", + "genesis_plus_gx_psg_channel_3_volume", + "genesis_plus_gx_md_channel_0_volume", + "genesis_plus_gx_md_channel_1_volume", + "genesis_plus_gx_md_channel_2_volume", + "genesis_plus_gx_md_channel_3_volume", + "genesis_plus_gx_md_channel_4_volume", + "genesis_plus_gx_md_channel_5_volume", + "genesis_plus_gx_sms_fm_channel_0_volume", + "genesis_plus_gx_sms_fm_channel_1_volume", + "genesis_plus_gx_sms_fm_channel_2_volume", + "genesis_plus_gx_sms_fm_channel_3_volume", + "genesis_plus_gx_sms_fm_channel_4_volume", + "genesis_plus_gx_sms_fm_channel_5_volume", + "genesis_plus_gx_sms_fm_channel_6_volume", + "genesis_plus_gx_sms_fm_channel_7_volume", + "genesis_plus_gx_sms_fm_channel_8_volume" + }; + + option_display.visible = show_advanced_av_settings; + + for (i = 0; i < 19; i++) + { + option_display.key = av_keys[i]; + environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display); + } + } + } +#endif + if (reinit) { #ifdef HAVE_OVERCLOCK @@ -1466,6 +1946,10 @@ static void check_variables(void) else bitmap.viewport.x = (config.overscan & 2) * 7; } + + /* Reinitialise frameskipping, if required */ + if ((update_frameskip || reinit) && !first_run) + init_frameskip(); } /* Cheat Support */ @@ -1488,7 +1972,8 @@ static uint32_t decode_cheat(char *string, int index) { if (i == 4) string++; p = strchr (ggvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = p - ggvalidchars; switch (i) { @@ -1532,7 +2017,8 @@ static uint32_t decode_cheat(char *string, int index) for (i=0; i<6; i++) { p = strchr (arvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = (p - arvalidchars) & 0xF; address |= (n << ((5 - i) * 4)); } @@ -1541,7 +2027,8 @@ static uint32_t decode_cheat(char *string, int index) for (i=0; i<4; i++) { p = strchr (arvalidchars, *string++); - if (p == NULL) break; + if (!p) + break; n = (p - arvalidchars) & 0xF; data |= (n << ((3 - i) * 4)); } @@ -1558,22 +2045,26 @@ static uint32_t decode_cheat(char *string, int index) for (i=0; i<2; i++) { p = strchr (arvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = (p - arvalidchars) & 0xF; data |= (n << ((1 - i) * 4)); } + /* decode 16-bit address (low 12-bits) */ for (i=0; i<3; i++) { if (i==1) string++; /* skip separator */ p = strchr (arvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = (p - arvalidchars) & 0xF; address |= (n << ((2 - i) * 4)); } /* decode 16-bit address (high 4-bits) */ p = strchr (arvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = (p - arvalidchars) & 0xF; n ^= 0xF; /* bits inversion */ address |= (n << 12); @@ -1584,7 +2075,8 @@ static uint32_t decode_cheat(char *string, int index) { string++; /* skip separator and 2nd digit */ p = strchr (arvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = (p - arvalidchars) & 0xF; ref |= (n << ((1 - i) * 4)); } @@ -1607,7 +2099,8 @@ static uint32_t decode_cheat(char *string, int index) for (i=0; i<4; i++) { p = strchr (arvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = (p - arvalidchars) & 0xF; address |= (n << ((3 - i) * 4)); if (i==1) string++; @@ -1616,7 +2109,8 @@ static uint32_t decode_cheat(char *string, int index) for (i=0; i<2; i++) { p = strchr (arvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = (p - arvalidchars) & 0xF; data |= (n << ((1 - i) * 4)); } @@ -1631,7 +2125,8 @@ static uint32_t decode_cheat(char *string, int index) for (i=0; i<4; i++) { p = strchr (arvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = (p - arvalidchars) & 0xF; address |= (n << ((3 - i) * 4)); } @@ -1640,7 +2135,8 @@ static uint32_t decode_cheat(char *string, int index) for (i=0; i<2; i++) { p = strchr (arvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = (p - arvalidchars) & 0xF; data |= (n << ((1 - i) * 4)); } @@ -1655,7 +2151,8 @@ static uint32_t decode_cheat(char *string, int index) for (i=0; i<2; i++) { p = strchr (arvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = (p - arvalidchars) & 0xF; ref |= (n << ((1 - i) * 4)); } @@ -1663,7 +2160,8 @@ static uint32_t decode_cheat(char *string, int index) for (i=0; i<4; i++) { p = strchr (arvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = (p - arvalidchars) & 0xF; address |= (n << ((3 - i) * 4)); } @@ -1672,7 +2170,8 @@ static uint32_t decode_cheat(char *string, int index) for (i=0; i<2; i++) { p = strchr (arvalidchars, *string++); - if (p == NULL) return 0; + if (!p) + return 0; n = (p - arvalidchars) & 0xF; data |= (n << ((1 - i) * 4)); } @@ -1681,9 +2180,7 @@ static uint32_t decode_cheat(char *string, int index) } /* convert to 24-bit Work RAM address */ if (address >= 0xC000) - { address = 0xFF0000 | (address & 0x1FFF); - } } /* Valid code found ? */ if (len) @@ -1850,6 +2347,26 @@ void ROMCheatUpdate(void) } } +static void set_memory_maps() +{ + if (system_hw == SYSTEM_MCD) + { + const size_t SCD_BIT = 1ULL << 31ULL; + const uint64_t mem = RETRO_MEMDESC_SYSTEM_RAM; + struct retro_memory_map mmaps; + struct retro_memory_descriptor descs[] = { + { mem, work_ram, 0, 0xFF0000, 0, 0, 0x10000, "68KRAM" }, + /* virtual address using SCD_BIT so all 512M of prg_ram can be accessed */ + /* at address $80020000 */ + { mem, scd.prg_ram, 0, SCD_BIT | 0x020000, 0, 0, 0x80000, "PRGRAM" }, + }; + + mmaps.descriptors = descs; + mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]); + environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmaps); + } +} + /**************************************************************************** * Disk control interface ****************************************************************************/ @@ -1995,50 +2512,6 @@ unsigned retro_api_version(void) { return RETRO_API_VERSION; } void retro_set_environment(retro_environment_t cb) { struct retro_vfs_interface_info vfs_iface_info; - static const struct retro_variable vars[] = { - { "genesis_plus_gx_system_hw", "System hardware; auto|sg-1000|sg-1000 II|mark-III|master system|master system II|game gear|mega drive / genesis" }, - { "genesis_plus_gx_region_detect", "System region; auto|ntsc-u|pal|ntsc-j" }, - { "genesis_plus_gx_force_dtack", "System lockups; enabled|disabled" }, - { "genesis_plus_gx_bios", "System bootrom; disabled|enabled" }, - { "genesis_plus_gx_bram", "CD System BRAM; per bios|per game" }, - { "genesis_plus_gx_addr_error", "68k address error; enabled|disabled" }, - { "genesis_plus_gx_add_on", "CD add-on (MD mode); auto|sega/mega cd|megasd|none" }, - { "genesis_plus_gx_lock_on", "Cartridge lock-on; disabled|game genie|action replay (pro)|sonic & knuckles" }, - { "genesis_plus_gx_ym2413", "Master System FM (YM2413); auto|disabled|enabled" }, -#ifdef HAVE_OPLL_CORE - { "genesis_plus_gx_ym2413_core", "Master System FM (YM2413) core; mame|nuked" }, -#endif -#ifdef HAVE_YM3438_CORE - { "genesis_plus_gx_ym2612", "Mega Drive / Genesis FM; mame (ym2612)|mame (asic ym3438)|mame (enhanced ym3438)|nuked (ym2612)|nuked (ym3438)" }, -#else - { "genesis_plus_gx_ym2612", "Mega Drive / Genesis FM; mame (ym2612)|mame (asic ym3438)|mame (enhanced ym3438)" }, -#endif - - { "genesis_plus_gx_sound_output", "Sound output; stereo|mono" }, - { "genesis_plus_gx_audio_filter", "Audio filter; disabled|low-pass" }, - { "genesis_plus_gx_lowpass_range", "Low-pass filter %; 60|65|70|75|80|85|90|95|5|10|15|20|25|30|35|40|45|50|55"}, - - #if HAVE_EQ - { "genesis_plus_gx_audio_eq_low", "EQ Low; 100|0|5|10|15|20|25|30|35|40|45|50|55|60|65|70|75|80|85|90|95" }, - { "genesis_plus_gx_audio_eq_mid", "EQ Mid; 100|0|5|10|15|20|25|30|35|40|45|50|55|60|65|70|75|80|85|90|95" }, - { "genesis_plus_gx_audio_eq_high", "EQ High; 100|0|5|10|15|20|25|30|35|40|45|50|55|60|65|70|75|80|85|90|95" }, - #endif - - { "genesis_plus_gx_blargg_ntsc_filter", "Blargg NTSC filter; disabled|monochrome|composite|svideo|rgb" }, - { "genesis_plus_gx_lcd_filter", "LCD Ghosting filter; disabled|enabled" }, - { "genesis_plus_gx_overscan", "Borders; disabled|top/bottom|left/right|full" }, - { "genesis_plus_gx_gg_extra", "Game Gear extended screen; disabled|enabled" }, - { "genesis_plus_gx_left_border", "Hide Master System Side Borders; disabled|left border|left & right borders" }, - { "genesis_plus_gx_aspect_ratio", "Core-provided aspect ratio; auto|NTSC PAR|PAL PAR" }, - { "genesis_plus_gx_render", "Interlaced mode 2 output; single field|double field" }, - { "genesis_plus_gx_gun_cursor", "Show Lightgun crosshair; disabled|enabled" }, - { "genesis_plus_gx_invert_mouse", "Invert Mouse Y-axis; disabled|enabled" }, -#ifdef HAVE_OVERCLOCK - { "genesis_plus_gx_overclock", "CPU speed; 100%|125%|150%|175%|200%" }, -#endif - { "genesis_plus_gx_no_sprite_limit", "Remove per-line sprite limit; disabled|enabled" }, - { NULL, NULL }, - }; static const struct retro_controller_description port_1[] = { { "Joypad Auto", RETRO_DEVICE_JOYPAD }, @@ -2194,10 +2667,42 @@ void retro_set_environment(retro_environment_t cb) { 0 }, }; + static const struct retro_system_content_info_override content_overrides[] = { + { + "mdx|md|smd|gen|bms|sms|gg|sg|68k|sgd", /* extensions */ +#if defined(LOW_MEMORY) + true, /* need_fullpath */ +#else + false, /* need_fullpath */ +#endif + false /* persistent_data */ + }, + { NULL, false, false } + }; + environ_cb = cb; - cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars); + + libretro_supports_option_categories = false; + libretro_set_core_options(environ_cb, + &libretro_supports_option_categories); + + /* If frontend supports core option categories, + * genesis_plus_gx_show_advanced_audio_settings + * is unused and should be hidden */ + if (libretro_supports_option_categories) + { + struct retro_core_option_display option_display; + + option_display.visible = false; + option_display.key = "genesis_plus_gx_show_advanced_audio_settings"; + + environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, + &option_display); + } + cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports); cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, (void*)desc); + cb(RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE, (void*)content_overrides); vfs_iface_info.required_interface_version = 1; vfs_iface_info.iface = NULL; @@ -2220,7 +2725,7 @@ void retro_get_system_info(struct retro_system_info *info) #define GIT_VERSION "" #endif info->library_version = "v1.7.4" GIT_VERSION; - info->valid_extensions = "m3u|mdx|md|smd|gen|bin|cue|iso|chd|sms|gg|sg"; + info->valid_extensions = "m3u|mdx|md|smd|gen|bin|cue|iso|chd|bms|sms|gg|sg|68k|sgd"; info->block_extract = false; info->need_fullpath = true; } @@ -2229,8 +2734,31 @@ void retro_get_system_av_info(struct retro_system_av_info *info) { info->geometry.base_width = vwidth; info->geometry.base_height = bitmap.viewport.h + (2 * bitmap.viewport.y); - info->geometry.max_width = 720; - info->geometry.max_height = 576; + /* Set maximum dimensions based upon emulated system/config */ + if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) + { + /* 16 bit system */ + if (config.ntsc) { + info->geometry.max_width = MD_NTSC_OUT_WIDTH(320 + (bitmap.viewport.x * 2)); + } else { + info->geometry.max_width = 320 + (bitmap.viewport.x * 2); + } + if (config.render) { + info->geometry.max_height = 480 + (vdp_pal * 96 * (config.overscan & 1)); + } else { + info->geometry.max_height = 240 + (vdp_pal * 48 * (config.overscan & 1)); + } + } + else + { + /* 8 bit system */ + if (config.ntsc) { + info->geometry.max_width = SMS_NTSC_OUT_WIDTH(256 + (bitmap.viewport.x * 2)); + } else { + info->geometry.max_width = 256 + (bitmap.viewport.x * 2); + } + info->geometry.max_height = 240 + (vdp_pal * 48 * (config.overscan & 1)); + } info->geometry.aspect_ratio = vaspect_ratio; info->timing.fps = (double)(system_clock) / (double)lines_per_frame / (double)MCYCLES_PER_LINE; info->timing.sample_rate = SOUND_FREQUENCY; @@ -2387,9 +2915,9 @@ bool retro_unserialize(const void *data, size_t size) return FALSE; #ifdef HAVE_OVERCLOCK - overclock_delay = OVERCLOCK_FRAME_DELAY; update_overclock(); #endif + return TRUE; } @@ -2452,18 +2980,75 @@ void retro_cheat_set(unsigned index, bool enabled, const char *code) bool retro_load_game(const struct retro_game_info *info) { int i; - char *dir = NULL; + char *dir = NULL; #if defined(_WIN32) char slash = '\\'; #else char slash = '/'; #endif + struct retro_game_info_ext *info_ext = NULL; + char content_path[256]; + char content_ext[8]; - if (!info) - return false; + content_path[0] = '\0'; + content_ext[0] = '\0'; - if (!info->path) - return false; + g_rom_data = NULL; + g_rom_size = 0; + + /* Attempt to fetch extended game info */ + if (environ_cb(RETRO_ENVIRONMENT_GET_GAME_INFO_EXT, &info_ext)) + { +#if !defined(LOW_MEMORY) + g_rom_data = (const void *)info_ext->data; + g_rom_size = info_ext->size; +#endif + strncpy(g_rom_dir, info_ext->dir, sizeof(g_rom_dir)); + g_rom_dir[sizeof(g_rom_dir) - 1] = '\0'; + + strncpy(g_rom_name, info_ext->name, sizeof(g_rom_name)); + g_rom_name[sizeof(g_rom_name) - 1] = '\0'; + + strncpy(content_ext, info_ext->ext, sizeof(content_ext)); + content_ext[sizeof(content_ext) - 1] = '\0'; + + if (info_ext->file_in_archive) + { + /* We don't have a 'physical' file in this + * case, but the core still needs a filename + * in order to detect associated content + * (Mega CD Mode 1/MegaSD MD+ mode). We therefore + * fake it, using the content directory, + * canonical content name, and content file + * extension */ + snprintf(content_path, sizeof(content_path), "%s%c%s.%s", + g_rom_dir, slash, g_rom_name, content_ext); + } + else + { + strncpy(content_path, info_ext->full_path, sizeof(content_path)); + content_path[sizeof(content_path) - 1] = '\0'; + } + } + else + { + const char *ext = NULL; + + if (!info || !info->path) + return false; + + extract_directory(g_rom_dir, info->path, sizeof(g_rom_dir)); + extract_name(g_rom_name, info->path, sizeof(g_rom_name)); + + strncpy(content_path, info->path, sizeof(content_path)); + content_path[sizeof(content_path) - 1] = '\0'; + + if (ext = strrchr(info->path, '.')) + { + strncpy(content_ext, ext + 1, sizeof(content_ext)); + content_ext[sizeof(content_ext) - 1] = '\0'; + } + } #ifdef FRONTEND_SUPPORTS_RGB565 { @@ -2480,9 +3065,6 @@ bool retro_load_game(const struct retro_game_info *info) init_bitmap(); config_default(); - extract_directory(g_rom_dir, info->path, sizeof(g_rom_dir)); - extract_name(g_rom_name, info->path, sizeof(g_rom_name)); - if (!environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) || !dir) { if (log_cb) @@ -2511,7 +3093,7 @@ bool retro_load_game(const struct retro_game_info *info) snprintf(CD_BIOS_JP, sizeof(CD_BIOS_JP), "%s%cbios_CD_J.bin", dir, slash); snprintf(CART_BRAM, sizeof(CART_BRAM), "%s%ccart.brm", save_dir, slash); - check_variables(); + check_variables(true); if (log_cb) { @@ -2533,9 +3115,6 @@ bool retro_load_game(const struct retro_game_info *info) log_cb(RETRO_LOG_INFO, "Sega/Mega CD RAM CART is located at: %s\n", CART_BRAM); } - g_rom_data = (void *)info->data; - g_rom_size = info->size; - /* Clear disk interface (already done in retro_unload_game but better be safe) */ disk_count = 0; disk_index = 0; @@ -2549,9 +3128,9 @@ bool retro_load_game(const struct retro_game_info *info) } /* M3U file list support */ - if ((strlen(info->path) > 4) && !strncmp(info->path + strlen(info->path) - 4, ".m3u", 4)) + if (!strcmp(content_ext, "m3u")) { - RFILE *fd = filestream_open(info->path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); + RFILE *fd = filestream_open(content_path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); if (fd) { int len; @@ -2622,14 +3201,14 @@ bool retro_load_game(const struct retro_game_info *info) } else { - if (load_rom((char *)info->path) <= 0) + if (load_rom(content_path) <= 0) return false; /* automatically add loaded CD to disk interface */ if ((system_hw == SYSTEM_MCD) && cdd.loaded) { disk_count = 1; - disk_info[0] = strdup(info->path); + disk_info[0] = strdup(content_path); } } @@ -2669,6 +3248,10 @@ bool retro_load_game(const struct retro_game_info *info) update_overclock(); #endif + set_memory_maps(); + + init_frameskip(); + return true; } @@ -2779,14 +3362,31 @@ void retro_init(void) else log_cb = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL)) + libretro_supports_bitmasks = true; + check_system_specs(); environ_cb(RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS, &serialization_quirks); environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_ctrl); + + frameskip_type = 0; + frameskip_threshold = 0; + frameskip_counter = 0; + retro_audio_buff_active = false; + retro_audio_buff_occupancy = 0; + retro_audio_buff_underrun = false; + audio_latency = 0; + update_audio_latency = false; } void retro_deinit(void) { + libretro_supports_option_categories = false; + libretro_supports_bitmasks = false; + + g_rom_data = NULL; + g_rom_size = 0; } void retro_reset(void) @@ -2800,10 +3400,11 @@ void retro_reset(void) void retro_run(void) { + int do_skip = 0; bool updated = false; - is_running = true; int vwoffset = 0; int bmdoffset = 0; + is_running = true; #ifdef HAVE_OVERCLOCK /* update overclock delay */ @@ -2811,17 +3412,65 @@ void retro_run(void) update_overclock(); #endif + environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated); + if (updated) + { + check_variables(false); + if (restart_eq) + { + audio_set_equalizer(); + restart_eq = false; + } + } + + /* Check whether current frame should + * be skipped */ + if ((frameskip_type > 0) && + retro_audio_buff_active && + !do_skip) + { + switch (frameskip_type) + { + case 1: /* auto */ + do_skip = retro_audio_buff_underrun ? 1 : 0; + break; + case 2: /* manual */ + do_skip = (retro_audio_buff_occupancy < frameskip_threshold) ? 1 : 0; + break; + default: + do_skip = 0; + break; + } + + if (!do_skip || (frameskip_counter >= FRAMESKIP_MAX)) + { + do_skip = 0; + frameskip_counter = 0; + } + else + frameskip_counter++; + } + + /* If frameskip settings have changed, update + * frontend audio latency */ + if (update_audio_latency) + { + environ_cb(RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY, + &audio_latency); + update_audio_latency = false; + } + if (system_hw == SYSTEM_MCD) { - system_frame_scd(0); + system_frame_scd(do_skip); } else if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { - system_frame_gen(0); + system_frame_gen(do_skip); } else { - system_frame_sms(0); + system_frame_sms(do_skip); } if (bitmap.viewport.changed & 9) @@ -2868,28 +3517,21 @@ void retro_run(void) { bmdoffset = 16; if (config.left_border == 1) - { vwoffset = 8; - } else - { vwoffset = 16; - } } - video_cb(bitmap.data + bmdoffset, vwidth - vwoffset, vheight, 720 * 2); - audio_cb(soundbuffer, audio_update(soundbuffer)); - - environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated); - if (updated) + if (!do_skip) { - check_variables(); - if (restart_eq) - { - audio_set_equalizer(); - restart_eq = false; - } + video_cb(bitmap.data + bmdoffset, vwidth - vwoffset, vheight, 720 * 2); + } + else + { + video_cb(NULL, vwidth - vwoffset, vheight, 720 * 2); } + + audio_cb(soundbuffer, audio_update(soundbuffer)); } #undef CHUNKSIZE diff --git a/libretro/libretro_core_options.h b/libretro/libretro_core_options.h new file mode 100644 index 0000000..f9605aa --- /dev/null +++ b/libretro/libretro_core_options.h @@ -0,0 +1,1552 @@ +#ifndef LIBRETRO_CORE_OPTIONS_H__ +#define LIBRETRO_CORE_OPTIONS_H__ + +#include +#include + +#include +#include + +#ifndef HAVE_NO_LANGEXTRA +#include "libretro_core_options_intl.h" +#endif + +/* + ******************************** + * VERSION: 2.0 + ******************************** + * + * - 2.0: Add support for core options v2 interface + * - 1.3: Move translations to libretro_core_options_intl.h + * - libretro_core_options_intl.h includes BOM and utf-8 + * fix for MSVC 2010-2013 + * - Added HAVE_NO_LANGEXTRA flag to disable translations + * on platforms/compilers without BOM support + * - 1.2: Use core options v1 interface when + * RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1 + * (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1) + * - 1.1: Support generation of core options v0 retro_core_option_value + * arrays containing options with a single value + * - 1.0: First commit +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + ******************************** + * Core Definitions + ******************************** +*/ + +#if defined(M68K_OVERCLOCK_SHIFT) || defined(Z80_OVERCLOCK_SHIFT) +#define HAVE_OVERCLOCK +#endif + +/* + ******************************** + * Core Option Definitions + ******************************** +*/ + +/* RETRO_LANGUAGE_ENGLISH */ + +/* Default language: + * - All other languages must include the same keys and values + * - Will be used as a fallback in the event that frontend language + * is not available + * - Will be used as a fallback for any missing entries in + * frontend language definition */ + +struct retro_core_option_v2_category option_cats_us[] = { + { + "system", + "System", + "Configure base hardware selection / region / BIOS / Sega CD save file parameters." + }, + { + "video", + "Video", + "Configure aspect ratio / display cropping / video filter / frame skipping parameters." + }, + { + "audio", + "Audio", + "Configure emulated audio devices." + }, + { + "input", + "Input", + "Configure light gun / mouse input." + }, + { + "hacks", + "Emulation Hacks", + "Configure processor overclocking and emulation accuracy parameters affecting low-level performance and compatibility." + }, + { + "channel_volume", + "Advanced Channel Volume Settings", + "Configure the volume of individual hardware audio channels." + }, + { NULL, NULL, NULL }, +}; + +struct retro_core_option_v2_definition option_defs_us[] = { + { + "genesis_plus_gx_system_hw", + "System Hardware", + NULL, + "Runs loaded content with a specific emulated console. 'Auto' will select the most appropriate system for the current game.", + NULL, + "system", + { + { "auto", "Auto" }, + { "sg-1000", "SG-1000" }, + { "sg-1000 II", "SG-1000 II" }, + { "mark-III", "Mark III" }, + { "master system", "Master System" }, + { "master system II", "Master System II" }, + { "game gear", "Game Gear" }, + { "mega drive / genesis", "Mega Drive/Genesis" }, + { NULL, NULL }, + }, + "auto" + }, + { + "genesis_plus_gx_region_detect", + "System Region", + NULL, + "Specify which region the system is from. For consoles other than the Game Gear, 'PAL' is 50hz while 'NTSC' is 60hz. Games may run faster or slower than normal if the incorrect region is selected.", + NULL, + "system", + { + { "auto", "Auto" }, + { "ntsc-u", "NTSC-U" }, + { "pal", "PAL" }, + { "ntsc-j", "NTSC-J" }, + { NULL, NULL }, + }, + "auto" + }, + { + "genesis_plus_gx_bios", + "System Boot ROM", + NULL, + "Use official BIOS/bootloader for emulated hardware, if present in RetroArch's system directory. Displays console-specific start-up sequence/animation, then runs loaded content.", + NULL, + "system", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_bram", + "CD System BRAM", + NULL, + "When running Sega CD content, specifies whether to share a single save file between all games from a specific region (Per-BIOS) or to create a separate save file for each game (Per-Game). Note that the Sega CD has limited internal storage, sufficient only for a handful of titles. To avoid running out of space, the 'Per-Game' setting is recommended.", + NULL, + "system", + { + { "per bios", "Per-BIOS" }, + { "per game", "Per-Game" }, + { NULL, NULL }, + }, + "per bios" + }, + { + "genesis_plus_gx_add_on", + "CD add-on (MD mode) (Requires Restart)", + NULL, + "Specify which add-on to use for CD audio playback.", + NULL, + "system", + { + { "auto", "Auto" }, + { "sega/mega cd", "Sega/Mega CD" }, + { "megasd", "MegaSD" }, + { "none", "None" }, + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_lock_on", + "Cartridge Lock-On", + NULL, + "Lock-On Technology is a Genesis feature that allowed an older game to connect to the pass-through port of a special cartridge for extended or altered gameplay. This option specifies which type of special 'lock-on' cartridge to emulate. A corresponding bios file must be present in RetroArch's system directory.", + NULL, + "system", + { + { "disabled", NULL }, + { "game genie", "Game Genie" }, + { "action replay (pro)", "Action Replay (Pro)" }, + { "sonic & knuckles", "Sonic & Knuckles" }, + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_aspect_ratio", + "Core-Provided Aspect Ratio", + NULL, + "Choose the preferred content aspect ratio. This will only apply when RetroArch's aspect ratio is set to 'Core provided' in the Video settings.", + NULL, + "video", + { + { "auto", "Auto" }, + { "NTSC PAR", NULL }, + { "PAL PAR", NULL }, + }, + "auto" + }, + { + "genesis_plus_gx_overscan", + "Borders", + NULL, + "Enable this to display the overscan regions at the top/bottom and/or left/right of the screen. These would normally be hidden by the bezel around the edge of a standard-definition television.", + NULL, + "video", + { + { "disabled", NULL }, + { "top/bottom", "Top/Bottom" }, + { "left/right", "Left/Right" }, + { "full", "Full" }, + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_left_border", + "Hide Master System Side Borders", + NULL, + "Cuts off 8 pixels from either the left side of the screen, or both left and right sides when running Master System games.", + NULL, + "video", + { + { "disabled", NULL }, + { "left border", "Left Border Only" }, + { "left & right borders", "Left & Right Borders" }, + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_gg_extra", + "Game Gear Extended Screen", + NULL, + "Forces Game Gear titles to run in 'SMS' mode, with an increased resolution of 256x192. May show additional content, but typically displays a border of corrupt/unwanted image data.", + NULL, + "video", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_blargg_ntsc_filter", + "Blargg NTSC Filter", + NULL, + "Apply a video filter to mimic various NTSC TV signals.", + NULL, + "video", + { + { "disabled", NULL }, + { "monochrome", "Monochrome" }, + { "composite", "Composite" }, + { "svideo", "S-Video" }, + { "rgb", "RGB" }, + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_lcd_filter", + "LCD Ghosting Filter", + NULL, + "Apply an image 'ghosting' filter to mimic the display characteristics of the Game Gear and 'Genesis Nomad' LCD panels.", + NULL, + "video", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_render", + "Interlaced Mode 2 Output", + NULL, + "Interlaced Mode 2 allows the Genesis to output a double height (high resolution) 320x448 image by drawing alternate scanlines each frame (this is used by 'Sonic the Hedgehog 2' and 'Combat Cars' multiplayer modes). 'Double Field' mimics original hardware, producing a sharp image with flickering/interlacing artefacts. 'Single Field' apples a de-interlacing filter, which stabilises the image but causes mild blurring.", + NULL, + "video", + { + { "single field", "Single Field" }, + { "double field", "Double Field" }, + { NULL, NULL }, + }, + "single field" + }, + { + "genesis_plus_gx_frameskip", + "Frameskip", + NULL, + "Skip frames to avoid audio buffer under-run (crackling). Improves performance at the expense of visual smoothness. 'Auto' skips frames when advised by the frontend. 'Manual' utilises the 'Frameskip Threshold (%)' setting.", + NULL, + "video", + { + { "disabled", NULL }, + { "auto", "Auto" }, + { "manual", "Manual" }, + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_frameskip_threshold", + "Frameskip Threshold (%)", + NULL, + "When 'Frameskip' is set to 'Manual', specifies the audio buffer occupancy threshold (percentage) below which frames will be skipped. Higher values reduce the risk of crackling by causing frames to be dropped more frequently.", + NULL, + "video", + { + { "15", NULL }, + { "18", NULL }, + { "21", NULL }, + { "24", NULL }, + { "27", NULL }, + { "30", NULL }, + { "33", NULL }, + { "36", NULL }, + { "39", NULL }, + { "42", NULL }, + { "45", NULL }, + { "48", NULL }, + { "51", NULL }, + { "54", NULL }, + { "57", NULL }, + { "60", NULL }, + { NULL, NULL }, + }, + "33" + }, + { + "genesis_plus_gx_ym2413", + "Master System FM (YM2413)", + NULL, + "Enable emulation of the FM Sound Unit used by certain Sega Mark III/Master System games for enhanced audio output.", + NULL, + "audio", + { + { "auto", "Auto" }, + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "auto" + }, +#ifdef HAVE_OPLL_CORE + { + "genesis_plus_gx_ym2413_core", + "Master System FM (YM2413) Core", + NULL, + "Select method used to emulate the FM Sound Unit of the Sega Mark III/Master System. 'MAME' option is fast, and runs full speed on most systems. 'Nuked' option is cycle accurate, very high quality, and has substantial CPU requirements.", + NULL, + "audio", + { + { "mame", "MAME" }, + { "nuked", "Nuked" }, + { NULL, NULL }, + }, + "mame" + }, +#endif + { + "genesis_plus_gx_ym2612", + "Mega Drive / Genesis FM", + NULL, +#ifdef HAVE_YM3438_CORE + "Select method used to emulate the FM synthesizer (main sound generator) of the Mega Drive/Genesis. 'MAME' options are fast, and run full speed on most systems. 'Nuked' options are cycle accurate, very high quality, and have substantial CPU requirements. The 'YM2612' chip is used by the original Model 1 Genesis. The 'YM3438' is used in later Genesis revisions.", +#else + "Select method used to emulate the FM synthesizer (main sound generator) of the Mega Drive/Genesis. The 'YM2612' chip is used by the original Model 1 Genesis. The 'YM3438' is used in later Genesis revisions.", +#endif + NULL, + "audio", + { + { "mame (ym2612)", "MAME (YM2612)" }, + { "mame (asic ym3438)", "MAME (ASIC YM3438)" }, + { "mame (enhanced ym3438)", "MAME (Enhanced YM3438)" }, +#ifdef HAVE_YM3438_CORE + { "nuked (ym2612)", "Nuked (YM2612)" }, + { "nuked (ym3438)", "Nuked (YM3438)" }, +#endif + { NULL, NULL }, + }, + "mame (ym2612)" + }, + { + "genesis_plus_gx_sound_output", + "Sound Output", + NULL, + "Select stereo or mono sound reproduction.", + NULL, + "audio", + { + { "stereo", "Stereo" }, + { "mono", "Mono" }, + { NULL, NULL }, + }, + "stereo" + }, + { + "genesis_plus_gx_audio_filter", + "Audio Filter", + NULL, + "Enable a low pass audio filter to better simulate the characteristic sound of a Model 1 Genesis.", + NULL, + "audio", + { + { "disabled", NULL }, + { "low-pass", "Low-Pass" }, +#if HAVE_EQ + { "EQ", NULL }, +#endif + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_lowpass_range", + "Low-Pass Filter %", + NULL, + "Specify the cut-off frequency of the audio low pass filter. A higher value increases the perceived 'strength' of the filter, since a wider range of the high frequency spectrum is attenuated.", + NULL, + "audio", + { + { "5", NULL }, + { "10", NULL }, + { "15", NULL }, + { "20", NULL }, + { "25", NULL }, + { "30", NULL }, + { "35", NULL }, + { "40", NULL }, + { "45", NULL }, + { "50", NULL }, + { "55", NULL }, + { "60", NULL }, + { "65", NULL }, + { "70", NULL }, + { "75", NULL }, + { "80", NULL }, + { "85", NULL }, + { "90", NULL }, + { "95", NULL }, + { NULL, NULL }, + }, + "60" + }, + { + "genesis_plus_gx_psg_preamp", + "PSG Preamp Level", + NULL, + "Set the audio preamplifier level of the emulated SN76496 4-channel Programmable Sound Generator found in the Master System, Game Gear and Genesis.", + NULL, + "audio", + { + { "0", NULL }, + { "5", NULL }, + { "10", NULL }, + { "15", NULL }, + { "20", NULL }, + { "25", NULL }, + { "30", NULL }, + { "35", NULL }, + { "40", NULL }, + { "45", NULL }, + { "50", NULL }, + { "55", NULL }, + { "60", NULL }, + { "65", NULL }, + { "70", NULL }, + { "75", NULL }, + { "80", NULL }, + { "85", NULL }, + { "90", NULL }, + { "95", NULL }, + { "100", NULL }, + { "105", NULL }, + { "110", NULL }, + { "115", NULL }, + { "120", NULL }, + { "125", NULL }, + { "130", NULL }, + { "135", NULL }, + { "140", NULL }, + { "145", NULL }, + { "150", NULL }, + { "155", NULL }, + { "160", NULL }, + { "165", NULL }, + { "170", NULL }, + { "175", NULL }, + { "180", NULL }, + { "185", NULL }, + { "190", NULL }, + { "195", NULL }, + { "200", NULL }, + { NULL, NULL }, + }, + "150" + }, + { + "genesis_plus_gx_fm_preamp", + "FM Preamp Level", + NULL, + "Set the audio preamplifier level of the emulated Sega Mark III/Master System FM Sound Unit.", + NULL, + "audio", + { + { "0", NULL }, + { "5", NULL }, + { "10", NULL }, + { "15", NULL }, + { "20", NULL }, + { "25", NULL }, + { "30", NULL }, + { "35", NULL }, + { "40", NULL }, + { "45", NULL }, + { "50", NULL }, + { "55", NULL }, + { "60", NULL }, + { "65", NULL }, + { "70", NULL }, + { "75", NULL }, + { "80", NULL }, + { "85", NULL }, + { "90", NULL }, + { "95", NULL }, + { "100", NULL }, + { "105", NULL }, + { "110", NULL }, + { "115", NULL }, + { "120", NULL }, + { "125", NULL }, + { "130", NULL }, + { "135", NULL }, + { "140", NULL }, + { "145", NULL }, + { "150", NULL }, + { "155", NULL }, + { "160", NULL }, + { "165", NULL }, + { "170", NULL }, + { "175", NULL }, + { "180", NULL }, + { "185", NULL }, + { "190", NULL }, + { "195", NULL }, + { "200", NULL }, + { NULL, NULL }, + }, + "100" + }, +#ifdef HAVE_EQ + { + "genesis_plus_gx_audio_eq_low", + "EQ Low", + NULL, + "Adjust the low range band of the internal audio equaliser.", + NULL, + "audio", + { + { "0", NULL }, + { "5", NULL }, + { "10", NULL }, + { "15", NULL }, + { "20", NULL }, + { "25", NULL }, + { "30", NULL }, + { "35", NULL }, + { "40", NULL }, + { "45", NULL }, + { "50", NULL }, + { "55", NULL }, + { "60", NULL }, + { "65", NULL }, + { "70", NULL }, + { "75", NULL }, + { "80", NULL }, + { "85", NULL }, + { "90", NULL }, + { "95", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_audio_eq_mid", + "EQ Mid", + NULL, + "Adjust the middle range band of the internal audio equaliser.", + NULL, + "audio", + { + { "0", NULL }, + { "5", NULL }, + { "10", NULL }, + { "15", NULL }, + { "20", NULL }, + { "25", NULL }, + { "30", NULL }, + { "35", NULL }, + { "40", NULL }, + { "45", NULL }, + { "50", NULL }, + { "55", NULL }, + { "60", NULL }, + { "65", NULL }, + { "70", NULL }, + { "75", NULL }, + { "80", NULL }, + { "85", NULL }, + { "90", NULL }, + { "95", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_audio_eq_high", + "EQ High", + NULL, + "Adjust the high range band of the internal audio equaliser.", + NULL, + "audio", + { + { "0", NULL }, + { "5", NULL }, + { "10", NULL }, + { "15", NULL }, + { "20", NULL }, + { "25", NULL }, + { "30", NULL }, + { "35", NULL }, + { "40", NULL }, + { "45", NULL }, + { "50", NULL }, + { "55", NULL }, + { "60", NULL }, + { "65", NULL }, + { "70", NULL }, + { "75", NULL }, + { "80", NULL }, + { "85", NULL }, + { "90", NULL }, + { "95", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, +#endif + { + "genesis_plus_gx_gun_input", + "Light Gun Input", + NULL, + "Use a mouse-controlled 'Light Gun' or 'Touchscreen' input.", + NULL, + "input", + { + { "lightgun", "Light Gun" }, + { "touchscreen", "Touchscreen" }, + { NULL, NULL }, + }, + "lightgun" + }, + { + "genesis_plus_gx_gun_cursor", + "Show Light Gun Crosshair", + NULL, + "Display light gun crosshairs when using the 'MD Menacer', 'MD Justifiers' and 'MS Light Phaser' input device types.", + NULL, + "input", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_invert_mouse", + "Invert Mouse Y-Axis", + NULL, + "Inverts the Y-axis of the 'MD Mouse' input device type.", + NULL, + "input", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_no_sprite_limit", + "Remove Per-Line Sprite Limit", + NULL, + "Removes the 8 (Master System) or 20 (Genesis) sprite-per-scanline hardware limit. This reduces flickering but can cause visual glitches, as some games exploit the hardware limit to generate special effects.", + NULL, + "hacks", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, +#ifdef HAVE_OVERCLOCK + { + "genesis_plus_gx_overclock", + "CPU Speed", + NULL, + "Overclock the emulated CPU. Can reduce slowdown, but may cause glitches.", + NULL, + "hacks", + { + { "100%", NULL }, + { "125%", NULL }, + { "150%", NULL }, + { "175%", NULL }, + { "200%", NULL }, + { NULL, NULL }, + }, + "100%" + }, +#endif + { + "genesis_plus_gx_force_dtack", + "System Lock-Ups", + NULL, + "Emulate system lock-ups that occur on real hardware when performing illegal address access. This should only be disabled when playing certain demos and homebrew that rely on illegal behaviour for correct operation.", + NULL, + "hacks", + { + { "enabled", NULL }, + { "disabled", NULL }, + { NULL, NULL }, + }, + "enabled" + }, + { + "genesis_plus_gx_addr_error", + "68K Address Error", + NULL, + "The Genesis CPU (Motorola 68000) produces an Address Error (crash) when attempting to perform unaligned memory access. Enabling '68K Address Error' simulates this behaviour. It should only be disabled when playing ROM hacks, since these are typically developed using less accurate emulators and may rely on invalid RAM access for correct operation.", + NULL, + "hacks", + { + { "enabled", NULL }, + { "disabled", NULL }, + { NULL, NULL }, + }, + "enabled" + }, +#ifdef USE_PER_SOUND_CHANNELS_CONFIG + { + "genesis_plus_gx_show_advanced_audio_settings", + "Show Advanced Audio Volume Settings (Reopen menu)", + NULL, + "Enable configuration of low-level audio channel parameters. NOTE: Quick Menu must be toggled for this setting to take effect.", + NULL, + "channel_volume", + { + { "enabled", NULL }, + { "disabled", NULL }, + { NULL, NULL}, + }, + "disabled" + }, + { + "genesis_plus_gx_psg_channel_0_volume", + "PSG Tone Channel 0 Volume %", + NULL, + "Reduce the volume of the PSG Tone Channel 0.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_psg_channel_1_volume", + "PSG Tone Channel 1 Volume %", + NULL, + "Reduce the volume of the PSG Tone Channel 1.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_psg_channel_2_volume", + "PSG Tone Channel 2 Volume %", + NULL, + "Reduce the volume of the PSG Tone Channel 2.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_psg_channel_3_volume", + "PSG Noise Channel 3 Volume %", + NULL, + "Reduce the volume of the PSG Noise Channel 3.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_md_channel_0_volume", + "Mega Drive / Genesis FM Channel 0 Volume %", + NULL, + "Reduce the volume of the Mega Drive / Genesis FM Channel 0. Only works with MAME FM emulators.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_md_channel_1_volume", + "Mega Drive / Genesis FM Channel 1 Volume %", + NULL, + "Reduce the volume of the Mega Drive / Genesis FM Channel 1. Only works with MAME FM emulators.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_md_channel_2_volume", + "Mega Drive / Genesis FM Channel 2 Volume %", + NULL, + "Reduce the volume of the Mega Drive / Genesis FM Channel 2. Only works with MAME FM emulators.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_md_channel_3_volume", + "Mega Drive / Genesis FM Channel 3 Volume %", + NULL, + "Reduce the volume of the Mega Drive / Genesis FM Channel 3. Only works with MAME FM emulators.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_md_channel_4_volume", + "Mega Drive / Genesis FM Channel 4 Volume %", + NULL, + "Reduce the volume of the Mega Drive / Genesis FM Channel 4. Only works with MAME FM emulators.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_md_channel_5_volume", + "Mega Drive / Genesis FM Channel 5 Volume %", + NULL, + "Reduce the volume of the Mega Drive / Genesis FM Channel 5. Only works with MAME FM emulators.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_sms_fm_channel_0_volume", + "Master System FM (YM2413) Channel 0 Volume %", + NULL, + "Reduce the volume of the Master System FM Channel 0.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_sms_fm_channel_1_volume", + "Master System FM (YM2413) Channel 1 Volume %", + NULL, + "Reduce the volume of the Master System FM Channel 1.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_sms_fm_channel_2_volume", + "Master System FM (YM2413) Channel 2 Volume %", + NULL, + "Reduce the volume of the Master System FM Channel 2.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_sms_fm_channel_3_volume", + "Master System FM (YM2413) Channel 3 Volume %", + NULL, + "Reduce the volume of the Master System FM Channel 3.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_sms_fm_channel_4_volume", + "Master System FM (YM2413) Channel 4 Volume %", + NULL, + "Reduce the volume of the Master System FM Channel 4.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_sms_fm_channel_5_volume", + "Master System FM (YM2413) Channel 5 Volume %", + NULL, + "Reduce the volume of the Master System FM Channel 5.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_sms_fm_channel_6_volume", + "Master System FM (YM2413) Channel 6 Volume %", + NULL, + "Reduce the volume of the Master System FM Channel 6.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_sms_fm_channel_7_volume", + "Master System FM (YM2413) Channel 7 Volume %", + NULL, + "Reduce the volume of the Master System FM Channel 7.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, + { + "genesis_plus_gx_sms_fm_channel_8_volume", + "Master System FM (YM2413) Channel 8 Volume %", + NULL, + "Reduce the volume of the Master System FM Channel 8.", + NULL, + "channel_volume", + { + { "0", NULL }, + { "10", NULL }, + { "20", NULL }, + { "30", NULL }, + { "40", NULL }, + { "50", NULL }, + { "60", NULL }, + { "70", NULL }, + { "80", NULL }, + { "90", NULL }, + { "100", NULL }, + { NULL, NULL }, + }, + "100" + }, +#endif + { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL }, +}; + +struct retro_core_options_v2 options_us = { + option_cats_us, + option_defs_us +}; + +/* + ******************************** + * Language Mapping + ******************************** +*/ + +#ifndef HAVE_NO_LANGEXTRA +struct retro_core_options_v2 *options_intl[RETRO_LANGUAGE_LAST] = { + &options_us, /* RETRO_LANGUAGE_ENGLISH */ + NULL, /* RETRO_LANGUAGE_JAPANESE */ + NULL, /* RETRO_LANGUAGE_FRENCH */ + NULL, /* RETRO_LANGUAGE_SPANISH */ + NULL, /* RETRO_LANGUAGE_GERMAN */ + NULL, /* RETRO_LANGUAGE_ITALIAN */ + NULL, /* RETRO_LANGUAGE_DUTCH */ + &options_pt_br, /* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */ + NULL, /* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */ + NULL, /* RETRO_LANGUAGE_RUSSIAN */ + NULL, /* RETRO_LANGUAGE_KOREAN */ + NULL, /* RETRO_LANGUAGE_CHINESE_TRADITIONAL */ + NULL, /* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */ + NULL, /* RETRO_LANGUAGE_ESPERANTO */ + NULL, /* RETRO_LANGUAGE_POLISH */ + NULL, /* RETRO_LANGUAGE_VIETNAMESE */ + NULL, /* RETRO_LANGUAGE_ARABIC */ + NULL, /* RETRO_LANGUAGE_GREEK */ + &options_tr, /* RETRO_LANGUAGE_TURKISH */ + NULL, /* RETRO_LANGUAGE_SLOVAK */ + NULL, /* RETRO_LANGUAGE_PERSIAN */ + NULL, /* RETRO_LANGUAGE_HEBREW */ + NULL, /* RETRO_LANGUAGE_ASTURIAN */ + NULL, /* RETRO_LANGUAGE_FINNISH */ +}; +#endif + +/* + ******************************** + * Functions + ******************************** +*/ + +/* Handles configuration/setting of core options. + * Should be called as early as possible - ideally inside + * retro_set_environment(), and no later than retro_load_game() + * > We place the function body in the header to avoid the + * necessity of adding more .c files (i.e. want this to + * be as painless as possible for core devs) + */ + +INLINE void libretro_set_core_options(retro_environment_t environ_cb, + bool *categories_supported) +{ + unsigned version = 0; +#ifndef HAVE_NO_LANGEXTRA + unsigned language = 0; +#endif + + if (!environ_cb || !categories_supported) + return; + + *categories_supported = false; + + if (!environ_cb(RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION, &version)) + version = 0; + + if (version >= 2) + { +#ifndef HAVE_NO_LANGEXTRA + struct retro_core_options_v2_intl core_options_intl; + + core_options_intl.us = &options_us; + core_options_intl.local = NULL; + + if (environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) && + (language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH)) + core_options_intl.local = options_intl[language]; + + *categories_supported = environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL, + &core_options_intl); +#else + *categories_supported = environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2, + &options_us); +#endif + } + else + { + size_t i, j; + size_t option_index = 0; + size_t num_options = 0; + struct retro_core_option_definition + *option_v1_defs_us = NULL; +#ifndef HAVE_NO_LANGEXTRA + size_t num_options_intl = 0; + struct retro_core_option_v2_definition + *option_defs_intl = NULL; + struct retro_core_option_definition + *option_v1_defs_intl = NULL; + struct retro_core_options_intl + core_options_v1_intl; +#endif + struct retro_variable *variables = NULL; + char **values_buf = NULL; + + /* Determine total number of options */ + while (true) + { + if (option_defs_us[num_options].key) + num_options++; + else + break; + } + + if (version >= 1) + { + /* Allocate US array */ + option_v1_defs_us = (struct retro_core_option_definition *) + calloc(num_options + 1, sizeof(struct retro_core_option_definition)); + + /* Copy parameters from option_defs_us array */ + for (i = 0; i < num_options; i++) + { + struct retro_core_option_v2_definition *option_def_us = &option_defs_us[i]; + struct retro_core_option_value *option_values = option_def_us->values; + struct retro_core_option_definition *option_v1_def_us = &option_v1_defs_us[i]; + struct retro_core_option_value *option_v1_values = option_v1_def_us->values; + + option_v1_def_us->key = option_def_us->key; + option_v1_def_us->desc = option_def_us->desc; + option_v1_def_us->info = option_def_us->info; + option_v1_def_us->default_value = option_def_us->default_value; + + /* Values must be copied individually... */ + while (option_values->value) + { + option_v1_values->value = option_values->value; + option_v1_values->label = option_values->label; + + option_values++; + option_v1_values++; + } + } + +#ifndef HAVE_NO_LANGEXTRA + if (environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) && + (language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH) && + options_intl[language]) + option_defs_intl = options_intl[language]->definitions; + + if (option_defs_intl) + { + /* Determine number of intl options */ + while (true) + { + if (option_defs_intl[num_options_intl].key) + num_options_intl++; + else + break; + } + + /* Allocate intl array */ + option_v1_defs_intl = (struct retro_core_option_definition *) + calloc(num_options_intl + 1, sizeof(struct retro_core_option_definition)); + + /* Copy parameters from option_defs_intl array */ + for (i = 0; i < num_options_intl; i++) + { + struct retro_core_option_v2_definition *option_def_intl = &option_defs_intl[i]; + struct retro_core_option_value *option_values = option_def_intl->values; + struct retro_core_option_definition *option_v1_def_intl = &option_v1_defs_intl[i]; + struct retro_core_option_value *option_v1_values = option_v1_def_intl->values; + + option_v1_def_intl->key = option_def_intl->key; + option_v1_def_intl->desc = option_def_intl->desc; + option_v1_def_intl->info = option_def_intl->info; + option_v1_def_intl->default_value = option_def_intl->default_value; + + /* Values must be copied individually... */ + while (option_values->value) + { + option_v1_values->value = option_values->value; + option_v1_values->label = option_values->label; + + option_values++; + option_v1_values++; + } + } + } + + core_options_v1_intl.us = option_v1_defs_us; + core_options_v1_intl.local = option_v1_defs_intl; + + environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL, &core_options_v1_intl); +#else + environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS, option_v1_defs_us); +#endif + } + else + { + /* Allocate arrays */ + variables = (struct retro_variable *)calloc(num_options + 1, + sizeof(struct retro_variable)); + values_buf = (char **)calloc(num_options, sizeof(char *)); + + if (!variables || !values_buf) + goto error; + + /* Copy parameters from option_defs_us array */ + for (i = 0; i < num_options; i++) + { + const char *key = option_defs_us[i].key; + const char *desc = option_defs_us[i].desc; + const char *default_value = option_defs_us[i].default_value; + struct retro_core_option_value *values = option_defs_us[i].values; + size_t buf_len = 3; + size_t default_index = 0; + + values_buf[i] = NULL; + + /* Skip options that are irrelevant when using the + * old style core options interface */ + if (strcmp(key, "genesis_plus_gx_show_advanced_audio_settings") == 0) + continue; + + if (desc) + { + size_t num_values = 0; + + /* Determine number of values */ + while (true) + { + if (values[num_values].value) + { + /* Check if this is the default value */ + if (default_value) + if (strcmp(values[num_values].value, default_value) == 0) + default_index = num_values; + + buf_len += strlen(values[num_values].value); + num_values++; + } + else + break; + } + + /* Build values string */ + if (num_values > 0) + { + buf_len += num_values - 1; + buf_len += strlen(desc); + + values_buf[i] = (char *)calloc(buf_len, sizeof(char)); + if (!values_buf[i]) + goto error; + + strcpy(values_buf[i], desc); + strcat(values_buf[i], "; "); + + /* Default value goes first */ + strcat(values_buf[i], values[default_index].value); + + /* Add remaining values */ + for (j = 0; j < num_values; j++) + { + if (j != default_index) + { + strcat(values_buf[i], "|"); + strcat(values_buf[i], values[j].value); + } + } + } + } + + variables[option_index].key = key; + variables[option_index].value = values_buf[i]; + option_index++; + } + + /* Set variables */ + environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables); + } + +error: + /* Clean up */ + + if (option_v1_defs_us) + { + free(option_v1_defs_us); + option_v1_defs_us = NULL; + } + +#ifndef HAVE_NO_LANGEXTRA + if (option_v1_defs_intl) + { + free(option_v1_defs_intl); + option_v1_defs_intl = NULL; + } +#endif + + if (values_buf) + { + for (i = 0; i < num_options; i++) + { + if (values_buf[i]) + { + free(values_buf[i]); + values_buf[i] = NULL; + } + } + + free(values_buf); + values_buf = NULL; + } + + if (variables) + { + free(variables); + variables = NULL; + } + } +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libretro/libretro_core_options_intl.h b/libretro/libretro_core_options_intl.h new file mode 100644 index 0000000..ca8cdd0 --- /dev/null +++ b/libretro/libretro_core_options_intl.h @@ -0,0 +1,1177 @@ +#ifndef LIBRETRO_CORE_OPTIONS_INTL_H__ +#define LIBRETRO_CORE_OPTIONS_INTL_H__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1500 && _MSC_VER < 1900) +/* https://support.microsoft.com/en-us/kb/980263 */ +#pragma execution_character_set("utf-8") +#pragma warning(disable:4566) +#endif + +#include + +/* + ******************************** + * VERSION: 1.3 + ******************************** + * + * - 1.3: Move translations to libretro_core_options_intl.h + * - libretro_core_options_intl.h includes BOM and utf-8 + * fix for MSVC 2010-2013 + * - Added HAVE_NO_LANGEXTRA flag to disable translations + * on platforms/compilers without BOM support + * - 1.2: Use core options v1 interface when + * RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1 + * (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1) + * - 1.1: Support generation of core options v0 retro_core_option_value + * arrays containing options with a single value + * - 1.0: First commit +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + ******************************** + * Core Option Definitions + ******************************** +*/ + +/* RETRO_LANGUAGE_JAPANESE */ + +/* RETRO_LANGUAGE_FRENCH */ + +/* RETRO_LANGUAGE_SPANISH */ + +/* RETRO_LANGUAGE_GERMAN */ + +/* RETRO_LANGUAGE_ITALIAN */ + +/* RETRO_LANGUAGE_DUTCH */ + +/* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */ + +struct retro_core_option_v2_category option_cats_pt_br[] = { + { + "system", + NULL, + NULL + }, + { + "video", + NULL, + NULL + }, + { + "audio", + NULL, + NULL + }, + { + "input", + NULL, + NULL + }, + { + "hacks", + NULL, + NULL + }, + { + "channel_volume", + NULL, + NULL + }, + { NULL, NULL, NULL }, +}; + +struct retro_core_option_v2_definition option_defs_pt_br[] = { + { + "genesis_plus_gx_system_hw", + "Hardware do sistema", + NULL, + "Executa o conteúdo carregado com um console emulado específico. 'Automático' selecionará o sistema mais apropriado para o jogo atual.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_region_detect", + "Região do sistema", + NULL, + "Especifica de que região o sistema é. Para consoles que não sejam o Game Gear, 'PAL' é de 50hz enquanto 'NTSC' é de 60hz. s jogos podem ser executados mais rapidamente ou mais lentamente do que o normal se a região incorreta estiver selecionada.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_force_dtack", + "Bloqueios do sistema", + NULL, + "Emula os bloqueios do sistema que ocorrem no hardware real ao executar acesso ilegal a endereços. Isso só deve ser desativado ao reproduzir certas demos e homebrews que dependem de comportamento ilegal para a operação correta.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_bios", + "ROM de inicialização do sistema", + NULL, + "Usa a BIOS/bootloader oficial para o hardware emulado, se estiver presente no diretório de sistema do RetroArch. Exibe a sequência/animação da inicialização específica do console e executa o conteúdo carregado.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_bram", + "Sistema BRAM do CD", + NULL, + "Ao executar o conteúdo de CD da Sega, especifica se um único arquivo salvo será salvo entre todos os jogos de uma região específica (Por-BIOS) ou se será criado um arquivo salvo separado para cada jogo (Por-Jogo). Observe que o CD da Sega possui armazenamento interno limitado, suficiente apenas para alguns títulos. Para evitar ficar sem espaço, a configuração 'Por jogo' é recomendada.", + NULL, + NULL, + { + { "per bios", "Por-BIOS" }, + { "per game", "Por-Jogo" }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_addr_error", + "Erro de endereçamento do 68K", + NULL, + "A CPU do Mega Drive (Motorola 68000) produz um erro de endereçamento (falha) ao tentar executar o acesso desalinhado à memória. A ativação do 'Erro de endereçamento do 68K' simula esse comportamento. Ele deve ser desativado apenas durante a execução de hacks de ROMs, pois geralmente são desenvolvidos usando emuladores menos precisos e podem contar com acesso inválido à RAM para a operação correta.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_lock_on", + "Cartucho Lock-On", + NULL, + "A tecnologia Lock-On é um recurso do Mega Drive que permite que um jogo mais antigo se conecte à porta de passagem de um cartucho especial para uma jogabilidade estendida ou alterada. Esta opção especifica qual tipo de cartucho especial 'lock-on' a ser emulado. Um arquivo do BIOS correspondente deve estar presente no diretório do sistema do RetroArch.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_ym2413", + "Som FM do Master System (YM2413)", + NULL, + "Ativa a emulação da unidade de som FM usada por certos jogos do Master System para obter uma saída de áudio aprimorada.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_ym2612", + "Som FM do Mega Drive", + NULL, +#ifdef HAVE_YM3438_CORE + "Seleciona o método usado para emular o sintetizador FM (gerador de som principal) do Mega Drive. As opções 'MAME' são rápidas e executam a toda velocidade na maioria dos sistemas. As opções 'Nuked' têm precisão de ciclo, qualidade muito alta e requisitos substanciais de CPU. O chip 'YM2612' é usado pelo Mega Drive Modelo 1 original. O 'YM3438' é usado em revisões posteriores do Mega Drive.", +#else + "Seleciona o método usado para emular o sintetizador de FM (gerador de som principal) do Mega Drive. O chip 'YM2612' é usado pelo Mega Drive Modelo 1 original. O 'YM3438' é usado em revisões posteriores do Mega Drive.", +#endif + NULL, + NULL, + { + { "mame (ym2612)", "MAME (YM2612)" }, + { "mame (asic ym3438)", "MAME (ASIC YM3438)" }, + { "mame (enhanced ym3438)", "MAME (YM3438 aprimorado)" }, +#ifdef HAVE_YM3438_CORE + { "nuked (ym2612)", "Nuked (YM2612)" }, + { "nuked (ym3438)", "Nuked (YM3438)" }, +#endif + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_sound_output", + "Saída de som", + NULL, + "Selecione reprodução de som estéreo ou mono.", + NULL, + NULL, + { + { "stereo", "Estéreo" }, + { "mono", "Mono" }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_psg_preamp", + "Nível de pré-amplificação do PSG", + NULL, + "Define o nível de pré-amplificação do áudio do Gerador de Som Programável (PSG) de 4 canais SN76496 emulado encontrado no Master System, Game Gear e Mega Drive.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_fm_preamp", + "Nível de pré-amplificação do FM", + NULL, + "Define o nível de pré-amplificação de áudio da Unidade de Som FM emulada do Master System.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_audio_filter", + "Filtro de áudio", + NULL, + "Ativa um filtro de áudio passa-baixo para simular melhor o som característico de um Mega Drive Modelo 1.", + NULL, + NULL, + { + { "disabled", NULL }, + { "low-pass", "Passa-baixo" }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_lowpass_range", + "Filtro passa-baixo %", + NULL, + "Especifica a frequência de corte do filtro de áudio passa-baixo. Um valor mais alto aumenta a 'força' percebida do filtro, uma vez que uma faixa mais ampla do espectro de alta frequência é atenuada.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, +#ifdef HAVE_EQ + { + "genesis_plus_gx_audio_eq_low", + "EQ baixa", + NULL, + "Ajuste a banda de alcance baixo do equalizador de áudio interno.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_audio_eq_mid", + "EQ média", + NULL, + "Ajuste a banda de alcance média do equalizador de áudio interno.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_audio_eq_high", + "EQ alta", + NULL, + "Ajuste a banda de alcance alta do equalizador de áudio interno.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, +#endif + { + "genesis_plus_gx_frameskip", + "Pulo de quadro", + NULL, + "Pula quadros para evitar subexecução do buffer de áudio (estalos). Melhora o desempenho em detrimento da suavidade visual. 'Auto' pula os quadros quando aconselhado pela interface. 'Manual' utiliza a configuração 'Limite de pulo de quadro (%).", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_frameskip_threshold", + "Limite de pulo de quadro (%)", + NULL, + "Quando 'Pulo de quadro' é definido como 'Manual', especifica o limite de ocupação do buffer de áudio (porcentagem) abaixo do qual os quadros serão ignorados. Valores mais altos reduzem o risco de estalos, fazendo com que os quadros sejam suprimidos com mais frequência.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_blargg_ntsc_filter", + "Filtro Blargg NTSC", + NULL, + "Aplica um filtro de vídeo para imitar vários sinais de TV NTSC.", + NULL, + NULL, + { + { "disabled", NULL }, + { "monochrome", "Monocromático" }, + { "composite", "Composto" }, + { "svideo", "S-Video" }, + { "rgb", "RGB" }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_lcd_filter", + "Filtro fantasma LCD", + NULL, + "Aplica um filtro de imagem fantasma para imitar as características de exibição dos painéis de LCD do Game Gear e do 'Genesis Nomad'.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_overscan", + "Bordas", + NULL, + "Ative esta opção para exibir as regiões de overscan na parte superior/inferior e/ou esquerda/direita da tela. Normalmente, seriam ocultas pelo painel ao redor da borda de uma televisão de definição padrão.", + NULL, + NULL, + { + { "disabled", NULL }, + { "top/bottom", "Superior/inferior" }, + { "left/right", "Esquerda/direita" }, + { "full", "Completa" }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_gg_extra", + "Tela estendida do Game Gear", + NULL, + "Força os títulos do Game Gear a serem executados no modo 'SMS', com uma resolução aumentada de 256x192. Pode mostrar conteúdo adicional, mas geralmente exibe uma borda de dados corrompidos/indesejados de imagem.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_left_border", + "Ocultar borda esquerda do Master System", + NULL, + "Corta 8 pixels do lado esquerdo e direito da tela ao executar os jogos do Master System, ocultando assim a borda vista no lado esquerdo da tela", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_aspect_ratio", + "Proporção de aspecto fornecida pelo núcleo", + NULL, + "Escolhe a proporção preferida do conteúdo. Isso se aplicará somente quando a proporção do RetroArch estiver configurada como 'Core provided' nas configurações de vídeo.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_render", + "Saída entrelaçada modo 2", + NULL, + "O Modo entrelaçado 2 permite que o Mega Drive produza uma imagem de 320x448 de altura dupla (alta resolução) desenhando linhas de varredura alternativas em cada quadro (isso é usado pelos modos multijogador do 'Sonic the Hedgehog 2' e 'Combat Cars'). O 'Campo duplo' imita o hardware original, produzindo uma imagem nítida com artefatos trêmulos/entrelaçados. O 'Campo único' é um filtro de desentrelaçamento, que estabiliza a imagem, mas causa desfoque suave.", + NULL, + NULL, + { + { "single field", "Campo único" }, + { "double field", "Campo duplo" }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_gun_cursor", + "Mostrar mira da pistola", + NULL, + "Exibe a mira da pistola de luz ao usar os tipos de dispositivo de entrada 'MD Menacer', 'MD Justifiers' e 'MS Light Phaser'.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_gun_input", + "Saída da Pistola de luz", + NULL, + "Usa uma entrada 'Pistola de luz' ou 'Toque na tela' controlada por mouse.", + NULL, + NULL, + { + { "lightgun", "Pistola de luz" }, + { "touchscreen", "Toque na tela" }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_invert_mouse", + "Inverter eixo Y do mouse", + NULL, + "Inverte o eixo Y do tipo de dispositivo de entrada 'MD Mouse'.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, +#ifdef HAVE_OVERCLOCK + { + "genesis_plus_gx_overclock", + "Velocidade da CPU", + NULL, + "Faz um overclock da CPU emulada. Pode reduzir a desaceleração, mas pode causar falhas.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, +#endif + { + "genesis_plus_gx_no_sprite_limit", + "Remover limite de sprite por linha", + NULL, + "Remove o limite de sprite por varredura do hardware, 8 (Master System) ou 20 (Mega Drive). Isso reduz a tremulação (flickering), mas pode causar falhas visuais, pois alguns jogos exploram o limite de hardware para gerar efeitos especiais.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, +#ifdef USE_PER_SOUND_CHANNELS_CONFIG + { + "genesis_plus_gx_show_advanced_audio_settings", + "Mostrar configurações avançadas de volume de áudio (reabrir menu)", + NULL, + "Ativa a configuração dos parâmetros do canal de áudio de baixo nível. NOTA: O Menu rápido deve ser alternado para que esta configuração tenha efeito.", + NULL, + NULL, + { + { NULL, NULL}, + }, + NULL + }, + { + "genesis_plus_gx_psg_channel_0_volume", + "% do volume do tom do PSG do canal 0", + NULL, + "Reduz o volume do tom PSG do canal 0.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_psg_channel_1_volume", + "% do volume do tom do PSG do canal 1", + NULL, + "Reduz o volume do tom PSG do canal 1.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_psg_channel_2_volume", + "% do volume do tom do PSG do canal 2", + NULL, + "Reduz o volume do tom PSG do canal 2.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_psg_channel_3_volume", + "% do volume de ruído do PSG do canal 3", + NULL, + "Reduz o volume do ruído do PSG do canal 3.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_md_channel_0_volume", + "% do volume do FM do Mega Drive do canal 0", + NULL, + "Reduz o volume do FM do Mega Drive do canal 0. Só funciona com o emulador de FM do MAME.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_md_channel_1_volume", + "% do volume do FM do Mega Drive do canal 1", + NULL, + "Reduz o volume do FM do Mega Drive do canal 1. Só funciona com o emulador de FM do MAME.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_md_channel_2_volume", + "% do volume do FM do Mega Drive do canal 2", + NULL, + "Reduz o volume do FM do Mega Drive do canal 2. Só funciona com o emulador de FM do MAME.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_md_channel_3_volume", + "% do volume do FM do Mega Drive do canal 3", + NULL, + "Reduz o volume do FM do Mega Drive do canal 3. Só funciona com o emulador de FM do MAME.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_md_channel_4_volume", + "% do volume do FM do Mega Drive do canal 4", + NULL, + "Reduz o volume do FM do Mega Drive do canal 4. Só funciona com o emulador de FM do MAME.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_md_channel_5_volume", + "% do volume do FM do Mega Drive do canal 5", + NULL, + "Reduz o volume do FM do Mega Drive do canal 5. Só funciona com o emulador de FM do MAME.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_sms_fm_channel_0_volume", + "% do volume do som FM do Master System (YM2413) do canal 0", + NULL, + "Reduz o volume do som FM do Master System do canal 0.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_sms_fm_channel_1_volume", + "% do volume do som FM do Master System (YM2413) do canal 1", + NULL, + "Reduz o volume do som FM do Master System do canal 1.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_sms_fm_channel_2_volume", + "% do volume do som FM do Master System (YM2413) do canal 2", + NULL, + "Reduz o volume do som FM do Master System do canal 2.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_sms_fm_channel_3_volume", + "% do volume do som FM do Master System (YM2413) do canal 3", + NULL, + "Reduz o volume do som FM do Master System do canal 3.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_sms_fm_channel_4_volume", + "% do volume do som FM do Master System (YM2413) do canal 4", + NULL, + "Reduz o volume do som FM do Master System do canal 4.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_sms_fm_channel_5_volume", + "% do volume do som FM do Master System (YM2413) do canal 5", + NULL, + "Reduz o volume do som FM do Master System do canal 5.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_sms_fm_channel_6_volume", + "% do volume do som FM do Master System (YM2413) do canal 6", + NULL, + "Reduz o volume do som FM do Master System do canal 6.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_sms_fm_channel_7_volume", + "% do volume do som FM do Master System (YM2413) do canal 7", + NULL, + "Reduz o volume do som FM do Master System do canal 7.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_sms_fm_channel_8_volume", + "% do volume do som FM do Master System (YM2413) do canal 8", + NULL, + "Reduz o volume do som FM do Master System do canal 8.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, +#endif + { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL }, +}; + +struct retro_core_options_v2 options_pt_br = { + option_cats_pt_br, + option_defs_pt_br +}; + +/* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */ + +/* RETRO_LANGUAGE_RUSSIAN */ + +/* RETRO_LANGUAGE_KOREAN */ + +/* RETRO_LANGUAGE_CHINESE_TRADITIONAL */ + +/* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */ + +/* RETRO_LANGUAGE_ESPERANTO */ + +/* RETRO_LANGUAGE_POLISH */ + +/* RETRO_LANGUAGE_VIETNAMESE */ + +/* RETRO_LANGUAGE_ARABIC */ + +/* RETRO_LANGUAGE_GREEK */ + +/* RETRO_LANGUAGE_TURKISH */ + +struct retro_core_option_v2_category option_cats_tr[] = { + { + "system", + NULL, + NULL + }, + { + "video", + NULL, + NULL + }, + { + "audio", + NULL, + NULL + }, + { + "input", + NULL, + NULL + }, + { + "hacks", + NULL, + NULL + }, + { + "channel_volume", + NULL, + NULL + }, + { NULL, NULL, NULL }, +}; + +struct retro_core_option_v2_definition option_defs_tr[] = { + { + "genesis_plus_gx_system_hw", + "Sistem Donanımı", + NULL, + "Yüklenen içeriği belirli bir öykünmüş konsolla çalıştırır. 'Otomatik' mevcut oyun için en uygun sistemi seçecektir.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_region_detect", + "Sistem Bölgesi", + NULL, + "Sistemin hangi bölgeden olduğunu belirtin. Game Gear dışındaki konsollar için 'PAL' 50hz, 'NTSC' 60hz'dir. Yanlış bölge seçiliyse, oyunlar normalden daha hızlı veya daha yavaş çalışabilir.", + NULL, + NULL, + { + { "auto", "Otomatik" }, + { "ntsc-u", "NTSC-U" }, + { "pal", "PAL" }, + { "ntsc-j", "NTSC-J" }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_force_dtack", + "Sistem Kilidi", + NULL, + "Geçersiz adres erişimi gerçekleştirirken gerçek donanımda meydana gelen sistem kilitlemelerine öykünün. Bu, yalnızca doğru işlem için yasadışı davranışa dayanan belirli demolar ve homebrew oynatılırken devre dışı bırakılmalıdır.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_bios", + "Sistem Önyükleme ROM'u", + NULL, + "RetroArch'ın sistem dizininde varsa, öykünülmüş donanım için resmi BIOS/önyükleyici kullanın. Konsola özgü başlangıç sırası/animasyonu görüntüler, ardından yüklü içeriği çalıştırır.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_bram", + "CD Sistemi BRAM'i", + NULL, + "Sega CD içeriği çalıştırılırken, belirli bir bölgedeki tüm oyunlar arasında (BIOS başına) tek bir kayıt dosyasının paylaşılmasını mı yoksa her oyun için ayrı bir kayıt dosyası oluşturup oluşturmamayı (Oyun Başına) belirtir. Sega CD'sinin sınırlı bir dahili depolama alanına sahip olduğunu ve yalnızca bir avuç başlık için yeterli olduğunu unutmayın. Boşluktan kaçınmak için 'Oyun Başına' ayarı önerilir.", + NULL, + NULL, + { + { "per bios", "BIOS Başına" }, + { "per game", "Oyun Başına" }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_addr_error", + "68K Adres Hatası", + NULL, + "Genesis CPU (Motorola 68000), hizalanmamış hafıza erişimi gerçekleştirmeye çalışırken bir Adres Hatası (kilitlenme) üretir. '68K Adres Hatası' özelliğini etkinleştirmek bu davranışı simüle eder. ROM hacklerini oynarken sadece devre dışı bırakılmalıdır, çünkü bunlar daha az doğru emülatörler kullanılarak geliştirilir ve doğru işlem için geçersiz RAM erişimine güvenebilir.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_lock_on", + "Kartuş Kilitleme", + NULL, + "Kilitleme Teknolojisi, eski bir oyunun genişletilmiş veya değiştirilmiş bir oyun için özel bir kartuşun geçiş portuna bağlanmasına izin veren bir Genesis özelliğidir. Bu seçenek, hangi tür 'özel kilitleme' kartuşunun taklit edileceğini belirler. RetroArch'ın sistem dizininde ilgili bir bios dosyası bulunmalıdır.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_ym2413", + "Master System FM (YM2413)", + NULL, + "Gelişmiş ses çıkışı için bazı Sega Mark III / Master System oyunları tarafından kullanılan FM Ses Ünitesinin emülasyonunu etkinleştirin.", + NULL, + NULL, + { + { "auto", "Otomatik" }, + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_ym2612", + "Mega Drive / Genesis FM", + NULL, +#ifdef HAVE_YM3438_CORE + "Mega Drive / Genesis'in FM sentezleyicisini (ana ses üreteci) taklit etmek için kullanılan yöntemi seçin. 'MAME' seçenekleri hızlı ve çoğu sistemde tam hızda çalışıyor. 'Nuke' seçenekleri döngüde doğrudur, çok kaliteli ve önemli CPU gereksinimlerine sahiptir. 'YM2612' yongası, orijinal Model 1 Genesis tarafından kullanılır. 'YM3438' daha sonra Genesis revizyonlarında kullanılır.", +#else + "Mega Drive / Genesis'in FM sentezleyicisini (ana ses üreteci) taklit etmek için kullanılan yöntemi seçin. 'YM2612' yongası, orijinal Model 1 Genesis tarafından kullanılır. 'YM3438' daha sonra Genesis revizyonlarında kullanılır.", +#endif + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_sound_output", + "Ses Çıkışı", + NULL, + "Stereo veya mono ses üretimini seçin.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_psg_preamp", + "PSG Preamp Level", + NULL, + "Master System, Game Gear ve Genesis'de bulunan öykünmüş SN76496 4 kanallı Programlanabilir Ses Üretecinin ses ön yükselticisi seviyesini ayarlayın.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_fm_preamp", + "FM Preamp Level", + NULL, + "Öykünülmüş Sega Mark III/Master System FM Ses Ünitesinin ses ön yükselticisi seviyesini ayarlayın.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_audio_filter", + "Ses Filtresi", + NULL, + "Model 1 Genesis'in karakteristik sesini daha iyi simüle etmek için düşük geçişli bir ses filtresini etkinleştirin.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_lowpass_range", + "Low-Pass Filtresi %", + NULL, + "Düşük ses geçiş filtresinin kesme frekansını belirtin. Daha yüksek bir değer, yüksek frekans spektrumunun daha geniş bir aralığı azaltıldığı için filtrenin algılanan gücünü arttırır.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, +#ifdef HAVE_EQ + { + "genesis_plus_gx_audio_eq_low", + "EQ Low", + NULL, + "Dahili ses ekolayzırın düşük aralık bandını ayarlayın.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_audio_eq_mid", + "EQ Mid", + NULL, + "Dahili ses ekolayzerinin orta aralık bandını ayarlayın.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_audio_eq_high", + "EQ High", + NULL, + "Dahili ses ekolayzerinin yüksek aralık bandını ayarlayın.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, +#endif + { + "genesis_plus_gx_blargg_ntsc_filter", + "Blargg NTSC Filtresi", + NULL, + "Çeşitli NTSC TV sinyallerini taklit etmek için bir video filtresi uygulayın.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_lcd_filter", + "LCD Gölgelenme Filtresi", + NULL, + "Game Gear ve 'Genesis Nomad' LCD panellerinin ekran özelliklerini taklit etmek için bir görüntü “gölgelenme” filtresi uygulayın.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_overscan", + "Çerçeveler", + NULL, + "Aşırı tarama bölgelerini ekranın üstünde/altında ve/veya sol/sağında görüntülemek için bunu etkinleştirin. Bunlar normalde standart tanımlı bir televizyonun kenarındaki çerçeve tarafından gizlenir.", + NULL, + NULL, + { + { "disabled", NULL }, + { "top/bottom", "Üstünde/Atında" }, + { "left/right", "Sol/Sağ" }, + { "full", "Tam" }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_gg_extra", + "Game Gear Genişletilmiş Ekran", + NULL, + "Game Gear oyunlarını 256x192 çözünürlüğe sahip 'SMS' modunda çalıştırmaya zorlar. Ek içerik gösterebilir, ancak genellikle bozuk/istenmeyen resim verilerinin kenarlığını görüntüler.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_aspect_ratio", + "Çekirdek Tarafından Sağlanan En Boy Oranı", + NULL, + "Tercih edilen içerik en boy oranını seçin. Bu, yalnızca RetroArch’ın en boy oranı Video ayarlarında 'Çekirdek Tarafından Sağlanan' olarak ayarlandığında uygulanacaktır.", + NULL, + NULL, + { + { "auto", "Otomatik" }, + { "NTSC PAR", NULL }, + { "PAL PAR", NULL }, + }, + NULL + }, + { + "genesis_plus_gx_render", + "Geçmeli Mod 2 Çıkışı", + NULL, + "Geçmeli Mod 2, Genesis'e, her kareye alternatif tarama çizgileri çizerek (bu, 'Sonic the Hedgehog 2' ve 'Savaş Arabaları' çok oyunculu modları tarafından kullanılır) çift yükseklikte (yüksek çözünürlüklü) 320x448 görüntü vermesini sağlar. 'Çift Alan' orijinal donanımı taklit eder, titreyen/birbirine geçen eserler ile keskin bir görüntü oluşturur. 'Tek Alan', görüntüyü dengeleyen ancak hafif bulanıklığa neden olan, birbirinin yerine geçen bir filtredir.", + NULL, + NULL, + { + { "single field", "Tek Alan" }, + { "double field", "Çift Alan" }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_gun_cursor", + "Light Gun Göstergesini Göster", + NULL, + "'MD Menacer', 'MD Justifiers' ve 'MS Light Phaser' giriş cihazı tiplerini kullanırken Light Gun göstergelerini gösterin.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_gun_input", + "Light Gun Girdisi", + NULL, + "Fare kontrollü 'Light Gun' veya 'Dokunmatik Ekran' girişi kullanın.", + NULL, + NULL, + { + { "lightgun", "Light Gun" }, + { "touchscreen", "Dokunmatik Ekran" }, + { NULL, NULL }, + }, + NULL + }, + { + "genesis_plus_gx_invert_mouse", + "Fare Y Eksenini Ters Çevir", + NULL, + "'MD Fare' giriş cihazı türünün Y eksenini ters çevirir.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, +#ifdef HAVE_OVERCLOCK + { + "genesis_plus_gx_overclock", + "CPU Hızı", + NULL, + "Öykünülmüş CPU hız aşırtması. Yavaşlamayı azaltabilir, ancak aksaklığa neden olabilir.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, +#endif + { + "genesis_plus_gx_no_sprite_limit", + "Satır Başına Sprite Limitini Kaldır", + NULL, + "8 (Ana Sistem) veya 20 (Genesis) tarama başına sprite donanım sınırını kaldırır. Bu, titremeyi azaltır ancak bazı oyunlar özel efektler oluşturmak için donanım sınırını kullandığı için görsel hatalara neden olabilir.", + NULL, + NULL, + { + { NULL, NULL }, + }, + NULL + }, + { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL }, +}; + +struct retro_core_options_v2 options_tr = { + option_cats_tr, + option_defs_tr +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libretro/osd.h b/libretro/osd.h index e8c7461..7ca38b3 100644 --- a/libretro/osd.h +++ b/libretro/osd.h @@ -131,6 +131,11 @@ typedef struct uint8 gun_cursor; uint32 overclock; uint8 no_sprite_limit; +#ifdef USE_PER_SOUND_CHANNELS_CONFIG + unsigned int psg_ch_volumes[4]; + int32 md_ch_volumes[6]; + signed int sms_fm_ch_volumes[9]; +#endif } t_config; extern t_config config;