aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--Makefile53
-rw-r--r--core.c4
-rw-r--r--menu.c21
-rw-r--r--options.c75
-rw-r--r--options.h3
-rw-r--r--patches/fbalpha2012/0001-trimui-build.patch48
-rw-r--r--patches/fbalpha2012/1000-update-libretro-h.patch854
-rw-r--r--patches/fbalpha2012/1001-add-auto-frameskip.patch297
-rw-r--r--patches/gambatte/0001-trimui-build.patch50
-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
-rw-r--r--patches/mame2003_plus/0001-trimui-build.patch48
-rw-r--r--patches/snes9x2002/0001-trimui-support.patch2
-rw-r--r--scale.c18
18 files changed, 2720 insertions, 44 deletions
diff --git a/.gitignore b/.gitignore
index 0c4c792..182cac4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,10 @@
/profile
picoarch
+/fbalpha2012
+/gambatte
/gpsp
+/mame2000
+/mame2003_plus
/snes9x2002
-/snes9x2005 \ No newline at end of file
+/snes9x2005
diff --git a/Makefile b/Makefile
index 785682c..db30344 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,8 @@ platform ?= unix
CC = $(CROSS_COMPILE)gcc
SYSROOT = $(shell $(CC) --print-sysroot)
+PROCS = -j4
+
OBJS = libpicofe/input.o libpicofe/in_sdl.o libpicofe/linux/in_evdev.o libpicofe/linux/plat.o libpicofe/fonts.o libpicofe/readpng.o libpicofe/config_file.o config.o core.o menu.o main.o options.o scale.o unzip.o
BIN = picoarch
@@ -14,7 +16,19 @@ CFLAGS += -I./ $(shell $(SYSROOT)/usr/bin/sdl-config --cflags)
LDFLAGS = -lc -ldl -lgcc -lm -lSDL -lasound -lpng -lz -Wl,--gc-sections -flto
-CORES = gpsp snes9x2002 snes9x2005
+# Unpolished or slow cores that build
+# EXTRA_CORES += fbalpha2012
+# EXTRA_CORES += gambatte
+# EXTRA_CORES += mame2003_plus
+
+CORES = gpsp mame2000 snes9x2002 snes9x2005 $(EXTRA_CORES)
+
+gambatte_REPO = https://github.com/libretro/gambatte-libretro
+mame2000_REPO = https://github.com/libretro/mame2000-libretro
+mame2003_plus_REPO = https://github.com/libretro/mame2003-plus-libretro
+
+fbalpha2012_BUILD_PATH = fbalpha2012/svn-current/trunk
+fbalpha2012_MAKEFILE = makefile.libretro
ifeq ($(platform), trimui)
OBJS += plat_trimui.o
@@ -59,6 +73,9 @@ endif
SOFILES = $(foreach core,$(CORES),$(core)_libretro.so)
+print-%:
+ @echo '$*=$($*)'
+
all: $(BIN) cores
plat_trimui.o: plat_sdl.c
@@ -71,6 +88,8 @@ define CORE_template =
$1_REPO ?= https://github.com/libretro/$(1)/
+$1_BUILD_PATH ?= $(1)
+
$1_MAKE = make $(and $($1_MAKEFILE),-f $($1_MAKEFILE)) platform=$(platform) $(and $(DEBUG),DEBUG=$(DEBUG)) $(and $(PROFILE),PROFILE=$(PROFILE)) $($(1)_FLAGS)
$(1):
@@ -79,11 +98,11 @@ $(1):
(test ! -d patches/$(1)) || (cd $(1) && $(foreach patch, $(sort $(wildcard patches/$(1)/*.patch)), patch -p1 < ../$(patch) &&) true)
$(1)_libretro.so: $(1)
- cd $(1) && $$($1_MAKE)
- cp $(1)/$(1)_libretro.so .
+ cd $$($1_BUILD_PATH) && $$($1_MAKE) $(PROCS)
+ cp $$($1_BUILD_PATH)/$(1)_libretro.so .
clean-$(1):
- test ! -d $(1) || cd $(1) && $$($1_MAKE) clean
+ test ! -d $(1) || cd $$($1_BUILD_PATH) && $$($1_MAKE) clean
rm -f $(1)_libretro.so
endef
@@ -102,6 +121,15 @@ force-clean: clean
ifeq ($(platform), trimui)
+fbalpha2012_NAME = fba2012
+fbalpha2012_ROM_DIR = ARCADE
+fbalpha2012_TYPES = zip
+fbalpha2012_PAK_NAME = Arcade (FBA)
+
+gambatte_ROM_DIR = GB
+gambatte_TYPES = gb,gbc,dmg,zip
+gambatte_PAK_NAME = Game Boy
+
gpsp_ROM_DIR = GBA
gpsp_TYPES = gba,bin,zip
gpsp_PAK_NAME = Game Boy Advance
@@ -111,12 +139,21 @@ needs-swap
endef
+mame2000_ROM_DIR = ARCADE
+mame2000_TYPES = zip
+mame2000_PAK_NAME = Arcade
+
+mame2003_plus_NAME = mame2003+
+mame2003_plus_ROM_DIR = ARCADE
+mame2003_plus_TYPES = zip
+mame2003_plus_PAK_NAME = Arcade (MAME 2003-plus)
+
snes9x2002_ROM_DIR = SFC
-snes9x2002_TYPES = smc,fig,sfc,gd3,gd7,dx2,bsx,swc
+snes9x2002_TYPES = smc,fig,sfc,gd3,gd7,dx2,bsx,swc,zip
snes9x2002_PAK_NAME = Super Nintendo
snes9x2005_ROM_DIR = SFC
-snes9x2005_TYPES = smc,fig,sfc,gd3,gd7,dx2,bsx,swc
+snes9x2005_TYPES = smc,fig,sfc,gd3,gd7,dx2,bsx,swc,zip
snes9x2005_PAK_NAME = Super Nintendo (2005)
# -- gmenunx
@@ -128,8 +165,10 @@ dist-gmenu-section:
define CORE_gmenushortcut =
+$1_NAME ?= $1
+
define $1_SHORTCUT
-title=$1
+title=$$($1_NAME)
exec=/mnt/SDCARD/Apps/picoarch/picoarch
params=/mnt/SDCARD/Apps/picoarch/$1_libretro.so
selectordir=/mnt/SDCARD/Roms/$($1_ROM_DIR)
diff --git a/core.c b/core.c
index 2bd8f29..d669b64 100644
--- a/core.c
+++ b/core.c
@@ -547,14 +547,12 @@ int core_load_content(const char *path) {
PA_INFO("Screen: %dx%d\n", av_info.geometry.base_width, av_info.geometry.base_height);
PA_INFO("Audio sample rate: %f\n", av_info.timing.sample_rate);
PA_INFO("Frame rate: %f\n", av_info.timing.fps);
+ PA_INFO("Reported aspect ratio: %f\n", av_info.geometry.aspect_ratio);
sample_rate = av_info.timing.sample_rate;
frame_rate = av_info.timing.fps;
aspect_ratio = av_info.geometry.aspect_ratio;
- if (!aspect_ratio)
- aspect_ratio = (float)av_info.geometry.base_width / (float)av_info.geometry.base_height;
-
#ifdef MMENU
gamepak_related_name(save_template_path, MAX_PATH, ".st%i");
#endif
diff --git a/menu.c b/menu.c
index 90a2ba4..ed10687 100644
--- a/menu.c
+++ b/menu.c
@@ -81,6 +81,24 @@ me_bind_action snes_ctrl_actions[] =
{ NULL, 0 }
};
+me_bind_action mame_ctrl_actions[] =
+{
+ { "UP ", 1 << RETRO_DEVICE_ID_JOYPAD_UP},
+ { "DOWN ", 1 << RETRO_DEVICE_ID_JOYPAD_DOWN },
+ { "LEFT ", 1 << RETRO_DEVICE_ID_JOYPAD_LEFT },
+ { "RIGHT ", 1 << RETRO_DEVICE_ID_JOYPAD_RIGHT },
+ { "BUTTON 1 ", 1 << RETRO_DEVICE_ID_JOYPAD_B },
+ { "BUTTON 2 ", 1 << RETRO_DEVICE_ID_JOYPAD_A },
+ { "BUTTON 3 ", 1 << RETRO_DEVICE_ID_JOYPAD_Y },
+ { "BUTTON 4 ", 1 << RETRO_DEVICE_ID_JOYPAD_X },
+ { "BUTTON 5 ", 1 << RETRO_DEVICE_ID_JOYPAD_L },
+ { "BUTTON 6 ", 1 << RETRO_DEVICE_ID_JOYPAD_R },
+ { "START ", 1 << RETRO_DEVICE_ID_JOYPAD_START },
+ { "COIN ", 1 << RETRO_DEVICE_ID_JOYPAD_SELECT },
+ { "OSD MENU ", 1 << RETRO_DEVICE_ID_JOYPAD_R2 },
+ { NULL, 0 }
+};
+
/* Must be a superset of all possible actions. This is used when
* saving config, and if an entry isn't here, the saver won't see
* it. */
@@ -273,6 +291,9 @@ static int key_config_loop_wrap(int id, int keys)
} else if (strstr(core_name, "snes")) {
actions = snes_ctrl_actions;
action_size = array_size(snes_ctrl_actions);
+ } else if (strstr(core_name, "mame")) {
+ actions = mame_ctrl_actions;
+ action_size = array_size(mame_ctrl_actions);
}
switch (id) {
diff --git a/options.c b/options.c
index a6c1520..0262c10 100644
--- a/options.c
+++ b/options.c
@@ -62,6 +62,14 @@ static int options_default_override(const char *key) {
return 1;
} else if (!strcmp(key, "snes9x2002_frameskip_interval")) {
return 3;
+ } else if (!strcmp(key, "mame2000-frameskip")) {
+ return 1;
+ } else if (!strcmp(key, "mame2000-frameskip_interval")) {
+ return 1;
+ } else if (!strcmp(key, "mame2000-skip_disclaimer")) {
+ return 1;
+ } else if (!strcmp(key, "mame2000-sample_rate")) {
+ return 1;
}
return -1;
@@ -85,16 +93,26 @@ void options_init(const struct retro_core_option_definition *defs) {
for (i = 0; i < core_options.len; i++) {
int j, len;
+ const struct retro_core_option_definition *def = &core_options.defs[i];
struct core_option_entry *entry = &core_options.entries[i];
- entry->def = &core_options.defs[i];
- entry->value = options_default_index(entry->def->key);
+ len = strlen(def->key) + 1;
+ entry->key = (char *)calloc(len, sizeof(char));
+ if (!entry->key) {
+ PA_ERROR("Error allocating option entries\n");
+ options_free();
+ return;
+ }
+ strncpy(entry->key, def->key, len);
+
+ entry->def = def;
+ entry->value = options_default_index(def->key);
entry->prev_value = entry->value;
- entry->blocked = option_blocked(entry->def->key);
+ entry->blocked = option_blocked(def->key);
if (entry->blocked)
core_options.visible_len--;
- len = strlen(entry->def->desc) + 1;
+ len = strlen(def->desc) + 1;
entry->desc = (char *)calloc(len, sizeof(char));
if (!entry->desc) {
PA_ERROR("Error allocating option entries\n");
@@ -102,22 +120,22 @@ void options_init(const struct retro_core_option_definition *defs) {
return;
}
- strncpy(entry->desc, entry->def->desc, len);
+ strncpy(entry->desc, def->desc, len);
truncate(entry->desc, MAX_DESC_LEN);
- if (entry->def->info) {
- len = strlen(entry->def->info) + 1;
+ if (def->info) {
+ len = strlen(def->info) + 1;
entry->info = (char *)calloc(len, sizeof(char));
if (!entry->info) {
PA_ERROR("Error allocating description string\n");
options_free();
return;
}
- strncpy(entry->info, entry->def->info, len);
+ strncpy(entry->info, def->info, len);
wrap(entry->info, MAX_LINE_LEN, MAX_LINES);
}
- for (j = 0; entry->def->values[j].value; j++)
+ for (j = 0; def->values[j].value; j++)
;
j++; /* Make room for NULL entry */
@@ -129,10 +147,10 @@ void options_init(const struct retro_core_option_definition *defs) {
}
- for (j = 0; entry->def->values[j].value; j++) {
- const char *label = entry->def->values[j].label;
+ for (j = 0; def->values[j].value; j++) {
+ const char *label = def->values[j].label;
if (!label) {
- label = entry->def->values[j].value;
+ label = def->values[j].value;
}
entry->options[j] = label;
}
@@ -146,7 +164,6 @@ void options_init_variables(const struct retro_variable *vars) {
;
core_options.visible_len = core_options.len = i;
- core_options.vars = vars;
core_options.entries = (struct core_option_entry *)calloc(core_options.len, sizeof(struct core_option_entry));
if (!core_options.entries) {
@@ -159,18 +176,27 @@ void options_init_variables(const struct retro_variable *vars) {
int j = 0;
int len;
struct core_option_entry *entry = &core_options.entries[i];
+ const struct retro_variable *var = &vars[i];
char *p;
char *opt_ptr;
char *value;
- entry->var = &core_options.vars[i];
- entry->value = options_default_index(entry->var->key);
+ len = strlen(var->key) + 1;
+ entry->key = (char *)calloc(len, sizeof(char));
+ if (!entry->key) {
+ PA_ERROR("Error allocating option entries\n");
+ options_free();
+ return;
+ }
+ strncpy(entry->key, var->key, len);
+
+ entry->value = options_default_index(var->key);
entry->prev_value = entry->value;
- entry->blocked = option_blocked(entry->var->key);
+ entry->blocked = option_blocked(var->key);
if (entry->blocked)
core_options.visible_len--;
- len = strlen(entry->var->value) + 1;
+ len = strlen(var->value) + 1;
value = (char *)calloc(len, sizeof(char));
if (!value) {
PA_ERROR("Error allocating option entries\n");
@@ -180,7 +206,7 @@ void options_init_variables(const struct retro_variable *vars) {
entry->retro_var_value = value;
- strncpy(value, entry->var->value, len);
+ strncpy(value, var->value, len);
p = strchr(value, ';');
if (p && *(p + 1) == ' ') {
@@ -236,18 +262,12 @@ void options_update_changed(void) {
}
const char* options_get_key(int index) {
- if (core_options.defs) {
- return core_options.defs[index].key;
- } else {
- return core_options.vars[index].key;
- }
+ return core_options.entries[index].key;
}
struct core_option_entry* options_get_entry(const char* key) {
for(size_t i = 0; i < core_options.len; i++) {
- const char *opt_key = core_options.defs ?
- core_options.defs[i].key :
- core_options.vars[i].key;
+ const char *opt_key = options_get_key(i);
if (!strcmp(opt_key, key)) {
return &core_options.entries[i];
@@ -366,6 +386,9 @@ void options_free(void) {
} else if (entry->desc) {
free(entry->desc);
}
+ if (entry->key) {
+ free(entry->key);
+ }
}
free(core_options.entries);
}
diff --git a/options.h b/options.h
index 614e3d2..f1ae5e7 100644
--- a/options.h
+++ b/options.h
@@ -11,6 +11,7 @@ extern enum scale_size scale_size;
extern enum scale_filter scale_filter;
struct core_option_entry {
+ char *key;
int value;
int prev_value;
char *desc;
@@ -19,7 +20,6 @@ struct core_option_entry {
char *info;
const char **options;
const struct retro_core_option_definition *def;
- const struct retro_variable *var;
};
struct core_options {
@@ -28,7 +28,6 @@ struct core_options {
bool changed;
struct core_option_entry *entries;
const struct retro_core_option_definition *defs;
- const struct retro_variable *vars;
};
extern struct core_options core_options;
diff --git a/patches/fbalpha2012/0001-trimui-build.patch b/patches/fbalpha2012/0001-trimui-build.patch
new file mode 100644
index 0000000..d1710ab
--- /dev/null
+++ b/patches/fbalpha2012/0001-trimui-build.patch
@@ -0,0 +1,48 @@
+diff --git a/svn-current/trunk/makefile.libretro b/svn-current/trunk/makefile.libretro
+index 3cff287..f619a20 100644
+--- a/svn-current/trunk/makefile.libretro
++++ b/svn-current/trunk/makefile.libretro
+@@ -334,6 +334,21 @@ else ifeq ($(platform), gcw0)
+ CFLAGS += -ffast-math -march=mips32 -mtune=mips32r2 -mhard-float
+ CXXFLAGS += -std=gnu++11 -ffast-math -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
++ SHARED := -shared -Wl,-no-undefined -Wl,--version-script=$(LIBRETRO_DIR)/link.T
++ LDFLAGS += -fno-PIC -flto
++ CFLAGS += -fomit-frame-pointer -ffast-math -mcpu=arm926ej-s -mtune=arm926ej-s -fno-PIC -flto
++ CXXFLAGS += -fomit-frame-pointer -ffast-math -mcpu=arm926ej-s -mtune=arm926ej-s -fno-PIC -flto -DUSE_CYCLONE
++ OPTIMIZE := -Ofast -DNDEBUG
++ ifeq (,$(DEBUG))
++ LDFLAGS += -s
++ endif
++
+ # Windows MSVC 2003 Xbox 1
+ else ifeq ($(platform), xbox1_msvc2003)
+ TARGET := $(TARGET_NAME)_libretro_xdk1.lib
+@@ -700,11 +715,21 @@ endif
+ ifeq ($(DEBUG), 1)
+ CFLAGS += -O0 -g
+ CXXFLAGS += -O0 -g
++else ifneq (,$(OPTIMIZE))
++ CFLAGS += $(OPTIMIZE)
++ CXXFLAGS += $(OPTIMIZE)
+ else
+ CFLAGS += -O2 -DNDEBUG
+ CXXFLAGS += -O2 -DNDEBUG
+ endif
+
++ifeq ($(PROFILE), GENERATE)
++ CFLAGS += -fprofile-generate=./profile/fbalpha2012
++ LDFLAGS += -lgcov
++else ifeq ($(PROFILE), APPLY)
++ CFLAGS += -fprofile-use -fprofile-dir=../profile/fbalpha2012 -fbranch-probabilities
++endif
++
+ ifeq ($(platform), sncps3)
+ WARNINGS_DEFINES =
+ else ifneq (,$(findstring msvc,$(platform)))
diff --git a/patches/fbalpha2012/1000-update-libretro-h.patch b/patches/fbalpha2012/1000-update-libretro-h.patch
new file mode 100644
index 0000000..03c35d2
--- /dev/null
+++ b/patches/fbalpha2012/1000-update-libretro-h.patch
@@ -0,0 +1,854 @@
+diff --git a/svn-current/trunk/src/burner/libretro/libretro.h b/svn-current/trunk/src/burner/libretro/libretro.h
+index 0c71f8d..32aa15f 100755
+--- a/svn-current/trunk/src/burner/libretro/libretro.h
++++ b/svn-current/trunk/src/burner/libretro/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'
+@@ -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/fbalpha2012/1001-add-auto-frameskip.patch b/patches/fbalpha2012/1001-add-auto-frameskip.patch
new file mode 100644
index 0000000..e497b81
--- /dev/null
+++ b/patches/fbalpha2012/1001-add-auto-frameskip.patch
@@ -0,0 +1,297 @@
+diff --git a/svn-current/trunk/src/burner/libretro/libretro.cpp b/svn-current/trunk/src/burner/libretro/libretro.cpp
+index 639aab9..1b23498 100644
+--- a/svn-current/trunk/src/burner/libretro/libretro.cpp
++++ b/svn-current/trunk/src/burner/libretro/libretro.cpp
+@@ -153,6 +153,72 @@ void retro_set_environment(retro_environment_t cb)
+ environ_cb = cb;
+ }
+
++/* Frameskipping Support */
++
++unsigned frameskip_type = 0;
++unsigned frameskip_threshold = 0;
++uint16_t frameskip_counter = 0;
++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;
++
++static unsigned audio_latency = 0;
++static bool update_audio_latency = 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;
++}
++
++void init_frameskip(void)
++{
++ if (frameskip_type > 0)
++ {
++ struct retro_audio_buffer_status_callback buf_status_cb;
++
++ buf_status_cb.callback = retro_audio_buff_status_cb;
++ if (!environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK,
++ &buf_status_cb))
++ {
++ if (log_cb)
++ log_cb(RETRO_LOG_WARN, "Frameskip disabled - frontend does not support audio buffer status monitoring.\n");
++
++ retro_audio_buff_active = false;
++ retro_audio_buff_occupancy = 0;
++ retro_audio_buff_underrun = false;
++ audio_latency = 0;
++ }
++ else
++ {
++ /* Frameskip is enabled - increase frontend
++ * audio latency to minimise potential
++ * buffer underruns */
++#ifdef FBACORES_CPS
++ float frame_time_msec = 1000.0f / 59.629403f;
++#else
++ float frame_time_msec = 1000.0f / ((float)nBurnFPS / 100.0f);
++#endif
++ /* Set latency to 6x current frame time... */
++ audio_latency = (unsigned)((6.0f * frame_time_msec) + 0.5f);
++
++ /* ...then round up to nearest multiple of 32 */
++ audio_latency = (audio_latency + 0x1F) & ~0x1F;
++ }
++ }
++ else
++ {
++ environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
++ audio_latency = 0;
++ }
++
++ update_audio_latency = true;
++}
++
+ char g_rom_dir[MAX_PATH];
+ char g_save_dir[1024];
+ char g_system_dir[1024];
+@@ -789,6 +855,14 @@ void retro_init()
+ log_cb = log_dummy;
+
+ BurnLibInit();
++ frameskip_type = 0;
++ frameskip_threshold = 0;
++ frameskip_counter = 0;
++ retro_audio_buff_active = false;
++ retro_audio_buff_occupancy = 0;
++ retro_audio_buff_underrun = false;
++ audio_latency = 0;
++ update_audio_latency = false;
+ }
+
+ void retro_deinit()
+@@ -828,10 +902,50 @@ void retro_run()
+ {
+ int width, height;
+ BurnDrvGetVisibleSize(&width, &height);
++ int nSkipFrame = 0;
++
+ pBurnDraw = (uint8_t*)g_fba_frame;
+
+ InputMake();
+
++ /* Check whether current frame should
++ * be skipped */
++ if ((frameskip_type > 0) && retro_audio_buff_active)
++ {
++ switch (frameskip_type)
++ {
++ case 1: /* auto */
++ nSkipFrame = retro_audio_buff_underrun ? 1 : 0;
++ break;
++ case 2: /* manual */
++ nSkipFrame = (retro_audio_buff_occupancy < frameskip_threshold) ? 1 : 0;
++ break;
++ default:
++ nSkipFrame = 0;
++ break;
++ }
++
++ if (!nSkipFrame || (frameskip_counter >= frameskip_interval))
++ {
++ nSkipFrame = 0;
++ frameskip_counter = 0;
++ }
++ else
++ frameskip_counter++;
++ }
++
++ if (nSkipFrame)
++ pBurnDraw = NULL;
++
++ /* If frameskip settings have changed, update
++ * frontend audio latency */
++ if (update_audio_latency)
++ {
++ environ_cb(RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY,
++ &audio_latency);
++ update_audio_latency = false;
++ }
++
+ ForceFrameStep();
+
+ unsigned drv_flags = BurnDrvGetFlags();
+@@ -851,7 +965,11 @@ void retro_run()
+ nBurnPitch = width * pitch_size;
+ }
+
+- video_cb(g_fba_frame, width, height, nBurnPitch);
++ if (!nSkipFrame)
++ video_cb(g_fba_frame, width, height, nBurnPitch);
++ else
++ video_cb(NULL, width, height, nBurnPitch);
++
+ audio_batch_cb(g_audio_buf, nBurnSoundLen);
+
+ bool updated = false;
+@@ -860,7 +978,7 @@ void retro_run()
+ neo_geo_modes old_g_opt_neo_geo_mode = g_opt_neo_geo_mode;
+ bool old_bVerticalMode = bVerticalMode;
+
+- check_variables();
++ check_variables(false);
+
+ apply_dipswitch_from_variables();
+
+@@ -1166,7 +1284,7 @@ bool retro_load_game(const struct retro_game_info *info)
+ SetControllerInfo();
+
+ set_environment();
+- check_variables();
++ check_variables(true);
+
+ pBurnSoundOut = g_audio_buf;
+ nBurnSoundRate = AUDIO_SAMPLERATE;
+diff --git a/svn-current/trunk/src/burner/libretro/retro_common.cpp b/svn-current/trunk/src/burner/libretro/retro_common.cpp
+index 6b8bf31..95c994a 100644
+--- a/svn-current/trunk/src/burner/libretro/retro_common.cpp
++++ b/svn-current/trunk/src/burner/libretro/retro_common.cpp
+@@ -62,6 +62,11 @@ INT32 g_audio_samplerate = 48000;
+ UINT8 *diag_input;
+ neo_geo_modes g_opt_neo_geo_mode = NEO_GEO_MODE_MVS;
+
++extern unsigned frameskip_type;
++extern unsigned frameskip_threshold;
++extern uint16_t frameskip_counter;
++extern unsigned frameskip_interval;
++
+ #ifdef USE_CYCLONE
+ // 0 - c68k, 1 - m68k
+ // we don't use cyclone by default because it breaks savestates cross-platform compatibility (including netplay)
+@@ -78,9 +83,11 @@ static UINT8 diag_input_select_l_r[] = {RETRO_DEVICE_ID_JOYPAD_SELECT, RETRO_DE
+
+ // Global core options
+ static const struct retro_variable var_empty = { NULL, NULL };
++static const struct retro_variable var_fbneo_frameskip = { "fbneo-frameskip-type", "Frameskip ; disabled|auto|threshold" };
++static const struct retro_variable var_fbneo_frameskip_threshold = { "fbneo-threshold", "FS Threshold (%) ; 30|40|50|60" };
++static const struct retro_variable var_fbneo_frameskip_interval = { "fbneo-interval", "FS Interval ; 0|1|2|3|4|5|6|7|8|9" };
+ static const struct retro_variable var_fbneo_allow_depth_32 = { "fbneo-allow-depth-32", "Use 32-bits color depth when available; disabled|enabled" };
+ static const struct retro_variable var_fbneo_vertical_mode = { "fbneo-vertical-mode", "Vertical mode; disabled|enabled" };
+-static const struct retro_variable var_fbneo_frameskip = { "fbneo-frameskip", "Frameskip; 0|1|2|3|4|5" };
+ static const struct retro_variable var_fbneo_cpu_speed_adjust = { "fbneo-cpu-speed-adjust", "CPU overclock; 100|110|120|130|140|150|160|170|180|190|200" };
+ static const struct retro_variable var_fbneo_diagnostic_input = { "fbneo-diagnostic-input", "Diagnostic Input; None|Hold Start|Start + A + B|Hold Start + A + B|Start + L + R|Hold Start + L + R|Hold Select|Select + A + B|Hold Select + A + B|Select + L + R|Hold Select + L + R" };
+ static const struct retro_variable var_fbneo_hiscores = { "fbneo-hiscores", "Hiscores; enabled|disabled" };
+@@ -227,9 +234,11 @@ void set_environment()
+ #endif
+
+ // Add the Global core options
++ vars_systems.push_back(&var_fbneo_frameskip);
++ vars_systems.push_back(&var_fbneo_frameskip_threshold);
++ vars_systems.push_back(&var_fbneo_frameskip_interval);
+ vars_systems.push_back(&var_fbneo_allow_depth_32);
+ vars_systems.push_back(&var_fbneo_vertical_mode);
+- vars_systems.push_back(&var_fbneo_frameskip);
+ vars_systems.push_back(&var_fbneo_cpu_speed_adjust);
+ vars_systems.push_back(&var_fbneo_hiscores);
+ if (nGameType != RETRO_GAME_TYPE_NEOCD)
+@@ -304,9 +313,40 @@ void set_environment()
+ #endif
+ }
+
+-void check_variables(void)
++void check_variables(bool first_run)
+ {
+ struct retro_variable var = {0};
++ bool prev_frameskip_type;
++
++ var.key = var_fbneo_frameskip.key;
++ 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 = var_fbneo_frameskip_threshold.key;
++ 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 = var_fbneo_frameskip_interval.key;
++ 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 = var_fbneo_cpu_speed_adjust.key;
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var))
+@@ -542,6 +582,11 @@ void check_variables(void)
+ bCycloneEnabled = false;
+ }
+ #endif
++
++ /* Reinitialise frameskipping, if required */
++ if (!first_run &&
++ ((frameskip_type != prev_frameskip_type)))
++ init_frameskip();
+ }
+
+ #ifdef USE_CYCLONE
+diff --git a/svn-current/trunk/src/burner/libretro/retro_common.h b/svn-current/trunk/src/burner/libretro/retro_common.h
+index 3efc786..f4feae6 100644
+--- a/svn-current/trunk/src/burner/libretro/retro_common.h
++++ b/svn-current/trunk/src/burner/libretro/retro_common.h
+@@ -78,7 +78,8 @@ char* str_char_replace(char* destination, char c_find, char c_replace);
+ void set_neo_system_bios();
+ void evaluate_neogeo_bios_mode(const char* drvname);
+ void set_environment();
+-void check_variables(void);
++void check_variables(bool first_run);
++void init_frameskip();
+ #ifdef USE_CYCLONE
+ void SetSekCpuCore();
+ #endif
+diff --git a/svn-current/trunk/src/burner/libretro/retro_input.cpp b/svn-current/trunk/src/burner/libretro/retro_input.cpp
+index d6b1980..2bf2e8d 100644
+--- a/svn-current/trunk/src/burner/libretro/retro_input.cpp
++++ b/svn-current/trunk/src/burner/libretro/retro_input.cpp
+@@ -2293,7 +2293,7 @@ void InputInit()
+ // Update core option for diagnostic inputs
+ set_environment();
+ // Read the user core option values
+- check_variables();
++ check_variables(false);
+
+ // The list of input descriptors is filled, we can assign them to retroarch
+ //SetInputDescriptors();
diff --git a/patches/gambatte/0001-trimui-build.patch b/patches/gambatte/0001-trimui-build.patch
new file mode 100644
index 0000000..9beb11b
--- /dev/null
+++ b/patches/gambatte/0001-trimui-build.patch
@@ -0,0 +1,50 @@
+diff --git a/Makefile.libretro b/Makefile.libretro
+index f41fcc2..50a1134 100644
+--- a/Makefile.libretro
++++ b/Makefile.libretro
+@@ -368,6 +368,21 @@ else ifeq ($(platform), gcw0)
+ CFLAGS += -fomit-frame-pointer -ffast-math -march=mips32 -mtune=mips32r2 -mhard-float
+ CXXFLAGS += $(CFLAGS)
+
++# Trimui
++else ifeq ($(platform), trimui)
++ TARGET := $(TARGET_NAME)_libretro.so
++ CC = $(CROSS_COMPILE)gcc
++ CXX = $(CROSS_COMPILE)g++
++ AR = $(CROSS_COMPILE)ar
++ SHARED := -shared -Wl,--version-script=$(version_script)
++ LDFLAGS += -fno-PIC -flto
++ PLATFORM_DEFINES := -DCC_RESAMPLER -DCC_RESAMPLER_NO_HIGHPASS
++ CFLAGS += -fomit-frame-pointer -ffast-math -mcpu=arm926ej-s -mtune=arm926ej-s -fno-PIC -flto
++ OPTIMIZE := -Ofast -DNDEBUG
++ ifeq (,$(DEBUG))
++ LDFLAGS += -s
++ endif
++
+ # RETROFW
+ else ifeq ($(platform), retrofw)
+ TARGET := $(TARGET_NAME)_libretro.so
+@@ -622,10 +637,23 @@ ifneq (,$(findstring msvc,$(platform)))
+ CFLAGS += -MT
+ CXXFLAGS += -MT
+ endif
++ifneq (,$(OPTIMIZE))
++ CFLAGS += $(OPTIMIZE)
++ CXXFLAGS += $(OPTIMIZE)
++else
+ CFLAGS += -O2 -DNDEBUG
+ CXXFLAGS += -O2 -DNDEBUG
+ endif
+
++endif
++
++ifeq ($(PROFILE), GENERATE)
++ CFLAGS += -fprofile-generate=./profile/gambatte
++ LDFLAGS += -lgcov
++else ifeq ($(PROFILE), APPLY)
++ CFLAGS += -fprofile-use -fprofile-dir=../profile/gambatte -fbranch-probabilities
++endif
++
+ ifeq (,$(findstring msvc,$(platform)))
+ CXXFLAGS += -fno-exceptions -fno-rtti
+ CXXFLAGS += -std=c++98
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;
diff --git a/patches/mame2003_plus/0001-trimui-build.patch b/patches/mame2003_plus/0001-trimui-build.patch
new file mode 100644
index 0000000..8b2bbac
--- /dev/null
+++ b/patches/mame2003_plus/0001-trimui-build.patch
@@ -0,0 +1,48 @@
+diff --git a/Makefile b/Makefile
+index 14082f8..364a327 100644
+--- a/Makefile
++++ b/Makefile
+@@ -489,6 +489,23 @@ else ifeq ($(platform), gcw0)
+ fpic := -fPIC -nostdlib
+ CFLAGS += -lm -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 += -std=c99 -fomit-frame-pointer -ffast-math -mcpu=arm926ej-s -mtune=arm926ej-s -fno-PIC -flto
++ CXXFLAGS = $(CFLAGS) -fno-rtti -fno-exceptions
++ LIBS := -lc -lgcc -lm
++ CPU_ARCH := arm
++ ARM = 1
++ OPTIMIZE := -Ofast -DNDEBUG
++ ifeq (,$(DEBUG))
++ LDFLAGS += -s
++ endif
++
+ # RetroFW
+ else ifeq ($(platform), retrofw)
+ TARGET := $(TARGET_NAME)_libretro.so
+@@ -795,10 +812,19 @@ CFLAGS += -DRETRO_PROFILE=$(RETRO_PROFILE)
+ # Disable optimization when debugging #####################
+ ifeq ($(DEBUG), 1)
+ CFLAGS += -O0 -g3
++else ifneq (,$(OPTIMIZE))
++ CFLAGS += $(OPTIMIZE)
+ else
+ CFLAGS += -O2 -DNDEBUG
+ endif
+
++ifeq ($(PROFILE), GENERATE)
++ CFLAGS += -fprofile-generate=./profile/mame2003_plus
++ LDFLAGS += -lgcov
++else ifeq ($(PROFILE), APPLY)
++ CFLAGS += -fprofile-use -fprofile-dir=../profile/mame2003_plus -fbranch-probabilities
++endif
++
+ ifneq (,$(findstring msvc,$(platform)))
+ ifeq ($(DEBUG),1)
+ CFLAGS += -MTd
diff --git a/patches/snes9x2002/0001-trimui-support.patch b/patches/snes9x2002/0001-trimui-support.patch
index eadb54c..1f7659d 100644
--- a/patches/snes9x2002/0001-trimui-support.patch
+++ b/patches/snes9x2002/0001-trimui-support.patch
@@ -12,7 +12,7 @@ index e42d169..a1f8794 100644
+ 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 += -DLSB_FIRST -DRIGHTSHIFT_IS_SAR
+ CFLAGS += -std=c99 -fomit-frame-pointer -ffast-math -mcpu=arm926ej-s -mtune=arm926ej-s
+ LDFLAGS += -flto
+ OFLAGS += -Ofast -DNDEBUG=1
diff --git a/scale.c b/scale.c
index 233b1ac..39175e2 100644
--- a/scale.c
+++ b/scale.c
@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <string.h>
#include "core.h"
+#include "main.h"
#include "options.h"
#include "scale.h"
@@ -326,6 +327,14 @@ static void scale_sharp_256xXXX_320xXXX(unsigned w, unsigned h, size_t pitch, co
}
static void scale_select_scaler(unsigned w, unsigned h, size_t pitch) {
+ double current_aspect_ratio = aspect_ratio > 0 ? aspect_ratio : ((double)w / (double)h);
+
+ /* mame2000 sets resolutions / aspect ration without notifying
+ * of changes, new should always override old */
+ if (!strcmp(core_name, "mame2000")) {
+ current_aspect_ratio = ((double)w / (double)h);
+ }
+
scaler = NULL;
if (blend_args.blend_line != NULL) {
@@ -340,13 +349,13 @@ static void scale_select_scaler(unsigned w, unsigned h, size_t pitch) {
} else if (scale_size == SCALE_SIZE_ASPECT ||
(scale_size == SCALE_SIZE_NONE && (w > SCREEN_WIDTH || h > SCREEN_HEIGHT))) {
dst_w = SCREEN_WIDTH;
- dst_h = SCREEN_WIDTH / aspect_ratio + 0.5;
+ dst_h = SCREEN_WIDTH / current_aspect_ratio + 0.5;
dst_offs = ((SCREEN_HEIGHT-dst_h)/2) * SCREEN_PITCH;
if (dst_h > SCREEN_HEIGHT) {
- dst_w = SCREEN_HEIGHT * aspect_ratio + 0.5;
+ dst_w = SCREEN_HEIGHT * current_aspect_ratio + 0.5;
dst_h = SCREEN_HEIGHT;
- dst_offs = ((SCREEN_WIDTH-dst_w)/2);
+ dst_offs = ((SCREEN_WIDTH-dst_w)/2) * SCREEN_BPP;
}
} else if (scale_size == SCALE_SIZE_NONE) {
unsigned dst_x = ((SCREEN_WIDTH - w) * SCREEN_BPP / 2);
@@ -369,7 +378,7 @@ static void scale_select_scaler(unsigned w, unsigned h, size_t pitch) {
}
}
- if (!scaler && aspect_ratio == 4.0f / 3.0f && w == 256) {
+ if (!scaler && current_aspect_ratio == 4.0f / 3.0f && w == 256) {
if (scale_filter == SCALE_FILTER_SHARP) {
scaler = scale_sharp_256xXXX_320xXXX;
return;
@@ -416,6 +425,7 @@ void scale_update_scaler(void) {
void scale(unsigned w, unsigned h, size_t pitch, const void *src, void *dst) {
if (w != prev.w || h != prev.h || pitch != prev.pitch) {
+ PA_INFO("Dimensions changed to %dx%d\n", w, h);
scale_select_scaler(w, h, pitch);
prev.w = w; prev.h = h; prev.pitch = pitch;
}