From f1a2b41ffbb1c4f428cf0a63cbdce19705b7fc6e Mon Sep 17 00:00:00 2001 From: Vincent-FK Date: Thu, 1 Apr 2021 23:42:24 +0200 Subject: [PATCH] added funkey changes from v1.93 --- .gitmodules | 2 +- Makefile | 2 +- configure | 9 +- platform/common/configfile.c | 272 ++++++ platform/common/configfile.h | 28 + platform/common/emu.c | 251 +++++- platform/common/emu.h | 14 +- platform/common/input_pico.h | 65 +- platform/common/inputmap_kbd.c | 76 +- platform/common/main.c | 170 +++- platform/common/menu_pico.c | 1317 +++++++++++++++++++++++++++ platform/common/menu_pico.h | 61 ++ platform/common/plat_sdl.c | 1542 +++++++++++++++++++++++++++++++- platform/common/plat_sdl.h | 1 + 14 files changed, 3741 insertions(+), 69 deletions(-) mode change 100755 => 100644 configure create mode 100644 platform/common/configfile.c create mode 100644 platform/common/configfile.h diff --git a/.gitmodules b/.gitmodules index 23cc3b36..f38d3399 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "platform/libpicofe"] path = platform/libpicofe - url = https://github.com/irixxxx/libpicofe.git + url = https://github.com/FunKey-Project/picofe-irixxxx.git [submodule "cpu/cyclone"] path = cpu/cyclone url = https://github.com/notaz/cyclone68000.git diff --git a/Makefile b/Makefile index cf1824d5..5e1c726e 100644 --- a/Makefile +++ b/Makefile @@ -210,7 +210,7 @@ endif ifeq "$(USE_FRONTEND)" "1" # common -OBJS += platform/common/main.o platform/common/emu.o \ +OBJS += platform/common/main.o platform/common/configfile.o platform/common/emu.o \ platform/common/menu_pico.o platform/common/config_file.o # libpicofe diff --git a/configure b/configure old mode 100755 new mode 100644 index 74202070..4d954b8d --- a/configure +++ b/configure @@ -42,15 +42,16 @@ check_define() platform_list="generic pandora gp2x wiz caanoo dingux retrofw gcw0 rg350 opendingux rpi1 rpi2 psp" platform="generic" sound_driver_list="oss alsa sdl" -sound_drivers="" +sound_drivers="sdl" have_armv5="" have_armv6="" -have_armv7="" +have_armv7="yes" have_arm_oabi="" -have_arm_neon="" +have_arm_neon="yes" have_libavcodec="" have_libchdr="" -need_sdl="no" +need_sdl="yes" +need_xlib="no" need_zlib="no" # these are for known platforms optimize_cortexa8="no" diff --git a/platform/common/configfile.c b/platform/common/configfile.c new file mode 100644 index 00000000..70b8d53b --- /dev/null +++ b/platform/common/configfile.c @@ -0,0 +1,272 @@ +// configfile.c - handles loading and saving the configuration options +#include +#include +#include +#include +#include +#include + +#include "configfile.h" + +#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0])) + +enum ConfigOptionType { + CONFIG_TYPE_BOOL, + CONFIG_TYPE_UINT, + CONFIG_TYPE_FLOAT, + CONFIG_TYPE_ASPECT_RATIO, +}; + +struct ConfigOption { + const char *name; + enum ConfigOptionType type; + union { + bool *boolValue; + unsigned int *uintValue; + float *floatValue; + }; +}; + +#undef X +#define X(a, b) b, +const char *aspect_ratio_name[] = {ASPECT_RATIOS}; +unsigned int aspect_ratio_factor_step = 10; + +/* + *Config options and default values + */ +unsigned int aspect_ratio = ASPECT_RATIOS_TYPE_STRETCHED; +unsigned int aspect_ratio_factor_percent = 50; + +static const struct ConfigOption options[] = { + {.name = "aspect_ratio", .type = CONFIG_TYPE_ASPECT_RATIO, .uintValue = &aspect_ratio}, + {.name = "aspect_ratio_factor_percent", .type = CONFIG_TYPE_UINT, .uintValue = &aspect_ratio_factor_percent}, +}; + +// Reads an entire line from a file (excluding the newline character) and returns an allocated string +// Returns NULL if no lines could be read from the file +static char *read_file_line(FILE *file) { + char *buffer; + size_t bufferSize = 64; + size_t offset = 0; // offset in buffer to write + + buffer = (char*)malloc(bufferSize); + while (1) { + // Read a line from the file + if (fgets(buffer + offset, bufferSize - offset, file) == NULL) { + free(buffer); + return NULL; // Nothing could be read. + } + offset = strlen(buffer); + assert(offset > 0); + + if (feof(file)) // EOF was reached + break; + + // If a newline was found, remove the trailing newline and exit + if (buffer[offset - 1] == '\n') { + buffer[offset - 1] = '\0'; + break; + } + + // If no newline or EOF was reached, then the whole line wasn't read. + bufferSize *= 2; // Increase buffer size + buffer = (char*)realloc(buffer, bufferSize); + assert(buffer != NULL); + } + + return buffer; +} + +// Returns the position of the first non-whitespace character +static char *skip_whitespace(char *str) { + while (isspace(*str)) + str++; + return str; +} + +// Returns the position of the first non-whitespace or '=' character +static char *skip_whitespace_or_equal(char *str) { + while (isspace(*str) || *str=='=') + str++; + return str; +} + +// NULL-terminates the current whitespace-delimited word, and returns a pointer to the next word +static char *word_split(char *str) { + // Precondition: str must not point to whitespace + assert(!isspace(*str)); + + // Find either the next whitespace, '=' or end of string + while (!isspace(*str) && *str != '\0' && *str != '=') + str++; + if (*str == '\0') // End of string + return str; + + // Terminate current word + *(str++) = '\0'; + + // Skip whitespace to next word + return skip_whitespace_or_equal(str); +} + +// Splits a string into words, and stores the words into the 'tokens' array +// 'maxTokens' is the length of the 'tokens' array +// Returns the number of tokens parsed +static unsigned int tokenize_string(char *str, int maxTokens, char **tokens) { + int count = 0; + + str = skip_whitespace(str); + while (str[0] != '\0' && count < maxTokens) { + tokens[count] = str; + str = word_split(str); + count++; + } + return count; +} + +// Loads the config file specified by 'filepath' +void configfile_load(const char *filepath) { + FILE *file; + char *line; + unsigned int cur_line = 0; + char *current_section = NULL; + + printf("Loading configuration from '%s'\n", filepath); + + // Open file or create it if it does not exist + file = fopen(filepath, "r"); + if (file == NULL) { + // Create a new config file and save defaults + printf("Config file '%s' not found. Creating it.\n", filepath); + configfile_save(filepath); + return; + } + + // Go through each line in the file + while ((line = read_file_line(file)) != NULL) { + char *p = line; + char *tokens[2]; + int numTokens; + cur_line++; + + // Get tokens + while (isspace(*p)) p++; + numTokens = tokenize_string(p, 2, tokens); + + // Get content + if (numTokens != 0) { + + // Pass comments + if(tokens[0][0]=='#') continue; + + // Check sections - useless for now + if(tokens[0][0]=='['){ + p=tokens[0]; + while(*p != '\0' && *p!=']') p++; + if(*p == '\0') continue; + *p=0; + if(current_section) free(current_section); + current_section = (char*)malloc(strlen(tokens[0])); //strlen(tokens[0])-1+1 + strcpy(current_section, &tokens[0][1]); + printf("New Section: %s\n", current_section); + continue; + } + + if (numTokens == 2) { + const struct ConfigOption *option = NULL; + + for (unsigned int i = 0; i < ARRAY_LEN(options); i++) { + if (strcmp(tokens[0], options[i].name) == 0) { + option = &options[i]; + break; + } + } + if (option == NULL){ + printf("Unknown option '%s'\n", tokens[0]); + } + else { + printf("Reading option: '%s', value: '%s'\n", tokens[0], tokens[1]); + switch (option->type) { + case CONFIG_TYPE_BOOL: + if (strcmp(tokens[1], "true") == 0) + *option->boolValue = true; + else if (strcmp(tokens[1], "false") == 0) + *option->boolValue = false; + else{ + printf("Unknown CONFIG_TYPE_BOOL value: '%s', using default: %s\n", + tokens[1], (*option->boolValue)?"true":"false"); + } + break; + case CONFIG_TYPE_UINT: + sscanf(tokens[1], "%u", option->uintValue); + break; + case CONFIG_TYPE_FLOAT: + sscanf(tokens[1], "%f", option->floatValue); + break; + case CONFIG_TYPE_ASPECT_RATIO: + ;unsigned int cur_ar; + for(cur_ar=0; cur_aruintValue = cur_ar; + break; + } + } + if(cur_ar >= NB_ASPECT_RATIOS_TYPES){ + printf("Unknown CONFIG_TYPE_ASPECT_RATIO value: '%s', using default value: %s\n", + tokens[1], aspect_ratio_name[*option->uintValue]); + } + break; + default: + printf("Unknown option type '%d'\n", option->type); + break; + } + } + } + else{ + fprintf(stderr, "Error in line %d: wrong format\n", cur_line); + } + } + free(line); + } + + fclose(file); +} + +// Writes the config file to 'filepath' +void configfile_save(const char *filepath) { + FILE *file; + + printf("Saving configuration to '%s'\n", filepath); + + file = fopen(filepath, "w"); + if (file == NULL) { + // error + printf("Could not save\n"); + return; + } + printf("Saved !\n"); + + for (unsigned int i = 0; i < ARRAY_LEN(options); i++) { + const struct ConfigOption *option = &options[i]; + + switch (option->type) { + case CONFIG_TYPE_BOOL: + fprintf(file, "%s = %s\n", option->name, *option->boolValue ? "true" : "false"); + break; + case CONFIG_TYPE_UINT: + fprintf(file, "%s = %u\n", option->name, *option->uintValue); + break; + case CONFIG_TYPE_FLOAT: + fprintf(file, "%s = %f\n", option->name, *option->floatValue); + break; + case CONFIG_TYPE_ASPECT_RATIO: + fprintf(file, "%s = %s\n", option->name, aspect_ratio_name[*option->uintValue]); + break; + default: + assert(0); // unknown type + } + } + + fclose(file); +} diff --git a/platform/common/configfile.h b/platform/common/configfile.h new file mode 100644 index 00000000..83db2150 --- /dev/null +++ b/platform/common/configfile.h @@ -0,0 +1,28 @@ +#ifndef CONFIGFILE_H +#define CONFIGFILE_H + +#include + + +///------ Definition of the different aspect ratios +#define ASPECT_RATIOS \ + X(ASPECT_RATIOS_TYPE_MANUAL, "ZOOMED") \ + X(ASPECT_RATIOS_TYPE_STRETCHED, "STRETCHED") \ + X(ASPECT_RATIOS_TYPE_CROPPED, "CROPPED") \ + X(ASPECT_RATIOS_TYPE_SCALED, "SCALED") \ + 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; + +extern unsigned int aspect_ratio; +extern unsigned int aspect_ratio_factor_percent; +extern const char * aspect_ratio_name[]; +extern unsigned int aspect_ratio_factor_step; + +void configfile_load(const char *filename); +void configfile_save(const char *filename); + +#endif diff --git a/platform/common/emu.c b/platform/common/emu.c index b296a647..c1c359fd 100644 --- a/platform/common/emu.c +++ b/platform/common/emu.c @@ -21,6 +21,7 @@ #include "../libpicofe/lprintf.h" #include "../libpicofe/plat.h" #include "emu.h" +#include "configfile.h" #include "input_pico.h" #include "menu_pico.h" #include "config_file.h" @@ -60,13 +61,15 @@ int pico_pen_x = 320/2, pico_pen_y = 240/2; int pico_inp_mode; int flip_after_sync; int engineState = PGS_Menu; +int show_fps_bypass = 0; +int need_screen_cleared = 0; static short __attribute__((aligned(4))) sndBuffer[2*44100/50]; /* tmp buff to reduce stack usage for plats with small stack */ -static char static_buff[512]; +static char static_buff[1024]; const char *rom_fname_reload; -char rom_fname_loaded[512]; +char rom_fname_loaded[1024]; int reset_timing = 0; static unsigned int notice_msg_time; /* when started showing */ static char noticeMsg[40]; @@ -119,9 +122,13 @@ static void fname_ext(char *dst, int dstlen, const char *prefix, const char *ext *dst = 0; if (prefix) { - int len = plat_get_root_dir(dst, dstlen); + /*int len = plat_get_root_dir(dst, dstlen); strcpy(dst + len, prefix); - prefix_len = len + strlen(prefix); + prefix_len = len + strlen(prefix);*/ + + /* Saves are in ROM folder */ + prefix_len = strlen(mRomPath)+1; + sprintf(dst, "%s/", mRomPath); } p = fname + strlen(fname) - 1; @@ -349,7 +356,9 @@ static void system_announce(void) tv_standard = Pico.m.pal ? "PAL" : "NTSC"; fps = Pico.m.pal ? 50 : 60; - emu_status_msg("%s %s / %dFPS%s", tv_standard, sys_name, fps, extra); + //emu_status_msg("%s %s / %dFPS%s", tv_standard, sys_name, fps, extra); + printf("\nSystem Announce: %s, %s / %dFPS%s\n", sys_name, tv_standard, fps, extra); + printf("PicoIn.AHW = %d, Pico.m.hardware=%d\n", PicoIn.AHW, Pico.m.hardware); } static void do_region_override(const char *media_fname) @@ -498,6 +507,15 @@ int emu_reload_rom(const char *rom_fname_in) PicoIn.opt &= ~POPT_DIS_VDP_FIFO; } + /* Set input map */ + if (PicoIn.AHW & PAHW_SMS) { + printf("plat set sms input\n"); + plat_set_sms_input(); + } + else{ + plat_set_genesis_input(); + } + strncpy(rom_fname_loaded, rom_fname, sizeof(rom_fname_loaded)-1); rom_fname_loaded[sizeof(rom_fname_loaded)-1] = 0; @@ -873,6 +891,19 @@ int emu_check_save_file(int slot, int *time) return emu_get_save_fname(1, 0, slot, time) ? 1 : 0; } +int emu_save_load_game_from_file(int load, char *saveFname){ + int ret = PicoState(saveFname, !load); + if (!ret) { + //emu_status_msg(load ? "STATE LOADED" : "STATE SAVED"); + } else { + //emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED"); + ret = -1; + } + + return ret; +} + + int emu_save_load_game(int load, int sram) { int ret = 0; @@ -881,8 +912,8 @@ int emu_save_load_game(int load, int sram) // make save filename saveFname = emu_get_save_fname(load, sram, state_slot, NULL); if (saveFname == NULL) { - if (!sram) - emu_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED"); + /*if (!sram) + emu_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED");*/ return -1; } @@ -950,9 +981,9 @@ int emu_save_load_game(int load, int sram) #ifdef __GP2X__ if (!load) sync(); #endif - emu_status_msg(load ? "STATE LOADED" : "STATE SAVED"); + //emu_status_msg(load ? "STATE LOADED" : "STATE SAVED"); } else { - emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED"); + //emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED"); ret = -1; } @@ -1092,6 +1123,11 @@ static void do_turbo(unsigned short *pad, int acts) static void run_events_ui(unsigned int which) { + char shell_cmd[100]; + FILE *fp; + //emu_action_old = emu_action; + //printf("New event: %d\n", which); + if (which & (PEV_STATE_LOAD|PEV_STATE_SAVE)) { int do_it = 1; @@ -1136,6 +1172,155 @@ static void run_events_ui(unsigned int which) { plat_video_toggle_renderer(1, 0); } + if (which & PEV_VOL_DOWN) + { + printf("PEV_VOL_DOWN\r\n"); + /// ----- Compute new value ----- + volume_percentage = (volume_percentage < STEP_CHANGE_VOLUME)? + 0:(volume_percentage-STEP_CHANGE_VOLUME); + /// ----- HUD msg ------ + char txt[100]; + sprintf(txt, "VOLUME %d%%", volume_percentage); + plat_status_msg_busy_first(txt); + /// ----- Shell cmd ---- + sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage); + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + printf("Failed to run command %s\n", shell_cmd); + } + } + if (which & PEV_VOL_UP) + { + printf("PEV_VOL_UP\r\n"); + /// ----- Compute new value ----- + volume_percentage = (volume_percentage > 100 - STEP_CHANGE_VOLUME)? + 100:(volume_percentage+STEP_CHANGE_VOLUME); + /// ----- HUD msg ------ + char txt[100]; + sprintf(txt, "VOLUME %d%%", volume_percentage); + plat_status_msg_busy_first(txt); + /// ----- Shell cmd ---- + sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage); + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + printf("Failed to run command %s\n", shell_cmd); + } + } + if (which & PEV_BRIGHT_UP) + { + printf("PEV_BRIGHT_UP\r\n"); + /// ----- Compute new value ----- + brightness_percentage = (brightness_percentage > 100 - STEP_CHANGE_BRIGHTNESS)? + 100:(brightness_percentage+STEP_CHANGE_BRIGHTNESS); + /// ----- HUD msg ------ + char txt[100]; + sprintf(txt, "BRIGHTNESS %d%%", brightness_percentage); + plat_status_msg_busy_first(txt); + /// ----- Shell cmd ---- + sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage); + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + printf("Failed to run command %s\n", shell_cmd); + } + } + if (which & PEV_BRIGHT_DOWN) + { + printf("PEV_BRIGHT_DOWN\r\n"); + /// ----- Compute new value ----- + brightness_percentage = (brightness_percentage < STEP_CHANGE_BRIGHTNESS)? + 0:(brightness_percentage-STEP_CHANGE_BRIGHTNESS); + /// ----- HUD msg ------ + char txt[100]; + sprintf(txt, "BRIGHTNESS %d%%", brightness_percentage); + plat_status_msg_busy_first(txt); + /// ----- Shell cmd ---- + sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage); + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + printf("Failed to run command %s\n", shell_cmd); + } + } + if (which & PEV_AR_FACT_UP) + { + printf("PEV_AR_FACT_UP\r\n"); + /// ----- Compute new value ----- + if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){ + aspect_ratio_factor_percent = (aspect_ratio_factor_percent+aspect_ratio_factor_step<100)? + aspect_ratio_factor_percent+aspect_ratio_factor_step:100; + need_screen_cleared = 1; + } + else{ + aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL; + } + aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL; + /// ----- HUD msg ------ + /*char txt[100]; + sprintf(txt, " DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent); + plat_status_msg_busy_first(txt);*/ + sprintf(shell_cmd, "%s %d \" DISPLAY MODE: ZOOMED %d%%%%\"", + SHELL_CMD_NOTIF, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent); + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + printf("Failed to run command %s\n", shell_cmd); + } + + // Save config file + configfile_save(cfg_file_rom); + } + if (which & PEV_AR_FACT_DOWN) + { + printf("PEV_AR_FACT_DOWN\r\n"); + /// ----- Compute new value ----- + if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){ + aspect_ratio_factor_percent = (aspect_ratio_factor_percent>aspect_ratio_factor_step)? + aspect_ratio_factor_percent-aspect_ratio_factor_step:0; + need_screen_cleared = 1; + } + else{ + aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL; + } + aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL; + /// ----- HUD msg ------ + /*char txt[100]; + sprintf(txt, " DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent); + plat_status_msg_busy_first(txt);*/ + sprintf(shell_cmd, "%s %d \" DISPLAY MODE: ZOOMED %d%%%%\"", + SHELL_CMD_NOTIF, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent); + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + printf("Failed to run command %s\n", shell_cmd); + + // Save config file + configfile_save(cfg_file_rom); + } + + } + if (which & PEV_DISPMODE) + { + printf("PEV_DISPMODE\r\n"); + /// ----- Compute new value ----- + aspect_ratio = (aspect_ratio+1)%NB_ASPECT_RATIOS_TYPES; + /// ----- HUD msg ------ + //char txt[100]; + if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){ + //sprintf(txt, " DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent); + sprintf(shell_cmd, "%s %d \" DISPLAY MODE: ZOOMED %d%%%%\"", + SHELL_CMD_NOTIF, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent); + } + else{ + //sprintf(txt, "DISPLAY MODE: %s", aspect_ratio_name[aspect_ratio]); + sprintf(shell_cmd, "%s %d \" DISPLAY MODE: %s\"", + SHELL_CMD_NOTIF, NOTIF_SECONDS_DISP, aspect_ratio_name[aspect_ratio]); + } + //plat_status_msg_busy_first(txt); + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + printf("Failed to run command %s\n", shell_cmd); + } + + // Save config file + configfile_save(cfg_file_rom); + } if (which & (PEV_SSLOT_PREV|PEV_SSLOT_NEXT)) { if (which & PEV_SSLOT_PREV) { @@ -1191,10 +1376,13 @@ void emu_update_input(void) events &= ~prev_events; + /* SMS */ if (PicoIn.AHW == PAHW_PICO) run_events_pico(events); + if (events) run_events_ui(events); + if (movie_data) update_movie(); @@ -1236,6 +1424,39 @@ void emu_cmn_forced_frame(int no_scale, int do_emu, void *buf) PicoIn.opt = po_old; } + + + +/* Quick save and turn off the console */ +void quick_save_and_poweroff() +{ + printf("Save Instant Play file\n"); + + /* Send command to cancel any previously scheduled powerdown */ + if (popen(SHELL_CMD_CANCEL_SCHED_POWERDOWN, "r") == NULL) + { + /* Countdown is still ticking, so better do nothing + than start writing and get interrupted! + */ + printf("Failed to cancel scheduled shutdown\n"); + exit(0); + } + + /* Save */ + emu_save_load_game_from_file(0, quick_save_file); + + /* Perform Instant Play save and shutdown */ + execlp(SHELL_CMD_INSTANT_PLAY, SHELL_CMD_INSTANT_PLAY, + prog_name, "-loadStateFile", quick_save_file, mRomName, NULL); + + /* Should not be reached */ + printf("Failed to perform Instant Play save and shutdown\n"); + + /* Exit Emulator */ + exit(0); +} + + void emu_init(void) { char path[512]; @@ -1309,6 +1530,8 @@ void emu_sound_start(void) if (currentConfig.EmuOpt & EOPT_EN_SOUND) { int is_stereo = (PicoIn.opt & POPT_EN_STEREO) ? 1 : 0; + /// Hard Bypass Stereo to mono + //is_stereo = 0; PsndRerate(Pico.m.frame_count ? 1 : 0); @@ -1444,8 +1667,10 @@ void emu_loop(void) sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2); printf("%s\n", fpsbuff); #else - if (currentConfig.EmuOpt & EOPT_SHOW_FPS) + if (currentConfig.EmuOpt & EOPT_SHOW_FPS || show_fps_bypass){ + printf("%02i/%02i \n", frames_shown, frames_done); snprintf(fpsbuff, 8, "%02i/%02i ", frames_shown, frames_done); + } #endif frames_shown = frames_done = 0; timestamp_fps_x3 += ms_to_ticks(1000) * 3; @@ -1486,6 +1711,12 @@ void emu_loop(void) diff = timestamp_aim_x3 - timestamp_x3; } + /* Quick save and poweroff */ + if(mQuickSaveAndPoweroff){ + quick_save_and_poweroff(); + mQuickSaveAndPoweroff = 0; + } + emu_update_input(); if (skip) { int do_audio = diff > -target_frametime_x3 * 2; diff --git a/platform/common/emu.h b/platform/common/emu.h index a2110026..f81ad157 100644 --- a/platform/common/emu.h +++ b/platform/common/emu.h @@ -87,6 +87,15 @@ extern int config_slot, config_slot_current; extern unsigned char *movie_data; extern int reset_timing; extern int flip_after_sync; +extern int show_fps_bypass; +extern int need_screen_cleared; +extern int mQuickSaveAndPoweroff; + +extern char *prog_name; +extern char *mRomName; +extern char *mRomPath; +extern char *quick_save_file; +extern char *cfg_file_rom; #define PICO_PEN_ADJUST_X 4 #define PICO_PEN_ADJUST_Y 2 @@ -94,7 +103,7 @@ extern int pico_pen_x, pico_pen_y; extern int pico_inp_mode; extern const char *rom_fname_reload; // ROM to try loading on next PGS_ReloadRom -extern char rom_fname_loaded[512]; // currently loaded ROM filename +extern char rom_fname_loaded[1024]; // currently loaded ROM filename // engine states extern int engineState; @@ -118,6 +127,7 @@ void emu_loop(void); int emu_reload_rom(const char *rom_fname_in); int emu_swap_cd(const char *fname); int emu_save_load_game(int load, int sram); +int emu_save_load_game_from_file(int load, char *saveFname); void emu_reset_game(void); void emu_prep_defconfig(void); @@ -165,6 +175,8 @@ void pemu_sound_start(void); void plat_early_init(void); void plat_init(void); void plat_finish(void); +void plat_set_sms_input(void); +void plat_set_genesis_input(void); /* used before things blocking for a while (these funcs redraw on return) */ void plat_status_msg_busy_first(const char *msg); diff --git a/platform/common/input_pico.h b/platform/common/input_pico.h index c0501d33..5337f55e 100644 --- a/platform/common/input_pico.h +++ b/platform/common/input_pico.h @@ -16,34 +16,45 @@ #define GBTN_MODE 11 /* ui events */ -#define PEVB_VOL_DOWN 30 -#define PEVB_VOL_UP 29 -#define PEVB_STATE_LOAD 28 -#define PEVB_STATE_SAVE 27 -#define PEVB_SWITCH_RND 26 -#define PEVB_SSLOT_PREV 25 -#define PEVB_SSLOT_NEXT 24 -#define PEVB_MENU 23 -#define PEVB_FF 22 -#define PEVB_PICO_PNEXT 21 -#define PEVB_PICO_PPREV 20 -#define PEVB_PICO_SWINP 19 -#define PEVB_RESET 18 +#define PEVB_VOL_DOWN 30 +#define PEVB_VOL_UP 29 +#define PEVB_STATE_LOAD 28 +#define PEVB_STATE_SAVE 27 +#define PEVB_SWITCH_RND 26 +#define PEVB_SSLOT_PREV 25 +#define PEVB_SSLOT_NEXT 24 +#define PEVB_MENU 23 +#define PEVB_FF 22 +#define PEVB_PICO_PNEXT 21 +#define PEVB_PICO_PPREV 20 +#define PEVB_PICO_SWINP 19 +#define PEVB_RESET 18 +#define PEVB_BRIGHT_UP 17 +#define PEVB_BRIGHT_DOWN 16 +#define PEVB_AR_FACT_UP 15 +#define PEVB_AR_FACT_DOWN 14 +#define PEVB_DISPMODE 13 -#define PEV_VOL_DOWN (1 << PEVB_VOL_DOWN) -#define PEV_VOL_UP (1 << PEVB_VOL_UP) -#define PEV_STATE_LOAD (1 << PEVB_STATE_LOAD) -#define PEV_STATE_SAVE (1 << PEVB_STATE_SAVE) -#define PEV_SWITCH_RND (1 << PEVB_SWITCH_RND) -#define PEV_SSLOT_PREV (1 << PEVB_SSLOT_PREV) -#define PEV_SSLOT_NEXT (1 << PEVB_SSLOT_NEXT) -#define PEV_MENU (1 << PEVB_MENU) -#define PEV_FF (1 << PEVB_FF) -#define PEV_PICO_PNEXT (1 << PEVB_PICO_PNEXT) -#define PEV_PICO_PPREV (1 << PEVB_PICO_PPREV) -#define PEV_PICO_SWINP (1 << PEVB_PICO_SWINP) -#define PEV_RESET (1 << PEVB_RESET) +#define PEV_VOL_DOWN (1 << PEVB_VOL_DOWN) +#define PEV_VOL_UP (1 << PEVB_VOL_UP) +#define PEV_STATE_LOAD (1 << PEVB_STATE_LOAD) +#define PEV_STATE_SAVE (1 << PEVB_STATE_SAVE) +#define PEV_SWITCH_RND (1 << PEVB_SWITCH_RND) +#define PEV_SSLOT_PREV (1 << PEVB_SSLOT_PREV) +#define PEV_SSLOT_NEXT (1 << PEVB_SSLOT_NEXT) +#define PEV_MENU (1 << PEVB_MENU) +#define PEV_FF (1 << PEVB_FF) +#define PEV_PICO_PNEXT (1 << PEVB_PICO_PNEXT) +#define PEV_PICO_PPREV (1 << PEVB_PICO_PPREV) +#define PEV_PICO_SWINP (1 << PEVB_PICO_SWINP) +#define PEV_RESET (1 << PEVB_RESET) +#define PEV_BRIGHT_UP (1 << PEVB_BRIGHT_UP) +#define PEV_BRIGHT_DOWN (1 << PEVB_BRIGHT_DOWN) +#define PEV_AR_FACT_UP (1 << PEVB_AR_FACT_UP) +#define PEV_AR_FACT_DOWN (1 << PEVB_AR_FACT_DOWN) +#define PEV_DISPMODE (1 << PEVB_DISPMODE) -#define PEV_MASK 0x7ffc0000 +//#define PEV_MASK 0x7ffc0000 +#define PEV_MASK 0x7fffe000 #endif /* INCLUDE_c48097f3ff2a6a9af1cce8fd7a9b3f0c */ diff --git a/platform/common/inputmap_kbd.c b/platform/common/inputmap_kbd.c index 0900fb1a..a8066b99 100644 --- a/platform/common/inputmap_kbd.c +++ b/platform/common/inputmap_kbd.c @@ -6,7 +6,7 @@ #include "../common/input_pico.h" #include "../common/plat_sdl.h" -const struct in_default_bind in_sdl_defbinds[] = { +/*const struct in_default_bind in_sdl_defbinds[] = { { SDLK_UP, IN_BINDTYPE_PLAYER12, GBTN_UP }, { SDLK_DOWN, IN_BINDTYPE_PLAYER12, GBTN_DOWN }, { SDLK_LEFT, IN_BINDTYPE_PLAYER12, GBTN_LEFT }, @@ -31,6 +31,80 @@ const struct in_default_bind in_sdl_defbinds[] = { { SDLK_F8, IN_BINDTYPE_EMU, PEVB_PICO_SWINP }, { SDLK_BACKSPACE, IN_BINDTYPE_EMU, PEVB_FF }, { 0, 0, 0 } +};*/ + +const struct in_default_bind in_sdl_defbinds[] = { + { SDLK_u, IN_BINDTYPE_PLAYER12, GBTN_UP }, + { SDLK_d, IN_BINDTYPE_PLAYER12, GBTN_DOWN }, + { SDLK_l, IN_BINDTYPE_PLAYER12, GBTN_LEFT }, + { SDLK_r, IN_BINDTYPE_PLAYER12, GBTN_RIGHT }, + { SDLK_y, IN_BINDTYPE_PLAYER12, GBTN_A }, + { SDLK_b, IN_BINDTYPE_PLAYER12, GBTN_B }, + { SDLK_a, IN_BINDTYPE_PLAYER12, GBTN_C }, + { SDLK_m, IN_BINDTYPE_PLAYER12, GBTN_X }, + { SDLK_x, IN_BINDTYPE_PLAYER12, GBTN_Y }, + { SDLK_n, IN_BINDTYPE_PLAYER12, GBTN_Z }, + { SDLK_s, IN_BINDTYPE_PLAYER12, GBTN_START }, + { SDLK_k, IN_BINDTYPE_PLAYER12, GBTN_MODE }, + { SDLK_q, IN_BINDTYPE_EMU, PEVB_MENU }, + { SDLK_TAB, IN_BINDTYPE_EMU, PEVB_RESET }, + //{ SDLK_p, IN_BINDTYPE_EMU, PEVB_STATE_SAVE }, + { SDLK_F1, IN_BINDTYPE_EMU, PEVB_STATE_SAVE }, + { SDLK_F2, IN_BINDTYPE_EMU, PEVB_STATE_LOAD }, + + { SDLK_e, IN_BINDTYPE_EMU, PEVB_VOL_DOWN }, + { SDLK_c, IN_BINDTYPE_EMU, PEVB_VOL_UP }, + { SDLK_w, IN_BINDTYPE_EMU, PEVB_BRIGHT_DOWN }, + { SDLK_g, IN_BINDTYPE_EMU, PEVB_BRIGHT_UP }, + { SDLK_j, IN_BINDTYPE_EMU, PEVB_AR_FACT_DOWN }, + { SDLK_i, IN_BINDTYPE_EMU, PEVB_AR_FACT_UP }, + { SDLK_h, IN_BINDTYPE_EMU, PEVB_DISPMODE }, + + { SDLK_F3, IN_BINDTYPE_EMU, PEVB_SSLOT_PREV }, + { SDLK_F4, IN_BINDTYPE_EMU, PEVB_SSLOT_NEXT }, + { SDLK_F5, IN_BINDTYPE_EMU, PEVB_SWITCH_RND }, + { SDLK_F6, IN_BINDTYPE_EMU, PEVB_PICO_PPREV }, + { SDLK_F7, IN_BINDTYPE_EMU, PEVB_PICO_PNEXT }, + { SDLK_F8, IN_BINDTYPE_EMU, PEVB_PICO_SWINP }, + { SDLK_BACKSPACE, IN_BINDTYPE_EMU, PEVB_FF }, + { 0, 0, 0 } +}; + +const struct in_default_bind in_sdl_defbinds_SMS[] = { + { SDLK_u, IN_BINDTYPE_PLAYER12, GBTN_UP }, + { SDLK_d, IN_BINDTYPE_PLAYER12, GBTN_DOWN }, + { SDLK_l, IN_BINDTYPE_PLAYER12, GBTN_LEFT }, + { SDLK_r, IN_BINDTYPE_PLAYER12, GBTN_RIGHT }, + { SDLK_y, IN_BINDTYPE_PLAYER12, GBTN_C }, + { SDLK_a, IN_BINDTYPE_PLAYER12, GBTN_C }, + { SDLK_b, IN_BINDTYPE_PLAYER12, GBTN_B }, + { SDLK_x, IN_BINDTYPE_PLAYER12, GBTN_B }, + { SDLK_m, IN_BINDTYPE_PLAYER12, GBTN_X }, + { SDLK_n, IN_BINDTYPE_PLAYER12, GBTN_Z }, + { SDLK_s, IN_BINDTYPE_PLAYER12, GBTN_START }, + { SDLK_k, IN_BINDTYPE_PLAYER12, GBTN_MODE }, + { SDLK_q, IN_BINDTYPE_EMU, PEVB_MENU }, + { SDLK_TAB, IN_BINDTYPE_EMU, PEVB_RESET }, + //{ SDLK_p, IN_BINDTYPE_EMU, PEVB_STATE_SAVE }, + { SDLK_F1, IN_BINDTYPE_EMU, PEVB_STATE_SAVE }, + { SDLK_F2, IN_BINDTYPE_EMU, PEVB_STATE_LOAD }, + + { SDLK_e, IN_BINDTYPE_EMU, PEVB_VOL_DOWN }, + { SDLK_c, IN_BINDTYPE_EMU, PEVB_VOL_UP }, + { SDLK_w, IN_BINDTYPE_EMU, PEVB_BRIGHT_DOWN }, + { SDLK_g, IN_BINDTYPE_EMU, PEVB_BRIGHT_UP }, + { SDLK_j, IN_BINDTYPE_EMU, PEVB_AR_FACT_DOWN }, + { SDLK_i, IN_BINDTYPE_EMU, PEVB_AR_FACT_UP }, + { SDLK_h, IN_BINDTYPE_EMU, PEVB_DISPMODE }, + + { SDLK_F3, IN_BINDTYPE_EMU, PEVB_SSLOT_PREV }, + { SDLK_F4, IN_BINDTYPE_EMU, PEVB_SSLOT_NEXT }, + { SDLK_F5, IN_BINDTYPE_EMU, PEVB_SWITCH_RND }, + { SDLK_F6, IN_BINDTYPE_EMU, PEVB_PICO_PPREV }, + { SDLK_F7, IN_BINDTYPE_EMU, PEVB_PICO_PNEXT }, + { SDLK_F8, IN_BINDTYPE_EMU, PEVB_PICO_SWINP }, + { SDLK_BACKSPACE, IN_BINDTYPE_EMU, PEVB_FF }, + { 0, 0, 0 } }; const struct menu_keymap in_sdl_key_map[] = { diff --git a/platform/common/main.c b/platform/common/main.c index a7a8312c..c6304bf9 100644 --- a/platform/common/main.c +++ b/platform/common/main.c @@ -8,8 +8,10 @@ #include #include +#include #include #include +#include #ifdef USE_SDL #include #endif @@ -18,6 +20,7 @@ #include "../libpicofe/plat.h" #include "menu_pico.h" #include "emu.h" +#include "configfile.h" #include "version.h" #include @@ -25,8 +28,43 @@ #include "file_stream_transforms.h" #endif -static int load_state_slot = -1; char **g_argv; +char *prog_name; +static char *load_state_file = NULL; +static int load_state_slot = -1; +static char *quick_save_file_extension = "quicksave"; +char *mRomName = NULL; +char *mRomPath = NULL; +char *quick_save_file = NULL; +char *cfg_file_default = NULL; +char *cfg_file_rom = NULL; +static char *cfg_file_default_name = "default_config"; +static char *cfg_file_extension = "cfg"; +int mQuickSaveAndPoweroff=0; + + +void usage(){ + printf("\n\n\nPicoDrive v" VERSION " (c) notaz, 2006-2009,2013\n"); + printf("usage: PicoDriveBin [options] [romfile]\n"); + printf("options:\n" + " -config use specified config file instead of default 'config.cfg'\n" + " -fps use to show fps\n" + " -loadStateSlot if ROM is specified, try loading savestate slot \n" + " -loadStateFile if ROM is specified, try loading savestate file \n"); + exit(1); +} + +/* Handler for SIGUSR1, caused by closing the console */ +void handle_sigusr1(int sig) +{ + //printf("Caught signal USR1 %d\n", sig); + + /* Exit menu if it was launched */ + stop_menu_loop = 1; + + /* Signal to quick save and poweoff after next loop */ + mQuickSaveAndPoweroff = 1; +} void parse_cmd_line(int argc, char *argv[]) { @@ -39,11 +77,19 @@ void parse_cmd_line(int argc, char *argv[]) if (strcasecmp(argv[x], "-config") == 0) { if (x+1 < argc) { ++x; PicoConfigFile = argv[x]; } } - else if (strcasecmp(argv[x], "-loadstate") == 0 + else if (strcasecmp(argv[x], "-loadStateSlot") == 0 || strcasecmp(argv[x], "-load") == 0) { if (x+1 < argc) { ++x; load_state_slot = atoi(argv[x]); } } + else if (strcasecmp(argv[x], "-loadStateFile") == 0) + { + if (x+1 < argc) { ++x; load_state_file = argv[x]; } + } + else if (strcasecmp(argv[x], "-fps") == 0) { + currentConfig.EmuOpt |= EOPT_SHOW_FPS; + show_fps_bypass = 1; + } else if (strcasecmp(argv[x], "-pdb") == 0) { if (x+1 < argc) { ++x; pdb_command(argv[x]); } } @@ -54,26 +100,62 @@ void parse_cmd_line(int argc, char *argv[]) unrecognized = 1; break; } - } else { + } + /* Check if file exists, Save ROM name, and ROM path */ + else { + mRomName = argv[x]; FILE *f = fopen(argv[x], "rb"); if (f) { + /* Save Rom path */ + mRomPath = (char *)malloc(strlen(mRomName)+1); + strcpy(mRomPath, mRomName); + char *slash = strrchr ((char*)mRomPath, '/'); + *slash = 0; + + /* Rom name without extension */ + char *point = strrchr ((char*)slash+1, '.'); + *point = 0; + + /* Set quicksave filename */ + quick_save_file = (char *)malloc(strlen(mRomPath) + strlen(slash+1) + + strlen(quick_save_file_extension) + 2 + 1); + sprintf(quick_save_file, "%s/%s.%s", + mRomPath, slash+1, quick_save_file_extension); + printf("Quick_save_file: %s\n", quick_save_file); + + /* Set rom cfg filepath */ + cfg_file_rom = (char *)malloc(strlen(mRomPath) + strlen(slash+1) + + strlen(cfg_file_extension) + 2 + 1); + sprintf(cfg_file_rom, "%s/%s.%s", + mRomPath, slash+1, cfg_file_extension); + printf("cfg_file_rom: %s\n", cfg_file_rom); + + /* Set console cfg filepath */ + cfg_file_default = (char *)malloc(strlen(mRomPath) + strlen(cfg_file_default_name) + + strlen(cfg_file_extension) + 2 + 1); + sprintf(cfg_file_default, "%s/%s.%s", + mRomPath, cfg_file_default_name, cfg_file_extension); + printf("cfg_file_default: %s\n", cfg_file_default); + + /** Load config files */ + configfile_load(cfg_file_default); + configfile_load(cfg_file_rom); + + /* Close file*/ fclose(f); rom_fname_reload = argv[x]; engineState = PGS_ReloadRom; } - else + else{ + printf("Rom %s not found \n", mRomName); unrecognized = 1; + } break; } } if (unrecognized) { - printf("\n\n\nPicoDrive v" VERSION " (c) notaz, 2006-2009,2013\n"); - printf("usage: %s [options] [romfile]\n", argv[0]); - printf("options:\n" - " -config use specified config file instead of default 'config.cfg'\n" - " -loadstate if ROM is specified, try loading savestate slot \n"); - exit(1); + usage(); } } @@ -82,6 +164,26 @@ int main(int argc, char *argv[]) { g_argv = argv; + /* Save program name */ + prog_name = argv[0]; + + /* Engine initial state */ + engineState = PGS_Menu; + + /* Parse arguments */ + if (argc > 1){ + parse_cmd_line(argc, argv); + } + else{ + usage(); + } + + /* Init Signals */ + signal(SIGUSR1, handle_sigusr1); + + /* Set env var for no mouse */ + putenv(strdup("SDL_NOMOUSE=1")); + plat_early_init(); in_init(); @@ -89,25 +191,54 @@ int main(int argc, char *argv[]) plat_target_init(); plat_init(); - menu_init(); + //menu_init(); emu_prep_defconfig(); // depends on input emu_read_config(NULL, 0); emu_init(); - - engineState = PGS_Menu; - - if (argc > 1) - parse_cmd_line(argc, argv); + menu_init(); if (engineState == PGS_ReloadRom) { if (emu_reload_rom(rom_fname_reload)) { engineState = PGS_Running; - if (load_state_slot >= 0) { - state_slot = load_state_slot; + + /* Load slot */ + if(load_state_slot != -1){ + printf("LOADING FROM SLOT %d...\n", load_state_slot+1); + char fname[1024]; emu_save_load_game(1, 0); + printf("LOADED FROM SLOT %d\n", load_state_slot+1); + load_state_slot = -1; + } + /* Load file */ + else if(load_state_file != NULL){ + printf("LOADING FROM FILE %s...\n", load_state_file); + emu_save_load_game_from_file(1, load_state_file); + printf("LOADED FROM SLOT %s\n", load_state_file); + load_state_file = NULL; + } + /* Load quick save file */ + else if(access( quick_save_file, F_OK ) != -1){ + printf("Found quick save file: %s\n", quick_save_file); + + int resume = launch_resume_menu_loop(); + if(resume == RESUME_YES){ + printf("Resume game from quick save file: %s\n", quick_save_file); + emu_save_load_game_from_file(1, quick_save_file); + } + else{ + printf("Reset game\n"); + + /* Remove quicksave file if present */ + if (remove(quick_save_file) == 0){ + printf("Deleted successfully: %s\n", quick_save_file); + } + else{ + printf("Unable to delete the file: %s\n", quick_save_file); + } + } } } } @@ -117,7 +248,8 @@ int main(int argc, char *argv[]) switch (engineState) { case PGS_Menu: - menu_loop(); + //menu_loop(); + menu_loop_funkey(); break; case PGS_TrayMenu: diff --git a/platform/common/menu_pico.c b/platform/common/menu_pico.c index f1cc9fc5..0f8f0db1 100644 --- a/platform/common/menu_pico.c +++ b/platform/common/menu_pico.c @@ -8,10 +8,16 @@ #include #include #include +#include +#include +#include +#include #include "emu.h" +#include "configfile.h" #include "menu_pico.h" #include "input_pico.h" +#include "../libpicofe/input.h" #include "version.h" #include @@ -81,6 +87,1292 @@ static const char *men_dummy[] = { NULL }; #define MENU_OPTIONS_GFX #define MENU_OPTIONS_ADV #endif + + + + + + + + + + + +/// -------------- DEFINES -------------- +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define ABS(x) (((x) < 0) ? (-x) : (x)) + +//#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 RES_HW_SCREEN_HORIZONTAL 240 +#define RES_HW_SCREEN_VERTICAL 240 +#define SCREEN_HORIZONTAL_SIZE 240 +#define SCREEN_VERTICAL_SIZE 240 + +#define SCROLL_SPEED_PX 30 +#define FPS_MENU 50 +#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 -------------- +extern SDL_Surface * hw_screen; +extern SDL_Surface * virtual_hw_screen; +SDL_Surface * draw_screen; + +static int backup_key_repeat_delay, backup_key_repeat_interval; +static SDL_Surface * backup_hw_screen = NULL; + +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; +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; +static uint16_t x_volume_bar = 0; +static uint16_t y_volume_bar = 0; +static uint16_t x_brightness_bar = 0; +static uint16_t y_brightness_bar = 0; + +int volume_percentage = 0; +int brightness_percentage = 0; + +static int quick_load_slot_chosen = 0; + +#undef X +#define X(a, b) b, +const char *resume_options_str[] = {RESUME_OPTIONS}; + + +/// -------------- FUNCTIONS IMPLEMENTATION -------------- +void init_menu_SDL(){ + /// ----- 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()); + } + + /// ----- Copy draw_screen at init ------ + backup_hw_screen = SDL_CreateRGBSurface(SDL_SWSURFACE, + virtual_hw_screen->w, virtual_hw_screen->h, 16, 0, 0, 0, 0); + if(backup_hw_screen == NULL){ + MENU_ERROR_PRINTF("ERROR in init_menu_SDL: Could not create backup_hw_screen: %s\n", SDL_GetError()); + } + + draw_screen = SDL_CreateRGBSurface(SDL_SWSURFACE, + virtual_hw_screen->w, virtual_hw_screen->h, 16, 0, 0, 0, 0); + if(draw_screen == NULL){ + MENU_ERROR_PRINTF("ERROR in init_menu_SDL: Could not create draw_screen: %s\n", 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()); + } + + /// ------ Init menu zones ------ + init_menu_zones(); +} + +void deinit_menu_SDL(){ + /// ------ 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++){ + SDL_FreeSurface(menu_zone_surfaces[i]); + } + SDL_FreeSurface(backup_hw_screen); + SDL_FreeSurface(draw_screen); + + 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; +} + + +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)); + } + + +} + + +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 = malloc(nb_menu_zones*sizeof(int)); + menu_zone_surfaces = malloc(nb_menu_zones*sizeof(SDL_Surface*)); + } + else{ + int *temp = realloc(idx_menus, nb_menu_zones*sizeof(int)); + idx_menus = temp; + menu_zone_surfaces = 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){ + 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; + 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; + 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; + 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; + 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; + 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; + 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; + default: + MENU_DEBUG_PRINTF("Warning - In add_menu_zone, unknown MENU_TYPE: %d\n", menu_type); + break; + } + + /// ------ Free Surfaces ------- + SDL_FreeSurface(text_surface); +} + +void init_menu_zones(){ + /// Init Volume Menu + add_menu_zone(MENU_TYPE_VOLUME); + /// Init Brightness Menu + add_menu_zone(MENU_TYPE_BRIGHTNESS); + /// Init Save Menu + add_menu_zone(MENU_TYPE_SAVE); + /// Init Load Menu + add_menu_zone(MENU_TYPE_LOAD); + /// Init Aspect Ratio Menu + add_menu_zone(MENU_TYPE_ASPECT_RATIO); + /// Init Exit Menu + add_menu_zone(MENU_TYPE_EXIT); + /// Init Powerdown Menu + //add_menu_zone(MENU_TYPE_POWERDOWN); +} + + +void init_menu_system_values(){ + FILE *fp; + char res[100]; + + /// ------- 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); + + /// 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); + } + } + + /// ------- 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); + + /// 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); + } + } + + /// ------ 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()); + } + + /// Get save slot from game + state_slot = (state_slot%MAX_SAVE_SLOTS); // security +} + +void menu_screen_refresh(int menuItem, int prevItem, int scroll, uint8_t menu_confirmation, uint8_t menu_action){ + /// --------- Vars --------- + int print_arrows = (scroll==0)?1:0; + + /// --------- Clear HW screen ---------- + //SDL_FillRect(draw_screen, NULL, SDL_MapRGB(draw_screen->format, 255, 0, 0)); + if(SDL_BlitSurface(backup_hw_screen, NULL, draw_screen, NULL)){ + MENU_ERROR_PRINTF("ERROR Could not Clear draw_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, draw_screen, NULL)){ + MENU_ERROR_PRINTF("ERROR Could not Blit surface on draw_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, draw_screen, &menu_blit_window)){ + MENU_ERROR_PRINTF("ERROR Could not Blit surface on draw_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, draw_screen, NULL)){ + MENU_ERROR_PRINTF("ERROR Could not Blit surface on draw_screen: %s\n", SDL_GetError()); + } + } + /// --------- No Scroll ? Blitting menu-specific info + else{ + SDL_Surface * text_surface = NULL; + char text_tmp[40]; + SDL_Rect text_pos; + /*char fname[MAXPATHLEN]; + memset(fname, 0, MAXPATHLEN);*/ + char *fname = NULL; + uint16_t limit_filename_size = 24; + + switch(idx_menus[menuItem]){ + case MENU_TYPE_VOLUME: + draw_progress_bar(draw_screen, x_volume_bar, y_volume_bar, + width_progress_bar, height_progress_bar, volume_percentage, 100/STEP_CHANGE_VOLUME); + break; + + case MENU_TYPE_BRIGHTNESS: + draw_progress_bar(draw_screen, x_volume_bar, y_volume_bar, + width_progress_bar, height_progress_bar, brightness_percentage, 100/STEP_CHANGE_BRIGHTNESS); + break; + + 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 = (draw_screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = draw_screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, draw_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{ + fname = emu_get_save_fname(1, 0, state_slot, NULL); + if (fname == NULL) { + text_surface = TTF_RenderText_Blended(menu_info_font, "Free", text_color); + } + else{ + printf("Found saved file: %s\n", fname); + char *p = strrchr (fname, '/'); + char *basename = p ? p + 1 : (char *) fname; + if(strlen(basename) > limit_filename_size){basename[limit_filename_size]=0;} //limiting size + text_surface = TTF_RenderText_Blended(menu_small_info_font, basename, text_color); + } + } + } + text_pos.x = (draw_screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = draw_screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, draw_screen, &text_pos); + break; + + case MENU_TYPE_LOAD: + /// ---- Write slot ----- + if(quick_load_slot_chosen){ + sprintf(text_tmp, "FROM AUTO SAVE"); + } + else{ + sprintf(text_tmp, "FROM SLOT < %d >", state_slot+1); + } + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (draw_screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = draw_screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, draw_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(quick_load_slot_chosen){ + text_surface = TTF_RenderText_Blended(menu_info_font, " ", text_color); + } + else{ + fname = emu_get_save_fname(1, 0, state_slot, NULL); + if (fname == NULL) { + text_surface = TTF_RenderText_Blended(menu_info_font, "Free", text_color); + } + else{ + printf("Found saved file: %s\n", fname); + char *p = strrchr (fname, '/'); + char *basename = p ? p + 1 : (char *) fname; + if(strlen(basename) > limit_filename_size){basename[limit_filename_size]=0;} //limiting size + text_surface = TTF_RenderText_Blended(menu_small_info_font, basename, text_color); + } + } + } + } + text_pos.x = (draw_screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = draw_screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, draw_screen, &text_pos); + break; + + case MENU_TYPE_ASPECT_RATIO: + sprintf(text_tmp, "< %s >", aspect_ratio_name[aspect_ratio]); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (draw_screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = draw_screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, draw_screen, &text_pos); + break; + + case MENU_TYPE_EXIT: + case MENU_TYPE_POWERDOWN: + if(menu_confirmation){ + sprintf(text_tmp, "Are you sure ?"); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (draw_screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = draw_screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, draw_screen, &text_pos); + } + break; + 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 = (draw_screen->w - img_arrow_top->w)/2; + pos_arrow_top.y = (draw_screen->h - MENU_BG_SQUARE_HEIGHT)/4 - img_arrow_top->h/2; + SDL_BlitSurface(img_arrow_top, NULL, draw_screen, &pos_arrow_top); + + /// Bottom arrow + SDL_Rect pos_arrow_bottom; + pos_arrow_bottom.x = (draw_screen->w - img_arrow_bottom->w)/2; + pos_arrow_bottom.y = draw_screen->h - + (draw_screen->h - MENU_BG_SQUARE_HEIGHT)/4 - img_arrow_bottom->h/2; + SDL_BlitSurface(img_arrow_bottom, NULL, draw_screen, &pos_arrow_bottom); + } + + // Rotate + //SDL_Rotate_270(hw_screen, virtual_hw_screen); + SDL_BlitSurface(draw_screen, NULL, hw_screen, NULL); + + /// --- Real Flip --- + SDL_Flip(hw_screen); +} + + +void run_menu_loop() +{ + MENU_DEBUG_PRINTF("Launch 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]; + FILE *fp; + uint8_t menu_confirmation = 0; + stop_menu_loop = 0; + char fname[MAXPATHLEN]; + + /// ------ Get init values ------- + init_menu_system_values(); + int prevItem=menuItem; + + /// ------ Copy currently displayed screen ------- + /* if(SDL_BlitSurface(virtual_hw_screen, NULL, backup_hw_screen, NULL)){ + MENU_ERROR_PRINTF("ERROR Could not copy draw_screen: %s\n", SDL_GetError()); + }*/ + + memcpy(backup_hw_screen->pixels, (uint16_t*) virtual_hw_screen->pixels, + RES_HW_SCREEN_HORIZONTAL * RES_HW_SCREEN_VERTICAL * sizeof(uint16_t)); + + /* Stop Ampli */ + popen(SHELL_CMD_TURN_AMPLI_OFF, "r"); + + /// ------ Wait for menu UP key event ------ + int actions[IN_BINDTYPE_COUNT] = { 0, }; + while(event.type != SDL_KEYUP || event.key.keysym.sym != SDLK_q){ + while (SDL_PollEvent(&event)){ + SDL_PushEvent(&event); + in_update(actions); + } + + /* 500ms timeout */ + if(SDL_GetTicks() - cur_ms > 500){ + MENU_ERROR_PRINTF("Timeout waiting for SDLK_q UP\n"); + break; + } + } + + /// -------- Main loop --------- + while (!stop_menu_loop) + { + /// -------- Handle Keyboard Events --------- + if(!scroll){ + while (SDL_PollEvent(&event)) + switch(event.type) + { + case SDL_QUIT: + engineState = PGS_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_d: + case SDLK_DOWN: + MENU_DEBUG_PRINTF("DOWN\n"); + /// ------ Start scrolling to new menu ------- + menuItem++; + if (menuItem>=nb_menu_zones) menuItem=0; + 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"); + /// ------ Start scrolling to new menu ------- + menuItem--; + if (menuItem<0) menuItem=nb_menu_zones-1; + 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"); + 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); + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + MENU_ERROR_PRINTF("Failed to run command %s\n", shell_cmd); + } + + /// ------ Refresh screen ------ + screen_refresh = 1; + } + else 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); + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + MENU_ERROR_PRINTF("Failed to run command %s\n", shell_cmd); + } + /// ------ Refresh screen ------ + screen_refresh = 1; + } + else 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 if(idx_menus[menuItem] == MENU_TYPE_LOAD){ + MENU_DEBUG_PRINTF("Load Slot DOWN\n"); + + /** Choose quick save file or standard saveslot for loading */ + if(!quick_load_slot_chosen && + state_slot == 0 && + access(quick_save_file, F_OK ) != -1){ + quick_load_slot_chosen = 1; + } + else if(quick_load_slot_chosen){ + quick_load_slot_chosen = 0; + state_slot = MAX_SAVE_SLOTS-1; + } + else{ + state_slot = (!state_slot)?(MAX_SAVE_SLOTS-1):(state_slot-1); + } + + /// ------ Refresh screen ------ + screen_refresh = 1; + } + else if(idx_menus[menuItem] == MENU_TYPE_ASPECT_RATIO){ + MENU_DEBUG_PRINTF("Aspect Ratio DOWN\n"); + aspect_ratio = (!aspect_ratio)?(NB_ASPECT_RATIOS_TYPES-1):(aspect_ratio-1); + + /// ------ Refresh screen ------ + screen_refresh = 1; + + // Save config file + configfile_save(cfg_file_rom); + } + break; + + case SDLK_r: + case SDLK_RIGHT: + //MENU_DEBUG_PRINTF("RIGHT\n"); + 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); + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + MENU_ERROR_PRINTF("Failed to run command %s\n", shell_cmd); + } + /// ------ Refresh screen ------ + screen_refresh = 1; + } + else 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); + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + MENU_ERROR_PRINTF("Failed to run command %s\n", shell_cmd); + } + /// ------ Refresh screen ------ + screen_refresh = 1; + } + else 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 if(idx_menus[menuItem] == MENU_TYPE_LOAD){ + MENU_DEBUG_PRINTF("Load Slot UP\n"); + + /** Choose quick save file or standard saveslot for loading */ + if(!quick_load_slot_chosen && + state_slot == MAX_SAVE_SLOTS-1 && + access(quick_save_file, F_OK ) != -1){ + quick_load_slot_chosen = 1; + } + else if(quick_load_slot_chosen){ + quick_load_slot_chosen = 0; + state_slot = 0; + } + else{ + state_slot = (state_slot+1)%MAX_SAVE_SLOTS; + } + + /// ------ Refresh screen ------ + screen_refresh = 1; + } + else if(idx_menus[menuItem] == MENU_TYPE_ASPECT_RATIO){ + MENU_DEBUG_PRINTF("Aspect Ratio UP\n"); + aspect_ratio = (aspect_ratio+1)%NB_ASPECT_RATIOS_TYPES; + + /// ------ Refresh screen ------ + screen_refresh = 1; + + // Save config file + configfile_save(cfg_file_rom); + } + break; + + case SDLK_a: + case SDLK_RETURN: + 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(menuItem, prevItem, scroll, menu_confirmation, 1); + + /// ------ Save game ------ + int ret = emu_save_load_game(0, 0); + + /// ----- Hud Msg ----- + if(ret){ + MENU_ERROR_PRINTF("Save Failed\n"); + sprintf(shell_cmd, "%s %d \" SAVE FAILED\"", + SHELL_CMD_NOTIF, NOTIF_SECONDS_DISP); + } + else{ + sprintf(shell_cmd, "%s %d \" SAVED IN SLOT %d\"", + SHELL_CMD_NOTIF, NOTIF_SECONDS_DISP, state_slot+1); + } + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + MENU_ERROR_PRINTF("Failed to run command %s\n", shell_cmd); + } + + stop_menu_loop = 1; + } + else{ + MENU_DEBUG_PRINTF("Save game - asking confirmation\n"); + menu_confirmation = 1; + /// ------ Refresh screen ------ + screen_refresh = 1; + } + } + else 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(menuItem, prevItem, scroll, menu_confirmation, 1); + + /// ------ Load game ------ + int ret; + if(quick_load_slot_chosen){ + ret = emu_save_load_game_from_file(1, quick_save_file); + } + else{ + ret = emu_save_load_game(1, 0); + } + + /// ----- Hud Msg ----- + if(ret){ + MENU_ERROR_PRINTF("Load Failed\n"); + sprintf(shell_cmd, "%s %d \" LOAD FAILED\"", + SHELL_CMD_NOTIF, NOTIF_SECONDS_DISP); + } + else{ + if(quick_load_slot_chosen){ + sprintf(shell_cmd, "%s %d \" LOADED FROM AUTO SAVE\"", + SHELL_CMD_NOTIF, NOTIF_SECONDS_DISP); + } + else{ + sprintf(shell_cmd, "%s %d \" LOADED FROM SLOT %d\"", + SHELL_CMD_NOTIF, NOTIF_SECONDS_DISP, state_slot+1); + } + } + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + MENU_ERROR_PRINTF("Failed to run command %s\n", shell_cmd); + } + + /*snprintf(hud_msg, sizeof(hud_msg), ret == 0 ? "LOADED" : "FAIL!"); + hud_new_msg = 3;*/ + stop_menu_loop = 1; + } + else{ + MENU_DEBUG_PRINTF("Save game - asking confirmation\n"); + menu_confirmation = 1; + /// ------ Refresh screen ------ + screen_refresh = 1; + } + } + else 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 ---- + int ret = emu_save_load_game_from_file(0, quick_save_file); + + /// ----- Exit game and back to launcher ---- + engineState = PGS_Quit; + stop_menu_loop = 1; + } + else{ + MENU_DEBUG_PRINTF("Exit game - asking confirmation\n"); + menu_confirmation = 1; + /// ------ Refresh screen ------ + screen_refresh = 1; + } + } + else if(idx_menus[menuItem] == MENU_TYPE_POWERDOWN){ + if(menu_confirmation){ + MENU_DEBUG_PRINTF("Powerdown - confirmed\n"); + /// ----- Shell cmd ---- + execlp(SHELL_CMD_SHUTDOWN_FUNKEY, SHELL_CMD_SHUTDOWN_FUNKEY, NULL); + MENU_ERROR_PRINTF("Failed to run command %s\n", SHELL_CMD_SHUTDOWN_FUNKEY); + exit(0); + } + else{ + MENU_DEBUG_PRINTF("Powerdown - 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 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(menuItem, prevItem, scroll, menu_confirmation, 0); + } + + /// --------- reset screen refresh --------- + screen_refresh = 0; + } + + /// ------ 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()); + } + + /* Start Ampli */ + popen(SHELL_CMD_TURN_AMPLI_ON, "r"); + + /// ------ Reset last screen ------ + SDL_BlitSurface(backup_hw_screen, NULL, hw_screen, NULL); + SDL_Flip(hw_screen); +} + + + + + + +/****************************/ +/* Quick Resume Menu */ +/****************************/ +int launch_resume_menu_loop() +{ + 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; + + /* Stop Ampli */ + popen(SHELL_CMD_TURN_AMPLI_OFF, "r"); + + /* 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: + engineState = PGS_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 DWON\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; + } + + /// ----- Clear screen ----- + SDL_FillRect(hw_screen, NULL, 0); + SDL_Flip(hw_screen); + + /* 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()); + } + + /* Start Ampli */ + popen(SHELL_CMD_TURN_AMPLI_ON, "r"); + + return option_idx; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + static void make_bg(int no_scale, int from_screen) { @@ -237,6 +1529,10 @@ static void cdload_progress_cb(const char *fname, int percent) void menu_romload_prepare(const char *rom_name) { +#ifndef ALLOW_TEXT_OUT + return; +#endif //ALLOW_TEXT_OUT + const char *p = rom_name + strlen(rom_name); while (p > rom_name && *p != '/') @@ -256,6 +1552,10 @@ void menu_romload_prepare(const char *rom_name) void menu_romload_end(void) { +#ifndef ALLOW_TEXT_OUT + return; +#endif //ALLOW_TEXT_OUT + PicoCartLoadProgressCB = NULL; PicoCDLoadProgressCB = NULL; @@ -1207,6 +2507,23 @@ void menu_loop(void) plat_video_menu_leave(); } +void menu_loop_funkey(void) +{ + in_set_config_int(0, IN_CFG_BLOCKING, 1); + run_menu_loop(); + + if (PicoGameLoaded) { + if (engineState == PGS_Menu) + engineState = PGS_Running; + /* wait until menu, ok, back is released */ + /*while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK)) + ;*/ + } + + in_set_config_int(0, IN_CFG_BLOCKING, 0); + plat_video_menu_leave(); +} + // --------- CD tray close menu ---------- static int mh_tray_load_cd(int id, int keys) diff --git a/platform/common/menu_pico.h b/platform/common/menu_pico.h index ee9d2bab..f88f93a6 100644 --- a/platform/common/menu_pico.h +++ b/platform/common/menu_pico.h @@ -1,8 +1,69 @@ #ifndef __MENU_PICO_H__ #define __MENU_PICO_H__ +#include #include "../libpicofe/menu.h" +typedef enum{ + MENU_TYPE_VOLUME, + MENU_TYPE_BRIGHTNESS, + MENU_TYPE_SAVE, + MENU_TYPE_LOAD, + MENU_TYPE_ASPECT_RATIO, + MENU_TYPE_EXIT, + MENU_TYPE_POWERDOWN, + NB_MENU_TYPES, +} ENUM_MENU_TYPE; + +///------ 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 ------- +#define STEP_CHANGE_VOLUME 10 +#define STEP_CHANGE_BRIGHTNESS 10 +#define NOTIF_SECONDS_DISP 2 + +////------ Menu commands ------- +#define SHELL_CMD_VOLUME_GET "volume_get" +#define SHELL_CMD_VOLUME_SET "volume_set" +#define SHELL_CMD_BRIGHTNESS_GET "brightness_get" +#define SHELL_CMD_BRIGHTNESS_SET "brightness_set" +#define SHELL_CMD_NOTIF "notif_set" +#define SHELL_CMD_WRITE_QUICK_LOAD_CMD "write_args_quick_load_file" +#define SHELL_CMD_TURN_AMPLI_ON "start_audio_amp 1" +#define SHELL_CMD_TURN_AMPLI_OFF "start_audio_amp 0" +#define SHELL_CMD_CANCEL_SCHED_POWERDOWN "cancel_sched_powerdown" +#define SHELL_CMD_INSTANT_PLAY "instant_play" +#define SHELL_CMD_SHUTDOWN_FUNKEY "shutdown_funkey" + +#define MAXPATHLEN 512 + +extern void SDL_Rotate_270(SDL_Surface * hw_surface, SDL_Surface * virtual_hw_surface); + +void init_menu_SDL(); +void deinit_menu_SDL(); +void init_menu_zones(); +void init_menu_system_values(); +void menu_loop_funkey(void); +void run_menu_loop(); +int launch_resume_menu_loop(); + +extern int volume_percentage; +extern int brightness_percentage; + +extern int stop_menu_loop; +extern char *mRomName; +extern char *mRomPath; +extern char *quick_save_file; + typedef enum { MA_NONE = 1, diff --git a/platform/common/plat_sdl.c b/platform/common/plat_sdl.c index 2cdb1979..a37b3367 100644 --- a/platform/common/plat_sdl.c +++ b/platform/common/plat_sdl.c @@ -7,6 +7,8 @@ */ #include +#include +#include #include "../libpicofe/input.h" #include "../libpicofe/plat.h" @@ -14,6 +16,7 @@ #include "../libpicofe/in_sdl.h" #include "../libpicofe/gl.h" #include "emu.h" +#include "configfile.h" #include "menu_pico.h" #include "input_pico.h" #include "plat_sdl.h" @@ -21,6 +24,40 @@ #include +#define RES_HW_SCREEN_HORIZONTAL 240 +#define RES_HW_SCREEN_VERTICAL 240 + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define ABS(x) (((x) < 0) ? (-x) : (x)) + + + +#define AVERAGE(z, x) ((((z) & 0xF7DEF7DE) >> 1) + (((x) & 0xF7DEF7DE) >> 1)) +#define AVERAGEHI(AB) ((((AB) & 0xF7DE0000) >> 1) + (((AB) & 0xF7DE) << 15)) +#define AVERAGELO(CD) ((((CD) & 0xF7DE) >> 1) + (((CD) & 0xF7DE0000) >> 17)) + +// Support math +#define Half(A) (((A) >> 1) & 0x7BEF) +#define Quarter(A) (((A) >> 2) & 0x39E7) +// Error correction expressions to piece back the lower bits together +#define RestHalf(A) ((A) & 0x0821) +#define RestQuarter(A) ((A) & 0x1863) + +// Error correction expressions for quarters of pixels +#define Corr1_3(A, B) Quarter(RestQuarter(A) + (RestHalf(B) << 1) + RestQuarter(B)) +#define Corr3_1(A, B) Quarter((RestHalf(A) << 1) + RestQuarter(A) + RestQuarter(B)) + +// Error correction expressions for halves +#define Corr1_1(A, B) ((A) & (B) & 0x0821) + +// Quarters +#define Weight1_3(A, B) (Quarter(A) + Half(B) + Quarter(B) + Corr1_3(A, B)) +#define Weight3_1(A, B) (Half(A) + Quarter(A) + Quarter(B) + Corr3_1(A, B)) + +// Halves +#define Weight1_1(A, B) (Half(A) + Half(B) + Corr1_1(A, B)) + static void *shadow_fb; static struct in_pdata in_sdl_platform_data = { @@ -29,12 +66,38 @@ static struct in_pdata in_sdl_platform_data = { .joy_map = in_sdl_joy_map, }; +static struct in_pdata in_sdl_platform_data_SMS = { + .defbinds = in_sdl_defbinds_SMS, + .key_map = in_sdl_key_map, + .joy_map = in_sdl_joy_map, +}; + /* YUV stuff */ static int yuv_ry[32], yuv_gy[32], yuv_by[32]; static unsigned char yuv_u[32 * 2], yuv_v[32 * 2]; static unsigned char yuv_y[256]; static struct uyvy { uint32_t y:8; uint32_t vyu:24; } yuv_uyvy[65536]; +SDL_Surface * hw_screen = NULL; +SDL_Surface * virtual_hw_screen = NULL; +SDL_Surface * sms_game_screen = NULL; + +void clear_screen(SDL_Surface *surface, uint16_t color) +{ + if(surface){ + uint16_t *dest_ptr = (uint16_t *)surface->pixels; + uint32_t x, y; + + for(y = 0; y < surface->h; y++) + { + for(x = 0; x < surface->w; x++, dest_ptr++) + { + *dest_ptr = color; + } + } + } +} + void bgr_to_uyvy_init(void) { int i, v; @@ -117,6 +180,1335 @@ void rgb565_to_uyvy(void *d, const void *s, int pixels, int x2) } } + + + + + + + +// Nearest neighboor +void flip_NN(SDL_Surface *virtual_screen, SDL_Surface *hardware_screen, int new_w, int new_h){ + int w2=new_w; + int h2=new_h; + int x_ratio = (int)((virtual_screen->w<<16)/w2) +1; + int y_ratio = (int)((virtual_screen->h<<16)/h2) +1; + //int x_ratio = (int)((w1<<16)/w2) ; + //int y_ratio = (int)((h1<<16)/h2) ; + //printf("virtual_screen->w=%d, virtual_screen->h=%d\n", virtual_screen->w, virtual_screen->h); + int x2, y2 ; + for (int i=0;i=RES_HW_SCREEN_VERTICAL){ + continue; + } + //printf("\n\ny=%d\n", i); + for (int j=0;j=RES_HW_SCREEN_HORIZONTAL){ + continue; + } + //printf("x=%d, ",j); + x2 = ((j*x_ratio)>>16) ; + y2 = ((i*y_ratio)>>16) ; + + //printf("y=%d, x=%d, y2=%d, x2=%d, (y2*virtual_screen->w)+x2=%d\n", i, j, y2, x2, (y2*virtual_screen->w)+x2); + *(uint16_t*)(hardware_screen->pixels+(i* ((w2>RES_HW_SCREEN_HORIZONTAL)?RES_HW_SCREEN_HORIZONTAL:w2 ) +j)*sizeof(uint16_t)) = + *(uint16_t*)(virtual_screen->pixels + ((y2*virtual_screen->w)+x2) *sizeof(uint16_t)) ; + } + } +} + +// Nearest neighboor with possible out of screen coordinates (for cropping) +void flip_NN_AllowOutOfScreen(SDL_Surface *virtual_screen, SDL_Surface *hardware_screen, int new_w, int new_h){ + int w2=new_w; + int h2=new_h; + int x_ratio = (int)((virtual_screen->w<<16)/w2) +1; + int y_ratio = (int)((virtual_screen->h<<16)/h2) +1; + //int x_ratio = (int)((w1<<16)/w2) ; + //int y_ratio = (int)((h1<<16)/h2) ; + //printf("virtual_screen->w=%d, virtual_screen->h=%d\n", virtual_screen->w, virtual_screen->h); + int x2, y2 ; + + /// --- Compute padding for centering when out of bounds --- + int x_padding = 0; + if(w2>RES_HW_SCREEN_HORIZONTAL){ + x_padding = (w2-RES_HW_SCREEN_HORIZONTAL)/2 + 1; + } + + for (int i=0;i=RES_HW_SCREEN_VERTICAL){ + continue; + } + //printf("\n\ny=%d\n", i); + for (int j=0;j=RES_HW_SCREEN_HORIZONTAL){ + continue; + } + //printf("x=%d, ",j); + x2 = ((j*x_ratio)>>16) ; + y2 = ((i*y_ratio)>>16) ; + + //printf("y=%d, x=%d, y2=%d, x2=%d, (y2*virtual_screen->w)+x2=%d\n", i, j, y2, x2, (y2*virtual_screen->w)+x2); + *(uint16_t*)(hardware_screen->pixels+(i* ((w2>RES_HW_SCREEN_HORIZONTAL)?RES_HW_SCREEN_HORIZONTAL:w2 ) +j)*sizeof(uint16_t)) = + *(uint16_t*)(virtual_screen->pixels + ((y2*virtual_screen->w)+x2 + x_padding) *sizeof(uint16_t)) ; + } + } +} + +/// Nearest neighboor optimized with possible out of screen coordinates (for cropping) +void flip_NNOptimized_AllowOutOfScreen(SDL_Surface *virtual_screen, SDL_Surface *hardware_screen, int new_w, int new_h){ + int w1=virtual_screen->w; + //int h1=virtual_screen->h; + int w2=new_w; + int h2=new_h; + int x_ratio = (int)((virtual_screen->w<<16)/w2); + int y_ratio = (int)((virtual_screen->h<<16)/h2); + + int y_padding = (RES_HW_SCREEN_VERTICAL-new_h)/2; + //int x_ratio = (int)((virtual_screen->w<<16)/w2); + //int y_ratio = (int)((virtual_screen->h<<16)/h2); + int x2, y2 ; + + /// --- Compute padding for centering when out of bounds --- + int x_padding = 0; + if(w2>RES_HW_SCREEN_HORIZONTAL){ + x_padding = (w2-RES_HW_SCREEN_HORIZONTAL)/2 + 1; + } + int x_padding_ratio = x_padding*w1/w2; + //printf("virtual_screen->h=%d, h2=%d\n", virtual_screen->h, h2); + + for (int i=0;i=RES_HW_SCREEN_VERTICAL){ + continue; + } + + uint16_t* t = (uint16_t*)(hardware_screen->pixels+((i+y_padding)* ((w2>RES_HW_SCREEN_HORIZONTAL)?RES_HW_SCREEN_HORIZONTAL:w2) )*sizeof(uint16_t)); + y2 = ((i*y_ratio)>>16); + uint16_t* p = (uint16_t*)(virtual_screen->pixels + (y2*w1 + x_padding_ratio) *sizeof(uint16_t)); + int rat = 0; + for (int j=0;j=RES_HW_SCREEN_HORIZONTAL){ + continue; + } + x2 = (rat>>16); + *t++ = p[x2]; + rat += x_ratio; + //printf("y=%d, x=%d, y2=%d, x2=%d, (y2*virtual_screen->w)+x2=%d\n", i, j, y2, x2, (y2*virtual_screen->w)+x2); + } + } +} + + +/// Nearest neighboor with 2D bilinear and interp by the number of pixel diff, not 2 +void flip_NNOptimized_MissingPixelsBilinear(SDL_Surface *virtual_screen, SDL_Surface *hardware_screen, int new_w, int new_h){ + int w1=virtual_screen->w; + int h1=virtual_screen->h; + int w2=new_w; + int h2=new_h; + int y_padding = (RES_HW_SCREEN_VERTICAL-new_h)/2; + //int x_ratio = (int)((w1<<16)/w2) +1; + //int y_ratio = (int)((h1<<16)/h2) +1; + int x_ratio = (int)((w1<<16)/w2); + int y_ratio = (int)((h1<<16)/h2); + int x1, y1; + /*int cnt_yes_x_yes_y, cnt_yes_x_no_y, cnt_no_x_yes_y, cnt_no_x_no_y; + cnt_yes_x_yes_y= cnt_yes_x_no_y= cnt_no_x_yes_y= cnt_no_x_no_y = 0;*/ + for (int i=0;ipixels+((i+y_padding)*w2)*sizeof(uint16_t)); + y1 = ((i*y_ratio)>>16); + int px_diff_next_y = MAX( (((i+1)*y_ratio)>>16) - y1, 1); + //printf("px_diff_next_y:%d\n", px_diff_next_y); + uint16_t* p = (uint16_t*)(virtual_screen->pixels + (y1*w1) *sizeof(uint16_t)); + int rat = 0; + for (int j=0;j>16); + int px_diff_next_x = MAX( ((rat+x_ratio)>>16) - x1, 1); + + // ------ optimized bilinear (to put in function) ------- + uint16_t * cur_p; + int cur_y_offset; + uint32_t red_comp = 0; + uint32_t green_comp = 0; + uint32_t blue_comp = 0; + for(int cur_px_diff_y=0; cur_px_diff_yw; + int h1=virtual_screen->h; + int w2=new_w; + int h2=new_h; + int y_padding = (RES_HW_SCREEN_VERTICAL-new_h)/2; + //int x_ratio = (int)((w1<<16)/w2) +1; + //int y_ratio = (int)((h1<<16)/h2) +1; + int x_ratio = (int)((w1<<16)/w2); + int y_ratio = (int)((h1<<16)/h2); + int x1, y1; + + uint16_t green_mask = 0x07E0; + + /// --- Compute padding for centering when out of bounds --- + int x_padding = 0; + if(w2>RES_HW_SCREEN_HORIZONTAL){ + x_padding = (w2-RES_HW_SCREEN_HORIZONTAL)/2 + 1; + } + int x_padding_ratio = x_padding*w1/w2; + + /// --- Interp params --- + int px_diff_prev_x = 0; + int px_diff_next_x = 0; + uint32_t ponderation_factor; + uint16_t * cur_p; + uint16_t * cur_p_left; + uint16_t * cur_p_right; + uint32_t red_comp, green_comp, blue_comp; + //int cnt_interp = 0; int cnt_no_interp = 0; + //printf("virtual_screen->w=%d, virtual_screen->w=%d\n", virtual_screen->w, virtual_screen->h); + + for (int i=0;i=RES_HW_SCREEN_VERTICAL){ + continue; + } + uint16_t* t = (uint16_t*)(hardware_screen->pixels+( (i+y_padding)*((w2>RES_HW_SCREEN_HORIZONTAL)?RES_HW_SCREEN_HORIZONTAL:w2))*sizeof(uint16_t)); + y1 = ((i*y_ratio)>>16); + uint16_t* p = (uint16_t*)(virtual_screen->pixels + (y1*w1 + x_padding_ratio) *sizeof(uint16_t)); + int rat = 0; + for (int j=0;j=RES_HW_SCREEN_HORIZONTAL){ + continue; + } + // ------ current x value ------ + x1 = (rat>>16); + px_diff_next_x = ((rat+x_ratio)>>16) - x1; + + // ------ adapted bilinear with 3x3 gaussian blur ------- + cur_p = p+x1; + if(px_diff_prev_x > 1 || px_diff_next_x > 1){ + red_comp=((*cur_p)&0xF800) << 1; + green_comp=((*cur_p)&0x07E0) << 1; + blue_comp=((*cur_p)&0x001F) << 1; + ponderation_factor = 2; + + // ---- Interpolate current and left ---- + if(px_diff_prev_x > 1 && x1>0){ + cur_p_left = p+x1-1; + + red_comp += ((*cur_p_left)&0xF800); + green_comp += ((*cur_p_left)&0x07E0); + blue_comp += ((*cur_p_left)&0x001F); + ponderation_factor++; + } + + // ---- Interpolate current and right ---- + if(px_diff_next_x > 1 && x1+1> 2)&0xF800; + green_comp = (green_comp >> 2)&green_mask; + blue_comp = (blue_comp >> 2)&0x001F; + } + else if(ponderation_factor==2){ + red_comp = (red_comp >> 1)&0xF800; + green_comp = (green_comp >> 1)&green_mask; + blue_comp = (blue_comp >> 1)&0x001F; + } + else{ + red_comp = (red_comp / ponderation_factor )&0xF800; + green_comp = (green_comp / ponderation_factor )&green_mask; + blue_comp = (blue_comp / ponderation_factor )&0x001F; + } + + /// --- write pixel --- + *t++ = red_comp+green_comp+blue_comp; + } + else{ + /// --- copy pixel --- + *t++ = (*cur_p); + } + + /// save number of pixels to interpolate + px_diff_prev_x = px_diff_next_x; + + // ------ next pixel ------ + rat += x_ratio; + } + } + //printf("cnt_interp = %d, int cnt_no_interp = %d\n", cnt_interp, cnt_no_interp); +} + +/// Nearest neighbor with 2D bilinear and interpolation with left, right, up and down pixels, pseudo gaussian weighting +void flip_NNOptimized_LeftRightUpDownBilinear(SDL_Surface *virtual_screen, SDL_Surface *hardware_screen, int new_w, int new_h){ + int w1=virtual_screen->w; + int h1=virtual_screen->h; + int w2=new_w; + int h2=new_h; + int y_padding = (RES_HW_SCREEN_VERTICAL-new_h)/2; + //int x_ratio = (int)((w1<<16)/w2) +1; + //int y_ratio = (int)((h1<<16)/h2) +1; + int x_ratio = (int)((w1<<16)/w2); + int y_ratio = (int)((h1<<16)/h2); + int x1, y1; + + uint16_t green_mask = 0x07E0; + + /// --- Compute padding for centering when out of bounds --- + int x_padding = 0; + if(w2>RES_HW_SCREEN_HORIZONTAL){ + x_padding = (w2-RES_HW_SCREEN_HORIZONTAL)/2 + 1; + } + int x_padding_ratio = x_padding*w1/w2; + + /// --- Interp params --- + int px_diff_prev_x = 0; + int px_diff_next_x = 0; + int px_diff_prev_y = 0; + int px_diff_next_y = 0; + uint32_t ponderation_factor; + uint16_t * cur_p; + uint16_t * cur_p_left; + uint16_t * cur_p_right; + uint16_t * cur_p_up; + uint16_t * cur_p_down; + uint32_t red_comp, green_comp, blue_comp; + //int cnt_interp = 0; int cnt_no_interp = 0; + //printf("virtual_screen->w=%d, virtual_screen->w=%d\n", virtual_screen->w, virtual_screen->h); + + ///Debug + + for (int i=0;i=RES_HW_SCREEN_VERTICAL){ + continue; + } + uint16_t* t = (uint16_t*)(hardware_screen->pixels+( (i+y_padding)*((w2>RES_HW_SCREEN_HORIZONTAL)?RES_HW_SCREEN_HORIZONTAL:w2))*sizeof(uint16_t)); + // ------ current and next y value ------ + y1 = ((i*y_ratio)>>16); + px_diff_next_y = MAX( (((i+1)*y_ratio)>>16) - y1, 1); + uint16_t* p = (uint16_t*)(virtual_screen->pixels + (y1*w1+x_padding_ratio) *sizeof(uint16_t)); + int rat = 0; + for (int j=0;j=RES_HW_SCREEN_HORIZONTAL){ + continue; + } + // ------ current x value ------ + x1 = (rat>>16); + px_diff_next_x = ((rat+x_ratio)>>16) - x1; + + // ------ adapted bilinear with 3x3 gaussian blur ------- + cur_p = p+x1; + if(px_diff_prev_x > 1 || px_diff_next_x > 1 || px_diff_prev_y > 1 || px_diff_next_y > 1){ + red_comp=((*cur_p)&0xF800) << 1; + green_comp=((*cur_p)&0x07E0) << 1; + blue_comp=((*cur_p)&0x001F) << 1; + ponderation_factor = 2; + + // ---- Interpolate current and left ---- + if(px_diff_prev_x > 1 && x1>0){ + cur_p_left = p+x1-1; + + red_comp += ((*cur_p_left)&0xF800); + green_comp += ((*cur_p_left)&0x07E0); + blue_comp += ((*cur_p_left)&0x001F); + ponderation_factor++; + } + + // ---- Interpolate current and right ---- + if(px_diff_next_x > 1 && x1+1 1 && y1 > 0){ + cur_p_up = p+x1-w1; + + red_comp += ((*cur_p_up)&0xF800); + green_comp += ((*cur_p_up)&0x07E0); + blue_comp += ((*cur_p_up)&0x001F); + ponderation_factor++; + } + + // ---- Interpolate current and down ---- + if(px_diff_next_y > 1 && y1 + 1 < h1){ + cur_p_down = p+x1+w1; + + red_comp += ((*cur_p_down)&0xF800); + green_comp += ((*cur_p_down)&0x07E0); + blue_comp += ((*cur_p_down)&0x001F); + ponderation_factor++; + } + + /// --- Compute new px value --- + if(ponderation_factor==4){ + red_comp = (red_comp >> 2)&0xF800; + green_comp = (green_comp >> 2)&green_mask; + blue_comp = (blue_comp >> 2)&0x001F; + } + else if(ponderation_factor==2){ + red_comp = (red_comp >> 1)&0xF800; + green_comp = (green_comp >> 1)&green_mask; + blue_comp = (blue_comp >> 1)&0x001F; + } + else{ + red_comp = (red_comp / ponderation_factor )&0xF800; + green_comp = (green_comp / ponderation_factor )&green_mask; + blue_comp = (blue_comp / ponderation_factor )&0x001F; + } + + /// --- write pixel --- + *t++ = red_comp+green_comp+blue_comp; + } + else{ + /// --- copy pixel --- + *t++ = (*cur_p); + } + + /// save number of pixels to interpolate + px_diff_prev_x = px_diff_next_x; + + // ------ next pixel ------ + rat += x_ratio; + } + px_diff_prev_y = px_diff_next_y; + } + //printf("cnt_interp = %d, int cnt_no_interp = %d\n", cnt_interp, cnt_no_interp); +} + + + +/// Nearest neighbor with 2D bilinear and interpolation with left, right, up and down pixels, pseudo gaussian weighting +void flip_NNOptimized_LeftRightUpDownBilinear_Optimized4(SDL_Surface *virtual_screen, SDL_Surface *hardware_screen, int new_w, int new_h){ + int w1=virtual_screen->w; + int h1=virtual_screen->h; + int w2=new_w; + int h2=new_h; + int y_padding = (RES_HW_SCREEN_VERTICAL-new_h)/2; + int x_ratio = (int)((w1<<16)/w2); + int y_ratio = (int)((h1<<16)/h2); + int x1, y1; + + uint16_t green_mask = 0x07E0; + + /// --- Compute padding for centering when out of bounds --- + int x_padding = 0; + if(w2>RES_HW_SCREEN_HORIZONTAL){ + x_padding = (w2-RES_HW_SCREEN_HORIZONTAL)/2 + 1; + } + int x_padding_ratio = x_padding*w1/w2; + + /// --- Interp params --- + int px_diff_prev_x = 0; + int px_diff_next_x = 0; + int px_diff_prev_y = 0; + int px_diff_next_y = 0; + uint32_t ponderation_factor; + uint8_t left_px_missing, right_px_missing, up_px_missing, down_px_missing; + int supposed_pond_factor; + + uint16_t * cur_p; + uint16_t * cur_p_left; + uint16_t * cur_p_right; + uint16_t * cur_p_up; + uint16_t * cur_p_down; + uint32_t red_comp, green_comp, blue_comp; + //printf("virtual_screen->w=%d, virtual_screen->w=%d\n", virtual_screen->w, virtual_screen->h); + + ///Debug + /*int occurence_pond[7]; + memset(occurence_pond, 0, 7*sizeof(int));*/ + + for (int i=0;i=RES_HW_SCREEN_VERTICAL){ + continue; + } + uint16_t* t = (uint16_t*)(hardware_screen->pixels+( (i+y_padding)*((w2>RES_HW_SCREEN_HORIZONTAL)?RES_HW_SCREEN_HORIZONTAL:w2))*sizeof(uint16_t)); + // ------ current and next y value ------ + y1 = ((i*y_ratio)>>16); + px_diff_next_y = MAX( (((i+1)*y_ratio)>>16) - y1, 1); + uint16_t* p = (uint16_t*)(virtual_screen->pixels + (y1*w1+x_padding_ratio) *sizeof(uint16_t)); + int rat = 0; + for (int j=0;j=RES_HW_SCREEN_HORIZONTAL){ + continue; + } + // ------ current x value ------ + x1 = (rat>>16); + px_diff_next_x = ((rat+x_ratio)>>16) - x1; + + // ------ adapted bilinear with 3x3 gaussian blur ------- + cur_p = p+x1; + if(px_diff_prev_x > 1 || px_diff_next_x > 1 || px_diff_prev_y > 1 || px_diff_next_y > 1){ + red_comp=((*cur_p)&0xF800) << 1; + green_comp=((*cur_p)&0x07E0) << 1; + blue_comp=((*cur_p)&0x001F) << 1; + ponderation_factor = 2; + left_px_missing = (px_diff_prev_x > 1 && x1>0); + right_px_missing = (px_diff_next_x > 1 && x1+1 1 && y1 > 0); + down_px_missing = (px_diff_next_y > 1 && y1 + 1 < h1); + supposed_pond_factor = 2 + left_px_missing + right_px_missing + + up_px_missing + down_px_missing; + + // ---- Interpolate current and up ---- + if(up_px_missing){ + cur_p_up = p+x1-w1; + + if(supposed_pond_factor==3){ + red_comp += ((*cur_p_up)&0xF800) << 1; + green_comp += ((*cur_p_up)&0x07E0) << 1; + blue_comp += ((*cur_p_up)&0x001F) << 1; + ponderation_factor+=2; + } + else if(supposed_pond_factor==4 || + (supposed_pond_factor==5 && !down_px_missing )){ + red_comp += ((*cur_p_up)&0xF800); + green_comp += ((*cur_p_up)&0x07E0); + blue_comp += ((*cur_p_up)&0x001F); + ponderation_factor++; + } + } + + // ---- Interpolate current and left ---- + if(left_px_missing){ + cur_p_left = p+x1-1; + + if(supposed_pond_factor==3){ + red_comp += ((*cur_p_left)&0xF800) << 1; + green_comp += ((*cur_p_left)&0x07E0) << 1; + blue_comp += ((*cur_p_left)&0x001F) << 1; + ponderation_factor+=2; + } + else if(supposed_pond_factor==4 || + (supposed_pond_factor==5 && !right_px_missing )){ + red_comp += ((*cur_p_left)&0xF800); + green_comp += ((*cur_p_left)&0x07E0); + blue_comp += ((*cur_p_left)&0x001F); + ponderation_factor++; + } + } + + // ---- Interpolate current and down ---- + if(down_px_missing){ + cur_p_down = p+x1+w1; + + if(supposed_pond_factor==3){ + red_comp += ((*cur_p_down)&0xF800) << 1; + green_comp += ((*cur_p_down)&0x07E0) << 1; + blue_comp += ((*cur_p_down)&0x001F) << 1; + ponderation_factor+=2; + } + else if(supposed_pond_factor>=4){ + red_comp += ((*cur_p_down)&0xF800); + green_comp += ((*cur_p_down)&0x07E0); + blue_comp += ((*cur_p_down)&0x001F); + ponderation_factor++; + } + } + + // ---- Interpolate current and right ---- + if(right_px_missing){ + cur_p_right = p+x1+1; + + if(supposed_pond_factor==3){ + red_comp += ((*cur_p_right)&0xF800) << 1; + green_comp += ((*cur_p_right)&0x07E0) << 1; + blue_comp += ((*cur_p_right)&0x001F) << 1; + ponderation_factor+=2; + } + else if(supposed_pond_factor>=4){ + red_comp += ((*cur_p_right)&0xF800); + green_comp += ((*cur_p_right)&0x07E0); + blue_comp += ((*cur_p_right)&0x001F); + ponderation_factor++; + } + } + + /// --- Compute new px value --- + if(ponderation_factor==4){ + red_comp = (red_comp >> 2)&0xF800; + green_comp = (green_comp >> 2)&green_mask; + blue_comp = (blue_comp >> 2)&0x001F; + } + else if(ponderation_factor==2){ + red_comp = (red_comp >> 1)&0xF800; + green_comp = (green_comp >> 1)&green_mask; + blue_comp = (blue_comp >> 1)&0x001F; + } + else{ + red_comp = (red_comp / ponderation_factor )&0xF800; + green_comp = (green_comp / ponderation_factor )&green_mask; + blue_comp = (blue_comp / ponderation_factor )&0x001F; + } + + /// Debug + //occurence_pond[ponderation_factor] += 1; + + /// --- write pixel --- + *t++ = red_comp+green_comp+blue_comp; + } + else{ + /// --- copy pixel --- + *t++ = (*cur_p); + + /// Debug + //occurence_pond[1] += 1; + } + + /// save number of pixels to interpolate + px_diff_prev_x = px_diff_next_x; + + // ------ next pixel ------ + rat += x_ratio; + } + px_diff_prev_y = px_diff_next_y; + } + /// Debug + /*printf("pond: [%d, %d, %d, %d, %d, %d]\n", occurence_pond[1], occurence_pond[2], occurence_pond[3], + occurence_pond[4], occurence_pond[5], occurence_pond[6]);*/ +} + + + +/// Nearest neighbor with 2D bilinear and interpolation with left, right, up and down pixels, pseudo gaussian weighting +void flip_NNOptimized_LeftRightUpDownBilinear_Optimized8(SDL_Surface *virtual_screen, SDL_Surface *hardware_screen, int new_w, int new_h){ + int w1=virtual_screen->w; + int h1=virtual_screen->h; + int w2=new_w; + int h2=new_h; + int y_padding = (RES_HW_SCREEN_VERTICAL-new_h)/2; + //int x_ratio = (int)((w1<<16)/w2) +1; + //int y_ratio = (int)((h1<<16)/h2) +1; + int x_ratio = (int)((w1<<16)/w2); + int y_ratio = (int)((h1<<16)/h2); + int x1, y1; + +#ifdef BLACKER_BLACKS + /// Optimization for blacker blacks (our screen do not handle green value of 1 very well) + uint16_t green_mask = 0x07C0; +#else + uint16_t green_mask = 0x07E0; +#endif + + /// --- Compute padding for centering when out of bounds --- + int x_padding = 0; + if(w2>RES_HW_SCREEN_HORIZONTAL){ + x_padding = (w2-RES_HW_SCREEN_HORIZONTAL)/2 + 1; + } + int x_padding_ratio = x_padding*w1/w2; + + /// --- Interp params --- + int px_diff_prev_x = 0; + int px_diff_next_x = 0; + int px_diff_prev_y = 0; + int px_diff_next_y = 0; + uint32_t ponderation_factor; + uint8_t left_px_missing, right_px_missing, up_px_missing, down_px_missing; + int supposed_pond_factor; + + uint16_t * cur_p; + uint16_t * cur_p_left; + uint16_t * cur_p_right; + uint16_t * cur_p_up; + uint16_t * cur_p_down; + uint32_t red_comp, green_comp, blue_comp; + //printf("virtual_screen->w=%d, virtual_screen->w=%d\n", virtual_screen->w, virtual_screen->h); + + ///Debug + /*int occurence_pond[9]; + memset(occurence_pond, 0, 9*sizeof(int));*/ + + for (int i=0;i=RES_HW_SCREEN_VERTICAL){ + continue; + } + uint16_t* t = (uint16_t*)(hardware_screen->pixels+( (i+y_padding)*((w2>RES_HW_SCREEN_HORIZONTAL)?RES_HW_SCREEN_HORIZONTAL:w2))*sizeof(uint16_t)); + // ------ current and next y value ------ + y1 = ((i*y_ratio)>>16); + px_diff_next_y = MAX( (((i+1)*y_ratio)>>16) - y1, 1); + uint16_t* p = (uint16_t*)(virtual_screen->pixels + (y1*w1+x_padding_ratio) *sizeof(uint16_t)); + int rat = 0; + for (int j=0;j=RES_HW_SCREEN_HORIZONTAL){ + continue; + } + // ------ current x value ------ + x1 = (rat>>16); + px_diff_next_x = ((rat+x_ratio)>>16) - x1; + + // ------ adapted bilinear with 3x3 gaussian blur ------- + cur_p = p+x1; + if(px_diff_prev_x > 1 || px_diff_next_x > 1 || px_diff_prev_y > 1 || px_diff_next_y > 1){ + red_comp=((*cur_p)&0xF800) << 1; + green_comp=((*cur_p)&0x07E0) << 1; + blue_comp=((*cur_p)&0x001F) << 1; + ponderation_factor = 2; + left_px_missing = (px_diff_prev_x > 1 && x1>0); + right_px_missing = (px_diff_next_x > 1 && x1+1 1 && y1 > 0); + down_px_missing = (px_diff_next_y > 1 && y1 + 1 < h1); + supposed_pond_factor = 2 + left_px_missing + right_px_missing + + up_px_missing + down_px_missing; + + // ---- Interpolate current and up ---- + if(up_px_missing){ + cur_p_up = p+x1-w1; + + if(supposed_pond_factor==3){ + red_comp += ((*cur_p_up)&0xF800) << 1; + green_comp += ((*cur_p_up)&0x07E0) << 1; + blue_comp += ((*cur_p_up)&0x001F) << 1; + ponderation_factor+=2; + } + else if(supposed_pond_factor == 4 || + (supposed_pond_factor == 5 && !down_px_missing) || + supposed_pond_factor == 6 ){ + red_comp += ((*cur_p_up)&0xF800); + green_comp += ((*cur_p_up)&0x07E0); + blue_comp += ((*cur_p_up)&0x001F); + ponderation_factor++; + } + } + + // ---- Interpolate current and left ---- + if(left_px_missing){ + cur_p_left = p+x1-1; + + if(supposed_pond_factor==3){ + red_comp += ((*cur_p_left)&0xF800) << 1; + green_comp += ((*cur_p_left)&0x07E0) << 1; + blue_comp += ((*cur_p_left)&0x001F) << 1; + ponderation_factor+=2; + } + else if(supposed_pond_factor == 4 || + (supposed_pond_factor == 5 && !right_px_missing) || + supposed_pond_factor == 6 ){ + red_comp += ((*cur_p_left)&0xF800); + green_comp += ((*cur_p_left)&0x07E0); + blue_comp += ((*cur_p_left)&0x001F); + ponderation_factor++; + } + } + + // ---- Interpolate current and down ---- + if(down_px_missing){ + cur_p_down = p+x1+w1; + + if(supposed_pond_factor==3 || supposed_pond_factor==6){ + red_comp += ((*cur_p_down)&0xF800) << 1; + green_comp += ((*cur_p_down)&0x07E0) << 1; + blue_comp += ((*cur_p_down)&0x001F) << 1; + ponderation_factor+=2; + } + else if(supposed_pond_factor >= 4 && supposed_pond_factor != 6){ + red_comp += ((*cur_p_down)&0xF800); + green_comp += ((*cur_p_down)&0x07E0); + blue_comp += ((*cur_p_down)&0x001F); + ponderation_factor++; + } + } + + // ---- Interpolate current and right ---- + if(right_px_missing){ + cur_p_right = p+x1+1; + + if(supposed_pond_factor==3 || supposed_pond_factor==6){ + red_comp += ((*cur_p_right)&0xF800) << 1; + green_comp += ((*cur_p_right)&0x07E0) << 1; + blue_comp += ((*cur_p_right)&0x001F) << 1; + ponderation_factor+=2; + } + else if(supposed_pond_factor >= 4 && supposed_pond_factor != 6){ + red_comp += ((*cur_p_right)&0xF800); + green_comp += ((*cur_p_right)&0x07E0); + blue_comp += ((*cur_p_right)&0x001F); + ponderation_factor++; + } + } + + /// --- Compute new px value --- + if(ponderation_factor==8){ + red_comp = (red_comp >> 3)&0xF800; + green_comp = (green_comp >> 3)&green_mask; + blue_comp = (blue_comp >> 3)&0x001F; + } + else if(ponderation_factor==4){ + red_comp = (red_comp >> 2)&0xF800; + green_comp = (green_comp >> 2)&green_mask; + blue_comp = (blue_comp >> 2)&0x001F; + } + else if(ponderation_factor==2){ + red_comp = (red_comp >> 1)&0xF800; + green_comp = (green_comp >> 1)&green_mask; + blue_comp = (blue_comp >> 1)&0x001F; + } + else{ + red_comp = (red_comp / ponderation_factor )&0xF800; + green_comp = (green_comp / ponderation_factor )&green_mask; + blue_comp = (blue_comp / ponderation_factor )&0x001F; + } + + /// Debug + //occurence_pond[ponderation_factor] += 1; + + /// --- write pixel --- + *t++ = red_comp+green_comp+blue_comp; + } + else{ + /// --- copy pixel --- + *t++ = (*cur_p); + + /// Debug + //occurence_pond[1] += 1; + } + + /// save number of pixels to interpolate + px_diff_prev_x = px_diff_next_x; + + // ------ next pixel ------ + rat += x_ratio; + } + px_diff_prev_y = px_diff_next_y; + } + /// Debug + /*printf("pond: [%d, %d, %d, %d, %d, %d, %d, %d]\n", occurence_pond[1], occurence_pond[2], occurence_pond[3], + occurence_pond[4], occurence_pond[5], occurence_pond[6], + occurence_pond[7], occurence_pond[8]);*/ +} + + +/// Nearest neighbor with full 2D uniform bilinear (interpolation with missing left, right, up and down pixels) +void flip_NNOptimized_FullBilinear_Uniform(SDL_Surface *virtual_screen, SDL_Surface *hardware_screen, int new_w, int new_h){ + int w1=virtual_screen->w; + int h1=virtual_screen->h; + int w2=new_w; + int h2=new_h; + int y_padding = (RES_HW_SCREEN_VERTICAL-new_h)/2; + //int x_ratio = (int)((w1<<16)/w2) +1; + //int y_ratio = (int)((h1<<16)/h2) +1; + int x_ratio = (int)((w1<<16)/w2); + int y_ratio = (int)((h1<<16)/h2); + int x1, y1; + int px_diff_prev_x = 1; + int px_diff_prev_y = 1; + //int cnt_interp = 0; int cnt_no_interp = 0; + //printf("virtual_screen->w=%d, virtual_screen->w=%d\n", virtual_screen->w, virtual_screen->h); + + /// ---- Compute padding for centering when out of bounds ---- + int x_padding = 0; + if(w2>RES_HW_SCREEN_HORIZONTAL){ + x_padding = (w2-RES_HW_SCREEN_HORIZONTAL)/2 + 1; + } + int x_padding_ratio = x_padding*w1/w2; + + /// ---- Copy and interpolate pixels ---- + for (int i=0;i=RES_HW_SCREEN_VERTICAL){ + continue; + } + + uint16_t* t = (uint16_t*)(hardware_screen->pixels+( (i+y_padding)*((w2>RES_HW_SCREEN_HORIZONTAL)?RES_HW_SCREEN_HORIZONTAL:w2))*sizeof(uint16_t)); + + // ------ current and next y value ------ + y1 = ((i*y_ratio)>>16); + int px_diff_next_y = MAX( (((i+1)*y_ratio)>>16) - y1, 1); + + uint16_t* p = (uint16_t*)(virtual_screen->pixels + (y1*w1 + x_padding_ratio) *sizeof(uint16_t)); + int rat = 0; + for (int j=0;j=RES_HW_SCREEN_HORIZONTAL){ + continue; + } + + // ------ current and next x value ------ + x1 = (rat>>16); + int px_diff_next_x = MAX( ((rat+x_ratio)>>16) - x1, 1); + + // ------ bilinear uniformly weighted -------- + uint32_t red_comp=0, green_comp=0, blue_comp=0, ponderation_factor=0; + uint16_t * cur_p; + int cur_y_offset; + + //printf("\npx_diff_prev_y=%d, px_diff_prev_x=%d, px_diff_next_y=%d, px_diff_next_x=%d, interp_px=", px_diff_prev_y, px_diff_prev_x, px_diff_next_y, px_diff_next_x); + + for(int cur_px_diff_y=-(px_diff_prev_y-1); cur_px_diff_y= h1 || y1 < -cur_px_diff_y){ + continue; + } + cur_y_offset = w1*cur_px_diff_y; + //printf("cur_diff_y=%d-> ", cur_px_diff_y); + + for(int cur_px_diff_x=-(px_diff_prev_x-1); cur_px_diff_x= w1 || x1 < -cur_px_diff_x){ + continue; + } + cur_p = (p+cur_y_offset+x1+cur_px_diff_x); + //printf("{y=%d,x=%d}, ", y1+cur_px_diff_y, x1+cur_px_diff_x); + red_comp += ((*cur_p)&0xF800); + green_comp += ((*cur_p)&0x07E0); + blue_comp += ((*cur_p)&0x001F); + ponderation_factor++; + } + } + //printf("\n"); + + /// ------ Ponderation ------- + red_comp = (red_comp / ponderation_factor )&0xF800; + green_comp = (green_comp / ponderation_factor )&0x07E0; + blue_comp = (blue_comp / ponderation_factor )&0x001F; + *t++ = red_comp+green_comp+blue_comp; + + /// ------ x Interpolation values ------- + px_diff_prev_x = px_diff_next_x; + + // ------ next pixel ------ + rat += x_ratio; + } + + /// ------ y Interpolation values ------- + px_diff_prev_y = px_diff_next_y; + } + //printf("cnt_interp = %d, int cnt_no_interp = %d\n", cnt_interp, cnt_no_interp); +} + + +/// Nearest neighbor with full 2D uniform bilinear (interpolation with missing left, right, up and down pixels) +void flip_NNOptimized_FullBilinear_GaussianWeighted(SDL_Surface *virtual_screen, SDL_Surface *hardware_screen, int new_w, int new_h){ + int w1=virtual_screen->w; + int h1=virtual_screen->h; + int w2=new_w; + int h2=new_h; + //printf("virtual_screen->w=%d, virtual_screen->w=%d\n", virtual_screen->w, virtual_screen->h); + int y_padding = (RES_HW_SCREEN_VERTICAL-new_h)/2; + int x_ratio = (int)((w1<<16)/w2); + int y_ratio = (int)((h1<<16)/h2); + int x1, y1; + int px_diff_prev_x = 1; + int px_diff_prev_y = 1; + //int cnt_interp = 0; int cnt_no_interp = 0; + + /// ---- Compute padding for centering when out of bounds ---- + int x_padding = 0; + if(w2>RES_HW_SCREEN_HORIZONTAL){ + x_padding = (w2-RES_HW_SCREEN_HORIZONTAL)/2 + 1; + } + int x_padding_ratio = x_padding*w1/w2; + + /// ---- Interpolation params ---- + uint32_t max_pix_interpolate = 3; + if(max_pix_interpolate > 3 || max_pix_interpolate<1){ + printf("ERROR cannot interpolate more than 3x3 px in flip_NNOptimized_FullBilinear_GaussianWeighted\n"); + return; + } + + /// ---- Convolutional mask ---- + int mask_weight_5x5[] = {36, 24, 6, 24, 16, 4, 6, 4, 1}; + int mask_weight_3x3[] = {4, 2, 2, 1}; + int mask_weight_1x1[] = {1}; + int *mask_weight; + if(max_pix_interpolate==3){ + mask_weight = mask_weight_5x5; + } + else if(max_pix_interpolate==2){ + mask_weight = mask_weight_3x3; + } + else{ + mask_weight = mask_weight_1x1; + } + + /// ---- Copy and interpolate pixels ---- + for (int i=0;i=RES_HW_SCREEN_VERTICAL){ + continue; + } + + uint16_t* t = (uint16_t*)(hardware_screen->pixels+( (i+y_padding)*((w2>RES_HW_SCREEN_HORIZONTAL)?RES_HW_SCREEN_HORIZONTAL:w2))*sizeof(uint16_t)); + + // ------ current and next y value ------ + y1 = ((i*y_ratio)>>16); + int px_diff_next_y = MIN( MAX( (((i+1)*y_ratio)>>16) - y1, 1), max_pix_interpolate); + + uint16_t* p = (uint16_t*)(virtual_screen->pixels + (y1*w1 + x_padding_ratio) *sizeof(uint16_t)); + int rat = 0; + for (int j=0;j=RES_HW_SCREEN_HORIZONTAL){ + continue; + } + + // ------ current and next x value ------ + x1 = (rat>>16); + int px_diff_next_x = MIN( MAX( ((rat+x_ratio)>>16) - x1, 1), max_pix_interpolate); //we interpolate max "max_pix_interpolate" pix in each dim + + // ------ bilinear uniformly weighted -------- + uint32_t red_comp=0, green_comp=0, blue_comp=0; + int ponderation_factor=0; + uint16_t * cur_p; + int cur_y_offset; + + //printf("\npx_diff_prev_y=%d, px_diff_prev_x=%d, px_diff_next_y=%d, px_diff_next_x=%d, interp_px=", px_diff_prev_y, px_diff_prev_x, px_diff_next_y, px_diff_next_x); + + for(int cur_px_diff_y=-(px_diff_prev_y-1); cur_px_diff_y= h1 || y1 < -cur_px_diff_y){ + continue; + } + cur_y_offset = w1*cur_px_diff_y; + //printf("cur_diff_y=%d-> ", cur_px_diff_y); + + for(int cur_px_diff_x=-(px_diff_prev_x-1); cur_px_diff_x= w1 || x1 < -cur_px_diff_x){ + continue; + } + cur_p = (p+cur_y_offset+x1+cur_px_diff_x); + int weight = mask_weight[ABS(cur_px_diff_y)*max_pix_interpolate+ABS(cur_px_diff_x)]; + + red_comp += ((*cur_p)&0xF800) * weight; + green_comp += ((*cur_p)&0x07E0) * weight; + blue_comp += ((*cur_p)&0x001F) * weight; + ponderation_factor += weight; + } + } + //printf("\n"); + + /// ------ Ponderation ------- + red_comp = (red_comp / ponderation_factor) & 0xF800; + green_comp = (green_comp / ponderation_factor )&0x07E0; + blue_comp = (blue_comp / ponderation_factor) & 0x001F; + *t++ = red_comp+green_comp+blue_comp; + + /// ------ x Interpolation values ------- + px_diff_prev_x = px_diff_next_x; + + // ------ next pixel ------ + rat += x_ratio; + } + + /// ------ y Interpolation values ------- + px_diff_prev_y = px_diff_next_y; + } + //printf("cnt_interp = %d, int cnt_no_interp = %d\n", cnt_interp, cnt_no_interp); +} + + + +/// Interpolation with left, right pixels, pseudo gaussian weighting for downscaling - operations on 16bits +void flip_Downscale_LeftRightGaussianFilter_Optimized(SDL_Surface *src_surface, SDL_Surface *dst_surface, int new_w, int new_h){ + int w1=src_surface->w; + int h1=src_surface->h; + int w2=dst_surface->w; + int h2=dst_surface->h; + //printf("src = %dx%d\n", w1, h1); + int x_ratio = (int)((w1<<16)/w2); + int y_ratio = (int)((h1<<16)/h2); + int y_padding = (RES_HW_SCREEN_VERTICAL-h2)/2; + int x1, y1; + uint16_t *src_screen = (uint16_t *)src_surface->pixels; + uint16_t *dst_screen = (uint16_t *)dst_surface->pixels; + + /// --- Compute padding for centering when out of bounds --- + int x_padding = 0; + if(w2>RES_HW_SCREEN_HORIZONTAL){ + x_padding = (w2-RES_HW_SCREEN_HORIZONTAL)/2 + 1; + } + int x_padding_ratio = x_padding*w1/w2; + + /// --- Interp params --- + int px_diff_prev_x = 0; + int px_diff_next_x = 0; + uint8_t left_px_missing, right_px_missing; + + uint16_t * cur_p; + uint16_t * cur_p_left; + uint16_t * cur_p_right; + + + for (int i=0;i=RES_HW_SCREEN_VERTICAL){ + continue; + } + uint16_t* t = (uint16_t*)(dst_screen + + (i+y_padding)*((w2>RES_HW_SCREEN_HORIZONTAL)?RES_HW_SCREEN_HORIZONTAL:w2) ); + // ------ current and next y value ------ + y1 = ((i*y_ratio)>>16); + uint16_t* p = (uint16_t*)(src_screen + (y1*w1+x_padding_ratio) ); + int rat = 0; + + for (int j=0;j=RES_HW_SCREEN_HORIZONTAL){ + continue; + } + + // ------ current x value ------ + x1 = (rat>>16); + px_diff_next_x = ((rat+x_ratio)>>16) - x1; + + //printf("x1=%d, px_diff_prev_x=%d, px_diff_next_x=%d\n", x1, px_diff_prev_x, px_diff_next_x); + + // ------ adapted bilinear with 3x3 gaussian blur ------- + cur_p = p+x1; + if(px_diff_prev_x > 1 || px_diff_next_x > 1 ){ + + left_px_missing = (px_diff_prev_x > 1 && x1>0); + right_px_missing = (px_diff_next_x > 1 && x1+1w; + int h1=src_surface->h; + int w2=dst_surface->w; + int h2=dst_surface->h; + + if(w1!=320){ + printf("src_surface->w (%d) != 320\n", src_surface->w); + return; + } + + //printf("src = %dx%d\n", w1, h1); + int y_ratio = (int)((h1<<16)/h2); + int y_padding = (RES_HW_SCREEN_VERTICAL-h2)/2; + int y1; + uint16_t *src_screen = (uint16_t *)src_surface->pixels; + uint16_t *dst_screen = (uint16_t *)dst_surface->pixels; + + /* Interpolation */ + for (int i=0;i=RES_HW_SCREEN_VERTICAL){ + continue; + } + uint16_t* t = (uint16_t*)(dst_screen + + (i+y_padding)*((w2>RES_HW_SCREEN_HORIZONTAL)?RES_HW_SCREEN_HORIZONTAL:w2) ); + + // ------ current and next y value ------ + y1 = ((i*y_ratio)>>16); + uint16_t* p = (uint16_t*)(src_screen + (y1*w1) ); + + for (int j=0;j<80;j++) + { + /* Horizontaly: + * Before(4): + * (a)(b)(c)(d) + * After(3): + * (aaab)(bc)(cddd) + */ + uint16_t _a = *(p ); + uint16_t _b = *(p + 1); + uint16_t _c = *(p + 2); + uint16_t _d = *(p + 3); + *(t ) = Weight3_1( _a, _b ); + *(t + 1) = Weight1_1( _b, _c ); + *(t + 2) = Weight1_3( _c, _d ); + + // ------ next dst pixel ------ + t+=3; + p+=4; + } + } +} + + + + + +/// Interpolation with left, right pixels, pseudo gaussian weighting for downscaling - operations on 16bits +void flip_Downscale_OptimizedWidth320_mergeUpDown(SDL_Surface *src_surface, SDL_Surface *dst_surface, int new_w, int new_h){ + int w1=src_surface->w; + int h1=src_surface->h; + int w2=dst_surface->w; + int h2=dst_surface->h; + + if(w1!=320){ + printf("src_surface->w (%d) != 320\n", src_surface->w); + return; + } + + //printf("src = %dx%d\n", w1, h1); + int y_ratio = (int)((h1<<16)/h2); + int y_padding = (RES_HW_SCREEN_VERTICAL-h2)/2; + int y1=0, prev_y1=-1, prev_prev_y1=-2; + uint16_t *src_screen = (uint16_t *)src_surface->pixels; + uint16_t *dst_screen = (uint16_t *)dst_surface->pixels; + + uint16_t *prev_t, *t_init=dst_screen; + + /* Interpolation */ + for (int i=0;i=RES_HW_SCREEN_VERTICAL){ + continue; + } + + prev_t = t_init; + t_init = (uint16_t*)(dst_screen + + (i+y_padding)*((w2>RES_HW_SCREEN_HORIZONTAL)?RES_HW_SCREEN_HORIZONTAL:w2) ); + uint16_t *t = t_init; + + // ------ current and next y value ------ + prev_prev_y1 = prev_y1; + prev_y1 = y1; + y1 = ((i*y_ratio)>>16); + + uint16_t* p = (uint16_t*)(src_screen + (y1*w1) ); + + for (int j=0;j<80;j++) + { + /* Horizontaly: + * Before(4): + * (a)(b)(c)(d) + * After(3): + * (aaab)(bc)(cddd) + */ + uint16_t _a = *(p ); + uint16_t _b = *(p + 1); + uint16_t _c = *(p + 2); + uint16_t _d = *(p + 3); + *(t ) = Weight3_1( _a, _b ); + *(t + 1) = Weight1_1( _b, _c ); + *(t + 2) = Weight1_3( _c, _d ); + + if(prev_y1 == prev_prev_y1 && y1 != prev_y1){ + //printf("we are here %d\n", ++count); + *(prev_t ) = Weight1_1(*(t ), *(prev_t )); + *(prev_t + 1) = Weight1_1(*(t + 1), *(prev_t + 1)); + *(prev_t + 2) = Weight1_1(*(t + 2), *(prev_t + 2)); + } + + + // ------ next dst pixel ------ + t+=3; + prev_t+=3; + p+=4; + } + } +} + +void SDL_Rotate_270(SDL_Surface * hw_surface, SDL_Surface * virtual_hw_surface){ + int i, j; + uint16_t *source_pixels = (uint16_t*) virtual_hw_surface->pixels; + uint16_t *dest_pixels = (uint16_t*) hw_surface->pixels; + + /// --- Checking for right pixel format --- + //printf("Source bpb = %d, Dest bpb = %d\n", virtual_hw_surface->format->BitsPerPixel, hw_surface->format->BitsPerPixel); + if(virtual_hw_surface->format->BitsPerPixel != 16){ + printf("Error in SDL_FastBlit, Wrong virtual_hw_surface pixel format: %d bpb, expected: 16 bpb\n", virtual_hw_surface->format->BitsPerPixel); + return; + } + if(hw_surface->format->BitsPerPixel != 16){ + printf("Error in SDL_FastBlit, Wrong hw_surface pixel format: %d bpb, expected: 16 bpb\n", hw_surface->format->BitsPerPixel); + return; + } + + /// --- Checking if same dimensions --- + if(hw_surface->w != virtual_hw_surface->w || hw_surface->h != virtual_hw_surface->h){ + printf("Error in SDL_FastBlit, hw_surface (%dx%d) and virtual_hw_surface (%dx%d) have different dimensions\n", + hw_surface->w, hw_surface->h, virtual_hw_surface->w, virtual_hw_surface->h); + return; + } + + /// --- Pixel copy and rotation (270) --- + uint16_t *cur_p_src, *cur_p_dst; + for(i=0; ih; i++){ + for(j=0; jw; j++){ + cur_p_src = source_pixels + i*virtual_hw_surface->w + j; + cur_p_dst = dest_pixels + (hw_surface->h-1-j)*hw_surface->w + i; + *cur_p_dst = *cur_p_src; + } + } +} + + + + + + + + + static int clear_buf_cnt, clear_stat_cnt; void plat_video_flip(void) @@ -135,7 +1527,7 @@ void plat_video_flip(void) else if (plat_sdl_gl_active) { gl_flip(shadow_fb, g_screen_ppitch, g_screen_height); } - else { + /*else { if (SDL_MUSTLOCK(plat_sdl_screen)) { SDL_UnlockSurface(plat_sdl_screen); SDL_Flip(plat_sdl_screen); @@ -148,6 +1540,99 @@ void plat_video_flip(void) memset(g_screen_ptr, 0, plat_sdl_screen->w*plat_sdl_screen->h * 2); clear_buf_cnt--; } + }*/ + else { + if (SDL_MUSTLOCK(plat_sdl_screen)) + SDL_UnlockSurface(plat_sdl_screen); + + /* Surface with game data */ + SDL_Surface *game_surface; + + /* Sega Master System -> 256*192 res in 320*240 surface */ + if (PicoIn.AHW & PAHW_SMS){ + + /* Copy sms game pixels */ + int offset_y = (plat_sdl_screen->h - sms_game_screen->h)/2; + int offset_x = (plat_sdl_screen->w - sms_game_screen->w)/2 + 6; + int y; + for(y=0; y<192; y++){ + memcpy((uint16_t*)sms_game_screen->pixels + sms_game_screen->w*y, + (uint16_t*)plat_sdl_screen->pixels + plat_sdl_screen->w*(y+offset_y) + offset_x, + sms_game_screen->w*sizeof(uint16_t)); + } + + game_surface = sms_game_screen; + } + else{ + game_surface = plat_sdl_screen; + } + + + /// --------------Optimized Flip depending on aspect ratio ------------- + static int prev_aspect_ratio; + if(prev_aspect_ratio != aspect_ratio || need_screen_cleared){ + //printf("aspect ratio changed: %d\n", aspect_ratio); + clear_screen(virtual_hw_screen, 0); + prev_aspect_ratio = aspect_ratio; + need_screen_cleared = 0; + } + + switch(aspect_ratio){ + case ASPECT_RATIOS_TYPE_STRETCHED: + if(game_surface->w == 320 && game_surface->h < RES_HW_SCREEN_VERTICAL){ + flip_Downscale_OptimizedWidth320_mergeUpDown(game_surface, virtual_hw_screen, + RES_HW_SCREEN_HORIZONTAL, RES_HW_SCREEN_VERTICAL); + } + else if(game_surface->w == 320){ + flip_Downscale_LeftRightGaussianFilter_OptimizedWidth320(game_surface, virtual_hw_screen, + RES_HW_SCREEN_HORIZONTAL, RES_HW_SCREEN_VERTICAL); + } + else{ + flip_Downscale_LeftRightGaussianFilter_Optimized(game_surface, virtual_hw_screen, + RES_HW_SCREEN_HORIZONTAL, RES_HW_SCREEN_VERTICAL); + /*flip_Downscale_LeftRightGaussianFilter(game_surface, hw_screen, + RES_HW_SCREEN_HORIZONTAL, RES_HW_SCREEN_VERTICAL);*/ + } + break; + case ASPECT_RATIOS_TYPE_MANUAL: + ;uint32_t h_scaled = MIN(game_surface->h*RES_HW_SCREEN_HORIZONTAL/game_surface->w, + RES_HW_SCREEN_VERTICAL); + uint32_t h_zoomed = MIN(h_scaled + aspect_ratio_factor_percent*(RES_HW_SCREEN_VERTICAL - h_scaled)/100, + RES_HW_SCREEN_VERTICAL); + flip_NNOptimized_LeftRightUpDownBilinear_Optimized8(game_surface, virtual_hw_screen, + MAX(game_surface->w*h_zoomed/game_surface->h, RES_HW_SCREEN_HORIZONTAL), + MIN(h_zoomed, RES_HW_SCREEN_VERTICAL)); + break; + case ASPECT_RATIOS_TYPE_CROPPED: + flip_NNOptimized_AllowOutOfScreen(game_surface, virtual_hw_screen, + MAX(game_surface->w*RES_HW_SCREEN_VERTICAL/game_surface->h, RES_HW_SCREEN_HORIZONTAL), + RES_HW_SCREEN_VERTICAL); + break; + case ASPECT_RATIOS_TYPE_SCALED: + flip_NNOptimized_LeftRightUpDownBilinear_Optimized8(game_surface, virtual_hw_screen, + RES_HW_SCREEN_HORIZONTAL, + MIN(game_surface->h*RES_HW_SCREEN_HORIZONTAL/game_surface->w, RES_HW_SCREEN_VERTICAL)); + break; + default: + printf("Wrong aspect ratio value: %d\n", aspect_ratio); + aspect_ratio = ASPECT_RATIOS_TYPE_STRETCHED; + flip_NNOptimized_LeftRightUpDownBilinear_Optimized8(game_surface, virtual_hw_screen, + RES_HW_SCREEN_HORIZONTAL, RES_HW_SCREEN_VERTICAL); + break; + } + + + // Rotate + //SDL_Rotate_270(hw_screen, virtual_hw_screen); + //SDL_BlitSurface(virtual_hw_screen, NULL, hw_screen, NULL); + memcpy(hw_screen->pixels, virtual_hw_screen->pixels, hw_screen->w*hw_screen->h*sizeof(uint16_t)); + + /// --- Real Flip --- + SDL_Flip(hw_screen); + + + g_screen_ptr = plat_sdl_screen->pixels; + PicoDrawSetOutBuf(g_screen_ptr, g_screen_ppitch * 2); } if (clear_stat_cnt) { unsigned short *d = (unsigned short *)g_screen_ptr + g_screen_ppitch * g_screen_height; @@ -216,7 +1701,12 @@ void plat_video_menu_end(void) else { if (SDL_MUSTLOCK(plat_sdl_screen)) SDL_UnlockSurface(plat_sdl_screen); - SDL_Flip(plat_sdl_screen); + flip_NNOptimized_LeftAndRightBilinear(plat_sdl_screen, virtual_hw_screen, RES_HW_SCREEN_HORIZONTAL, RES_HW_SCREEN_VERTICAL); + + memcpy(hw_screen->pixels, virtual_hw_screen->pixels, hw_screen->w*hw_screen->h*sizeof(uint16_t)); + SDL_Flip(hw_screen); + //SDL_Rotate_270(hw_screen, virtual_hw_screen); + //SDL_Flip(plat_sdl_screen); } g_menuscreen_ptr = NULL; } @@ -255,7 +1745,8 @@ void plat_early_init(void) static void plat_sdl_quit(void) { // for now.. - exit(1); + engineState = PGS_Quit; + //exit(1); } void plat_init(void) @@ -272,10 +1763,35 @@ void plat_init(void) plat_target.vout_method = 0; #endif + if(TTF_Init()) + { + fprintf(stderr, "Error TTF_Init: %s\n", TTF_GetError()); + exit(EXIT_FAILURE); + } + + hw_screen = SDL_SetVideoMode(RES_HW_SCREEN_HORIZONTAL, RES_HW_SCREEN_VERTICAL, 16, SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF); + if(hw_screen == NULL) + { + fprintf(stderr, "Error SDL_SetVideoMode: %s\n", SDL_GetError()); + exit(EXIT_FAILURE); + } + plat_sdl_quit_cb = plat_sdl_quit; SDL_WM_SetCaption("PicoDrive " VERSION, NULL); + virtual_hw_screen = SDL_CreateRGBSurface(SDL_SWSURFACE, + RES_HW_SCREEN_HORIZONTAL, RES_HW_SCREEN_VERTICAL, 16, 0xFFFF, 0xFFFF, 0xFFFF, 0); + if (virtual_hw_screen == NULL) { + fprintf(stderr, "virtual_hw_screen failed: %s\n", SDL_GetError()); + } + + sms_game_screen = SDL_CreateRGBSurface(SDL_SWSURFACE, + 256, 192, 16, 0xFFFF, 0xFFFF, 0xFFFF, 0); + if (sms_game_screen == NULL) { + fprintf(stderr, "sms_game_screen failed: %s\n", SDL_GetError()); + } + g_menuscreen_w = plat_sdl_screen->w; g_menuscreen_h = plat_sdl_screen->h; g_menuscreen_pp = g_menuscreen_w; @@ -300,17 +1816,33 @@ void plat_init(void) in_sdl_platform_data.kmap_size = in_sdl_key_map_sz, in_sdl_platform_data.jmap_size = in_sdl_joy_map_sz, in_sdl_platform_data.key_names = *in_sdl_key_names, - in_sdl_init(&in_sdl_platform_data, plat_sdl_event_handler); - in_probe(); + /** Done later depending on SMS or genesis */ + /*in_sdl_init(&in_sdl_platform_data, plat_sdl_event_handler); + in_probe();*/ + init_menu_SDL(); bgr_to_uyvy_init(); } +void plat_set_sms_input(void){ + in_sdl_init(&in_sdl_platform_data_SMS, plat_sdl_event_handler); + in_probe(); +} + +void plat_set_genesis_input(void){ + in_sdl_init(&in_sdl_platform_data, plat_sdl_event_handler); + in_probe(); +} + void plat_finish(void) { + SDL_FreeSurface(virtual_hw_screen); + SDL_FreeSurface(sms_game_screen); + deinit_menu_SDL(); free(shadow_fb); shadow_fb = NULL; free(g_menubg_ptr); g_menubg_ptr = NULL; + TTF_Quit(); plat_sdl_finish(); } diff --git a/platform/common/plat_sdl.h b/platform/common/plat_sdl.h index ea8680fe..88fd9f15 100644 --- a/platform/common/plat_sdl.h +++ b/platform/common/plat_sdl.h @@ -1,5 +1,6 @@ extern const struct in_default_bind in_sdl_defbinds[]; +extern const struct in_default_bind in_sdl_defbinds_SMS[]; extern const struct menu_keymap in_sdl_key_map[]; extern const int in_sdl_key_map_sz; extern const struct menu_keymap in_sdl_joy_map[];