aboutsummaryrefslogtreecommitdiff
path: root/patches/mame2000
diff options
context:
space:
mode:
authorneonloop2021-08-10 00:16:40 +0000
committerneonloop2021-08-10 00:16:40 +0000
commitebcd69fef4208af05aea6b1f963db1a9613bc470 (patch)
tree1c44f731176dc7479d843dd1b8e2e58c4100b87c /patches/mame2000
parent8ad26356f5e92bd396e58290217da55858345a4e (diff)
downloadpicoarch-ebcd69fef4208af05aea6b1f963db1a9613bc470.tar.gz
picoarch-ebcd69fef4208af05aea6b1f963db1a9613bc470.tar.bz2
picoarch-ebcd69fef4208af05aea6b1f963db1a9613bc470.zip
Adds mame2000 core and fixes options/scaler
Diffstat (limited to 'patches/mame2000')
-rw-r--r--patches/mame2000/0001-trimui-build.patch51
-rw-r--r--patches/mame2000/1000-update-libretro-h.patch863
-rw-r--r--patches/mame2000/1001-add-frameskip.patch276
-rw-r--r--patches/mame2000/1002-add-sound-options.patch72
-rw-r--r--patches/mame2000/1003-shorten-options.patch23
5 files changed, 1285 insertions, 0 deletions
diff --git a/patches/mame2000/0001-trimui-build.patch b/patches/mame2000/0001-trimui-build.patch
new file mode 100644
index 0000000..52492b8
--- /dev/null
+++ b/patches/mame2000/0001-trimui-build.patch
@@ -0,0 +1,51 @@
+diff --git a/Makefile b/Makefile
+index 4b0667a..8ebaebe 100644
+--- a/Makefile
++++ b/Makefile
+@@ -335,6 +335,26 @@ else ifeq ($(platform), gcw0)
+ DISABLE_ERROR_LOGGING := 1
+ CFLAGS += -march=mips32 -mtune=mips32r2 -mhard-float
+
++# Trimui
++else ifeq ($(platform), trimui)
++ TARGET = $(TARGET_NAME)_libretro.so
++ CC = $(CROSS_COMPILE)gcc
++ CXX = $(CROSS_COMPILE)g++
++ AR = $(CROSS_COMPILE)ar
++ LDFLAGS += -shared -Wl,--version-script=link.T -Wl,-no-undefined -fno-PIC -flto
++ CFLAGS += -fomit-frame-pointer -ffast-math -mcpu=arm926ej-s -mtune=arm926ej-s -fno-PIC -flto
++ CFLAGS += -fsingle-precision-constant
++ CFLAGS += -fno-exceptions -fcommon -fno-unwind-tables -fno-asynchronous-unwind-tables
++ CFLAGS += -funroll-loops -fno-optimize-sibling-calls
++ DISABLE_ERROR_LOGGING := 1
++ ARM = 1
++ USE_CYCLONE = 1
++ USE_DRZ80 = 1
++ OPTIMIZE := -Ofast -DNDEBUG
++ ifeq (,$(DEBUG))
++ LDFLAGS += -s
++ endif
++
+ # Windows MSVC 2010 x64
+ else ifeq ($(platform), windows_msvc2010_x64)
+ CC = cl.exe
+@@ -522,10 +542,19 @@ endif
+
+ ifeq ($(DEBUG), 1)
+ CFLAGS += -O0 -g
++else ifneq (,$(OPTIMIZE))
++CFLAGS += $(OPTIMIZE)
+ else
+ CFLAGS += -O2 -DNDEBUG
+ endif
+
++ifeq ($(PROFILE), GENERATE)
++ CFLAGS += -fprofile-generate=./profile/mame2000
++ LDFLAGS += -lgcov
++else ifeq ($(PROFILE), APPLY)
++ CFLAGS += -fprofile-use -fprofile-dir=../profile/mame2000 -fbranch-probabilities
++endif
++
+ # compiler, linker and utilities
+ RM = rm -f
+ LIBS = -lm
diff --git a/patches/mame2000/1000-update-libretro-h.patch b/patches/mame2000/1000-update-libretro-h.patch
new file mode 100644
index 0000000..c1568ac
--- /dev/null
+++ b/patches/mame2000/1000-update-libretro-h.patch
@@ -0,0 +1,863 @@
+diff --git a/src/libretro/libretro-common/include/libretro.h b/src/libretro/libretro-common/include/libretro.h
+index 2b7fc95..32aa15f 100644
+--- a/src/libretro/libretro-common/include/libretro.h
++++ b/src/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
+@@ -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'
+@@ -1117,7 +1125,7 @@ enum retro_mod
+ * This may be still be done regardless of the core options
+ * interface version.
+ *
+- * If version is 1 however, core options may instead be set by
++ * If version is >= 1 however, core options may instead be set by
+ * passing an array of retro_core_option_definition structs to
+ * RETRO_ENVIRONMENT_SET_CORE_OPTIONS, or a 2D array of
+ * retro_core_option_definition structs to RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL.
+@@ -1132,8 +1140,8 @@ enum retro_mod
+ * GET_VARIABLE.
+ * This allows the frontend to present these variables to
+ * a user dynamically.
+- * This should only be called if RETRO_ENVIRONMENT_GET_ENHANCED_CORE_OPTIONS
+- * returns an API version of 1.
++ * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION
++ * returns an API version of >= 1.
+ * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES.
+ * This should be called the first time as early as
+ * possible (ideally in retro_set_environment).
+@@ -1169,8 +1177,6 @@ enum retro_mod
+ * i.e. it should be feasible to cycle through options
+ * without a keyboard.
+ *
+- * First entry should be treated as a default.
+- *
+ * Example entry:
+ * {
+ * "foo_option",
+@@ -1196,8 +1202,8 @@ enum retro_mod
+ * GET_VARIABLE.
+ * This allows the frontend to present these variables to
+ * a user dynamically.
+- * This should only be called if RETRO_ENVIRONMENT_GET_ENHANCED_CORE_OPTIONS
+- * returns an API version of 1.
++ * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION
++ * returns an API version of >= 1.
+ * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES.
+ * This should be called the first time as early as
+ * possible (ideally in retro_set_environment).
+@@ -1257,7 +1263,236 @@ enum retro_mod
+ *
+ * 'data' points to an unsigned variable
+ */
+-
++
++#define RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION 57
++ /* unsigned * --
++ * Unsigned value is the API version number of the disk control
++ * interface supported by the frontend. If callback return false,
++ * API version is assumed to be 0.
++ *
++ * In legacy code, the disk control interface is defined by passing
++ * a struct of type retro_disk_control_callback to
++ * RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE.
++ * This may be still be done regardless of the disk control
++ * interface version.
++ *
++ * If version is >= 1 however, the disk control interface may
++ * instead be defined by passing a struct of type
++ * retro_disk_control_ext_callback to
++ * RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE.
++ * This allows the core to provide additional information about
++ * disk images to the frontend and/or enables extra
++ * disk control functionality by the frontend.
++ */
++
++#define RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE 58
++ /* const struct retro_disk_control_ext_callback * --
++ * Sets an interface which frontend can use to eject and insert
++ * disk images, and also obtain information about individual
++ * disk image files registered by the core.
++ * This is used for games which consist of multiple images and
++ * must be manually swapped out by the user (e.g. PSX, floppy disk
++ * based systems).
++ */
++
++#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:
+@@ -2147,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.
+ * */
+@@ -2307,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.
+ *
+@@ -2365,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;
+@@ -2378,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.
+@@ -2408,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. */
+@@ -2428,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. */
+@@ -2470,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. */
+@@ -2522,8 +3155,20 @@ struct retro_core_option_display
+ };
+
+ /* Maximum number of values permitted for a core option
+- * NOTE: This may be increased on a core-by-core basis
+- * if required (doing so has no effect on the frontend) */
++ * > Note: We have to set a maximum value due the limitations
++ * of the C language - i.e. it is not possible to create an
++ * array of structs each containing a variable sized array,
++ * so the retro_core_option_definition values array must
++ * have a fixed size. The size limit of 128 is a balancing
++ * act - it needs to be large enough to support all 'sane'
++ * core options, but setting it too large may impact low memory
++ * platforms. In practise, if a core option has more than
++ * 128 values then the implementation is likely flawed.
++ * To quote the above API reference:
++ * "The number of possible options should be very limited
++ * i.e. it should be feasible to cycle through options
++ * without a keyboard."
++ */
+ #define RETRO_NUM_CORE_OPTION_VALUES_MAX 128
+
+ struct retro_core_option_value
+@@ -2615,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/mame2000/1001-add-frameskip.patch b/patches/mame2000/1001-add-frameskip.patch
new file mode 100644
index 0000000..a07582e
--- /dev/null
+++ b/patches/mame2000/1001-add-frameskip.patch
@@ -0,0 +1,276 @@
+diff --git a/src/libretro/libretro.c b/src/libretro/libretro.c
+index 521a561..514e335 100644
+--- a/src/libretro/libretro.c
++++ b/src/libretro/libretro.c
+@@ -51,6 +51,20 @@ static scond_t *libretro_cond = NULL;
+ static slock_t *libretro_mutex = NULL;
+ #endif
+
++unsigned frameskip_type = 0;
++unsigned frameskip_threshold = 0;
++unsigned frameskip_counter = 0;
++unsigned frameskip_interval = 0;
++
++int retro_audio_buff_active = false;
++unsigned retro_audio_buff_occupancy = 0;
++int retro_audio_buff_underrun = false;
++
++unsigned retro_audio_latency = 0;
++int update_audio_latency = false;
++
++int should_skip_frame = 0;
++
+ int game_index = -1;
+ unsigned short *gp2x_screen15;
+ int thread_done = 0;
+@@ -151,10 +165,87 @@ static bool libretro_supports_bitmasks = false;
+
+ unsigned skip_disclaimer = 0;
+
+-static void update_variables(void)
++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 = 1000000.0 / Machine->drv->frames_per_second;
++
++ /* 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 void update_variables(bool first_run)
+ {
+ struct retro_variable var;
+-
++ bool prev_frameskip_type;
++
++ var.key = "mame2000-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 = "mame2000-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 = "mame2000-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.value = NULL;
+ var.key = "mame2000-skip_disclaimer";
+
+@@ -180,11 +271,19 @@ static void update_variables(void)
+ }
+ else
+ global_showinfo = 0;
++
++ /* Reinitialise frameskipping, if required */
++ if (!first_run &&
++ ((frameskip_type != prev_frameskip_type)))
++ retro_set_audio_buff_status_cb();
+ }
+
+ void retro_set_environment(retro_environment_t cb)
+ {
+ static const struct retro_variable vars[] = {
++ { "mame2000-frameskip", "Frameskip ; disabled|auto|threshold" },
++ { "mame2000-frameskip_threshold", "Frameskip Threshold (%); 30|40|50|60" },
++ { "mame2000-frameskip_interval", "Frameskip Interval; 1|2|3|4|5|6|7|8|9" },
+ { "mame2000-skip_disclaimer", "Skip Disclaimer; enabled|disabled" },
+ { "mame2000-show_gameinfo", "Show Game Information; disabled|enabled" },
+ { NULL, NULL },
+@@ -475,7 +574,7 @@ void retro_init(void)
+ libretro_mutex = slock_new();
+ #endif
+ init_joy_list();
+- update_variables();
++ update_variables(true);
+
+ if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
+ libretro_supports_bitmasks = true;
+@@ -496,6 +595,14 @@ void retro_deinit(void)
+ #endif
+
+ 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;
+ }
+
+ unsigned retro_api_version(void)
+@@ -560,9 +667,13 @@ void retro_run(void)
+ update_input();
+
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
+- update_variables();
++ update_variables(false);
++
++ if (should_skip_frame)
++ video_cb(NULL, gfx_width, gfx_height, gfx_width * 2);
++ else
++ video_cb(gp2x_screen15, gfx_width, gfx_height, gfx_width * 2);
+
+- video_cb(gp2x_screen15, gfx_width, gfx_height, gfx_width * 2);
+ if (samples_per_frame)
+ {
+ if (usestereo)
+@@ -584,6 +695,19 @@ void retro_run(void)
+ #ifndef WANT_LIBCO
+ unlock_mame();
+ #endif
++
++ /* 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;
++ }
++
+ }
+
+ bool retro_load_game(const struct retro_game_info *info)
+@@ -895,6 +1019,7 @@ bool retro_load_game(const struct retro_game_info *info)
+ run_thread = sthread_create(run_thread_proc, NULL);
+ #endif
+
++ retro_set_audio_buff_status_cb();
+ return true;
+ }
+
+diff --git a/src/libretro/video.c b/src/libretro/video.c
+index ce13ea0..f97ac05 100644
+--- a/src/libretro/video.c
++++ b/src/libretro/video.c
+@@ -8,6 +8,17 @@ extern int isIpad;
+ extern int emulated_width;
+ extern int emulated_height;
+ extern int safe_render_path;
++
++extern unsigned frameskip_type;
++extern unsigned frameskip_threshold;
++extern unsigned frameskip_counter;
++extern unsigned frameskip_interval;
++
++extern int retro_audio_buff_active;
++extern unsigned retro_audio_buff_occupancy;
++extern int retro_audio_buff_underrun;
++extern int should_skip_frame;
++
+ int iOS_exitPause = 0;
+ int iOS_cropVideo = 0;
+ int iOS_aspectRatio = 0;
+@@ -793,7 +804,7 @@ static INLINE void pan_display(void)
+
+ int osd_skip_this_frame(void)
+ {
+- return 0;
++ return should_skip_frame;
+ }
+
+ /* Update the display. */
+@@ -902,6 +913,41 @@ void osd_update_video_and_audio(struct osd_bitmap *bitmap)
+ /* Check for PGUP, PGDN and pan screen */
+ pan_display();
+
++ should_skip_frame = 0;
++ /* Check whether current frame should
++ * be skipped */
++ if ((frameskip_type > 0) &&
++ retro_audio_buff_active)
++ {
++ int 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 = 0;
++ break;
++ }
++
++ if (skip_frame)
++ {
++ if(frameskip_counter < frameskip_interval)
++ {
++ should_skip_frame = 1;
++ frameskip_counter++;
++ }
++ else
++ frameskip_counter = 0;
++ }
++ else
++ frameskip_counter = 0;
++ }
++
+ hook_video_done();
+ }
+
diff --git a/patches/mame2000/1002-add-sound-options.patch b/patches/mame2000/1002-add-sound-options.patch
new file mode 100644
index 0000000..266ce95
--- /dev/null
+++ b/patches/mame2000/1002-add-sound-options.patch
@@ -0,0 +1,72 @@
+diff --git a/src/libretro/libretro.c b/src/libretro/libretro.c
+index bf0ed70..d51b9f0 100644
+--- a/src/libretro/libretro.c
++++ b/src/libretro/libretro.c
+@@ -65,6 +65,9 @@ int update_audio_latency = false;
+
+ int should_skip_frame = 0;
+
++static int sample_rate = 32000;
++static int stereo_enabled = true;
++
+ int game_index = -1;
+ unsigned short *gp2x_screen15;
+ int thread_done = 0;
+@@ -272,6 +275,27 @@ static void update_variables(bool first_run)
+ else
+ global_showinfo = 0;
+
++ var.value = NULL;
++ var.key = "mame2000-sample_rate";
++
++ sample_rate = 32000;
++
++ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
++ sample_rate = strtol(var.value, NULL, 10);
++
++ var.value = NULL;
++ var.key = "mame2000-stereo";
++
++ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
++ {
++ if(strcmp(var.value, "enabled") == 0)
++ stereo_enabled = true;
++ else
++ stereo_enabled = false;
++ }
++ else
++ stereo_enabled = true;
++
+ /* Reinitialise frameskipping, if required */
+ if (!first_run &&
+ ((frameskip_type != prev_frameskip_type)))
+@@ -286,6 +310,8 @@ void retro_set_environment(retro_environment_t cb)
+ { "mame2000-frameskip_interval", "Frameskip Interval; 1|2|3|4|5|6|7|8|9" },
+ { "mame2000-skip_disclaimer", "Skip Disclaimer; enabled|disabled" },
+ { "mame2000-show_gameinfo", "Show Game Information; disabled|enabled" },
++ { "mame2000-sample_rate", "Audio Rate (Restart); 32000|11025|22050|32000|44100" },
++ { "mame2000-stereo", "Stereo (Restart); enabled|disabled" },
+ { NULL, NULL },
+ };
+ environ_cb = cb;
+@@ -643,7 +669,7 @@ void retro_get_system_av_info(struct retro_system_av_info *info)
+ };
+ struct retro_system_timing t = {
+ Machine->drv->frames_per_second,
+- 32000.0
++ (double)Machine->sample_rate
+ };
+ info->timing = t;
+ info->geometry = g;
+@@ -881,8 +907,9 @@ bool retro_load_game(const struct retro_game_info *info)
+ i=create_path_recursive(cheatdir);
+ if(i!=0)printf("error %d creating cheat \"%s\"\n", i,cheatdir);
+
+- Machine->sample_rate = 32000;
+- options.samplerate = 32000;
++ Machine->sample_rate = sample_rate;
++ options.samplerate = sample_rate;
++ usestereo = stereo_enabled;
+
+ /* This is needed so emulated YM3526/YM3812 chips are used instead on physical ones. */
+ options.use_emulated_ym3812 = 1;
diff --git a/patches/mame2000/1003-shorten-options.patch b/patches/mame2000/1003-shorten-options.patch
new file mode 100644
index 0000000..49e0d5c
--- /dev/null
+++ b/patches/mame2000/1003-shorten-options.patch
@@ -0,0 +1,23 @@
+diff --git a/src/libretro/libretro.c b/src/libretro/libretro.c
+index f9b3cfe..aad93bc 100644
+--- a/src/libretro/libretro.c
++++ b/src/libretro/libretro.c
+@@ -306,12 +306,12 @@ void retro_set_environment(retro_environment_t cb)
+ {
+ static const struct retro_variable vars[] = {
+ { "mame2000-frameskip", "Frameskip ; disabled|auto|threshold" },
+- { "mame2000-frameskip_threshold", "Frameskip Threshold (%); 30|40|50|60" },
+- { "mame2000-frameskip_interval", "Frameskip Interval; 1|2|3|4|5|6|7|8|9" },
+- { "mame2000-skip_disclaimer", "Skip Disclaimer; enabled|disabled" },
+- { "mame2000-show_gameinfo", "Show Game Information; disabled|enabled" },
+- { "mame2000-sample_rate", "Audio Rate (Restart); 32000|11025|22050|32000|44100" },
+- { "mame2000-stereo", "Stereo (Restart); enabled|disabled" },
++ { "mame2000-frameskip_threshold", "FS Threshold (%); 30|40|50|60" },
++ { "mame2000-frameskip_interval", "FS Interval; 1|2|3|4|5|6|7|8|9" },
++ { "mame2000-skip_disclaimer", "Skip Disclaimer; disabled|enabled" },
++ { "mame2000-show_gameinfo", "Show Game Info; disabled|enabled" },
++ { "mame2000-sample_rate", "Snd. Rate (Restart); 11025|22050|32000|44100" },
++ { "mame2000-stereo", "Stereo (Restart); disabled|enabled" },
+ { NULL, NULL },
+ };
+ environ_cb = cb;