aboutsummaryrefslogtreecommitdiff
path: root/patches/snes9x2002
diff options
context:
space:
mode:
authorneonloop2021-08-04 15:09:12 +0000
committerneonloop2021-08-04 15:09:12 +0000
commit99632f66e74fc57c463072be312d634aeb67bc61 (patch)
treee4ccaf52b93d04c69865d82556e2ce4cd3a6c599 /patches/snes9x2002
downloadpicoarch-99632f66e74fc57c463072be312d634aeb67bc61.tar.gz
picoarch-99632f66e74fc57c463072be312d634aeb67bc61.tar.bz2
picoarch-99632f66e74fc57c463072be312d634aeb67bc61.zip
Initial commit
Diffstat (limited to 'patches/snes9x2002')
-rw-r--r--patches/snes9x2002/0001-trimui-support.patch50
-rw-r--r--patches/snes9x2002/0002-core-options.patch13
-rw-r--r--patches/snes9x2002/1000-update-libretro-h.patch876
-rw-r--r--patches/snes9x2002/1001-audio-frameskip.patch247
4 files changed, 1186 insertions, 0 deletions
diff --git a/patches/snes9x2002/0001-trimui-support.patch b/patches/snes9x2002/0001-trimui-support.patch
new file mode 100644
index 0000000..eadb54c
--- /dev/null
+++ b/patches/snes9x2002/0001-trimui-support.patch
@@ -0,0 +1,50 @@
+diff --git a/Makefile b/Makefile
+index e42d169..a1f8794 100644
+--- a/Makefile
++++ b/Makefile
+@@ -244,6 +244,23 @@ else ifeq ($(platform), gcw0)
+ CFLAGS += -fno-builtin -fno-exceptions
+ CFLAGS += -DPATH_MAX=256 -DFAST_LSB_WORD_ACCESS
+
++else ifeq ($(platform), trimui)
++ TARGET := $(TARGET_NAME)_libretro.so
++ CC = $(CROSS_COMPILE)gcc
++ AR = $(CROSS_COMPILE)ar
++ SHARED := -shared -Wl,--version-script=libretro/link.T -Wl,--no-undefined
++ CFLAGS += -fsingle-precision-constant -fno-PIC -flto
++ CFLAGS += -DLSB_FIRST -DFAST_ALIGNED_LSB_WORD_ACCESS -DRIGHTSHIFT_IS_SAR
++ CFLAGS += -std=c99 -fomit-frame-pointer -ffast-math -mcpu=arm926ej-s -mtune=arm926ej-s
++ LDFLAGS += -flto
++ OFLAGS += -Ofast -DNDEBUG=1
++ ARM_ASM = 1
++ ASM_CPU = 0
++ ASM_SPC700 = 0
++ ifeq (,$(DEBUG))
++ LDFLAGS += -s
++ endif
++
+ # Windows MSVC 2010 x64
+ else ifeq ($(platform), windows_msvc2010_x64)
+ CC = cl.exe
+@@ -381,12 +398,21 @@ LIBRETRO_DIR := ./libretro
+
+ ifeq ($(DEBUG), 1)
+ DEFINES += -O0 -g
++else ifneq (,$(OFLAGS))
++DEFINES += $(OFLAGS)
+ else ifneq (,$(findstring msvc,$(platform)))
+ DEFINES += -O2 -DNDEBUG=1
+ else
+ DEFINES += -O3 -DNDEBUG=1
+ endif
+
++ifeq ($(PROFILE), GENERATE)
++ CFLAGS += -fprofile-generate=./profile/snes9x2002
++ LDFLAGS += -lgcov
++else ifeq ($(PROFILE), APPLY)
++ CFLAGS += -fprofile-use -fprofile-dir=../profile/snes9x2002 -fbranch-probabilities
++endif
++
+ ifneq (,$(findstring msvc,$(platform)))
+ ifeq ($(DEBUG),1)
+ DEFINES += -MTd
diff --git a/patches/snes9x2002/0002-core-options.patch b/patches/snes9x2002/0002-core-options.patch
new file mode 100644
index 0000000..a908343
--- /dev/null
+++ b/patches/snes9x2002/0002-core-options.patch
@@ -0,0 +1,13 @@
+diff --git a/libretro/libretro.c b/libretro/libretro.c
+index 81ce4aa..bc64376 100644
+--- a/libretro/libretro.c
++++ b/libretro/libretro.c
+@@ -339,7 +339,7 @@ void retro_init (void)
+ {
+ static const struct retro_variable vars[] =
+ {
+- { "snes9x2002_overclock_cycles", "Reduce Slowdown (Hack, Unsafe, Restart); disabled|compatible|max" },
++ { "snes9x2002_overclock_cycles", "Overclock (Restart); disabled|compatible|max" },
+ { NULL, NULL },
+ };
+
diff --git a/patches/snes9x2002/1000-update-libretro-h.patch b/patches/snes9x2002/1000-update-libretro-h.patch
new file mode 100644
index 0000000..b5984f2
--- /dev/null
+++ b/patches/snes9x2002/1000-update-libretro-h.patch
@@ -0,0 +1,876 @@
+diff --git a/libretro/libretro-common/include/libretro.h b/libretro/libretro-common/include/libretro.h
+index f97a969..32aa15f 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).
+@@ -278,6 +278,11 @@ enum retro_language
+ 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) */
+@@ -708,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 * --
+@@ -1087,10 +1095,10 @@ enum retro_mod
+
+ #define RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE (50 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+ /* float * --
+- * Float value that lets us know what target refresh rate
++ * 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
++ * The core can use the returned value to set an ideal
+ * refresh rate/framerate.
+ */
+
+@@ -1098,7 +1106,7 @@ enum retro_mod
+ /* 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
++ * 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'
+@@ -1174,7 +1182,7 @@ enum retro_mod
+ * "foo_option",
+ * "Speed hack coprocessor X",
+ * "Provides increased performance at the expense of reduced accuracy",
+- * {
++ * {
+ * { "false", NULL },
+ * { "true", NULL },
+ * { "unstable", "Turbo (Unstable)" },
+@@ -1246,6 +1254,245 @@ enum retro_mod
+ * 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()
++ */
++
+ /* VFS functionality */
+
+ /* File paths:
+@@ -1382,17 +1629,17 @@ typedef int (RETRO_CALLCONV *retro_vfs_closedir_t)(struct retro_vfs_dir_handle *
+ struct retro_vfs_interface
+ {
+ /* VFS API v1 */
+- retro_vfs_get_path_t get_path;
+- retro_vfs_open_t open;
+- retro_vfs_close_t close;
+- retro_vfs_size_t size;
+- retro_vfs_tell_t tell;
+- retro_vfs_seek_t seek;
+- retro_vfs_read_t read;
+- retro_vfs_write_t write;
+- retro_vfs_flush_t flush;
+- retro_vfs_remove_t remove;
+- retro_vfs_rename_t rename;
++ retro_vfs_get_path_t get_path;
++ retro_vfs_open_t open;
++ retro_vfs_close_t close;
++ retro_vfs_size_t size;
++ retro_vfs_tell_t tell;
++ retro_vfs_seek_t seek;
++ retro_vfs_read_t read;
++ retro_vfs_write_t write;
++ retro_vfs_flush_t flush;
++ retro_vfs_remove_t remove;
++ retro_vfs_rename_t rename;
+ /* VFS API v2 */
+ retro_vfs_truncate_t truncate;
+ /* VFS API v3 */
+@@ -1420,11 +1667,11 @@ struct retro_vfs_interface_info
+
+ enum retro_hw_render_interface_type
+ {
+- RETRO_HW_RENDER_INTERFACE_VULKAN = 0,
+- RETRO_HW_RENDER_INTERFACE_D3D9 = 1,
+- RETRO_HW_RENDER_INTERFACE_D3D10 = 2,
+- RETRO_HW_RENDER_INTERFACE_D3D11 = 3,
+- RETRO_HW_RENDER_INTERFACE_D3D12 = 4,
++ RETRO_HW_RENDER_INTERFACE_VULKAN = 0,
++ RETRO_HW_RENDER_INTERFACE_D3D9 = 1,
++ 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
+ };
+@@ -1922,6 +2169,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
+ };
+@@ -1930,6 +2181,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);
+@@ -2127,6 +2382,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.
+ * */
+@@ -2287,7 +2566,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.
+ *
+@@ -2345,6 +2625,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;
+@@ -2358,6 +2685,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.
+@@ -2388,6 +2736,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. */
+@@ -2408,7 +2854,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. */
+@@ -2450,6 +2896,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. */
+@@ -2607,6 +3260,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/patches/snes9x2002/1001-audio-frameskip.patch b/patches/snes9x2002/1001-audio-frameskip.patch
new file mode 100644
index 0000000..2154500
--- /dev/null
+++ b/patches/snes9x2002/1001-audio-frameskip.patch
@@ -0,0 +1,247 @@
+diff --git a/libretro/libretro.c b/libretro/libretro.c
+index bc64376..b09354a 100644
+--- a/libretro/libretro.c
++++ b/libretro/libretro.c
+@@ -96,6 +96,21 @@ bool8 ROMAPUEnabled = 0;
+ bool overclock_cycles = false;
+ int one_c, slow_one_c, two_c;
+
++static unsigned frameskip_type = 0;
++static unsigned frameskip_threshold = 0;
++static uint16_t frameskip_counter = 0;
++static unsigned frameskip_interval = 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 30
++
++static unsigned retro_audio_latency = 0;
++static bool update_audio_latency = false;
++
+ static memstream_t *s_stream = NULL;
+
+ static int s_open(const char *fname, const char *mode)
+@@ -208,6 +223,57 @@ void retro_get_system_info(struct retro_system_info *info)
+ info->block_extract = false;
+ }
+
++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 retro_set_audio_buff_status_cb(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))
++ {
++ retro_audio_buff_active = false;
++ retro_audio_buff_occupancy = 0;
++ retro_audio_buff_underrun = false;
++ retro_audio_latency = 0;
++ }
++ else
++ {
++ /* Frameskip is enabled - increase frontend
++ * audio latency to minimise potential
++ * buffer underruns */
++ uint32_t frame_time_usec = Settings.FrameTime;
++
++ if (Settings.ForceNTSC)
++ frame_time_usec = Settings.FrameTimeNTSC;
++ if (Settings.ForcePAL)
++ frame_time_usec = Settings.FrameTimePAL;
++
++ /* Set latency to 6x current frame time... */
++ retro_audio_latency = (unsigned)(6 * frame_time_usec / 1000);
++
++ /* ...then round up to nearest multiple of 32 */
++ retro_audio_latency = (retro_audio_latency + 0x1F) & ~0x1F;
++ }
++ }
++ else
++ {
++ environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
++ retro_audio_latency = 0;
++ }
++
++ update_audio_latency = true;
++}
++
+ static int16 audio_buf[0x10000];
+ static unsigned avail;
+ static float samplerate = 32040.5f;
+@@ -339,6 +405,9 @@ void retro_init (void)
+ {
+ static const struct retro_variable vars[] =
+ {
++ { "snes9x2002_frameskip", "Frameskip ; disabled|auto|threshold" },
++ { "snes9x2002_frameskip_threshold", "FS Threshold (%); 30|40|50|60" },
++ { "snes9x2002_frameskip_interval", "FS Interval; 1|2|3|4|5|6|7|8|9" },
+ { "snes9x2002_overclock_cycles", "Overclock (Restart); disabled|compatible|max" },
+ { NULL, NULL },
+ };
+@@ -383,6 +452,14 @@ void retro_deinit(void)
+ GFX.SubZBuffer_buffer = NULL;
+
+ libretro_supports_bitmasks = false;
++ frameskip_type = 0;
++ frameskip_threshold = 0;
++ frameskip_counter = 0;
++ retro_audio_buff_active = false;
++ retro_audio_buff_occupancy = 0;
++ retro_audio_buff_underrun = false;
++ retro_audio_latency = 0;
++ update_audio_latency = false;
+ }
+
+ void retro_reset (void)
+@@ -423,9 +500,42 @@ static void report_buttons (void)
+ }
+ }
+
+-static void check_variables(void)
++static void check_variables(bool first_run)
+ {
+ struct retro_variable var;
++ bool prev_force_ntsc;
++ bool prev_force_pal;
++ bool prev_frameskip_type;
++
++ var.key = "snes9x2002_frameskip";
++ var.value = NULL;
++
++ prev_frameskip_type = frameskip_type;
++ frameskip_type = 0;
++
++ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
++ {
++ if (strcmp(var.value, "auto") == 0)
++ frameskip_type = 1;
++ if (strcmp(var.value, "threshold") == 0)
++ frameskip_type = 2;
++ }
++
++ var.key = "snes9x2002_frameskip_threshold";
++ var.value = NULL;
++
++ frameskip_threshold = 30;
++
++ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
++ frameskip_threshold = strtol(var.value, NULL, 10);
++
++ var.key = "snes9x2002_frameskip_interval";
++ var.value = NULL;
++
++ frameskip_interval = 1;
++
++ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
++ frameskip_interval = strtol(var.value, NULL, 10);
+
+ var.key = "snes9x2002_overclock_cycles";
+ var.value = NULL;
+@@ -449,6 +559,13 @@ static void check_variables(void)
+ else
+ overclock_cycles = false;
+ }
++
++ /* Reinitialise frameskipping, if required */
++ if (!first_run &&
++ ((frameskip_type != prev_frameskip_type) ||
++ (Settings.ForceNTSC != prev_force_ntsc) ||
++ (Settings.ForcePAL != prev_force_pal)))
++ retro_set_audio_buff_status_cb();
+ }
+
+ //#define FRAME_SKIP
+@@ -458,7 +575,7 @@ void retro_run (void)
+ bool updated = false;
+
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
+- check_variables();
++ check_variables(false);
+
+ #ifdef FRAME_SKIP
+ IPPU.RenderThisFrame = !IPPU.RenderThisFrame;
+@@ -466,6 +583,53 @@ void retro_run (void)
+ IPPU.RenderThisFrame = TRUE;
+ #endif
+
++ /* Check whether current frame should
++ * be skipped */
++ if ((frameskip_type > 0) &&
++ retro_audio_buff_active &&
++ IPPU.RenderThisFrame)
++ {
++ bool skip_frame;
++
++ switch (frameskip_type)
++ {
++ case 1: /* auto */
++ skip_frame = retro_audio_buff_underrun;
++ break;
++ case 2: /* threshold */
++ skip_frame = (retro_audio_buff_occupancy < frameskip_threshold);
++ break;
++ default:
++ skip_frame = false;
++ break;
++ }
++
++ if (skip_frame)
++ {
++ if(frameskip_counter < frameskip_interval)
++ {
++ IPPU.RenderThisFrame = false;
++ frameskip_counter++;
++ }
++ else
++ frameskip_counter = 0;
++ }
++ else
++ frameskip_counter = 0;
++ }
++
++ /* If frameskip/timing settings have changed,
++ * update frontend audio latency
++ * > Can do this before or after the frameskip
++ * check, but doing it after means we at least
++ * retain the current frame's audio output */
++ if (update_audio_latency)
++ {
++ environ_cb(RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY,
++ &retro_audio_latency);
++ update_audio_latency = false;
++ }
++
+ poll_cb();
+ report_buttons();
+
+@@ -559,7 +723,7 @@ bool retro_load_game(const struct retro_game_info *game)
+ bool8 loaded;
+ enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565;
+
+- check_variables();
++ check_variables(true);
+
+ if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
+ return false;
+@@ -585,6 +749,7 @@ bool retro_load_game(const struct retro_game_info *game)
+
+ memset(audio_buf, 0, sizeof(audio_buf));
+
++ retro_set_audio_buff_status_cb();
+ return true;
+ }
+