From 6d0d8f2ce924a0b1a433280160d90f71121509a4 Mon Sep 17 00:00:00 2001
From: neonloop
Date: Mon, 9 Jan 2023 14:36:40 +0000
Subject: Adds initial Funkey S support
---
.gitignore | 1 +
Makefile | 54 +-
README.funkey-s.md | 58 +
cheat.c | 9 +-
core.c | 25 +-
core.h | 3 +-
funkey/fk_instant_play.c | 90 ++
funkey/fk_instant_play.h | 50 +
funkey/fk_menu.c | 1689 ++++++++++++++++++++
funkey/fk_menu.h | 170 ++
main.c | 136 +-
main.h | 13 +-
menu.c | 41 +-
options.c | 5 +-
overrides.c | 2 +
overrides/gpsp.h | 2 +-
overrides/mame2000.h | 2 +-
overrides/pcsx_rearmed.h | 2 +-
overrides/picodrive.h | 2 +-
overrides/snes9x2002.h | 2 +-
overrides/snes9x2005.h | 7 +-
overrides/snes9x2005_plus.h | 75 +
patches/libpicofe/0002-small-screen.patch | 13 +
patches/snes9x2005/1001-funkey-s-support.patch | 93 ++
.../snes9x2005_plus/1001-funkey-s-support.patch | 93 ++
plat.h | 2 -
plat_funkey.c | 115 ++
scale.c | 46 +-
scale.h | 12 +
29 files changed, 2751 insertions(+), 61 deletions(-)
create mode 100644 README.funkey-s.md
create mode 100644 funkey/fk_instant_play.c
create mode 100644 funkey/fk_instant_play.h
create mode 100644 funkey/fk_menu.c
create mode 100644 funkey/fk_menu.h
create mode 100644 overrides/snes9x2005_plus.h
create mode 100644 patches/libpicofe/0002-small-screen.patch
create mode 100644 patches/snes9x2005/1001-funkey-s-support.patch
create mode 100644 patches/snes9x2005_plus/1001-funkey-s-support.patch
create mode 100644 plat_funkey.c
diff --git a/.gitignore b/.gitignore
index 2c7810f..1ab541e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,4 @@ picoarch
/smsplus-gx
/snes9x2002
/snes9x2005
+/snes9x2005_plus
diff --git a/Makefile b/Makefile
index dcad83b..5b4c912 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,6 @@
# Global definitions
platform ?= unix
+core_platform ?= $(platform)
CC = $(CROSS_COMPILE)gcc
SYSROOT = $(shell $(CC) --print-sysroot)
@@ -19,7 +20,12 @@ LDFLAGS = -lc -ldl -lgcc -lm -lSDL -lasound -lpng -lz -Wl,--gc-sections -flto
# Unpolished or slow cores that build
# EXTRA_CORES += fbalpha2012
# EXTRA_CORES += mame2003_plus
-CORES = beetle-pce-fast bluemsx fceumm fmsx gambatte gme gpsp mame2000 pcsx_rearmed picodrive quicknes smsplus-gx snes9x2002 snes9x2005 $(EXTRA_CORES)
+
+CORES = beetle-pce-fast bluemsx fceumm fmsx gambatte gme gpsp mame2000 pcsx_rearmed picodrive quicknes smsplus-gx snes9x2002 snes9x2005 $(EXTRA_CORES)
+
+ifeq ($(platform), funkey-s)
+CORES := $(CORES) snes9x2005_plus
+endif
beetle-pce-fast_REPO = https://github.com/libretro/beetle-pce-fast-libretro
beetle-pce-fast_CORE = mednafen_pce_fast_libretro.so
@@ -53,11 +59,20 @@ smsplus-gx_CORE = smsplus_libretro.so
snes9x2005_REPO = https://git.crowdedwood.com/snes9x2005
snes9x2005_REVISION = performance
+snes9x2005_plus_REPO = https://git.crowdedwood.com/snes9x2005
+snes9x2005_plus_REVISION = performance
+snes9x2005_plus_FLAGS = USE_BLARGG_APU=1
+
ifeq ($(platform), trimui)
OBJS += plat_trimui.o
CFLAGS += -mcpu=arm926ej-s -mtune=arm926ej-s -fno-PIC -DCONTENT_DIR='"/mnt/SDCARD/Roms"'
LDFLAGS += -fno-PIC
-
+else ifeq ($(platform), funkey-s)
+ OBJS += plat_funkey.o funkey/fk_menu.o funkey/fk_instant_play.o
+ CFLAGS += -DCONTENT_DIR='"/mnt"' -DFUNKEY_S
+ LDFLAGS += -fPIC
+ LDFLAGS += -lSDL_image -lSDL_ttf # For fk_menu
+ core_platform = classic_armv7_a7
else ifeq ($(platform), unix)
OBJS += plat_linux.o
LDFLAGS += -fPIE
@@ -105,12 +120,13 @@ print-%:
all: $(BIN) cores
libpicofe/.patched:
- cd libpicofe && git apply -p1 < ../patches/libpicofe/0001-key-combos.patch && touch .patched
+ cd libpicofe && git apply -p1 < ../patches/libpicofe/0001-key-combos.patch && git apply -p1 < ../patches/libpicofe/0002-small-screen.patch && touch .patched
clean-libpicofe:
- test ! -f libpicofe/.patched || (cd libpicofe && git apply -p1 -R < ../patches/libpicofe/0001-key-combos.patch && rm .patched)
+ test ! -f libpicofe/.patched || (cd libpicofe && git apply -p1 -R < ../patches/libpicofe/0002-small-screen.patch && git apply -p1 -R < ../patches/libpicofe/0001-key-combos.patch && rm .patched)
plat_trimui.o: plat_sdl.c
+plat_funkey.o: plat_sdl.c
plat_linux.o: plat_sdl.c
$(BIN): libpicofe/.patched $(OBJS)
@@ -122,7 +138,7 @@ $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_MAKE = make $(and $($1_MAKEFILE),-f $($1_MAKEFILE)) platform=$(core_platform) $(and $(DEBUG),DEBUG=$(DEBUG)) $(and $(PROFILE),PROFILE=$(PROFILE)) $($(1)_FLAGS)
$(1):
git clone $(if $($1_REVISION),,--depth 1) --recursive $$($(1)_REPO) $(1)
@@ -338,3 +354,31 @@ picoarch.zip:
cd pkg && zip -r ../picoarch.zip *
endif # platform=trimui
+
+ifeq ($(platform), funkey-s)
+
+define picoarch_DESKTOP
+[Desktop Entry]
+Name=picoarch
+Comment=Small screen libretro frontend
+Exec=picoarch
+Icon=
+Terminal=false
+Type=Application
+StartupNotify=true
+Categories=emulators;
+endef
+
+picoarch.opk: $(BIN) cores
+ mkdir -p ".opkdata"
+ $(file >default.funkey-s.desktop,$(picoarch_DESKTOP))
+ mv default.funkey-s.desktop ".opkdata"
+ cp $(BIN) $(SOFILES) ".opkdata"
+ cd .opkdata && mksquashfs * ../picoarch.opk -all-root -no-xattrs -noappend -no-exports
+ rm -r .opkdata
+
+picoarch-funkey-s.zip: picoarch.opk
+ rm -f picoarch-funkey-s.zip
+ zip picoarch-funkey-s.zip README.funkey-s.md picoarch.opk
+
+endif # platform=funkey-s
diff --git a/README.funkey-s.md b/README.funkey-s.md
new file mode 100644
index 0000000..0513f5a
--- /dev/null
+++ b/README.funkey-s.md
@@ -0,0 +1,58 @@
+# picoarch - a libretro frontend designed for small screens and low power
+
+picoarch runs libretro cores (emulators) for various systems with low overhead and UI designed for small screen, low-powered devices like the Trimui Model S (PowKiddy A66) and Funkey S.
+
+It supports:
+
+- **Arcade** (mame2000)
+- **Colecovision** (blueMSX, smsplus)
+- **Game Boy / Game Boy Color** (gambatte)
+- **Game Boy Advance** (gpsp)
+- **Game Gear** (picodrive, smsplus)
+- **Genesis** (picodrive)
+- **MSX** (fMSX, blueMSX)
+- **NES** (quicknes, fceumm)
+- **Sega Master System** (picodrive, smsplus)
+- **Super NES** (snes9x2002, snes9x2005, snes9x2005_plus)
+- **PCE / TurboGrafx-16** (beetle-pce-fast)
+- **PlayStation** (pcsx_rearmed)
+- more to come
+
+picoarch can also play game music (gme).
+
+All emulators have:
+
+- FunKey menu
+- fast-forward
+- soft scaling options
+- per-game config
+- screenshots
+
+Most have:
+- FunKey Instant Play (close / reopen device, requires core with save states)
+- autosave and resume (requires core with save states)
+- cheat support
+- IPS/BPS softpatching
+- auto-frameskip for smooth audio
+
+## Install
+
+Mount USB and copy picoarch.opk into Emulators directory.
+
+## Notes / extra features
+
+### BIOS
+
+Some emulators require bios files. bios files are placed into `/mnt/FunKey/.picoarch/system`. This directory is created after first launch.
+
+The libretro documentation specifies which bios is required for each core. For example, needed fMSX bios files are listed here:
+
+### Cheats
+
+Cheats use RetroArch .cht file format. Many cheat files are here
+
+Cheat file name needs to match ROM name, and go underneath save directory. For example, `/Apps/.picoarch-gambatte/cheats/Super Mario Land (World).cht`. When a cheat file is detected, a "cheats" menu item will appear in advanced menu. Not all cheats work with all cores, may want to clean up files to just the cheats you want.
+
+### IPS / BPS soft-patching
+
+Many cores can apply patches when loading. For example, loading `/roms/game.gba` will apply patches named `/roms/game.ips`, `/roms/game.ips1`, `/roms/game.IPS2`, `/roms/game.bps`, etc. Patching is temporary, original files are unmodified. Patches are loaded in case-insensitive alphabetical order. Note that `.ips12` loads before `.ips2`, but after `.ips02`.
diff --git a/cheat.c b/cheat.c
index cb644ee..64f4c19 100644
--- a/cheat.c
+++ b/cheat.c
@@ -6,9 +6,14 @@
#include "main.h"
#include "util.h"
+#define MAX_LINE_LEN SCREEN_WIDTH / 6
+
+#if SCREEN_WIDTH == 240
+#define MAX_DESC_LEN 22
+#else
#define MAX_DESC_LEN 27
-#define MAX_LINE_LEN 52
-#define MAX_LINES 3
+#endif
+#define MAX_LINES 4
static size_t parse_count(FILE *file) {
size_t count = 0;
diff --git a/core.c b/core.c
index 4e6c1dc..57ad0d3 100644
--- a/core.c
+++ b/core.c
@@ -42,9 +42,11 @@ static int core_load_game_info(struct content *content, struct retro_game_info *
return content_load_game_info(content, game_info, info.need_fullpath);
}
-void config_file_name(char *buf, size_t len, int is_game)
+void config_file_name(char *buf, size_t len, config_type config_type)
{
- if (is_game && content) {
+ if (config_type == CONFIG_TYPE_AUTO) {
+ snprintf(buf, len, "%s%s", config_dir, "picoarch-auto.cfg");
+ } else if (config_type == CONFIG_TYPE_GAME && content) {
content_based_name(content, buf, len, save_dir, NULL, ".cfg");
} else {
snprintf(buf, len, "%s%s", config_dir, "picoarch.cfg");
@@ -115,12 +117,18 @@ bool state_allowed(void) {
}
void state_file_name(char *name, size_t size, int slot) {
- char extension[5] = {0};
+ char extension[6] = {0};
- snprintf(extension, 5, ".st%d", slot);
+ snprintf(extension, 6, ".st%d", slot);
content_based_name(content, name, MAX_PATH, save_dir, NULL, extension);
}
+bool state_exists(int slot) {
+ char fname[MAX_PATH];
+ state_file_name(fname, sizeof(fname), slot);
+ return access(fname, F_OK) == 0;
+}
+
int state_read(void) {
char filename[MAX_PATH];
FILE *state_file = NULL;
@@ -287,6 +295,15 @@ static void set_directories(const char *core_name) {
#ifdef MINUI
strncpy(system_dir, save_dir, MAX_PATH-1);
#else
+
+#ifdef FUNKEY_S
+ if (home != NULL) {
+ snprintf(system_dir, MAX_PATH, "%s/.picoarch", home);
+ mkdir(system_dir, 0755);
+ snprintf(system_dir, MAX_PATH, "%s/.picoarch/system", home);
+ mkdir(system_dir, 0755);
+ } else
+#endif
if (getcwd(cwd, MAX_PATH)) {
snprintf(system_dir, MAX_PATH, "%s/system", cwd);
mkdir(system_dir, 0755);
diff --git a/core.h b/core.h
index 847510b..a667e36 100644
--- a/core.h
+++ b/core.h
@@ -43,7 +43,7 @@ extern unsigned audio_buffer_size_override;
extern int state_slot;
extern int resume_slot;
-void config_file_name(char *buf, size_t len, int is_game);
+void config_file_name(char *buf, size_t len, config_type config_type);
void save_relative_path(char *buf, size_t len, const char *basename);
void sram_read(void);
@@ -51,6 +51,7 @@ void sram_write(void);
bool state_allowed(void);
void state_file_name(char *name, size_t size, int slot);
+bool state_exists(int slot);
int state_read(void);
int state_write(void);
int state_resume(void);
diff --git a/funkey/fk_instant_play.c b/funkey/fk_instant_play.c
new file mode 100644
index 0000000..eeacae8
--- /dev/null
+++ b/funkey/fk_instant_play.c
@@ -0,0 +1,90 @@
+/*
+ FK - FunKey retro gaming console library
+ Copyright (C) 2020-2021 Vincent Buso
+ Copyright (C) 2020-2021 Michel Stempin
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Vincent Buso
+ vincent.buso@funkey-project.com
+ Michel Stempin
+ michel.stempin@funkey-project.com
+*/
+
+/**
+ * @file FK_Instant_Play.c
+ * This is the Instant Play API for the FunKey retro gaming console library
+ */
+
+#include
+#include
+#include "fk_menu.h"
+#include "fk_instant_play.h"
+#include "core.h"
+
+#ifndef SHELL_CMD_POWERDOWN
+#define SHELL_CMD_POWERDOWN "shutdown_funkey"
+#define SHELL_CMD_SCHEDULE_POWERDOWN "sched_shutdown"
+#define SHELL_CMD_CANCEL_SCHED_POWERDOWN "cancel_sched_powerdown"
+#endif
+
+static char *prog_name;
+
+/* Handler for SIGUSR1, caused by closing the console */
+static void handle_sigusr1(int signal)
+{
+ FILE *fp;
+
+ printf("Caught signal USR1(%d)\n", signal);
+
+ /* Exit menu if it was launched */
+ FK_EndMenu();
+
+ /* Send command to cancel any previously scheduled powerdown */
+ fp = popen(SHELL_CMD_CANCEL_SCHED_POWERDOWN, "r");
+ if (fp == NULL)
+ {
+ /* Countdown is still ticking, so better do nothing
+ than start writing and get interrupted!
+ */
+ printf("Failed to cancel scheduled shutdown\n");
+ exit(0);
+ }
+ pclose(fp);
+
+ state_slot = AUTOSAVE_SLOT;
+ if(!state_write()) {
+ printf("Save failed");
+ state_slot = 0;
+ }
+
+ save_config(CONFIG_TYPE_AUTO);
+
+ /* Perform Instant Play save and shutdown */
+ execlp(SHELL_CMD_INSTANT_PLAY, SHELL_CMD_INSTANT_PLAY, "save", prog_name, core_path, content->path, NULL);
+
+ /* Should not be reached */
+ printf("Failed to perform shutdown\n");
+
+ /* Exit application */
+ exit(0);
+}
+
+void FK_InitInstantPlay(int argc, char **argv)
+{
+ prog_name = argv[0];
+ signal(SIGUSR1, handle_sigusr1);
+}
+
diff --git a/funkey/fk_instant_play.h b/funkey/fk_instant_play.h
new file mode 100644
index 0000000..b3e3845
--- /dev/null
+++ b/funkey/fk_instant_play.h
@@ -0,0 +1,50 @@
+/*
+ FK - FunKey retro gaming console library
+ Copyright (C) 2020-2021 Vincent Buso
+ Copyright (C) 2020-2021 Michel Stempin
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Vincent Buso
+ vincent.buso@funkey-project.com
+ Michel Stempin
+ michel.stempin@funkey-project.com
+*/
+
+/**
+ * @file FK_Instant_Play.h
+ * This is the Instant Play API for the FunKey retro gaming console library
+ */
+
+#ifndef _FK_instant_play_h
+#define _FK_instant_play_h
+
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#define AUTOSAVE_SLOT 99
+
+extern void FK_InitInstantPlay(int argc, char **argv);
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FK_instant_play_h */
diff --git a/funkey/fk_menu.c b/funkey/fk_menu.c
new file mode 100644
index 0000000..8323d21
--- /dev/null
+++ b/funkey/fk_menu.c
@@ -0,0 +1,1689 @@
+/*
+ FK - FunKey retro gaming console library
+ Copyright (C) 2020-2021 Vincent Buso
+ Copyright (C) 2020-2021 Michel Stempin
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Vincent Buso
+ vincent.buso@funkey-project.com
+ Michel Stempin
+ michel.stempin@funkey-project.com
+*/
+
+/**
+ * @file FK_menu.c
+ * This is the menu API for the FunKey retro gaming console library
+ */
+
+#include "fk_menu.h"
+#include "main.h"
+#include "core.h"
+#include "scale.h"
+#include "plat.h"
+
+/// -------------- DEFINES --------------
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+//#define MENU_DEBUG
+#define MENU_ERROR
+
+#ifdef MENU_DEBUG
+#define MENU_DEBUG_PRINTF(...) printf(__VA_ARGS__);
+#else
+#define MENU_DEBUG_PRINTF(...)
+#endif //MENU_DEBUG
+
+#ifdef MENU_ERROR
+#define MENU_ERROR_PRINTF(...) printf(__VA_ARGS__);
+#else
+#define MENU_ERROR_PRINTF(...)
+#endif //MENU_ERROR
+
+#define SCREEN_HORIZONTAL_SIZE 240 //RES_HW_SCREEN_HORIZONTAL
+#define SCREEN_VERTICAL_SIZE 240 //RES_HW_SCREEN_VERTICAL
+
+#define SCROLL_SPEED_PX 30
+#define FPS_MENU 60
+#define ARROWS_PADDING 8
+
+#define MENU_ZONE_WIDTH SCREEN_HORIZONTAL_SIZE
+#define MENU_ZONE_HEIGHT SCREEN_VERTICAL_SIZE
+#define MENU_BG_SQUARE_WIDTH 180
+#define MENU_BG_SQUARE_HEIGHT 140
+
+#define MENU_FONT_NAME_TITLE "/usr/games/menu_resources/OpenSans-Bold.ttf"
+#define MENU_FONT_SIZE_TITLE 22
+#define MENU_FONT_NAME_INFO "/usr/games/menu_resources/OpenSans-Bold.ttf"
+#define MENU_FONT_SIZE_INFO 16
+#define MENU_FONT_NAME_SMALL_INFO "/usr/games/menu_resources/OpenSans-Regular.ttf"
+#define MENU_FONT_SIZE_SMALL_INFO 13
+#define MENU_PNG_BG_PATH "/usr/games/menu_resources/zone_bg.png"
+#define MENU_PNG_ARROW_TOP_PATH "/usr/games/menu_resources/arrow_top.png"
+#define MENU_PNG_ARROW_BOTTOM_PATH "/usr/games/menu_resources/arrow_bottom.png"
+
+#define GRAY_MAIN_R 85
+#define GRAY_MAIN_G 85
+#define GRAY_MAIN_B 85
+#define WHITE_MAIN_R 236
+#define WHITE_MAIN_G 236
+#define WHITE_MAIN_B 236
+
+#define MAX_SAVE_SLOTS 9
+
+#define MAXPATHLEN 512
+
+
+/// -------------- STATIC VARIABLES --------------
+static SDL_Surface * background_screen = NULL;
+static int backup_key_repeat_delay=0;
+static int backup_key_repeat_interval=0;
+static TTF_Font *menu_title_font = NULL;
+static TTF_Font *menu_info_font = NULL;
+static TTF_Font *menu_small_info_font = NULL;
+static SDL_Surface *img_arrow_top = NULL;
+static SDL_Surface *img_arrow_bottom = NULL;
+static SDL_Surface ** menu_zone_surfaces = NULL;
+static int *idx_menus = NULL;
+static int nb_menu_zones = 0;
+static int menuItem=0;
+static int stop_menu_loop = 0;
+
+static SDL_Color text_color = {GRAY_MAIN_R, GRAY_MAIN_G, GRAY_MAIN_B};
+static int padding_y_from_center_menu_zone = 18;
+static uint16_t width_progress_bar = 100;
+static uint16_t height_progress_bar = 20;
+
+#ifdef HAS_MENU_VOLUME
+static uint16_t x_volume_bar = 0;
+static uint16_t y_volume_bar = 0;
+static int volume_percentage = 0;
+#endif
+#ifdef HAS_MENU_BRIGHTNESS
+static uint16_t x_brightness_bar = 0;
+static uint16_t y_brightness_bar = 0;
+static int brightness_percentage = 0;
+#endif
+
+#ifdef HAS_MENU_ASPECT_RATIO
+#undef X
+#define X(a, b) b,
+static const char *aspect_ratio_name[] = {ASPECT_RATIOS};
+static int menu_aspect_ratio = ASPECT_RATIOS_TYPE_STRETCHED;
+/* static int aspect_ratio_factor_percent = 50;
+ * static int aspect_ratio_factor_step = 10; */
+#endif
+
+#ifdef HAS_MENU_THEME
+static Configuration *config = NULL;
+static int indexChooseLayout = 0;
+#endif
+
+#undef X
+#define X(a, b) b,
+const char *resume_options_str[] = {RESUME_OPTIONS};
+
+/* #if defined(HAS_MENU_SAVE) || defined (HAS_MENU_LOAD)
+ * static int savestate_slot = 0;
+ * #endif */
+
+#ifdef HAS_MENU_USB
+/// USB stuff
+static int usb_data_connected = 0;
+static int usb_sharing = 0;
+#endif
+
+#ifdef HAS_MENU_RO_RW
+static int read_write = 0;
+#endif
+
+/// -------------- FUNCTIONS IMPLEMENTATION --------------
+
+#if defined(HAS_MENU_VOLUME) || defined(HAS_MENU_BRIGHTNESS)
+static void draw_progress_bar(SDL_Surface * surface, uint16_t x, uint16_t y, uint16_t width,
+ uint16_t height, uint8_t percentage, uint16_t nb_bars)
+{
+ /// ------ Init Variables ------
+ uint16_t line_width = 1; //px
+ uint16_t padding_bars_ratio = 3;
+ uint16_t nb_full_bars = 0;
+
+ /// ------ Check values ------
+ percentage = (percentage > 100)?100:percentage;
+ x = (x > (surface->w-1))?(surface->w-1):x;
+ y = (y > surface->h-1)?(surface->h-1):y;
+ width = (width < line_width*2+1)?(line_width*2+1):width;
+ width = (width > surface->w-x-1)?(surface->w-x-1):width;
+ height = (height < line_width*2+1)?(line_width*2+1):height;
+ height = (height > surface->h-y-1)?(surface->h-y-1):height;
+ uint16_t nb_bars_max = ( width * padding_bars_ratio / (line_width*2+1) + 1 ) / (padding_bars_ratio+1);
+ nb_bars = (nb_bars > nb_bars_max)?nb_bars_max:nb_bars;
+ uint16_t bar_width = (width / nb_bars)*padding_bars_ratio/(padding_bars_ratio+1)+1;
+ uint16_t bar_padding_x = bar_width/padding_bars_ratio;
+ nb_full_bars = nb_bars*percentage/100;
+
+ /// ------ draw full bars ------
+ for (int i = 0; i < nb_full_bars; ++i)
+ {
+ /// ---- draw one bar ----
+ //MENU_DEBUG_PRINTF("Drawing filled bar %d\n", i);
+ SDL_Rect rect = {x+ i*(bar_width +bar_padding_x),
+ y, bar_width, height};
+ SDL_FillRect(surface, &rect, SDL_MapRGB(surface->format, GRAY_MAIN_R, GRAY_MAIN_G, GRAY_MAIN_B));
+ }
+
+ /// ------ draw full bars ------
+ for (int i = 0; i < (nb_bars-nb_full_bars); ++i)
+ {
+ /// ---- draw one bar ----
+ //MENU_DEBUG_PRINTF("Drawing empty bar %d\n", i);
+ SDL_Rect rect = {x+ i*(bar_width +bar_padding_x) + nb_full_bars*(bar_width +bar_padding_x),
+ y, bar_width, height};
+ SDL_FillRect(surface, &rect, SDL_MapRGB(surface->format, GRAY_MAIN_R, GRAY_MAIN_G, GRAY_MAIN_B));
+
+ SDL_Rect rect2 = {x+ i*(bar_width +bar_padding_x) + line_width + nb_full_bars*(bar_width +bar_padding_x),
+ y + line_width, bar_width - line_width*2, height - line_width*2};
+ SDL_FillRect(surface, &rect2, SDL_MapRGB(surface->format, WHITE_MAIN_R, WHITE_MAIN_R, WHITE_MAIN_R));
+ }
+
+
+}
+#endif
+
+#if defined(HAS_MENU_ASPECT_RATIO)
+static void read_aspect_ratio(void)
+{
+ switch (scale_size) {
+ case SCALE_SIZE_FULL:
+ menu_aspect_ratio = ASPECT_RATIOS_TYPE_STRETCHED;
+ break;
+ case SCALE_SIZE_ASPECT:
+ menu_aspect_ratio = ASPECT_RATIOS_TYPE_SCALED;
+ break;
+ case SCALE_SIZE_CROP:
+ menu_aspect_ratio = ASPECT_RATIOS_TYPE_CROPPED;
+ break;
+ case SCALE_SIZE_NONE:
+ menu_aspect_ratio = ASPECT_RATIOS_TYPE_NONE;
+ break;
+ }
+}
+
+static void update_aspect_ratio(void)
+{
+ switch (menu_aspect_ratio) {
+ case ASPECT_RATIOS_TYPE_STRETCHED:
+ scale_size = SCALE_SIZE_FULL;
+ scale_filter = SCALE_FILTER_SMOOTH;
+ break;
+ case ASPECT_RATIOS_TYPE_SCALED:
+ scale_size = SCALE_SIZE_ASPECT;
+ scale_filter = SCALE_FILTER_SHARP;
+ break;
+ case ASPECT_RATIOS_TYPE_CROPPED:
+ scale_size = SCALE_SIZE_CROP;
+ scale_filter = SCALE_FILTER_SMOOTH;
+ break;
+ case ASPECT_RATIOS_TYPE_NONE:
+ scale_size = SCALE_SIZE_NONE;
+ scale_filter = SCALE_FILTER_NEAREST;
+ break;
+ }
+ scale_update_scaler();
+}
+#endif
+
+static void add_menu_zone(ENUM_MENU_TYPE menu_type)
+{
+ /// ------ Increase nb of menu zones -------
+ nb_menu_zones++;
+
+ /// ------ Realoc idx Menus array -------
+ if(!idx_menus){
+ idx_menus = (int*) malloc(nb_menu_zones*sizeof(int));
+ menu_zone_surfaces = (SDL_Surface**) malloc(nb_menu_zones*sizeof(SDL_Surface*));
+ }
+ else{
+ int *temp = (int*) realloc(idx_menus, nb_menu_zones*sizeof(int));
+ idx_menus = temp;
+ menu_zone_surfaces = (SDL_Surface**) realloc(menu_zone_surfaces, nb_menu_zones*sizeof(SDL_Surface*));
+ }
+ idx_menus[nb_menu_zones-1] = menu_type;
+
+ /// ------ Reinit menu surface with height increased -------
+ menu_zone_surfaces[nb_menu_zones-1] = IMG_Load(MENU_PNG_BG_PATH);
+ if(!menu_zone_surfaces[nb_menu_zones-1]) {
+ MENU_ERROR_PRINTF("ERROR IMG_Load: %s\n", IMG_GetError());
+ }
+
+ /// --------- Init Common Variables --------
+ SDL_Surface *text_surface = NULL;
+ SDL_Surface *surface = menu_zone_surfaces[nb_menu_zones-1];
+ SDL_Rect text_pos;
+
+ /// --------- Add new zone ---------
+ switch(menu_type){
+#ifdef HAS_MENU_VOLUME
+ case MENU_TYPE_VOLUME:
+ MENU_DEBUG_PRINTF("Init MENU_TYPE_VOLUME\n");
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "VOLUME", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+
+ x_volume_bar = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - width_progress_bar)/2;
+ y_volume_bar = surface->h - MENU_ZONE_HEIGHT/2 - height_progress_bar/2 + padding_y_from_center_menu_zone;
+ draw_progress_bar(surface, x_volume_bar, y_volume_bar,
+ width_progress_bar, height_progress_bar, 0, 100/STEP_CHANGE_VOLUME);
+ break;
+#endif
+#ifdef HAS_MENU_BRIGHTNESS
+ case MENU_TYPE_BRIGHTNESS:
+ MENU_DEBUG_PRINTF("Init MENU_TYPE_BRIGHTNESS\n");
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "BRIGHTNESS", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+
+ x_brightness_bar = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - width_progress_bar)/2;
+ y_brightness_bar = surface->h - MENU_ZONE_HEIGHT/2 - height_progress_bar/2 + padding_y_from_center_menu_zone;
+ draw_progress_bar(surface, x_brightness_bar, y_brightness_bar,
+ width_progress_bar, height_progress_bar, 0, 100/STEP_CHANGE_BRIGHTNESS);
+ break;
+#endif
+#ifdef HAS_MENU_SAVE
+ case MENU_TYPE_SAVE:
+ MENU_DEBUG_PRINTF("Init MENU_TYPE_SAVE\n");
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "SAVE", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone*2;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+ break;
+#endif
+#ifdef HAS_MENU_LOAD
+ case MENU_TYPE_LOAD:
+ MENU_DEBUG_PRINTF("Init MENU_TYPE_LOAD\n");
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "LOAD", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone*2;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+ break;
+#endif
+#ifdef HAS_MENU_ASPECT_RATIO
+ case MENU_TYPE_ASPECT_RATIO:
+ MENU_DEBUG_PRINTF("Init MENU_TYPE_ASPECT_RATIO\n");
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "ASPECT RATIO", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+ break;
+#endif
+#ifdef HAS_MENU_USB
+ case MENU_TYPE_USB:
+ MENU_DEBUG_PRINTF("Init MENU_TYPE_USB\n");
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "USB", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+ break;
+#endif
+#ifdef HAS_MENU_THEME
+ case MENU_TYPE_THEME:
+ MENU_DEBUG_PRINTF("Init MENU_TYPE_THEME\n");
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "SET THEME", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone*2;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+ break;
+#endif
+#ifdef HAS_MENU_LAUNCHER
+ case MENU_TYPE_LAUNCHER:
+ MENU_DEBUG_PRINTF("Init MENU_TYPE_LAUNCHER\n");
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "SET LAUNCHER", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone*2;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "GMENU2X", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+ break;
+#endif
+#ifdef HAS_MENU_RO_RW
+ case MENU_TYPE_RO_RW:
+ MENU_DEBUG_PRINTF("Init MENU_TYPE_RO_RW\n");
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "SET SYSTEM:", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone*2;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+ break;
+#endif
+#ifdef HAS_MENU_ADVANCED
+ case MENU_TYPE_ADVANCED:
+ MENU_DEBUG_PRINTF("Init MENU_TYPE_ADVANCED\n");
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "ADVANCED", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+ break;
+#endif
+#ifdef HAS_MENU_EXIT
+ case MENU_TYPE_EXIT:
+ MENU_DEBUG_PRINTF("Init MENU_TYPE_EXIT\n");
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "EXIT GAME", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+ break;
+#endif
+#ifdef HAS_MENU_POWERDOWN
+ case MENU_TYPE_POWERDOWN:
+ MENU_DEBUG_PRINTF("Init MENU_TYPE_POWERDOWN\n");
+ /// ------ Text ------
+ text_surface = TTF_RenderText_Blended(menu_title_font, "POWERDOWN", text_color);
+ text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2;
+ SDL_BlitSurface(text_surface, NULL, surface, &text_pos);
+ break;
+#endif
+ default:
+ MENU_DEBUG_PRINTF("Warning - In add_menu_zone, unknown MENU_TYPE: %d\n", menu_type);
+ break;
+ }
+
+ /// ------ Free Surfaces -------
+ SDL_FreeSurface(text_surface);
+}
+
+static void init_menu_zones(void)
+{
+#ifdef HAS_MENU_VOLUME
+ add_menu_zone(MENU_TYPE_VOLUME);
+#endif
+#ifdef HAS_MENU_BRIGHTNESS
+ add_menu_zone(MENU_TYPE_BRIGHTNESS);
+#endif
+#ifdef HAS_MENU_SAVE
+ if (state_allowed()) {
+ add_menu_zone(MENU_TYPE_SAVE);
+ }
+#endif
+#ifdef HAS_MENU_LOAD
+ if (state_allowed()) {
+ add_menu_zone(MENU_TYPE_LOAD);
+ }
+#endif
+#ifdef HAS_MENU_ASPECT_RATIO
+ add_menu_zone(MENU_TYPE_ASPECT_RATIO);
+#endif
+#ifdef HAS_MENU_RO_RW
+ add_menu_zone(MENU_TYPE_RO_RW);
+#endif
+#ifdef HAS_MENU_ADVANCED
+ add_menu_zone(MENU_TYPE_ADVANCED);
+#endif
+#ifdef HAS_MENU_EXIT
+ add_menu_zone(MENU_TYPE_EXIT);
+#endif
+#ifdef HAS_MENU_USB
+ add_menu_zone(MENU_TYPE_USB);
+#endif
+#ifdef HAS_MENU_THEME
+ add_menu_zone(MENU_TYPE_THEME);
+#endif
+#ifdef HAS_MENU_LAUNCHER
+ add_menu_zone(MENU_TYPE_LAUNCHER);
+#endif
+#ifdef HAS_MENU_POWERDOWN
+ add_menu_zone(MENU_TYPE_POWERDOWN);
+#endif
+}
+
+
+#ifdef HAS_MENU_THEME
+void FK_InitMenu(Configuration &c)
+ #else
+void FK_InitMenu(void)
+#endif
+{
+ MENU_DEBUG_PRINTF("Init Menu\n");
+ /// ----- Loading the fonts -----
+ menu_title_font = TTF_OpenFont(MENU_FONT_NAME_TITLE, MENU_FONT_SIZE_TITLE);
+ if(!menu_title_font){
+ MENU_ERROR_PRINTF("ERROR in init_menu_SDL: Could not open menu font %s, %s\n", MENU_FONT_NAME_TITLE, SDL_GetError());
+ }
+ menu_info_font = TTF_OpenFont(MENU_FONT_NAME_INFO, MENU_FONT_SIZE_INFO);
+ if(!menu_info_font){
+ MENU_ERROR_PRINTF("ERROR in init_menu_SDL: Could not open menu font %s, %s\n", MENU_FONT_NAME_INFO, SDL_GetError());
+ }
+ menu_small_info_font = TTF_OpenFont(MENU_FONT_NAME_SMALL_INFO, MENU_FONT_SIZE_SMALL_INFO);
+ if(!menu_small_info_font){
+ MENU_ERROR_PRINTF("ERROR in init_menu_SDL: Could not open menu font %s, %s\n", MENU_FONT_NAME_SMALL_INFO, SDL_GetError());
+ }
+
+ /// ------ Load arrows imgs -------
+ img_arrow_top = IMG_Load(MENU_PNG_ARROW_TOP_PATH);
+ if(!img_arrow_top) {
+ MENU_ERROR_PRINTF("ERROR IMG_Load: %s\n", IMG_GetError());
+ }
+ img_arrow_bottom = IMG_Load(MENU_PNG_ARROW_BOTTOM_PATH);
+ if(!img_arrow_bottom) {
+ MENU_ERROR_PRINTF("ERROR IMG_Load: %s\n", IMG_GetError());
+ }
+
+#ifdef HAS_MENU_THEME
+ /// ------ Save config pointer ------
+ config = &c;
+#endif
+#ifdef HAS_MENU_RO_RW
+ /// ----- Shell cmd ----
+ if (system(SHELL_CMD_RO) < 0) {
+ MENU_ERROR_PRINTF("Failed to run command %s\n", SHELL_CMD_RO);
+ }
+#endif
+
+ /// ------ Init menu zones ------
+ init_menu_zones();
+
+ return;
+}
+
+
+void FK_EndMenu(void)
+{
+ MENU_DEBUG_PRINTF("End Menu \n");
+ /// ------ Close font -------
+ TTF_CloseFont(menu_title_font);
+ TTF_CloseFont(menu_info_font);
+ TTF_CloseFont(menu_small_info_font);
+
+ /// ------ Free Surfaces -------
+ for(int i=0; i < nb_menu_zones; i++){
+ if(menu_zone_surfaces[i] != NULL){
+ SDL_FreeSurface(menu_zone_surfaces[i]);
+ }
+ }
+ SDL_FreeSurface(img_arrow_top);
+ SDL_FreeSurface(img_arrow_bottom);
+
+ /// ------ Free Menu memory and reset vars -----
+ if(idx_menus){
+ free(idx_menus);
+ }
+ idx_menus=NULL;
+ nb_menu_zones = 0;
+
+#ifdef HAS_MENU_RO_RW
+ /// ----- Shell cmd ----
+ if (system(SHELL_CMD_RO) < 0) {
+ MENU_ERROR_PRINTF("Failed to run command %s\n", SHELL_CMD_RO);
+ }
+#endif
+
+ return;
+}
+
+void FK_StopMenu(void)
+{
+ stop_menu_loop = 1;
+}
+
+
+static void init_menu_system_values(void)
+{
+#if defined(HAS_MENU_VOLUME) || defined(HAS_MENU_BRIGHTNESS) || defined(HAS_MENU_USB) || defined(HAS_MENU_RO_RW)
+ FILE *fp;
+ char res[100];
+#endif
+
+#ifdef HAS_MENU_VOLUME
+ /// ------- Get system volume percentage --------
+ fp = popen(SHELL_CMD_VOLUME_GET, "r");
+ if (fp == NULL) {
+ MENU_ERROR_PRINTF("Failed to run command %s\n", SHELL_CMD_VOLUME_GET );
+ volume_percentage = 50; ///wrong value: setting default to 50
+ }
+ else{
+ fgets(res, sizeof(res)-1, fp);
+ pclose(fp);
+
+ /// Check if Volume is a number (at least the first char)
+ if(res[0] < '0' || res[0] > '9'){
+ MENU_ERROR_PRINTF("Wrong return value: %s for volume cmd: %s\n",res, SHELL_CMD_VOLUME_GET);
+ volume_percentage = 50; ///wrong value: setting default to 50
+ }
+ else{
+ volume_percentage = atoi(res);
+ MENU_DEBUG_PRINTF("System volume = %d%%\n", volume_percentage);
+ }
+ }
+#endif
+#ifdef HAS_MENU_BRIGHTNESS
+ /// ------- Get system brightness percentage -------
+ fp = popen(SHELL_CMD_BRIGHTNESS_GET, "r");
+ if (fp == NULL) {
+ MENU_ERROR_PRINTF("Failed to run command %s\n", SHELL_CMD_BRIGHTNESS_GET );
+ brightness_percentage = 50; ///wrong value: setting default to 50
+ }
+ else{
+ fgets(res, sizeof(res)-1, fp);
+ pclose(fp);
+
+ /// Check if brightness is a number (at least the first char)
+ if(res[0] < '0' || res[0] > '9'){
+ MENU_ERROR_PRINTF("Wrong return value: %s for volume cmd: %s\n",res, SHELL_CMD_BRIGHTNESS_GET);
+ brightness_percentage = 50; ///wrong value: setting default to 50
+ }
+ else{
+ brightness_percentage = atoi(res);
+ MENU_DEBUG_PRINTF("System brightness = %d%%\n", brightness_percentage);
+ }
+ }
+#endif
+#ifdef HAS_MENU_USB
+ /// ------- Get USB Value -------
+ usb_data_connected = Utils::executeRawPath(SHELL_CMD_USB_DATA_CONNECTED);
+ usb_sharing = Utils::executeRawPath(SHELL_CMD_USB_CHECK_IS_SHARING);
+
+ /** Sanity check if usb not connected */
+ if(!usb_data_connected){
+ usb_sharing = 0;
+
+ if(idx_menus[menuItem] == MENU_TYPE_USB){
+ menuItem = 0;
+ }
+ }
+
+ /** Sanity check if currently in USB sharing (should not happen) */
+ if(usb_sharing){
+
+ /// Force USB menu to launch
+ for(int cur_idx=0; cur_idx < nb_menu_zones; cur_idx++){
+ if(idx_menus[cur_idx] == MENU_TYPE_USB){
+ menuItem = cur_idx;
+ printf("USB mounted, setting menu item to %d\n", menuItem);
+ break;
+ }
+ }
+ }
+#endif
+}
+
+static void menu_screen_refresh(SDL_Surface *screen, int menuItem, int prevItem, int scroll, uint8_t menu_confirmation, uint8_t menu_action)
+{
+ /// --------- Vars ---------
+#ifdef HAS_MENU_USB
+ int print_arrows = (scroll || usb_sharing)?0:1;
+#else
+ int print_arrows = 1;
+#endif
+
+ /// --------- Clear HW screen ----------
+ if(SDL_BlitSurface(background_screen, NULL, screen, NULL)){
+ MENU_ERROR_PRINTF("ERROR Could not Clear screen: %s\n", SDL_GetError());
+ }
+
+ /// --------- Setup Blit Window ----------
+ SDL_Rect menu_blit_window;
+ menu_blit_window.x = 0;
+ menu_blit_window.w = SCREEN_HORIZONTAL_SIZE;
+
+ /// --------- Blit prev menu Zone going away ----------
+ menu_blit_window.y = scroll;
+ menu_blit_window.h = SCREEN_VERTICAL_SIZE;
+ if(SDL_BlitSurface(menu_zone_surfaces[prevItem], &menu_blit_window, screen, NULL)){
+ MENU_ERROR_PRINTF("ERROR Could not Blit surface on screen: %s\n", SDL_GetError());
+ }
+
+ /// --------- Blit new menu Zone going in (only during animations) ----------
+ if(scroll>0){
+ menu_blit_window.y = SCREEN_VERTICAL_SIZE-scroll;
+ menu_blit_window.h = SCREEN_VERTICAL_SIZE;
+ if(SDL_BlitSurface(menu_zone_surfaces[menuItem], NULL, screen, &menu_blit_window)){
+ MENU_ERROR_PRINTF("ERROR Could not Blit surface on screen: %s\n", SDL_GetError());
+ }
+ }
+ else if(scroll<0){
+ menu_blit_window.y = SCREEN_VERTICAL_SIZE+scroll;
+ menu_blit_window.h = SCREEN_VERTICAL_SIZE;
+ if(SDL_BlitSurface(menu_zone_surfaces[menuItem], &menu_blit_window, screen, NULL)){
+ MENU_ERROR_PRINTF("ERROR Could not Blit surface on screen: %s\n", SDL_GetError());
+ }
+ }
+ /// --------- No Scroll ? Blitting menu-specific info
+ else{
+ SDL_Surface * text_surface = NULL;
+ char text_tmp[100];
+ SDL_Rect text_pos;
+#ifdef HAS_MENU_THEME
+ char *curLayoutName;
+ bool dots=false;
+ int max_chars = 15;
+#endif
+
+ switch(idx_menus[menuItem]){
+#ifdef HAS_MENU_VOLUME
+ case MENU_TYPE_VOLUME:
+ draw_progress_bar(screen, x_volume_bar, y_volume_bar,
+ width_progress_bar, height_progress_bar, volume_percentage, 100/STEP_CHANGE_VOLUME);
+ break;
+#endif
+#ifdef HAS_MENU_BRIGHTNESS
+ case MENU_TYPE_BRIGHTNESS:
+ draw_progress_bar(screen, x_volume_bar, y_volume_bar,
+ width_progress_bar, height_progress_bar, brightness_percentage, 100/STEP_CHANGE_BRIGHTNESS);
+ break;
+#endif
+#ifdef HAS_MENU_SAVE
+ case MENU_TYPE_SAVE:
+ /// ---- Write slot -----
+ sprintf(text_tmp, "IN SLOT < %d >", state_slot+1);
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+
+ if(menu_action){
+ sprintf(text_tmp, "Saving...");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ }
+ else{
+ if(menu_confirmation){
+ sprintf(text_tmp, "Are you sure?");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ }
+ else{
+ if (state_exists(state_slot)) {
+ text_surface = TTF_RenderText_Blended(menu_info_font, "Used", text_color);
+ } else {
+ text_surface = TTF_RenderText_Blended(menu_info_font, "Free", text_color);
+ }
+ }
+ }
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ break;
+#endif
+#ifdef HAS_MENU_LOAD
+ case MENU_TYPE_LOAD:
+ /// ---- Write slot -----
+ sprintf(text_tmp, "FROM SLOT < %d >", state_slot+1);
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+
+ if(menu_action){
+ sprintf(text_tmp, "Loading...");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ }
+ else{
+ if(menu_confirmation){
+ sprintf(text_tmp, "Are you sure?");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ }
+ else{
+ if (state_exists(state_slot)) {
+ text_surface = TTF_RenderText_Blended(menu_info_font, "Used", text_color);
+ } else {
+ text_surface = TTF_RenderText_Blended(menu_info_font, "Free", text_color);
+ }
+ }
+ }
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ break;
+#endif
+#ifdef HAS_MENU_ASPECT_RATIO
+ case MENU_TYPE_ASPECT_RATIO:
+ sprintf(text_tmp, "< %s >", aspect_ratio_name[menu_aspect_ratio]);
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ break;
+#endif
+#ifdef HAS_MENU_USB
+ case MENU_TYPE_USB:
+ /// ---- Write slot -----
+ sprintf(text_tmp, "%s USB", usb_sharing?"EJECT":"MOUNT");
+ text_surface = TTF_RenderText_Blended(menu_title_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+
+ if(menu_action){
+ sprintf(text_tmp, "in progress ...");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ }
+ else if(menu_confirmation){
+ sprintf(text_tmp, "Are you sure?");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ }
+ else{
+ ///Nothing
+ }
+ break;
+#endif
+#ifdef HAS_MENU_THEME
+ case MENU_TYPE_THEME:
+ /// ---- Write current chosen theme -----
+ curLayoutName = (char*)Utils::getFileName(config->layouts_.at(indexChooseLayout)).c_str();
+
+ // no more than max_chars chars in name to fit screen
+ if(strlen(curLayoutName) > max_chars){
+ curLayoutName[max_chars-2] = 0;
+ dots = true;
+ }
+ sprintf(text_tmp, "< %s%s >", curLayoutName, dots?"...":"" );
+
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+
+ if(menu_action){
+ sprintf(text_tmp, "In progress...");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ }
+ else if(menu_confirmation){
+ sprintf(text_tmp, "Are you sure?");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ }
+ break;
+#endif
+#ifdef HAS_MENU_LAUNCHER
+ case MENU_TYPE_LAUNCHER:
+ if(menu_action){
+ sprintf(text_tmp, "In progress...");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ }
+ else if(menu_confirmation){
+ sprintf(text_tmp, "Are you sure?");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ }
+ break;
+#endif
+#ifdef HAS_MENU_EXIT
+ case MENU_TYPE_EXIT:
+#endif
+#ifdef HAS_MENU_POWERDOWN
+ case MENU_TYPE_POWERDOWN:
+#endif
+#if defined(HAS_MENU_EXIT) || defined(HAS_MENU_POWERDOWN)
+ if(menu_action){
+ sprintf(text_tmp, "Shutting down...");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ }
+ else{
+ if(menu_confirmation){
+ sprintf(text_tmp, "Are you sure?");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ }
+ }
+ break;
+#endif
+#ifdef HAS_MENU_RO_RW
+ case MENU_TYPE_RO_RW:
+ sprintf(text_tmp, "%s", read_write?"READ-ONLY":"READ-WRITE");
+ text_surface = TTF_RenderText_Blended(menu_title_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+
+ if(menu_action){
+ sprintf(text_tmp, "in progress ...");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ }
+ else if(menu_confirmation){
+ sprintf(text_tmp, "Are you sure?");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, screen, &text_pos);
+ }
+ else{
+ ///Nothing
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+
+ /// ------ Free Surfaces -------
+ if(text_surface)
+ SDL_FreeSurface(text_surface);
+ }
+
+ /// --------- Print arrows --------
+ if(print_arrows){
+ /// Top arrow
+ SDL_Rect pos_arrow_top;
+ pos_arrow_top.x = (screen->w - img_arrow_top->w)/2;
+ pos_arrow_top.y = (screen->h - MENU_BG_SQUARE_HEIGHT)/4 - img_arrow_top->h/2;
+ SDL_BlitSurface(img_arrow_top, NULL, screen, &pos_arrow_top);
+
+ /// Bottom arrow
+ SDL_Rect pos_arrow_bottom;
+ pos_arrow_bottom.x = (screen->w - img_arrow_bottom->w)/2;
+ pos_arrow_bottom.y = screen->h -
+ (screen->h - MENU_BG_SQUARE_HEIGHT)/4 - img_arrow_bottom->h/2;
+ SDL_BlitSurface(img_arrow_bottom, NULL, screen, &pos_arrow_bottom);
+ }
+
+ /// --------- Flip Screen ----------
+ SDL_Flip(screen);
+}
+
+
+int FK_RunMenu(SDL_Surface *screen)
+{
+ MENU_DEBUG_PRINTF("Run Menu\n");
+
+ SDL_Event event;
+ uint32_t prev_ms = SDL_GetTicks();
+ uint32_t cur_ms = SDL_GetTicks();
+ int scroll=0;
+ int start_scroll=0;
+ uint8_t screen_refresh = 1;
+ char shell_cmd[100];
+ uint8_t menu_confirmation = 0;
+ stop_menu_loop = 0;
+#ifdef HAS_MENU_THEME
+ indexChooseLayout = config->currentLayoutIdx_;
+#endif
+ int returnCode = MENU_RETURN_OK;
+
+ /// ------ Load default keymap ------
+ system(SHELL_CMD_KEYMAP_DEFAULT);
+
+ /// ------ Get System values -------
+ init_menu_system_values();
+ int prevItem=menuItem;
+
+ /// Save prev key repeat params and set new Key repeat
+ SDL_GetKeyRepeat(&backup_key_repeat_delay, &backup_key_repeat_interval);
+ if(SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL)){
+ MENU_ERROR_PRINTF("ERROR with SDL_EnableKeyRepeat: %s\n", SDL_GetError());
+ }
+
+#if defined(HAS_MENU_SAVE) || defined(HAS_MENU_LOAD)
+ /// Get save slot from game
+ state_slot = (state_slot%MAX_SAVE_SLOTS); // security
+#endif
+
+#if defined(HAS_MENU_ASPECT_RATIO)
+ read_aspect_ratio();
+#endif
+
+ /// ------ Backup currently displayed app screen -------
+ background_screen = SDL_CreateRGBSurface(SDL_SWSURFACE,
+ screen->w, screen->h, 32, 0, 0, 0, 0);
+ if(background_screen == NULL){
+ MENU_ERROR_PRINTF("ERROR Could not create background_screen: %s\n", SDL_GetError());
+ return MENU_RETURN_ERROR;
+ }
+ if(SDL_BlitSurface(screen, NULL, background_screen, NULL)){
+ MENU_ERROR_PRINTF("ERROR Could not copy screen: %s\n", SDL_GetError());
+ }
+
+ /// -------- Main loop ---------
+ while (!stop_menu_loop)
+ {
+ /// -------- Handle Keyboard Events ---------
+ if(!scroll){
+ while (SDL_PollEvent(&event))
+ switch(event.type)
+ {
+ case SDL_QUIT:
+ stop_menu_loop = 1;
+ returnCode = MENU_RETURN_EXIT;
+ break;
+ case SDL_KEYDOWN:
+ switch (event.key.keysym.sym)
+ {
+ case SDLK_b:
+ if(menu_confirmation){
+ /// ------ Reset menu confirmation ------
+ menu_confirmation = 0;
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ }
+ /*else{
+ stop_menu_loop = 1;
+ }*/
+ break;
+
+ case SDLK_q:
+ case SDLK_ESCAPE:
+ /// ------ Check if no action ------
+#ifdef HAS_MENU_USB
+ if(usb_sharing){
+ break;
+ }
+#endif
+ stop_menu_loop = 1;
+ break;
+
+ case SDLK_d:
+ case SDLK_DOWN:
+ MENU_DEBUG_PRINTF("DOWN\n");
+ /// ------ Check if no action ------
+#ifdef HAS_MENU_USB
+ if(usb_sharing){
+ break;
+ }
+#endif
+ /// ------ Start scrolling to new menu -------
+ menuItem++;
+ if (menuItem>=nb_menu_zones) menuItem=0;
+
+#ifdef HAS_MENU_USB
+ /// Skip if usb menu if usb not connected
+ if(idx_menus[menuItem] == MENU_TYPE_USB && !usb_data_connected){
+ menuItem++;
+ if (menuItem>=nb_menu_zones) menuItem=0;
+ }
+#endif
+ start_scroll=1;
+
+ /// ------ Reset menu confirmation ------
+ menu_confirmation = 0;
+
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ break;
+
+ case SDLK_u:
+ case SDLK_UP:
+ MENU_DEBUG_PRINTF("UP\n");
+ /// ------ Check if no action ------
+#ifdef HAS_MENU_USB
+ if(usb_sharing){
+ break;
+ }
+#endif
+ /// ------ Start scrolling to new menu -------
+ menuItem--;
+ if (menuItem<0) menuItem=nb_menu_zones-1;
+
+#ifdef HAS_MENU_USB
+ /// Skip if usb menu if usb not connected
+ if(idx_menus[menuItem] == MENU_TYPE_USB && !usb_data_connected){
+ menuItem--;
+ if (menuItem<0) menuItem=nb_menu_zones-1;
+ }
+#endif
+ start_scroll=-1;
+
+ /// ------ Reset menu confirmation ------
+ menu_confirmation = 0;
+
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ break;
+
+ case SDLK_l:
+ case SDLK_LEFT:
+ //MENU_DEBUG_PRINTF("LEFT\n");
+#ifdef HAS_MENU_VOLUME
+ if(idx_menus[menuItem] == MENU_TYPE_VOLUME){
+ MENU_DEBUG_PRINTF("Volume DOWN\n");
+ /// ----- Compute new value -----
+ volume_percentage = (volume_percentage < STEP_CHANGE_VOLUME)?
+ 0:(volume_percentage-STEP_CHANGE_VOLUME);
+
+ /// ----- Shell cmd ----
+ sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage);
+ system(shell_cmd);
+
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ } else
+#endif
+#ifdef HAS_MENU_BRIGHTNESS
+ if(idx_menus[menuItem] == MENU_TYPE_BRIGHTNESS){
+ MENU_DEBUG_PRINTF("Brightness DOWN\n");
+ /// ----- Compute new value -----
+ brightness_percentage = (brightness_percentage < STEP_CHANGE_BRIGHTNESS)?
+ 0:(brightness_percentage-STEP_CHANGE_BRIGHTNESS);
+
+ /// ----- Shell cmd ----
+ sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage);
+ system(shell_cmd);
+
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ } else
+#endif
+#ifdef HAS_MENU_LOAD
+ if(idx_menus[menuItem] == MENU_TYPE_SAVE){
+ MENU_DEBUG_PRINTF("Save Slot DOWN\n");
+ state_slot = (!state_slot)?(MAX_SAVE_SLOTS-1):(state_slot-1);
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ } else
+#endif
+#ifdef HAS_MENU_LOAD
+ if(idx_menus[menuItem] == MENU_TYPE_LOAD){
+ MENU_DEBUG_PRINTF("Load Slot DOWN\n");
+ //idx_load_slot = (!idx_load_slot)?(MAX_SAVE_SLOTS-1):(idx_load_slot-1);
+ state_slot = (!state_slot)?(MAX_SAVE_SLOTS-1):(state_slot-1);
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ } else
+#endif
+#ifdef HAS_MENU_ASPECT_RATIO
+ if(idx_menus[menuItem] == MENU_TYPE_ASPECT_RATIO){
+ MENU_DEBUG_PRINTF("Aspect Ratio DOWN\n");
+ menu_aspect_ratio = (!menu_aspect_ratio)?(NB_ASPECT_RATIOS_TYPES-1):(menu_aspect_ratio-1);
+ update_aspect_ratio();
+
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ } else
+#endif
+#ifdef HAS_MENU_THEME
+ if(idx_menus[menuItem] == MENU_TYPE_THEME){
+ MENU_DEBUG_PRINTF("Theme previous\n");
+ indexChooseLayout = (!indexChooseLayout)?(config->layouts_.size()-1):(indexChooseLayout-1);
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ } else
+#endif
+ {}
+ break;
+
+ case SDLK_r:
+ case SDLK_RIGHT:
+ //MENU_DEBUG_PRINTF("RIGHT\n");
+#ifdef HAS_MENU_VOLUME
+ if(idx_menus[menuItem] == MENU_TYPE_VOLUME){
+ MENU_DEBUG_PRINTF("Volume UP\n");
+ /// ----- Compute new value -----
+ volume_percentage = (volume_percentage > 100 - STEP_CHANGE_VOLUME)?
+ 100:(volume_percentage+STEP_CHANGE_VOLUME);
+
+ /// ----- Shell cmd ----
+ sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage);
+ system(shell_cmd);
+
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ } else
+#endif
+#ifdef HAS_MENU_BRIGHTNESS
+ if(idx_menus[menuItem] == MENU_TYPE_BRIGHTNESS){
+ MENU_DEBUG_PRINTF("Brightness UP\n");
+ /// ----- Compute new value -----
+ brightness_percentage = (brightness_percentage > 100 - STEP_CHANGE_BRIGHTNESS)?
+ 100:(brightness_percentage+STEP_CHANGE_BRIGHTNESS);
+
+ /// ----- Shell cmd ----
+ sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage);
+ system(shell_cmd);
+
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ } else
+#endif
+#ifdef HAS_MENU_SAVE
+ if(idx_menus[menuItem] == MENU_TYPE_SAVE){
+ MENU_DEBUG_PRINTF("Save Slot UP\n");
+ state_slot = (state_slot+1)%MAX_SAVE_SLOTS;
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ } else
+#endif
+#ifdef HAS_MENU_LOAD
+ if(idx_menus[menuItem] == MENU_TYPE_LOAD){
+ MENU_DEBUG_PRINTF("Load Slot UP\n");
+ //idx_load_slot = (idx_load_slot+1)%MAX_SAVE_SLOTS;
+ state_slot = (state_slot+1)%MAX_SAVE_SLOTS;
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ } else
+#endif
+#ifdef HAS_MENU_ASPECT_RATIO
+ if(idx_menus[menuItem] == MENU_TYPE_ASPECT_RATIO){
+ MENU_DEBUG_PRINTF("Aspect Ratio UP\n");
+ menu_aspect_ratio = (menu_aspect_ratio+1)%NB_ASPECT_RATIOS_TYPES;
+ update_aspect_ratio();
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ } else
+#endif
+#ifdef HAS_MENU_THEME
+ if(idx_menus[menuItem] == MENU_TYPE_THEME){
+ MENU_DEBUG_PRINTF("Theme previous\n");
+ indexChooseLayout = (indexChooseLayout+1)%config->layouts_.size();
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ } else
+#endif
+ {}
+ break;
+
+ case SDLK_a:
+ case SDLK_RETURN:
+#ifdef HAS_MENU_SAVE
+ if(idx_menus[menuItem] == MENU_TYPE_SAVE){
+ if(menu_confirmation){
+ MENU_DEBUG_PRINTF("Saving in slot %d\n", state_slot);
+ /// ------ Refresh Screen -------
+ menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1);
+
+ /// ------ Save game ------
+ state_write();
+ stop_menu_loop = 1;
+ }
+ else{
+ MENU_DEBUG_PRINTF("Save game - asking confirmation\n");
+ menu_confirmation = 1;
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ }
+ } else
+#endif
+#ifdef HAS_MENU_LOAD
+ if(idx_menus[menuItem] == MENU_TYPE_LOAD){
+ if(menu_confirmation){
+ MENU_DEBUG_PRINTF("Loading in slot %d\n", state_slot);
+ /// ------ Refresh Screen -------
+ menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1);
+
+ /// ------ Load game ------
+ state_read();
+ stop_menu_loop = 1;
+ }
+ else{
+ MENU_DEBUG_PRINTF("Save game - asking confirmation\n");
+ menu_confirmation = 1;
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ }
+ } else
+#endif
+#ifdef HAS_MENU_USB
+ if(idx_menus[menuItem] == MENU_TYPE_USB){
+ MENU_DEBUG_PRINTF("USB %s\n", usb_sharing?"unmount":"mount");
+ if(menu_confirmation){
+ MENU_DEBUG_PRINTF("%s USB - confirmed\n", usb_sharing?"Unmount":"Mount");
+ /// ----- Refresh screen ----
+ menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1);
+
+ /// ----- Shell cmd ----
+ /*system(usb_sharing?SHELL_CMD_USB_UNMOUNT:SHELL_CMD_USB_MOUNT);*/
+ bool res = Utils::executeRawPath(usb_sharing?SHELL_CMD_USB_UNMOUNT:SHELL_CMD_USB_MOUNT);
+ if (!res) {
+ MENU_ERROR_PRINTF("Failed to run command %s\n", shell_cmd);
+ }
+ else{
+ usb_sharing = !usb_sharing;
+ }
+
+ /// ------ Refresh screen ------
+ menu_confirmation = 0;
+ screen_refresh = 1;
+ }
+ else{
+ MENU_DEBUG_PRINTF("%s USB - asking confirmation\n", usb_sharing?"Unmount":"Mount");
+ menu_confirmation = 1;
+ screen_refresh = 1;
+ }
+ } else
+#endif
+#ifdef HAS_MENU_THEME
+ if(idx_menus[menuItem] == MENU_TYPE_THEME){
+ if(menu_confirmation){
+ MENU_DEBUG_PRINTF("Theme change - confirmed\n");
+
+ /// ------ Refresh Screen -------
+ menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1);
+
+ /// ----- Write new theme and restart RetroFe ----
+ config->exportCurrentLayout(Utils::combinePath(Configuration::absolutePath, "layout.conf"),
+ Utils::getFileName(config->layouts_.at(indexChooseLayout)));
+ stop_menu_loop = 1;
+ returnCode = MENU_RETURN_EXIT;
+ }
+ else{
+ MENU_DEBUG_PRINTF("Theme change - asking confirmation\n");
+ menu_confirmation = 1;
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ }
+ } else
+#endif
+#ifdef HAS_MENU_LAUNCHER
+ if(idx_menus[menuItem] == MENU_TYPE_LAUNCHER){
+ if(menu_confirmation){
+ MENU_DEBUG_PRINTF("Lancher change - confirmed\n");
+
+ /// ------ Refresh Screen -------
+ menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1);
+
+ /// ----- Shell cmd ----
+ MENU_DEBUG_PRINTF("Running command: %s\n", SHELL_CMD_SET_LAUNCHER_GMENU2X);
+ Utils::executeRawPath(SHELL_CMD_SET_LAUNCHER_GMENU2X);
+
+ stop_menu_loop = 1;
+ returnCode = MENU_RETURN_EXIT;
+ }
+ else{
+ MENU_DEBUG_PRINTF("Launcher change - asking confirmation\n");
+ menu_confirmation = 1;
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ }
+ } else
+#endif
+#ifdef HAS_MENU_ADVANCED
+ if(idx_menus[menuItem] == MENU_TYPE_ADVANCED){
+ MENU_DEBUG_PRINTF("Advanced menu\n");
+
+ SDL_Event sdlevent;
+ do {
+ SDL_WaitEvent(&sdlevent);
+ } while (sdlevent.type != SDL_KEYUP || sdlevent.key.keysym.sym != event.key.keysym.sym);
+
+ stop_menu_loop = 1;
+ returnCode = MENU_RETURN_MENU;
+ } else
+#endif
+#ifdef HAS_MENU_EXIT
+ if(idx_menus[menuItem] == MENU_TYPE_EXIT){
+ MENU_DEBUG_PRINTF("Exit game\n");
+ if(menu_confirmation){
+ MENU_DEBUG_PRINTF("Exit game - confirmed\n");
+ /// ----- The game should be saved here ----
+
+ /// ----- Exit game and back to launcher ----
+ stop_menu_loop = 1;
+ returnCode = MENU_RETURN_EXIT;
+ }
+ else{
+ MENU_DEBUG_PRINTF("Exit game - asking confirmation\n");
+ menu_confirmation = 1;
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ }
+ } else
+#endif
+#ifdef HAS_MENU_POWERDOWN
+ if(idx_menus[menuItem] == MENU_TYPE_POWERDOWN){
+ if(menu_confirmation){
+ MENU_DEBUG_PRINTF("Powerdown - confirmed\n");
+
+ /// ------ Refresh Screen -------
+ menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1);
+
+ /// ----- Shell cmd ----
+ system(SHELL_CMD_POWERDOWN);
+ return MENU_RETURN_EXIT;
+ }
+ else{
+ MENU_DEBUG_PRINTF("Powerdown - asking confirmation\n");
+ menu_confirmation = 1;
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ }
+ } else
+#endif
+#ifdef HAS_MENU_RO_RW
+ if(idx_menus[menuItem] == MENU_TYPE_RO_RW){
+ MENU_DEBUG_PRINTF("%s\n", read_write?"RO":"RW");
+ if(menu_confirmation){
+ MENU_DEBUG_PRINTF("SYSTEM %s - confirmed\n", read_write?"RO":"RW");
+ /// ----- Refresh screen ----
+ menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1);
+
+ /// ----- Shell cmd ----
+ if (system(read_write?SHELL_CMD_RO:SHELL_CMD_RW) < 0) {
+ MENU_ERROR_PRINTF("Failed to run command %s\n", shell_cmd);
+ }
+ else{
+ read_write = !read_write;
+ }
+
+ /// ------ Refresh screen ------
+ menu_confirmation = 0;
+ screen_refresh = 1;
+ }
+ else{
+ MENU_DEBUG_PRINTF("SYSTEM %s - asking confirmation\n", read_write?"RW":"RO");
+ menu_confirmation = 1;
+ screen_refresh = 1;
+ }
+ } else
+#endif
+ {}
+ break;
+
+ default:
+ //MENU_DEBUG_PRINTF("Keydown: %d\n", event.key.keysym.sym);
+ break;
+ }
+ break;
+ }
+ }
+
+ /// --------- Handle Scroll effect ---------
+ if ((scroll>0) || (start_scroll>0)){
+ scroll+=MIN(SCROLL_SPEED_PX, MENU_ZONE_HEIGHT-scroll);
+ start_scroll = 0;
+ screen_refresh = 1;
+ }
+ else if ((scroll<0) || (start_scroll<0)){
+ scroll-=MIN(SCROLL_SPEED_PX, MENU_ZONE_HEIGHT+scroll);
+ start_scroll = 0;
+ screen_refresh = 1;
+ }
+ if (scroll>=MENU_ZONE_HEIGHT || scroll<=-MENU_ZONE_HEIGHT) {
+ prevItem=menuItem;
+ scroll=0;
+ screen_refresh = 1;
+ }
+
+ /// --------- Handle FPS ---------
+ cur_ms = SDL_GetTicks();
+ if(cur_ms-prev_ms < 1000/FPS_MENU){
+ SDL_Delay(1000/FPS_MENU - (cur_ms-prev_ms));
+ }
+ prev_ms = SDL_GetTicks();
+
+
+ /// --------- Refresh screen
+ if(screen_refresh){
+ menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 0);
+ }
+
+ /// --------- reset screen refresh ---------
+ screen_refresh = 0;
+ }
+
+ /// ------ Restore last keymap ------
+ system(SHELL_CMD_KEYMAP_RESUME);
+
+ /// ------ Reset prev key repeat params -------
+ if(SDL_EnableKeyRepeat(backup_key_repeat_delay, backup_key_repeat_interval)){
+ MENU_ERROR_PRINTF("ERROR with SDL_EnableKeyRepeat: %s\n", SDL_GetError());
+ }
+
+ /// --------- Clear HW screen ----------
+ if(SDL_BlitSurface(background_screen, NULL, screen, NULL)){
+ MENU_ERROR_PRINTF("ERROR Could not Clear screen: %s\n", SDL_GetError());
+ }
+
+ /// --------- Flip Screen ----------
+ SDL_Flip(screen);
+
+ if(background_screen != NULL){
+ SDL_FreeSurface(background_screen);
+ background_screen = NULL;
+ }
+ MENU_DEBUG_PRINTF("Leave Menu\n");
+ plat_video_menu_leave();
+ return returnCode;
+}
+
+#ifdef HAS_MENU_ASPECT_RATIO
+void FK_NextAspectRatio(void)
+{
+ char shell_cmd[100];
+ FILE *fp;
+
+ read_aspect_ratio();
+ menu_aspect_ratio = (menu_aspect_ratio+1)%NB_ASPECT_RATIOS_TYPES;
+ update_aspect_ratio();
+ scale_update_scaler();
+ plat_video_menu_leave();
+ snprintf(shell_cmd, 100, "%s %d \" DISPLAY MODE: %s\"",
+ SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_name[menu_aspect_ratio]);
+ fp = popen(shell_cmd, "r");
+ if (fp == NULL) {
+ printf("Failed to run command %s\n", shell_cmd);
+ } else {
+ pclose(fp);
+ }
+}
+#endif
+
+/****************************/
+/* Quick Resume Menu */
+/****************************/
+int FK_RunResumeMenu(SDL_Surface *hw_screen)
+{
+ MENU_DEBUG_PRINTF("Init resume menu\n");
+
+ /* Decare vars */
+ SDL_Surface *text_surface = NULL;
+ char text_tmp[40];
+ SDL_Rect text_pos;
+ SDL_Event event;
+ uint32_t prev_ms = SDL_GetTicks();
+ uint32_t cur_ms = SDL_GetTicks();
+ stop_menu_loop = 0;
+ uint8_t screen_refresh = 1;
+ uint8_t menu_confirmation = 0;
+ int option_idx=RESUME_YES;
+
+ /* Save prev key repeat params and set new Key repeat */
+ SDL_GetKeyRepeat(&backup_key_repeat_delay, &backup_key_repeat_interval);
+ if(SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL)){
+ MENU_ERROR_PRINTF("ERROR with SDL_EnableKeyRepeat: %s\n", SDL_GetError());
+ }
+
+ /* Load BG */
+ SDL_Surface *img_square_bg = IMG_Load(MENU_PNG_BG_PATH);
+ if(!img_square_bg) {
+ MENU_ERROR_PRINTF("ERROR IMG_Load: %s\n", IMG_GetError());
+ }
+ SDL_Surface *bg_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, hw_screen->w, hw_screen->h, 16, 0, 0, 0, 0);
+ SDL_BlitSurface(img_square_bg, NULL, bg_surface, NULL);
+ SDL_FreeSurface(img_square_bg);
+
+
+ /* Print top arrow */
+ SDL_Rect pos_arrow_top;
+ pos_arrow_top.x = (bg_surface->w - img_arrow_top->w)/2;
+ pos_arrow_top.y = (bg_surface->h - MENU_BG_SQUARE_HEIGHT)/4 - img_arrow_top->h/2;
+ SDL_BlitSurface(img_arrow_top, NULL, bg_surface, &pos_arrow_top);
+
+ /* Print bottom arrow */
+ SDL_Rect pos_arrow_bottom;
+ pos_arrow_bottom.x = (bg_surface->w - img_arrow_bottom->w)/2;
+ pos_arrow_bottom.y = bg_surface->h -
+ (bg_surface->h - MENU_BG_SQUARE_HEIGHT)/4 - img_arrow_bottom->h/2;
+ SDL_BlitSurface(img_arrow_bottom, NULL, bg_surface, &pos_arrow_bottom);
+
+ if (text_surface)
+ SDL_FreeSurface(text_surface);
+
+ /* Main loop */
+ while (!stop_menu_loop)
+ {
+ /* Handle keyboard events */
+ while (SDL_PollEvent(&event))
+ switch(event.type)
+ {
+ case SDL_QUIT:
+ stop_menu_loop = 1;
+ break;
+ case SDL_KEYDOWN:
+ switch (event.key.keysym.sym)
+ {
+ case SDLK_b:
+ if(menu_confirmation){
+ /// ------ Reset menu confirmation ------
+ menu_confirmation = 0;
+
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ }
+ /*else{
+ stop_menu_loop = 1;
+ }*/
+ break;
+
+ case SDLK_q:
+ case SDLK_ESCAPE:
+ /*stop_menu_loop = 1;*/
+ break;
+
+ case SDLK_u:
+ case SDLK_UP:
+ MENU_DEBUG_PRINTF("Option UP\n");
+ option_idx = (!option_idx)?(NB_RESUME_OPTIONS-1):(option_idx-1);
+
+ /// ------ Reset menu confirmation ------
+ menu_confirmation = 0;
+
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ break;
+
+ case SDLK_d:
+ case SDLK_DOWN:
+ MENU_DEBUG_PRINTF("Option DOWN\n");
+ option_idx = (option_idx+1)%NB_RESUME_OPTIONS;
+
+ /// ------ Reset menu confirmation ------
+ menu_confirmation = 0;
+
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ break;
+
+ case SDLK_a:
+ case SDLK_RETURN:
+ MENU_DEBUG_PRINTF("Pressed A\n");
+ if(menu_confirmation){
+ MENU_DEBUG_PRINTF("Confirmed\n");
+
+ /// ----- exit menu ----
+ stop_menu_loop = 1;
+ }
+ else{
+ MENU_DEBUG_PRINTF("Asking confirmation\n");
+ menu_confirmation = 1;
+
+ /// ------ Refresh screen ------
+ screen_refresh = 1;
+ }
+ break;
+
+ default:
+ //MENU_DEBUG_PRINTF("Keydown: %d\n", event.key.keysym.sym);
+ break;
+ }
+ break;
+ }
+
+ /* Handle FPS */
+ cur_ms = SDL_GetTicks();
+ if(cur_ms-prev_ms < 1000/FPS_MENU){
+ SDL_Delay(1000/FPS_MENU - (cur_ms-prev_ms));
+ }
+ prev_ms = SDL_GetTicks();
+
+ /* Refresh screen */
+ if(screen_refresh){
+ /* Clear and draw BG */
+ SDL_FillRect(hw_screen, NULL, 0);
+ if(SDL_BlitSurface(bg_surface, NULL, hw_screen, NULL)){
+ MENU_ERROR_PRINTF("ERROR Could not draw background: %s\n", SDL_GetError());
+ }
+
+ /* Draw resume or reset option */
+ text_surface = TTF_RenderText_Blended(menu_title_font, resume_options_str[option_idx], text_color);
+ text_pos.x = (hw_screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = hw_screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2;
+ SDL_BlitSurface(text_surface, NULL, hw_screen, &text_pos);
+
+ /* Draw confirmation */
+ if(menu_confirmation){
+ sprintf(text_tmp, "Are you sure ?");
+ text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color);
+ text_pos.x = (hw_screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2;
+ text_pos.y = hw_screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone;
+ SDL_BlitSurface(text_surface, NULL, hw_screen, &text_pos);
+ }
+
+ /* Flip Screen */
+ SDL_Flip(hw_screen);
+ }
+
+ /* reset screen refresh */
+ screen_refresh = 0;
+ }
+
+ /* Free SDL Surfaces */
+ if(bg_surface)
+ SDL_FreeSurface(bg_surface);
+ if(text_surface)
+ SDL_FreeSurface(text_surface);
+
+ /* Reset prev key repeat params */
+ if(SDL_EnableKeyRepeat(backup_key_repeat_delay, backup_key_repeat_interval)){
+ MENU_ERROR_PRINTF("ERROR with SDL_EnableKeyRepeat: %s\n", SDL_GetError());
+ }
+
+ return option_idx;
+}
diff --git a/funkey/fk_menu.h b/funkey/fk_menu.h
new file mode 100644
index 0000000..36bc460
--- /dev/null
+++ b/funkey/fk_menu.h
@@ -0,0 +1,170 @@
+/*
+ FK - FunKey retro gaming console library
+ Copyright (C) 2020-2021 Vincent Buso
+ Copyright (C) 2020-2021 Michel Stempin
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Vincent Buso
+ vincent.buso@funkey-project.com
+ Michel Stempin
+ michel.stempin@funkey-project.com
+*/
+
+/**
+ * @file FK_menu.h
+ * This is the menu API for the FunKey retro gaming console library
+ */
+
+#ifndef _FK_menu_h
+#define _FK_menu_h
+
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+
+#define HAS_MENU_VOLUME
+#define HAS_MENU_BRIGHTNESS
+#define HAS_MENU_SAVE
+#define HAS_MENU_LOAD
+#define HAS_MENU_ASPECT_RATIO
+//#define HAS_MENU_USB
+//#define HAS_MENU_THEME
+//#define HAS_MENU_LAUNCHER
+#define HAS_MENU_ADVANCED
+#define HAS_MENU_EXIT
+//#define HAS_MENU_POWERDOWN
+//#define HAS_MENU_RO_RW
+
+typedef enum{
+ MENU_TYPE_VOLUME,
+ MENU_TYPE_BRIGHTNESS,
+ MENU_TYPE_SAVE,
+ MENU_TYPE_LOAD,
+ MENU_TYPE_ASPECT_RATIO,
+ MENU_TYPE_USB,
+ MENU_TYPE_THEME,
+ MENU_TYPE_LAUNCHER,
+ MENU_TYPE_ADVANCED,
+ MENU_TYPE_EXIT,
+ MENU_TYPE_POWERDOWN,
+ MENU_TYPE_RO_RW,
+ NB_MENU_TYPES,
+} ENUM_MENU_TYPE;
+
+typedef enum{
+ MENU_RETURN_OK,
+ MENU_RETURN_MENU,
+ MENU_RETURN_EXIT,
+ MENU_RETURN_ERROR,
+ NB_MENU_RETURN_CODES,
+} ENUM_MENU_RETURN_CODES;
+
+#ifdef HAS_MENU_ASPECT_RATIO
+///------ Definition of the different aspect ratios
+#define ASPECT_RATIOS \
+ /* X(ASPECT_RATIOS_TYPE_MANUAL, "MANUAL ZOOM") */ \
+ X(ASPECT_RATIOS_TYPE_STRETCHED, "STRETCHED") \
+ X(ASPECT_RATIOS_TYPE_CROPPED, "CROPPED") \
+ X(ASPECT_RATIOS_TYPE_SCALED, "SCALED") \
+ X(ASPECT_RATIOS_TYPE_NONE, "NONE") \
+ X(NB_ASPECT_RATIOS_TYPES, "")
+
+////------ Enumeration of the different aspect ratios ------
+#undef X
+#define X(a, b) a,
+typedef enum {ASPECT_RATIOS} ENUM_ASPECT_RATIOS_TYPES;
+#endif
+
+///------ Definition of the different resume options
+#define RESUME_OPTIONS \
+ X(RESUME_YES, "RESUME GAME") \
+ X(RESUME_NO, "NEW GAME") \
+ X(NB_RESUME_OPTIONS, "")
+
+////------ Enumeration of the different resume options ------
+#undef X
+#define X(a, b) a,
+typedef enum {RESUME_OPTIONS} ENUM_RESUME_OPTIONS;
+
+////------ Defines to be shared -------
+#ifdef HAS_MENU_VOLUME
+#define STEP_CHANGE_VOLUME 10
+#endif
+#ifdef HAS_MENU_BRIGHTNESS
+#define STEP_CHANGE_BRIGHTNESS 10
+#endif
+#define NOTIF_SECONDS_DISP 2
+
+////------ Menu commands -------
+#ifdef HAS_MENU_VOLUME
+#define SHELL_CMD_VOLUME_GET "volume get"
+#define SHELL_CMD_VOLUME_SET "volume set"
+#endif
+#ifdef HAS_MENU_BRIGHTNESS
+#define SHELL_CMD_BRIGHTNESS_GET "brightness get"
+#define SHELL_CMD_BRIGHTNESS_SET "brightness set"
+#endif
+#ifdef HAS_MENU_USB
+#define SHELL_CMD_USB_DATA_CONNECTED "is_usb_data_connected"
+#define SHELL_CMD_USB_MOUNT "share start"
+#define SHELL_CMD_USB_UNMOUNT "share stop"
+#define SHELL_CMD_USB_CHECK_IS_SHARING "share is_sharing"
+#endif
+#ifdef HAS_MENU_POWERDOWN
+#define SHELL_CMD_POWERDOWN "shutdown_funkey"
+#define SHELL_CMD_SCHEDULE_POWERDOWN "sched_shutdown"
+#define SHELL_CMD_CANCEL_SCHED_POWERDOWN "cancel_sched_powerdown"
+#endif
+#ifdef HAS_MENU_LAUNCHER
+#define SHELL_CMD_SET_LAUNCHER_GMENU2X "set_launcher gmenu2x"
+#define SHELL_CMD_SET_LAUNCHER_RETROFE "set_launcher retrofe"
+#endif
+#ifdef HAS_MENU_RO_RW
+#define SHELL_CMD_RO "ro"
+#define SHELL_CMD_RW "rw"
+#endif
+
+#define SHELL_CMD_INSTANT_PLAY "instant_play"
+#define SHELL_CMD_NOTIF_SET "notif set"
+#define SHELL_CMD_NOTIF_CLEAR "notif clear"
+#define SHELL_CMD_KEYMAP_DEFAULT "keymap default"
+#define SHELL_CMD_KEYMAP_RESUME "keymap resume"
+
+#ifdef HAS_MENU_THEME
+extern void FK_InitMenu(Configuration &c);
+#else
+extern void FK_InitMenu(void);
+#endif
+extern void FK_EndMenu(void);
+extern int FK_RunMenu(SDL_Surface *screen);
+extern int FK_RunResumeMenu(SDL_Surface *screen);
+extern void FK_StopMenu(void);
+
+#ifdef HAS_MENU_ASPECT_RATIO
+extern void FK_NextAspectRatio(void);
+#endif
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FK_menu_h */
diff --git a/main.c b/main.c
index e004101..727d6d0 100644
--- a/main.c
+++ b/main.c
@@ -22,6 +22,12 @@ void* mmenu = NULL;
char save_template_path[MAX_PATH];
#endif
+#ifdef FUNKEY_S
+#include "funkey/fk_menu.h"
+#include "funkey/fk_instant_play.h"
+static bool instant_play = false;
+#endif
+
bool should_quit = false;
unsigned current_audio_buffer_size;
char core_name[MAX_PATH];
@@ -204,6 +210,24 @@ void set_defaults(void)
audio_buffer_size = 5;
scale_size = SCALE_SIZE_NONE;
scale_filter = SCALE_FILTER_NEAREST;
+
+ /* Sets better defaults for small screen */
+ if (SCREEN_WIDTH == 240) {
+ scale_size = SCALE_SIZE_CROP;
+ scale_filter = SCALE_FILTER_SMOOTH;
+
+ if (!strcmp(core_name, "gambatte")) {
+ scale_size = SCALE_SIZE_ASPECT;
+ scale_filter = SCALE_FILTER_SHARP;
+ }
+
+ if (!strcmp(core_name, "pcsx_rearmed") ||
+ !strcmp(core_name, "picodrive")) {
+ scale_size = SCALE_SIZE_FULL;
+ scale_filter = SCALE_FILTER_SMOOTH;
+ }
+ }
+
scale_update_scaler();
if (current_audio_buffer_size < audio_buffer_size)
@@ -218,12 +242,12 @@ void set_defaults(void)
options_update_changed();
}
-int save_config(int is_game)
+int save_config(config_type config_type)
{
char config_filename[MAX_PATH];
FILE *config_file;
- config_file_name(config_filename, MAX_PATH, is_game);
+ config_file_name(config_filename, MAX_PATH, config_type);
config_file = fopen(config_filename, "wb");
if (!config_file) {
fprintf(stderr, "Could not write config to %s\n", config_filename);
@@ -235,7 +259,7 @@ int save_config(int is_game)
fclose(config_file);
- if (is_game)
+ if (config_type == CONFIG_TYPE_GAME)
config_override = 1;
return 0;
@@ -245,15 +269,25 @@ static void alloc_config_buffer(char **config_ptr) {
char config_filename[MAX_PATH];
FILE *config_file;
size_t length;
+ int config_auto = 0;
config_override = 0;
- config_file_name(config_filename, MAX_PATH, 1);
+ config_file_name(config_filename, MAX_PATH, CONFIG_TYPE_AUTO);
config_file = fopen(config_filename, "rb");
if (config_file) {
- config_override = 1;
+ config_auto = 1;
+#ifdef FUNKEY_S
+ instant_play = true;
+#endif
} else {
- config_file_name(config_filename, MAX_PATH, 0);
+ config_file_name(config_filename, MAX_PATH, CONFIG_TYPE_GAME);
config_file = fopen(config_filename, "rb");
+ if (config_file) {
+ config_override = 1;
+ } else {
+ config_file_name(config_filename, MAX_PATH, CONFIG_TYPE_CORE);
+ config_file = fopen(config_filename, "rb");
+ }
}
if (!config_file)
@@ -269,6 +303,13 @@ static void alloc_config_buffer(char **config_ptr) {
fread(*config_ptr, 1, length, config_file);
fclose(config_file);
+
+ if (config_auto) {
+ config_file_name(config_filename, MAX_PATH, CONFIG_TYPE_GAME);
+ if (access(config_filename, F_OK) == 0) {
+ config_override = 1;
+ }
+ }
}
void load_config(void)
@@ -307,13 +348,13 @@ void load_config_keys(void)
}
}
-int remove_config(int is_game) {
+int remove_config(config_type config_type) {
char config_filename[MAX_PATH];
int ret;
- config_file_name(config_filename, MAX_PATH, is_game);
+ config_file_name(config_filename, MAX_PATH, config_type);
ret = remove(config_filename);
- if (ret == 0)
+ if (ret == 0 && config_type == CONFIG_TYPE_GAME)
config_override = 0;
return ret;
@@ -331,7 +372,7 @@ void handle_emu_action(emu_action action)
case EACTION_MENU:
toggle_fast_forward(1); /* Force FF off */
sram_write();
-#ifdef MMENU
+#if defined(MMENU)
if (mmenu && content && content->path) {
ShowMenu_t ShowMenu = (ShowMenu_t)dlsym(mmenu, "ShowMenu");
SDL_Surface *screen = SDL_GetVideoSurface();
@@ -361,10 +402,27 @@ void handle_emu_action(emu_action action)
SDL_PushEvent(&sdlevent);
}
else {
-#endif
menu_loop();
-#ifdef MMENU
}
+#elif defined(FUNKEY_S)
+ {
+ SDL_Surface *screen = SDL_GetVideoSurface();
+ int return_code = FK_RunMenu(screen);
+
+ if (return_code == MENU_RETURN_MENU) {
+ menu_loop();
+ } else if (return_code == MENU_RETURN_EXIT) {
+ should_quit = 1;
+ }
+
+ // release that menu key
+ SDL_Event sdlevent;
+ sdlevent.type = SDL_KEYUP;
+ sdlevent.key.keysym.sym = SDLK_q;
+ SDL_PushEvent(&sdlevent);
+ }
+#else
+ menu_loop();
#endif
break;
case EACTION_TOGGLE_HUD:
@@ -384,6 +442,11 @@ void handle_emu_action(emu_action action)
case EACTION_SCREENSHOT:
screenshot();
break;
+#ifdef FUNKEY_S
+ case EACTION_NEXT_SCALER:
+ FK_NextAspectRatio();
+ break;
+#endif
case EACTION_QUIT:
should_quit = 1;
break;
@@ -508,6 +571,10 @@ static void adjust_audio(void) {
int main(int argc, char **argv) {
char content_path[MAX_PATH];
+#ifdef FUNKEY_S
+ char autosave_path[MAX_PATH];
+#endif
+
if (argc > 1) {
if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
@@ -567,10 +634,43 @@ int main(int argc, char **argv) {
ResumeSlot_t ResumeSlot = (ResumeSlot_t)dlsym(mmenu, "ResumeSlot");
if (ResumeSlot) resume_slot = ResumeSlot();
}
+#endif
+#ifdef FUNKEY_S
+ if (IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF | IMG_INIT_WEBP) == 0) {
+ PA_ERROR("Error initializing SDL_Image\n");
+ quit(-1);
+ }
+
+ if (TTF_Init() == -1) {
+ PA_ERROR("Error initializing SDL_ttf\n");
+ quit(-1);
+ }
+ FK_InitMenu();
+
+ state_file_name(autosave_path, MAX_PATH, AUTOSAVE_SLOT);
+ if (access(autosave_path, F_OK) == 0) {
+ if (instant_play) {
+ resume_slot = AUTOSAVE_SLOT;
+ } else {
+ SDL_Surface *screen = SDL_GetVideoSurface();
+ int resume = FK_RunResumeMenu(screen);
+ if (resume == RESUME_YES) {
+ resume_slot = AUTOSAVE_SLOT;
+ }
+ }
+
+ instant_play = false;
+ }
#endif
show_startup_message();
state_resume();
+#ifdef FUNKEY_S
+ remove(autosave_path);
+ remove_config(CONFIG_TYPE_AUTO);
+ FK_InitInstantPlay(argc, argv);
+#endif
+
do {
count_fps();
adjust_audio();
@@ -584,6 +684,18 @@ int main(int argc, char **argv) {
int quit(int code) {
menu_finish();
+
+#ifdef FUNKEY_S
+ if (current_core.initialized && state_allowed()) {
+ state_slot = AUTOSAVE_SLOT;
+ state_write();
+ }
+
+ FK_EndMenu();
+ TTF_Quit();
+ IMG_Quit();
+#endif
+
core_unload();
plat_finish();
exit(code);
diff --git a/main.h b/main.h
index b46ab2c..1d47177 100644
--- a/main.h
+++ b/main.h
@@ -15,9 +15,18 @@ typedef enum {
EACTION_SAVE_STATE,
EACTION_LOAD_STATE,
EACTION_SCREENSHOT,
+#ifdef FUNKEY_S
+ EACTION_NEXT_SCALER,
+#endif
EACTION_QUIT,
} emu_action;
+typedef enum {
+ CONFIG_TYPE_CORE = 0,
+ CONFIG_TYPE_GAME,
+ CONFIG_TYPE_AUTO,
+} config_type;
+
extern bool should_quit;
extern unsigned current_audio_buffer_size;
extern char core_name[MAX_PATH];
@@ -42,10 +51,10 @@ extern char save_template_path[MAX_PATH];
int screenshot(void);
void set_defaults(void);
-int save_config(int is_game);
+int save_config(config_type config_type);
void load_config(void);
void load_config_keys(void);
-int remove_config(int is_game);
+int remove_config(config_type config_type);
void handle_emu_action(emu_action action);
void pa_log(enum retro_log_level level, const char *fmt, ...);
diff --git a/menu.c b/menu.c
index 2926a86..eee7b19 100644
--- a/menu.c
+++ b/menu.c
@@ -74,17 +74,7 @@ me_bind_action emuctrl_actions[] =
static int emu_check_save_file(int slot, int *time)
{
- char fname[MAX_PATH];
- struct stat status;
- int ret;
-
- state_file_name(fname, sizeof(fname), slot);
-
- ret = stat(fname, &status);
- if (ret != 0)
- return 0;
-
- return 1;
+ return state_exists(slot);
}
static int emu_save_load_game(int load, int unused)
@@ -123,7 +113,7 @@ static int mh_restore_defaults(int id, int keys)
static int mh_savecfg(int id, int keys)
{
- if (save_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
+ if (save_config(id == MA_OPT_SAVECFG_GAME ? CONFIG_TYPE_GAME : CONFIG_TYPE_CORE) == 0)
menu_update_msg("config saved");
else
menu_update_msg("failed to write config");
@@ -484,6 +474,8 @@ static const char h_restore_def[] = "Switches back to default settings";
static const char h_show_fps[] = "Shows frames and vsyncs per second";
static const char h_show_cpu[] = "Shows CPU usage";
+
+#if (SCREEN_WIDTH >= 320)
static const char h_enable_drc[] = "Dynamically adjusts audio rate for smoother video";
static const char h_audio_buffer_size[] =
@@ -500,15 +492,36 @@ static const char h_scale_filter[] =
"When stretching, how missing pixels are filled.\n"
"Nearest copies the last pixel. Sharp keeps pixels\n"
"aligned where possible. Smooth adds a blur effect.";
+#else
+static const char h_enable_drc[] =
+ "Dynamically adjusts audio rate for\n"
+ "smoother video.";
+
+static const char h_audio_buffer_size[] =
+ "The audio buffer size, in frames.\n"
+ "Higher values reduce the risk of audio\n"
+ "crackling at the cost of delayed sound.";
+static const char h_scale_size[] =
+ "How much to stretch the screen when\n"
+ "scaling. Native does no stretching.\n"
+ "Aspect uses the correct aspect ratio.\n"
+ "Full uses the whole screen.";
+
+static const char h_scale_filter[] =
+ "When stretching, how missing pixels\n"
+ "are filled. Nearest copies the last\n"
+ "pixel. Sharp tries to keep pixels\n"
+ "aligned. Smooth adds a blur effect.";
+#endif
-static const char *men_scale_size[] = { "Native", "Aspect", "Full", NULL};
+static const char *men_scale_size[] = { "Native", "Aspect", "Full", "Crop", NULL};
static const char *men_scale_filter[] = { "Nearest", "Sharp", "Smooth", NULL};
static menu_entry e_menu_video_options[] =
{
mee_onoff_h ("Show FPS", 0, show_fps, 1, h_show_fps),
- mee_onoff_h ("Show CPU %", 0, show_cpu, 1, h_show_cpu),
+ mee_onoff_h ("Show CPU %%", 0, show_cpu, 1, h_show_cpu),
mee_enum_h ("Screen size", 0, scale_size, men_scale_size, h_scale_size),
mee_enum_h ("Filter", 0, scale_filter, men_scale_filter, h_scale_filter),
mee_range_h ("Audio buffer", 0, audio_buffer_size, 1, 15, h_audio_buffer_size),
diff --git a/options.c b/options.c
index 6cf27cd..decc29c 100644
--- a/options.c
+++ b/options.c
@@ -5,6 +5,7 @@
#include "options.h"
#include "overrides.h"
#include "util.h"
+#include "scale.h"
int show_fps;
int show_cpu;
@@ -19,8 +20,8 @@ enum scale_filter scale_filter;
struct core_options core_options;
#define MAX_DESC_LEN 20
-#define MAX_LINE_LEN 52
-#define MAX_LINES 3
+#define MAX_LINE_LEN SCREEN_WIDTH / 6
+#define MAX_LINES 4
static int options_default_index(const struct core_option_entry* entry, const char *default_value) {
const char *value;
diff --git a/overrides.c b/overrides.c
index bbadb62..576328a 100644
--- a/overrides.c
+++ b/overrides.c
@@ -13,6 +13,7 @@
#include "overrides/smsplus.h"
#include "overrides/snes9x2002.h"
#include "overrides/snes9x2005.h"
+#include "overrides/snes9x2005_plus.h"
#include "util.h"
static const struct core_override overrides[] = {
@@ -30,6 +31,7 @@ static const struct core_override overrides[] = {
smsplus_overrides,
snes9x2002_overrides,
snes9x2005_overrides,
+ snes9x2005_plus_overrides,
};
static const struct core_override *override;
diff --git a/overrides/gpsp.h b/overrides/gpsp.h
index 44a35e0..86fc866 100644
--- a/overrides/gpsp.h
+++ b/overrides/gpsp.h
@@ -29,7 +29,7 @@ static const struct core_override_option gpsp_core_option_overrides[] = {
},
{
.key = "gpsp_frameskip_threshold",
- .desc = "FS Threshold (%)",
+ .desc = "FS Threshold (%%)",
.info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.",
},
{
diff --git a/overrides/mame2000.h b/overrides/mame2000.h
index 9d306c0..81af3fc 100644
--- a/overrides/mame2000.h
+++ b/overrides/mame2000.h
@@ -11,7 +11,7 @@ static const struct core_override_option mame2000_core_option_overrides[] = {
{
.key = "mame2000-frameskip_threshold",
.info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.",
- .retro_var_value = "FS Threshold (%); 30|40|50|60",
+ .retro_var_value = "FS Threshold (%%); 30|40|50|60",
},
{
.key = "mame2000-frameskip_interval",
diff --git a/overrides/pcsx_rearmed.h b/overrides/pcsx_rearmed.h
index e68724e..b35cc2d 100644
--- a/overrides/pcsx_rearmed.h
+++ b/overrides/pcsx_rearmed.h
@@ -12,7 +12,7 @@ static const struct core_override_option pcsx_rearmed_core_option_overrides[] =
},
{
.key = "pcsx_rearmed_frameskip_threshold",
- .desc = "FS Threshold (%)",
+ .desc = "FS Threshold (%%)",
.info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.",
},
{
diff --git a/overrides/picodrive.h b/overrides/picodrive.h
index aa1dc30..f0c9ca0 100644
--- a/overrides/picodrive.h
+++ b/overrides/picodrive.h
@@ -43,7 +43,7 @@ static const struct core_override_option picodrive_core_option_overrides[] = {
},
{
.key = "picodrive_frameskip_threshold",
- .desc = "FS Threshold (%)",
+ .desc = "FS Threshold (%%)",
.info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.",
},
{
diff --git a/overrides/snes9x2002.h b/overrides/snes9x2002.h
index fb54780..4cb3b5d 100644
--- a/overrides/snes9x2002.h
+++ b/overrides/snes9x2002.h
@@ -8,7 +8,7 @@ static const struct core_override_option snes9x2002_core_option_overrides[] = {
},
{
.key = "snes9x2002_frameskip_threshold",
- .desc = "FS Threshold (%)",
+ .desc = "FS Threshold (%%)",
.info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.",
},
{
diff --git a/overrides/snes9x2005.h b/overrides/snes9x2005.h
index 237a89a..b9c04a5 100644
--- a/overrides/snes9x2005.h
+++ b/overrides/snes9x2005.h
@@ -3,6 +3,7 @@
static const struct core_override_option snes9x2005_core_option_overrides[] = {
{
.key = "snes9x_2005_region",
+ .desc = "Region",
.info = "'PAL' is 50hz, 'NTSC' is 60hz. Games will run faster or slower than normal if the incorrect region is selected.",
},
{
@@ -12,7 +13,7 @@ static const struct core_override_option snes9x2005_core_option_overrides[] = {
},
{
.key = "snes9x_2005_frameskip_threshold",
- .desc = "FS Threshold (%)",
+ .desc = "FS Threshold (%%)",
.info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.",
},
{
@@ -33,7 +34,11 @@ static const struct core_override_option snes9x2005_core_option_overrides[] = {
},
{
.key = "snes9x_2005_dynarec",
+#ifdef FUNKEY_S
+ .default_value = "disabled",
+#else
.default_value = "enabled",
+#endif
},
{ NULL }
};
diff --git a/overrides/snes9x2005_plus.h b/overrides/snes9x2005_plus.h
new file mode 100644
index 0000000..2720b45
--- /dev/null
+++ b/overrides/snes9x2005_plus.h
@@ -0,0 +1,75 @@
+#include "overrides.h"
+
+static const struct core_override_option snes9x2005_plus_core_option_overrides[] = {
+ {
+ .key = "snes9x_2005_region",
+ .desc = "Region",
+ .info = "'PAL' is 50hz, 'NTSC' is 60hz. Games will run faster or slower than normal if the incorrect region is selected.",
+ },
+ {
+ .key = "snes9x_2005_frameskip",
+ .info = "Skip frames to avoid audio crackling. Improves performance at the expense of visual smoothness.",
+ .default_value = "auto"
+ },
+ {
+ .key = "snes9x_2005_frameskip_threshold",
+ .desc = "FS Threshold (%%)",
+ .info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.",
+ },
+ {
+ .key = "snes9x_2005_frameskip_interval",
+ .desc = "FS Interval",
+ .info = "The maximum number of frames that can be skipped before a new frame is rendered.",
+ .default_value = "3"
+ },
+ {
+ .key = "snes9x_2005_overclock_cycles",
+ .desc = "Overclock (Restart)",
+ .info = "Alleviate normal SNES slowdown. Compatible keeps as much compatibility as possible. Max will reduce more slowdown but break more games.",
+ },
+ {
+ .key = "snes9x_2005_reduce_sprite_flicker",
+ .desc = "Reduce Flicker",
+ .info = "Raises sprite limit to reduce flickering in games.",
+ },
+ {
+ .key = "snes9x_2005_dynarec",
+#ifdef FUNKEY_S
+ .default_value = "disabled",
+#else
+ .default_value = "enabled",
+#endif
+ },
+ { NULL }
+};
+
+me_bind_action snes9x2005_plus_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 },
+ { "A BUTTON ", 1 << RETRO_DEVICE_ID_JOYPAD_A },
+ { "B BUTTON ", 1 << RETRO_DEVICE_ID_JOYPAD_B },
+ { "X BUTTON ", 1 << RETRO_DEVICE_ID_JOYPAD_X },
+ { "Y BUTTON ", 1 << RETRO_DEVICE_ID_JOYPAD_Y },
+ { "START ", 1 << RETRO_DEVICE_ID_JOYPAD_START },
+ { "SELECT ", 1 << RETRO_DEVICE_ID_JOYPAD_SELECT },
+ { "L BUTTON ", 1 << RETRO_DEVICE_ID_JOYPAD_L },
+ { "R BUTTON ", 1 << RETRO_DEVICE_ID_JOYPAD_R },
+ { NULL, 0 }
+};
+
+const struct core_override_fast_forward snes9x2005_plus_fast_forward = {
+ .type_key = "snes9x_2005_frameskip",
+ .type_value = "auto",
+ .interval_key = "snes9x_2005_frameskip_interval"
+};
+
+#define snes9x2005_plus_overrides { \
+ .core_name = "snes9x2005_plus", \
+ .fast_forward = &snes9x2005_plus_fast_forward, \
+ .actions = snes9x2005_plus_ctrl_actions, \
+ .action_size = array_size(snes9x2005_plus_ctrl_actions), \
+ .options = snes9x2005_plus_core_option_overrides \
+}
diff --git a/patches/libpicofe/0002-small-screen.patch b/patches/libpicofe/0002-small-screen.patch
new file mode 100644
index 0000000..2a5d828
--- /dev/null
+++ b/patches/libpicofe/0002-small-screen.patch
@@ -0,0 +1,13 @@
+diff --git a/menu.c b/menu.c
+index e91f84a..3c98f1f 100644
+--- a/menu.c
++++ b/menu.c
+@@ -518,7 +518,7 @@ static void me_draw(const menu_entry *entries, int sel, void (*draw_more)(void))
+ {
+ const menu_entry *ent, *ent_sel = entries;
+ int x, y, w = 0, h = 0;
+- int offs, col2_offs = 27 * me_mfont_w;
++ int offs, col2_offs = (g_menuscreen_w >= 320 ? 27 : 0) * me_mfont_w;
+ int vi_sel_ln = 0;
+ const char *name;
+ int i, n;
diff --git a/patches/snes9x2005/1001-funkey-s-support.patch b/patches/snes9x2005/1001-funkey-s-support.patch
new file mode 100644
index 0000000..1825917
--- /dev/null
+++ b/patches/snes9x2005/1001-funkey-s-support.patch
@@ -0,0 +1,93 @@
+diff --git a/Makefile b/Makefile
+index 23b8979..0a09e85 100644
+--- a/Makefile
++++ b/Makefile
+@@ -74,13 +74,46 @@ ifeq ($(platform), unix)
+ fpic := -fPIC
+ SHARED := -shared -Wl,--no-undefined -Wl,--version-script=link.T
+ CFLAGS += -fno-builtin -fno-exceptions -ffunction-sections
+-# ARM
++# (armv7 a7, hard point, neon based) ###
++# NESC, SNESC, C64 mini
++else ifeq ($(platform), classic_armv7_a7)
++ TARGET := $(TARGET_NAME)_libretro.so
++ fpic := -fPIC
++ SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined
++ CFLAGS += -Ofast \
++ -flto=4 -fwhole-program -fuse-linker-plugin \
++ -fdata-sections -ffunction-sections -Wl,--gc-sections \
++ -fno-stack-protector -fno-ident -fomit-frame-pointer \
++ -falign-functions=1 -falign-jumps=1 -falign-loops=1 \
++ -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-unroll-loops \
++ -fmerge-all-constants -fno-math-errno \
++ -marm -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard
++ CC = $(CROSS_COMPILE)gcc
++ AR = $(CROSS_COMPILE)ar
++ CXXFLAGS += $(CFLAGS)
++ CPPFLAGS += $(CFLAGS)
++ ASFLAGS += $(CFLAGS)
++ HAVE_NEON = 1
++ ARCH = arm
++ BUILTIN_GPU = neon
++ USE_DYNAREC = 1
++ ifeq ($(shell echo `$(CC) -dumpversion` "< 4.9" | bc -l), 1)
++ CFLAGS += -march=armv7-a
++ else
++ CFLAGS += -march=armv7ve
++ # If gcc is 5.0 or later
++ ifeq ($(shell echo `$(CC) -dumpversion` ">= 5" | bc -l), 1)
++ LDFLAGS += -static-libgcc -static-libstdc++
++ endif
++ endif
++#######################################
++# generic ARM
+ else ifneq (,$(findstring armv,$(platform)))
+ TARGET := $(TARGET_NAME)_libretro.so
+ fpic := -fPIC
+ SHARED := -shared -Wl,--no-undefined -Wl,--version-script=link.T
+- CC = gcc
+- CXX = g++
++ CC = $(CROSS_COMPILE)gcc
++ CXX = $(CROSS_COMPILE)g++
+ PLATFORM_DEFINES += -marm
+ ifneq (,$(findstring softfloat,$(platform)))
+ PLATFORM_DEFINES += -mfloat-abi=softfp
+@@ -306,38 +339,6 @@ else
+ endif
+ OPTIMIZE += -Ofast -DNDEBUG=1
+
+-# (armv7 a7, hard point, neon based) ###
+-# NESC, SNESC, C64 mini
+-else ifeq ($(platform), classic_armv7_a7)
+- TARGET := $(TARGET_NAME)_libretro.so
+- fpic := -fPIC
+- SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined
+- CFLAGS += -Ofast \
+- -flto=4 -fwhole-program -fuse-linker-plugin \
+- -fdata-sections -ffunction-sections -Wl,--gc-sections \
+- -fno-stack-protector -fno-ident -fomit-frame-pointer \
+- -falign-functions=1 -falign-jumps=1 -falign-loops=1 \
+- -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-unroll-loops \
+- -fmerge-all-constants -fno-math-errno \
+- -marm -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard
+- CXXFLAGS += $(CFLAGS)
+- CPPFLAGS += $(CFLAGS)
+- ASFLAGS += $(CFLAGS)
+- HAVE_NEON = 1
+- ARCH = arm
+- BUILTIN_GPU = neon
+- USE_DYNAREC = 1
+- ifeq ($(shell echo `$(CC) -dumpversion` "< 4.9" | bc -l), 1)
+- CFLAGS += -march=armv7-a
+- else
+- CFLAGS += -march=armv7ve
+- # If gcc is 5.0 or later
+- ifeq ($(shell echo `$(CC) -dumpversion` ">= 5" | bc -l), 1)
+- LDFLAGS += -static-libgcc -static-libstdc++
+- endif
+- endif
+-#######################################
+-
+ # Windows MSVC 2010 x64
+ else ifeq ($(platform), windows_msvc2010_x64)
+ CC = cl.exe
diff --git a/patches/snes9x2005_plus/1001-funkey-s-support.patch b/patches/snes9x2005_plus/1001-funkey-s-support.patch
new file mode 100644
index 0000000..1825917
--- /dev/null
+++ b/patches/snes9x2005_plus/1001-funkey-s-support.patch
@@ -0,0 +1,93 @@
+diff --git a/Makefile b/Makefile
+index 23b8979..0a09e85 100644
+--- a/Makefile
++++ b/Makefile
+@@ -74,13 +74,46 @@ ifeq ($(platform), unix)
+ fpic := -fPIC
+ SHARED := -shared -Wl,--no-undefined -Wl,--version-script=link.T
+ CFLAGS += -fno-builtin -fno-exceptions -ffunction-sections
+-# ARM
++# (armv7 a7, hard point, neon based) ###
++# NESC, SNESC, C64 mini
++else ifeq ($(platform), classic_armv7_a7)
++ TARGET := $(TARGET_NAME)_libretro.so
++ fpic := -fPIC
++ SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined
++ CFLAGS += -Ofast \
++ -flto=4 -fwhole-program -fuse-linker-plugin \
++ -fdata-sections -ffunction-sections -Wl,--gc-sections \
++ -fno-stack-protector -fno-ident -fomit-frame-pointer \
++ -falign-functions=1 -falign-jumps=1 -falign-loops=1 \
++ -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-unroll-loops \
++ -fmerge-all-constants -fno-math-errno \
++ -marm -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard
++ CC = $(CROSS_COMPILE)gcc
++ AR = $(CROSS_COMPILE)ar
++ CXXFLAGS += $(CFLAGS)
++ CPPFLAGS += $(CFLAGS)
++ ASFLAGS += $(CFLAGS)
++ HAVE_NEON = 1
++ ARCH = arm
++ BUILTIN_GPU = neon
++ USE_DYNAREC = 1
++ ifeq ($(shell echo `$(CC) -dumpversion` "< 4.9" | bc -l), 1)
++ CFLAGS += -march=armv7-a
++ else
++ CFLAGS += -march=armv7ve
++ # If gcc is 5.0 or later
++ ifeq ($(shell echo `$(CC) -dumpversion` ">= 5" | bc -l), 1)
++ LDFLAGS += -static-libgcc -static-libstdc++
++ endif
++ endif
++#######################################
++# generic ARM
+ else ifneq (,$(findstring armv,$(platform)))
+ TARGET := $(TARGET_NAME)_libretro.so
+ fpic := -fPIC
+ SHARED := -shared -Wl,--no-undefined -Wl,--version-script=link.T
+- CC = gcc
+- CXX = g++
++ CC = $(CROSS_COMPILE)gcc
++ CXX = $(CROSS_COMPILE)g++
+ PLATFORM_DEFINES += -marm
+ ifneq (,$(findstring softfloat,$(platform)))
+ PLATFORM_DEFINES += -mfloat-abi=softfp
+@@ -306,38 +339,6 @@ else
+ endif
+ OPTIMIZE += -Ofast -DNDEBUG=1
+
+-# (armv7 a7, hard point, neon based) ###
+-# NESC, SNESC, C64 mini
+-else ifeq ($(platform), classic_armv7_a7)
+- TARGET := $(TARGET_NAME)_libretro.so
+- fpic := -fPIC
+- SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined
+- CFLAGS += -Ofast \
+- -flto=4 -fwhole-program -fuse-linker-plugin \
+- -fdata-sections -ffunction-sections -Wl,--gc-sections \
+- -fno-stack-protector -fno-ident -fomit-frame-pointer \
+- -falign-functions=1 -falign-jumps=1 -falign-loops=1 \
+- -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-unroll-loops \
+- -fmerge-all-constants -fno-math-errno \
+- -marm -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard
+- CXXFLAGS += $(CFLAGS)
+- CPPFLAGS += $(CFLAGS)
+- ASFLAGS += $(CFLAGS)
+- HAVE_NEON = 1
+- ARCH = arm
+- BUILTIN_GPU = neon
+- USE_DYNAREC = 1
+- ifeq ($(shell echo `$(CC) -dumpversion` "< 4.9" | bc -l), 1)
+- CFLAGS += -march=armv7-a
+- else
+- CFLAGS += -march=armv7ve
+- # If gcc is 5.0 or later
+- ifeq ($(shell echo `$(CC) -dumpversion` ">= 5" | bc -l), 1)
+- LDFLAGS += -static-libgcc -static-libstdc++
+- endif
+- endif
+-#######################################
+-
+ # Windows MSVC 2010 x64
+ else ifeq ($(platform), windows_msvc2010_x64)
+ CC = cl.exe
diff --git a/plat.h b/plat.h
index 84e4d1e..52e7652 100644
--- a/plat.h
+++ b/plat.h
@@ -8,8 +8,6 @@ struct audio_frame {
int16_t right;
};
-#define HUD_LEN 41
-
int plat_init(void);
int plat_reinit(void);
void plat_finish(void);
diff --git a/plat_funkey.c b/plat_funkey.c
new file mode 100644
index 0000000..3447994
--- /dev/null
+++ b/plat_funkey.c
@@ -0,0 +1,115 @@
+#include
+#include "libretro.h"
+#include "libpicofe/plat.h"
+#include "libpicofe/input.h"
+#include "libpicofe/in_sdl.h"
+#include "main.h"
+#include "util.h"
+
+#define MAX_SAMPLE_RATE 48000
+
+static const struct in_default_bind in_sdl_defbinds[] = {
+ { SDLK_u, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_UP },
+ { SDLK_d, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_DOWN },
+ { SDLK_l, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_LEFT },
+ { SDLK_r, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_RIGHT },
+ { SDLK_b, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_B },
+ { SDLK_a, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_A },
+ { SDLK_x, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_X },
+ { SDLK_y, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_Y },
+ { SDLK_s, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_START },
+ { SDLK_k, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_SELECT },
+ { SDLK_m, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_L },
+ { SDLK_n, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_R },
+ { SDLK_v, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_L2 },
+ { SDLK_o, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_R2 },
+ { SDLK_q, IN_BINDTYPE_EMU, EACTION_MENU },
+ { SDLK_h, IN_BINDTYPE_EMU, EACTION_NEXT_SCALER },
+ { 0, 0, 0 }
+};
+
+const struct menu_keymap in_sdl_key_map[] =
+{
+ { SDLK_u, PBTN_UP },
+ { SDLK_d, PBTN_DOWN },
+ { SDLK_l, PBTN_LEFT },
+ { SDLK_r, PBTN_RIGHT },
+ { SDLK_a, PBTN_MOK },
+ { SDLK_b, PBTN_MBACK },
+ { SDLK_x, PBTN_MA2 },
+ { SDLK_y, PBTN_MA3 },
+ { SDLK_m, PBTN_L },
+ { SDLK_n, PBTN_R },
+ { SDLK_q, PBTN_MENU },
+};
+
+const struct menu_keymap in_sdl_joy_map[] =
+{
+ { SDLK_u, PBTN_UP },
+ { SDLK_d, PBTN_DOWN },
+ { SDLK_l, PBTN_LEFT },
+ { SDLK_r, PBTN_RIGHT },
+ { SDLK_WORLD_0, PBTN_MOK },
+ { SDLK_WORLD_1, PBTN_MBACK },
+ { SDLK_WORLD_2, PBTN_MA2 },
+ { SDLK_WORLD_3, PBTN_MA3 },
+};
+
+static const char * const in_sdl_key_names[SDLK_LAST] = {
+ [SDLK_u] = "UP",
+ [SDLK_d] = "DOWN",
+ [SDLK_l] = "LEFT",
+ [SDLK_r] = "RIGHT",
+ [SDLK_x] = "X",
+ [SDLK_b] = "B",
+ [SDLK_a] = "A",
+ [SDLK_y] = "Y",
+ [SDLK_s] = "START",
+ [SDLK_k] = "FN",
+ [SDLK_m] = "L",
+ [SDLK_n] = "R",
+ [SDLK_v] = "FN+L",
+ [SDLK_o] = "FN+R",
+ [SDLK_1] = "MENU+UP",
+ [SDLK_2] = "MENU+DOWN",
+ [SDLK_3] = "MENU+LEFT",
+ [SDLK_4] = "MENU+RIGHT",
+ [SDLK_5] = "MENU+B",
+ [SDLK_6] = "MENU+A",
+ [SDLK_7] = "MENU+X",
+ [SDLK_8] = "MENU+Y",
+ [SDLK_9] = "MENU+START",
+ [SDLK_0] = "MENU+SELECT",
+ [SDLK_TAB] = "MENU+L",
+ [SDLK_BACKSLASH] = "MENU+R",
+ [SDLK_q] = "MENU",
+};
+
+static const struct mod_keymap in_sdl_mod_keymap[] = {
+ { SDLK_u, SDLK_1 },
+ { SDLK_d, SDLK_2 },
+ { SDLK_l, SDLK_3 },
+ { SDLK_r, SDLK_4 },
+ { SDLK_b, SDLK_5 },
+ { SDLK_a, SDLK_6 },
+ { SDLK_x, SDLK_7 },
+ { SDLK_y, SDLK_8 },
+ { SDLK_s, SDLK_9 },
+ { SDLK_k, SDLK_0 },
+ { SDLK_TAB, SDLK_TAB }, /* mod+L1 = L2 */
+ { SDLK_BACKSPACE, SDLK_BACKSLASH }, /* mod+R1 = R2 */
+};
+
+static const struct in_pdata in_sdl_platform_data = {
+ .defbinds = in_sdl_defbinds,
+ .key_map = in_sdl_key_map,
+ .kmap_size = array_size(in_sdl_key_map),
+ .joy_map = in_sdl_joy_map,
+ .jmap_size = array_size(in_sdl_joy_map),
+ .key_names = in_sdl_key_names,
+ /* .mod_key = SDLK_ESCAPE,
+ * .mod_keymap = in_sdl_mod_keymap,
+ * .modmap_size = array_size(in_sdl_mod_keymap), */
+};
+
+#include "plat_sdl.c"
diff --git a/scale.c b/scale.c
index 2f21ddc..61f44d4 100644
--- a/scale.c
+++ b/scale.c
@@ -104,6 +104,29 @@ static void scale_1x(unsigned w, unsigned h, size_t pitch, const void *src, void
}
}
+static void scale_crop(unsigned w, unsigned h, size_t pitch, const void *src, void *dst) {
+ int dst_y = ((SCREEN_HEIGHT - (short)h) / 2);
+ int dst_x = ((SCREEN_WIDTH - (short)w) * SCREEN_BPP / 2);
+
+ if (dst_y < 0) {
+ src += -dst_y * pitch;
+ dst_y = 0;
+ h = SCREEN_HEIGHT;
+ }
+
+ if (dst_x < 0) {
+ src += -dst_x;
+ dst_x = 0;
+ w = SCREEN_WIDTH;
+ }
+
+ dst += dst_y * SCREEN_PITCH + dst_x;
+
+ for (unsigned y = 0; y < h; y++) {
+ memcpy(dst + y * SCREEN_PITCH, src + y * pitch, w * SCREEN_BPP);
+ }
+}
+
static void scale_nearest(unsigned w, unsigned h, size_t pitch, const void *src, void *dst) {
int dy = -dst_h;
unsigned lines = h;
@@ -416,7 +439,10 @@ static void scale_select_scaler(unsigned w, unsigned h, size_t pitch) {
blend_args.blend_line = NULL;
}
- if (scale_size == SCALE_SIZE_FULL) {
+ if (scale_size == SCALE_SIZE_CROP) {
+ scaler = scale_crop;
+ return;
+ } if (scale_size == SCALE_SIZE_FULL) {
dst_w = SCREEN_WIDTH;
dst_h = SCREEN_HEIGHT;
dst_offs = 0;
@@ -447,8 +473,8 @@ static void scale_select_scaler(unsigned w, unsigned h, size_t pitch) {
if (!scaler && w == 160 && h == 144) {
if (scale_size == SCALE_SIZE_ASPECT && scale_filter == SCALE_FILTER_SHARP) {
- unsigned dst_x = ((320 - 240) * SCREEN_BPP / 2);
- unsigned dst_y = ((240 - 216) / 2);
+ unsigned dst_x = ((SCREEN_WIDTH - 240) * SCREEN_BPP / 2);
+ unsigned dst_y = ((SCREEN_HEIGHT - 216) / 2);
dst_offs = dst_y * SCREEN_PITCH + dst_x;
scaler = scale_sharp_160x144_240x216;
@@ -456,18 +482,16 @@ static void scale_select_scaler(unsigned w, unsigned h, size_t pitch) {
}
}
- if (!scaler && w == 240 && h == 160) {
- if (scale_filter == SCALE_FILTER_SHARP) {
+ if (SCREEN_WIDTH == 320 && scale_filter == SCALE_FILTER_SHARP) {
+ if (!scaler && w == 240 && h == 160) {
scaler = scale_sharp_240x160_320xXXX;
return;
}
- }
- if (!scaler &&
- w == 256 &&
- (current_aspect_ratio == 4.0f / 3.0f || scale_size == SCALE_SIZE_FULL))
- {
- if (scale_filter == SCALE_FILTER_SHARP) {
+ if (!scaler &&
+ w == 256 &&
+ (current_aspect_ratio == 4.0f / 3.0f || scale_size == SCALE_SIZE_FULL))
+ {
scaler = scale_sharp_256xXXX_320xXXX;
return;
}
diff --git a/scale.h b/scale.h
index c415124..fe02d92 100644
--- a/scale.h
+++ b/scale.h
@@ -1,7 +1,18 @@
#ifndef __SCALE_H__
#define __SCALE_H__
+#ifdef FUNKEY_S
+#define SCREEN_WIDTH 240
+#else
#define SCREEN_WIDTH 320
+#endif
+
+#if SCREEN_WIDTH == 240
+#define HUD_LEN 31
+#else
+#define HUD_LEN 41
+#endif
+
#define SCREEN_HEIGHT 240
#define SCREEN_BPP 2
#define SCREEN_PITCH (SCREEN_BPP * SCREEN_WIDTH)
@@ -10,6 +21,7 @@ enum scale_size {
SCALE_SIZE_NONE,
SCALE_SIZE_ASPECT,
SCALE_SIZE_FULL,
+ SCALE_SIZE_CROP,
};
enum scale_filter {
--
cgit v1.2.3