diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..572c3c9ccd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# EditorConfig: http://EditorConfig.org + +# Top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +# 3 space indentation +[*.{c,h,js,css,html}] +indent_style = space +indent_size = 3 + +# Tab indentation +[Makefile*] +indent_style = tab diff --git a/.empty b/.empty deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/.travis.yml b/.travis.yml index c519643cca..be5eeb2880 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,9 @@ script: - ./configure - make +env: + global: + - secure: "qc91ReC3OlzSh2gFaSH6TYzC2qIQvgA2AZff6J13eaH8xijAhuMzttZ0rMQJ0DWCIhPeUb0kIzVyaGoe4MwPALzpw1C1AznIWiZJ53HN+hWCOcS/af7YVPk6HPySnwqrS+Wv3AIIvIKFV2mxv21F/JbT/N+pArlRrp904Xj+KPo=" addons: apt: packages: @@ -35,3 +38,11 @@ addons: - libsdl-image1.2-dev - libsdl-mixer1.2-dev - libsdl-ttf2.0-dev + coverity_scan: + project: + name: "RetroArch" + description: "RetroArch is the official reference frontend for the libretro API." + notification_email: libretro@gmail.com + build_command_prepend: "./configure; make clean" + build_command: "make" + branch_pattern: coverity_scan diff --git a/Makefile.common b/Makefile.common index 25286b2e61..409cc4e837 100644 --- a/Makefile.common +++ b/Makefile.common @@ -5,11 +5,11 @@ ifeq ($(HAVE_GL_CONTEXT),) HAVE_GL_CONTEXT=1 endif - ifeq ($(HAVE_GLES), 1) + ifeq ($(HAVE_OPENGLES), 1) HAVE_GL_CONTEXT=1 endif - ifeq ($(HAVE_GLES3), 1) + ifeq ($(HAVE_OPENGLES3), 1) HAVE_GL_CONTEXT=1 endif endif @@ -132,6 +132,8 @@ OBJ += frontend/frontend.o \ ui/drivers/null/ui_null_application.o \ core_impl.o \ retroarch.o \ + dirs.o \ + paths.o \ input/input_keyboard.o \ command.o \ msg_hash.o \ @@ -140,11 +142,11 @@ OBJ += frontend/frontend.o \ libretro-common/algorithms/mismatch.o \ libretro-common/queues/task_queue.o \ tasks/task_content.o \ - tasks/task_save_ram.o \ - tasks/task_save_state.o \ + tasks/task_save.o \ tasks/task_file_transfer.o \ tasks/task_image.o \ libretro-common/encodings/encoding_utf.o \ + libretro-common/encodings/encoding_crc32.o \ libretro-common/lists/file_list.o \ libretro-common/lists/dir_list.o \ libretro-common/file/retro_dirent.o \ @@ -505,12 +507,16 @@ ifeq ($(HAVE_MENU_COMMON), 1) OBJ += menu/menu_driver.o \ menu/menu_content.o \ menu/menu_input.o \ - menu/menu_entry.o \ + menu/menu_event.o \ menu/menu_entries.o \ menu/menu_navigation.o \ menu/menu_setting.o \ menu/menu_shader.o \ - menu/widgets/menu_popup.o \ + menu/widgets/menu_dialog.o \ + menu/widgets/menu_input_dialog.o \ + menu/widgets/menu_input_bind_dialog.o \ + menu/widgets/menu_entry.o \ + menu/widgets/menu_list.o \ menu/menu_cbs.o \ menu/cbs/menu_cbs_ok.o \ menu/cbs/menu_cbs_cancel.o \ @@ -596,7 +602,7 @@ ifeq ($(HAVE_X11), 1) ifeq ($(HAVE_XCB),1) LIBS += -lX11-xcb endif -ifneq ($(HAVE_GLES), 1) +ifneq ($(HAVE_OPENGLES), 1) OBJ += gfx/drivers_context/x_ctx.o endif endif @@ -612,6 +618,11 @@ ifeq ($(HAVE_XKBCOMMON), 1) LIBS += $(XKBCOMMON_LIBS) endif +ifeq ($(HAVE_DBUS), 1) + LIBS += $(DBUS_LIBS) + CFLAGS += $(DBUS_CFLAGS) +endif + ifeq ($(HAVE_UDEV), 1) DEFINES += $(UDEV_CFLAGS) LIBS += $(UDEV_LIBS) @@ -679,6 +690,7 @@ OBJ += gfx/video_context_driver.o \ gfx/video_state_tracker.o \ libretro-common/gfx/math/vector_2.o \ libretro-common/gfx/math/vector_3.o \ + libretro-common/gfx/math/vector_4.o \ libretro-common/gfx/math/matrix_4x4.o \ libretro-common/gfx/math/matrix_3x3.o @@ -743,7 +755,7 @@ ifeq ($(HAVE_GL_CONTEXT), 1) endif ifeq ($(HAVE_FFMPEG), 1) ifneq ($(C89_BUILD), 1) -ifneq ($(HAVE_GLES), 1) +ifneq ($(HAVE_OPENGLES), 1) OBJ += cores/libretro-ffmpeg/fft/fft.o DEFINES += -Ideps -DHAVE_GL_FFT NEED_CXX_LINKER=1 @@ -751,10 +763,10 @@ endif endif endif - ifeq ($(HAVE_GLES), 1) + ifeq ($(HAVE_OPENGLES), 1) LIBS += $(GLES_LIBS) DEFINES += $(GLES_CFLAGS) -DHAVE_OPENGLES - ifeq ($(HAVE_GLES3), 1) + ifeq ($(HAVE_OPENGLES3), 1) DEFINES += -DHAVE_OPENGLES3 else DEFINES += -DHAVE_OPENGLES2 @@ -959,6 +971,8 @@ endif # Compression/Archive +OBJ += libretro-common/file/archive_file.o + ifeq ($(HAVE_7ZIP),1) CFLAGS += -I./deps/7zip HAVE_COMPRESSION = 1 @@ -977,14 +991,13 @@ ifeq ($(HAVE_7ZIP),1) deps/7zip/7zCrc.o \ deps/7zip/Lzma2Dec.o \ deps/7zip/7zBuf.o - OBJ += $(7ZOBJ) + OBJ += libretro-common/file/archive_file_7z.o \ + $(7ZOBJ) endif ifeq ($(HAVE_ZLIB), 1) - OBJ += libretro-common/file/archive_file.o \ - libretro-common/file/archive_file_zlib.o \ - tasks/task_decompress.o + OBJ += libretro-common/file/archive_file_zlib.o OBJ += $(ZLIB_OBJS) DEFINES += -DHAVE_ZLIB HAVE_COMPRESSION = 1 @@ -1133,8 +1146,10 @@ ifeq ($(HAVE_FFMPEG), 1) DEFINES += -Wno-deprecated-declarations -DHAVE_FFMPEG -Iffmpeg endif + ifeq ($(HAVE_COMPRESSION), 1) DEFINES += -DHAVE_COMPRESSION + OBJ += tasks/task_decompress.o endif #ifeq ($(HAVE_DIRECTX), 1) diff --git a/Makefile.ctr b/Makefile.ctr index fdaa48ea28..1539ba72aa 100644 --- a/Makefile.ctr +++ b/Makefile.ctr @@ -46,6 +46,7 @@ else CFLAGS += -DHAVE_COMPRESSION OBJS += libretro-common/file/archive_file.o OBJS += libretro-common/file/archive_file_zlib.o + OBJS += libretro-common/file/archive_file_7z.o OBJS += libretro-common/encodings/encoding_utf.o OBJS += verbosity.o OBJS += performance.o diff --git a/Makefile.emscripten b/Makefile.emscripten index 7ff0221912..e3bc40f6eb 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -9,9 +9,8 @@ DEFINES := -DRARCH_INTERNAL -DHAVE_OVERLAY -DHAVE_MAIN DEFINES += -DHAVE_OPENGL -DHAVE_OPENGLES -DHAVE_OPENGLES2 -DHAVE_EGL -DHAVE_OVERLAY -DHAVE_GLSL -DHAVE_FILTERS_BUILTIN HAVE_EGL = 1 -HAVE_OPENGL = 1 -HAVE_GLES = 1 -HAVE_RJPEG = 1 +HAVE_OPENGLES = 1 +HAVE_RJPEG = 0 HAVE_RPNG = 1 HAVE_EMSCRIPTEN = 1 HAVE_RGUI = 1 @@ -84,7 +83,7 @@ else CFLAGS += -O2 endif -CFLAGS += -DHAVE_RPNG -DHAVE_RJPEG -Wall -Wno-unused-result -Wno-unused-variable -I. -Ilibretro-common/include -std=gnu99 -s USE_ZLIB=1 \ +CFLAGS += -DHAVE_RPNG -Wall -Wno-unused-result -Wno-unused-variable -I. -Ilibretro-common/include -std=gnu99 -s USE_ZLIB=1 \ -s EXPORTED_FUNCTIONS="['_main', '_malloc', '_cmd_savefiles', '_cmd_save_state', '_cmd_take_screenshot']" all: $(TARGET) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 684e098f18..3601e1dbdf 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -796,8 +796,10 @@ bool audio_driver_find_driver(void) void audio_driver_deinit_resampler(void) { - rarch_resampler_freep(&audio_driver_resampler, - &audio_driver_resampler_data); + if (audio_driver_resampler && audio_driver_resampler_data) + audio_driver_resampler->free(audio_driver_resampler_data); + audio_driver_resampler = NULL; + audio_driver_resampler_data = NULL; } bool audio_driver_free_devices_list(void) @@ -852,8 +854,8 @@ void audio_driver_process_resampler(void *data) struct resampler_data *resampler = (struct resampler_data*)data; performance_counter_init(&resampler_proc, "resampler_proc"); performance_counter_start(&resampler_proc); - rarch_resampler_process(audio_driver_resampler, - audio_driver_resampler_data, resampler); + + audio_driver_resampler->process(audio_driver_resampler_data, resampler); performance_counter_stop(&resampler_proc); } diff --git a/audio/audio_resampler_driver.c b/audio/audio_resampler_driver.c index 0d83acf0c2..fdc32ab1e8 100644 --- a/audio/audio_resampler_driver.c +++ b/audio/audio_resampler_driver.c @@ -19,10 +19,6 @@ #include #include -#ifdef HAVE_CONFIG_H -#include "../config.h" -#endif - #include "audio_resampler_driver.h" #include "../config_file_userdata.h" #include "../performance_counters.h" diff --git a/audio/audio_resampler_driver.h b/audio/audio_resampler_driver.h index 0366d5f872..7f3f995930 100644 --- a/audio/audio_resampler_driver.h +++ b/audio/audio_resampler_driver.h @@ -181,20 +181,6 @@ const char *audio_resampler_driver_find_ident(int index); bool rarch_resampler_realloc(void **re, const rarch_resampler_t **backend, const char *ident, double bw_ratio); -/* Convenience macros. - * freep makes sure to set handles to NULL to avoid double-free - * in rarch_resampler_realloc. */ -#define rarch_resampler_freep(backend, handle) do { \ - if (*(backend) && *(handle)) \ - (*backend)->free(*handle); \ - *backend = NULL; \ - *handle = NULL; \ -} while(0) - -#define rarch_resampler_process(backend, handle, data) do { \ - (backend)->process(handle, data); \ -} while(0) - RETRO_END_DECLS #endif diff --git a/audio/test/main.c b/audio/test/main.c index 047095c62f..a322632c53 100644 --- a/audio/test/main.c +++ b/audio/test/main.c @@ -87,7 +87,7 @@ int main(int argc, char *argv[]) data.input_frames = sizeof(input_f) / (2 * sizeof(float)); data.ratio = ratio * rate_mod; - rarch_resampler_process(resampler, re, &data); + resampler->process(re, &data); output_samples = data.output_frames * 2; @@ -97,6 +97,9 @@ int main(int argc, char *argv[]) break; } - rarch_resampler_freep(&resampler, &re); + if (resampler && re) + resampler->free(re); + resampler = NULL; + re = NULL; } diff --git a/audio/test/snr.c b/audio/test/snr.c index a61e6feb14..e3e284a40b 100644 --- a/audio/test/snr.c +++ b/audio/test/snr.c @@ -327,7 +327,7 @@ int main(int argc, char *argv[]) data.input_frames = in_rate * 2; data.ratio = ratio; - rarch_resampler_process(resampler, re, &data); + resampler->process(re, &data); /* We generate 2 seconds worth of audio, however, * only the last second is considered so phase has stabilized. */ @@ -346,7 +346,10 @@ int main(int argc, char *argv[]) res.alias_freq[2] / (float)in_rate, res.alias_power[2]); } - rarch_resampler_freep(&resampler, &re); + if (resampler && re) + resampler->free(re); + resampler = NULL; + re = NULL; free(input); free(output); diff --git a/command.c b/command.c index 3af331896b..f5f826b54e 100644 --- a/command.c +++ b/command.c @@ -48,7 +48,7 @@ #include "menu/menu_content.h" #include "menu/menu_display.h" #include "menu/menu_shader.h" -#include "menu/widgets/menu_popup.h" +#include "menu/widgets/menu_dialog.h" #endif #ifdef HAVE_NETPLAY @@ -74,6 +74,7 @@ #include "dynamic.h" #include "content.h" #include "movie.h" +#include "paths.h" #include "msg_hash.h" #include "retroarch.h" #include "managers/cheat_manager.h" @@ -989,8 +990,8 @@ static bool command_event_disk_control_append_image(const char *path) * If we actually use append_image, we assume that we * started out in a single disk case, and that this way * of doing it makes the most sense. */ - retroarch_set_pathnames(path); - retroarch_fill_pathnames(); + path_set_names(path); + path_fill_names(); } command_event(CMD_EVENT_AUTOSAVE_INIT, NULL); @@ -1202,22 +1203,6 @@ static void command_event_init_cheats(void) /* TODO/FIXME - add some stuff here. */ } -static bool event_load_save_files(void) -{ - unsigned i; - global_t *global = global_get_ptr(); - - if (!global) - return false; - if (!global->savefiles || global->sram.load_disable) - return false; - - for (i = 0; i < global->savefiles->size; i++) - content_load_ram_file(i); - - return true; -} - static void command_event_load_auto_state(void) { bool ret; @@ -1327,7 +1312,7 @@ static bool event_init_content(void) return true; if (!content_does_not_need_content()) - retroarch_fill_pathnames(); + path_fill_names(); if (!content_init()) return false; @@ -1479,14 +1464,12 @@ static bool command_event_save_core_config(void) bool found_path = false; bool overrides_active = false; settings_t *settings = config_get_ptr(); - global_t *global = global_get_ptr(); - *config_dir = '\0'; if (!string_is_empty(settings->directory.menu_config)) strlcpy(config_dir, settings->directory.menu_config, sizeof(config_dir)); - else if (!string_is_empty(global->path.config)) /* Fallback */ - fill_pathname_basedir(config_dir, global->path.config, + else if (!path_is_config_empty()) /* Fallback */ + fill_pathname_basedir(config_dir, path_get_config(), sizeof(config_dir)); else { @@ -1496,8 +1479,7 @@ static bool command_event_save_core_config(void) } /* Infer file name based on libretro core. */ - if (!string_is_empty(config_get_active_core_path()) - && path_file_exists(config_get_active_core_path())) + if (!string_is_empty(path_get_core()) && path_file_exists(path_get_core())) { unsigned i; RARCH_LOG("%s\n", msg_hash_to_str(MSG_USING_CORE_NAME_FOR_NEW_CONFIG)); @@ -1509,7 +1491,7 @@ static bool command_event_save_core_config(void) fill_pathname_base_noext( config_name, - config_get_active_core_path(), + path_get_core(), sizeof(config_name)); fill_pathname_join(config_path, config_dir, config_name, @@ -1556,8 +1538,7 @@ static bool command_event_save_core_config(void) if ((ret = config_save_file(config_path))) { - strlcpy(global->path.config, config_path, - sizeof(global->path.config)); + path_set_config(config_path); snprintf(msg, sizeof(msg), "%s \"%s\".", msg_hash_to_str(MSG_SAVED_NEW_CONFIG_TO), config_path); @@ -1589,17 +1570,16 @@ static bool command_event_save_core_config(void) **/ void command_event_save_current_config(int override_type) { - settings_t *settings = config_get_ptr(); - global_t *global = global_get_ptr(); + char msg[128] = {0}; if (!override_type) { + settings_t *settings = config_get_ptr(); - if (settings->config_save_on_exit && !string_is_empty(global->path.config)) + if (settings->config_save_on_exit && !path_is_config_empty()) { bool ret = false; - char msg[128] = {0}; - const char *config_path = config_get_active_path(); + const char *config_path = path_get_config(); /* Save last core-specific config to the default config location, * needed on consoles for core switching and reusing last good @@ -1614,41 +1594,38 @@ void command_event_save_current_config(int override_type) { snprintf(msg, sizeof(msg), "%s \"%s\".", msg_hash_to_str(MSG_SAVED_NEW_CONFIG_TO), - global->path.config); + path_get_config()); RARCH_LOG("%s\n", msg); } else { snprintf(msg, sizeof(msg), "%s \"%s\".", msg_hash_to_str(MSG_FAILED_SAVING_CONFIG_TO), - global->path.config); + path_get_config()); RARCH_ERR("%s\n", msg); } - - runloop_msg_queue_push(msg, 1, 180, true); } } else { - bool ret = false; - char msg[128] = {0}; - - ret = config_save_overrides(override_type); - - if (ret) + if (config_save_overrides(override_type)) { snprintf(msg, sizeof(msg), "Overrides saved successfully"); RARCH_LOG("[overrides] %s\n", msg); + + /* set overrides to active so the original config can be + restored after closing content */ + runloop_ctl(RUNLOOP_CTL_SET_OVERRIDES_ACTIVE, NULL); } else { snprintf(msg, sizeof(msg), "Error saving overrides"); RARCH_ERR("[overrides] %s\n", msg); } - - runloop_msg_queue_push(msg, 1, 180, true); - return; } + + if (!string_is_empty(msg)) + runloop_msg_queue_push(msg, 1, 180, true); } /** @@ -1720,6 +1697,10 @@ static void command_event_load_state(const char *path, char *s, size_t len) return; } +#ifdef HAVE_NETPLAY + netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, NULL); +#endif + if (settings->state_slot < 0) snprintf(s, len, "%s #-1 (auto).", msg_hash_to_str(MSG_LOADED_STATE_FROM_SLOT)); @@ -1747,6 +1728,10 @@ static void command_event_undo_load_state(char *s, size_t len) return; } +#ifdef HAVE_NETPLAY + netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, NULL); +#endif + strlcpy(s, msg_hash_to_str(MSG_UNDID_LOAD_STATE), len); } @@ -1801,7 +1786,7 @@ void handle_quit_event() settings_t *settings = config_get_ptr(); #ifdef HAVE_MENU if (settings && settings->confirm_on_exit && - menu_popup_is_active()) + menu_dialog_is_active()) return; #endif @@ -1884,17 +1869,14 @@ bool command_event(enum event_command cmd, void *data) core_info_ctx_find_t info_find; #if defined(HAVE_DYNAMIC) - if (string_is_empty(config_get_active_core_path())) + if (string_is_empty(path_get_core())) return false; - +#endif libretro_get_system_info( - config_get_active_core_path(), + path_get_core(), system, ptr); -#else - libretro_get_system_info_static(system, ptr); -#endif - info_find.path = config_get_active_core_path(); + info_find.path = path_get_core(); if (!core_info_load(&info_find)) { @@ -1918,11 +1900,6 @@ bool command_event(enum event_command cmd, void *data) if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) return false; -#ifdef HAVE_NETPLAY - if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) - return false; -#endif - #ifdef HAVE_CHEEVOS if (settings->cheevos.hardcore_mode_enable) return false; @@ -2026,9 +2003,9 @@ bool command_event(enum event_command cmd, void *data) case CMD_EVENT_QUIT: #ifdef HAVE_MENU if (settings && settings->confirm_on_exit && - !menu_popup_is_active() && !runloop_is_quit_confirm()) + !menu_dialog_is_active() && !runloop_is_quit_confirm()) { - menu_popup_show_message(MENU_POPUP_QUIT_CONFIRM, MENU_ENUM_LABEL_CONFIRM_ON_EXIT); + menu_dialog_show_message(MENU_DIALOG_QUIT_CONFIRM, MENU_ENUM_LABEL_CONFIRM_ON_EXIT); break; } #endif @@ -2442,45 +2419,6 @@ bool command_event(enum event_command cmd, void *data) if (!runloop_ctl(RUNLOOP_CTL_SHADER_DIR_INIT, NULL)) return false; break; - case CMD_EVENT_SAVEFILES: - { - global_t *global = global_get_ptr(); - if (!global->savefiles || !global->sram.use) - return false; - - for (i = 0; i < global->savefiles->size; i++) - content_save_ram_file(i); - } - return true; - case CMD_EVENT_SAVEFILES_DEINIT: - { - global_t *global = global_get_ptr(); - if (!global) - break; - - if (global->savefiles) - string_list_free(global->savefiles); - global->savefiles = NULL; - } - break; - case CMD_EVENT_SAVEFILES_INIT: - { - global_t *global = global_get_ptr(); - global->sram.use = global->sram.use && !global->sram.save_disable; -#ifdef HAVE_NETPLAY - global->sram.use = global->sram.use && - (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL) - || !global->netplay.is_client); -#endif - - if (!global->sram.use) - RARCH_LOG("%s\n", - msg_hash_to_str(MSG_SRAM_WILL_NOT_BE_SAVED)); - - if (global->sram.use) - command_event(CMD_EVENT_AUTOSAVE_INIT, NULL); - } - break; case CMD_EVENT_BSV_MOVIE_DEINIT: bsv_movie_ctl(BSV_MOVIE_CTL_DEINIT, NULL); break; diff --git a/command.h b/command.h index d34b68b3e4..f07078c671 100644 --- a/command.h +++ b/command.h @@ -157,11 +157,6 @@ enum event_command CMD_EVENT_SHADER_DIR_DEINIT, /* Initializes controllers. */ CMD_EVENT_CONTROLLERS_INIT, - CMD_EVENT_SAVEFILES, - /* Initializes savefiles. */ - CMD_EVENT_SAVEFILES_INIT, - /* Deinitializes savefiles. */ - CMD_EVENT_SAVEFILES_DEINIT, /* Initializes cheats. */ CMD_EVENT_CHEATS_INIT, /* Deinitializes cheats. */ diff --git a/config.def.h b/config.def.h index 74d841be25..9ba8bc1195 100644 --- a/config.def.h +++ b/config.def.h @@ -650,10 +650,8 @@ static const bool font_enable = true; * disable VSync, and leave this at its default. */ #ifdef _3DS static const float refresh_rate = (32730.0 * 8192.0) / 4481134.0 ; -#elif defined(RARCH_CONSOLE) -static const float refresh_rate = 60/1.001; #else -static const float refresh_rate = 59.95; +static const float refresh_rate = 60/1.001; #endif /* Allow games to set rotation. If false, rotation requests are @@ -874,7 +872,7 @@ static char buildbot_assets_server_url[] = "http://buildbot.libretro.com/assets/ /* User 1 */ static const struct retro_keybind retro_keybinds_1[] = { - /* | RetroPad button | desc | keyboard key | js btn | js axis | */ + /* | RetroPad button | desc | keyboard key | js btn | js axis | */ { true, RETRO_DEVICE_ID_JOYPAD_B, RETRO_LBL_JOYPAD_B, RETROK_z, NO_BTN, 0, AXIS_NONE }, { true, RETRO_DEVICE_ID_JOYPAD_Y, RETRO_LBL_JOYPAD_Y, RETROK_a, NO_BTN, 0, AXIS_NONE }, { true, RETRO_DEVICE_ID_JOYPAD_SELECT, RETRO_LBL_JOYPAD_SELECT, RETROK_RSHIFT, NO_BTN, 0, AXIS_NONE }, @@ -938,7 +936,7 @@ static const struct retro_keybind retro_keybinds_1[] = { /* Users 2 to MAX_USERS */ static const struct retro_keybind retro_keybinds_rest[] = { - /* | RetroPad button | desc | keyboard key | js btn | js axis | */ + /* | RetroPad button | desc | keyboard key | js btn | js axis | */ { true, RETRO_DEVICE_ID_JOYPAD_B, RETRO_LBL_JOYPAD_B, RETROK_UNKNOWN, NO_BTN, 0, AXIS_NONE }, { true, RETRO_DEVICE_ID_JOYPAD_Y, RETRO_LBL_JOYPAD_Y, RETROK_UNKNOWN, NO_BTN, 0, AXIS_NONE }, { true, RETRO_DEVICE_ID_JOYPAD_SELECT, RETRO_LBL_JOYPAD_SELECT, RETROK_UNKNOWN, NO_BTN, 0, AXIS_NONE }, diff --git a/configuration.c b/configuration.c index 99c3504982..3e37daf4e1 100644 --- a/configuration.c +++ b/configuration.c @@ -38,6 +38,8 @@ #include "input/input_remapping.h" #include "defaults.h" #include "core.h" +#include "dirs.h" +#include "paths.h" #include "retroarch.h" #include "runloop.h" #include "verbosity.h" @@ -407,38 +409,6 @@ const char *config_get_default_joypad(void) return "null"; } -#ifdef HAVE_MENU -/** - * config_get_default_menu: - * - * Gets default menu driver. - * - * Returns: Default menu driver. - **/ -const char *config_get_default_menu(void) -{ - if (!string_is_empty(g_defaults.settings.menu)) - return g_defaults.settings.menu; - - switch (MENU_DEFAULT_DRIVER) - { - case MENU_RGUI: - return "rgui"; - case MENU_XUI: - return "xui"; - case MENU_MATERIALUI: - return "glui"; - case MENU_XMB: - return "xmb"; - case MENU_NUKLEAR: - return "nuklear"; - default: - break; - } - - return "null"; -} -#endif /** * config_get_default_camera: @@ -488,14 +458,38 @@ const char *config_get_default_location(void) return "null"; } -bool config_overlay_enable_default(void) +#ifdef HAVE_MENU +/** + * config_get_default_menu: + * + * Gets default menu driver. + * + * Returns: Default menu driver. + **/ +const char *config_get_default_menu(void) { - if (g_defaults.overlay.set) - return g_defaults.overlay.enable; - return true; + if (!string_is_empty(g_defaults.settings.menu)) + return g_defaults.settings.menu; + + switch (MENU_DEFAULT_DRIVER) + { + case MENU_RGUI: + return "rgui"; + case MENU_XUI: + return "xui"; + case MENU_MATERIALUI: + return "glui"; + case MENU_XMB: + return "xmb"; + case MENU_NUKLEAR: + return "nuklear"; + default: + break; + } + + return "null"; } -#ifdef HAVE_MENU static unsigned config_menu_btn_ok_default(void) { if (g_defaults.menu.controls.set) @@ -511,6 +505,13 @@ static unsigned config_menu_btn_cancel_default(void) } #endif +bool config_overlay_enable_default(void) +{ + if (g_defaults.overlay.set) + return g_defaults.overlay.enable; + return true; +} + static int populate_settings_array(settings_t *settings, struct config_array_setting **out) { unsigned count = 0; @@ -636,9 +637,9 @@ static int populate_settings_path(settings_t *settings, struct config_path_setti SETTING_PATH("audio_filter_dir", settings->directory.audio_filter, true, NULL, true); SETTING_PATH("savefile_directory", - global->dir.savefile, true, NULL, false); + dir_get_savefile_ptr(), true, NULL, false); SETTING_PATH("savestate_directory", - global->dir.savestate, true, NULL, false); + dir_get_savestate_ptr(), true, NULL, false); #ifdef HAVE_MENU SETTING_PATH("rgui_browser_directory", settings->directory.menu_content, true, NULL, true); @@ -651,11 +652,11 @@ static int populate_settings_path(settings_t *settings, struct config_path_setti #endif #ifdef HAVE_OVERLAY SETTING_PATH("osk_overlay_directory", - global->dir.osk_overlay, true, NULL, true); + dir_get_osk_overlay_ptr(), true, NULL, true); #endif #ifndef HAVE_DYNAMIC SETTING_PATH("libretro_path", - config_get_active_core_path_ptr(), false, NULL, false); + path_get_core_ptr(), false, NULL, false); #endif SETTING_PATH( "screenshot_directory", @@ -911,7 +912,8 @@ static int populate_settings_int(settings_t *settings, struct config_int_setting SETTING_INT("state_slot", (unsigned*)&settings->state_slot, false, 0 /* TODO */, false); #ifdef HAVE_NETPLAY SETTING_INT("netplay_ip_port", &global->netplay.port, false, 0 /* TODO */, false); - SETTING_INT("netplay_delay_frames", &global->netplay.sync_frames, false, 0 /* TODO */, false); + SETTING_INT("netplay_delay_frames", &global->netplay.sync_frames, false, 16, false); + SETTING_INT("netplay_check_frames", &global->netplay.check_frames, false, 0, false); #endif #ifdef HAVE_LANGEXTRA SETTING_INT("user_language", &settings->user_language, true, RETRO_LANGUAGE_ENGLISH, false); @@ -1117,9 +1119,9 @@ static void config_set_defaults(void) /* Make sure settings from other configs carry over into defaults * for another config. */ if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_SAVE_PATH)) - *global->dir.savefile = '\0'; + dir_clear_savefile(); if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_STATE_PATH)) - *global->dir.savestate = '\0'; + dir_clear_savestate(); *settings->path.libretro_info = '\0'; if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_LIBRETRO_DIRECTORY)) @@ -1246,20 +1248,30 @@ static void config_set_defaults(void) if (!string_is_empty(g_defaults.dir.osk_overlay)) { - fill_pathname_expand_special(global->dir.osk_overlay, - g_defaults.dir.osk_overlay, sizeof(global->dir.osk_overlay)); + char temp_path[PATH_MAX_LENGTH] = {0}; + + fill_pathname_expand_special(temp_path, + g_defaults.dir.osk_overlay, sizeof(temp_path)); #ifdef RARCH_MOBILE if (string_is_empty(settings->path.osk_overlay)) fill_pathname_join(settings->path.osk_overlay, - global->dir.osk_overlay, + temp_path, "keyboards/modular-keyboard/opaque/big.cfg", sizeof(settings->path.osk_overlay)); #endif + + dir_set_osk_overlay(temp_path); } else - strlcpy(global->dir.osk_overlay, + { + char temp_path[PATH_MAX_LENGTH] = {0}; + + strlcpy(temp_path, settings->directory.overlay, - sizeof(global->dir.osk_overlay)); + sizeof(temp_path)); + + dir_set_osk_overlay(temp_path); + } #endif #ifdef HAVE_MENU if (!string_is_empty(g_defaults.dir.menu_config)) @@ -1278,12 +1290,12 @@ static void config_set_defaults(void) if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_STATE_PATH) && !string_is_empty(g_defaults.dir.savestate)) - strlcpy(global->dir.savestate, - g_defaults.dir.savestate, sizeof(global->dir.savestate)); + dir_set_savestate(g_defaults.dir.savestate); + if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_SAVE_PATH) && !string_is_empty(g_defaults.dir.sram)) - strlcpy(global->dir.savefile, - g_defaults.dir.sram, sizeof(global->dir.savefile)); + dir_set_savefile(g_defaults.dir.sram); + if (!string_is_empty(g_defaults.dir.system)) strlcpy(settings->directory.system, g_defaults.dir.system, sizeof(settings->directory.system)); @@ -1301,8 +1313,12 @@ static void config_set_defaults(void) sizeof(settings->directory.content_history)); if (!string_is_empty(g_defaults.path.config)) - fill_pathname_expand_special(global->path.config, - g_defaults.path.config, sizeof(global->path.config)); + { + char temp_str[PATH_MAX_LENGTH]; + fill_pathname_expand_special(temp_str, + g_defaults.path.config, sizeof(temp_str)); + path_set_config(temp_str); + } /* Avoid reloading config on every content load */ if (default_block_config_read) @@ -1335,7 +1351,6 @@ static config_file_t *open_default_config_file(void) char conf_path[PATH_MAX_LENGTH] = {0}; char app_path[PATH_MAX_LENGTH] = {0}; config_file_t *conf = NULL; - global_t *global = NULL; #if defined(_WIN32) && !defined(_XBOX) fill_pathname_application_path(app_path, sizeof(app_path)); @@ -1500,10 +1515,8 @@ static config_file_t *open_default_config_file(void) if (!conf) return NULL; - global = global_get_ptr(); + path_set_config(conf_path); - if (global) - strlcpy(global->path.config, conf_path, sizeof(global->path.config)); return conf; } @@ -1640,9 +1653,7 @@ static bool config_load_file(const char *path, bool set_defaults, unsigned i; bool tmp_bool = false; char *save = NULL; - const char *extra_path = NULL; char tmp_str[PATH_MAX_LENGTH] = {0}; - char tmp_append_path[PATH_MAX_LENGTH] = {0}; /* Don't destroy append_config_path. */ unsigned msg_color = 0; config_file_t *conf = NULL; struct config_int_setting *int_settings = NULL; @@ -1676,20 +1687,29 @@ static bool config_load_file(const char *path, bool set_defaults, if (set_defaults) config_set_defaults(); - strlcpy(tmp_append_path, global->path.append_config, - sizeof(tmp_append_path)); - extra_path = strtok_r(tmp_append_path, "|", &save); - - while (extra_path) + if (!path_is_config_append_empty()) { - bool ret = config_append_file(conf, extra_path); + /* Don't destroy append_config_path, store in temporary + * variable. */ + char tmp_append_path[PATH_MAX_LENGTH] = {0}; + const char *extra_path = NULL; - RARCH_LOG("Config: appending config \"%s\"\n", extra_path); + strlcpy(tmp_append_path, path_get_config_append(), + sizeof(tmp_append_path)); + extra_path = strtok_r(tmp_append_path, "|", &save); - if (!ret) - RARCH_ERR("Config: failed to append config \"%s\"\n", extra_path); - extra_path = strtok_r(NULL, "|", &save); + while (extra_path) + { + bool ret = config_append_file(conf, extra_path); + + RARCH_LOG("Config: appending config \"%s\"\n", extra_path); + + if (!ret) + RARCH_ERR("Config: failed to append config \"%s\"\n", extra_path); + extra_path = strtok_r(NULL, "|", &save); + } } + #if 0 if (verbosity_is_enabled()) { @@ -1717,6 +1737,7 @@ static bool config_load_file(const char *path, bool set_defaults, if (config_get_bool(conf, bool_settings[i].ident, &tmp)) *bool_settings[i].ptr = tmp; } + if (!rarch_ctl(RARCH_CTL_IS_FORCE_FULLSCREEN, NULL)) CONFIG_GET_BOOL_BASE(conf, settings, video.fullscreen, "video_fullscreen"); @@ -1788,6 +1809,8 @@ static bool config_load_file(const char *path, bool set_defaults, #ifdef HAVE_NETPLAY if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_NETPLAY_DELAY_FRAMES)) CONFIG_GET_INT_BASE(conf, global, netplay.sync_frames, "netplay_delay_frames"); + if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_NETPLAY_CHECK_FRAMES)) + CONFIG_GET_INT_BASE(conf, global, netplay.sync_frames, "netplay_check_frames"); if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_NETPLAY_IP_PORT)) CONFIG_GET_INT_BASE(conf, global, netplay.port, "netplay_ip_port"); #endif @@ -1865,7 +1888,7 @@ static bool config_load_file(const char *path, bool set_defaults, #ifndef HAVE_DYNAMIC if (config_get_path(conf, "libretro_path", tmp_str, sizeof(tmp_str))) - config_set_active_core_path(tmp_str); + path_set_core(tmp_str); #endif #ifdef RARCH_CONSOLE @@ -1905,7 +1928,7 @@ static bool config_load_file(const char *path, bool set_defaults, { fill_pathname_resolve_relative( settings->path.content_history, - global->path.config, + path_get_config(), file_path_str(FILE_PATH_CONTENT_HISTORY), sizeof(settings->path.content_history)); } @@ -1924,7 +1947,7 @@ static bool config_load_file(const char *path, bool set_defaults, { fill_pathname_resolve_relative( settings->path.content_music_history, - global->path.config, + path_get_config(), file_path_str(FILE_PATH_CONTENT_MUSIC_HISTORY), sizeof(settings->path.content_music_history)); } @@ -1943,7 +1966,7 @@ static bool config_load_file(const char *path, bool set_defaults, { fill_pathname_resolve_relative( settings->path.content_video_history, - global->path.config, + path_get_config(), file_path_str(FILE_PATH_CONTENT_VIDEO_HISTORY), sizeof(settings->path.content_video_history)); } @@ -1962,7 +1985,7 @@ static bool config_load_file(const char *path, bool set_defaults, { fill_pathname_resolve_relative( settings->path.content_image_history, - global->path.config, + path_get_config(), file_path_str(FILE_PATH_CONTENT_IMAGE_HISTORY), sizeof(settings->path.content_image_history)); } @@ -1988,12 +2011,12 @@ static bool config_load_file(const char *path, bool set_defaults, } /* Safe-guard against older behavior. */ - if (path_is_directory(config_get_active_core_path())) + if (path_is_directory(path_get_core())) { RARCH_WARN("\"libretro_path\" is a directory, using this for \"libretro_directory\" instead.\n"); - strlcpy(settings->directory.libretro, config_get_active_core_path(), + strlcpy(settings->directory.libretro, path_get_core(), sizeof(settings->directory.libretro)); - config_clear_active_core_path(); + path_clear_core(); } if (string_is_equal(settings->path.menu_wallpaper, "default")) @@ -2024,8 +2047,8 @@ static bool config_load_file(const char *path, bool set_defaults, #ifdef HAVE_OVERLAY if (string_is_equal(settings->directory.overlay, "default")) *settings->directory.overlay = '\0'; - if (string_is_equal(global->dir.osk_overlay, "default")) - *global->dir.osk_overlay = '\0'; + if (string_is_equal(dir_get_osk_overlay(), "default")) + dir_clear_osk_overlay(); #endif if (string_is_equal(settings->directory.system, "default")) *settings->directory.system = '\0'; @@ -2048,16 +2071,16 @@ static bool config_load_file(const char *path, bool set_defaults, config_get_path(conf, "savefile_directory", tmp_str, sizeof(tmp_str))) { if (string_is_equal(tmp_str, "default")) - strlcpy(global->dir.savefile, g_defaults.dir.sram, - sizeof(global->dir.savefile)); + dir_set_savefile(g_defaults.dir.sram); + else if (path_is_directory(tmp_str)) { - strlcpy(global->dir.savefile, tmp_str, - sizeof(global->dir.savefile)); + dir_set_savefile(tmp_str); + strlcpy(global->name.savefile, tmp_str, sizeof(global->name.savefile)); fill_pathname_dir(global->name.savefile, - global->name.base, + path_get_basename(), file_path_str(FILE_PATH_SRM_EXTENSION), sizeof(global->name.savefile)); } @@ -2069,16 +2092,15 @@ static bool config_load_file(const char *path, bool set_defaults, config_get_path(conf, "savestate_directory", tmp_str, sizeof(tmp_str))) { if (string_is_equal(tmp_str, "default")) - strlcpy(global->dir.savestate, g_defaults.dir.savestate, - sizeof(global->dir.savestate)); + dir_set_savestate(g_defaults.dir.savestate); else if (path_is_directory(tmp_str)) { - strlcpy(global->dir.savestate, tmp_str, - sizeof(global->dir.savestate)); + dir_set_savestate(tmp_str); + strlcpy(global->name.savestate, tmp_str, sizeof(global->name.savestate)); fill_pathname_dir(global->name.savestate, - global->name.base, + path_get_basename(), file_path_str(FILE_PATH_STATE_EXTENSION), sizeof(global->name.savestate)); } @@ -2133,14 +2155,15 @@ bool config_load_override(void) const char *game_name = NULL; bool should_append = false; rarch_system_info_t *system = NULL; +#ifdef HAVE_NETPLAY global_t *global = global_get_ptr(); +#endif runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system); if (system) core_name = system->info.library_name; - if (global) - game_name = path_basename(global->name.base); + game_name = path_basename(path_get_basename()); if (string_is_empty(core_name) || string_is_empty(game_name)) return false; @@ -2167,10 +2190,10 @@ bool config_load_override(void) /* If a core override exists, add its location to append_config_path */ if (new_conf) { - config_file_free(new_conf); - RARCH_LOG("[overrides] core-specific overrides found at %s.\n", core_path); - strlcpy(global->path.append_config, core_path, sizeof(global->path.append_config)); + + config_file_free(new_conf); + path_set_config_append(core_path); should_append = true; } @@ -2183,16 +2206,22 @@ bool config_load_override(void) /* If a game override exists, add it's location to append_config_path */ if (new_conf) { + char temp_path[PATH_MAX_LENGTH] = {0}; + config_file_free(new_conf); RARCH_LOG("[overrides] game-specific overrides found at %s.\n", game_path); + if (should_append) { - strlcat(global->path.append_config, "|", sizeof(global->path.append_config)); - strlcat(global->path.append_config, game_path, sizeof(global->path.append_config)); + strlcpy(temp_path, path_get_config_append(), sizeof(temp_path)); + strlcat(temp_path, "|", sizeof(temp_path)); + strlcat(temp_path, game_path, sizeof(temp_path)); } else - strlcpy(global->path.append_config, game_path, sizeof(global->path.append_config)); + strlcpy(temp_path, game_path, sizeof(temp_path)); + + path_set_config_append(temp_path); should_append = true; } @@ -2204,7 +2233,7 @@ bool config_load_override(void) /* Re-load the configuration with any overrides that might have been found */ #ifdef HAVE_NETPLAY - if (global->netplay.enable) + if (global && global->netplay.enable) { RARCH_WARN("[overrides] can't use overrides in conjunction with netplay, disabling overrides.\n"); return false; @@ -2213,24 +2242,26 @@ bool config_load_override(void) /* Store the libretro_path we're using since it will be * overwritten by the override when reloading. */ - strlcpy(buf, config_get_active_core_path(), sizeof(buf)); + strlcpy(buf, path_get_core(), sizeof(buf)); /* Toggle has_save_path to false so it resets */ retroarch_override_setting_unset(RARCH_OVERRIDE_SETTING_STATE_PATH); retroarch_override_setting_unset(RARCH_OVERRIDE_SETTING_SAVE_PATH); - if (!config_load_file(global->path.config, false, config_get_ptr())) + if (!config_load_file(path_get_config(), false, config_get_ptr())) return false; /* Restore the libretro_path we're using * since it will be overwritten by the override when reloading. */ - config_set_active_core_path(buf); + path_set_core(buf); runloop_msg_queue_push("Configuration override loaded.", 1, 100, true); /* Reset save paths. */ retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_STATE_PATH); retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_SAVE_PATH); - global->path.append_config[0] = '\0'; + + path_clear_config_append(); + return true; } @@ -2244,18 +2275,13 @@ bool config_load_override(void) */ bool config_unload_override(void) { - global_t *global = global_get_ptr(); - - if (!global) - return false; - - *global->path.append_config = '\0'; + path_clear_config_append(); /* Toggle has_save_path to false so it resets */ retroarch_override_setting_unset(RARCH_OVERRIDE_SETTING_STATE_PATH); retroarch_override_setting_unset(RARCH_OVERRIDE_SETTING_SAVE_PATH); - if (config_load_file(global->path.config, false, config_get_ptr())) + if (config_load_file(path_get_config(), false, config_get_ptr())) { RARCH_LOG("[overrides] configuration overrides unloaded, original configuration restored.\n"); @@ -2290,7 +2316,6 @@ bool config_load_remap(void) config_file_t *new_conf = NULL; const char *core_name = NULL; const char *game_name = NULL; - global_t *global = global_get_ptr(); settings_t *settings = config_get_ptr(); rarch_system_info_t *system = NULL; @@ -2298,8 +2323,8 @@ bool config_load_remap(void) if (system) core_name = system->info.library_name; - if (global) - game_name = path_basename(global->name.base); + + game_name = path_basename(path_get_basename()); if (string_is_empty(core_name) || string_is_empty(game_name)) return false; @@ -2408,12 +2433,11 @@ static bool check_shader_compatibility(enum file_path_enum enum_idx) bool config_load_shader_preset(void) { unsigned idx; - char shader_directory[PATH_MAX_LENGTH] = {0}; /* path to the directory containing retroarch.cfg (prefix) */ + char shader_directory[PATH_MAX_LENGTH] = {0}; /* path to the directory containing retroarch.cfg (prefix) */ char core_path[PATH_MAX_LENGTH] = {0}; /* final path for core-specific configuration (prefix+suffix) */ char game_path[PATH_MAX_LENGTH] = {0}; /* final path for game-specific configuration (prefix+suffix) */ const char *core_name = NULL; const char *game_name = NULL; - global_t *global = global_get_ptr(); settings_t *settings = config_get_ptr(); rarch_system_info_t *system = NULL; @@ -2421,8 +2445,8 @@ bool config_load_shader_preset(void) if (system) core_name = system->info.library_name; - if (global) - game_name = path_basename(global->name.base); + + game_name = path_basename(path_get_basename()); if (string_is_empty(core_name) || string_is_empty(game_name)) return false; @@ -2507,26 +2531,23 @@ bool config_load_shader_preset(void) static void parse_config_file(void) { - global_t *global = global_get_ptr(); - bool ret = config_load_file((*global->path.config) - ? global->path.config : NULL, false, config_get_ptr()); + bool ret = config_load_file(path_get_config(), false, config_get_ptr()); - if (!string_is_empty(global->path.config)) + if (!path_is_config_empty()) { - RARCH_LOG("Config: loading config from: %s.\n", global->path.config); + RARCH_LOG("Config: loading config from: %s.\n", path_get_config()); } else { RARCH_LOG("Loading default config.\n"); - if (!string_is_empty(global->path.config)) - RARCH_LOG("Config: found default config: %s.\n", global->path.config); + if (!path_is_config_empty()) + RARCH_LOG("Config: found default config: %s.\n", path_get_config()); } if (ret) return; - RARCH_ERR("Config: couldn't find config at path: \"%s\"\n", - global->path.config); + RARCH_ERR("Config: couldn't find config at path: \"%s\"\n", path_get_config()); } @@ -3007,7 +3028,6 @@ bool config_save_overrides(int override_type) const char *game_name = NULL; config_file_t *conf = NULL; settings_t *settings = NULL; - global_t *global = global_get_ptr(); settings_t *overrides = config_get_ptr(); rarch_system_info_t *system = NULL; struct config_bool_setting *bool_settings = NULL; @@ -3025,8 +3045,8 @@ bool config_save_overrides(int override_type) if (system) core_name = system->info.library_name; - if (global) - game_name = path_basename(global->name.base); + + game_name = path_basename(path_get_basename()); if (string_is_empty(core_name) || string_is_empty(game_name)) return false; @@ -3059,7 +3079,7 @@ bool config_save_overrides(int override_type) conf = config_file_new(NULL); /* Load the original config file in memory */ - config_load_file(global->path.config, false, settings); + config_load_file(path_get_config(), false, settings); bool_settings_size = populate_settings_bool(settings, &bool_settings); populate_settings_bool (overrides, &bool_overrides); @@ -3184,25 +3204,24 @@ bool config_replace(char *path) { content_ctx_info_t content_info = {0}; settings_t *settings = config_get_ptr(); - global_t *global = global_get_ptr(); - if (!path || !global) + if (!path) return false; /* If config file to be replaced is the same as the * current config file, exit. */ - if (string_is_equal(path, global->path.config)) + if (string_is_equal(path, path_get_config())) return false; - if (settings->config_save_on_exit && !string_is_empty(global->path.config)) - config_save_file(global->path.config); + if (settings->config_save_on_exit && !path_is_config_empty()) + config_save_file(path_get_config()); - strlcpy(global->path.config, path, sizeof(global->path.config)); + path_set_config(path); rarch_ctl(RARCH_CTL_UNSET_BLOCK_CONFIG_READ, NULL); /* Load core in new config. */ - config_clear_active_core_path(); + path_clear_core(); if (!task_push_content_load_default( NULL, NULL, @@ -3215,48 +3234,6 @@ bool config_replace(char *path) return true; } -static char path_libretro[PATH_MAX_LENGTH]; - -char *config_get_active_core_path_ptr(void) -{ - return path_libretro; -} - -const char *config_get_active_core_path(void) -{ - return path_libretro; -} - -bool config_active_core_path_is_empty(void) -{ - return !path_libretro[0]; -} - -size_t config_get_active_core_path_size(void) -{ - return sizeof(path_libretro); -} - -void config_set_active_core_path(const char *path) -{ - strlcpy(path_libretro, path, sizeof(path_libretro)); -} - -void config_clear_active_core_path(void) -{ - *path_libretro = '\0'; -} - -const char *config_get_active_path(void) -{ - global_t *global = global_get_ptr(); - - if (!string_is_empty(global->path.config)) - return global->path.config; - - return NULL; -} - void config_free_state(void) { } diff --git a/configuration.h b/configuration.h index 602e3cb25c..a2f38187ae 100644 --- a/configuration.h +++ b/configuration.h @@ -631,20 +631,6 @@ bool config_overlay_enable_default(void); void config_free(void); -const char *config_get_active_path(void); - -const char *config_get_active_core_path(void); - -char *config_get_active_core_path_ptr(void); - -void config_set_active_core_path(const char *path); - -void config_clear_active_core_path(void); - -bool config_active_core_path_is_empty(void); - -size_t config_get_active_core_path_size(void); - void config_free_state(void); settings_t *config_get_ptr(void); diff --git a/core_impl.c b/core_impl.c index 1a5a9a667a..0dd8f537b0 100644 --- a/core_impl.c +++ b/core_impl.c @@ -87,9 +87,6 @@ void core_set_input_state(retro_ctx_input_state_info_t *info) static bool core_init_libretro_cbs(void *data) { struct retro_callbacks *cbs = (struct retro_callbacks*)data; -#ifdef HAVE_NETPLAY - global_t *global = global_get_ptr(); -#endif if (!cbs) return false; @@ -109,20 +106,10 @@ static bool core_init_libretro_cbs(void *data) /* Force normal poll type for netplay. */ core_poll_type = POLL_TYPE_NORMAL; - if (global->netplay.is_spectate) - { - core.retro_set_input_state( - (global->netplay.is_client ? - input_state_spectate_client : input_state_spectate) - ); - } - else - { - core.retro_set_video_refresh(video_frame_net); - core.retro_set_audio_sample(audio_sample_net); - core.retro_set_audio_sample_batch(audio_sample_batch_net); - core.retro_set_input_state(input_state_net); - } + core.retro_set_video_refresh(video_frame_net); + core.retro_set_audio_sample(audio_sample_net); + core.retro_set_audio_sample_batch(audio_sample_batch_net); + core.retro_set_input_state(input_state_net); #endif return true; diff --git a/core_info.c b/core_info.c index e8ced4a08f..d712930891 100644 --- a/core_info.c +++ b/core_info.c @@ -572,10 +572,9 @@ core_info_t *core_info_get(core_info_list_t *list, size_t i) void core_info_list_get_supported_cores(core_info_list_t *core_info_list, const char *path, const core_info_t **infos, size_t *num_infos) { -#ifdef HAVE_ZLIB + size_t i; struct string_list *list = NULL; -#endif - size_t supported = 0, i; + size_t supported = 0; if (!core_info_list) return; @@ -608,10 +607,8 @@ void core_info_list_get_supported_cores(core_info_list_t *core_info_list, break; } -#ifdef HAVE_ZLIB if (list) string_list_free(list); -#endif *infos = core_info_list->list; *num_infos = supported; diff --git a/cores/libretro-ffmpeg/ffmpeg_core.c b/cores/libretro-ffmpeg/ffmpeg_core.c index bfbe5beff7..c0eefbb081 100644 --- a/cores/libretro-ffmpeg/ffmpeg_core.c +++ b/cores/libretro-ffmpeg/ffmpeg_core.c @@ -272,10 +272,10 @@ void CORE_PREFIX(retro_set_environment)(retro_environment_t cb) static const struct retro_variable vars[] = { #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) { "ffmpeg_temporal_interp", "Temporal Interpolation; enabled|disabled" }, -#endif #ifdef HAVE_GL_FFT { "ffmpeg_fft_resolution", "GLFFT Resolution; 1280x720|1920x1080|640x360|320x180" }, { "ffmpeg_fft_multisample", "GLFFT Multisample; 1x|2x|4x" }, +#endif #endif { "ffmpeg_color_space", "Colorspace; auto|BT.709|BT.601|FCC|SMPTE240M" }, { NULL, NULL }, @@ -343,7 +343,6 @@ static void check_variables(void) else if (!strcmp(var.value, "disabled")) temporal_interpolation = false; } -#endif #ifdef HAVE_GL_FFT fft_var.key = "ffmpeg_fft_resolution"; @@ -365,6 +364,7 @@ static void check_variables(void) if (CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_GET_VARIABLE, &fft_ms_var) && fft_ms_var.value) fft_multisample = strtoul(fft_ms_var.value, NULL, 0); +#endif #endif color_var.key = "ffmpeg_color_space"; @@ -436,9 +436,9 @@ void CORE_PREFIX(retro_run)(void) double min_pts; int16_t audio_buffer[2048]; bool left, right, up, down, l, r; - size_t to_read_frames = 0; - int seek_frames = 0; - bool updated = false; + size_t to_read_frames = 0; + int seek_frames = 0; + bool updated = false; #ifdef HAVE_GL_FFT unsigned old_fft_width = fft_width; unsigned old_fft_height = fft_height; @@ -1172,12 +1172,10 @@ static void decode_thread(void *data) (void)data; if (video_stream >= 0) - { sws = sws_getCachedContext(NULL, media.width, media.height, vctx->pix_fmt, media.width, media.height, PIX_FMT_RGB32, SWS_POINT, NULL, NULL, NULL); - } for (i = 0; (int)i < audio_streams_num; i++) { diff --git a/cores/libretro-ffmpeg/fft/fft.cpp b/cores/libretro-ffmpeg/fft/fft.cpp index b21539ab2e..14c0d67bf2 100644 --- a/cores/libretro-ffmpeg/fft/fft.cpp +++ b/cores/libretro-ffmpeg/fft/fft.cpp @@ -153,12 +153,6 @@ static GLuint fft_compile_program(glfft_t *fft, static void fft_render(glfft_t *fft, GLuint backbuffer, unsigned width, unsigned height) { - /* Render scene. */ - glBindFramebuffer(GL_FRAMEBUFFER, fft->ms_fbo ? fft->ms_fbo : backbuffer); - glViewport(0, 0, width, height); - glClearColor(0.1f, 0.15f, 0.1f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - vec3 eye = vec3(0, 80, -60); vec3 center = eye + vec3(0.0f, 0.0f, 1.0f); vec3 up = vec3(0.0f, 1.0f, 0.0f); @@ -166,6 +160,12 @@ static void fft_render(glfft_t *fft, GLuint backbuffer, unsigned width, unsigned mat4 mvp_lookat = lookAt(eye, center, up); mat4 mvp = mvp_persp * mvp_lookat; + /* Render scene. */ + glBindFramebuffer(GL_FRAMEBUFFER, fft->ms_fbo ? fft->ms_fbo : backbuffer); + glViewport(0, 0, width, height); + glClearColor(0.1f, 0.15f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glUseProgram(fft->block.prog); glUniformMatrix4fv(glGetUniformLocation(fft->block.prog, "uMVP"), 1, GL_FALSE, value_ptr(mvp)); @@ -524,12 +524,12 @@ static void fft_init(glfft_t *fft) static void fft_init_block(glfft_t *fft) { unsigned x, y; - GLuint *bp; - GLushort *block_vertices; - GLuint *block_indices; unsigned block_vertices_size; unsigned block_indices_size; - int pos = 0; + int pos = 0; + GLuint *bp = NULL; + GLushort *block_vertices = NULL; + GLuint *block_indices = NULL; fft->block.prog = fft_compile_program(fft, fft_vertex_program_heightmap, fft_fragment_program_heightmap); @@ -605,13 +605,13 @@ static bool fft_context_reset(glfft_t *fft, unsigned fft_steps, fft->block_size = fft->size / 4 + 1; fft->passes_size = fft_steps; - fft->passes = (Pass*)calloc(fft->passes_size, sizeof(Pass)); + fft->passes = (Pass*)calloc(fft->passes_size, sizeof(Pass)); if (!fft->passes) return false; fft->sliding_size = 2 * fft->size; - fft->sliding = (GLshort*)calloc(fft->sliding_size, sizeof(GLshort)); + fft->sliding = (GLshort*)calloc(fft->sliding_size, sizeof(GLshort)); if (!fft->sliding) return false; diff --git a/database_info.h b/database_info.h index 2b9bfd0197..36de4ba305 100644 --- a/database_info.h +++ b/database_info.h @@ -2,7 +2,7 @@ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2016 - Daniel De Matteis * Copyright (C) 2013-2015 - Jason Fetters - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. @@ -24,10 +24,6 @@ #include #include -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include "libretro-db/libretrodb.h" RETRO_BEGIN_DECLS @@ -46,7 +42,7 @@ enum database_type { DATABASE_TYPE_NONE = 0, DATABASE_TYPE_ITERATE, - DATABASE_TYPE_ITERATE_ZIP, + DATABASE_TYPE_ITERATE_ARCHIVE, DATABASE_TYPE_ITERATE_LUTRO, DATABASE_TYPE_SERIAL_LOOKUP, DATABASE_TYPE_CRC_LOOKUP @@ -58,9 +54,7 @@ typedef struct enum database_type type; size_t list_ptr; struct string_list *list; -#ifdef HAVE_ZLIB file_archive_transfer_t state; -#endif } database_info_handle_t; typedef struct diff --git a/dirs.c b/dirs.c new file mode 100644 index 0000000000..374b24960b --- /dev/null +++ b/dirs.c @@ -0,0 +1,245 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dirs.h" +#include "defaults.h" + +static char dir_osk_overlay[PATH_MAX_LENGTH] = {0}; +static char dir_system[PATH_MAX_LENGTH] = {0}; +static char dir_savefile[PATH_MAX_LENGTH] = {0}; +static char dir_savestate[PATH_MAX_LENGTH] = {0}; + +/* empty functions */ + +bool dir_is_system_empty(void) +{ + return string_is_empty(dir_savefile); +} + +bool dir_is_savefile_empty(void) +{ + return string_is_empty(dir_savefile); +} + +bool dir_is_savestate_empty(void) +{ + return string_is_empty(dir_savestate); +} + +bool dir_is_osk_overlay_empty(void) +{ + return string_is_empty(dir_osk_overlay); +} + +/* get size functions */ + +size_t dir_get_system_size(void) +{ + return sizeof(dir_system); +} + +size_t dir_get_savestate_size(void) +{ + return sizeof(dir_savestate); +} + +size_t dir_get_savefile_size(void) +{ + return sizeof(dir_savefile); +} + +size_t dir_get_osk_overlay_size(void) +{ + return sizeof(dir_osk_overlay); +} + +/* clear functions */ + +void dir_clear_system(void) +{ + *dir_system = '\0'; +} + +void dir_clear_savefile(void) +{ + *dir_savefile = '\0'; +} + +void dir_clear_savestate(void) +{ + *dir_savestate = '\0'; +} + +void dir_clear_osk_overlay(void) +{ + *dir_osk_overlay = '\0'; +} + +void dir_clear_all(void) +{ + dir_clear_system(); + dir_clear_osk_overlay(); + dir_clear_savefile(); + dir_clear_savestate(); +} + +/* get ptr functions */ + +char *dir_get_osk_overlay_ptr(void) +{ + return dir_osk_overlay; +} + +char *dir_get_savefile_ptr(void) +{ + return dir_savefile; +} + +char *dir_get_system_ptr(void) +{ + return dir_system; +} + +char *dir_get_savestate_ptr(void) +{ + return dir_savestate; +} + +/* get functions */ + +const char *dir_get_osk_overlay(void) +{ + return dir_osk_overlay; +} + +const char *dir_get_system(void) +{ + return dir_system; +} + +const char *dir_get_savefile(void) +{ + return dir_savefile; +} + +const char *dir_get_savestate(void) +{ + return dir_savestate; +} + +/* set functions */ + +void dir_set_osk_overlay(const char *path) +{ + strlcpy(dir_osk_overlay, path, + sizeof(dir_osk_overlay)); +} + +void dir_set_system(const char *path) +{ + strlcpy(dir_system, path, + sizeof(dir_system)); +} + +void dir_set_savestate(const char *path) +{ + strlcpy(dir_savestate, path, + sizeof(dir_savestate)); +} + +void dir_set_savefile(const char *path) +{ + strlcpy(dir_savefile, path, + sizeof(dir_savefile)); +} + +static void check_defaults_dir_create_dir(const char *path) +{ + char new_path[PATH_MAX_LENGTH] = {0}; + fill_pathname_expand_special(new_path, + path, sizeof(new_path)); + + if (path_is_directory(new_path)) + return; + path_mkdir(new_path); +} + +void dir_check_defaults(void) +{ + /* early return for people with a custom folder setup + so it doesn't create unnecessary directories + */ + if (path_file_exists("custom.ini")) + return; + + if (!string_is_empty(g_defaults.dir.core_assets)) + check_defaults_dir_create_dir(g_defaults.dir.core_assets); + if (!string_is_empty(g_defaults.dir.remap)) + check_defaults_dir_create_dir(g_defaults.dir.remap); + if (!string_is_empty(g_defaults.dir.screenshot)) + check_defaults_dir_create_dir(g_defaults.dir.screenshot); + if (!string_is_empty(g_defaults.dir.core)) + check_defaults_dir_create_dir(g_defaults.dir.core); + if (!string_is_empty(g_defaults.dir.autoconfig)) + check_defaults_dir_create_dir(g_defaults.dir.autoconfig); + if (!string_is_empty(g_defaults.dir.audio_filter)) + check_defaults_dir_create_dir(g_defaults.dir.audio_filter); + if (!string_is_empty(g_defaults.dir.video_filter)) + check_defaults_dir_create_dir(g_defaults.dir.video_filter); + if (!string_is_empty(g_defaults.dir.assets)) + check_defaults_dir_create_dir(g_defaults.dir.assets); + if (!string_is_empty(g_defaults.dir.playlist)) + check_defaults_dir_create_dir(g_defaults.dir.playlist); + if (!string_is_empty(g_defaults.dir.core)) + check_defaults_dir_create_dir(g_defaults.dir.core); + if (!string_is_empty(g_defaults.dir.core_info)) + check_defaults_dir_create_dir(g_defaults.dir.core_info); + if (!string_is_empty(g_defaults.dir.overlay)) + check_defaults_dir_create_dir(g_defaults.dir.overlay); + if (!string_is_empty(g_defaults.dir.port)) + check_defaults_dir_create_dir(g_defaults.dir.port); + if (!string_is_empty(g_defaults.dir.shader)) + check_defaults_dir_create_dir(g_defaults.dir.shader); + if (!string_is_empty(g_defaults.dir.savestate)) + check_defaults_dir_create_dir(g_defaults.dir.savestate); + if (!string_is_empty(g_defaults.dir.sram)) + check_defaults_dir_create_dir(g_defaults.dir.sram); + if (!string_is_empty(g_defaults.dir.system)) + check_defaults_dir_create_dir(g_defaults.dir.system); + if (!string_is_empty(g_defaults.dir.resampler)) + check_defaults_dir_create_dir(g_defaults.dir.resampler); + if (!string_is_empty(g_defaults.dir.menu_config)) + check_defaults_dir_create_dir(g_defaults.dir.menu_config); + if (!string_is_empty(g_defaults.dir.content_history)) + check_defaults_dir_create_dir(g_defaults.dir.content_history); + if (!string_is_empty(g_defaults.dir.cache)) + check_defaults_dir_create_dir(g_defaults.dir.cache); + if (!string_is_empty(g_defaults.dir.database)) + check_defaults_dir_create_dir(g_defaults.dir.database); + if (!string_is_empty(g_defaults.dir.cursor)) + check_defaults_dir_create_dir(g_defaults.dir.cursor); + if (!string_is_empty(g_defaults.dir.cheats)) + check_defaults_dir_create_dir(g_defaults.dir.cheats); + if (!string_is_empty(g_defaults.dir.thumbnails)) + check_defaults_dir_create_dir(g_defaults.dir.thumbnails); +} diff --git a/dirs.h b/dirs.h new file mode 100644 index 0000000000..d97a156533 --- /dev/null +++ b/dirs.h @@ -0,0 +1,92 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __DIRS_H +#define __DIRS_H + +#include +#include + +RETRO_BEGIN_DECLS + +/* empty functions */ + +bool dir_is_savefile_empty(void); + +bool dir_is_savestate_empty(void); + +bool dir_is_system_empty(void); + +bool dir_is_osk_overlay_empty(void); + +/* clear functions */ + +void dir_clear_system(void); + +void dir_clear_savefile(void); + +void dir_clear_savestate(void); + +void dir_clear_osk_overlay(void); + +void dir_clear_all(void); + +/* get size functions */ + +size_t dir_get_osk_overlay_size(void); + +size_t dir_get_system_size(void); + +size_t dir_get_savestate_size(void); + +size_t dir_get_savefile_size(void); + +/* get ptr functions */ + +char *dir_get_osk_overlay_ptr(void); + +char *dir_get_savefile_ptr(void); + +char *dir_get_savestate_ptr(void); + +char *dir_get_system_ptr(void); + +char *dir_get_osk_overlay_ptr(void); + +/* get functions */ + +const char *dir_get_osk_overlay(void); + +const char *dir_get_savefile(void); + +const char *dir_get_savestate(void); + +const char *dir_get_system(void); + +/* set functions */ + +void dir_set_osk_overlay(const char *path); + +void dir_set_savefile(const char *path); + +void dir_set_savestate(const char *path); + +void dir_set_system(const char *path); + +void dir_check_defaults(void); + +RETRO_END_DECLS + +#endif diff --git a/driver.c b/driver.c index a2b0e2648d..7bf7f0e094 100644 --- a/driver.c +++ b/driver.c @@ -167,15 +167,16 @@ static bool driver_find_first(const char *label, char *s, size_t len) static bool driver_find_prev(const char *label, char *s, size_t len) { int i = driver_find_index(label, s); + if (i > 0) - find_driver_nonempty(label, i - 1, s, len); - else { - RARCH_WARN( - "Couldn't find any previous driver (current one: \"%s\").\n", s); - return false; + find_driver_nonempty(label, i - 1, s, len); + return true; } - return true; + + RARCH_WARN( + "Couldn't find any previous driver (current one: \"%s\").\n", s); + return false; } /** @@ -189,16 +190,17 @@ static bool driver_find_prev(const char *label, char *s, size_t len) bool driver_find_next(const char *label, char *s, size_t len) { int i = driver_find_index(label, s); + if (i >= 0 && !string_is_equal(s, "null")) - find_driver_nonempty(label, i + 1, s, len); - else { - RARCH_WARN("%s (current one: \"%s\").\n", - msg_hash_to_str(MSG_COULD_NOT_FIND_ANY_NEXT_DRIVER), - s); - return false; + find_driver_nonempty(label, i + 1, s, len); + return true; } - return true; + + RARCH_WARN("%s (current one: \"%s\").\n", + msg_hash_to_str(MSG_COULD_NOT_FIND_ANY_NEXT_DRIVER), + s); + return false; } static void driver_adjust_system_rates(void) diff --git a/driver.h b/driver.h index b32704c14f..9a260e9825 100644 --- a/driver.h +++ b/driver.h @@ -24,10 +24,6 @@ #include #include -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - RETRO_BEGIN_DECLS #define DRIVERS_CMD_ALL \ diff --git a/dynamic.c b/dynamic.c index 9d17974e95..b504844c87 100644 --- a/dynamic.c +++ b/dynamic.c @@ -51,6 +51,8 @@ #include "cores/internal_cores.h" #include "frontend/frontend_driver.h" #include "content.h" +#include "dirs.h" +#include "paths.h" #include "retroarch.h" #include "runloop.h" #include "configuration.h" @@ -167,41 +169,6 @@ static bool environ_cb_get_system_info(unsigned cmd, void *data) return true; } -#ifndef HAVE_DYNAMIC -bool libretro_get_system_info_static(struct retro_system_info *info, - bool *load_no_content) -{ - struct retro_system_info dummy_info = {0}; - - if (load_no_content) - { - load_no_content_hook = load_no_content; - - /* load_no_content gets set in this callback. */ - retro_set_environment(environ_cb_get_system_info); - - /* It's possible that we just set get_system_info callback - * to the currently running core. - * - * Make sure we reset it to the actual environment callback. - * Ignore any environment callbacks here in case we're running - * on the non-current core. */ - ignore_environment_cb = true; - retro_set_environment(rarch_environment_cb); - ignore_environment_cb = false; - } - - retro_get_system_info(&dummy_info); - memcpy(info, &dummy_info, sizeof(*info)); - - info->library_name = strdup(dummy_info.library_name); - info->library_version = strdup(dummy_info.library_version); - if (dummy_info.valid_extensions) - info->valid_extensions = strdup(dummy_info.valid_extensions); - return true; -} -#endif - #ifdef HAVE_DYNAMIC /** * libretro_get_environment_info: @@ -239,6 +206,48 @@ void libretro_get_environment_info(void (*func)(retro_environment_t), ignore_environment_cb = false; } +static void load_dynamic_core(void) +{ + function_t sym = dylib_proc(NULL, "retro_init"); + + if (sym) + { + /* Try to verify that -lretro was not linked in from other modules + * since loading it dynamically and with -l will fail hard. */ + RARCH_ERR("Serious problem. RetroArch wants to load libretro cores" + "dyamically, but it is already linked.\n"); + RARCH_ERR("This could happen if other modules RetroArch depends on " + "link against libretro directly.\n"); + RARCH_ERR("Proceeding could cause a crash. Aborting ...\n"); + retroarch_fail(1, "init_libretro_sym()"); + } + + if (string_is_empty(path_get_core())) + { + RARCH_ERR("RetroArch is built for dynamic libretro cores, but " + "libretro_path is not set. Cannot continue.\n"); + retroarch_fail(1, "init_libretro_sym()"); + } + + /* Need to use absolute path for this setting. It can be + * saved to content history, and a relative path would + * break in that scenario. */ + path_resolve_realpath( + path_get_core_ptr(), + path_get_core_size()); + + RARCH_LOG("Loading dynamic libretro core from: \"%s\"\n", + path_get_core()); + lib_handle = dylib_load(path_get_core()); + if (!lib_handle) + { + RARCH_ERR("Failed to open libretro core: \"%s\"\n", + path_get_core()); + RARCH_ERR("Error(s): %s\n", dylib_error()); + retroarch_fail(1, "load_dynamic()"); + } +} + static dylib_t libretro_get_system_info_lib(const char *path, struct retro_system_info *info, bool *load_no_content) { @@ -280,6 +289,40 @@ static dylib_t libretro_get_system_info_lib(const char *path, return lib; } +#else +static bool libretro_get_system_info_static(struct retro_system_info *info, + bool *load_no_content) +{ + struct retro_system_info dummy_info = {0}; + + if (load_no_content) + { + load_no_content_hook = load_no_content; + + /* load_no_content gets set in this callback. */ + retro_set_environment(environ_cb_get_system_info); + + /* It's possible that we just set get_system_info callback + * to the currently running core. + * + * Make sure we reset it to the actual environment callback. + * Ignore any environment callbacks here in case we're running + * on the non-current core. */ + ignore_environment_cb = true; + retro_set_environment(rarch_environment_cb); + ignore_environment_cb = false; + } + + retro_get_system_info(&dummy_info); + memcpy(info, &dummy_info, sizeof(*info)); + + info->library_name = strdup(dummy_info.library_name); + info->library_version = strdup(dummy_info.library_version); + if (dummy_info.valid_extensions) + info->valid_extensions = strdup(dummy_info.valid_extensions); + return true; +} +#endif /** * libretro_get_system_info: @@ -296,6 +339,7 @@ static dylib_t libretro_get_system_info_lib(const char *path, bool libretro_get_system_info(const char *path, struct retro_system_info *info, bool *load_no_content) { +#ifdef HAVE_DYNAMIC struct retro_system_info dummy_info = {0}; dylib_t lib = libretro_get_system_info_lib(path, &dummy_info, load_no_content); @@ -308,51 +352,13 @@ bool libretro_get_system_info(const char *path, if (dummy_info.valid_extensions) info->valid_extensions = strdup(dummy_info.valid_extensions); dylib_close(lib); +#else + if (!libretro_get_system_info_static(info, load_no_content)) + return false; +#endif return true; } -static void load_dynamic_core(void) -{ - function_t sym = dylib_proc(NULL, "retro_init"); - - if (sym) - { - /* Try to verify that -lretro was not linked in from other modules - * since loading it dynamically and with -l will fail hard. */ - RARCH_ERR("Serious problem. RetroArch wants to load libretro cores" - "dyamically, but it is already linked.\n"); - RARCH_ERR("This could happen if other modules RetroArch depends on " - "link against libretro directly.\n"); - RARCH_ERR("Proceeding could cause a crash. Aborting ...\n"); - retroarch_fail(1, "init_libretro_sym()"); - } - - if (string_is_empty(config_get_active_core_path())) - { - RARCH_ERR("RetroArch is built for dynamic libretro cores, but " - "libretro_path is not set. Cannot continue.\n"); - retroarch_fail(1, "init_libretro_sym()"); - } - - /* Need to use absolute path for this setting. It can be - * saved to content history, and a relative path would - * break in that scenario. */ - path_resolve_realpath( - config_get_active_core_path_ptr(), - config_get_active_core_path_size()); - - RARCH_LOG("Loading dynamic libretro core from: \"%s\"\n", - config_get_active_core_path()); - lib_handle = dylib_load(config_get_active_core_path()); - if (!lib_handle) - { - RARCH_ERR("Failed to open libretro core: \"%s\"\n", - config_get_active_core_path()); - RARCH_ERR("Error(s): %s\n", dylib_error()); - retroarch_fail(1, "load_dynamic()"); - } -} -#endif /** * load_symbols: @@ -934,7 +940,6 @@ bool rarch_environment_cb(unsigned cmd, void *data) { unsigned p; settings_t *settings = config_get_ptr(); - global_t *global = global_get_ptr(); rarch_system_info_t *system = NULL; runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system); @@ -1025,15 +1030,17 @@ bool rarch_environment_cb(unsigned cmd, void *data) if (runloop_ctl(RUNLOOP_CTL_GET_CONTENT_PATH, &fullpath) && fullpath) { + char temp_path[PATH_MAX_LENGTH] = {0}; + RARCH_WARN("SYSTEM DIR is empty, assume CONTENT DIR %s\n", fullpath); - fill_pathname_basedir(global->dir.systemdir, fullpath, - sizeof(global->dir.systemdir)); + fill_pathname_basedir(temp_path, fullpath, sizeof(temp_path)); + dir_set_system(temp_path); } - *(const char**)data = global->dir.systemdir; + *(const char**)data = dir_get_system_ptr(); RARCH_LOG("Environ SYSTEM_DIRECTORY: \"%s\".\n", - global->dir.systemdir); + dir_get_system()); } else { @@ -1045,7 +1052,7 @@ bool rarch_environment_cb(unsigned cmd, void *data) break; case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY: - *(const char**)data = retroarch_get_current_savefile_dir(); + *(const char**)data = path_get_current_savefile_dir(); break; case RETRO_ENVIRONMENT_GET_USERNAME: @@ -1259,7 +1266,7 @@ bool rarch_environment_cb(unsigned cmd, void *data) { const char **path = (const char**)data; #ifdef HAVE_DYNAMIC - *path = config_get_active_core_path(); + *path = path_get_core(); #else *path = NULL; #endif diff --git a/dynamic.h b/dynamic.h index 9ea5a6cce0..af0281b957 100644 --- a/dynamic.h +++ b/dynamic.h @@ -23,10 +23,6 @@ #include "core_type.h" -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - RETRO_BEGIN_DECLS /** @@ -49,7 +45,6 @@ RETRO_BEGIN_DECLS void libretro_get_environment_info(void (*)(retro_environment_t), bool *load_no_content); -#ifdef HAVE_DYNAMIC /** * libretro_get_system_info: * @path : Path to libretro library. @@ -64,21 +59,6 @@ void libretro_get_environment_info(void (*)(retro_environment_t), **/ bool libretro_get_system_info(const char *path, struct retro_system_info *info, bool *load_no_content); -#else -/** - * libretro_get_system_info_static: - * @info : System info information. - * @load_no_content : If true, core should be able to auto-start - * without any content loaded. - * - * Gets system info from the current statically linked libretro library. - * The struct returned must be freed as strings are allocated dynamically. - * - * Returns: true (1) if successful, otherwise false (0). - **/ -bool libretro_get_system_info_static(struct retro_system_info *info, - bool *load_no_content); -#endif /** * libretro_free_system_info: diff --git a/fetch-submodules.sh b/fetch-submodules.sh index 3cb3f53eed..7515dbbf32 100755 --- a/fetch-submodules.sh +++ b/fetch-submodules.sh @@ -75,3 +75,5 @@ fetch_git "https://github.com/libretro/common-overlays.git" "media/overlays" fetch_git "https://github.com/libretro/retroarch-assets.git" "media/assets" fetch_git "https://github.com/libretro/retroarch-joypad-autoconfig.git" "media/autoconfig" fetch_git "https://github.com/libretro/libretro-database.git" "media/libretrodb" + +git submodule update --init --recursive diff --git a/file_path_special.c b/file_path_special.c index 76a569a7c0..3c094566ba 100644 --- a/file_path_special.c +++ b/file_path_special.c @@ -45,6 +45,7 @@ #include "configuration.h" #include "file_path_special.h" +#include "paths.h" #include "runloop.h" #include "verbosity.h" @@ -284,13 +285,13 @@ void fill_pathname_application_special(char *s, size_t len, enum application_spe case APPLICATION_SPECIAL_DIRECTORY_CONFIG: { settings_t *settings = config_get_ptr(); - global_t *global = global_get_ptr(); + /* Try config directory setting first, * fallback to the location of the current configuration file. */ if (!string_is_empty(settings->directory.menu_config)) strlcpy(s, settings->directory.menu_config, len); - else if (!string_is_empty(global->path.config)) - fill_pathname_basedir(s, global->path.config, len); + else if (!path_is_config_empty()) + fill_pathname_basedir(s, path_get_config(), len); } break; case APPLICATION_SPECIAL_DIRECTORY_ASSETS_ZARCH_ICONS: diff --git a/frontend/drivers/platform_ctr.c b/frontend/drivers/platform_ctr.c index fee219113f..a172d449bf 100644 --- a/frontend/drivers/platform_ctr.c +++ b/frontend/drivers/platform_ctr.c @@ -22,6 +22,11 @@ #include <3ds.h> #include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + #ifndef IS_SALAMANDER #include #endif diff --git a/frontend/drivers/platform_darwin.m b/frontend/drivers/platform_darwin.m index d573974e8b..1a9a38b1f9 100644 --- a/frontend/drivers/platform_darwin.m +++ b/frontend/drivers/platform_darwin.m @@ -45,6 +45,7 @@ #endif #include +#include #include #include #include @@ -105,32 +106,6 @@ typedef enum CFAllDomainsMask = 0x0ffff /* All domains: all of the above and future items */ } CFDomainMask; -#ifndef __has_feature -/* Compatibility with non-Clang compilers. */ -#define __has_feature(x) 0 -#endif - -#ifndef CF_RETURNS_RETAINED -#if __has_feature(attribute_cf_returns_retained) -#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) -#else -#define CF_RETURNS_RETAINED -#endif -#endif - -#ifndef NS_INLINE -#define NS_INLINE inline -#endif - -NS_INLINE CF_RETURNS_RETAINED CFTypeRef CFBridgingRetainCompat(id X) -{ -#if __has_feature(objc_arc) - return (__bridge_retained CFTypeRef)X; -#else - return X; -#endif -} - static NSSearchPathDirectory NSConvertFlagsCF(unsigned flags) { switch (flags) diff --git a/frontend/drivers/platform_emscripten.c b/frontend/drivers/platform_emscripten.c index 5941702c6a..cfad3f4497 100644 --- a/frontend/drivers/platform_emscripten.c +++ b/frontend/drivers/platform_emscripten.c @@ -39,6 +39,7 @@ #include "../../retroarch.h" #include "../../runloop.h" #include "../../command.h" +#include "../../tasks/tasks_internal.h" #include "../../file_path_special.h" static void emscripten_mainloop(void) @@ -57,7 +58,7 @@ static void emscripten_mainloop(void) void cmd_savefiles(void) { - command_event(CMD_EVENT_SAVEFILES, NULL); + event_save_files(); } void cmd_save_state(void) @@ -126,7 +127,7 @@ static void frontend_emscripten_get_env(int *argc, char *argv[], fill_pathname_join(g_defaults.dir.menu_content, user_path, "content", sizeof(g_defaults.dir.menu_content)); fill_pathname_join(g_defaults.dir.core_assets, user_path, - "downloads", sizeof(g_defaults.dir.core_assets)); + "content/downloads", sizeof(g_defaults.dir.core_assets)); fill_pathname_join(g_defaults.dir.playlist, user_path, "playlists", sizeof(g_defaults.dir.playlist)); fill_pathname_join(g_defaults.dir.remap, g_defaults.dir.menu_config, diff --git a/frontend/drivers/platform_gx.c b/frontend/drivers/platform_gx.c index 4c37656e47..4590fecb80 100644 --- a/frontend/drivers/platform_gx.c +++ b/frontend/drivers/platform_gx.c @@ -24,6 +24,10 @@ #include #include +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + #if defined(HW_RVL) && !defined(IS_SALAMANDER) #include #include "../../memory/wii/mem2_manager.h" @@ -61,6 +65,7 @@ static enum frontend_fork gx_fork_mode = FRONTEND_FORK_NONE; #endif #ifndef IS_SALAMANDER +#include "../../paths.h" enum { @@ -410,7 +415,7 @@ static void frontend_gx_process_args(int *argc, char *argv[]) /* A big hack: sometimes Salamander doesn't save the new core * it loads on first boot, so we make sure * active core path is set here. */ - if (config_active_core_path_is_empty() && *argc >= 1 && strrchr(argv[0], '/')) + if (path_is_core_empty() && *argc >= 1 && strrchr(argv[0], '/')) { char path[PATH_MAX_LENGTH] = {0}; strlcpy(path, strrchr(argv[0], '/') + 1, sizeof(path)); diff --git a/frontend/drivers/platform_null.c b/frontend/drivers/platform_null.c index 056f657116..4dc3800c23 100644 --- a/frontend/drivers/platform_null.c +++ b/frontend/drivers/platform_null.c @@ -14,10 +14,9 @@ * If not, see . */ -#include "../frontend_driver.h" - #include -#include + +#include "../frontend_driver.h" frontend_ctx_driver_t frontend_ctx_null = { NULL, /* environment_get */ diff --git a/frontend/drivers/platform_ps3.c b/frontend/drivers/platform_ps3.c index 746dee3ab8..3747d7a19b 100644 --- a/frontend/drivers/platform_ps3.c +++ b/frontend/drivers/platform_ps3.c @@ -32,6 +32,7 @@ #endif #include +#include #include #ifndef IS_SALAMANDER #include @@ -125,7 +126,7 @@ static void frontend_ps3_get_environment_settings(int *argc, char *argv[], /* not launched from external launcher, set default path */ // second param is multiMAN SELF file if(path_file_exists(argv[2]) && *argc > 1 - && (!strcmp(argv[2], EMULATOR_CONTENT_DIR))) + && (string_is_equal(argv[2], EMULATOR_CONTENT_DIR))) { multiman_detected = true; RARCH_LOG("Started from multiMAN, auto-game start enabled.\n"); diff --git a/frontend/drivers/platform_psp.c b/frontend/drivers/platform_psp.c index 16aabd32a8..72725d9f06 100644 --- a/frontend/drivers/platform_psp.c +++ b/frontend/drivers/platform_psp.c @@ -18,6 +18,10 @@ #include #include +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + #ifdef VITA #include #include diff --git a/frontend/drivers/platform_xdk.cpp b/frontend/drivers/platform_xdk.cpp index 82dec30de1..b5a92a6161 100644 --- a/frontend/drivers/platform_xdk.cpp +++ b/frontend/drivers/platform_xdk.cpp @@ -22,6 +22,11 @@ #include #include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + #ifndef IS_SALAMANDER #include #endif diff --git a/frontend/frontend.c b/frontend/frontend.c index 577f8409b1..13df9a5a2b 100644 --- a/frontend/frontend.c +++ b/frontend/frontend.c @@ -30,8 +30,8 @@ #include "../ui/ui_companion_driver.h" #include "../tasks/tasks_internal.h" -#include "../configuration.h" #include "../driver.h" +#include "../paths.h" #include "../retroarch.h" #include "../runloop.h" @@ -60,9 +60,7 @@ void main_exit(void *args) #endif frontend_driver_deinit(args); - frontend_driver_exitspawn( - config_get_active_core_path_ptr(), - config_get_active_core_path_size()); + frontend_driver_exitspawn(path_get_core_ptr(), path_get_core_size()); rarch_ctl(RARCH_CTL_DESTROY, NULL); diff --git a/frontend/frontend_driver.c b/frontend/frontend_driver.c index 070a87bd67..1e6c6073f3 100644 --- a/frontend/frontend_driver.c +++ b/frontend/frontend_driver.c @@ -17,6 +17,7 @@ #include #include +#include #ifdef HAVE_CONFIG_H #include "../config.h" @@ -82,7 +83,7 @@ frontend_ctx_driver_t *frontend_ctx_find_driver(const char *ident) for (i = 0; frontend_ctx_drivers[i]; i++) { - if (!strcmp(frontend_ctx_drivers[i]->ident, ident)) + if (string_is_equal(frontend_ctx_drivers[i]->ident, ident)) return frontend_ctx_drivers[i]; } diff --git a/frontend/frontend_driver.h b/frontend/frontend_driver.h index 66f4bc348a..b20751aee0 100644 --- a/frontend/frontend_driver.h +++ b/frontend/frontend_driver.h @@ -23,10 +23,6 @@ #include #include -#ifdef HAVE_CONFIG_H -#include "../config.h" -#endif - RETRO_BEGIN_DECLS enum frontend_powerstate diff --git a/frontend/frontend_salamander.c b/frontend/frontend_salamander.c index a8581aab09..7a34499e95 100644 --- a/frontend/frontend_salamander.c +++ b/frontend/frontend_salamander.c @@ -118,12 +118,9 @@ static void find_and_set_first_file(char *s, size_t len, static void salamander_init(char *s, size_t len) { /* normal executable loading path */ - bool config_file_exists = false; + bool config_exists = config_file_exists(g_defaults.path.config); - if (path_file_exists(g_defaults.path.config)) - config_file_exists = true; - - if (config_file_exists) + if (config_exists) { char tmp_str[PATH_MAX_LENGTH]; config_file_t * conf = (config_file_t*)config_file_new(g_defaults.path.config); @@ -133,7 +130,7 @@ static void salamander_init(char *s, size_t len) config_get_array(conf, "libretro_path", tmp_str, sizeof(tmp_str)); config_file_free(conf); - if (strcmp(tmp_str, "builtin") != 0) + if (!string_is_equal(tmp_str, "builtin")) strlcpy(s, tmp_str, len); } #ifdef GEKKO @@ -141,12 +138,12 @@ static void salamander_init(char *s, size_t len) * the file is there when it doesn't. */ else { - config_file_exists = false; + config_exists = false; } #endif } - if (!config_file_exists || !strcmp(s, "")) + if (!config_exists || string_is_equal(s, "")) { char executable_name[PATH_MAX_LENGTH]; @@ -157,7 +154,7 @@ static void salamander_init(char *s, size_t len) else RARCH_LOG("Start [%s] found in retroarch.cfg.\n", s); - if (!config_file_exists) + if (!config_exists) { config_file_t *conf = (config_file_t*)config_file_new(NULL); diff --git a/gfx/common/win32_common.cpp b/gfx/common/win32_common.cpp index 7559ff6532..0ea3b22de8 100644 --- a/gfx/common/win32_common.cpp +++ b/gfx/common/win32_common.cpp @@ -25,6 +25,7 @@ #include "../../configuration.h" #include "../../verbosity.h" #include "../../driver.h" +#include "../../paths.h" #include "../../runloop.h" #include "../../tasks/tasks_internal.h" #include "../../core_info.h" @@ -41,7 +42,7 @@ #include #include "../../retroarch.h" #include "../video_thread_wrapper.h" -#include +#include #ifndef _MSC_VER extern "C" { #endif @@ -287,7 +288,7 @@ static int win32_drag_query_file(HWND hwnd, WPARAM wparam) runloop_ctl(RUNLOOP_CTL_SET_CONTENT_PATH,szFilename); - if (!string_is_empty(config_get_active_core_path())) + if (!path_is_core_empty()) { unsigned i; core_info_t *current_core = NULL; @@ -301,7 +302,7 @@ static int win32_drag_query_file(HWND hwnd, WPARAM wparam) if(!string_is_equal(info->systemname, current_core->systemname)) break; - if(string_is_equal(config_get_active_core_path(), info->path)) + if(string_is_equal(path_get_core(), info->path)) { /* Our previous core supports the current rom */ content_ctx_info_t content_info = {0}; diff --git a/gfx/common/x11_common.c b/gfx/common/x11_common.c index 7f1058b785..f1d4bf54c1 100644 --- a/gfx/common/x11_common.c +++ b/gfx/common/x11_common.c @@ -34,6 +34,15 @@ #include "../../verbosity.h" #include "../../runloop.h" +#ifdef HAVE_DBUS +#include +static DBusConnection* dbus_connection = NULL; +static unsigned int dbus_screensaver_cookie = 0; +#endif + +static bool xdg_screensaver_available = true; +static bool xdg_screensaver_running = false; + Colormap g_x11_cmap; Window g_x11_win; Display *g_x11_dpy; @@ -56,6 +65,133 @@ unsigned g_x11_screen; #define MOVERESIZE_X_SHIFT 8 #define MOVERESIZE_Y_SHIFT 9 +#ifdef HAVE_DBUS +static void dbus_ensure_connection(void) +{ + DBusError err; + int ret; + + dbus_error_init(&err); + + dbus_connection = dbus_bus_get_private(DBUS_BUS_SESSION, &err); + + if (dbus_error_is_set(&err)) + { + RARCH_LOG("[DBus]: Failed to get DBus connection. Screensaver will not be suspended via DBus.\n"); + dbus_error_free(&err); + } + + if (dbus_connection) + dbus_connection_set_exit_on_disconnect(dbus_connection, true); +} + +static void dbus_close_connection(void) +{ + if (!dbus_connection) + return; + + dbus_connection_close(dbus_connection); + dbus_connection_unref(dbus_connection); + dbus_connection = NULL; +} + +static bool dbus_screensaver_inhibit(void) +{ + const char *app = "RetroArch"; + const char *reason = "Playing a game"; + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + bool ret = false; + + if (!dbus_connection) + return false; /* DBus connection was not obtained */ + + if (dbus_screensaver_cookie > 0) + return true; /* Already inhibited */ + + msg = dbus_message_new_method_call("org.freedesktop.ScreenSaver", + "/org/freedesktop/ScreenSaver", + "org.freedesktop.ScreenSaver", + "Inhibit"); + + if (!msg) + return false; + + if (!dbus_message_append_args(msg, + DBUS_TYPE_STRING, &app, + DBUS_TYPE_STRING, &reason, + DBUS_TYPE_INVALID)) + { + dbus_message_unref(msg); + return false; + } + + reply = dbus_connection_send_with_reply_and_block(dbus_connection, + msg, 300, NULL); + + if (reply != NULL) + { + if (!dbus_message_get_args(reply, NULL, + DBUS_TYPE_UINT32, &dbus_screensaver_cookie, + DBUS_TYPE_INVALID)) + dbus_screensaver_cookie = 0; + else + ret = true; + + dbus_message_unref(reply); + } + + dbus_message_unref(msg); + + if (dbus_screensaver_cookie == 0) + { + RARCH_ERR("[DBus]: Failed to suspend screensaver via DBus.\n"); + } + else + { + RARCH_LOG("[DBus]: Suspended screensaver via DBus.\n"); + } + + return ret; +} + +static void dbus_screensaver_uninhibit(void) +{ + DBusMessage *msg = NULL; + + if (!dbus_connection) + return; + + if (dbus_screensaver_cookie == 0) + return; + + msg = dbus_message_new_method_call("org.freedesktop.ScreenSaver", + "/org/freedesktop/ScreenSaver", + "org.freedesktop.ScreenSaver", + "UnInhibit"); + if (!msg) + return; + + dbus_message_append_args(msg, + DBUS_TYPE_UINT32, &dbus_screensaver_cookie, + DBUS_TYPE_INVALID); + + if (dbus_connection_send(dbus_connection, msg, NULL)) + dbus_connection_flush(dbus_connection); + dbus_message_unref(msg); + + dbus_screensaver_cookie = 0; +} + +/* Returns false when fallback should be attempted */ +bool x11_suspend_screensaver_dbus(bool enable) +{ + if (enable) return dbus_screensaver_inhibit(); + dbus_screensaver_uninhibit(); + return false; +} +#endif + static void x11_hide_mouse(Display *dpy, Window win) { static char bm_no_data[] = {0, 0, 0, 0, 0, 0, 0, 0}; @@ -94,13 +230,13 @@ void x11_windowed_fullscreen(Display *dpy, Window win) XA_INIT(_NET_WM_STATE); XA_INIT(_NET_WM_STATE_FULLSCREEN); - xev.xclient.type = ClientMessage; - xev.xclient.send_event = True; + xev.xclient.type = ClientMessage; + xev.xclient.send_event = True; xev.xclient.message_type = XA_NET_WM_STATE; - xev.xclient.window = win; - xev.xclient.format = 32; - xev.xclient.data.l[0] = _NET_WM_STATE_ADD; - xev.xclient.data.l[1] = XA_NET_WM_STATE_FULLSCREEN; + xev.xclient.window = win; + xev.xclient.format = 32; + xev.xclient.data.l[0] = _NET_WM_STATE_ADD; + xev.xclient.data.l[1] = XA_NET_WM_STATE_FULLSCREEN; XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureRedirectMask | SubstructureNotifyMask, @@ -145,33 +281,81 @@ void x11_set_window_attr(Display *dpy, Window win) x11_set_window_class(dpy, win); } -void x11_suspend_screensaver(Window wnd, bool enable) +static void xdg_screensaver_inhibit(Window wnd) { int ret; - char cmd[64] = {0}; - static bool screensaver_na = false; + char cmd[64] = {0}; - if (!enable) - return; - - if (screensaver_na) + if (!xdg_screensaver_available) return; - RARCH_LOG("Suspending screensaver (X11).\n"); + if (xdg_screensaver_running) + return; + + RARCH_LOG("Suspending screensaver (X11, xdg-screensaver).\n"); snprintf(cmd, sizeof(cmd), "xdg-screensaver suspend 0x%x", (int)wnd); ret = system(cmd); if (ret == -1) { - screensaver_na = true; + xdg_screensaver_available = false; RARCH_WARN("Failed to launch xdg-screensaver.\n"); } else if (WEXITSTATUS(ret)) { - screensaver_na = true; + xdg_screensaver_available = false; RARCH_WARN("Could not suspend screen saver.\n"); } + else + { + xdg_screensaver_running = true; + } +} + +static void xdg_screensaver_uninhibit(Window wnd) +{ + int ret; + char cmd[64] = {0}; + + if (!xdg_screensaver_available) + return; + + if (!xdg_screensaver_running) + return; + + RARCH_LOG("Resuming screensaver (X11, xdg-screensaver).\n"); + + snprintf(cmd, sizeof(cmd), "xdg-screensaver resume 0x%x", (int)wnd); + + ret = system(cmd); + + if (ret == -1) + { + xdg_screensaver_available = false; + RARCH_WARN("Failed to launch xdg-screensaver.\n"); + } + else if (WEXITSTATUS(ret)) + { + xdg_screensaver_available = false; + RARCH_WARN("Could not suspend screen saver.\n"); + } +} + +void x11_suspend_screensaver_xdg_screensaver(Window wnd, bool enable) +{ + if (enable) + xdg_screensaver_inhibit(wnd); + xdg_screensaver_uninhibit(wnd); +} + +void x11_suspend_screensaver(Window wnd, bool enable) +{ +#ifdef HAVE_DBUS + if (x11_suspend_screensaver_dbus(enable)) + return; +#endif + x11_suspend_screensaver_xdg_screensaver(wnd, enable); } static bool get_video_mode(Display *dpy, unsigned width, unsigned height, @@ -293,11 +477,11 @@ bool x11_get_xinerama_coord(Display *dpy, int screen, unsigned x11_get_xinerama_monitor(Display *dpy, int x, int y, int w, int h) { - int i, num_screens = 0; - unsigned monitor = 0; - int largest_area = 0; - + int i, num_screens = 0; + unsigned monitor = 0; + int largest_area = 0; XineramaScreenInfo *info = x11_query_screens(dpy, &num_screens); + RARCH_LOG("[X11]: Xinerama screens: %d.\n", num_screens); for (i = 0; i < num_screens; i++) @@ -308,8 +492,8 @@ unsigned x11_get_xinerama_monitor(Display *dpy, int x, int y, int max_ty = MAX(y, info[i].y_org); int min_by = MIN(y + h, info[i].y_org + info[i].height); - int len_x = min_rx - max_lx; - int len_y = min_by - max_ty; + int len_x = min_rx - max_lx; + int len_y = min_by - max_ty; /* The whole window is outside the screen. */ if (len_x < 0 || len_y < 0) @@ -374,13 +558,12 @@ void x11_destroy_input_context(XIM *xim, XIC *xic) bool x11_get_metrics(void *data, enum display_metric_types type, float *value) { - int pixels_x, pixels_y, physical_width, physical_height; unsigned screen_no = 0; Display *dpy = (Display*)XOpenDisplay(NULL); - pixels_x = DisplayWidth(dpy, screen_no); - pixels_y = DisplayHeight(dpy, screen_no); - physical_width = DisplayWidthMM(dpy, screen_no); - physical_height = DisplayHeightMM(dpy, screen_no); + int pixels_x = DisplayWidth(dpy, screen_no); + int pixels_y = DisplayHeight(dpy, screen_no); + int physical_width = DisplayWidthMM(dpy, screen_no); + int physical_height = DisplayHeightMM(dpy, screen_no); (void)pixels_y; @@ -533,6 +716,11 @@ bool x11_connect(void) return false; } +#ifdef HAVE_DBUS + dbus_ensure_connection(); +#endif + + return true; } @@ -573,6 +761,11 @@ void x11_window_destroy(bool fullscreen) if (!fullscreen) XDestroyWindow(g_x11_dpy, g_x11_win); g_x11_win = None; + +#ifdef HAVE_DBUS + dbus_screensaver_uninhibit(); + dbus_close_connection(); +#endif } void x11_colormap_destroy(void) diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index e2d0c05d4f..d362e0e07c 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -2,7 +2,7 @@ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2016 - Daniel De Matteis * Copyright (C) 2012-2015 - Michael Lelli - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. @@ -104,7 +104,7 @@ static const GLfloat vertexes_flipped[] = { }; /* Used when rendering to an FBO. - * Texture coords have to be aligned + * Texture coords have to be aligned * with vertex coordinates. */ static const GLfloat vertexes[] = { 0, 0, @@ -341,8 +341,8 @@ void gl_set_viewport(void *data, unsigned viewport_width, if (fabsf(device_aspect - desired_aspect) < 0.0001f) { - /* If the aspect ratios of screen and desired aspect - * ratio are sufficiently equal (floating point stuff), + /* If the aspect ratios of screen and desired aspect + * ratio are sufficiently equal (floating point stuff), * assume they are actually equal. */ } @@ -401,7 +401,7 @@ static bool gl_shader_init(gl_t *gl) video_shader_ctx_init_t init_data; enum rarch_shader_type type; settings_t *settings = config_get_ptr(); - const char *shader_path = (settings->video.shader_enable + const char *shader_path = (settings->video.shader_enable && *settings->path.shader) ? settings->path.shader : NULL; if (!gl) @@ -411,7 +411,7 @@ static bool gl_shader_init(gl_t *gl) } type = video_shader_parse_type(shader_path, - gl_query_core_context_in_use() + gl_query_core_context_in_use() ? RARCH_SHADER_GLSL : DEFAULT_SHADER_TYPE); switch (type) @@ -521,7 +521,7 @@ static void gl_update_input_size(gl_t *gl, unsigned width, bool set_coords = false; - if ((width != gl->last_width[gl->tex_index] || + if ((width != gl->last_width[gl->tex_index] || height != gl->last_height[gl->tex_index]) && gl->empty_buf) { /* Resolution change. Need to clear out texture. */ @@ -547,12 +547,12 @@ static void gl_update_input_size(gl_t *gl, unsigned width, set_coords = true; } - else if ((width != + else if ((width != gl->last_width[(gl->tex_index + gl->textures - 1) % gl->textures]) || - (height != + (height != gl->last_height[(gl->tex_index + gl->textures - 1) % gl->textures])) { - /* We might have used different texture coordinates + /* We might have used different texture coordinates * last frame. Edge case if resolution changes very rapidly. */ set_coords = true; } @@ -582,7 +582,7 @@ static void gl_init_textures_data(gl_t *gl) gl->prev_info[i].tex_size[0] = gl->tex_w; gl->prev_info[i].input_size[1] = gl->tex_h; gl->prev_info[i].tex_size[1] = gl->tex_h; - memcpy(gl->prev_info[i].coord, tex_coords, sizeof(tex_coords)); + memcpy(gl->prev_info[i].coord, tex_coords, sizeof(tex_coords)); } } @@ -598,7 +598,7 @@ static void gl_init_textures_reference(gl_t *gl, unsigned i, #ifdef HAVE_PSGL glTextureReferenceSCE(GL_TEXTURE_2D, 1, - gl->tex_w, gl->tex_h, 0, + gl->tex_w, gl->tex_h, 0, internal_fmt, gl->tex_w * gl->base_size, gl->tex_w * gl->tex_h * i * gl->base_size); @@ -632,7 +632,7 @@ static void gl_init_textures(gl_t *gl, const video_info_t *video) glBindBuffer(GL_TEXTURE_REFERENCE_BUFFER_SCE, gl->pbo); glBufferData(GL_TEXTURE_REFERENCE_BUFFER_SCE, - gl->tex_w * gl->tex_h * gl->base_size * gl->textures, + gl->tex_w * gl->tex_h * gl->base_size * gl->textures, NULL, GL_STREAM_DRAW); #endif @@ -684,7 +684,7 @@ static INLINE void gl_copy_frame(gl_t *gl, const void *frame, #if defined(HAVE_PSGL) { unsigned h; - size_t buffer_addr = gl->tex_w * gl->tex_h * + size_t buffer_addr = gl->tex_w * gl->tex_h * gl->tex_index * gl->base_size; size_t buffer_stride = gl->tex_w * gl->base_size; const uint8_t *frame_copy = frame; @@ -729,7 +729,7 @@ static INLINE void gl_copy_frame(gl_t *gl, const void *frame, { bool use_rgba = video_driver_supports_rgba(); - glPixelStorei(GL_UNPACK_ALIGNMENT, + glPixelStorei(GL_UNPACK_ALIGNMENT, video_pixel_get_alignment(width * gl->base_size)); /* Fallback for GLES devices without GL_BGRA_EXT. */ @@ -761,7 +761,7 @@ static INLINE void gl_copy_frame(gl_t *gl, const void *frame, if (width != pitch_width) { - /* Slow path - conv_buffer is preallocated + /* Slow path - conv_buffer is preallocated * just in case we hit this path. */ unsigned h; @@ -777,7 +777,7 @@ static INLINE void gl_copy_frame(gl_t *gl, const void *frame, glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, gl->texture_type, - gl->texture_fmt, data_buf); + gl->texture_fmt, data_buf); } } #else @@ -790,7 +790,7 @@ static INLINE void gl_copy_frame(gl_t *gl, const void *frame, /* Convert to 32-bit textures on desktop GL. * * It is *much* faster (order of magnitude on my setup) - * to use a custom SIMD-optimized conversion routine + * to use a custom SIMD-optimized conversion routine * than letting GL do it. */ video_frame_convert_rgb16_to_rgb32( &gl->scaler, @@ -847,7 +847,7 @@ void gl_load_texture_data( glBindTexture(GL_TEXTURE_2D, id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); - + if (!gl_check_capability(GL_CAPS_MIPMAP)) { /* Assume no mipmapping support. */ @@ -1141,7 +1141,7 @@ static bool gl_frame(void *data, const void *frame, { gl_check_fbo_dimensions(gl); - /* Go back to what we're supposed to do, + /* Go back to what we're supposed to do, * render to FBO #0. */ gl_renderchain_start_render(gl); } @@ -1156,7 +1156,7 @@ static bool gl_frame(void *data, const void *frame, glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); /* Can be NULL for frame dupe / NULL render. */ - if (frame) + if (frame) { #ifdef HAVE_FBO if (!gl->hw_render_fbo_init) @@ -1166,13 +1166,13 @@ static bool gl_frame(void *data, const void *frame, gl_copy_frame(gl, frame, frame_width, frame_height, pitch); } - /* No point regenerating mipmaps + /* No point regenerating mipmaps * if there are no new frames. */ if (gl->tex_mipmap && gl_check_capability(GL_CAPS_MIPMAP)) glGenerateMipmap(GL_TEXTURE_2D); } - /* Have to reset rendering state which libretro core + /* Have to reset rendering state which libretro core * could easily have overridden. */ #ifdef HAVE_FBO if (gl->hw_render_fbo_init) @@ -1345,7 +1345,7 @@ static bool gl_frame(void *data, const void *frame, performance_counter_init(&gl_fence, "gl_fence"); performance_counter_start(&gl_fence); glClear(GL_COLOR_BUFFER_BIT); - gl->fences[gl->fence_count++] = + gl->fences[gl->fence_count++] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); while (gl->fence_count > settings->video.hard_sync_frames) @@ -1491,7 +1491,7 @@ static bool resolve_extensions(gl_t *gl, const char *context_ident) video_driver_get_hw_context(); gl_query_core_context_set(hwr->context_type == RETRO_HW_CONTEXT_OPENGL_CORE); - + if (gl_query_core_context_in_use()) { RARCH_LOG("[GL]: Using Core GL context.\n"); @@ -1504,10 +1504,10 @@ static bool resolve_extensions(gl_t *gl, const char *context_ident) } /* GL_RGB565 internal format support. - * Even though ES2 support is claimed, the format + * Even though ES2 support is claimed, the format * is not supported on older ATI catalyst drivers. * - * The speed gain from using GL_RGB565 is worth + * The speed gain from using GL_RGB565 is worth * adding some workarounds for. */ gl->have_es2_compat = gl_check_capability(GL_CAPS_ES2_COMPAT); @@ -1624,11 +1624,11 @@ static void gl_init_pbo_readback(gl_t *gl) #endif /* Only bother with this if we're doing GPU recording. - * Check recording_is_enabled() and not - * driver.recording_data, because recording is + * Check recording_is_enabled() and not + * driver.recording_data, because recording is * not initialized yet. */ - gl->pbo_readback_enable = settings->video.gpu_record + gl->pbo_readback_enable = settings->video.gpu_record && *recording_enabled; if (!gl->pbo_readback_enable) @@ -1640,7 +1640,7 @@ static void gl_init_pbo_readback(gl_t *gl) for (i = 0; i < 4; i++) { glBindBuffer(GL_PIXEL_PACK_BUFFER, gl->pbo_readback[i]); - glBufferData(GL_PIXEL_PACK_BUFFER, gl->vp.width * + glBufferData(GL_PIXEL_PACK_BUFFER, gl->vp.width * gl->vp.height * sizeof(uint32_t), NULL, GL_STREAM_READ); } @@ -1693,7 +1693,7 @@ static const gfx_ctx_driver_t *gl_get_context(gl_t *gl) api = GFX_CTX_OPENGL_API; api_name = "OpenGL"; #endif - + (void)api_name; gl_shared_context_use = settings->video.shared_context @@ -1896,7 +1896,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo goto error; /* Clear out potential error flags in case we use cached context. */ - glGetError(); + glGetError(); vendor = (const char*)glGetString(GL_VENDOR); renderer = (const char*)glGetString(GL_RENDERER); @@ -1934,7 +1934,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo temp_height = mode.height; mode.width = 0; mode.height = 0; - + /* Get real known video size, which might have been altered by context. */ if (temp_width != 0 && temp_height != 0) @@ -1946,7 +1946,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo hwr = video_driver_get_hw_context(); - gl->vertex_ptr = hwr->bottom_left_origin + gl->vertex_ptr = hwr->bottom_left_origin ? vertexes : vertexes_flipped; /* Better pipelining with GPU due to synchronous glSubTexImage. @@ -2010,7 +2010,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo gl->tex_w = gl->tex_h = (RARCH_SCALE_BASE * video->input_scale); gl->keep_aspect = video->force_aspect; - /* Apparently need to set viewport for passes + /* Apparently need to set viewport for passes * when we aren't using FBOs. */ gl_set_shader_viewport(gl, 0); gl_set_shader_viewport(gl, 1); @@ -2021,14 +2021,14 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo shader_filter.smooth = &force_smooth; if (video_shader_driver_filter_type(&shader_filter)) - gl->tex_min_filter = gl->tex_mipmap ? (force_smooth ? - GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) + gl->tex_min_filter = gl->tex_mipmap ? (force_smooth ? + GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) : (force_smooth ? GL_LINEAR : GL_NEAREST); else - gl->tex_min_filter = gl->tex_mipmap ? - (video->smooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) + gl->tex_min_filter = gl->tex_mipmap ? + (video->smooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) : (video->smooth ? GL_LINEAR : GL_NEAREST); - + gl->tex_mag_filter = min_filter_to_mag(gl->tex_min_filter); wrap_info.idx = 1; @@ -2055,7 +2055,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo gl->coords.lut_tex_coord = tex_coords; gl->coords.vertices = 4; - /* Empty buffer that we use to clear out + /* Empty buffer that we use to clear out * the texture with on res change. */ gl->empty_buf = calloc(sizeof(uint32_t), gl->tex_w * gl->tex_h); @@ -2072,7 +2072,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo #ifdef HAVE_FBO gl_renderchain_init(gl, gl->tex_w, gl->tex_h); - if (gl->hw_render_use && + if (gl->hw_render_use && !gl_init_hw_render(gl, gl->tex_w, gl->tex_h)) goto error; #endif @@ -2081,10 +2081,10 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo inp.input_data = input_data; video_context_driver_input_driver(&inp); - + if (settings->video.font_enable) { - if (!font_driver_init_first(NULL, NULL, gl, *settings->path.font + if (!font_driver_init_first(NULL, NULL, gl, *settings->path.font ? settings->path.font : NULL, settings->video.font_size, false, FONT_DRIVER_RENDER_OPENGL_API)) RARCH_ERR("[GL]: Failed to initialize font renderer.\n"); @@ -2094,7 +2094,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo gl_init_pbo_readback(gl); #endif - if (!gl_check_error(error_string)) + if (!gl_check_error(&error_string)) { RARCH_ERR("%s\n", error_string); free(error_string); @@ -2190,8 +2190,8 @@ static void gl_update_tex_filter_frame(gl_t *gl) gl->tex_mipmap = video_shader_driver_mipmap_input(&mip_level); gl->video_info.smooth = smooth; - new_filt = gl->tex_mipmap ? (smooth ? - GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) + new_filt = gl->tex_mipmap ? (smooth ? + GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) : (smooth ? GL_LINEAR : GL_NEAREST); if (new_filt == gl->tex_min_filter && wrap_mode == gl->wrap_mode) @@ -2366,7 +2366,7 @@ static bool gl_read_viewport(void *data, uint8_t *buffer) /* Don't readback if we're in menu mode. * We haven't buffered up enough frames yet, come back later. */ - if (!gl->pbo_readback_valid[gl->pbo_readback_index]) + if (!gl->pbo_readback_valid[gl->pbo_readback_index]) goto error; gl->pbo_readback_valid[gl->pbo_readback_index] = false; @@ -2404,15 +2404,15 @@ static bool gl_read_viewport(void *data, uint8_t *buffer) glUnmapBuffer(GL_PIXEL_PACK_BUFFER); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } - else /* Use slow synchronous readbacks. Use this with plain screenshots + else /* Use slow synchronous readbacks. Use this with plain screenshots as we don't really care about performance in this case. */ #endif { - /* GLES2 only guarantees GL_RGBA/GL_UNSIGNED_BYTE + /* GLES2 only guarantees GL_RGBA/GL_UNSIGNED_BYTE * readbacks so do just that. - * GLES2 also doesn't support reading back data - * from front buffer, so render a cached frame - * and have gl_frame() do the readback while it's + * GLES2 also doesn't support reading back data + * from front buffer, so render a cached frame + * and have gl_frame() do the readback while it's * in the back buffer. * * Keep codepath similar for GLES and desktop GL. @@ -2528,12 +2528,12 @@ bool gl_load_luts(const struct video_shader *shader, } #ifdef HAVE_OVERLAY -static bool gl_overlay_load(void *data, +static bool gl_overlay_load(void *data, const void *image_data, unsigned num_images) { unsigned i, j; gl_t *gl = (gl_t*)data; - const struct texture_image *images = + const struct texture_image *images = (const struct texture_image*)image_data; if (!gl) @@ -2557,8 +2557,8 @@ static bool gl_overlay_load(void *data, gl->overlay_color_coord = (GLfloat*) calloc(4 * 4 * num_images, sizeof(GLfloat)); - if ( !gl->overlay_vertex_coord - || !gl->overlay_tex_coord + if ( !gl->overlay_vertex_coord + || !gl->overlay_tex_coord || !gl->overlay_color_coord) return false; @@ -2567,7 +2567,7 @@ static bool gl_overlay_load(void *data, for (i = 0; i < num_images; i++) { - unsigned alignment = video_pixel_get_alignment(images[i].width + unsigned alignment = video_pixel_get_alignment(images[i].width * sizeof(uint32_t)); gl_load_texture_data(gl->overlay_tex[i], @@ -2724,7 +2724,7 @@ static void video_texture_load_gl( { /* Generate the OpenGL texture object */ glGenTextures(1, (GLuint*)id); - gl_load_texture_data((GLuint)*id, + gl_load_texture_data((GLuint)*id, RARCH_WRAP_EDGE, filter_type, 4 /* TODO/FIXME - dehardcode */, ti->width, ti->height, ti->pixels, diff --git a/gfx/drivers/gl_capabilities.c b/gfx/drivers/gl_capabilities.c index 82165327de..4e8d86aad3 100644 --- a/gfx/drivers/gl_capabilities.c +++ b/gfx/drivers/gl_capabilities.c @@ -2,7 +2,7 @@ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2016 - Daniel De Matteis * Copyright (C) 2012-2015 - Michael Lelli - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. @@ -72,31 +72,29 @@ static bool gl_query_extension(const char *ext) return ret; } -bool gl_check_error(char *error_string) +bool gl_check_error(char **error_string) { int error = glGetError(); switch (error) { case GL_INVALID_ENUM: - error_string = strdup("GL: Invalid enum."); + *error_string = strdup("GL: Invalid enum."); break; case GL_INVALID_VALUE: - error_string = strdup("GL: Invalid value."); + *error_string = strdup("GL: Invalid value."); break; case GL_INVALID_OPERATION: - error_string = strdup("GL: Invalid operation."); + *error_string = strdup("GL: Invalid operation."); break; case GL_OUT_OF_MEMORY: - error_string = strdup("GL: Out of memory."); + *error_string = strdup("GL: Out of memory."); break; case GL_NO_ERROR: return true; default: - error_string = strdup("Non specified GL error."); + *error_string = strdup("Non specified GL error."); break; } - - (void)error_string; return false; } @@ -114,7 +112,7 @@ bool gl_check_capability(enum gl_capability_enum enum_idx) if (version && sscanf(version, "%u.%u", &major, &minor) != 2) #endif major = minor = 0; - + (void)vendor; switch (enum_idx) @@ -167,15 +165,15 @@ bool gl_check_capability(enum gl_capability_enum enum_idx) && !gl_query_extension("EXT_framebuffer_object")) return false; - if (glGenFramebuffers - && glBindFramebuffer - && glFramebufferTexture2D - && glCheckFramebufferStatus - && glDeleteFramebuffers - && glGenRenderbuffers - && glBindRenderbuffer - && glFramebufferRenderbuffer - && glRenderbufferStorage + if (glGenFramebuffers + && glBindFramebuffer + && glFramebufferTexture2D + && glCheckFramebufferStatus + && glDeleteFramebuffers + && glGenRenderbuffers + && glBindRenderbuffer + && glFramebufferRenderbuffer + && glRenderbufferStorage && glDeleteRenderbuffers) return true; break; @@ -205,7 +203,7 @@ bool gl_check_capability(enum gl_capability_enum enum_idx) video_driver_get_hw_context(); if (major >= 3) return true; - if (hwr->stencil + if (hwr->stencil && !gl_query_extension("OES_packed_depth_stencil") && !gl_query_extension("EXT_packed_depth_stencil")) return false; @@ -273,7 +271,7 @@ bool gl_check_capability(enum gl_capability_enum enum_idx) if (major >= 3 || gl_query_extension("EXT_sRGB")) return true; #elif defined(HAVE_FBO) - if (gl_query_core_context_in_use() || + if (gl_query_core_context_in_use() || (gl_query_extension("EXT_texture_sRGB") && gl_query_extension("ARB_framebuffer_sRGB"))) return true; diff --git a/gfx/drivers/gl_capabilities.h b/gfx/drivers/gl_capabilities.h index ff229f99a6..e6bffe6aa6 100644 --- a/gfx/drivers/gl_capabilities.h +++ b/gfx/drivers/gl_capabilities.h @@ -2,7 +2,7 @@ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2016 - Daniel De Matteis * Copyright (C) 2012-2015 - Michael Lelli - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. @@ -138,7 +138,7 @@ #define RARCH_GL_FORMAT32 GL_UNSIGNED_INT_8_8_8_8_REV #define RARCH_GL_FORMAT16 GL_UNSIGNED_INT_8_8_8_8_REV -/* GL_RGB565 internal format isn't in desktop GL +/* GL_RGB565 internal format isn't in desktop GL * until 4.1 core (ARB_ES2_compatibility). * Check for this. */ #ifndef GL_RGB565 @@ -202,7 +202,7 @@ enum gl_capability_enum RETRO_BEGIN_DECLS -bool gl_check_error(char *error_string); +bool gl_check_error(char **error_string); bool gl_query_core_context_in_use(void); diff --git a/gfx/drivers/vg.c b/gfx/drivers/vg.c index 614b938a5e..cb7ccdd56c 100644 --- a/gfx/drivers/vg.c +++ b/gfx/drivers/vg.c @@ -15,6 +15,7 @@ */ #include +#include #include #include diff --git a/gfx/drivers_context/cocoa_gl_ctx.m b/gfx/drivers_context/cocoa_gl_ctx.m index 712cd5ff0a..d211fdff86 100644 --- a/gfx/drivers_context/cocoa_gl_ctx.m +++ b/gfx/drivers_context/cocoa_gl_ctx.m @@ -36,6 +36,7 @@ #endif #include +#include #import "../../ui/drivers/cocoa/cocoa_common.h" #include "../video_context_driver.h" diff --git a/gfx/drivers_font_renderer/freetype.c b/gfx/drivers_font_renderer/freetype.c index cd2a58c162..654037c824 100644 --- a/gfx/drivers_font_renderer/freetype.c +++ b/gfx/drivers_font_renderer/freetype.c @@ -112,7 +112,7 @@ static bool font_renderer_create_atlas(ft_font_renderer_t *handle) glyph->draw_offset_x = slot->bitmap_left; glyph->draw_offset_y = -slot->bitmap_top; - if (buffer[i]) + if (buffer[i] && slot->bitmap.buffer) memcpy(buffer[i], slot->bitmap.buffer, slot->bitmap.rows * pitches[i]); max_width = MAX(max_width, (unsigned)slot->bitmap.width); diff --git a/gfx/drivers_shader/glslang_util.cpp b/gfx/drivers_shader/glslang_util.cpp index d00f6d3002..12923f2c98 100644 --- a/gfx/drivers_shader/glslang_util.cpp +++ b/gfx/drivers_shader/glslang_util.cpp @@ -19,10 +19,11 @@ #include #include +#include #include #include #include -#include +#include #include "glslang_util.hpp" #include "glslang.hpp" @@ -227,7 +228,7 @@ const char *glslang_format_to_string(enum glslang_format fmt) static glslang_format glslang_find_format(const char *fmt) { #undef FMT -#define FMT(x) if (!strcmp(fmt, #x)) return SLANG_FORMAT_ ## x +#define FMT(x) if (string_is_equal(fmt, #x)) return SLANG_FORMAT_ ## x FMT(R8_UNORM); FMT(R8_UINT); FMT(R8_SINT); @@ -309,7 +310,7 @@ static bool glslang_parse_meta(const vector &lines, glslang_meta *meta) * if they are exactly the same. */ if (itr != end(meta->parameters)) { - if (itr->desc != desc || + if ( itr->desc != desc || itr->initial != initial || itr->minimum != minimum || itr->maximum != maximum || diff --git a/gfx/drivers_shader/shader_glsl.c b/gfx/drivers_shader/shader_glsl.c index 8d1fddd4a6..2849ae1896 100644 --- a/gfx/drivers_shader/shader_glsl.c +++ b/gfx/drivers_shader/shader_glsl.c @@ -1,6 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. @@ -93,7 +93,7 @@ struct shader_uniforms int frame_direction; int lut_texture[GFX_MAX_TEXTURES]; - + struct shader_uniforms_frame orig; struct shader_uniforms_frame feedback; struct shader_uniforms_frame pass[GFX_MAX_SHADERS]; @@ -290,7 +290,7 @@ static bool gl_glsl_compile_shader(glsl_shader_data_t *glsl, static bool gl_glsl_link_program(GLuint prog) { GLint status; - + glLinkProgram(prog); glGetProgramiv(prog, GL_LINK_STATUS, &status); @@ -358,7 +358,7 @@ static bool gl_glsl_compile_program( goto error; /* Clean up dead memory. We're not going to relink the program. - * Detaching first seems to kill some mobile drivers + * Detaching first seems to kill some mobile drivers * (according to the intertubes anyways). */ if (program->vprg) glDeleteShader(program->vprg); @@ -445,8 +445,8 @@ static bool gl_glsl_compile_programs( shader_prog_info.fragment = fragment; shader_prog_info.is_file = false; - if (!gl_glsl_compile_program(glsl, i, - &program[i], + if (!gl_glsl_compile_program(glsl, i, + &program[i], &shader_prog_info)) { RARCH_ERR("Failed to create GL program #%u.\n", i); @@ -472,7 +472,7 @@ static void gl_glsl_reset_attrib(glsl_shader_data_t *glsl) static void gl_glsl_set_vbo(GLfloat **buffer, size_t *buffer_elems, const GLfloat *data, size_t elems) { - if (elems != *buffer_elems || + if (elems != *buffer_elems || memcmp(data, *buffer, elems * sizeof(GLfloat))) { if (elems > *buffer_elems) @@ -712,12 +712,12 @@ static void *gl_glsl_init(void *data, const char *path) shader_support = glCreateProgram && glUseProgram && glCreateShader && glDeleteShader && glShaderSource && glCompileShader && glAttachShader && glDetachShader && glLinkProgram && glGetUniformLocation - && glUniform1i && glUniform1f && glUniform2fv && glUniform4fv + && glUniform1i && glUniform1f && glUniform2fv && glUniform4fv && glUniformMatrix4fv - && glGetShaderiv && glGetShaderInfoLog && glGetProgramiv - && glGetProgramInfoLog + && glGetShaderiv && glGetShaderInfoLog && glGetProgramiv + && glGetProgramInfoLog && glDeleteProgram && glGetAttachedShaders - && glGetAttribLocation && glEnableVertexAttribArray + && glGetAttribLocation && glEnableVertexAttribArray && glDisableVertexAttribArray && glVertexAttribPointer && glGenBuffers && glBufferData && glDeleteBuffers && glBindBuffer; @@ -766,9 +766,9 @@ static void *gl_glsl_init(void *data, const char *path) { RARCH_WARN("[GL]: Stock GLSL shaders will be used.\n"); glsl->shader->passes = 1; - glsl->shader->pass[0].source.string.vertex = + glsl->shader->pass[0].source.string.vertex = strdup(glsl_core ? stock_vertex_core : stock_vertex_modern); - glsl->shader->pass[0].source.string.fragment = + glsl->shader->pass[0].source.string.fragment = strdup(glsl_core ? stock_fragment_core : stock_fragment_modern); glsl->shader->modern = true; } @@ -846,7 +846,7 @@ static void *gl_glsl_init(void *data, const char *path) gl_glsl_find_uniforms(glsl, i, glsl->prg[i].id, &glsl->uniforms[i]); #ifdef GLSL_DEBUG - if (!gl_check_error(error_string)) + if (!gl_check_error(&error_string)) { RARCH_ERR("%s\n", error_string); free(error_string); @@ -877,17 +877,17 @@ static void *gl_glsl_init(void *data, const char *path) if (!glsl->state_tracker) RARCH_WARN("Failed to init state tracker.\n"); } - + glsl->prg[glsl->shader->passes + 1] = glsl->prg[0]; glsl->uniforms[glsl->shader->passes + 1] = glsl->uniforms[0]; if (glsl->shader->modern) { - shader_prog_info.vertex = - glsl_core ? + shader_prog_info.vertex = + glsl_core ? stock_vertex_core_blend : stock_vertex_modern_blend; - shader_prog_info.fragment = - glsl_core ? + shader_prog_info.fragment = + glsl_core ? stock_fragment_core_blend : stock_fragment_modern_blend; shader_prog_info.is_file = false; @@ -1011,12 +1011,12 @@ static void gl_glsl_set_uniform_parameter( } static void gl_glsl_set_params(void *data, void *shader_data, - unsigned width, unsigned height, - unsigned tex_width, unsigned tex_height, + unsigned width, unsigned height, + unsigned tex_width, unsigned tex_height, unsigned out_width, unsigned out_height, unsigned frame_count, - const void *_info, - const void *_prev_info, + const void *_info, + const void *_prev_info, const void *_feedback_info, const void *_fbo_info, unsigned fbo_info_cnt) { @@ -1186,7 +1186,7 @@ static void gl_glsl_set_params(void *data, void *shader_data, texunit++; } - + if (uni->prev[i].texture_size >= 0) glUniform2fv(uni->prev[i].texture_size, 1, prev_info[i].tex_size); @@ -1218,7 +1218,7 @@ static void gl_glsl_set_params(void *data, void *shader_data, /* #pragma parameters. */ for (i = 0; i < glsl->shader->num_parameters; i++) { - + int location = glGetUniformLocation( glsl->prg[glsl->active_idx].id, glsl->shader->parameters[i].id); @@ -1294,7 +1294,7 @@ static bool gl_glsl_set_coords(void *handle_data, void *shader_data, const struc buffer = short_buffer; if (coords->vertices > 4) - buffer = (GLfloat*)calloc(coords->vertices * + buffer = (GLfloat*)calloc(coords->vertices * (2 + 2 + 4 + 2), sizeof(*buffer)); if (!buffer) @@ -1348,7 +1348,7 @@ static void gl_glsl_use(void *data, void *shader_data, unsigned idx, bool set_ac glsl->active_idx = idx; id = glsl->prg[idx].id; } - else + else id = (GLuint)idx; glUseProgram(id); @@ -1365,7 +1365,7 @@ static unsigned gl_glsl_num(void *data) static bool gl_glsl_filter_type(void *data, unsigned idx, bool *smooth) { glsl_shader_data_t *glsl = (glsl_shader_data_t*)data; - if (glsl && idx + if (glsl && idx && (glsl->shader->pass[idx - 1].filter != RARCH_FILTER_UNSPEC) ) { diff --git a/gfx/drivers_shader/shader_vulkan.cpp b/gfx/drivers_shader/shader_vulkan.cpp index dbd999f306..813903caff 100644 --- a/gfx/drivers_shader/shader_vulkan.cpp +++ b/gfx/drivers_shader/shader_vulkan.cpp @@ -44,7 +44,7 @@ static const uint32_t opaque_frag[] = static unsigned num_miplevels(unsigned width, unsigned height) { - unsigned size = std::max(width, height); + unsigned size = std::max(width, height); unsigned levels = 0; while (size) { @@ -60,7 +60,7 @@ static void image_layout_transition_levels( VkAccessFlags src_access, VkAccessFlags dst_access, VkPipelineStageFlags src_stages, VkPipelineStageFlags dst_stages) { - VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; barrier.srcAccessMask = src_access; barrier.dstAccessMask = dst_access; @@ -1218,8 +1218,7 @@ void *Buffer::map() { if (vkMapMemory(device, memory, 0, size, 0, &mapped) == VK_SUCCESS) return mapped; - else - return nullptr; + return nullptr; } return mapped; } @@ -1614,7 +1613,7 @@ CommonResources::CommonResources(VkDevice device, memcpy(ptr, vbo_data, sizeof(vbo_data)); vbo->unmap(); - VkSamplerCreateInfo info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + VkSamplerCreateInfo info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; info.mipLodBias = 0.0f; info.maxAnisotropy = 1.0f; info.compareEnable = false; @@ -1739,6 +1738,10 @@ bool Pass::init_feedback() bool Pass::build() { + unordered_map semantic_map; + unsigned i; + unsigned j = 0; + framebuffer.reset(); framebuffer_feedback.reset(); @@ -1750,8 +1753,6 @@ bool Pass::build() pass_info.rt_format, pass_info.max_levels)); } - unordered_map semantic_map; - unsigned j = 0; for (auto ¶m : parameters) { if (!set_unique_map(semantic_map, param.id, @@ -1760,24 +1761,23 @@ bool Pass::build() j++; } - reflection = slang_reflection{}; - reflection.pass_number = pass_number; - reflection.texture_semantic_map = &common->texture_semantic_map; + reflection = slang_reflection{}; + reflection.pass_number = pass_number; + reflection.texture_semantic_map = &common->texture_semantic_map; reflection.texture_semantic_uniform_map = &common->texture_semantic_uniform_map; - reflection.semantic_map = &semantic_map; + reflection.semantic_map = &semantic_map; if (!slang_reflect_spirv(vertex_shader, fragment_shader, &reflection)) return false; // Filter out parameters which we will never use anyways. filtered_parameters.clear(); - for (unsigned i = 0; i < reflection.semantic_float_parameters.size(); i++) + + for (i = 0; i < reflection.semantic_float_parameters.size(); i++) { if (reflection.semantic_float_parameters[i].uniform || reflection.semantic_float_parameters[i].push_constant) - { filtered_parameters.push_back(parameters[i]); - } } if (!init_pipeline()) @@ -1837,9 +1837,7 @@ void Pass::set_semantic_texture_array(VkDescriptorSet set, { if (index < reflection.semantic_textures[semantic].size() && reflection.semantic_textures[semantic][index].texture) - { set_texture(set, reflection.semantic_textures[semantic][index].binding, texture); - } } void Pass::build_semantic_texture_array_vec4(uint8_t *data, slang_texture_semantic semantic, @@ -1850,20 +1848,16 @@ void Pass::build_semantic_texture_array_vec4(uint8_t *data, slang_texture_semant return; if (data && refl[index].uniform) - { build_vec4( reinterpret_cast(data + refl[index].ubo_offset), width, height); - } if (refl[index].push_constant) - { build_vec4( reinterpret_cast(push.buffer.data() + (refl[index].push_constant_offset >> 2)), width, height); - } } void Pass::build_semantic_texture_vec4(uint8_t *data, slang_texture_semantic semantic, @@ -1876,21 +1870,18 @@ void Pass::build_semantic_vec4(uint8_t *data, slang_semantic semantic, unsigned width, unsigned height) { auto &refl = reflection.semantics[semantic]; + if (data && refl.uniform) - { build_vec4( reinterpret_cast(data + refl.ubo_offset), width, height); - } if (refl.push_constant) - { build_vec4( reinterpret_cast(push.buffer.data() + (refl.push_constant_offset >> 2)), width, height); - } } void Pass::build_semantic_parameter(uint8_t *data, unsigned index, float value) diff --git a/gfx/video_context_driver.h b/gfx/video_context_driver.h index b05bd4332d..111e1bbb0f 100644 --- a/gfx/video_context_driver.h +++ b/gfx/video_context_driver.h @@ -22,10 +22,6 @@ #include "video_driver.h" -#ifdef HAVE_CONFIG_H -#include "../config.h" -#endif - RETRO_BEGIN_DECLS #ifndef MAX_EGLIMAGE_TEXTURES diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 9842f5019d..5f3767e06b 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -108,22 +108,23 @@ typedef struct video_pixel_scaler * could potentially make use of this. */ static uintptr_t video_driver_display; static uintptr_t video_driver_window; -static enum rarch_display_type video_driver_display_type; -static char video_driver_title_buf[64]; - -static uint64_t video_driver_frame_count; - -static void *video_driver_data = NULL; -static video_driver_t *current_video = NULL; - -/* Interface for "poking". */ -static const video_poke_interface_t *video_driver_poke = NULL; static video_driver_state_t video_driver_state; +static enum rarch_display_type video_driver_display_type = RARCH_DISPLAY_NONE; +static char video_driver_title_buf[64] = {0}; + +static uint64_t video_driver_frame_count = 0; + +static void *video_driver_data = NULL; +static video_driver_t *current_video = NULL; + +/* Interface for "poking". */ +static const video_poke_interface_t *video_driver_poke = NULL; + /* Used for 16-bit -> 16-bit conversions that take place before * being passed to video driver. */ -static video_pixel_scaler_t *video_driver_scaler_ptr = NULL; +static video_pixel_scaler_t *video_driver_scaler_ptr = NULL; char rotation_lut[4][32] = { @@ -1711,8 +1712,7 @@ bool video_driver_is_alive(void) { if (current_video) return current_video->alive(video_driver_data); - else - return true; + return true; } bool video_driver_is_focused(void) @@ -2152,9 +2152,7 @@ void video_driver_frame(const void *data, unsigned width, video_driver_data, data, width, height, video_driver_frame_count, pitch, video_driver_msg)) - { video_driver_unset_active(); - } video_driver_frame_count++; } diff --git a/gfx/video_driver.h b/gfx/video_driver.h index db8078e92b..916c2208f4 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -24,6 +24,14 @@ #include #include +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#ifdef HAVE_OVERLAY +#include "../input/input_overlay.h" +#endif + #include "font_driver.h" #include "video_filter.h" #include "video_shader_parse.h" diff --git a/gfx/video_filter.h b/gfx/video_filter.h index d8af27ad47..5921657b78 100644 --- a/gfx/video_filter.h +++ b/gfx/video_filter.h @@ -22,6 +22,7 @@ #include #define RARCH_SOFTFILTER_THREADS_AUTO 0 + typedef struct rarch_softfilter rarch_softfilter_t; rarch_softfilter_t *rarch_softfilter_new(const char *filter_path, diff --git a/gfx/video_frame.c b/gfx/video_frame.c index 4ccd8ba441..c1fca69aab 100644 --- a/gfx/video_frame.c +++ b/gfx/video_frame.c @@ -13,6 +13,8 @@ * If not, see . */ +#include + #include #include "../performance_counters.h" @@ -21,12 +23,14 @@ #include "video_frame.h" void video_frame_convert_rgb16_to_rgb32( - struct scaler_ctx *scaler, + void *data, void *output, const void *input, int width, int height, int in_pitch) { + struct scaler_ctx *scaler = (struct scaler_ctx*)data; + if (width != scaler->in_width || height != scaler->in_height) { scaler->in_width = width; @@ -46,7 +50,7 @@ void video_frame_convert_rgb16_to_rgb32( } void video_frame_scale( - struct scaler_ctx *scaler, + void *data, void *output, const void *input, enum scaler_pix_fmt format, @@ -57,6 +61,8 @@ void video_frame_scale( unsigned height, unsigned pitch) { + struct scaler_ctx *scaler = (struct scaler_ctx*)data; + if ( width != (unsigned)scaler->in_width || height != (unsigned)scaler->in_height @@ -80,7 +86,7 @@ void video_frame_scale( } void video_frame_record_scale( - struct scaler_ctx *scaler, + void *data, void *output, const void *input, unsigned scaler_width, @@ -91,6 +97,8 @@ void video_frame_record_scale( unsigned pitch, bool bilinear) { + struct scaler_ctx *scaler = (struct scaler_ctx*)data; + if ( width != (unsigned)scaler->in_width || height != (unsigned)scaler->in_height @@ -114,10 +122,12 @@ void video_frame_record_scale( } void video_frame_convert_argb8888_to_abgr8888( - struct scaler_ctx *scaler, + void *data, void *output, const void *input, int width, int height, int in_pitch) { + struct scaler_ctx *scaler = (struct scaler_ctx*)data; + if (width != scaler->in_width || height != scaler->in_height) { scaler->in_width = width; @@ -136,11 +146,13 @@ void video_frame_convert_argb8888_to_abgr8888( } void video_frame_convert_to_bgr24( - struct scaler_ctx *scaler, + void *data, void *output, const void *input, int width, int height, int in_pitch, bool bgr24) { + struct scaler_ctx *scaler = (struct scaler_ctx*)data; + scaler->in_width = width; scaler->in_height = height; scaler->out_width = width; @@ -179,11 +191,12 @@ void video_frame_convert_rgba_to_bgr( } bool video_pixel_frame_scale( - struct scaler_ctx *scaler, + void *scaler_data, void *output, const void *data, unsigned width, unsigned height, size_t pitch) { + struct scaler_ctx *scaler = (struct scaler_ctx*)scaler_data; static struct retro_perf_counter video_frame_conv = {0}; performance_counter_init(&video_frame_conv, "video_frame_conv"); diff --git a/gfx/video_frame.h b/gfx/video_frame.h index 452ef76a26..da1ecfea7f 100644 --- a/gfx/video_frame.h +++ b/gfx/video_frame.h @@ -17,18 +17,19 @@ #define _VIDEO_FRAME_H #include +#include -#include +RETRO_BEGIN_DECLS void video_frame_convert_rgb16_to_rgb32( - struct scaler_ctx *scaler, + void *data, void *output, const void *input, int width, int height, int in_pitch); void video_frame_scale( - struct scaler_ctx *scaler, + void *data, void *output, const void *input, enum scaler_pix_fmt format, @@ -40,7 +41,7 @@ void video_frame_scale( unsigned pitch); void video_frame_record_scale( - struct scaler_ctx *scaler, + void *data, void *output, const void *input, unsigned scaler_width, @@ -52,12 +53,12 @@ void video_frame_record_scale( bool bilinear); void video_frame_convert_argb8888_to_abgr8888( - struct scaler_ctx *scaler, + void *data, void *output, const void *input, int width, int height, int in_pitch); void video_frame_convert_to_bgr24( - struct scaler_ctx *scaler, + void *data, void *output, const void *input, int width, int height, int in_pitch, bool bgr24); @@ -68,10 +69,12 @@ void video_frame_convert_rgba_to_bgr( unsigned width); bool video_pixel_frame_scale( - struct scaler_ctx *scaler, + void *scaler_data, void *output, const void *data, unsigned width, unsigned height, size_t pitch); +RETRO_END_DECLS + #endif diff --git a/griffin/griffin.c b/griffin/griffin.c index e38f6657b0..f40d58836f 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -54,16 +54,21 @@ ALGORITHMS /*============================================================ ARCHIVE FILE ============================================================ */ +#include "../libretro-common/file/archive_file.c" #ifdef HAVE_ZLIB -#include "../libretro-common/file/archive_file.c" #include "../libretro-common/file/archive_file_zlib.c" #endif +#ifdef HAVE_7ZIP +#include "../libretro-common/file/archive_file_7z.c" +#endif + /*============================================================ ENCODINGS ============================================================ */ #include "../libretro-common/encodings/encoding_utf.c" +#include "../libretro-common/encodings/encoding_crc32.c" /*============================================================ PERFORMANCE @@ -268,6 +273,7 @@ VIDEO DRIVER #include "../libretro-common/gfx/math/matrix_3x3.c" #include "../libretro-common/gfx/math/vector_2.c" #include "../libretro-common/gfx/math/vector_3.c" +#include "../libretro-common/gfx/math/vector_4.c" #if defined(GEKKO) #ifdef HW_RVL @@ -796,6 +802,8 @@ RETROARCH ============================================================ */ #include "../core_impl.c" #include "../retroarch.c" +#include "../dirs.c" +#include "../paths.c" #include "../runloop.c" #include "../libretro-common/queues/task_queue.c" @@ -860,8 +868,7 @@ NETPLAY DATA RUNLOOP ============================================================ */ #include "../tasks/task_content.c" -#include "../tasks/task_save_ram.c" -#include "../tasks/task_save_state.c" +#include "../tasks/task_save.c" #include "../tasks/task_image.c" #include "../tasks/task_file_transfer.c" #ifdef HAVE_ZLIB @@ -888,12 +895,16 @@ MENU #ifdef HAVE_MENU #include "../menu/menu_driver.c" #include "../menu/menu_input.c" -#include "../menu/menu_entry.c" +#include "../menu/menu_event.c" #include "../menu/menu_entries.c" #include "../menu/menu_setting.c" #include "../menu/menu_cbs.c" #include "../menu/menu_content.c" -#include "../menu/widgets/menu_popup.c" +#include "../menu/widgets/menu_entry.c" +#include "../menu/widgets/menu_dialog.c" +#include "../menu/widgets/menu_input_dialog.c" +#include "../menu/widgets/menu_input_bind_dialog.c" +#include "../menu/widgets/menu_list.c" #include "../menu/cbs/menu_cbs_ok.c" #include "../menu/cbs/menu_cbs_cancel.c" #include "../menu/cbs/menu_cbs_select.c" @@ -1046,7 +1057,7 @@ HTTP SERVER ============================================================ */ #if defined(HAVE_HTTPSERVER) && defined(HAVE_ZLIB) #include "../deps/civetweb/civetweb.c" -#include "httpserver/httpserver.c" +#include "network/httpserver/httpserver.c" #endif #ifdef __cplusplus diff --git a/httpserver/httpserver.h b/httpserver/httpserver.h deleted file mode 100644 index 62ef7116f4..0000000000 --- a/httpserver/httpserver.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __RARCH_HTTPSERVR_H -#define __RARCH_HTTPSERVR_H - -#ifdef __cplusplus -extern "C" { -#endif - - int httpserver_init(unsigned port); - void httpserver_destroy(void); - -#ifdef __cplusplus -} -#endif - -#endif /* __RARCH_HTTPSERVR_H */ diff --git a/input/drivers/udev_input.c b/input/drivers/udev_input.c index 8c21698cf7..bf2306dc1e 100644 --- a/input/drivers/udev_input.c +++ b/input/drivers/udev_input.c @@ -375,6 +375,9 @@ static void udev_input_poll(void *data) struct epoll_event events[32]; udev_input_t *udev = (udev_input_t*)data; + if (!udev) + return; + udev->mouse_x = udev->mouse_y = 0; udev->mouse_wu = udev->mouse_wd = 0; udev->mouse_whu = udev->mouse_whd = 0; @@ -457,9 +460,10 @@ static int16_t udev_lightgun_state(udev_input_t *udev, unsigned id) static int16_t udev_analog_pressed(const struct retro_keybind *binds, unsigned idx, unsigned id) { - unsigned id_minus = 0; - unsigned id_plus = 0; - int16_t pressed_minus = 0, pressed_plus = 0; + unsigned id_minus = 0; + unsigned id_plus = 0; + int16_t pressed_minus = 0; + int16_t pressed_plus = 0; input_conv_analog_id_to_bind_id(idx, id, &id_minus, &id_plus); @@ -647,7 +651,7 @@ static bool open_devices(udev_input_t *udev, const char *type, device_handle_cb static void *udev_input_init(void) { settings_t *settings = config_get_ptr(); - udev_input_t *udev = (udev_input_t*)calloc(1, sizeof(*udev)); + udev_input_t *udev = (udev_input_t*)calloc(1, sizeof(*udev)); if (!udev) return NULL; diff --git a/input/drivers_hid/iohidmanager_hid.c b/input/drivers_hid/iohidmanager_hid.c index ef7ab25035..9fa5341181 100644 --- a/input/drivers_hid/iohidmanager_hid.c +++ b/input/drivers_hid/iohidmanager_hid.c @@ -187,15 +187,17 @@ static void iohidmanager_hid_device_input_callback(void *data, IOReturn result, default: { int i; - // +0/-0 => Left Stick Horizontal => 48 - // +1/-1 => Left Stick Vertical => 49 - // +2/-2 => Right Stick Horizontal => 51 - // +3/-3 => Right Stick Vertical => 52 - // +4/-4 => Left Trigger (if exists) => 50 - // +5/-5 => Right Trigger (if exists) => 53 static const uint32_t axis_use_ids[6] = { 48, 49, 51, 52, 50, 53 }; + /* +0/-0 => Left Stick Horizontal => 48 + * +1/-1 => Left Stick Vertical => 49 + * +2/-2 => Right Stick Horizontal => 51 + * +3/-3 => Right Stick Vertical => 52 + * +4/-4 => Left Trigger (if exists) => 50 + * +5/-5 => Right Trigger (if exists) => 53 + */ + for (i = 0; i < 6; i ++) { CFIndex min = IOHIDElementGetPhysicalMin(element); @@ -262,13 +264,10 @@ static int32_t iohidmanager_hid_device_get_int_property( int32_t value; CFNumberRef ref = (CFNumberRef)IOHIDDeviceGetProperty(device, key); - if (ref) + if (ref && (CFGetTypeID(ref) == CFNumberGetTypeID())) { - if (CFGetTypeID(ref) == CFNumberGetTypeID()) - { - CFNumberGetValue((CFNumberRef)ref, kCFNumberIntType, &value); - return value; - } + CFNumberGetValue((CFNumberRef)ref, kCFNumberIntType, &value); + return value; } return 0; @@ -410,15 +409,13 @@ static int iohidmanager_hid_manager_init(iohidmanager_hid_t *hid) hid->ptr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - if (hid->ptr) - { - IOHIDManagerSetDeviceMatching(hid->ptr, NULL); - IOHIDManagerScheduleWithRunLoop(hid->ptr, CFRunLoopGetCurrent(), - kCFRunLoopDefaultMode); - return 0; - } + if (!hid->ptr) + return -1; - return -1; + IOHIDManagerSetDeviceMatching(hid->ptr, NULL); + IOHIDManagerScheduleWithRunLoop(hid->ptr, CFRunLoopGetCurrent(), + kCFRunLoopDefaultMode); + return 0; } @@ -469,6 +466,7 @@ static void *iohidmanager_hid_init(void) if (!hid_apple) goto error; hid_apple->slots = pad_connection_init(MAX_USERS); + if (!hid_apple->slots) goto error; if (iohidmanager_hid_manager_init(hid_apple) == -1) diff --git a/input/drivers_joypad/psp_joypad.c b/input/drivers_joypad/psp_joypad.c index 0880915bba..1eae1fbda5 100644 --- a/input/drivers_joypad/psp_joypad.c +++ b/input/drivers_joypad/psp_joypad.c @@ -21,7 +21,7 @@ #include "../../configuration.h" #if defined(SN_TARGET_PSP2) || defined(VITA) -#define PSP_MAX_PADS 2 +#define PSP_MAX_PADS 4 #else #define PSP_MAX_PADS 1 #endif @@ -148,7 +148,7 @@ static void psp_joypad_poll(void) * can be 0 or 1 to read the first controller on * a PSTV, but HAS to be 0 for a real VITA and 2 * for the 2nd controller on a PSTV */ - unsigned p = (player == 1) ? 2 : player; + unsigned p = (player > 0) ? player+1 : player; int32_t ret = CtrlPeekBufferPositive(p, &state_tmp, 1); pad_state[i] = 0; diff --git a/input/drivers_keyboard/keyboard_event_android.c b/input/drivers_keyboard/keyboard_event_android.c index 8b10ef86b1..47b1208f2c 100644 --- a/input/drivers_keyboard/keyboard_event_android.c +++ b/input/drivers_keyboard/keyboard_event_android.c @@ -13,6 +13,8 @@ * If not, see . */ +#include + #include "keyboard_event_android.h" #define AKEYCODE_ASSIST 219 diff --git a/input/drivers_keyboard/keyboard_event_udev.c b/input/drivers_keyboard/keyboard_event_udev.c index 5ad3b6135e..a2d1274388 100644 --- a/input/drivers_keyboard/keyboard_event_udev.c +++ b/input/drivers_keyboard/keyboard_event_udev.c @@ -13,6 +13,12 @@ * If not, see . */ +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + #include "../input_keymaps.h" #include "../input_keyboard.h" #include "../../driver.h" diff --git a/input/drivers_keyboard/keyboard_event_x11.c b/input/drivers_keyboard/keyboard_event_x11.c index 1abe5a1de4..d632b6d4e8 100644 --- a/input/drivers_keyboard/keyboard_event_x11.c +++ b/input/drivers_keyboard/keyboard_event_x11.c @@ -18,15 +18,19 @@ #include #include -#ifdef HAVE_XINERAMA -#include -#endif - #include #include #include #include +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#ifdef HAVE_XINERAMA +#include +#endif + #include "../input_keyboard.h" #include "../input_keymaps.h" diff --git a/input/input_autodetect.c b/input/input_autodetect.c index c3e912b1df..40b121cd73 100644 --- a/input/input_autodetect.c +++ b/input/input_autodetect.c @@ -103,23 +103,18 @@ static int input_try_autoconfigure_joypad_from_conf(config_file_t *conf, && params->pid != 0 && input_vid != 0 && input_pid != 0) - { score += 3; - } /* Check for name match */ if (string_is_equal(ident, params->name)) - { score += 2; - } else { if (!string_is_empty(ident) && !strncmp(params->name, ident, strlen(ident))) - { score += 1; - } } + return score; } @@ -225,7 +220,8 @@ static bool input_autoconfigure_joypad_from_conf_dir( for (i = 0; i < list->size; i++) { conf = config_file_new(list->elems[i].data); - ret = input_try_autoconfigure_joypad_from_conf(conf, params); + ret = input_try_autoconfigure_joypad_from_conf(conf, params); + if(ret >= current_best) { index = i; diff --git a/input/input_driver.c b/input/input_driver.c index 8f27a5d6c3..0b31266798 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -99,7 +99,7 @@ struct turbo_buttons static turbo_buttons_t input_driver_turbo_btns; #ifdef HAVE_COMMAND -static command_t *input_driver_command = NULL; +static command_t *input_driver_command = NULL; #endif #ifdef HAVE_NETWORKGAMEPAD static input_remote_t *input_driver_remote = NULL; diff --git a/input/input_driver.h b/input/input_driver.h index c9722fc35a..8f30b0bd7b 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -27,10 +27,6 @@ #include "input_joypad_driver.h" #include "input_defines.h" -#ifdef HAVE_OVERLAY -#include "input_overlay.h" -#endif - RETRO_BEGIN_DECLS typedef struct retro_input diff --git a/intl/msg_hash_it.c b/intl/msg_hash_it.c index 0aa2c91bbf..92421f64d0 100644 --- a/intl/msg_hash_it.c +++ b/intl/msg_hash_it.c @@ -1110,12 +1110,18 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) { switch (msg) { - case MENU_ENUM_LABEL_VALUE_INPUT_ICADE_ENABLE: + case MENU_ENUM_LABEL_VALUE_PARENT_DIRECTORY: + return "Directory precedente"; + case MENU_ENUM_LABEL_VALUE_INPUT_ICADE_ENABLE: return "Abilita mappatura gamepad tastiera"; case MENU_ENUM_LABEL_VALUE_INPUT_KEYBOARD_GAMEPAD_MAPPING_TYPE: return "Tipologia di mappatura gamepad tastiera"; case MENU_ENUM_LABEL_VALUE_INPUT_SMALL_KEYBOARD_ENABLE: return "Abilita tastiera ridotta"; + case MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG_OVERRIDE_CORE: + return "Salva override del core"; + case MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG_OVERRIDE_GAME: + return "Salva override di gioco"; case MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG: return "Salva configurazione attuale"; case MENU_ENUM_LABEL_VALUE_STATE_SLOT: @@ -1187,7 +1193,7 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) case MENU_ENUM_LABEL_VALUE_UPDATE_CORE_INFO_FILES: return "Aggiorna i files info dei core"; case MENU_ENUM_LABEL_VALUE_DOWNLOAD_CORE_CONTENT: - return "Contenuto scaricato"; + return "Scarica contenuto"; case MENU_ENUM_LABEL_VALUE_SCAN_THIS_DIRECTORY: return ""; case MENU_ENUM_LABEL_VALUE_SCAN_FILE: @@ -1353,6 +1359,8 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) return "Directory degli asset"; case MENU_ENUM_LABEL_VALUE_DYNAMIC_WALLPAPERS_DIRECTORY: return "Directory degli sfondi dinamici"; + case MENU_ENUM_LABEL_VALUE_THUMBNAILS_DIRECTORY: + return "Directory delle miniature"; case MENU_ENUM_LABEL_VALUE_RGUI_BROWSER_DIRECTORY: return "Directory di selezione file"; case MENU_ENUM_LABEL_VALUE_RGUI_CONFIG_DIRECTORY: @@ -1419,6 +1427,30 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) return "Abilita DPI Override"; case MENU_ENUM_LABEL_VALUE_DPI_OVERRIDE_VALUE: return "DPI Override"; + case MENU_ENUM_LABEL_VALUE_XMB_SCALE_FACTOR: + return "Fattore di scala del menù"; + case MENU_ENUM_LABEL_VALUE_XMB_ALPHA_FACTOR: + return "Fattore alpha del menù"; + case MENU_ENUM_LABEL_VALUE_XMB_FONT: + return "Carattere del menù"; + case MENU_ENUM_LABEL_VALUE_XMB_THEME: + return "Icona a tema del menù"; + case MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME: + return "Colore tema del menù"; + case MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME: + return "Colore tema del menù"; + case MENU_ENUM_LABEL_VALUE_XMB_SHADOWS_ENABLE: + return "Abilità ombre per l'icona"; + case MENU_ENUM_LABEL_VALUE_XMB_SHOW_SETTINGS: + return "Mostra scheda settaggi"; + case MENU_ENUM_LABEL_VALUE_XMB_SHOW_IMAGES: + return "Mostra scheda immagini"; + case MENU_ENUM_LABEL_VALUE_XMB_SHOW_MUSIC: + return "Mostra scheda musica"; + case MENU_ENUM_LABEL_VALUE_XMB_SHOW_VIDEO: + return "Mostra scheda video"; + case MENU_ENUM_LABEL_VALUE_XMB_SHOW_HISTORY: + return "Mostra scheda storia"; case MENU_ENUM_LABEL_VALUE_SUSPEND_SCREENSAVER_ENABLE: return "Spegni salvaschermo"; case MENU_ENUM_LABEL_VALUE_VIDEO_DISABLE_COMPOSITION: @@ -1445,8 +1477,8 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) return "Fotogrammi stimati del monitor"; case MENU_ENUM_LABEL_VALUE_DUMMY_ON_CORE_SHUTDOWN: return "Valore fittizio sull'arresto del core"; - case MENU_ENUM_LABEL_VALUE_CORE_SET_SUPPORTS_NO_CONTENT_ENABLE: /* TODO/FIXME */ - return "Non avviare automaticamente un core"; + case MENU_ENUM_LABEL_VALUE_CORE_SET_SUPPORTS_NO_CONTENT_ENABLE: + return "Avvia automaticamente un core"; case MENU_ENUM_LABEL_VALUE_FRAME_THROTTLE_ENABLE: return "Limita la velocità massima di caricamento"; case MENU_ENUM_LABEL_VALUE_FASTFORWARD_RATIO: @@ -1454,7 +1486,11 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) case MENU_ENUM_LABEL_VALUE_AUTO_REMAPS_ENABLE: return "Carica file di rimappatura automaticamente"; case MENU_ENUM_LABEL_VALUE_SLOWMOTION_RATIO: - return "Slow-Motion Ratio"; + return "Rapporto di slow-motion"; + case MENU_ENUM_LABEL_VALUE_MENU_LINEAR_FILTER: + return "Filtro lineare del menù"; + case MENU_ENUM_LABEL_VALUE_MENU_ENUM_THROTTLE_FRAMERATE: + return "Frequenza fotogrammi del menù"; case MENU_ENUM_LABEL_VALUE_CORE_SPECIFIC_CONFIG: return "Configurazione per core"; case MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS: @@ -1465,6 +1501,10 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) return "Carica file di override automaticamente"; case MENU_ENUM_LABEL_VALUE_CONFIG_SAVE_ON_EXIT: return "Salva configurazione all'uscita"; + case MENU_ENUM_LABEL_VALUE_CONFIRM_ON_EXIT: + return "Chiedi conferma all'uscita"; + case MENU_ENUM_LABEL_VALUE_SHOW_HIDDEN_FILES: + return "Mostra files e cartelle nascoste"; case MENU_ENUM_LABEL_VALUE_VIDEO_SMOOTH: return "Filtro bilineare hardware"; case MENU_ENUM_LABEL_VALUE_VIDEO_GAMMA: @@ -1475,6 +1515,8 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) return "Sincronizza GPU"; case MENU_ENUM_LABEL_VALUE_VIDEO_SWAP_INTERVAL: return "Intervallo di swap vsync"; + case MENU_ENUM_LABEL_VALUE_VIDEO_MAX_SWAPCHAIN_IMAGES: + return "Massimo swapchain di immagini"; case MENU_ENUM_LABEL_VALUE_VIDEO_VSYNC: return "VSync"; case MENU_ENUM_LABEL_VALUE_VIDEO_THREADED: @@ -1587,8 +1629,8 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) return "Collezione"; case MENU_ENUM_LABEL_VALUE_DETECT_CORE_LIST: return "Seleziona il file ed intercetta il core"; - case MENU_ENUM_LABEL_VALUE_DOWNLOADED_FILE_DETECT_CORE_LIST: /* TODO/FIXME - rewrite */ - return "Seleziona file scaricati ed intercetta il core"; + case MENU_ENUM_LABEL_VALUE_DOWNLOADED_FILE_DETECT_CORE_LIST: + return "Scarica directories"; case MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_HISTORY: return "Carica Recenti"; case MENU_ENUM_LABEL_VALUE_AUDIO_ENABLE: @@ -1633,6 +1675,8 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) return "Contatore dei core"; case MENU_ENUM_LABEL_VALUE_TAKE_SCREENSHOT: return "Cattura Screenshot"; + case MENU_ENUM_LABEL_VALUE_DELETE_ENTRY: + return "Rimuovi dalla Playlist"; case MENU_ENUM_LABEL_VALUE_RESUME: return "Riprendi"; case MENU_ENUM_LABEL_VALUE_DISK_INDEX: @@ -1682,7 +1726,7 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) case MENU_ENUM_LABEL_VALUE_NAVIGATION_WRAPAROUND: return "Navigazione avvolgente"; case MENU_ENUM_LABEL_VALUE_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE: - return "Filtra con estensioni supportate"; /* TODO/FIXME - rewrite */ + return "Filtra estensioni sconosciute"; case MENU_ENUM_LABEL_VALUE_CORE_UPDATER_AUTO_EXTRACT_ARCHIVE: return "Estrai automaticamente gli archivi scaricati"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFORMATION: @@ -1709,6 +1753,12 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) return "Salva stato"; case MENU_ENUM_LABEL_VALUE_LOAD_STATE: return "Carica stato"; + case MENU_ENUM_LABEL_VALUE_UNDO_LOAD_STATE: + return "Annulla carica stato"; + case MENU_ENUM_LABEL_VALUE_UNDO_SAVE_STATE: + return "Annulla salva stato"; + case MSG_UNDID_LOAD_STATE: + return "Annullato carica stato."; case MENU_ENUM_LABEL_VALUE_RESUME_CONTENT: return "Riprendi"; case MENU_ENUM_LABEL_VALUE_INPUT_DRIVER: @@ -1747,7 +1797,9 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) return "Sfondo dinamico"; case MENU_ENUM_LABEL_VALUE_CORE_INPUT_REMAPPING_OPTIONS: return "Opzioni di rimappatura degli input del core"; - case MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS: /* UPDATE/FIXME */ + case MENU_ENUM_LABEL_VALUE_THUMBNAILS: + return "Miniature"; + case MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS: return "Shaders"; case MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PARAMETERS: return "Antemprima Parametri Shader"; @@ -2063,6 +2115,8 @@ const char *msg_hash_to_str_it(enum msg_hash_enums msg) return "Avvia Core"; case MENU_ENUM_LABEL_VALUE_INPUT_POLL_TYPE_BEHAVIOR: return "Tipo di ritardo"; + case MENU_ENUM_LABEL_VALUE_MENU_WALLPAPER_OPACITY: + return "Opacità dello sfondo"; default: break; } diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 36f0f7d8d4..dfefcb4f0b 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -1558,6 +1558,22 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) "Increasing this value will increase \n" "performance, but introduce more latency."); break; + case MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES: + snprintf(s, len, + "The frequency in frames with which netplay \n" + "will verify that the host and client are in \n" + "sync. \n" + " \n" + "With most cores, this value will have no \n" + "visible effect and can be ignored. With \n" + "nondeterminstic cores, this value determines \n" + "how often the netplay peers will be brought \n" + "into sync. With buggy cores, setting this \n" + "to any non-zero value will cause severe \n" + "performance issues. Set to zero to perform \n" + "no checks. This value is only used on the \n" + "netplay host. \n"); + break; case MENU_ENUM_LABEL_VIDEO_MAX_SWAPCHAIN_IMAGES: snprintf(s, len, "Maximum amount of swapchain images. This \n" @@ -2424,6 +2440,8 @@ static const char *menu_hash_to_str_us_label_enum(enum msg_hash_enums msg) return "bluetooth_enable"; case MENU_ENUM_LABEL_NETPLAY_DELAY_FRAMES: return "netplay_delay_frames"; + case MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES: + return "netplay_check_frames"; case MENU_ENUM_LABEL_NETPLAY_MODE: return "netplay_mode"; case MENU_ENUM_LABEL_RGUI_SHOW_START_SCREEN: @@ -3747,6 +3765,8 @@ const char *msg_hash_to_str_us(enum msg_hash_enums msg) return "Bluetooth Enable"; case MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES: return "Netplay Delay Frames"; + case MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES: + return "Netplay Check Frames"; case MENU_ENUM_LABEL_VALUE_NETPLAY_MODE: return "Netplay Client Enable"; case MENU_ENUM_LABEL_VALUE_RGUI_SHOW_START_SCREEN: diff --git a/libretro-common/encodings/encoding_crc32.c b/libretro-common/encodings/encoding_crc32.c new file mode 100644 index 0000000000..8a13cce4a2 --- /dev/null +++ b/libretro-common/encodings/encoding_crc32.c @@ -0,0 +1,89 @@ +/* Copyright (C) 2010-2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (encoding_crc32.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +static const uint32_t crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +uint32_t encoding_crc32(uint32_t crc, const uint8_t *buf, size_t len) +{ + crc = crc ^ 0xffffffff; + + while (len--) + crc = crc32_table[(crc ^ (*buf++)) & 0xff] ^ (crc >> 8); + + return crc ^ 0xffffffff; +} diff --git a/libretro-common/encodings/encoding_utf.c b/libretro-common/encodings/encoding_utf.c index dd584de418..d7989ce004 100644 --- a/libretro-common/encodings/encoding_utf.c +++ b/libretro-common/encodings/encoding_utf.c @@ -1,7 +1,7 @@ /* Copyright (C) 2010-2016 The RetroArch team * * --------------------------------------------------------------------------------------- - * The following license statement only applies to this file (encodings_utf.c). + * The following license statement only applies to this file (encoding_utf.c). * --------------------------------------------------------------------------------------- * * Permission is hereby granted, free of charge, @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -40,7 +41,7 @@ static INLINE unsigned leading_ones(uint8_t c) return ones; } -/* Simple implementation. Assumes the sequence is +/* Simple implementation. Assumes the sequence is * properly synchronized and terminated. */ size_t utf8_conv_utf32(uint32_t *out, size_t out_chars, @@ -119,14 +120,14 @@ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars, if (value < (((uint32_t)1) << (numAdds * 5 + 6))) break; if (out) - out[out_pos] = (char)(kUtf8Limits[numAdds - 1] + out[out_pos] = (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds))); out_pos++; do { numAdds--; if (out) - out[out_pos] = (char)(0x80 + out[out_pos] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F)); out_pos++; }while (numAdds != 0); @@ -136,13 +137,13 @@ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars, return false; } -/* Acts mostly like strlcpy. +/* Acts mostly like strlcpy. * - * Copies the given number of UTF-8 characters, + * Copies the given number of UTF-8 characters, * but at most d_len bytes. * - * Always NULL terminates. - * Does not copy half a character. + * Always NULL terminates. + * Does not copy half a character. * * Returns number of bytes. 's' is assumed valid UTF-8. * Use only if 'chars' is considerably less than 'd_len'. */ @@ -205,20 +206,55 @@ uint32_t utf8_walk(const char **string) { uint8_t first = utf8_walkbyte(string); uint32_t ret; - + if (first<128) return first; - + ret = 0; ret = (ret<<6) | (utf8_walkbyte(string) & 0x3F); if (first >= 0xE0) ret = (ret<<6) | (utf8_walkbyte(string) & 0x3F); if (first >= 0xF0) ret = (ret<<6) | (utf8_walkbyte(string) & 0x3F); - + if (first >= 0xF0) return ret | (first&31)<<18; if (first >= 0xE0) return ret | (first&15)<<12; return ret | (first&7)<<6; } + +static bool utf16_to_char(uint8_t **utf_data, + size_t *dest_len, const uint16_t *in) +{ + unsigned len = 0; + + while (in[len] != '\0') + len++; + + utf16_conv_utf8(NULL, dest_len, in, len); + *dest_len += 1; + *utf_data = (uint8_t*)malloc(*dest_len); + if (*utf_data == 0) + return false; + + return utf16_conv_utf8(*utf_data, dest_len, in, len); +} + +bool utf16_to_char_string(const uint16_t *in, char *s, size_t len) +{ + size_t dest_len = 0; + uint8_t *utf16_data = NULL; + bool ret = utf16_to_char(&utf16_data, &dest_len, in); + + if (ret) + { + utf16_data[dest_len] = 0; + strlcpy(s, (const char*)utf16_data, len); + } + + free(utf16_data); + utf16_data = NULL; + + return ret; +} diff --git a/libretro-common/file/archive_file.c b/libretro-common/file/archive_file.c index cbb9e45d4f..dda4281d3f 100644 --- a/libretro-common/file/archive_file.c +++ b/libretro-common/file/archive_file.c @@ -45,30 +45,7 @@ #include #include #include - -#ifndef CENTRAL_FILE_HEADER_SIGNATURE -#define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50 -#endif - -#ifndef END_OF_CENTRAL_DIR_SIGNATURE -#define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50 -#endif - -struct zip_extract_userdata -{ - char *zip_path; - char *first_extracted_file_path; - const char *extraction_directory; - size_t zip_path_size; - struct string_list *ext; - bool found_content; -}; - -enum file_archive_compression_mode -{ - ZLIB_MODE_UNCOMPRESSED = 0, - ZLIB_MODE_DEFLATE = 8 -}; +#include typedef struct { @@ -81,7 +58,7 @@ typedef struct #ifdef HAVE_MMAP /* Closes, unmaps and frees. */ -static void file_archive_free(void *handle) +void file_archive_free(void *handle) { file_archive_file_data_t *data = (file_archive_file_data_t*)handle; @@ -95,7 +72,7 @@ static void file_archive_free(void *handle) free(data); } -static const uint8_t *file_archive_data(void *handle) +const uint8_t *file_archive_data(void *handle) { file_archive_file_data_t *data = (file_archive_file_data_t*)handle; if (!data) @@ -146,16 +123,17 @@ error: #else /* Closes, unmaps and frees. */ -static void file_archive_free(void *handle) +void file_archive_free(void *handle) { file_archive_file_data_t *data = (file_archive_file_data_t*)handle; if (!data) return; - free(data->data); + if(data->data) + free(data->data); free(data); } -static const uint8_t *file_archive_data(void *handle) +const uint8_t *file_archive_data(void *handle) { file_archive_file_data_t *data = (file_archive_file_data_t*)handle; if (!data) @@ -204,12 +182,12 @@ static int file_archive_get_file_list_cb( uint32_t csize, uint32_t size, uint32_t checksum, - void *userdata) + struct archive_extract_userdata *userdata) { union string_list_elem_attr attr; struct string_list *ext_list = NULL; const char *file_ext = NULL; - struct string_list *list = (struct string_list*)userdata; + size_t pathLen = strlen(path); (void)cdata; (void)cmode; @@ -219,13 +197,16 @@ static int file_archive_get_file_list_cb( memset(&attr, 0, sizeof(attr)); + if (!pathLen) + return 0; + if (valid_exts) ext_list = string_split(valid_exts, "|"); if (ext_list) { /* Checks if this entry is a directory or a file. */ - char last_char = path[strlen(path)-1]; + char last_char = path[pathLen-1]; /* Skip if directory. */ if (last_char == '/' || last_char == '\\' ) @@ -233,7 +214,7 @@ static int file_archive_get_file_list_cb( file_ext = path_get_extension(path); - if (!file_ext || + if (!file_ext || !string_list_find_elem_prefix(ext_list, ".", file_ext)) goto error; @@ -241,8 +222,8 @@ static int file_archive_get_file_list_cb( string_list_free(ext_list); } - return string_list_append(list, path, attr); - + return string_list_append(userdata->list, path, attr); + error: string_list_free(ext_list); return 0; @@ -251,155 +232,78 @@ error: static int file_archive_extract_cb(const char *name, const char *valid_exts, const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, - uint32_t checksum, void *userdata) + uint32_t checksum, struct archive_extract_userdata *userdata) { const char *ext = path_get_extension(name); - struct zip_extract_userdata *data = (struct zip_extract_userdata*)userdata; - /* Extract first content that matches our list. */ - if (ext && string_list_find_elem(data->ext, ext)) + /* Extract first file that matches our list. */ + if (ext && string_list_find_elem(userdata->ext, ext)) { char new_path[PATH_MAX_LENGTH] = {0}; - if (data->extraction_directory) - fill_pathname_join(new_path, data->extraction_directory, + if (userdata->extraction_directory) + fill_pathname_join(new_path, userdata->extraction_directory, path_basename(name), sizeof(new_path)); else - fill_pathname_resolve_relative(new_path, data->zip_path, + fill_pathname_resolve_relative(new_path, userdata->archive_path, path_basename(name), sizeof(new_path)); - data->first_extracted_file_path = strdup(new_path); - data->found_content = file_archive_perform_mode(new_path, - valid_exts, cdata, cmode, csize, size, - 0, NULL); + userdata->first_extracted_file_path = strdup(new_path); + + char wanted_file[PATH_MAX_LENGTH] = {0}; + const char *delim = path_get_archive_delim(userdata->archive_path); + + if (delim) + strlcpy(wanted_file, delim + 1, strlen(delim) + 1); + + if (!string_is_equal_noncase(userdata->extracted_file_path, + wanted_file)) + return 1; // keep searching for the right file + + if (file_archive_perform_mode(new_path, + valid_exts, cdata, cmode, csize, size, + 0, userdata)) + userdata->found_file = true; + return 0; } return 1; } -static uint32_t read_le(const uint8_t *data, unsigned size) -{ - unsigned i; - uint32_t val = 0; - - size *= 8; - for (i = 0; i < size; i += 8) - val |= (uint32_t)*data++ << i; - - return val; -} - -static int file_archive_parse_file_iterate_step_internal( - file_archive_transfer_t *state, char *filename, - const uint8_t **cdata, - unsigned *cmode, uint32_t *size, uint32_t *csize, - uint32_t *checksum, unsigned *payback) -{ - uint32_t offset; - uint32_t namelength, extralength, commentlength, - offsetNL, offsetEL; - uint32_t signature = read_le(state->directory + 0, 4); - - if (signature != CENTRAL_FILE_HEADER_SIGNATURE) - return 0; - - *cmode = read_le(state->directory + 10, 2); - *checksum = read_le(state->directory + 16, 4); - *csize = read_le(state->directory + 20, 4); - *size = read_le(state->directory + 24, 4); - - namelength = read_le(state->directory + 28, 2); - extralength = read_le(state->directory + 30, 2); - commentlength = read_le(state->directory + 32, 2); - - if (namelength >= PATH_MAX_LENGTH) - return -1; - - memcpy(filename, state->directory + 46, namelength); - - offset = read_le(state->directory + 42, 4); - offsetNL = read_le(state->data + offset + 26, 2); - offsetEL = read_le(state->data + offset + 28, 2); - - *cdata = state->data + offset + 30 + offsetNL + offsetEL; - - *payback = 46 + namelength + extralength + commentlength; - - return 1; -} - -static int file_archive_parse_file_iterate_step(file_archive_transfer_t *state, - const char *valid_exts, void *userdata, file_archive_file_cb file_cb) -{ - const uint8_t *cdata = NULL; - uint32_t checksum = 0; - uint32_t size = 0; - uint32_t csize = 0; - unsigned cmode = 0; - unsigned payload = 0; - char filename[PATH_MAX_LENGTH] = {0}; - int ret = file_archive_parse_file_iterate_step_internal(state, filename, - &cdata, &cmode, &size, &csize, - &checksum, &payload); - - if (ret != 1) - return ret; - -#if 0 - RARCH_LOG("OFFSET: %u, CSIZE: %u, SIZE: %u.\n", offset + 30 + - offsetNL + offsetEL, csize, size); -#endif - - if (!file_cb(filename, valid_exts, cdata, cmode, - csize, size, checksum, userdata)) - return 0; - - state->directory += payload; - - return 1; -} - -static int file_archive_parse_file_init(file_archive_transfer_t *state, +int file_archive_parse_file_init(file_archive_transfer_t *state, const char *file) { - state->backend = file_archive_get_default_file_backend(); + char *last = NULL; + char path[PATH_MAX_LENGTH] = {0}; + strlcpy(path, file, sizeof(path)); + + last = (char*)path_get_archive_delim(path); + + if (last) + *last = '\0'; + + state->backend = file_archive_get_file_backend(path); if (!state->backend) return -1; - state->handle = file_archive_open(file); + state->handle = file_archive_open(path); if (!state->handle) return -1; - state->zip_size = file_archive_size(state->handle); - if (state->zip_size < 22) - return -1; - + state->archive_size = file_archive_size(state->handle); state->data = file_archive_data(state->handle); - state->footer = state->data + state->zip_size - 22; + state->footer = 0; + state->directory = 0; - for (;; state->footer--) - { - if (state->footer <= state->data + 22) - return -1; - if (read_le(state->footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE) - { - unsigned comment_len = read_le(state->footer + 20, 2); - if (state->footer + 22 + comment_len == state->data + state->zip_size) - break; - } - } - - state->directory = state->data + read_le(state->footer + 16, 4); - - return 0; + return state->backend->archive_parse_file_init(state, path); } /** * file_archive_decompress_data_to_file: * @path : filename path of archive. - * @valid_exts : Valid extensions of archive to be parsed. + * @valid_exts : Valid extensions of archive to be parsed. * If NULL, allow all. * @cdata : input data. * @csize : size of input data. @@ -420,26 +324,21 @@ static int file_archive_decompress_data_to_file( uint32_t size, uint32_t checksum) { - if (handle) - { - handle->backend->stream_free(handle->stream); - free(handle->stream); - } - if (!handle || ret == -1) { ret = 0; goto end; } - handle->real_checksum = handle->backend->stream_crc_calculate( - 0, handle->data, size); + handle->backend->stream_free(handle->stream); #if 0 + handle->real_checksum = handle->backend->stream_crc_calculate( + 0, handle->data, size); if (handle->real_checksum != checksum) { - /* File CRC difers from ZIP CRC. */ - printf("File CRC differs from ZIP CRC. File: 0x%x, ZIP: 0x%x.\n", + /* File CRC difers from archive CRC. */ + printf("File CRC differs from archive CRC. File: 0x%x, Archive: 0x%x.\n", (unsigned)handle->real_checksum, (unsigned)checksum); } #endif @@ -456,82 +355,111 @@ end: return ret; } +void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state) +{ + if (!state || !state->handle) + return; + + state->type = ARCHIVE_TRANSFER_DEINIT; + file_archive_parse_file_iterate(state, NULL, NULL, NULL, NULL, NULL); +} + int file_archive_parse_file_iterate( file_archive_transfer_t *state, bool *returnerr, const char *file, const char *valid_exts, file_archive_file_cb file_cb, - void *userdata) + struct archive_extract_userdata *userdata) { if (!state) return -1; switch (state->type) { - case ZLIB_TRANSFER_NONE: + case ARCHIVE_TRANSFER_NONE: break; - case ZLIB_TRANSFER_INIT: + case ARCHIVE_TRANSFER_INIT: if (file_archive_parse_file_init(state, file) == 0) - state->type = ZLIB_TRANSFER_ITERATE; - else - state->type = ZLIB_TRANSFER_DEINIT_ERROR; - break; - case ZLIB_TRANSFER_ITERATE: { - int ret = file_archive_parse_file_iterate_step(state, - valid_exts, userdata, file_cb); - if (ret != 1) - state->type = ZLIB_TRANSFER_DEINIT; - if (ret == -1) - state->type = ZLIB_TRANSFER_DEINIT_ERROR; + if (userdata) + userdata->context = state->stream; + state->type = ARCHIVE_TRANSFER_ITERATE; + } + else + state->type = ARCHIVE_TRANSFER_DEINIT_ERROR; + break; + case ARCHIVE_TRANSFER_ITERATE: + { + const struct file_archive_file_backend *backend = + file_archive_get_file_backend(file); + + if (backend) + { + int ret = backend->archive_parse_file_iterate_step(state, + valid_exts, userdata, file_cb); + if (ret != 1) + state->type = ARCHIVE_TRANSFER_DEINIT; + if (ret == -1) + state->type = ARCHIVE_TRANSFER_DEINIT_ERROR; + + /* early return to prevent deinit from never firing */ + return 0; + } + else + return -1; } break; - case ZLIB_TRANSFER_DEINIT_ERROR: + case ARCHIVE_TRANSFER_DEINIT_ERROR: *returnerr = false; - case ZLIB_TRANSFER_DEINIT: + case ARCHIVE_TRANSFER_DEINIT: if (state->handle) + { file_archive_free(state->handle); - state->handle = NULL; + state->handle = NULL; + } + if (state->stream && state->backend) + { + state->backend->stream_free(state->stream); + + if (state->stream) + free(state->stream); + + state->stream = NULL; + + if (userdata) + userdata->context = NULL; + } break; } - if (state->type == ZLIB_TRANSFER_DEINIT || - state->type == ZLIB_TRANSFER_DEINIT_ERROR) + if (state->type == ARCHIVE_TRANSFER_DEINIT || + state->type == ARCHIVE_TRANSFER_DEINIT_ERROR) return -1; return 0; } -void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state) -{ - if (!state || !state->handle) - return; - - state->type = ZLIB_TRANSFER_DEINIT; - file_archive_parse_file_iterate(state, NULL, NULL, NULL, NULL, NULL); -} - /** * file_archive_parse_file: * @file : filename path of archive - * @valid_exts : Valid extensions of archive to be parsed. + * @valid_exts : Valid extensions of archive to be parsed. * If NULL, allow all. * @file_cb : file_cb function pointer * @userdata : userdata to pass to file_cb function pointer. * - * Low-level file parsing. Enumerates over all files and calls + * Low-level file parsing. Enumerates over all files and calls * file_cb with userdata. * * Returns: true (1) on success, otherwise false (0). **/ static bool file_archive_parse_file(const char *file, const char *valid_exts, - file_archive_file_cb file_cb, void *userdata) + file_archive_file_cb file_cb, struct archive_extract_userdata *userdata) { file_archive_transfer_t state = {0}; bool returnerr = true; - state.type = ZLIB_TRANSFER_INIT; + state.type = ARCHIVE_TRANSFER_INIT; for (;;) { @@ -547,33 +475,34 @@ int file_archive_parse_file_progress(file_archive_transfer_t *state) { /* FIXME: this estimate is worse than before */ ptrdiff_t delta = state->directory - state->data; - return delta * 100 / state->zip_size; + return delta * 100 / state->archive_size; } /** - * file_archive_extract_first_content_file: - * @zip_path : filename path to ZIP archive. - * @zip_path_size : size of ZIP archive. - * @valid_exts : valid extensions for a content file. + * file_archive_extract_file: + * @archive_path : filename path to archive. + * @archive_path_size : size of archive. + * @valid_exts : valid extensions for the file. * @extraction_directory : the directory to extract temporary - * unzipped content to. + * file to. * - * Extract first content file from archive. + * Extract file from archive. If no file inside the archive is + * specified, the first file found will be used. * * Returns : true (1) on success, otherwise false (0). **/ -bool file_archive_extract_first_content_file( - char *zip_path, - size_t zip_path_size, +bool file_archive_extract_file( + char *archive_path, + size_t archive_path_size, const char *valid_exts, const char *extraction_directory, char *out_path, size_t len) { struct string_list *list = NULL; bool ret = true; - struct zip_extract_userdata userdata = {0}; + struct archive_extract_userdata userdata = {0}; - /* We cannot unzip if the libretro + /* We cannot extract if the libretro * implementation does not have any valid extensions. */ if (!valid_exts) return false; @@ -585,12 +514,15 @@ bool file_archive_extract_first_content_file( goto end; } - userdata.zip_path = zip_path; - userdata.zip_path_size = zip_path_size; + userdata.archive_path = archive_path; + userdata.archive_path_size = archive_path_size; userdata.extraction_directory = extraction_directory; userdata.ext = list; + userdata.list = NULL; + userdata.context = NULL; + userdata.list_only = false; - if (!file_archive_parse_file(zip_path, valid_exts, + if (!file_archive_parse_file(archive_path, valid_exts, file_archive_extract_cb, &userdata)) { /* Parsing file archive failed. */ @@ -598,9 +530,9 @@ bool file_archive_extract_first_content_file( goto end; } - if (!userdata.found_content) + if (!userdata.found_file) { - /* Didn't find any content that matched valid extensions + /* Didn't find any file that matched valid extensions * for libretro implementation. */ ret = false; goto end; @@ -626,39 +558,44 @@ end: struct string_list *file_archive_get_file_list(const char *path, const char *valid_exts) { - struct string_list *list = string_list_new(); + struct archive_extract_userdata userdata = {0}; + userdata.list_only = true; - if (!list) + userdata.list = string_list_new(); + + if (!userdata.list) goto error; if (!file_archive_parse_file(path, valid_exts, - file_archive_get_file_list_cb, list)) + file_archive_get_file_list_cb, &userdata)) goto error; - return list; + return userdata.list; error: - if (list) - string_list_free(list); + if (userdata.list) + string_list_free(userdata.list); return NULL; } bool file_archive_perform_mode(const char *path, const char *valid_exts, const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, - uint32_t crc32, void *userdata) + uint32_t crc32, struct archive_extract_userdata *userdata) { switch (cmode) { - case ZLIB_MODE_UNCOMPRESSED: + case ARCHIVE_MODE_UNCOMPRESSED: if (!filestream_write_file(path, cdata, size)) goto error; break; - case ZLIB_MODE_DEFLATE: + case ARCHIVE_MODE_COMPRESSED: { int ret = 0; file_archive_file_handle_t handle = {0}; - handle.backend = file_archive_get_default_file_backend(); + + handle.backend = file_archive_get_file_backend(userdata->archive_path); + handle.stream = userdata->context; if (!handle.backend->stream_decompress_data_to_file_init(&handle, cdata, csize, size)) @@ -685,7 +622,156 @@ error: return false; } -const struct file_archive_file_backend *file_archive_get_default_file_backend(void) +/* Generic compressed file loader. + * Extracts to buf, unless optional_filename != 0 + * Then extracts to optional_filename and leaves buf alone. + */ +int file_archive_compressed_read( + const char * path, void **buf, + const char* optional_filename, ssize_t *length) { - return &zlib_backend; + const struct file_archive_file_backend *backend = NULL; + int ret = 0; + struct string_list *str_list = file_archive_filename_split(path); + + /* Safety check. + * If optional_filename and optional_filename + * exists, we simply return 0, + * hoping that optional_filename is the + * same as requested. + */ + if (optional_filename && path_file_exists(optional_filename)) + { + *length = 0; + string_list_free(str_list); + return 1; + } + + /* We assure that there is something after the '#' symbol. + * + * This error condition happens for example, when + * path = /path/to/file.7z, or + * path = /path/to/file.7z# + */ + if (str_list->size <= 1) + goto error; + + backend = file_archive_get_file_backend(str_list->elems[0].data); + + *length = backend->compressed_file_read(str_list->elems[0].data, + str_list->elems[1].data, buf, optional_filename); + + if (*length != -1) + ret = 1; + + string_list_free(str_list); + return ret; + +error: + /* could not extract string and substring. */ + string_list_free(str_list); + *length = 0; + return 0; +} + +struct string_list *file_archive_file_list_new(const char *path, + const char* ext) +{ +#ifdef HAVE_COMPRESSION + bool compressed = path_is_compressed_file(path); + + if (compressed) + return file_archive_get_file_list(path, ext); +#endif + return NULL; +} + +/** + * file_archive_filename_split: + * @str : filename to turn into a string list + * + * Creates a new string list based on filename @path, delimited by a hash (#). + * + * Returns: new string list if successful, otherwise NULL. + */ +struct string_list *file_archive_filename_split(const char *path) +{ + union string_list_elem_attr attr; + struct string_list *list = string_list_new(); + const char *delim = NULL; + + memset(&attr, 0, sizeof(attr)); + + delim = path_get_archive_delim(path); + + if (delim) + { + /* add archive path to list first */ + if (!string_list_append_n(list, path, delim - path, attr)) + goto error; + + /* now add the path within the archive */ + delim++; + + if (*delim) + { + if (!string_list_append(list, delim, attr)) + goto error; + } + } + else + if (!string_list_append(list, path, attr)) + goto error; + + return list; + +error: + string_list_free(list); + return NULL; +} + +const struct file_archive_file_backend *file_archive_get_zlib_file_backend(void) +{ +#ifdef HAVE_ZLIB + return &zlib_backend; +#else + return NULL; +#endif +} + +const struct file_archive_file_backend *file_archive_get_7z_file_backend(void) +{ +#ifdef HAVE_7ZIP + return &sevenzip_backend; +#else + return NULL; +#endif +} + +const struct file_archive_file_backend* file_archive_get_file_backend(const char *path) +{ + const char *file_ext = NULL; + char *last = NULL; + char newpath[PATH_MAX_LENGTH] = {0}; + + strlcpy(newpath, path, sizeof(newpath)); + + last = (char*)path_get_archive_delim(newpath); + + if (last) + *last = '\0'; + + file_ext = path_get_extension(newpath); + +#ifdef HAVE_7ZIP + if (string_is_equal_noncase(file_ext, "7z")) + return &sevenzip_backend; +#endif + +#ifdef HAVE_ZLIB + if (string_is_equal_noncase(file_ext, "zip")) + return &zlib_backend; +#endif + + return NULL; } diff --git a/libretro-common/file/archive_file_7z.c b/libretro-common/file/archive_file_7z.c new file mode 100644 index 0000000000..1d1cf45926 --- /dev/null +++ b/libretro-common/file/archive_file_7z.c @@ -0,0 +1,420 @@ +/* Copyright (C) 2010-2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (archive_file_sevenzip.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../deps/7zip/7z.h" +#include "../../deps/7zip/7zAlloc.h" +#include "../../deps/7zip/7zCrc.h" +#include "../../deps/7zip/7zFile.h" + +#define SEVENZIP_MAGIC "7z\xBC\xAF\x27\x1C" +#define SEVENZIP_MAGIC_LEN 6 + +struct sevenzip_context_t { + CFileInStream archiveStream; + CLookToRead lookStream; + ISzAlloc allocImp; + ISzAlloc allocTempImp; + CSzArEx db; + size_t temp_size; + uint32_t block_index; + uint32_t index; + uint32_t packIndex; + uint8_t *output; + file_archive_file_handle_t *handle; +}; + +static void* sevenzip_stream_new(void) +{ + struct sevenzip_context_t *sevenzip_context = + (struct sevenzip_context_t*)calloc(1, sizeof(struct sevenzip_context_t)); + + /* These are the allocation routines - currently using + * the non-standard 7zip choices. */ + sevenzip_context->allocImp.Alloc = SzAlloc; + sevenzip_context->allocImp.Free = SzFree; + sevenzip_context->allocTempImp.Alloc = SzAllocTemp; + sevenzip_context->allocTempImp.Free = SzFreeTemp; + sevenzip_context->index = 0; + sevenzip_context->packIndex = 0; + sevenzip_context->temp_size = 0; + sevenzip_context->block_index = 0xFFFFFFFF; + sevenzip_context->output = NULL; + + return sevenzip_context; +} + +static void sevenzip_stream_free(void *data) +{ + struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)data; + + if (!sevenzip_context) + return; + + SzArEx_Free(&sevenzip_context->db, &sevenzip_context->allocImp); + File_Close(&sevenzip_context->archiveStream.file); +} + +/* Extract the relative path (needle) from a 7z archive + * (path) and allocate a buf for it to write it in. + * If optional_outfile is set, extract to that instead + * and don't allocate buffer. + */ +static int sevenzip_file_read( + const char *path, + const char *needle, void **buf, + const char *optional_outfile) +{ + CFileInStream archiveStream; + CLookToRead lookStream; + ISzAlloc allocImp; + ISzAlloc allocTempImp; + CSzArEx db; + uint8_t *output = 0; + long outsize = -1; + + /*These are the allocation routines. + * Currently using the non-standard 7zip choices. */ + allocImp.Alloc = SzAlloc; + allocImp.Free = SzFree; + allocTempImp.Alloc = SzAllocTemp; + allocTempImp.Free = SzFreeTemp; + + /* Could not open 7zip archive? */ + if (InFile_Open(&archiveStream.file, path)) + return -1; + + FileInStream_CreateVTable(&archiveStream); + LookToRead_CreateVTable(&lookStream, False); + lookStream.realStream = &archiveStream.s; + LookToRead_Init(&lookStream); + CrcGenerateTable(); + SzArEx_Init(&db); + + if (SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp) == SZ_OK) + { + uint32_t i; + bool file_found = false; + uint16_t *temp = NULL; + size_t temp_size = 0; + uint32_t block_index = 0xFFFFFFFF; + SRes res = SZ_OK; + + for (i = 0; i < db.db.NumFiles; i++) + { + size_t len; + char infile[PATH_MAX_LENGTH] = {0}; + size_t offset = 0; + size_t outSizeProcessed = 0; + const CSzFileItem *f = db.db.Files + i; + + /* We skip over everything which is not a directory. + * FIXME: Why continue then if f->IsDir is true?*/ + if (f->IsDir) + continue; + + len = SzArEx_GetFileNameUtf16(&db, i, NULL); + + if (len > temp_size) + { + if (temp) + free(temp); + temp_size = len; + temp = (uint16_t *)malloc(temp_size * sizeof(temp[0])); + + if (temp == 0) + { + res = SZ_ERROR_MEM; + break; + } + } + + SzArEx_GetFileNameUtf16(&db, i, temp); + res = SZ_ERROR_FAIL; + if (temp) + res = utf16_to_char_string(temp, infile, sizeof(infile)) + ? SZ_OK : SZ_ERROR_FAIL; + + if (string_is_equal(infile, needle)) + { + size_t output_size = 0; + + /*RARCH_LOG_OUTPUT("Opened archive %s. Now trying to extract %s\n", + path, needle);*/ + + /* C LZMA SDK does not support chunked extraction - see here: + * sourceforge.net/p/sevenzip/discussion/45798/thread/6fb59aaf/ + * */ + file_found = true; + res = SzArEx_Extract(&db, &lookStream.s, i, &block_index, + &output, &output_size, &offset, &outSizeProcessed, + &allocImp, &allocTempImp); + + if (res != SZ_OK) + break; /* This goes to the error section. */ + + outsize = outSizeProcessed; + + if (optional_outfile != NULL) + { + const void *ptr = (const void*)(output + offset); + + if (!filestream_write_file(optional_outfile, ptr, outsize)) + { + /*RARCH_ERR("Could not open outfilepath %s.\n", + optional_outfile);*/ + res = SZ_OK; + file_found = true; + outsize = -1; + } + } + else + { + /*We could either use the 7Zip allocated buffer, + * or create our own and use it. + * We would however need to realloc anyways, because RetroArch + * expects a \0 at the end, therefore we allocate new, + * copy and free the old one. */ + *buf = malloc(outsize + 1); + ((char*)(*buf))[outsize] = '\0'; + memcpy(*buf,output + offset,outsize); + } + break; + } + } + + if (temp) + free(temp); + IAlloc_Free(&allocImp, output); + + if (!(file_found && res == SZ_OK)) + { + /* Error handling + * + * Failed to open compressed file inside 7zip archive. + */ + + outsize = -1; + } + } + + SzArEx_Free(&db, &allocImp); + File_Close(&archiveStream.file); + + return outsize; +} + +static bool sevenzip_stream_decompress_data_to_file_init( + file_archive_file_handle_t *handle, + const uint8_t *cdata, uint32_t csize, uint32_t size) +{ + struct sevenzip_context_t *sevenzip_context = + (struct sevenzip_context_t*)handle->stream; + + if (!sevenzip_context) + return false; + + sevenzip_context->handle = handle; + + return true; +} + +static int sevenzip_stream_decompress_data_to_file_iterate(void *data) +{ + struct sevenzip_context_t *sevenzip_context = + (struct sevenzip_context_t*)data; + + if (sevenzip_context->handle) + sevenzip_context->handle->data = sevenzip_context->output; + + return 1; +} + +static int sevenzip_parse_file_init(file_archive_transfer_t *state, + const char *file) +{ + struct sevenzip_context_t *sevenzip_context = NULL; + if (state->archive_size < SEVENZIP_MAGIC_LEN) + return -1; + + if (memcmp(state->data, SEVENZIP_MAGIC, SEVENZIP_MAGIC_LEN) != 0) + return -1; + + sevenzip_context = (struct sevenzip_context_t*)sevenzip_stream_new(); + + /* could not open 7zip archive? */ + if (InFile_Open(&sevenzip_context->archiveStream.file, file)) + return -1; + + FileInStream_CreateVTable(&sevenzip_context->archiveStream); + LookToRead_CreateVTable(&sevenzip_context->lookStream, False); + sevenzip_context->lookStream.realStream = &sevenzip_context->archiveStream.s; + LookToRead_Init(&sevenzip_context->lookStream); + CrcGenerateTable(); + SzArEx_Init(&sevenzip_context->db); + + SzArEx_Open(&sevenzip_context->db, &sevenzip_context->lookStream.s, + &sevenzip_context->allocImp, &sevenzip_context->allocTempImp); + + state->stream = sevenzip_context; + + return 0; +} + +static int sevenzip_parse_file_iterate_step_internal( + file_archive_transfer_t *state, char *filename, + const uint8_t **cdata, unsigned *cmode, + uint32_t *size, uint32_t *csize, uint32_t *checksum, + unsigned *payback, struct archive_extract_userdata *userdata) +{ + struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)state->stream; + const CSzFileItem *file = sevenzip_context->db.db.Files + sevenzip_context->index; + + if (sevenzip_context->index < sevenzip_context->db.db.NumFiles) + { + size_t len = SzArEx_GetFileNameUtf16(&sevenzip_context->db, sevenzip_context->index, NULL); + uint64_t compressed_size = 0; + + if (sevenzip_context->packIndex < sevenzip_context->db.db.NumPackStreams) + { + compressed_size = sevenzip_context->db.db.PackSizes[sevenzip_context->packIndex]; + sevenzip_context->packIndex++; + } + + if (len < PATH_MAX_LENGTH && !file->IsDir) + { + SRes res = SZ_ERROR_FAIL; + char infile[PATH_MAX_LENGTH] = {0}; + uint16_t *temp = (uint16_t*)malloc(len * sizeof(uint16_t)); + + if (!temp) + return -1; + + SzArEx_GetFileNameUtf16(&sevenzip_context->db, sevenzip_context->index, temp); + + if (temp) + { + res = utf16_to_char_string(temp, infile, sizeof(infile)) + ? SZ_OK : SZ_ERROR_FAIL; + free(temp); + } + + if (res != SZ_OK) + return -1; + + strlcpy(filename, infile, PATH_MAX_LENGTH); + + if (!userdata->list_only) + { + size_t output_size = 0; + size_t offset = 0; + size_t outSizeProcessed = 0; + + res = SzArEx_Extract(&sevenzip_context->db, + &sevenzip_context->lookStream.s, sevenzip_context->index, + &sevenzip_context->block_index, &sevenzip_context->output, + &output_size, &offset, &outSizeProcessed, + &sevenzip_context->allocImp, &sevenzip_context->allocTempImp); + } + + if (res != SZ_OK) + return -1; + + *cmode = ARCHIVE_MODE_COMPRESSED; + *checksum = file->Crc; + *size = file->Size; + *csize = compressed_size; + } + } + + *payback = 1; + + return 1; +} + +static int sevenzip_parse_file_iterate_step(file_archive_transfer_t *state, + const char *valid_exts, struct archive_extract_userdata *userdata, file_archive_file_cb file_cb) +{ + const uint8_t *cdata = NULL; + uint32_t checksum = 0; + uint32_t size = 0; + uint32_t csize = 0; + unsigned cmode = 0; + unsigned payload = 0; + struct sevenzip_context_t *sevenzip_context = NULL; + char filename[PATH_MAX_LENGTH] = {0}; + int ret = sevenzip_parse_file_iterate_step_internal(state, filename, + &cdata, &cmode, &size, &csize, + &checksum, &payload, userdata); + + if (ret != 1) + return ret; + + userdata->extracted_file_path = filename; + + if (!file_cb(filename, valid_exts, cdata, cmode, + csize, size, checksum, userdata)) + return 0; + + sevenzip_context = (struct sevenzip_context_t*)state->stream; + + sevenzip_context->index += payload; + + return 1; +} + +static uint32_t sevenzip_stream_crc32_calculate(uint32_t crc, + const uint8_t *data, size_t length) +{ + return encoding_crc32(crc, data, length); +} + +const struct file_archive_file_backend sevenzip_backend = { + sevenzip_stream_new, + sevenzip_stream_free, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + sevenzip_stream_decompress_data_to_file_init, + sevenzip_stream_decompress_data_to_file_iterate, + NULL, + NULL, + NULL, + sevenzip_stream_crc32_calculate, + sevenzip_file_read, + sevenzip_parse_file_init, + sevenzip_parse_file_iterate_step, + "7z" +}; diff --git a/libretro-common/file/archive_file_zlib.c b/libretro-common/file/archive_file_zlib.c index fd9fb281bc..2aa998bd6e 100644 --- a/libretro-common/file/archive_file_zlib.c +++ b/libretro-common/file/archive_file_zlib.c @@ -25,8 +25,19 @@ #include #include #include +#include +#include +#include -static void *zlib_stream_new(void) +#ifndef CENTRAL_FILE_HEADER_SIGNATURE +#define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50 +#endif + +#ifndef END_OF_CENTRAL_DIR_SIGNATURE +#define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50 +#endif + +static void* zlib_stream_new(void) { return (z_stream*)calloc(1, sizeof(z_stream)); } @@ -138,7 +149,7 @@ static bool zlib_stream_decompress_data_to_file_init( if (!(handle->stream = (z_stream*)zlib_stream_new())) goto error; - + if (inflateInit2((z_streamp)handle->stream, -MAX_WBITS) != Z_OK) goto error; @@ -194,7 +205,257 @@ static void zlib_stream_compress_init(void *data, int level) static uint32_t zlib_stream_crc32_calculate(uint32_t crc, const uint8_t *data, size_t length) { - return crc32(crc, data, length); + return encoding_crc32(crc, data, length); +} + +static bool zip_file_decompressed_handle( + file_archive_file_handle_t *handle, + const uint8_t *cdata, uint32_t csize, + uint32_t size, uint32_t crc32) +{ + int ret = 0; + + handle->backend = &zlib_backend; + + if (!handle->backend->stream_decompress_data_to_file_init( + handle, cdata, csize, size)) + return false; + + do{ + ret = handle->backend->stream_decompress_data_to_file_iterate( + handle->stream); + }while(ret == 0); +#if 0 + handle->real_checksum = handle->backend->stream_crc_calculate(0, + handle->data, size); + + if (handle->real_checksum != crc32) + goto error; +#endif + + if (handle->stream) + free(handle->stream); + + return true; +#if 0 +error: + if (handle->stream) + free(handle->stream); + if (handle->data) + free(handle->data); + + handle->stream = NULL; + handle->data = NULL; + return false; +#endif +} + +/* Extract the relative path (needle) from a + * ZIP archive (path) and allocate a buffer for it to write it in. + * + * optional_outfile if not NULL will be used to extract the file to. + * buf will be 0 then. + */ + +static int zip_file_decompressed( + const char *name, const char *valid_exts, + const uint8_t *cdata, unsigned cmode, + uint32_t csize, uint32_t size, + uint32_t crc32, struct archive_extract_userdata *userdata) +{ + /* Ignore directories. */ + if (name[strlen(name) - 1] == '/' || name[strlen(name) - 1] == '\\') + return 1; + +#if 0 + RARCH_LOG("[deflate] Path: %s, CRC32: 0x%x\n", name, crc32); +#endif + + if (strstr(name, userdata->decomp_state.needle)) + { + bool goto_error = false; + file_archive_file_handle_t handle = {0}; + + userdata->decomp_state.found = true; + + if (zip_file_decompressed_handle(&handle, + cdata, csize, size, crc32)) + { + if (userdata->decomp_state.opt_file != 0) + { + /* Called in case core has need_fullpath enabled. */ + char *buf = (char*)malloc(size); + + if (buf) + { + /*RARCH_LOG("%s: %s\n", + msg_hash_to_str(MSG_EXTRACTING_FILE), + userdata->decomp_state.opt_file);*/ + memcpy(buf, handle.data, size); + + if (!filestream_write_file(userdata->decomp_state.opt_file, buf, size)) + goto_error = true; + } + + free(buf); + + userdata->decomp_state.size = 0; + } + else + { + /* Called in case core has need_fullpath disabled. + * Will copy decompressed content directly into + * RetroArch's ROM buffer. */ + *userdata->decomp_state.buf = malloc(size); + memcpy(*userdata->decomp_state.buf, handle.data, size); + + userdata->decomp_state.size = size; + } + } + + if (handle.data) + free(handle.data); + + if (goto_error) + return 0; + } + + return 1; +} + +static int zip_file_read( + const char *path, + const char *needle, void **buf, + const char *optional_outfile) +{ + file_archive_transfer_t zlib; + bool returnerr = true; + int ret = 0; + struct archive_extract_userdata userdata = {0}; + + zlib.type = ARCHIVE_TRANSFER_INIT; + + userdata.decomp_state.needle = NULL; + userdata.decomp_state.opt_file = NULL; + userdata.decomp_state.found = false; + userdata.decomp_state.buf = buf; + + if (needle) + userdata.decomp_state.needle = strdup(needle); + if (optional_outfile) + userdata.decomp_state.opt_file = strdup(optional_outfile); + + do + { + ret = file_archive_parse_file_iterate(&zlib, &returnerr, path, + "", zip_file_decompressed, &userdata); + if (!returnerr) + break; + }while(ret == 0 && !userdata.decomp_state.found); + + file_archive_parse_file_iterate_stop(&zlib); + + if (userdata.decomp_state.opt_file) + free(userdata.decomp_state.opt_file); + if (userdata.decomp_state.needle) + free(userdata.decomp_state.needle); + + if (!userdata.decomp_state.found) + return -1; + + return userdata.decomp_state.size; +} + +static int zip_parse_file_init(file_archive_transfer_t *state, + const char *file) +{ + if (state->archive_size < 22) + return -1; + + state->footer = state->data + state->archive_size - 22; + + for (;; state->footer--) + { + if (state->footer <= state->data + 22) + return -1; + if (read_le(state->footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE) + { + unsigned comment_len = read_le(state->footer + 20, 2); + if (state->footer + 22 + comment_len == state->data + state->archive_size) + break; + } + } + + state->directory = state->data + read_le(state->footer + 16, 4); + + return 0; +} + +static int zip_parse_file_iterate_step_internal( + file_archive_transfer_t *state, char *filename, + const uint8_t **cdata, + unsigned *cmode, uint32_t *size, uint32_t *csize, + uint32_t *checksum, unsigned *payback) +{ + uint32_t offset; + uint32_t namelength, extralength, commentlength, + offsetNL, offsetEL; + uint32_t signature = read_le(state->directory + 0, 4); + + if (signature != CENTRAL_FILE_HEADER_SIGNATURE) + return 0; + + *cmode = read_le(state->directory + 10, 2); /* compression mode, 0 = store, 8 = deflate */ + *checksum = read_le(state->directory + 16, 4); /* CRC32 */ + *csize = read_le(state->directory + 20, 4); /* compressed size */ + *size = read_le(state->directory + 24, 4); /* uncompressed size */ + + namelength = read_le(state->directory + 28, 2); /* file name length */ + extralength = read_le(state->directory + 30, 2); /* extra field length */ + commentlength = read_le(state->directory + 32, 2); /* file comment length */ + + if (namelength >= PATH_MAX_LENGTH) + return -1; + + memcpy(filename, state->directory + 46, namelength); /* file name */ + + offset = read_le(state->directory + 42, 4); /* relative offset of local file header */ + offsetNL = read_le(state->data + offset + 26, 2); /* file name length */ + offsetEL = read_le(state->data + offset + 28, 2); /* extra field length */ + + *cdata = state->data + offset + 30 + offsetNL + offsetEL; + + *payback = 46 + namelength + extralength + commentlength; + + return 1; +} + +static int zip_parse_file_iterate_step(file_archive_transfer_t *state, + const char *valid_exts, struct archive_extract_userdata *userdata, file_archive_file_cb file_cb) +{ + const uint8_t *cdata = NULL; + uint32_t checksum = 0; + uint32_t size = 0; + uint32_t csize = 0; + unsigned cmode = 0; + unsigned payload = 0; + char filename[PATH_MAX_LENGTH] = {0}; + int ret = zip_parse_file_iterate_step_internal(state, filename, + &cdata, &cmode, &size, &csize, + &checksum, &payload); + + if (ret != 1) + return ret; + + userdata->extracted_file_path = filename; + + if (!file_cb(filename, valid_exts, cdata, cmode, + csize, size, checksum, userdata)) + return 0; + + state->directory += payload; + + return 1; } const struct file_archive_file_backend zlib_backend = { @@ -212,5 +473,8 @@ const struct file_archive_file_backend zlib_backend = { zlib_stream_compress_free, zlib_stream_compress_data_to_file, zlib_stream_crc32_calculate, + zip_file_read, + zip_parse_file_init, + zip_parse_file_iterate_step, "zlib" }; diff --git a/libretro-common/file/config_file.c b/libretro-common/file/config_file.c index 9b049b0013..452b735619 100644 --- a/libretro-common/file/config_file.c +++ b/libretro-common/file/config_file.c @@ -948,3 +948,12 @@ bool config_get_entry_list_next(struct config_file_entry *entry) return true; } +bool config_file_exists(const char *path) +{ + config_file_t *config = config_file_new(path); + if (!config) + return false; + + config_file_free(config); + return true; +} diff --git a/libretro-common/file/file_path.c b/libretro-common/file/file_path.c index ed8095fe7a..7f99e489d8 100644 --- a/libretro-common/file/file_path.c +++ b/libretro-common/file/file_path.c @@ -194,12 +194,12 @@ bool path_is_compressed_file(const char* path) const char *ext = path_get_extension(path); #ifdef HAVE_ZLIB - if (strcasestr(ext, "zip")) + if (string_is_equal_noncase(ext, "zip")) return true; #endif #ifdef HAVE_7ZIP - if (strcasestr(ext, "7z")) + if (string_is_equal_noncase(ext, "7z")) return true; #endif diff --git a/libretro-common/formats/png/rpng.c b/libretro-common/formats/png/rpng.c index 47abc04e4e..8844b44918 100644 --- a/libretro-common/formats/png/rpng.c +++ b/libretro-common/formats/png/rpng.c @@ -870,7 +870,7 @@ static struct rpng_process *rpng_process_init(rpng_t *rpng, unsigned *width, uns if (!process) return NULL; - process->stream_backend = file_archive_get_default_file_backend(); + process->stream_backend = file_archive_get_zlib_file_backend(); png_pass_geom(&rpng->ihdr, rpng->ihdr.width, rpng->ihdr.height, NULL, NULL, &process->inflate_buf_size); diff --git a/libretro-common/formats/png/rpng_encode.c b/libretro-common/formats/png/rpng_encode.c index b164c15a66..7e47899ca8 100644 --- a/libretro-common/formats/png/rpng_encode.c +++ b/libretro-common/formats/png/rpng_encode.c @@ -48,7 +48,7 @@ static bool png_write_crc(RFILE *file, const uint8_t *data, size_t size) { uint8_t crc_raw[4] = {0}; const struct file_archive_file_backend *stream_backend = - file_archive_get_default_file_backend(); + file_archive_get_zlib_file_backend(); uint32_t crc = stream_backend->stream_crc_calculate(0, data, size); dword_write_be(crc_raw, crc); @@ -230,7 +230,7 @@ static bool rpng_save_image(const char *path, if (!file) GOTO_END_ERROR(); - stream_backend = file_archive_get_default_file_backend(); + stream_backend = file_archive_get_zlib_file_backend(); if (filestream_write(file, png_magic, sizeof(png_magic)) != sizeof(png_magic)) GOTO_END_ERROR(); diff --git a/libretro-common/gfx/math/matrix_4x4.c b/libretro-common/gfx/math/matrix_4x4.c index 1e4b72b2cd..8b557074d1 100644 --- a/libretro-common/gfx/math/matrix_4x4.c +++ b/libretro-common/gfx/math/matrix_4x4.c @@ -24,6 +24,7 @@ #include #include +#include void matrix_4x4_copy(math_matrix_4x4 *dst, const math_matrix_4x4 *src) { @@ -160,17 +161,60 @@ void matrix_4x4_translate(math_matrix_4x4 *out, float x, /* * Creates a perspective projection matrix. */ -void matrix_4x4_projection(math_matrix_4x4 *out, float znear, +void matrix_4x4_projection(math_matrix_4x4 *out, + float y_fov, + float aspect, + float znear, float zfar) { + float const a = 1.f / tan(y_fov / 2.f); float delta_z = zfar - znear; memset(out, 0, sizeof(*out)); - MAT_ELEM_4X4(*out, 0, 0) = znear; - MAT_ELEM_4X4(*out, 1, 1) = zfar; - MAT_ELEM_4X4(*out, 2, 2) = (zfar + znear) / delta_z; - MAT_ELEM_4X4(*out, 2, 3) = -2.0f * zfar * znear / delta_z; - MAT_ELEM_4X4(*out, 3, 2) = -1.0f; + MAT_ELEM_4X4(*out, 0, 0) = a / aspect; + MAT_ELEM_4X4(*out, 1, 1) = a; + MAT_ELEM_4X4(*out, 2, 2) = -((zfar + znear) / delta_z); + MAT_ELEM_4X4(*out, 2, 3) = -1.f; + MAT_ELEM_4X4(*out, 3, 2) = -((2.f * zfar * znear) / delta_z); +} + +/* TODO/FIXME - finish */ +void matrix_4x4_lookat(math_matrix_4x4 *out, + vec3_t eye, + vec3_t center, + vec3_t up) +{ + vec3_t s, t, f; + + vec3_copy(&f[0], center); + vec3_subtract(&f[0], eye); + vec3_normalize(&f[0]); + + vec3_cross(&s[0], &f[0], up); + vec3_normalize(&s[0]); + + vec3_cross(&t[0], &s[0], f); + + memset(out, 0, sizeof(*out)); + + MAT_ELEM_4X4(*out, 0, 0) = s[0]; + MAT_ELEM_4X4(*out, 0, 1) = t[0]; + MAT_ELEM_4X4(*out, 0, 2) = -f[0]; + + MAT_ELEM_4X4(*out, 1, 0) = s[1]; + MAT_ELEM_4X4(*out, 1, 1) = t[1]; + MAT_ELEM_4X4(*out, 1, 2) = -f[1]; + + MAT_ELEM_4X4(*out, 2, 0) = s[2]; + MAT_ELEM_4X4(*out, 2, 1) = t[2]; + MAT_ELEM_4X4(*out, 2, 2) = -f[2]; + + MAT_ELEM_4X4(*out, 3, 3) = 1.f; + +#if 0 + mat4x4_translate_in_place(m, -eye[0], -eye[1], -eye[2]); +#endif + } /* diff --git a/libretro-common/gfx/math/vector_2.c b/libretro-common/gfx/math/vector_2.c index 6719fac93a..b7745e456e 100644 --- a/libretro-common/gfx/math/vector_2.c +++ b/libretro-common/gfx/math/vector_2.c @@ -37,18 +37,27 @@ float vec2_cross(const float *a, const float *b) void vec2_add(float *dst, const float *src) { - dst[0] += src[0]; - dst[1] += src[1]; + unsigned i; + unsigned n = 2; + + for (i = 0; i < n; i++) + dst[i] += src[i]; } void vec2_subtract(float *dst, const float *src) { - dst[0] -= src[0]; - dst[1] -= src[1]; + unsigned i; + unsigned n = 2; + + for (i = 0; i < n; i++) + dst[i] -= src[i]; } void vec2_copy(float *dst, const float *src) { - dst[0] = src[0]; - dst[1] = src[1]; + unsigned i; + unsigned n = 2; + + for (i = 0; i < n; i++) + dst[i] = src[i]; } diff --git a/libretro-common/gfx/math/vector_3.c b/libretro-common/gfx/math/vector_3.c index 1ec04acab7..4f76dde269 100644 --- a/libretro-common/gfx/math/vector_3.c +++ b/libretro-common/gfx/math/vector_3.c @@ -46,30 +46,38 @@ float vec3_length(const float *a) void vec3_add(float *dst, const float *src) { - dst[0] += src[0]; - dst[1] += src[1]; - dst[2] += src[2]; + unsigned i; + unsigned n = 3; + + for (i = 0; i < n; i++) + dst[i] += src[i]; } void vec3_subtract(float *dst, const float *src) { - dst[0] -= src[0]; - dst[1] -= src[1]; - dst[2] -= src[2]; + unsigned i; + unsigned n = 3; + + for (i = 0; i < n; i++) + dst[i] -= src[i]; } void vec3_scale(float *dst, const float scale) { - dst[0] *= scale; - dst[1] *= scale; - dst[2] *= scale; + unsigned i; + unsigned n = 3; + + for (i = 0; i < n; i++) + dst[i] *= scale; } void vec3_copy(float *dst, const float *src) { - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; + unsigned i; + unsigned n = 3; + + for (i = 0; i < n; i++) + dst[i] = src[i]; } void vec3_normalize(float *dst) diff --git a/libretro-common/gfx/math/vector_4.c b/libretro-common/gfx/math/vector_4.c new file mode 100644 index 0000000000..0c2d4d5fcf --- /dev/null +++ b/libretro-common/gfx/math/vector_4.c @@ -0,0 +1,62 @@ +/* Copyright (C) 2010-2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (vector_4.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include + +void vec4_add(float *dst, const float *src) +{ + unsigned i; + unsigned n = 4; + + for (i = 0; i < n; i++) + dst[i] += src[i]; +} + +void vec4_subtract(float *dst, const float *src) +{ + unsigned i; + unsigned n = 4; + + for (i = 0; i < n; i++) + dst[i] -= src[i]; +} + +void vec4_scale(float *dst, const float scale) +{ + unsigned i; + unsigned n = 4; + + for (i = 0; i < n; i++) + dst[i] *= scale; +} + +void vec4_copy(float *dst, const float *src) +{ + unsigned i; + unsigned n = 4; + + for (i = 0; i < n; i++) + dst[i] = src[i]; +} diff --git a/libretro-common/gfx/scaler/scaler_filter.c b/libretro-common/gfx/scaler/scaler_filter.c index 50c290c8c3..a4996977b5 100644 --- a/libretro-common/gfx/scaler/scaler_filter.c +++ b/libretro-common/gfx/scaler/scaler_filter.c @@ -30,6 +30,8 @@ #include #include +#define FILTER_UNITY (1 << 14) + static bool allocate_filters(struct scaler_ctx *ctx) { ctx->horiz.filter = (int16_t*)scaler_alloc(sizeof(int16_t), ctx->horiz.filter_stride * ctx->out_width); diff --git a/libretro-common/glsm/glsm.c b/libretro-common/glsm/glsm.c index e60ad6a56b..4c4546a1e0 100644 --- a/libretro-common/glsm/glsm.c +++ b/libretro-common/glsm/glsm.c @@ -38,6 +38,16 @@ struct gl_cached_state GLuint *ids; } bind_textures; + struct + { + bool used[MAX_ATTRIB]; + GLint size[MAX_ATTRIB]; + GLenum type[MAX_ATTRIB]; + GLboolean normalized[MAX_ATTRIB]; + GLsizei stride[MAX_ATTRIB]; + const GLvoid *pointer[MAX_ATTRIB]; + } attrib_pointer; + #ifndef HAVE_OPENGLES GLenum colorlogicop; #endif @@ -1212,6 +1222,12 @@ void rglVertexAttribPointer(GLuint name, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer) { + gl_state.attrib_pointer.used[name] = 1; + gl_state.attrib_pointer.size[name] = size; + gl_state.attrib_pointer.type[name] = type; + gl_state.attrib_pointer.normalized[name] = normalized; + gl_state.attrib_pointer.stride[name] = stride; + gl_state.attrib_pointer.pointer[name] = pointer; glVertexAttribPointer(name, size, type, normalized, stride, pointer); } @@ -1835,6 +1851,18 @@ void *rglFenceSync(GLenum condition, GLbitfield flags) #endif } +/* + * + * Core in: + * OpenGL : 3.2 + * OpenGLES : 3.0 + */ +void rglDeleteSync(GLsync sync) { +#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) && defined(HAVE_OPENGLES3) + glDeleteSync(sync); +#endif +} + /* * * Core in: @@ -1848,6 +1876,56 @@ void rglWaitSync(void *sync, GLbitfield flags, uint64_t timeout) #endif } +/* + * + * Core in: + * OpenGL : 4.4 + * OpenGLES : Not available + */ +void rglBufferStorage(GLenum target, GLsizeiptr size, const GLvoid *data, GLbitfield flags) { +#if defined(HAVE_OPENGL) + glBufferStorage(target, size, data, flags); +#endif +} + +/* + * + * Core in: + * OpenGL : 3.0 + * OpenGLES : 3.0 + */ +void rglFlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length) { +#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) && defined(HAVE_OPENGLES3) + glFlushMappedBufferRange(target, offset, length); +#endif +} + +/* + * + * Core in: + * OpenGL : 3.2 + * OpenGLES : 3.0 + */ +GLenum rglClientWaitSync(void *sync, GLbitfield flags, uint64_t timeout) +{ +#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) && defined(HAVE_OPENGLES3) + return glClientWaitSync((GLsync)sync, flags, (GLuint64)timeout); +#endif +} + +/* + * + * Core in: + * OpenGL : 3.2 + * OpenGLES : Not available + */ +void rglDrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, + GLvoid *indices, GLint basevertex) { +#if defined(HAVE_OPENGL) + glDrawElementsBaseVertex(mode, count, type, indices, basevertex); +#endif +} + /* GLSM-side */ static void glsm_state_setup(void) @@ -1870,7 +1948,10 @@ static void glsm_state_setup(void) #endif for (i = 0; i < MAX_ATTRIB; i++) + { gl_state.vertex_attrib_pointer.enabled[i] = 0; + gl_state.attrib_pointer.used[i] = 0; + } glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &glsm_max_textures); @@ -1917,6 +1998,17 @@ static void glsm_state_bind(void) glEnableVertexAttribArray(i); else glDisableVertexAttribArray(i); + + if (gl_state.attrib_pointer.used[i]) + { + glVertexAttribPointer( + i, + gl_state.attrib_pointer.size[i], + gl_state.attrib_pointer.type[i], + gl_state.attrib_pointer.normalized[i], + gl_state.attrib_pointer.stride[i], + gl_state.attrib_pointer.pointer[i]); + } } glBindFramebuffer(RARCH_GL_FRAMEBUFFER, hw_render.get_current_framebuffer()); @@ -2076,6 +2168,8 @@ static bool glsm_state_ctx_destroy(void *data) if (gl_state.bind_textures.ids) free(gl_state.bind_textures.ids); gl_state.bind_textures.ids = NULL; + + return true; } static bool glsm_state_ctx_init(void *data) diff --git a/libretro-common/include/compat/apple_compat.h b/libretro-common/include/compat/apple_compat.h index 431243e746..423dc30952 100644 --- a/libretro-common/include/compat/apple_compat.h +++ b/libretro-common/include/compat/apple_compat.h @@ -20,6 +20,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifndef __APPLE_COMPAT_H +#define __APPLE_COMPAT_H + #ifdef __APPLE__ #include #endif @@ -32,6 +35,32 @@ typedef unsigned NSUInteger; typedef float CGFloat; #endif +#ifndef __has_feature +/* Compatibility with non-Clang compilers. */ +#define __has_feature(x) 0 +#endif + +#ifndef CF_RETURNS_RETAINED +#if __has_feature(attribute_cf_returns_retained) +#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) +#else +#define CF_RETURNS_RETAINED +#endif +#endif + +#ifndef NS_INLINE +#define NS_INLINE inline +#endif + +NS_INLINE CF_RETURNS_RETAINED CFTypeRef CFBridgingRetainCompat(id X) +{ +#if __has_feature(objc_arc) + return (__bridge_retained CFTypeRef)X; +#else + return X; +#endif +} + #endif #ifdef IOS @@ -43,7 +72,13 @@ typedef float CGFloat; #import #import #import +#endif +#else + +#ifdef __OBJC__ #include #endif #endif + +#endif diff --git a/libretro-common/include/encodings/crc32.h b/libretro-common/include/encodings/crc32.h new file mode 100644 index 0000000000..b7dc00f95d --- /dev/null +++ b/libretro-common/include/encodings/crc32.h @@ -0,0 +1,31 @@ +/* Copyright (C) 2010-2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (crc32.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LIBRETRO_ENCODINGS_CRC32_H +#define _LIBRETRO_ENCODINGS_CRC32_H + +#include +#include + +uint32_t encoding_crc32(uint32_t crc, const uint8_t *buf, size_t len); + +#endif diff --git a/libretro-common/include/encodings/utf.h b/libretro-common/include/encodings/utf.h index 88585bc228..aabe113e2c 100644 --- a/libretro-common/include/encodings/utf.h +++ b/libretro-common/include/encodings/utf.h @@ -42,4 +42,6 @@ const char *utf8skip(const char *str, size_t chars); uint32_t utf8_walk(const char **string); +bool utf16_to_char_string(const uint16_t *in, char *s, size_t len); + #endif diff --git a/libretro-common/include/file/archive_file.h b/libretro-common/include/file/archive_file.h index 1700718acd..cfc4f077b6 100644 --- a/libretro-common/include/file/archive_file.h +++ b/libretro-common/include/file/archive_file.h @@ -25,26 +25,90 @@ #include #include - #include +#include + enum file_archive_transfer_type { - ZLIB_TRANSFER_NONE = 0, - ZLIB_TRANSFER_INIT, - ZLIB_TRANSFER_ITERATE, - ZLIB_TRANSFER_DEINIT, - ZLIB_TRANSFER_DEINIT_ERROR + ARCHIVE_TRANSFER_NONE = 0, + ARCHIVE_TRANSFER_INIT, + ARCHIVE_TRANSFER_ITERATE, + ARCHIVE_TRANSFER_DEINIT, + ARCHIVE_TRANSFER_DEINIT_ERROR }; typedef struct file_archive_handle { void *stream; - uint8_t *data; + uint8_t *data; uint32_t real_checksum; const struct file_archive_file_backend *backend; } file_archive_file_handle_t; +typedef struct file_archive_transfer +{ + void *handle; + void *stream; + const uint8_t *footer; + const uint8_t *directory; + const uint8_t *data; + int32_t archive_size; + enum file_archive_transfer_type type; + const struct file_archive_file_backend *backend; +} file_archive_transfer_t; + +enum file_archive_compression_mode +{ + ARCHIVE_MODE_UNCOMPRESSED = 0, + ARCHIVE_MODE_COMPRESSED = 8 +}; + +struct decomp_state_t +{ + char *opt_file; + char *needle; + void **buf; + size_t size; + bool found; +}; + +typedef struct +{ + char *source_file; + char *subdir; + char *target_dir; + char *target_file; + char *valid_ext; + + char *callback_error; + + file_archive_transfer_t archive; +} decompress_state_t; + +struct archive_extract_userdata +{ + char *archive_path; + char *first_extracted_file_path; + char *extracted_file_path; + const char *extraction_directory; + size_t archive_path_size; + struct string_list *ext; + struct string_list *list; + bool found_file; + bool list_only; + void *context; + char archive_name[PATH_MAX_LENGTH]; + uint32_t crc; + struct decomp_state_t decomp_state; + decompress_state_t *dec; +}; + +/* Returns true when parsing should continue. False to stop. */ +typedef int (*file_archive_file_cb)(const char *name, const char *valid_exts, + const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, + uint32_t crc32, struct archive_extract_userdata *userdata); + struct file_archive_file_backend { void *(*stream_new)(void); @@ -63,76 +127,87 @@ struct file_archive_file_backend void (*stream_compress_free)(void *); int (*stream_compress_data_to_file)(void *); uint32_t (*stream_crc_calculate)(uint32_t, const uint8_t *, size_t); + int (*compressed_file_read)(const char *path, const char *needle, void **buf, + const char *optional_outfile); + int (*archive_parse_file_init)( + file_archive_transfer_t *state, + const char *file); + int (*archive_parse_file_iterate_step)( + file_archive_transfer_t *state, + const char *valid_exts, + struct archive_extract_userdata *userdata, + file_archive_file_cb file_cb); const char *ident; }; -typedef struct file_archive_transfer -{ - void *handle; - const uint8_t *footer; - const uint8_t *directory; - const uint8_t *data; - int32_t zip_size; - enum file_archive_transfer_type type; - const struct file_archive_file_backend *backend; -} file_archive_transfer_t; - - -/* Returns true when parsing should continue. False to stop. */ -typedef int (*file_archive_file_cb)(const char *name, const char *valid_exts, - const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, - uint32_t crc32, void *userdata); - int file_archive_parse_file_iterate( file_archive_transfer_t *state, bool *returnerr, const char *file, const char *valid_exts, file_archive_file_cb file_cb, - void *userdata); + struct archive_extract_userdata *userdata); void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state); int file_archive_parse_file_progress(file_archive_transfer_t *state); /** - * file_archive_extract_first_content_file: + * file_archive_extract_file: * @zip_path : filename path to ZIP archive. * @zip_path_size : size of ZIP archive. - * @valid_exts : valid extensions for a content file. + * @valid_exts : valid extensions for a file. * @extraction_directory : the directory to extract temporary - * unzipped content to. + * unzipped file to. * - * Extract first content file from archive. + * Extract file from archive. If no file inside the archive is + * specified, the first file found will be used. * * Returns : true (1) on success, otherwise false (0). **/ -bool file_archive_extract_first_content_file(char *zip_path, size_t zip_path_size, +bool file_archive_extract_file(char *zip_path, size_t zip_path_size, const char *valid_exts, const char *extraction_dir, char *out_path, size_t len); /** * file_archive_get_file_list: * @path : filename path of archive - * @valid_exts : Valid extensions of archive to be parsed. + * @valid_exts : Valid extensions of archive to be parsed. * If NULL, allow all. * * Returns: string listing of files from archive on success, otherwise NULL. **/ -struct string_list *file_archive_get_file_list(const char *path, const char *valid_exts); +struct string_list* file_archive_get_file_list(const char *path, const char *valid_exts); bool file_archive_perform_mode(const char *name, const char *valid_exts, const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, - uint32_t crc32, void *userdata); - -struct string_list *compressed_file_list_new(const char *filename, - const char* ext); + uint32_t crc32, struct archive_extract_userdata *userdata); void file_archive_deflate_init(void *data, int level); -const struct file_archive_file_backend *file_archive_get_default_file_backend(void); +int file_archive_compressed_read( + const char* path, void **buf, + const char* optional_filename, ssize_t *length); + +struct string_list* file_archive_file_list_new(const char *path, + const char *ext); + +struct string_list* file_archive_filename_split(const char *path); + +const uint8_t* file_archive_data(void *handle); + +int file_archive_parse_file_init(file_archive_transfer_t *state, + const char *file); + +void file_archive_free(void *handle); + +const struct file_archive_file_backend* file_archive_get_zlib_file_backend(void); +const struct file_archive_file_backend* file_archive_get_7z_file_backend(void); + +const struct file_archive_file_backend* file_archive_get_file_backend(const char *path); extern const struct file_archive_file_backend zlib_backend; +extern const struct file_archive_file_backend sevenzip_backend; #endif diff --git a/libretro-common/include/file/config_file.h b/libretro-common/include/file/config_file.h index 344f0931c8..b13686cd95 100644 --- a/libretro-common/include/file/config_file.h +++ b/libretro-common/include/file/config_file.h @@ -158,6 +158,8 @@ bool config_file_write(config_file_t *conf, const char *path); * Does not close the file. */ void config_file_dump(config_file_t *conf, FILE *file); +bool config_file_exists(const char *path); + RETRO_END_DECLS #endif diff --git a/libretro-common/include/gfx/math/matrix_4x4.h b/libretro-common/include/gfx/math/matrix_4x4.h index 883dafbec6..aab05c2644 100644 --- a/libretro-common/include/gfx/math/matrix_4x4.h +++ b/libretro-common/include/gfx/math/matrix_4x4.h @@ -52,7 +52,7 @@ void matrix_4x4_multiply(math_matrix_4x4 *out, const math_matrix_4x4 *a, const m void matrix_4x4_scale(math_matrix_4x4 *out, float x, float y, float z); void matrix_4x4_translate(math_matrix_4x4 *out, float x, float y, float z); -void matrix_4x4_projection(math_matrix_4x4 *out, float znear, float zfar); +void matrix_4x4_projection(math_matrix_4x4 *out, float y_fov, float aspect, float znear, float zfar); #endif diff --git a/libretro-common/include/gfx/math/vector_4.h b/libretro-common/include/gfx/math/vector_4.h new file mode 100644 index 0000000000..494d8dcebd --- /dev/null +++ b/libretro-common/include/gfx/math/vector_4.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2010-2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (vector_4.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __LIBRETRO_SDK_GFX_MATH_VECTOR_4_H__ +#define __LIBRETRO_SDK_GFX_MATH_VECTOR_4_H__ + +#include + +typedef float vec4_t[4]; + +void vec4_add(float *dst, const float *src); + +void vec4_subtract(float *dst, const float *src); + +void vec4_scale(float *dst, const float scale); + +void vec4_copy(float *dst, const float *src); + +#endif + diff --git a/libretro-common/include/gfx/scaler/scaler.h b/libretro-common/include/gfx/scaler/scaler.h index 8a68f92af9..64a52e4479 100644 --- a/libretro-common/include/gfx/scaler/scaler.h +++ b/libretro-common/include/gfx/scaler/scaler.h @@ -32,8 +32,6 @@ RETRO_BEGIN_DECLS -#define FILTER_UNITY (1 << 14) - enum scaler_pix_fmt { SCALER_FMT_ARGB8888 = 0, diff --git a/libretro-common/include/glsm/glsmsym.h b/libretro-common/include/glsm/glsmsym.h index 5596117434..40caa27fcc 100644 --- a/libretro-common/include/glsm/glsmsym.h +++ b/libretro-common/include/glsm/glsmsym.h @@ -161,6 +161,11 @@ RETRO_BEGIN_DECLS #define glClearBufferfi rglClearBufferfi #define glWaitSync rglWaitSync #define glFenceSync rglFenceSync +#define glDeleteSync rglDeleteSync +#define glBufferStorage rglBufferStorage +#define glFlushMappedBufferRange rglFlushMappedBufferRange +#define glClientWaitSync rglClientWaitSync +#define glDrawElementsBaseVertex rglDrawElementsBaseVertex const GLubyte* rglGetStringi(GLenum name, GLuint index); void rglTexBuffer(GLenum target, GLenum internalFormat, GLuint buffer); @@ -396,7 +401,13 @@ void rglTexSubImage2D( GLenum target, const GLvoid * pixels); void rglDeleteVertexArrays(GLsizei n, const GLuint *arrays); void *rglFenceSync(GLenum condition, GLbitfield flags); +void rglDeleteSync(GLsync sync); void rglWaitSync(void *sync, GLbitfield flags, uint64_t timeout); +void rglBufferStorage(GLenum target, GLsizeiptr size, const GLvoid *data, GLbitfield flags); +void rglFlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length); +GLenum rglClientWaitSync(void *sync, GLbitfield flags, uint64_t timeout); +void rglDrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, + GLvoid *indices, GLint basevertex); RETRO_END_DECLS diff --git a/libretro-common/include/lists/string_list.h b/libretro-common/include/lists/string_list.h index 0079bd6d9b..386ceab9ad 100644 --- a/libretro-common/include/lists/string_list.h +++ b/libretro-common/include/lists/string_list.h @@ -138,7 +138,7 @@ void string_list_free(struct string_list *list); * @list : pointer to string list. * @delim : delimiter character for @list. * - * A string list will be joined/concatenated as a + * A string list will be joined/concatenated as a * string to @buffer, delimited by @delim. */ void string_list_join_concat(char *buffer, size_t size, diff --git a/libretro-common/include/retro_miscellaneous.h b/libretro-common/include/retro_miscellaneous.h index b133550bdc..37e2b368a6 100644 --- a/libretro-common/include/retro_miscellaneous.h +++ b/libretro-common/include/retro_miscellaneous.h @@ -33,7 +33,7 @@ #elif defined(GEKKO) || defined(__PSL1GHT__) || defined(__QNX__) #include #elif defined(PSP) -#include +#include #elif defined(VITA) #include #elif defined(_3DS) @@ -159,6 +159,18 @@ static INLINE float db_to_gain(float db) return powf(10.0f, db / 20.0f); } +static INLINE uint32_t read_le(const uint8_t *data, unsigned size) +{ + unsigned i; + uint32_t val = 0; + + size *= 8; + for (i = 0; i < size; i += 8) + val |= (uint32_t)*data++ << i; + + return val; +} + /* Helper macros and struct to keep track of many booleans. * To check for multiple bits, use &&, not &. * For OR, | can be used. */ diff --git a/libretro-common/lists/string_list.c b/libretro-common/lists/string_list.c index 2e82d22a14..ae65eb0e20 100644 --- a/libretro-common/lists/string_list.c +++ b/libretro-common/lists/string_list.c @@ -186,7 +186,7 @@ void string_list_set(struct string_list *list, * @list : pointer to string list. * @delim : delimiter character for @list. * - * A string list will be joined/concatenated as a + * A string list will be joined/concatenated as a * string to @buffer, delimited by @delim. */ void string_list_join_concat(char *buffer, size_t size, diff --git a/libretro-common/net/net_http.c b/libretro-common/net/net_http.c index ff7f851206..0f99dccf10 100644 --- a/libretro-common/net/net_http.c +++ b/libretro-common/net/net_http.c @@ -28,6 +28,7 @@ #include #include #include +#include enum { @@ -350,7 +351,7 @@ bool net_http_update(struct http_t *state, size_t* progress, size_t* total) state->len = strtol(state->data + strlen("Content-Length: "), NULL, 10); } - if (!strcmp(state->data, "Transfer-Encoding: chunked")) + if (string_is_equal(state->data, "Transfer-Encoding: chunked")) state->bodytype = T_CHUNK; /* TODO: save headers somewhere */ diff --git a/libretro-common/net/test/Makefile b/libretro-common/net/test/Makefile index 6578f5116d..f4ae977a6b 100644 --- a/libretro-common/net/test/Makefile +++ b/libretro-common/net/test/Makefile @@ -13,9 +13,10 @@ CFLAGS += -Wall -pedantic -std=gnu99 HTTP_TEST_C = \ $(LIBRETRO_COMM_DIR)/net/net_http.c \ - net_http_test.c \ $(LIBRETRO_COMM_DIR)/net/net_compat.c \ - $(LIBRETRO_COMM_DIR)/compat/compat_strl.c + $(LIBRETRO_COMM_DIR)/compat/compat_strl.c \ + $(LIBRETRO_COMM_DIR)/string/stdstring.c \ + net_http_test.c HTTP_TEST_OBJS := $(HTTP_TEST_C:.c=.o) diff --git a/libretro-db/Makefile b/libretro-db/Makefile index cb6412f5ea..52db777aeb 100644 --- a/libretro-db/Makefile +++ b/libretro-db/Makefile @@ -1,7 +1,7 @@ DEBUG = 0 LIBRETRODB_DIR := . -LIBRETRO_COMMON_DIR := ../libretro-common -INCFLAGS = -I. -I$(LIBRETRO_COMMON_DIR)/include +LIBRETRO_COMM_DIR := ../libretro-common +INCFLAGS = -I. -I$(LIBRETRO_COMM_DIR)/include TARGETS = rmsgpack_test libretrodb_tool c_converter @@ -12,7 +12,7 @@ CFLAGS = -g -O2 -Wall -DNDEBUG endif LIBRETRO_COMMON_C = \ - $(LIBRETRO_COMMON_DIR)/streams/file_stream.c + $(LIBRETRO_COMM_DIR)/streams/file_stream.c C_CONVERTER_C = \ $(LIBRETRODB_DIR)/rmsgpack.c \ @@ -21,10 +21,11 @@ C_CONVERTER_C = \ $(LIBRETRODB_DIR)/bintree.c \ $(LIBRETRODB_DIR)/query.c \ $(LIBRETRODB_DIR)/c_converter.c \ - $(LIBRETRO_COMMON_DIR)/hash/rhash.c \ - $(LIBRETRO_COMMON_DIR)/compat/compat_fnmatch.c \ + $(LIBRETRO_COMM_DIR)/hash/rhash.c \ + $(LIBRETRO_COMM_DIR)/compat/compat_fnmatch.c \ + $(LIBRETRO_COMM_DIR)/string/stdstring.c \ $(LIBRETRO_COMMON_C) \ - $(LIBRETRO_COMMON_DIR)/compat/compat_strl.c + $(LIBRETRO_COMM_DIR)/compat/compat_strl.c C_CONVERTER_OBJS := $(C_CONVERTER_C:.c=.o) @@ -35,9 +36,10 @@ RARCHDB_TOOL_C = \ $(LIBRETRODB_DIR)/bintree.c \ $(LIBRETRODB_DIR)/query.c \ $(LIBRETRODB_DIR)/libretrodb.c \ - $(LIBRETRO_COMMON_DIR)/compat/compat_fnmatch.c \ + $(LIBRETRO_COMM_DIR)/compat/compat_fnmatch.c \ + $(LIBRETRO_COMM_DIR)/string/stdstring.c \ $(LIBRETRO_COMMON_C) \ - $(LIBRETRO_COMMON_DIR)/compat/compat_strl.c + $(LIBRETRO_COMM_DIR)/compat/compat_strl.c RARCHDB_TOOL_OBJS := $(RARCHDB_TOOL_C:.c=.o) diff --git a/libretro-db/c_converter.c b/libretro-db/c_converter.c index 7284ef4e2b..7876e0d10d 100644 --- a/libretro-db/c_converter.c +++ b/libretro-db/c_converter.c @@ -31,6 +31,7 @@ #include #include +#include #include "libretrodb.h" @@ -341,13 +342,13 @@ static dat_converter_list_t* dat_parser_table( if (!map.key) { - if (!strcmp(current->token.label, ")")) + if (string_is_equal(current->token.label, ")")) { current++; *start_token = current; return parsed_table; } - else if (!strcmp(current->token.label, "(")) + else if (string_is_equal(current->token.label, "(")) { printf("%s:%d:%d: fatal error: Unexpected '(' instead of key\n", current->token.fname, @@ -363,14 +364,14 @@ static dat_converter_list_t* dat_parser_table( } else { - if (!strcmp(current->token.label, "(")) + if (string_is_equal(current->token.label, "(")) { current++; map.type = DAT_CONVERTER_LIST_MAP; map.value.list = dat_parser_table(¤t); dat_converter_list_append(parsed_table, &map); } - else if (!strcmp(current->token.label, ")")) + else if (string_is_equal(current->token.label, ")")) { printf("%s:%d:%d: fatal error: Unexpected ')' instead of value\n", current->token.fname, @@ -468,7 +469,7 @@ static const char* dat_converter_get_match( { if (list->values[i].map.hash == match_key->hash) { - retro_assert(!strcmp(list->values[i].map.key, match_key->value)); + retro_assert(string_is_equal(list->values[i].map.key, match_key->value)); if (match_key->next) return dat_converter_get_match( @@ -510,14 +511,14 @@ static dat_converter_list_t* dat_converter_parser( { if (!map.key) { - if (!strcmp(current->token.label, "game")) + if (string_is_equal(current->token.label, "game")) skip = false; map.key = current->token.label; current++; } else { - if (!strcmp(current->token.label, "(")) + if (string_is_equal(current->token.label, "(")) { current++; map.value.list = dat_parser_table(¤t); diff --git a/libretro-db/libretrodb_tool.c b/libretro-db/libretrodb_tool.c index b8c8f25c34..e8b5ed5f69 100644 --- a/libretro-db/libretrodb_tool.c +++ b/libretro-db/libretrodb_tool.c @@ -23,6 +23,8 @@ #include #include +#include + #include "libretrodb.h" #include "rmsgpack_dom.h" @@ -59,7 +61,7 @@ int main(int argc, char ** argv) printf("Could not open db file '%s': %s\n", path, strerror(-rv)); goto error; } - else if (!strcmp(command, "list")) + else if (string_is_equal(command, "list")) { if ((rv = libretrodb_cursor_open(db, cur, NULL)) != 0) { @@ -80,7 +82,7 @@ int main(int argc, char ** argv) rmsgpack_dom_value_free(&item); } } - else if (!strcmp(command, "find")) + else if (string_is_equal(command, "find")) { if (argc != 4) { @@ -111,7 +113,7 @@ int main(int argc, char ** argv) rmsgpack_dom_value_free(&item); } } - else if (!strcmp(command, "create-index")) + else if (string_is_equal(command, "create-index")) { const char * index_name, * field_name; diff --git a/libretro-db/rmsgpack.c b/libretro-db/rmsgpack.c index 2d31222a08..dfac0c448a 100644 --- a/libretro-db/rmsgpack.c +++ b/libretro-db/rmsgpack.c @@ -122,7 +122,7 @@ int rmsgpack_write_array_header(RFILE *fd, uint32_t size) if (filestream_write(fd, &MPF_ARRAY32, sizeof(MPF_ARRAY32)) == -1) goto error; - + tmp_i32 = swap_if_little32(size); if (filestream_write(fd, (void *)(&tmp_i32), sizeof(uint32_t)) == -1) @@ -247,7 +247,7 @@ int rmsgpack_write_bin(RFILE *fd, const void *s, uint32_t len) if (filestream_write(fd, &tmp_i32, sizeof(uint32_t)) == -1) goto error; } - + if (filestream_write(fd, s, len) == -1) goto error; @@ -482,6 +482,7 @@ static int read_buff(RFILE *fd, size_t size, char **pbuff, uint64_t *len) error: free(*pbuff); + *pbuff = NULL; return -errno; } diff --git a/managers/core_option_manager.c b/managers/core_option_manager.c index df6575adde..70ca896b96 100644 --- a/managers/core_option_manager.c +++ b/managers/core_option_manager.c @@ -247,7 +247,7 @@ bool core_option_manager_flush(core_option_manager_t *opt) * successfully saved to disk, otherwise false (0). **/ bool core_option_manager_flush_game_specific( - core_option_manager_t *opt, char* path) + core_option_manager_t *opt, const char* path) { size_t i; for (i = 0; i < opt->size; i++) diff --git a/managers/core_option_manager.h b/managers/core_option_manager.h index 2ee2db3354..46929df1f8 100644 --- a/managers/core_option_manager.h +++ b/managers/core_option_manager.h @@ -90,7 +90,7 @@ bool core_option_manager_flush(core_option_manager_t *opt); * successfully saved to disk, otherwise false (0). **/ bool core_option_manager_flush_game_specific( - core_option_manager_t *opt, char* path); + core_option_manager_t *opt, const char* path); /** * core_option_manager_free: diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 762858fd09..8215c06075 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -38,6 +38,7 @@ #include "../../managers/core_option_manager.h" #include "../../managers/cheat_manager.h" #include "../../performance_counters.h" +#include "../../paths.h" #include "../../runloop.h" #include "../../intl/intl.h" @@ -125,12 +126,11 @@ static void menu_action_setting_disp_set_label_configurations( const char *path, char *s2, size_t len2) { - global_t *global = global_get_ptr(); - *w = 19; strlcpy(s2, path, len2); - if (global && *global->path.config) - fill_pathname_base(s, global->path.config, + + if (!path_is_config_empty()) + fill_pathname_base(s, path_get_config(), len); else strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DIRECTORY_DEFAULT), len); @@ -1324,7 +1324,6 @@ static void menu_action_setting_disp_set_label_core_option_create( char *s2, size_t len2) { rarch_system_info_t *system = NULL; - global_t *global = global_get_ptr(); runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system); if (!system) @@ -1335,8 +1334,8 @@ static void menu_action_setting_disp_set_label_core_option_create( strlcpy(s, "", len); - if (!string_is_empty(global->name.base)) - strlcpy(s, path_basename(global->name.base), len); + if (!string_is_empty(path_get_basename())) + strlcpy(s, path_basename(path_get_basename()), len); strlcpy(s2, path, len2); } diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 4312602f51..299149ff05 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -30,6 +30,8 @@ #include "../menu_shader.h" #include "../menu_navigation.h" +#include "../widgets/menu_list.h" + #include "../../configuration.h" #include "../../core.h" #include "../../core_info.h" diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index a6acfe2f46..eb21afd318 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -31,7 +31,8 @@ #include "../menu_setting.h" #include "../menu_shader.h" #include "../menu_navigation.h" -#include "../widgets/menu_popup.h" +#include "../widgets/menu_dialog.h" +#include "../widgets/menu_input_dialog.h" #include "../menu_content.h" #include "../../core.h" @@ -42,6 +43,7 @@ #include "../../managers/cheat_manager.h" #include "../../tasks/tasks_internal.h" #include "../../input/input_remapping.h" +#include "../../paths.h" #include "../../retroarch.h" #include "../../runloop.h" #include "../../verbosity.h" @@ -245,7 +247,7 @@ int generic_action_ok_displaylist_push(const char *path, break; case ACTION_OK_DL_HELP: info_label = label; - menu_popup_push_pending(true, (enum menu_popup_type)type); + menu_dialog_push_pending(true, (enum menu_dialog_type)type); dl_type = DISPLAYLIST_HELP; break; case ACTION_OK_DL_RPL_ENTRY: @@ -1446,7 +1448,7 @@ static int action_ok_shader_pass_load(const char *path, static int generic_action_ok_help(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, - enum msg_hash_enums id, enum menu_popup_type id2) + enum msg_hash_enums id, enum menu_dialog_type id2) { const char *lbl = msg_hash_to_str(id); @@ -1461,7 +1463,21 @@ static int action_ok_cheevos(const char *path, return generic_action_ok_help(path, label, new_id, idx, entry_idx, MENU_ENUM_LABEL_CHEEVOS_DESCRIPTION, - MENU_POPUP_HELP_CHEEVOS_DESCRIPTION); + MENU_DIALOG_HELP_CHEEVOS_DESCRIPTION); +} + +static void menu_input_st_cheat_cb(void *userdata, const char *str) +{ + (void)userdata; + + if (str && *str) + { + unsigned cheat_index = menu_input_dialog_get_kb_type() + - MENU_SETTINGS_CHEAT_BEGIN; + cheat_manager_set_code(cheat_index, str); + } + + menu_input_dialog_end(); } static int action_ok_cheat(const char *path, @@ -1475,7 +1491,7 @@ static int action_ok_cheat(const char *path, line.idx = idx; line.cb = menu_input_st_cheat_cb; - if (!menu_input_ctl(MENU_INPUT_CTL_START_LINE, &line)) + if (!menu_input_dialog_start(&line)) return -1; return 0; } @@ -1485,11 +1501,9 @@ static void menu_input_st_string_cb_save_preset(void *userdata, { if (str && *str) { - rarch_setting_t *setting = NULL; - const char *label = NULL; - bool ret = false; - - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_LABEL_SETTING, &label); + rarch_setting_t *setting = NULL; + bool ret = false; + const char *label = menu_input_dialog_get_label_buffer(); if (!string_is_empty(label)) setting = menu_setting_find(label); @@ -1512,7 +1526,7 @@ static void menu_input_st_string_cb_save_preset(void *userdata, 1, 100, true); } - menu_input_key_end_line(); + menu_input_dialog_end(); } static int action_ok_shader_preset_save_as(const char *path, @@ -1526,7 +1540,7 @@ static int action_ok_shader_preset_save_as(const char *path, line.idx = idx; line.cb = menu_input_st_string_cb_save_preset; - if (!menu_input_ctl(MENU_INPUT_CTL_START_LINE, &line)) + if (!menu_input_dialog_start(&line)) return -1; return 0; } @@ -1576,8 +1590,7 @@ static int generic_action_ok_shader_preset_save(const char *path, break; case ACTION_OK_SHADER_PRESET_SAVE_GAME: { - global_t *global = global_get_ptr(); - const char *game_name = path_basename(global->name.base); + const char *game_name = path_basename(path_get_basename()); fill_pathname_join(file, directory, game_name, sizeof(file)); } break; @@ -1616,10 +1629,8 @@ static void menu_input_st_string_cb_cheat_file_save_as( { if (str && *str) { - rarch_setting_t *setting = NULL; - const char *label = NULL; - - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_LABEL_SETTING, &label); + rarch_setting_t *setting = NULL; + const char *label = menu_input_dialog_get_label_buffer(); if (!string_is_empty(label)) setting = menu_setting_find(label); @@ -1633,7 +1644,7 @@ static void menu_input_st_string_cb_cheat_file_save_as( cheat_manager_save(str); } - menu_input_key_end_line(); + menu_input_dialog_end(); } static int action_ok_cheat_file_save_as(const char *path, @@ -1647,7 +1658,7 @@ static int action_ok_cheat_file_save_as(const char *path, line.idx = idx; line.cb = menu_input_st_string_cb_cheat_file_save_as; - if (!menu_input_ctl(MENU_INPUT_CTL_START_LINE, &line)) + if (!menu_input_dialog_start(&line)) return -1; return 0; } @@ -1687,8 +1698,7 @@ static int generic_action_ok_remap_file_save(const char *path, break; case ACTION_OK_REMAP_FILE_SAVE_GAME: { - global_t *global = global_get_ptr(); - const char *game_name = path_basename(global->name.base); + const char *game_name = path_basename(path_get_basename()); fill_pathname_join(file, core_name, game_name, sizeof(file)); } break; @@ -2486,14 +2496,10 @@ static int action_ok_option_create(const char *path, if(config_file_write(conf, game_path)) { - global_t *global = global_get_ptr(); - runloop_msg_queue_push( msg_hash_to_str(MSG_CORE_OPTIONS_FILE_CREATED_SUCCESSFULLY), 1, 100, true); - - strlcpy(global->path.core_options_path, - game_path, sizeof(global->path.core_options_path)); + path_set_core_options(game_path); } config_file_free(conf); @@ -3150,35 +3156,35 @@ static int action_ok_help_audio_video_troubleshooting(const char *path, { return generic_action_ok_help(path, label, type, idx, entry_idx, MENU_ENUM_LABEL_HELP_AUDIO_VIDEO_TROUBLESHOOTING, - MENU_POPUP_HELP_AUDIO_VIDEO_TROUBLESHOOTING); + MENU_DIALOG_HELP_AUDIO_VIDEO_TROUBLESHOOTING); } static int action_ok_help(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return generic_action_ok_help(path, label, type, idx, entry_idx, - MENU_ENUM_LABEL_HELP, MENU_POPUP_WELCOME); + MENU_ENUM_LABEL_HELP, MENU_DIALOG_WELCOME); } static int action_ok_help_controls(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return generic_action_ok_help(path, label, type, idx, entry_idx, - MENU_ENUM_LABEL_HELP_CONTROLS, MENU_POPUP_HELP_CONTROLS); + MENU_ENUM_LABEL_HELP_CONTROLS, MENU_DIALOG_HELP_CONTROLS); } static int action_ok_help_what_is_a_core(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return generic_action_ok_help(path, label, type, idx, entry_idx, - MENU_ENUM_LABEL_HELP_WHAT_IS_A_CORE, MENU_POPUP_HELP_WHAT_IS_A_CORE); + MENU_ENUM_LABEL_HELP_WHAT_IS_A_CORE, MENU_DIALOG_HELP_WHAT_IS_A_CORE); } static int action_ok_help_scanning_content(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return generic_action_ok_help(path, label, type, idx, entry_idx, - MENU_ENUM_LABEL_HELP_SCANNING_CONTENT, MENU_POPUP_HELP_SCANNING_CONTENT); + MENU_ENUM_LABEL_HELP_SCANNING_CONTENT, MENU_DIALOG_HELP_SCANNING_CONTENT); } static int action_ok_help_change_virtual_gamepad(const char *path, @@ -3186,14 +3192,14 @@ static int action_ok_help_change_virtual_gamepad(const char *path, { return generic_action_ok_help(path, label, type, idx, entry_idx, MENU_ENUM_LABEL_HELP_CHANGE_VIRTUAL_GAMEPAD, - MENU_POPUP_HELP_CHANGE_VIRTUAL_GAMEPAD); + MENU_DIALOG_HELP_CHANGE_VIRTUAL_GAMEPAD); } static int action_ok_help_load_content(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { return generic_action_ok_help(path, label, type, idx, entry_idx, - MENU_ENUM_LABEL_HELP_LOADING_CONTENT, MENU_POPUP_HELP_LOADING_CONTENT); + MENU_ENUM_LABEL_HELP_LOADING_CONTENT, MENU_DIALOG_HELP_LOADING_CONTENT); } static int action_ok_video_resolution(const char *path, diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 47b252569a..1bf35e2d85 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -30,6 +30,8 @@ #include "../menu_shader.h" #include "../menu_navigation.h" +#include "../widgets/menu_list.h" + #include "../../configuration.h" #include "../../core.h" #include "../../core_info.h" diff --git a/menu/cbs/menu_cbs_scan.c b/menu/cbs/menu_cbs_scan.c index 844b4292ad..d47da9047f 100644 --- a/menu/cbs/menu_cbs_scan.c +++ b/menu/cbs/menu_cbs_scan.c @@ -22,7 +22,6 @@ #include "../menu_driver.h" #include "../menu_cbs.h" -#include "../menu_entry.h" #include "../menu_setting.h" #include "../../configuration.h" diff --git a/menu/cbs/menu_cbs_select.c b/menu/cbs/menu_cbs_select.c index bb85faa76f..39c06eff57 100644 --- a/menu/cbs/menu_cbs_select.c +++ b/menu/cbs/menu_cbs_select.c @@ -20,7 +20,7 @@ #endif #include "../menu_driver.h" -#include "../menu_entry.h" +#include "../widgets/menu_entry.h" #include "../menu_cbs.h" #include "../menu_setting.h" diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 795b50d56a..d1cae46ae0 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -34,6 +34,10 @@ #include "../../config.h" #endif +#ifndef HAVE_DYNAMIC +#include "../../frontend/frontend_driver.h" +#endif + #include "menu_generic.h" #include "../menu_driver.h" @@ -41,10 +45,11 @@ #include "../menu_navigation.h" #include "../menu_display.h" +#include "../widgets/menu_input_dialog.h" + #include "../../core_info.h" #include "../../core.h" #include "../../configuration.h" -#include "../../frontend/frontend_driver.h" #include "../../retroarch.h" #include "../../runloop.h" #include "../../verbosity.h" @@ -658,8 +663,8 @@ static void mui_render_menu_list(mui_handle_t *mui, int y; size_t selection; char rich_label[PATH_MAX_LENGTH] = {0}; + char entry_value[PATH_MAX_LENGTH] = {0}; bool entry_selected = false; - menu_entry_t entry = {{0}}; if (!menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection)) continue; @@ -670,7 +675,7 @@ static void mui_render_menu_list(mui_handle_t *mui, || ((y + (int)mui->line_height) < 0)) continue; - menu_entry_get(&entry, 0, i, NULL, true); + menu_entry_get_value(i, NULL, entry_value, sizeof(entry_value)); menu_entry_get_rich_label(i, rich_label, sizeof(rich_label)); entry_selected = selection == i; @@ -684,7 +689,7 @@ static void mui_render_menu_list(mui_handle_t *mui, entry_selected ? font_hover_color : font_normal_color, entry_selected, rich_label, - entry.value, + entry_value, menu_list_color ); } @@ -842,7 +847,6 @@ static void mui_frame(void *data) unsigned header_height = 0; size_t selection = 0; size_t title_margin = 0; - bool display_kb = false; mui_handle_t *mui = (mui_handle_t*)data; uint64_t *frame_count = video_driver_get_frame_count_ptr(); settings_t *settings = config_get_ptr(); @@ -1201,16 +1205,11 @@ static void mui_frame(void *data) mui_draw_scrollbar(mui, width, height, &grey_bg[0]); - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_DISPLAY, &display_kb); - - if (display_kb) + if (menu_input_dialog_get_display_kb()) { - const char *str = NULL, *label = NULL; - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_BUFF_PTR, &str); - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_LABEL, &label); + const char *str = menu_input_dialog_get_buffer(); + const char *label = menu_input_dialog_get_label_buffer(); - if (!str) - str = ""; mui_render_quad(mui, 0, 0, width, height, width, height, &black_bg[0]); snprintf(msg, sizeof(msg), "%s\n%s", label, str); mui_render_messagebox(mui, msg, &body_bg_color[0], font_hover_color); diff --git a/menu/drivers/menu_generic.c b/menu/drivers/menu_generic.c index 1dbd5a1fdc..a103664149 100644 --- a/menu/drivers/menu_generic.c +++ b/menu/drivers/menu_generic.c @@ -22,13 +22,9 @@ #include "../menu_driver.h" #include "../menu_display.h" -#include "../menu_displaylist.h" #include "../menu_navigation.h" -#include "../menu_entries.h" -#include "../widgets/menu_popup.h" - -#include "../../configuration.h" -#include "../../performance_counters.h" +#include "../widgets/menu_dialog.h" +#include "../widgets/menu_input_bind_dialog.h" #include "../../verbosity.h" #include "../../runloop.h" @@ -103,16 +99,16 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) switch (iterate_type) { case ITERATE_TYPE_HELP: - ret = menu_popup_iterate( + ret = menu_dialog_iterate( menu->menu_state.msg, sizeof(menu->menu_state.msg), label); BIT64_SET(menu->state, MENU_STATE_RENDER_MESSAGEBOX); BIT64_SET(menu->state, MENU_STATE_POST_ITERATE); if (ret == 1 || action == MENU_ACTION_OK) { BIT64_SET(menu->state, MENU_STATE_POP_STACK); - menu_popup_set_active(false); + menu_dialog_set_active(false); - if (menu_popup_get_current_type() == MENU_POPUP_QUIT_CONFIRM) + if (menu_dialog_get_current_type() == MENU_DIALOG_QUIT_CONFIRM) { runloop_set_quit_confirm(true); command_event(CMD_EVENT_QUIT_CONFIRM, NULL); @@ -122,9 +118,9 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) if (action == MENU_ACTION_CANCEL) { BIT64_SET(menu->state, MENU_STATE_POP_STACK); - menu_popup_set_active(false); + menu_dialog_set_active(false); - if (menu_popup_get_current_type() == MENU_POPUP_QUIT_CONFIRM) + if (menu_dialog_get_current_type() == MENU_DIALOG_QUIT_CONFIRM) { runloop_set_quit_confirm(false); @@ -141,7 +137,7 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) bind.s = menu->menu_state.msg; bind.len = sizeof(menu->menu_state.msg); - if (menu_input_ctl(MENU_INPUT_CTL_BIND_ITERATE, &bind)) + if (menu_input_key_bind_iterate(&bind)) { menu_entries_pop_stack(&selection, 0, 0); menu_navigation_ctl( @@ -234,7 +230,7 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) BIT64_SET(menu->state, MENU_STATE_POST_ITERATE); if (action == MENU_ACTION_OK || action == MENU_ACTION_CANCEL) BIT64_SET(menu->state, MENU_STATE_POP_STACK); - menu_popup_set_active(false); + menu_dialog_set_active(false); break; case ITERATE_TYPE_DEFAULT: /* FIXME: Crappy hack, needed for mouse controls @@ -253,7 +249,7 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) BIT64_SET(menu->state, MENU_STATE_POST_ITERATE); /* Have to defer it so we let settings refresh. */ - menu_popup_push(); + menu_dialog_push(); break; } diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index 4209a3bfa8..ae09d10b2a 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -42,6 +42,8 @@ #include "../menu_display.h" #include "../menu_navigation.h" +#include "../widgets/menu_input_dialog.h" + #include "../../configuration.h" #include "../../runloop.h" #include "../../gfx/drivers_font_renderer/bitmap.h" @@ -383,11 +385,11 @@ static void rgui_render(void *data) { menu_animation_ctx_ticker_t ticker; unsigned x, y; - bool display_kb, msg_force; uint16_t hover_color, normal_color; size_t i, end, fb_pitch, old_start; unsigned fb_width, fb_height; int bottom; + bool msg_force = false; uint64_t *frame_count = NULL; char title[256] = {0}; char title_buf[256] = {0}; @@ -580,7 +582,7 @@ static void rgui_render(void *data) entry_title_buf[0] = '\0'; type_str_buf[0] = '\0'; - menu_entry_get_value(i, entry_value, sizeof(entry_value)); + menu_entry_get_value(i, NULL, entry_value, sizeof(entry_value)); menu_entry_get_rich_label(i, entry_path, sizeof(entry_path)); ticker.s = entry_title_buf; @@ -609,17 +611,11 @@ static void rgui_render(void *data) entry_selected ? hover_color : normal_color); } - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_DISPLAY, &display_kb); - - if (display_kb) + if (menu_input_dialog_get_display_kb()) { - const char *str = NULL; - const char *label = NULL; - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_BUFF_PTR, &str); - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_LABEL, &label); + const char *str = menu_input_dialog_get_buffer(); + const char *label = menu_input_dialog_get_label_buffer(); - if (!str) - str = ""; snprintf(msg, sizeof(msg), "%s\n%s", label, str); rgui_render_messagebox(msg); } diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index da491d6b89..22438f8186 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -33,23 +33,26 @@ #include "../../config.h" #endif +#ifndef HAVE_DYNAMIC +#include "../../frontend/frontend_driver.h" +#endif + #include "menu_generic.h" #include "../menu_driver.h" -#include "../menu_entry.h" #include "../menu_animation.h" #include "../menu_display.h" -#include "../menu_display.h" #include "../menu_navigation.h" +#include "../widgets/menu_entry.h" +#include "../widgets/menu_list.h" +#include "../widgets/menu_input_dialog.h" + #include "../menu_cbs.h" -#include "../../frontend/frontend_driver.h" -#include "../../core.h" #include "../../verbosity.h" #include "../../configuration.h" #include "../../retroarch.h" -#include "../../file_path_special.h" #include "../../tasks/tasks_internal.h" @@ -92,6 +95,7 @@ enum XMB_TEXTURE_RESUME, XMB_TEXTURE_SAVESTATE, XMB_TEXTURE_LOADSTATE, + XMB_TEXTURE_UNDO, XMB_TEXTURE_CORE_INFO, XMB_TEXTURE_CORE_OPTIONS, XMB_TEXTURE_INPUT_REMAPPING_OPTIONS, @@ -750,6 +754,7 @@ static void xmb_update_thumbnail_path(void *data, unsigned i) settings->directory.thumbnails, xmb->title_name, sizeof(xmb->thumbnail_file_path)); + fill_pathname_join(xmb->thumbnail_file_path, xmb->thumbnail_file_path, xmb_thumbnails_ident(), sizeof(xmb->thumbnail_file_path)); @@ -757,8 +762,9 @@ static void xmb_update_thumbnail_path(void *data, unsigned i) if (tmp) { - fill_pathname_join(xmb->thumbnail_file_path, xmb->thumbnail_file_path, - tmp, sizeof(xmb->thumbnail_file_path)); + char tmp_new[PATH_MAX_LENGTH]; + fill_pathname_join(tmp_new, xmb->thumbnail_file_path, tmp, sizeof(tmp_new)); + strlcpy(xmb->thumbnail_file_path, tmp_new, sizeof(xmb->thumbnail_file_path)); free(tmp); } @@ -1623,6 +1629,9 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, return xmb->textures.list[XMB_TEXTURE_SAVESTATE]; case MENU_ENUM_LABEL_LOAD_STATE: return xmb->textures.list[XMB_TEXTURE_LOADSTATE]; + case MENU_ENUM_LABEL_UNDO_LOAD_STATE: + case MENU_ENUM_LABEL_UNDO_SAVE_STATE: + return xmb->textures.list[XMB_TEXTURE_UNDO]; case MENU_ENUM_LABEL_TAKE_SCREENSHOT: return xmb->textures.list[XMB_TEXTURE_SCREENSHOT]; case MENU_ENUM_LABEL_DELETE_ENTRY: @@ -1758,18 +1767,19 @@ static void xmb_draw_items(xmb_handle_t *xmb, for (; i < end; i++) { - menu_entry_t entry = {{0}}; float icon_x, icon_y; menu_animation_ctx_ticker_t ticker; - char ticker_str[PATH_MAX_LENGTH] = {0}; - char name[PATH_MAX_LENGTH] = {0}; - char value[PATH_MAX_LENGTH] = {0}; - const float half_size = xmb->icon.size / 2.0f; - uintptr_t texture_switch = 0; - xmb_node_t * node = (xmb_node_t*) + menu_entry_t entry = {{0}}; + char ticker_str[PATH_MAX_LENGTH] = {0}; + char name[PATH_MAX_LENGTH] = {0}; + char value[PATH_MAX_LENGTH] = {0}; + char entry_value[PATH_MAX_LENGTH] = {0}; + const float half_size = xmb->icon.size / 2.0f; + uintptr_t texture_switch = 0; + xmb_node_t * node = (xmb_node_t*) menu_entries_get_userdata_at_offset(list, i); - bool do_draw_text = false; - unsigned ticker_limit = 35; + bool do_draw_text = false; + unsigned ticker_limit = 35; if (!node) continue; @@ -1795,17 +1805,18 @@ static void xmb_draw_items(xmb_handle_t *xmb, fill_short_pathname_representation(entry.path, entry.path, sizeof(entry.path)); + menu_entry_get_value(i, list, entry_value, sizeof(entry_value)); - if (string_is_equal(entry.value, "disabled") || - string_is_equal(entry.value, "off")) + if (string_is_equal(entry_value, "disabled") || + string_is_equal(entry_value, "off")) { if (xmb->textures.list[XMB_TEXTURE_SWITCH_OFF]) texture_switch = xmb->textures.list[XMB_TEXTURE_SWITCH_OFF]; else do_draw_text = true; } - else if (string_is_equal(entry.value, "enabled") || - string_is_equal(entry.value, "on")) + else if (string_is_equal(entry_value, "enabled") || + string_is_equal(entry_value, "on")) { if (xmb->textures.list[XMB_TEXTURE_SWITCH_ON]) texture_switch = xmb->textures.list[XMB_TEXTURE_SWITCH_ON]; @@ -1814,7 +1825,7 @@ static void xmb_draw_items(xmb_handle_t *xmb, } else { - switch (msg_hash_to_file_type(msg_hash_calculate(entry.value))) + switch (msg_hash_to_file_type(msg_hash_calculate(entry_value))) { case FILE_TYPE_COMPRESSED: case FILE_TYPE_MORE: @@ -1845,7 +1856,7 @@ static void xmb_draw_items(xmb_handle_t *xmb, } } - if (string_is_empty(entry.value)) + if (string_is_empty(entry_value)) { if (!string_is_equal(xmb_thumbnails_ident(), "OFF") && xmb->thumbnail) ticker_limit = 40; @@ -1873,7 +1884,7 @@ static void xmb_draw_items(xmb_handle_t *xmb, ticker.s = value; ticker.len = 35; ticker.idx = *frame_count / 20; - ticker.str = entry.value; + ticker.str = entry_value; ticker.selected = (i == current); menu_animation_ctl(MENU_ANIMATION_CTL_TICKER, &ticker); @@ -2142,7 +2153,6 @@ static void xmb_frame(void *data) char msg[PATH_MAX_LENGTH] = {0}; char title_msg[256] = {0}; char title_truncated[256] = {0}; - bool display_kb = false; bool render_background = false; file_list_t *selection_buf = NULL; file_list_t *menu_stack = NULL; @@ -2355,17 +2365,11 @@ static void xmb_frame(void *data) menu_display_font_flush_block(); - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_DISPLAY, &display_kb); - - if (display_kb) + if (menu_input_dialog_get_display_kb()) { - const char *str = NULL; - const char *label = NULL; - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_BUFF_PTR, &str); - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_LABEL, &label); + const char *str = menu_input_dialog_get_buffer(); + const char *label = menu_input_dialog_get_label_buffer(); - if (!str) - str = ""; snprintf(msg, sizeof(msg), "%s\n%s", label, str); render_background = true; } @@ -2813,6 +2817,8 @@ static const char *xmb_texture_path(unsigned id) return "savestate.png"; case XMB_TEXTURE_LOADSTATE: return "loadstate.png"; + case XMB_TEXTURE_UNDO: + return "undo.png"; case XMB_TEXTURE_CORE_INFO: return "core-infos.png"; case XMB_TEXTURE_CORE_OPTIONS: diff --git a/menu/drivers/xui.cpp b/menu/drivers/xui.cpp index 50c3c7ee62..dad913825a 100644 --- a/menu/drivers/xui.cpp +++ b/menu/drivers/xui.cpp @@ -32,7 +32,7 @@ #include "../menu_driver.h" #include "../menu_animation.h" -#include "../menu_entry.h" +#include "../widgets/menu_entry.h" #include "../menu_entries.h" #include "../menu_input.h" #include "../menu_navigation.h" @@ -525,19 +525,14 @@ static void xui_set_list_text(int index, const wchar_t* leftText, static void xui_render(void *data) { - uint64_t *frame_count; - bool display_kb, msg_force; - unsigned fb_width; size_t end, i, selection; char title[PATH_MAX_LENGTH] = {0}; const char *dir = NULL; const char *label = NULL; unsigned menu_type = 0; - - frame_count = video_driver_get_frame_count_ptr(); - - fb_width = menu_display_get_width(); - msg_force = menu_display_get_msg_force(); + uint64_t *frame_count = video_driver_get_frame_count_ptr(); + unsigned fb_width = menu_display_get_width(); + bool msg_force = menu_display_get_msg_force(); if ( menu_entries_ctl(MENU_ENTRIES_CTL_NEEDS_REFRESH, NULL) @@ -577,28 +572,25 @@ static void xui_render(void *data) wchar_t msg_right[PATH_MAX_LENGTH] = {0}; wchar_t msg_left[PATH_MAX_LENGTH] = {0}; - menu_entry_get_value(i, entry_value, sizeof(entry_value)); + menu_entry_get_value(i, NULL, entry_value, sizeof(entry_value)); menu_entry_get_path(i, entry_path, sizeof(entry_path)); mbstowcs(msg_left, entry_path, sizeof(msg_left) / sizeof(wchar_t)); mbstowcs(msg_right, entry_value, sizeof(msg_right) / sizeof(wchar_t)); xui_set_list_text(i, msg_left, msg_right); } + if (!menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection)) return; + XuiListSetCurSelVisible(m_menulist, selection); - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_DISPLAY, &display_kb); - - if (display_kb) + if (menu_input_dialog_get_display_kb()) { - char msg[1024] = {0}; - const char *str = NULL, *label = NULL; - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_BUFF_PTR, &str); - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_LABEL, &label); + char msg[1024] = {0}; + const char *str = menu_input_dialog_get_buffer(); + const char *label = menu_input_dialog_get_label_buffer(); - if (!str) - str = ""; snprintf(msg, sizeof(msg), "%s\n%s", label, str); xui_render_messagebox(msg); } diff --git a/menu/drivers/zarch.c b/menu/drivers/zarch.c index 059e7fb714..6eff103d7b 100644 --- a/menu/drivers/zarch.c +++ b/menu/drivers/zarch.c @@ -41,7 +41,7 @@ #include "../menu_driver.h" #include "../menu_animation.h" -#include "../menu_entry.h" +#include "../widgets/menu_entry.h" #include "../menu_display.h" #include "../menu_navigation.h" #include "../../retroarch.h" @@ -166,43 +166,41 @@ struct zui_tabbed }; -static enum zarch_layout_type zarch_layout; +static enum zarch_layout_type zarch_layout = LAY_HOME; static float zarch_zui_strwidth(void *fb_buf, const char *text, float scale) { return font_driver_get_message_width(fb_buf, text, strlen(text), scale); } - static int16_t zarch_zui_input_state(zui_t *zui, enum zarch_zui_input_state state) { - switch (state) - { - case MENU_ZARCH_MOUSE_X: - return menu_input_mouse_state(MENU_MOUSE_X_AXIS); - case MENU_ZARCH_MOUSE_Y: - return menu_input_mouse_state(MENU_MOUSE_Y_AXIS); - case MENU_POINTER_ZARCH_X: - return menu_input_pointer_state(MENU_POINTER_X_AXIS); - case MENU_POINTER_ZARCH_Y: - return menu_input_pointer_state(MENU_POINTER_Y_AXIS); - case MENU_ZARCH_PRESSED: - if ( menu_input_mouse_state(MENU_MOUSE_LEFT_BUTTON) - || menu_input_pointer_state(MENU_POINTER_PRESSED)) - return 1; - if (zui->action == MENU_ACTION_OK) - return 1; - break; - } - - return 0; + switch (state) + { + case MENU_ZARCH_MOUSE_X: + return menu_input_mouse_state(MENU_MOUSE_X_AXIS); + case MENU_ZARCH_MOUSE_Y: + return menu_input_mouse_state(MENU_MOUSE_Y_AXIS); + case MENU_POINTER_ZARCH_X: + return menu_input_pointer_state(MENU_POINTER_X_AXIS); + case MENU_POINTER_ZARCH_Y: + return menu_input_pointer_state(MENU_POINTER_Y_AXIS); + case MENU_ZARCH_PRESSED: + if ( menu_input_mouse_state(MENU_MOUSE_LEFT_BUTTON) + || menu_input_pointer_state(MENU_POINTER_PRESSED)) + return 1; + if (zui->action == MENU_ACTION_OK) + return 1; + break; + } + + return 0; } static bool zarch_zui_check_button_down(zui_t *zui, unsigned id, int x1, int y1, int x2, int y2) { menu_input_ctx_hitbox_t hitbox; - bool result = false; hitbox.x1 = x1; hitbox.x2 = x2; @@ -215,11 +213,11 @@ static bool zarch_zui_check_button_down(zui_t *zui, if ( zui->item.hot == id && zarch_zui_input_state(zui, MENU_ZARCH_PRESSED)) { - result = true; zui->item.active = id; + return true; } - return result; + return false; } static bool zarch_zui_check_button_up(zui_t *zui, @@ -328,9 +326,7 @@ static bool zarch_zui_list_item(zui_t *zui, struct zui_tabbed *tab, int x1, int frame_count = video_driver_get_frame_count_ptr(); if (tab->active_id != tab->prev_id) - { tab->prev_id = tab->active_id; - } if (selected) { @@ -398,9 +394,7 @@ static bool zarch_zui_tab(zui_t *zui, struct zui_tabbed *tab, if (zui->item.active == id || tab->active_id == ~0U || !tab->inited) tab->active_id = id; else if (id > tab->active_id) - { tab->next_id = id; - } if (!tab->inited) tab->inited = true; @@ -518,15 +512,17 @@ static int zarch_zui_render_lay_root_recent(zui_t *zui, struct zui_tabbed *tabbe for (i = zui->recent_dlist_first; i < size; ++i) { - char rich_label[PATH_MAX_LENGTH] = {0}; - menu_entry_t entry = {{0}}; + char rich_label[PATH_MAX_LENGTH] = {0}; + char entry_value[PATH_MAX_LENGTH] = {0}; + menu_entry_t entry = {{0}}; menu_entry_get(&entry, 0, i, NULL, true); menu_entry_get_rich_label(i, rich_label, sizeof(rich_label)); + menu_entry_get_value(i, NULL, entry_value,sizeof(entry_value)); if (zarch_zui_list_item(zui, tabbed, 0, tabbed->tabline_size + j * ZUI_ITEM_SIZE_PX, - rich_label, i, entry.value, gamepad_index == (signed)i)) + rich_label, i, entry_value, gamepad_index == (signed)i)) { if (menu_entry_action(&entry, i, MENU_ACTION_OK)) return 1; @@ -850,9 +846,7 @@ static void zarch_frame(void *data) menu_display_ctx_coord_draw_t coord_draw; settings_t *settings = config_get_ptr(); zui_t *zui = (zui_t*)data; - video_coord_array_t *ca = NULL; - - ca = menu_display_get_coords_array(); + video_coord_array_t *ca = menu_display_get_coords_array(); if (!zui) return; @@ -1127,8 +1121,8 @@ static int zarch_iterate(void *data, void *userdata, enum menu_action action) static bool zarch_menu_init_list(void *data) { menu_displaylist_info_t info = {0}; - file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); - file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); strlcpy(info.label, msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB), sizeof(info.label)); diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index 16c9cfa33d..abd2423133 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -19,6 +19,13 @@ #include #include +#include + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +RETRO_BEGIN_DECLS enum { @@ -205,4 +212,6 @@ void menu_cbs_init(void *data, int menu_cbs_exit(void); +RETRO_END_DECLS + #endif diff --git a/menu/menu_content.c b/menu/menu_content.c index f207170646..311481a260 100644 --- a/menu/menu_content.c +++ b/menu/menu_content.c @@ -20,6 +20,10 @@ #include #include +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + #include "menu_content.h" #include "menu_driver.h" #include "menu_display.h" diff --git a/menu/menu_display.c b/menu/menu_display.c index 634c417200..b68f41c827 100644 --- a/menu/menu_display.c +++ b/menu/menu_display.c @@ -22,6 +22,14 @@ #include #include +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#ifdef HAVE_THREADS +#include "../gfx/video_thread_wrapper.h" +#endif + #include "../config.def.h" #include "../retroarch.h" #include "../configuration.h" @@ -35,9 +43,6 @@ #include "menu_animation.h" #include "menu_display.h" -#ifdef HAVE_THREADS -#include "../gfx/video_thread_wrapper.h" -#endif uintptr_t menu_display_white_texture; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 95f2549667..7f452b4d22 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -44,7 +44,8 @@ #include "menu_content.h" #include "menu_driver.h" #include "menu_navigation.h" -#include "widgets/menu_popup.h" +#include "widgets/menu_dialog.h" +#include "widgets/menu_list.h" #include "menu_cbs.h" #include "../configuration.h" @@ -52,6 +53,7 @@ #include "../defaults.h" #include "../managers/cheat_manager.h" #include "../managers/core_option_manager.h" +#include "../paths.h" #include "../retroarch.h" #include "../runloop.h" #include "../core.h" @@ -461,7 +463,7 @@ static uint64_t bytes_to_gb(uint64_t bytes) static int menu_displaylist_parse_system_info(menu_displaylist_info_t *info) { int controller; -#if defined(HAVE_OPENGL) || defined(HAVE_GLES) +#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) gfx_ctx_ident_t ident_info; #endif char tmp[PATH_MAX_LENGTH] = {0}; @@ -797,7 +799,7 @@ static int menu_displaylist_parse_system_info(menu_displaylist_info_t *info) } } -#if defined(HAVE_OPENGL) || defined(HAVE_GLES) +#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) video_context_driver_get_ident(&ident_info); tmp_string = ident_info.ident; @@ -2422,12 +2424,16 @@ loop: menu_settings_list_increment(&setting); } - if (count == 0 && add_empty_entry) - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), - msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), - MENU_ENUM_LABEL_NO_SETTINGS_FOUND, - 0, 0, 0); + if (count == 0) + { + if (add_empty_entry) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), + MENU_ENUM_LABEL_NO_SETTINGS_FOUND, + 0, 0, 0); + return -1; + } return 0; } @@ -3366,7 +3372,9 @@ static int menu_displaylist_parse_generic( filter_ext = true; if (path_is_compressed) - str_list = compressed_file_list_new(info->path, info->exts); + { + str_list = file_archive_file_list_new(info->path, info->exts); + } else str_list = dir_list_new(info->path, filter_ext ? info->exts : NULL, @@ -3480,7 +3488,7 @@ static int menu_displaylist_parse_generic( if (settings->multimedia.builtin_mediaplayer_enable || settings->multimedia.builtin_imageviewer_enable) { - switch (retroarch_path_is_media_type(path)) + switch (path_is_media_type(path)) { case RARCH_CONTENT_MOVIE: #ifdef HAVE_FFMPEG @@ -4182,7 +4190,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) case DISPLAYLIST_HELP: menu_entries_append_enum(info->list, info->path, info->label, MSG_UNKNOWN, info->type, info->directory_ptr, 0); - menu_popup_unset_pending_push(); + menu_dialog_unset_pending_push(); break; case DISPLAYLIST_SETTING_ENUM: { @@ -4624,67 +4632,106 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) info->need_push = true; break; case DISPLAYLIST_UPDATER_SETTINGS_LIST: - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_CORE_UPDATER_BUILDBOT_URL, - PARSE_ONLY_STRING, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_BUILDBOT_ASSETS_URL, - PARSE_ONLY_STRING, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_CORE_UPDATER_AUTO_EXTRACT_ARCHIVE, - PARSE_ONLY_BOOL, false); + { + unsigned count = 0; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_CORE_UPDATER_BUILDBOT_URL, + PARSE_ONLY_STRING, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_BUILDBOT_ASSETS_URL, + PARSE_ONLY_STRING, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_CORE_UPDATER_AUTO_EXTRACT_ARCHIVE, + PARSE_ONLY_BOOL, false) != -1) + count++; + + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), + MENU_ENUM_LABEL_NO_SETTINGS_FOUND, + 0, 0, 0); + } info->need_refresh = true; info->need_push = true; break; case DISPLAYLIST_NETWORK_SETTINGS_LIST: - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETPLAY_ENABLE, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETPLAY_IP_ADDRESS, - PARSE_ONLY_STRING, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETPLAY_MODE, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETPLAY_SPECTATOR_MODE_ENABLE, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETPLAY_DELAY_FRAMES, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETWORK_CMD_ENABLE, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETWORK_CMD_PORT, - PARSE_ONLY_UINT, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETWORK_REMOTE_ENABLE, - PARSE_ONLY_BOOL, false); - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETWORK_REMOTE_PORT, - PARSE_ONLY_UINT, false); - { unsigned user; + unsigned count = 0; + + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY_ENABLE, + PARSE_ONLY_BOOL, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT, + PARSE_ONLY_BOOL, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY_IP_ADDRESS, + PARSE_ONLY_STRING, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY_MODE, + PARSE_ONLY_BOOL, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY_SPECTATOR_MODE_ENABLE, + PARSE_ONLY_BOOL, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY_DELAY_FRAMES, + PARSE_ONLY_UINT, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES, + PARSE_ONLY_UINT, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT, + PARSE_ONLY_UINT, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETWORK_CMD_ENABLE, + PARSE_ONLY_BOOL, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETWORK_CMD_PORT, + PARSE_ONLY_UINT, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETWORK_REMOTE_ENABLE, + PARSE_ONLY_BOOL, false) != -1) + count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETWORK_REMOTE_PORT, + PARSE_ONLY_UINT, false) != -1) + count++; + for(user = 0; user < settings->input.max_users; user++) { - menu_displaylist_parse_settings_enum(menu, info, + if (menu_displaylist_parse_settings_enum(menu, info, (enum msg_hash_enums)(MENU_ENUM_LABEL_NETWORK_REMOTE_USER_1_ENABLE + user), - PARSE_ONLY_BOOL, false); + PARSE_ONLY_BOOL, false) != -1) + count++; } - } - menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_STDIN_CMD_ENABLE, - PARSE_ONLY_BOOL, false); + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_STDIN_CMD_ENABLE, + PARSE_ONLY_BOOL, false) != -1) + count++; + + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), + MENU_ENUM_LABEL_NO_SETTINGS_FOUND, + 0, 0, 0); + } info->need_refresh = true; info->need_push = true; diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index 452481a657..3359d8dfc5 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -19,6 +19,7 @@ #include #include +#include #include #include diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 75ed0853bd..fd5bb74dba 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -29,7 +29,8 @@ #include "menu_cbs.h" #include "menu_display.h" #include "menu_navigation.h" -#include "widgets/menu_popup.h" +#include "widgets/menu_dialog.h" +#include "widgets/menu_list.h" #include "menu_shader.h" #include "../config.def.h" @@ -130,7 +131,7 @@ const char *config_get_menu_driver_options(void) return char_list_new_special(STRING_LIST_MENU_DRIVERS, NULL); } -#ifdef HAVE_ZLIB +#ifdef HAVE_COMPRESSION static void bundle_decompressed(void *task_data, void *user_data, const char *err) { @@ -179,7 +180,7 @@ static bool menu_init(menu_handle_t *menu_data) if (settings->menu_show_start_screen) { - menu_popup_push_pending(true, MENU_POPUP_WELCOME); + menu_dialog_push_pending(true, MENU_DIALOG_WELCOME); settings->menu_show_start_screen = false; command_event(CMD_EVENT_MENU_SAVE_CURRENT_CONFIG, NULL); } @@ -188,15 +189,15 @@ static bool menu_init(menu_handle_t *menu_data) && !string_is_empty(settings->path.bundle_assets_src) && !string_is_empty(settings->path.bundle_assets_dst) #ifdef IOS - && menu_popup_is_push_pending() + && menu_dialog_is_push_pending() #else && (settings->bundle_assets_extract_version_current != settings->bundle_assets_extract_last_version) #endif ) { - menu_popup_push_pending(true, MENU_POPUP_HELP_EXTRACT); -#ifdef HAVE_ZLIB + menu_dialog_push_pending(true, MENU_DIALOG_HELP_EXTRACT); +#ifdef HAVE_COMPRESSION task_push_decompress(settings->path.bundle_assets_src, settings->path.bundle_assets_dst, NULL, settings->path.bundle_assets_dst_subdir, @@ -579,7 +580,7 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) core_info_deinit_list(); core_info_free_current_core(); - menu_popup_reset(); + menu_dialog_reset(); free(menu_driver_data); } diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 33f277156c..3b85383e12 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -26,6 +26,8 @@ #include #include +#include "widgets/menu_entry.h" +#include "menu_input.h" #include "menu_entries.h" #include "../gfx/video_shader_driver.h" @@ -284,20 +286,6 @@ typedef struct menu_ctx_load_image enum menu_image_type type; } menu_ctx_load_image_t; -typedef struct menu_ctx_list -{ - file_list_t *list; - size_t list_size; - const char *path; - const char *label; - size_t idx; - enum menu_list_type type; - unsigned action; - size_t selection; - size_t size; - void *entry; -} menu_ctx_list_t; - typedef struct menu_ctx_displaylist { menu_displaylist_info_t *info; diff --git a/menu/menu_entries.c b/menu/menu_entries.c index a44d7c1a69..30d05b6cba 100644 --- a/menu/menu_entries.c +++ b/menu/menu_entries.c @@ -24,107 +24,13 @@ #include "menu_cbs.h" #include "menu_navigation.h" +#include "widgets/menu_list.h" + #include "../core.h" #include "../configuration.h" #include "../runloop.h" #include "../version.h" -struct menu_list -{ - file_list_t **menu_stack; - size_t menu_stack_size; - file_list_t **selection_buf; - size_t selection_buf_size; -}; - -static void menu_list_free_list(file_list_t *list) -{ - unsigned i; - - for (i = 0; i < list->size; i++) - { - menu_ctx_list_t list_info; - - list_info.list = list; - list_info.idx = i; - list_info.list_size = list->size; - - menu_driver_ctl(RARCH_MENU_CTL_LIST_FREE, &list_info); - } - - file_list_free(list); -} - -static void menu_list_free(menu_list_t *menu_list) -{ - unsigned i; - if (!menu_list) - return; - - for (i = 0; i < menu_list->menu_stack_size; i++) - { - if (!menu_list->menu_stack[i]) - continue; - - menu_list_free_list(menu_list->menu_stack[i]); - menu_list->menu_stack[i] = NULL; - } - for (i = 0; i < menu_list->selection_buf_size; i++) - { - if (!menu_list->selection_buf[i]) - continue; - - menu_list_free_list(menu_list->selection_buf[i]); - menu_list->selection_buf[i] = NULL; - } - - free(menu_list->menu_stack); - free(menu_list->selection_buf); - - free(menu_list); -} - -static menu_list_t *menu_list_new(void) -{ - unsigned i; - menu_list_t *list = (menu_list_t*)calloc(1, sizeof(*list)); - - if (!list) - return NULL; - - list->menu_stack = (file_list_t**)calloc(1, sizeof(*list->menu_stack)); - - if (!list->menu_stack) - goto error; - - list->selection_buf = (file_list_t**)calloc(1, sizeof(*list->selection_buf)); - - if (!list->selection_buf) - goto error; - - list->menu_stack_size = 1; - list->selection_buf_size = 1; - - for (i = 0; i < list->menu_stack_size; i++) - list->menu_stack[i] = (file_list_t*)calloc(1, sizeof(*list->menu_stack[i])); - - for (i = 0; i < list->selection_buf_size; i++) - list->selection_buf[i] = (file_list_t*)calloc(1, sizeof(*list->selection_buf[i])); - - return list; - -error: - menu_list_free(list); - return NULL; -} - -static size_t menu_list_get_stack_size(menu_list_t *list, size_t idx) -{ - if (!list) - return 0; - return file_list_get_size(list->menu_stack[idx]); -} - void menu_entries_get_at_offset(const file_list_t *list, size_t idx, const char **path, const char **label, unsigned *file_type, size_t *entry_idx, const char **alt) @@ -157,85 +63,6 @@ menu_file_list_cbs_t *menu_entries_get_actiondata_at_offset( file_list_get_actiondata_at_offset(list, idx); } -static int menu_entries_flush_stack_type(const char *needle, const char *label, - unsigned type, unsigned final_type) -{ - return needle ? strcmp(needle, label) : (type != final_type); -} - -static bool menu_list_pop_stack(menu_list_t *list, - size_t idx, size_t *directory_ptr, bool animate) -{ - menu_ctx_list_t list_info; - bool refresh = false; - file_list_t *menu_list = NULL; - if (!list) - return false; - - menu_list = list->menu_stack[idx]; - - if (menu_list_get_stack_size(list, idx) <= 1) - return false; - - list_info.type = MENU_LIST_PLAIN; - list_info.action = 0; - - if (animate) - menu_driver_ctl(RARCH_MENU_CTL_LIST_CACHE, &list_info); - - if (menu_list->size != 0) - { - menu_ctx_list_t list_info; - - list_info.list = menu_list; - list_info.idx = menu_list->size - 1; - list_info.list_size = menu_list->size - 1; - - menu_driver_ctl(RARCH_MENU_CTL_LIST_FREE, &list_info); - } - - file_list_pop(menu_list, directory_ptr); - menu_driver_ctl(RARCH_MENU_CTL_LIST_SET_SELECTION, menu_list); - if (animate) - menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); - - return true; -} - -static void menu_list_flush_stack(menu_list_t *list, - size_t idx, const char *needle, unsigned final_type) -{ - bool refresh = false; - const char *path = NULL; - const char *label = NULL; - unsigned type = 0; - size_t entry_idx = 0; - if (!list) - return; - - menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); - menu_entries_get_last(list->menu_stack[idx], - &path, &label, &type, &entry_idx); - - while (menu_entries_flush_stack_type( - needle, label, type, final_type) != 0) - { - size_t new_selection_ptr; - - menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, - &new_selection_ptr); - - if (!menu_list_pop_stack(list, idx, &new_selection_ptr, 1)) - break; - - menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, - &new_selection_ptr); - - menu_entries_get_last(list->menu_stack[idx], - &path, &label, &type, &entry_idx); - } -} - static bool menu_entries_clear(file_list_t *list) { unsigned i; @@ -385,14 +212,15 @@ size_t menu_entries_get_end(void) } /* Get an entry from the top of the menu stack */ -void menu_entries_get(size_t i, menu_entry_t *entry) +void menu_entries_get(size_t i, void *entry_data) { const char *label = NULL; const char *path = NULL; const char *entry_label = NULL; menu_file_list_cbs_t *cbs = NULL; enum msg_hash_enums enum_idx = MSG_UNKNOWN; - file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + menu_entry_t *entry = (menu_entry_t*)entry_data; + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); menu_entries_get_last_stack(NULL, &label, NULL, &enum_idx, NULL); @@ -479,7 +307,7 @@ file_list_t *menu_entries_get_menu_stack_ptr(size_t idx) menu_entries_ctl(MENU_ENTRIES_CTL_LIST_GET, &menu_list); if (!menu_list) return NULL; - return menu_list->menu_stack[idx]; + return menu_list_get(menu_list, idx); } file_list_t *menu_entries_get_selection_buf_ptr(size_t idx) @@ -488,7 +316,7 @@ file_list_t *menu_entries_get_selection_buf_ptr(size_t idx) menu_entries_ctl(MENU_ENTRIES_CTL_LIST_GET, &menu_list); if (!menu_list) return NULL; - return menu_list->selection_buf[idx]; + return menu_list_get_selection(menu_list, idx); } static bool menu_entries_init(void) @@ -622,7 +450,7 @@ menu_file_list_cbs_t *menu_entries_get_last_stack_actiondata(void) if (!menu_list) return NULL; return (menu_file_list_cbs_t*)file_list_get_last_actiondata( - menu_list->menu_stack[0]); + menu_list_get(menu_list, 0)); } void menu_entries_get_last_stack(const char **path, const char **label, @@ -634,7 +462,7 @@ void menu_entries_get_last_stack(const char **path, const char **label, if (!menu_list) return; - menu_entries_get_last(menu_list->menu_stack[0], + menu_entries_get_last(menu_list_get(menu_list, 0), path, label, file_type, entry_idx); cbs = menu_entries_get_last_stack_actiondata(); if (cbs) @@ -672,7 +500,7 @@ size_t menu_entries_get_size(void) menu_entries_ctl(MENU_ENTRIES_CTL_LIST_GET, &menu_list); if (!menu_list) return 0; - return file_list_get_size(menu_list->selection_buf[0]); + return file_list_get_size(menu_list_get_selection(menu_list, 0)); } rarch_setting_t *menu_entries_get_setting(uint32_t i) diff --git a/menu/menu_entries.h b/menu/menu_entries.h index d1a4a1cb5b..7a0832c619 100644 --- a/menu/menu_entries.h +++ b/menu/menu_entries.h @@ -22,19 +22,13 @@ #include #include +#include "widgets/menu_list.h" + #include "menu_setting.h" -#include "menu_entry.h" #include "menu_displaylist.h" RETRO_BEGIN_DECLS -enum menu_list_type -{ - MENU_LIST_PLAIN = 0, - MENU_LIST_HORIZONTAL, - MENU_LIST_TABS -}; - enum menu_entries_ctl_state { MENU_ENTRIES_CTL_NONE = 0, @@ -130,12 +124,9 @@ typedef struct menu_file_list_cbs } menu_file_list_cbs_t; - -typedef struct menu_list menu_list_t; - size_t menu_entries_get_end(void); -void menu_entries_get(size_t i, menu_entry_t *entry); +void menu_entries_get(size_t i, void *data_entry); int menu_entries_get_title(char *title, size_t title_len); diff --git a/menu/menu_event.c b/menu/menu_event.c new file mode 100644 index 0000000000..3accf3efc7 --- /dev/null +++ b/menu/menu_event.c @@ -0,0 +1,232 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#include "widgets/menu_entry.h" +#include "widgets/menu_input_dialog.h" + +#include "menu_event.h" + +#include "menu_driver.h" +#include "menu_input.h" +#include "menu_animation.h" +#include "menu_display.h" +#include "menu_navigation.h" + +#include "../configuration.h" + +static int menu_event_pointer(unsigned *action) +{ + const struct retro_keybind *binds[MAX_USERS] = {NULL}; + menu_input_t *menu_input = menu_input_get_ptr(); + unsigned fb_width = menu_display_get_width(); + unsigned fb_height = menu_display_get_height(); + int pointer_device = + menu_driver_ctl(RARCH_MENU_CTL_IS_SET_TEXTURE, NULL) ? + RETRO_DEVICE_POINTER : RARCH_DEVICE_POINTER_SCREEN; + int pointer_x = + input_driver_state(binds, 0, pointer_device, + 0, RETRO_DEVICE_ID_POINTER_X); + int pointer_y = + input_driver_state(binds, 0, pointer_device, + 0, RETRO_DEVICE_ID_POINTER_Y); + + menu_input->pointer.pressed[0] = input_driver_state(binds, + 0, pointer_device, + 0, RETRO_DEVICE_ID_POINTER_PRESSED); + menu_input->pointer.pressed[1] = input_driver_state(binds, + 0, pointer_device, + 1, RETRO_DEVICE_ID_POINTER_PRESSED); + menu_input->pointer.back = input_driver_state(binds, 0, pointer_device, + 0, RARCH_DEVICE_ID_POINTER_BACK); + + menu_input->pointer.x = ((pointer_x + 0x7fff) * (int)fb_width) / 0xFFFF; + menu_input->pointer.y = ((pointer_y + 0x7fff) * (int)fb_height) / 0xFFFF; + + return 0; +} + +unsigned menu_event(retro_input_t input, + retro_input_t trigger_input) +{ + menu_animation_ctx_delta_t delta; + float delta_time; + /* Used for key repeat */ + static float delay_timer = 0.0f; + static float delay_count = 0.0f; + unsigned ret = MENU_ACTION_NOOP; + static bool initial_held = true; + static bool first_held = false; + bool set_scroll = false; + bool mouse_enabled = false; + size_t new_scroll_accel = 0; + menu_input_t *menu_input = NULL; + settings_t *settings = config_get_ptr(); + + if (input.state) + { + if (!first_held) + { + /* don't run anything first frame, only capture held inputs + * for old_input_state. */ + + first_held = true; + delay_timer = initial_held ? 12 : 6; + delay_count = 0; + } + + if (delay_count >= delay_timer) + { + retro_input_t input_repeat = {0}; + BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_UP); + BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_DOWN); + BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_LEFT); + BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_RIGHT); + BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_B); + BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_A); + BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_L); + BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_R); + + set_scroll = true; + first_held = false; + trigger_input.state |= input.state & input_repeat.state; + + menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SCROLL_ACCEL, + &new_scroll_accel); + + new_scroll_accel = MIN(new_scroll_accel + 1, 64); + } + + initial_held = false; + } + else + { + set_scroll = true; + first_held = false; + initial_held = true; + } + + if (set_scroll) + menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SCROLL_ACCEL, + &new_scroll_accel); + + menu_animation_ctl(MENU_ANIMATION_CTL_DELTA_TIME, &delta_time); + + delta.current = delta_time; + + if (menu_animation_ctl(MENU_ANIMATION_CTL_IDEAL_DELTA_TIME_GET, &delta)) + delay_count += delta.ideal; + + if (menu_input_dialog_get_display_kb()) + { + static unsigned ti_char = 64; + static bool ti_next = false; + + if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_DOWN)) + { + if (ti_char > 32) + ti_char--; + if (! ti_next) + input_keyboard_event(true, '\x7f', '\x7f', 0, RETRO_DEVICE_KEYBOARD); + input_keyboard_event(true, ti_char, ti_char, 0, RETRO_DEVICE_KEYBOARD); + ti_next = false; + } + + if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_UP)) + { + if (ti_char < 125) + ti_char++; + if (! ti_next) + input_keyboard_event(true, '\x7f', '\x7f', 0, RETRO_DEVICE_KEYBOARD); + input_keyboard_event(true, ti_char, ti_char, 0, RETRO_DEVICE_KEYBOARD); + ti_next = false; + } + + if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_A)) + { + ti_char = 64; + ti_next = true; + } + + if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_B)) + { + input_keyboard_event(true, '\x7f', '\x7f', 0, RETRO_DEVICE_KEYBOARD); + ti_char = 64; + ti_next = false; + } + + /* send return key to close keyboard input window */ + if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_START)) + input_keyboard_event(true, '\n', '\n', 0, RETRO_DEVICE_KEYBOARD); + + trigger_input.state = 0; + } + + if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_UP)) + ret = MENU_ACTION_UP; + else if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_DOWN)) + ret = MENU_ACTION_DOWN; + else if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_LEFT)) + ret = MENU_ACTION_LEFT; + else if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_RIGHT)) + ret = MENU_ACTION_RIGHT; + else if (trigger_input.state & (UINT64_C(1) << settings->menu_scroll_up_btn)) + ret = MENU_ACTION_SCROLL_UP; + else if (trigger_input.state & (UINT64_C(1) << settings->menu_scroll_down_btn)) + ret = MENU_ACTION_SCROLL_DOWN; + else if (trigger_input.state & (UINT64_C(1) << settings->menu_cancel_btn)) + ret = MENU_ACTION_CANCEL; + else if (trigger_input.state & (UINT64_C(1) << settings->menu_ok_btn)) + ret = MENU_ACTION_OK; + else if (trigger_input.state & (UINT64_C(1) << settings->menu_search_btn)) + ret = MENU_ACTION_SEARCH; + else if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_Y)) + ret = MENU_ACTION_SCAN; + else if (trigger_input.state & (UINT64_C(1) << settings->menu_default_btn)) + ret = MENU_ACTION_START; + else if (trigger_input.state & (UINT64_C(1) << settings->menu_info_btn)) + ret = MENU_ACTION_INFO; + else if (trigger_input.state & (UINT64_C(1) << RARCH_MENU_TOGGLE)) + ret = MENU_ACTION_TOGGLE; + + mouse_enabled = settings->menu.mouse.enable; +#ifdef HAVE_OVERLAY + if (!mouse_enabled) + mouse_enabled = !(settings->input.overlay_enable + && input_overlay_is_alive(NULL)); +#endif + + if (!(menu_input = menu_input_get_ptr())) + return 0; + + if (!mouse_enabled) + menu_input->mouse.ptr = 0; + + if (settings->menu.pointer.enable) + menu_event_pointer(&ret); + else + memset(&menu_input->pointer, 0, sizeof(menu_input->pointer)); + + return ret; +} diff --git a/menu/menu_event.h b/menu/menu_event.h new file mode 100644 index 0000000000..1cef678b44 --- /dev/null +++ b/menu/menu_event.h @@ -0,0 +1,35 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef _MENU_EVENT_H +#define _MENU_EVENT_H + +#include + +RETRO_BEGIN_DECLS + +/* Send input code to menu for one frame. + * + * TODO/FIXME - needs to be overhauled so we can send multiple + * events per frame if we want to, and we shouldn't send the + * entire button state either but do a separate event per button + * state. + */ +unsigned menu_event(retro_input_t input, retro_input_t trigger_state); + +RETRO_END_DECLS + +#endif diff --git a/menu/menu_input.c b/menu/menu_input.c index 15b1d53cd1..6b7f834f1d 100644 --- a/menu/menu_input.c +++ b/menu/menu_input.c @@ -14,43 +14,26 @@ * If not, see . */ -#ifdef HAVE_CONFIG_H -#include "../../config.h" -#endif - -#define MENU_MAX_BUTTONS 219 -#define MENU_MAX_AXES 32 -#define MENU_MAX_HATS 4 - #include #include #include #include -#include -#include -#include - #ifdef HAVE_CONFIG_H #include "../config.h" #endif +#include "widgets/menu_entry.h" +#include "widgets/menu_input_dialog.h" +#include "widgets/menu_input_bind_dialog.h" + #include "menu_driver.h" #include "menu_input.h" #include "menu_animation.h" #include "menu_display.h" -#include "menu_entry.h" -#include "menu_setting.h" -#include "menu_shader.h" #include "menu_navigation.h" -#include "../managers/cheat_manager.h" -#include "../performance_counters.h" #include "../configuration.h" -#include "../core.h" -#include "../input/input_joypad_driver.h" -#include "../input/input_remapping.h" -#include "../input/input_config.h" enum menu_mouse_action { @@ -65,555 +48,12 @@ enum menu_mouse_action MENU_MOUSE_ACTION_HORIZ_WHEEL_DOWN }; -struct menu_bind_state_port -{ - bool buttons[MENU_MAX_BUTTONS]; - int16_t axes[MENU_MAX_AXES]; - uint16_t hats[MENU_MAX_HATS]; -}; - -struct menu_bind_axis_state -{ - /* Default axis state. */ - int16_t rested_axes[MENU_MAX_AXES]; - /* Locked axis state. If we configured an axis, - * avoid having the same axis state trigger something again right away. */ - int16_t locked_axes[MENU_MAX_AXES]; -}; - -struct menu_bind_state -{ - struct retro_keybind *target; - /* For keyboard binding. */ - int64_t timeout_end; - unsigned begin; - unsigned last; - unsigned user; - struct menu_bind_state_port state[MAX_USERS]; - struct menu_bind_axis_state axis_state[MAX_USERS]; - bool skip; -}; - -typedef struct menu_input -{ - struct menu_bind_state binds; - - struct - { - unsigned ptr; - } mouse; - - struct - { - int16_t x; - int16_t y; - int16_t dx; - int16_t dy; - float accel; - bool pressed[2]; - bool back; - unsigned ptr; - } pointer; - - struct - { - bool display; - unsigned type; - unsigned idx; - } keyboard; - - /* Used for key repeat */ - struct - { - float timer; - float count; - } delay; -} menu_input_t; - -static unsigned bind_port; - -static menu_input_t *menu_input_get_ptr(void) +menu_input_t *menu_input_get_ptr(void) { static menu_input_t menu_input_state; return &menu_input_state; } -void menu_input_key_end_line(void) -{ - bool keyboard_display = false; - - menu_input_ctl(MENU_INPUT_CTL_SET_KEYBOARD_DISPLAY, &keyboard_display); - menu_input_ctl(MENU_INPUT_CTL_UNSET_KEYBOARD_LABEL, NULL); - menu_input_ctl(MENU_INPUT_CTL_UNSET_KEYBOARD_LABEL_SETTING, NULL); - - /* Avoid triggering states on pressing return. */ - input_driver_set_flushing_input(); -} - -static void menu_input_search_cb(void *userdata, const char *str) -{ - size_t idx = 0; - file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); - - if (!selection_buf) - return; - - if (str && *str && file_list_search(selection_buf, str, &idx)) - { - bool scroll = true; - menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &idx); - menu_navigation_ctl(MENU_NAVIGATION_CTL_SET, &scroll); - } - - menu_input_key_end_line(); -} - -void menu_input_st_uint_cb(void *userdata, const char *str) -{ - if (str && *str) - { - rarch_setting_t *setting = NULL; - const char *label = NULL; - - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_LABEL_SETTING, &label); - - setting = menu_setting_find(label); - setting_set_with_string_representation(setting, str); - } - - menu_input_key_end_line(); -} - -void menu_input_st_hex_cb(void *userdata, const char *str) -{ - if (str && *str) - { - rarch_setting_t *setting = NULL; - const char *label = NULL; - - menu_input_ctl(MENU_INPUT_CTL_KEYBOARD_LABEL_SETTING, &label); - - setting = menu_setting_find(label); - - if (setting) - { - unsigned *ptr = (unsigned*)setting_get_ptr(setting); - if (str[0] == '#') - str++; - if (ptr) - *ptr = strtoul(str, NULL, 16); - } - } - - menu_input_key_end_line(); -} - -void menu_input_st_cheat_cb(void *userdata, const char *str) -{ - menu_input_t *menu_input = menu_input_get_ptr(); - - (void)userdata; - - if (!menu_input) - return; - - if (str && *str) - { - unsigned cheat_index = - menu_input->keyboard.type - MENU_SETTINGS_CHEAT_BEGIN; - cheat_manager_set_code(cheat_index, str); - } - - menu_input_key_end_line(); -} - -static bool menu_input_key_bind_custom_bind_keyboard_cb( - void *data, unsigned code) -{ - menu_input_t *menu_input = menu_input_get_ptr(); - settings_t *settings = config_get_ptr(); - - if (!menu_input) - return false; - - menu_input->binds.target->key = (enum retro_key)code; - menu_input->binds.begin++; - menu_input->binds.target++; - menu_input->binds.timeout_end = cpu_features_get_time_usec() + - settings->input.bind_timeout * 1000000; - - return (menu_input->binds.begin <= menu_input->binds.last); -} - -static int menu_input_key_bind_set_mode_common( - enum menu_input_ctl_state state, - rarch_setting_t *setting) -{ - size_t selection; - unsigned index_offset, bind_type; - menu_displaylist_info_t info = {0}; - struct retro_keybind *keybind = NULL; - file_list_t *menu_stack = NULL; - settings_t *settings = config_get_ptr(); - menu_input_t *menu_input = menu_input_get_ptr(); - - if (!setting) - return -1; - - index_offset = setting_get_index_offset(setting); - menu_stack = menu_entries_get_menu_stack_ptr(0); - - menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection); - - switch (state) - { - case MENU_INPUT_CTL_BIND_NONE: - return -1; - case MENU_INPUT_CTL_BIND_SINGLE: - keybind = (struct retro_keybind*)setting_get_ptr(setting); - - if (!keybind) - return -1; - - bind_type = setting_get_bind_type(setting); - - menu_input->binds.begin = bind_type; - menu_input->binds.last = bind_type; - menu_input->binds.target = keybind; - menu_input->binds.user = index_offset; - - info.list = menu_stack; - info.type = MENU_SETTINGS_CUSTOM_BIND_KEYBOARD; - info.directory_ptr = selection; - info.enum_idx = MENU_ENUM_LABEL_CUSTOM_BIND; - strlcpy(info.label, - msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND), sizeof(info.label)); - - if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info)) - menu_displaylist_ctl(DISPLAYLIST_PROCESS, &info); - break; - case MENU_INPUT_CTL_BIND_ALL: - menu_input->binds.target = &settings->input.binds - [index_offset][0]; - menu_input->binds.begin = MENU_SETTINGS_BIND_BEGIN; - menu_input->binds.last = MENU_SETTINGS_BIND_LAST; - - info.list = menu_stack; - info.type = MENU_SETTINGS_CUSTOM_BIND_KEYBOARD; - info.directory_ptr = selection; - info.enum_idx = MENU_ENUM_LABEL_CUSTOM_BIND_ALL; - strlcpy(info.label, - msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND_ALL), - sizeof(info.label)); - - if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info)) - menu_displaylist_ctl(DISPLAYLIST_PROCESS, &info); - break; - default: - case MENU_INPUT_CTL_NONE: - break; - } - - return 0; -} - -static void menu_input_key_bind_poll_bind_get_rested_axes( - struct menu_bind_state *state, unsigned port) -{ - unsigned a; - const input_device_driver_t *joypad = - input_driver_get_joypad_driver(); - const input_device_driver_t *sec_joypad = - input_driver_get_sec_joypad_driver(); - - if (!state || !joypad) - return; - - /* poll only the relevant port */ - for (a = 0; a < MENU_MAX_AXES; a++) - state->axis_state[port].rested_axes[a] = - input_joypad_axis_raw(joypad, port, a); - - if (sec_joypad) - { - /* poll only the relevant port */ - for (a = 0; a < MENU_MAX_AXES; a++) - state->axis_state[port].rested_axes[a] = - input_joypad_axis_raw(sec_joypad, port, a); - } -} - -static void menu_input_key_bind_poll_bind_state_internal( - const input_device_driver_t *joypad, - struct menu_bind_state *state, - unsigned port, - bool timed_out) -{ - unsigned b, a, h; - if (!joypad) - return; - - if (joypad->poll) - joypad->poll(); - - /* poll only the relevant port */ - /* for (i = 0; i < settings->input.max_users; i++) */ - for (b = 0; b < MENU_MAX_BUTTONS; b++) - state->state[port].buttons[b] = - input_joypad_button_raw(joypad, port, b); - - for (a = 0; a < MENU_MAX_AXES; a++) - state->state[port].axes[a] = - input_joypad_axis_raw(joypad, port, a); - - for (h = 0; h < MENU_MAX_HATS; h++) - { - if (input_joypad_hat_raw(joypad, port, HAT_UP_MASK, h)) - state->state[port].hats[h] |= HAT_UP_MASK; - if (input_joypad_hat_raw(joypad, port, HAT_DOWN_MASK, h)) - state->state[port].hats[h] |= HAT_DOWN_MASK; - if (input_joypad_hat_raw(joypad, port, HAT_LEFT_MASK, h)) - state->state[port].hats[h] |= HAT_LEFT_MASK; - if (input_joypad_hat_raw(joypad, port, HAT_RIGHT_MASK, h)) - state->state[port].hats[h] |= HAT_RIGHT_MASK; - } -} - -static void menu_input_key_bind_poll_bind_state( - struct menu_bind_state *state, - unsigned port, - bool timed_out) -{ - const input_device_driver_t *joypad = - input_driver_get_joypad_driver(); - const input_device_driver_t *sec_joypad = - input_driver_get_sec_joypad_driver(); - - if (!state) - return; - - memset(state->state, 0, sizeof(state->state)); - state->skip = timed_out || input_driver_state(NULL, 0, - RETRO_DEVICE_KEYBOARD, 0, RETROK_RETURN); - - menu_input_key_bind_poll_bind_state_internal( - joypad, state, port, timed_out); - - if (sec_joypad) - menu_input_key_bind_poll_bind_state_internal( - sec_joypad, state, port, timed_out); -} - -static bool menu_input_key_bind_set_mode( - enum menu_input_ctl_state state, void *data) -{ - unsigned index_offset; - input_keyboard_ctx_wait_t keys; - menu_handle_t *menu = NULL; - menu_input_t *menu_input = menu_input_get_ptr(); - rarch_setting_t *setting = (rarch_setting_t*)data; - settings_t *settings = config_get_ptr(); - - if (!setting) - return false; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return false; - if (menu_input_key_bind_set_mode_common(state, setting) == -1) - return false; - - index_offset = setting_get_index_offset(setting); - bind_port = settings->input.joypad_map[index_offset]; - - menu_input_key_bind_poll_bind_get_rested_axes( - &menu_input->binds, bind_port); - menu_input_key_bind_poll_bind_state( - &menu_input->binds, bind_port, false); - - menu_input->binds.timeout_end = cpu_features_get_time_usec() + - settings->input.bind_timeout * 1000000; - - keys.userdata = menu; - keys.cb = menu_input_key_bind_custom_bind_keyboard_cb; - - input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_START_WAIT_KEYS, &keys); - return true; -} - -static bool menu_input_key_bind_poll_find_trigger_pad( - struct menu_bind_state *state, - struct menu_bind_state *new_state, - unsigned p) -{ - unsigned a, b, h; - const struct menu_bind_state_port *n = (const struct menu_bind_state_port*) - &new_state->state[p]; - const struct menu_bind_state_port *o = (const struct menu_bind_state_port*) - &state->state[p]; - - for (b = 0; b < MENU_MAX_BUTTONS; b++) - { - bool iterate = n->buttons[b] && !o->buttons[b]; - - if (!iterate) - continue; - - state->target->joykey = b; - state->target->joyaxis = AXIS_NONE; - return true; - } - - /* Axes are a bit tricky ... */ - for (a = 0; a < MENU_MAX_AXES; a++) - { - int locked_distance = abs(n->axes[a] - - new_state->axis_state[p].locked_axes[a]); - int rested_distance = abs(n->axes[a] - - new_state->axis_state[p].rested_axes[a]); - - if (abs(n->axes[a]) >= 20000 && - locked_distance >= 20000 && - rested_distance >= 20000) - { - /* Take care of case where axis rests on +/- 0x7fff - * (e.g. 360 controller on Linux) */ - state->target->joyaxis = n->axes[a] > 0 - ? AXIS_POS(a) : AXIS_NEG(a); - state->target->joykey = NO_BTN; - - /* Lock the current axis */ - new_state->axis_state[p].locked_axes[a] = - n->axes[a] > 0 ? - 0x7fff : -0x7fff; - return true; - } - - if (locked_distance >= 20000) /* Unlock the axis. */ - new_state->axis_state[p].locked_axes[a] = 0; - } - - for (h = 0; h < MENU_MAX_HATS; h++) - { - uint16_t trigged = n->hats[h] & (~o->hats[h]); - uint16_t sane_trigger = 0; - - if (trigged & HAT_UP_MASK) - sane_trigger = HAT_UP_MASK; - else if (trigged & HAT_DOWN_MASK) - sane_trigger = HAT_DOWN_MASK; - else if (trigged & HAT_LEFT_MASK) - sane_trigger = HAT_LEFT_MASK; - else if (trigged & HAT_RIGHT_MASK) - sane_trigger = HAT_RIGHT_MASK; - - if (sane_trigger) - { - state->target->joykey = HAT_MAP(h, sane_trigger); - state->target->joyaxis = AXIS_NONE; - return true; - } - } - - return false; -} - -static bool menu_input_key_bind_poll_find_trigger( - struct menu_bind_state *state, - struct menu_bind_state *new_state) -{ - unsigned i; - settings_t *settings = config_get_ptr(); - - if (!state || !new_state) - return false; - - for (i = 0; i < settings->input.max_users; i++) - { - if (!menu_input_key_bind_poll_find_trigger_pad( - state, new_state, i)) - continue; - - /* Update the joypad mapping automatically. - * More friendly that way. */ -#if 0 - settings->input.joypad_map[state->user] = i; -#endif - return true; - } - return false; -} - - -static bool menu_input_key_bind_iterate(char *s, size_t len) -{ - struct menu_bind_state binds; - bool timed_out = false; - menu_input_t *menu_input = menu_input_get_ptr(); - settings_t *settings = config_get_ptr(); - int64_t current = cpu_features_get_time_usec(); - int timeout = - (menu_input->binds.timeout_end - current) / 1000000; - - if (timeout <= 0) - { - input_driver_keyboard_mapping_set_block(false); - - menu_input->binds.begin++; - menu_input->binds.target++; - menu_input->binds.timeout_end = cpu_features_get_time_usec() + - settings->input.bind_timeout * 1000000; - timed_out = true; - } - - snprintf(s, len, - "[%s]\npress keyboard or joypad\n(timeout %d %s)", - input_config_bind_map_get_desc( - menu_input->binds.begin - MENU_SETTINGS_BIND_BEGIN), - timeout, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SECONDS)); - - /* binds.begin is updated in keyboard_press callback. */ - if (menu_input->binds.begin > menu_input->binds.last) - { - /* Avoid new binds triggering things right away. */ - input_driver_set_flushing_input(); - - /* We won't be getting any key events, so just cancel early. */ - if (timed_out) - input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_CANCEL_WAIT_KEYS, NULL); - - return true; - } - - binds = menu_input->binds; - - input_driver_keyboard_mapping_set_block(true); - menu_input_key_bind_poll_bind_state(&binds, bind_port, timed_out); - - if ((binds.skip && !menu_input->binds.skip) || - menu_input_key_bind_poll_find_trigger(&menu_input->binds, &binds)) - { - input_driver_keyboard_mapping_set_block(false); - - /* Avoid new binds triggering things right away. */ - input_driver_set_flushing_input(); - - binds.begin++; - - if (binds.begin > binds.last) - { - input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_CANCEL_WAIT_KEYS, NULL); - return true; - } - - binds.target++; - binds.timeout_end = cpu_features_get_time_usec() + - settings->input.bind_timeout * 1000000; - } - menu_input->binds = binds; - - return false; -} - bool menu_input_mouse_check_vector_inside_hitbox(menu_input_ctx_hitbox_t *hitbox) { int16_t mouse_x = menu_input_mouse_state(MENU_MOUSE_X_AXIS); @@ -630,9 +70,6 @@ bool menu_input_mouse_check_vector_inside_hitbox(menu_input_ctx_hitbox_t *hitbox bool menu_input_ctl(enum menu_input_ctl_state state, void *data) { - static char menu_input_keyboard_label_setting[256]; - static const char **menu_input_keyboard_buffer; - static const char *menu_input_keyboard_label = NULL; static bool pointer_dragging = false; menu_input_t *menu_input = menu_input_get_ptr(); @@ -641,34 +78,10 @@ bool menu_input_ctl(enum menu_input_ctl_state state, void *data) switch (state) { - case MENU_INPUT_CTL_BIND_SET_MIN_MAX: - { - menu_input_ctx_bind_limits_t *lim = - (menu_input_ctx_bind_limits_t*)data; - if (!lim || !menu_input) - return false; - - menu_input->binds.begin = lim->min; - menu_input->binds.last = lim->max; - } - break; case MENU_INPUT_CTL_DEINIT: memset(menu_input, 0, sizeof(menu_input_t)); pointer_dragging = false; break; - case MENU_INPUT_CTL_SEARCH_START: - { - menu_handle_t *menu = NULL; - if (!menu_driver_ctl( - RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return false; - - menu_input->keyboard.display = true; - menu_input_keyboard_label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH); - menu_input_keyboard_buffer = - input_keyboard_start_line(menu, menu_input_search_cb); - } - break; case MENU_INPUT_CTL_MOUSE_PTR: { unsigned *ptr = (unsigned*)data; @@ -701,89 +114,6 @@ bool menu_input_ctl(enum menu_input_ctl_state state, void *data) case MENU_INPUT_CTL_UNSET_POINTER_DRAGGED: pointer_dragging = false; break; - case MENU_INPUT_CTL_KEYBOARD_DISPLAY: - { - bool *ptr = (bool*)data; - *ptr = menu_input->keyboard.display; - } - break; - case MENU_INPUT_CTL_SET_KEYBOARD_DISPLAY: - { - bool *ptr = (bool*)data; - menu_input->keyboard.display = *ptr; - } - break; - case MENU_INPUT_CTL_KEYBOARD_BUFF_PTR: - { - const char **ptr = (const char**)data; - *ptr = *menu_input_keyboard_buffer; - } - break; - case MENU_INPUT_CTL_KEYBOARD_LABEL: - { - const char **ptr = (const char**)data; - *ptr = menu_input_keyboard_label; - } - break; - case MENU_INPUT_CTL_SET_KEYBOARD_LABEL: - { - char **ptr = (char**)data; - menu_input_keyboard_label = *ptr; - } - break; - case MENU_INPUT_CTL_UNSET_KEYBOARD_LABEL: - menu_input_keyboard_label = NULL; - break; - case MENU_INPUT_CTL_KEYBOARD_LABEL_SETTING: - { - const char **ptr = (const char**)data; - *ptr = menu_input_keyboard_label_setting; - } - break; - case MENU_INPUT_CTL_SET_KEYBOARD_LABEL_SETTING: - { - char **ptr = (char**)data; - strlcpy(menu_input_keyboard_label_setting, - *ptr, sizeof(menu_input_keyboard_label_setting)); - } - break; - case MENU_INPUT_CTL_UNSET_KEYBOARD_LABEL_SETTING: - menu_input_keyboard_label_setting[0] = '\0'; - break; - case MENU_INPUT_CTL_BIND_NONE: - case MENU_INPUT_CTL_BIND_SINGLE: - case MENU_INPUT_CTL_BIND_ALL: - return menu_input_key_bind_set_mode(state, data); - case MENU_INPUT_CTL_BIND_ITERATE: - { - menu_input_ctx_bind_t *bind = (menu_input_ctx_bind_t*)data; - if (!bind) - return false; - return menu_input_key_bind_iterate(bind->s, bind->len); - } - case MENU_INPUT_CTL_START_LINE: - { - bool keyboard_display = true; - menu_handle_t *menu = NULL; - menu_input_ctx_line_t *line = (menu_input_ctx_line_t*)data; - if (!menu_input || !line) - return false; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return false; - - menu_input_ctl(MENU_INPUT_CTL_SET_KEYBOARD_DISPLAY, - &keyboard_display); - menu_input_ctl(MENU_INPUT_CTL_SET_KEYBOARD_LABEL, - &line->label); - menu_input_ctl(MENU_INPUT_CTL_SET_KEYBOARD_LABEL_SETTING, - &line->label_setting); - - menu_input->keyboard.type = line->type; - menu_input->keyboard.idx = line->idx; - menu_input_keyboard_buffer = - input_keyboard_start_line(menu, line->cb); - } - break; default: case MENU_INPUT_CTL_NONE: break; @@ -792,92 +122,6 @@ bool menu_input_ctl(enum menu_input_ctl_state state, void *data) return true; } -static int menu_input_pointer(unsigned *action) -{ - const struct retro_keybind *binds[MAX_USERS] = {NULL}; - menu_input_t *menu_input = menu_input_get_ptr(); - unsigned fb_width = menu_display_get_width(); - unsigned fb_height = menu_display_get_height(); - int pointer_device = - menu_driver_ctl(RARCH_MENU_CTL_IS_SET_TEXTURE, NULL) ? - RETRO_DEVICE_POINTER : RARCH_DEVICE_POINTER_SCREEN; - int pointer_x = - input_driver_state(binds, 0, pointer_device, - 0, RETRO_DEVICE_ID_POINTER_X); - int pointer_y = - input_driver_state(binds, 0, pointer_device, - 0, RETRO_DEVICE_ID_POINTER_Y); - - menu_input->pointer.pressed[0] = input_driver_state(binds, - 0, pointer_device, - 0, RETRO_DEVICE_ID_POINTER_PRESSED); - menu_input->pointer.pressed[1] = input_driver_state(binds, - 0, pointer_device, - 1, RETRO_DEVICE_ID_POINTER_PRESSED); - menu_input->pointer.back = input_driver_state(binds, 0, pointer_device, - 0, RARCH_DEVICE_ID_POINTER_BACK); - - menu_input->pointer.x = ((pointer_x + 0x7fff) * (int)fb_width) / 0xFFFF; - menu_input->pointer.y = ((pointer_y + 0x7fff) * (int)fb_height) / 0xFFFF; - - return 0; -} - -static int menu_input_mouse_frame( - menu_file_list_cbs_t *cbs, menu_entry_t *entry, - uint64_t input_mouse, unsigned action) -{ - int ret = 0; - menu_input_t *menu_input = menu_input_get_ptr(); - - if (BIT64_GET(input_mouse, MENU_MOUSE_ACTION_BUTTON_L)) - { - menu_ctx_pointer_t point; - - point.x = menu_input_mouse_state(MENU_MOUSE_X_AXIS); - point.y = menu_input_mouse_state(MENU_MOUSE_Y_AXIS); - point.ptr = menu_input->mouse.ptr; - point.cbs = cbs; - point.entry = entry; - point.action = action; - - menu_driver_ctl(RARCH_MENU_CTL_POINTER_TAP, &point); - - ret = point.retcode; - } - - if (BIT64_GET(input_mouse, MENU_MOUSE_ACTION_BUTTON_R)) - { - size_t selection; - menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection); - menu_entry_action(entry, selection, MENU_ACTION_CANCEL); - } - - if (BIT64_GET(input_mouse, MENU_MOUSE_ACTION_WHEEL_DOWN)) - { - unsigned increment_by = 1; - menu_navigation_ctl(MENU_NAVIGATION_CTL_INCREMENT, &increment_by); - } - - if (BIT64_GET(input_mouse, MENU_MOUSE_ACTION_WHEEL_UP)) - { - unsigned decrement_by = 1; - menu_navigation_ctl(MENU_NAVIGATION_CTL_DECREMENT, &decrement_by); - } - - if (BIT64_GET(input_mouse, MENU_MOUSE_ACTION_HORIZ_WHEEL_UP)) - { - /* stub */ - } - - if (BIT64_GET(input_mouse, MENU_MOUSE_ACTION_HORIZ_WHEEL_DOWN)) - { - /* stub */ - } - - return ret; -} - static int menu_input_mouse_post_iterate(uint64_t *input_mouse, menu_file_list_cbs_t *cbs, unsigned action) { @@ -885,9 +129,6 @@ static int menu_input_mouse_post_iterate(uint64_t *input_mouse, static bool mouse_oldleft = false; static bool mouse_oldright = false; - *input_mouse = MENU_MOUSE_ACTION_NONE; - - if ( !settings->menu.mouse.enable #ifdef HAVE_OVERLAY @@ -895,9 +136,14 @@ static int menu_input_mouse_post_iterate(uint64_t *input_mouse, #endif ) { - /* HACK: Need to lie to avoid false hits if mouse is held when entering the RetroArch window */ - /* this happens if, for example, someone double clicks the window border to maximize it */ - /* the proper fix is, of course, triggering on WM_LBUTTONDOWN rather than this state change */ + /* HACK: Need to lie to avoid false hits if mouse is held + * when entering the RetroArch window. */ + + /* This happens if, for example, someone double clicks the + * window border to maximize it. + * + * The proper fix is, of course, triggering on WM_LBUTTONDOWN + * rather than this state change. */ mouse_oldleft = true; mouse_oldright = true; return 0; @@ -963,6 +209,67 @@ static int menu_input_mouse_post_iterate(uint64_t *input_mouse, return 0; } +static int menu_input_mouse_frame( + menu_file_list_cbs_t *cbs, menu_entry_t *entry, + unsigned action) +{ + uint64_t mouse_state = MENU_MOUSE_ACTION_NONE; + int ret = 0; + settings_t *settings = config_get_ptr(); + menu_input_t *menu_input = menu_input_get_ptr(); + + if (settings->menu.mouse.enable) + ret = menu_input_mouse_post_iterate(&mouse_state, cbs, action); + + if (BIT64_GET(mouse_state, MENU_MOUSE_ACTION_BUTTON_L)) + { + menu_ctx_pointer_t point; + + point.x = menu_input_mouse_state(MENU_MOUSE_X_AXIS); + point.y = menu_input_mouse_state(MENU_MOUSE_Y_AXIS); + point.ptr = menu_input->mouse.ptr; + point.cbs = cbs; + point.entry = entry; + point.action = action; + + menu_driver_ctl(RARCH_MENU_CTL_POINTER_TAP, &point); + + ret = point.retcode; + } + + if (BIT64_GET(mouse_state, MENU_MOUSE_ACTION_BUTTON_R)) + { + size_t selection; + menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection); + menu_entry_action(entry, selection, MENU_ACTION_CANCEL); + } + + if (BIT64_GET(mouse_state, MENU_MOUSE_ACTION_WHEEL_DOWN)) + { + unsigned increment_by = 1; + menu_navigation_ctl(MENU_NAVIGATION_CTL_INCREMENT, &increment_by); + } + + if (BIT64_GET(mouse_state, MENU_MOUSE_ACTION_WHEEL_UP)) + { + unsigned decrement_by = 1; + menu_navigation_ctl(MENU_NAVIGATION_CTL_DECREMENT, &decrement_by); + } + + if (BIT64_GET(mouse_state, MENU_MOUSE_ACTION_HORIZ_WHEEL_UP)) + { + /* stub */ + } + + if (BIT64_GET(mouse_state, MENU_MOUSE_ACTION_HORIZ_WHEEL_DOWN)) + { + /* stub */ + } + + return ret; +} + + int16_t menu_input_pointer_state(enum menu_input_pointer_state state) { menu_input_t *menu = menu_input_get_ptr(); @@ -1038,16 +345,18 @@ static int menu_input_pointer_post_iterate( static int16_t pointer_old_x = 0; static int16_t pointer_old_y = 0; int ret = 0; + bool check_overlay = false; menu_input_t *menu_input = menu_input_get_ptr(); settings_t *settings = config_get_ptr(); - bool check_overlay = settings ? !settings->menu.pointer.enable : false; - + if (!menu_input || !settings) return -1; + check_overlay = !settings->menu.pointer.enable; #ifdef HAVE_OVERLAY - check_overlay = check_overlay || - (settings->input.overlay_enable && input_overlay_is_alive(NULL)); + if (!check_overlay) + check_overlay = (settings->input.overlay_enable + && input_overlay_is_alive(NULL)); #endif if (check_overlay) @@ -1149,7 +458,6 @@ static int menu_input_pointer_post_iterate( void menu_input_post_iterate(int *ret, unsigned action) { size_t selection; - uint64_t mouse_state = 0; menu_file_list_cbs_t *cbs = NULL; menu_entry_t entry = {{0}}; settings_t *settings = config_get_ptr(); @@ -1163,188 +471,9 @@ void menu_input_post_iterate(int *ret, unsigned action) menu_entry_get(&entry, 0, selection, NULL, false); - if (settings->menu.mouse.enable) - *ret = menu_input_mouse_post_iterate(&mouse_state, cbs, action); - - *ret = menu_input_mouse_frame(cbs, &entry, mouse_state, action); + *ret = menu_input_mouse_frame(cbs, &entry, action); if (settings->menu.pointer.enable) *ret |= menu_input_pointer_post_iterate(cbs, &entry, action); } -static unsigned menu_input_frame_pointer(unsigned *data) -{ - unsigned ret = *data; - settings_t *settings = config_get_ptr(); - menu_input_t *menu_input = menu_input_get_ptr(); - bool mouse_enabled = settings->menu.mouse.enable; -#ifdef HAVE_OVERLAY - if (!mouse_enabled) - mouse_enabled = !(settings->input.overlay_enable - && input_overlay_is_alive(NULL)); -#endif - - if (!mouse_enabled) - menu_input->mouse.ptr = 0; - - if (settings->menu.pointer.enable) - menu_input_pointer(&ret); - else - memset(&menu_input->pointer, 0, sizeof(menu_input->pointer)); - - return ret; -} - -static unsigned menu_input_frame_build(retro_input_t trigger_input) -{ - settings_t *settings = config_get_ptr(); - unsigned ret = MENU_ACTION_NOOP; - - if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_UP)) - ret = MENU_ACTION_UP; - else if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_DOWN)) - ret = MENU_ACTION_DOWN; - else if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_LEFT)) - ret = MENU_ACTION_LEFT; - else if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_RIGHT)) - ret = MENU_ACTION_RIGHT; - else if (trigger_input.state & (UINT64_C(1) << settings->menu_scroll_up_btn)) - ret = MENU_ACTION_SCROLL_UP; - else if (trigger_input.state & (UINT64_C(1) << settings->menu_scroll_down_btn)) - ret = MENU_ACTION_SCROLL_DOWN; - else if (trigger_input.state & (UINT64_C(1) << settings->menu_cancel_btn)) - ret = MENU_ACTION_CANCEL; - else if (trigger_input.state & (UINT64_C(1) << settings->menu_ok_btn)) - ret = MENU_ACTION_OK; - else if (trigger_input.state & (UINT64_C(1) << settings->menu_search_btn)) - ret = MENU_ACTION_SEARCH; - else if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_Y)) - ret = MENU_ACTION_SCAN; - else if (trigger_input.state & (UINT64_C(1) << settings->menu_default_btn)) - ret = MENU_ACTION_START; - else if (trigger_input.state & (UINT64_C(1) << settings->menu_info_btn)) - ret = MENU_ACTION_INFO; - else if (trigger_input.state & (UINT64_C(1) << RARCH_MENU_TOGGLE)) - ret = MENU_ACTION_TOGGLE; - - return menu_input_frame_pointer(&ret); -} - - -unsigned menu_input_frame_retropad(retro_input_t input, - retro_input_t trigger_input) -{ - menu_animation_ctx_delta_t delta; - float delta_time; - static bool initial_held = true; - static bool first_held = false; - bool set_scroll = false; - size_t new_scroll_accel = 0; - menu_input_t *menu_input = menu_input_get_ptr(); - - if (!menu_input) - return 0; - - core_poll(); - - /* don't run anything first frame, only capture held inputs - * for old_input_state. */ - - if (input.state) - { - if (!first_held) - { - first_held = true; - menu_input->delay.timer = initial_held ? 12 : 6; - menu_input->delay.count = 0; - } - - if (menu_input->delay.count >= menu_input->delay.timer) - { - retro_input_t input_repeat = {0}; - BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_UP); - BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_DOWN); - BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_LEFT); - BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_RIGHT); - BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_B); - BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_A); - BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_L); - BIT32_SET(input_repeat.state, RETRO_DEVICE_ID_JOYPAD_R); - - set_scroll = true; - first_held = false; - trigger_input.state |= input.state & input_repeat.state; - - menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SCROLL_ACCEL, - &new_scroll_accel); - - new_scroll_accel = MIN(new_scroll_accel + 1, 64); - } - - initial_held = false; - } - else - { - set_scroll = true; - first_held = false; - initial_held = true; - } - - if (set_scroll) - menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SCROLL_ACCEL, - &new_scroll_accel); - - menu_animation_ctl(MENU_ANIMATION_CTL_DELTA_TIME, &delta_time); - - delta.current = delta_time; - - if (menu_animation_ctl(MENU_ANIMATION_CTL_IDEAL_DELTA_TIME_GET, &delta)) - menu_input->delay.count += delta.ideal; - - if (menu_input->keyboard.display) - { - static unsigned ti_char = 64; - static bool ti_next = false; - - if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_DOWN)) - { - if (ti_char > 32) - ti_char--; - if (! ti_next) - input_keyboard_event(true, '\x7f', '\x7f', 0, RETRO_DEVICE_KEYBOARD); - input_keyboard_event(true, ti_char, ti_char, 0, RETRO_DEVICE_KEYBOARD); - ti_next = false; - } - - if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_UP)) - { - if (ti_char < 125) - ti_char++; - if (! ti_next) - input_keyboard_event(true, '\x7f', '\x7f', 0, RETRO_DEVICE_KEYBOARD); - input_keyboard_event(true, ti_char, ti_char, 0, RETRO_DEVICE_KEYBOARD); - ti_next = false; - } - - if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_A)) - { - ti_char = 64; - ti_next = true; - } - - if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_B)) - { - input_keyboard_event(true, '\x7f', '\x7f', 0, RETRO_DEVICE_KEYBOARD); - ti_char = 64; - ti_next = false; - } - - /* send return key to close keyboard input window */ - if (trigger_input.state & (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_START)) - input_keyboard_event(true, '\n', '\n', 0, RETRO_DEVICE_KEYBOARD); - - trigger_input.state = 0; - } - - return menu_input_frame_build(trigger_input); -} diff --git a/menu/menu_input.h b/menu/menu_input.h index 14dc68e448..cb61e9a45d 100644 --- a/menu/menu_input.h +++ b/menu/menu_input.h @@ -76,25 +76,29 @@ enum menu_input_ctl_state MENU_INPUT_CTL_IS_POINTER_DRAGGED, MENU_INPUT_CTL_SET_POINTER_DRAGGED, MENU_INPUT_CTL_UNSET_POINTER_DRAGGED, - MENU_INPUT_CTL_KEYBOARD_DISPLAY, - MENU_INPUT_CTL_SET_KEYBOARD_DISPLAY, - MENU_INPUT_CTL_KEYBOARD_BUFF_PTR, - MENU_INPUT_CTL_KEYBOARD_LABEL, - MENU_INPUT_CTL_SET_KEYBOARD_LABEL, - MENU_INPUT_CTL_UNSET_KEYBOARD_LABEL, - MENU_INPUT_CTL_KEYBOARD_LABEL_SETTING, - MENU_INPUT_CTL_SET_KEYBOARD_LABEL_SETTING, - MENU_INPUT_CTL_UNSET_KEYBOARD_LABEL_SETTING, - MENU_INPUT_CTL_SEARCH_START, - MENU_INPUT_CTL_DEINIT, - MENU_INPUT_CTL_BIND_NONE, - MENU_INPUT_CTL_BIND_SINGLE, - MENU_INPUT_CTL_BIND_ALL, - MENU_INPUT_CTL_BIND_ITERATE, - MENU_INPUT_CTL_BIND_SET_MIN_MAX, - MENU_INPUT_CTL_START_LINE + MENU_INPUT_CTL_DEINIT }; +typedef struct menu_input +{ + struct + { + unsigned ptr; + } mouse; + + struct + { + int16_t x; + int16_t y; + int16_t dx; + int16_t dy; + float accel; + bool pressed[2]; + bool back; + unsigned ptr; + } pointer; +} menu_input_t; + typedef struct menu_input_ctx_hitbox { int32_t x1; @@ -103,34 +107,6 @@ typedef struct menu_input_ctx_hitbox int32_t y2; } menu_input_ctx_hitbox_t; -typedef struct menu_input_ctx_bind -{ - char *s; - size_t len; -} menu_input_ctx_bind_t; - -typedef struct menu_input_ctx_line -{ - const char *label; - const char *label_setting; - unsigned type; - unsigned idx; - input_keyboard_line_complete_t cb; -} menu_input_ctx_line_t; - -typedef struct menu_input_ctx_bind_limits -{ - unsigned min; - unsigned max; -} menu_input_ctx_bind_limits_t; - -/* Keyboard input callbacks */ -void menu_input_st_uint_cb (void *userdata, const char *str); -void menu_input_st_hex_cb (void *userdata, const char *str); -void menu_input_st_cheat_cb (void *userdata, const char *str); - -unsigned menu_input_frame_retropad(retro_input_t input, retro_input_t trigger_state); - void menu_input_post_iterate(int *ret, unsigned action); int16_t menu_input_pointer_state(enum menu_input_pointer_state state); @@ -139,10 +115,10 @@ int16_t menu_input_mouse_state(enum menu_input_mouse_state state); bool menu_input_mouse_check_vector_inside_hitbox(menu_input_ctx_hitbox_t *hitbox); -void menu_input_key_end_line(void); - bool menu_input_ctl(enum menu_input_ctl_state state, void *data); +menu_input_t *menu_input_get_ptr(void); + RETRO_END_DECLS #endif diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 6560e3fb41..080ec912ee 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -42,6 +42,8 @@ #include "../frontend/frontend_driver.h" +#include "widgets/menu_input_bind_dialog.h" + #include "menu_setting.h" #include "menu_driver.h" #include "menu_animation.h" @@ -54,7 +56,8 @@ #include "../msg_hash.h" #include "../defaults.h" #include "../driver.h" -#include "../core.h" +#include "../dirs.h" +#include "../paths.h" #include "../dynamic.h" #include "../runloop.h" #include "../verbosity.h" @@ -1240,7 +1243,7 @@ static int setting_action_right_bind_device(void *data, bool wraparound) static int setting_action_ok_bind_all(void *data, bool wraparound) { (void)wraparound; - if (!menu_input_ctl(MENU_INPUT_CTL_BIND_ALL, data)) + if (!menu_input_key_bind_set_mode(MENU_INPUT_BINDS_CTL_BIND_ALL, data)) return -1; return 0; } @@ -1294,7 +1297,7 @@ static int setting_action_ok_bind_defaults(void *data, bool wraparound) lim.min = MENU_SETTINGS_BIND_BEGIN; lim.max = MENU_SETTINGS_BIND_LAST; - menu_input_ctl(MENU_INPUT_CTL_BIND_SET_MIN_MAX, &lim); + menu_input_key_bind_set_min_max(&lim); for (i = MENU_SETTINGS_BIND_BEGIN; i <= MENU_SETTINGS_BIND_LAST; i++, target++) @@ -1673,6 +1676,17 @@ void general_write_handler(void *data) } #endif break; + case MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES: +#ifdef HAVE_NETPLAY + { + bool val = (global->netplay.check_frames > 0); + + if (val) + retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_NETPLAY_CHECK_FRAMES); + else + retroarch_override_setting_unset(RARCH_OVERRIDE_SETTING_NETPLAY_CHECK_FRAMES); + } +#endif default: break; } @@ -2131,8 +2145,8 @@ static bool setting_append_list( &group_info, &subgroup_info, parent_group); - (*list)[list_info->index - 1].size = config_get_active_core_path_size(); - (*list)[list_info->index - 1].value.target.string = config_get_active_core_path_ptr(); + (*list)[list_info->index - 1].size = path_get_core_size(); + (*list)[list_info->index - 1].value.target.string = path_get_core_ptr(); (*list)[list_info->index - 1].values = ext_name; menu_settings_list_current_add_cmd(list, list_info, CMD_EVENT_LOAD_CORE); settings_data_list_current_add_flags(list, list_info, SD_FLAG_BROWSER_ACTION); @@ -2427,7 +2441,7 @@ static bool setting_append_list( parent_group); menu_settings_list_current_add_enum_idx(list, list_info, MENU_ENUM_LABEL_MENU_SETTINGS); -#if !defined(RARCH_CONSOLE) +#if !defined(RARCH_CONSOLE) && !defined(HAVE_LAKKA) CONFIG_ACTION( list, list_info, msg_hash_to_str(MENU_ENUM_LABEL_USER_INTERFACE_SETTINGS), @@ -4746,7 +4760,7 @@ static bool setting_append_list( sizeof(settings->path.osk_overlay), msg_hash_to_str(MENU_ENUM_LABEL_KEYBOARD_OVERLAY_PRESET), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_KEYBOARD_OVERLAY_PRESET), - global->dir.osk_overlay, + dir_get_osk_overlay_ptr(), &group_info, &subgroup_info, parent_group, @@ -5812,6 +5826,21 @@ static bool setting_append_list( settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED); menu_settings_list_current_add_enum_idx(list, list_info, MENU_ENUM_LABEL_NETPLAY_DELAY_FRAMES); + CONFIG_UINT( + list, list_info, + &global->netplay.check_frames, + msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES), + 0, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + menu_settings_list_current_add_range(list, list_info, 0, 10, 1, true, false); + settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED); + menu_settings_list_current_add_enum_idx(list, list_info, MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES); + CONFIG_UINT( list, list_info, &global->netplay.port, @@ -6440,8 +6469,8 @@ static bool setting_append_list( CONFIG_DIR( list, list_info, - global->dir.osk_overlay, - sizeof(global->dir.osk_overlay), + dir_get_osk_overlay_ptr(), + dir_get_osk_overlay_size(), msg_hash_to_str(MENU_ENUM_LABEL_OSK_OVERLAY_DIRECTORY), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OSK_OVERLAY_DIRECTORY), g_defaults.dir.osk_overlay, @@ -6516,8 +6545,8 @@ static bool setting_append_list( CONFIG_DIR( list, list_info, - global->dir.savefile, - sizeof(global->dir.savefile), + dir_get_savefile_ptr(), + dir_get_savefile_size(), msg_hash_to_str(MENU_ENUM_LABEL_SAVEFILE_DIRECTORY), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SAVEFILE_DIRECTORY), "", @@ -6531,8 +6560,8 @@ static bool setting_append_list( CONFIG_DIR( list, list_info, - global->dir.savestate, - sizeof(global->dir.savestate), + dir_get_savestate_ptr(), + dir_get_savestate_size(), msg_hash_to_str(MENU_ENUM_LABEL_SAVESTATE_DIRECTORY), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SAVESTATE_DIRECTORY), "", diff --git a/menu/menu_shader.c b/menu/menu_shader.c index 11c41c3bd4..96455461d3 100644 --- a/menu/menu_shader.c +++ b/menu/menu_shader.c @@ -29,6 +29,7 @@ #include "menu_shader.h" #include "../file_path_special.h" #include "../configuration.h" +#include "../paths.h" #include "../runloop.h" #include "../verbosity.h" @@ -52,7 +53,7 @@ void menu_shader_manager_init(void) struct video_shader *shader = NULL; config_file_t *conf = NULL; settings_t *settings = config_get_ptr(); - const char *config_path = config_get_active_path(); + const char *config_path = path_get_config(); menu_driver_ctl(RARCH_MENU_CTL_SHADER_GET, &shader); @@ -221,7 +222,6 @@ bool menu_shader_manager_save_preset( config_file_t *conf = NULL; bool ret = false; struct video_shader *shader = NULL; - global_t *global = global_get_ptr(); settings_t *settings = config_get_ptr(); menu_handle_t *menu = NULL; @@ -295,10 +295,10 @@ bool menu_shader_manager_save_preset( strlcpy(buffer, conf_path, sizeof(buffer)); } - if (!string_is_empty(global->path.config)) + if (!path_is_config_empty()) fill_pathname_basedir( config_directory, - global->path.config, + path_get_config(), sizeof(config_directory)); if (!fullpath) diff --git a/menu/widgets/menu_popup.c b/menu/widgets/menu_dialog.c similarity index 75% rename from menu/widgets/menu_popup.c rename to menu/widgets/menu_dialog.c index 9c0f376fc3..afe64af74a 100644 --- a/menu/widgets/menu_popup.c +++ b/menu/widgets/menu_dialog.c @@ -24,7 +24,7 @@ #include "../../cheevos.h" #endif -#include "menu_popup.h" +#include "menu_dialog.h" #include "../menu_display.h" #include "../menu_driver.h" @@ -35,13 +35,13 @@ #include "../../input/input_autodetect.h" #include "../../input/input_config.h" -static bool menu_popup_pending_push = false; -static bool menu_popup_active = false; -static unsigned menu_popup_current_id = 0; -static enum menu_popup_type menu_popup_current_type = MENU_POPUP_NONE; -static enum msg_hash_enums menu_popup_current_msg = MSG_UNKNOWN; +static bool menu_dialog_pending_push = false; +static bool menu_dialog_active = false; +static unsigned menu_dialog_current_id = 0; +static enum menu_dialog_type menu_dialog_current_type = MENU_DIALOG_NONE; +static enum msg_hash_enums menu_dialog_current_msg = MSG_UNKNOWN; -int menu_popup_iterate(char *s, size_t len, const char *label) +int menu_dialog_iterate(char *s, size_t len, const char *label) { #ifdef HAVE_CHEEVOS cheevos_ctx_desc_t desc_info; @@ -49,9 +49,9 @@ int menu_popup_iterate(char *s, size_t len, const char *label) bool do_exit = false; settings_t *settings = config_get_ptr(); - switch (menu_popup_current_type) + switch (menu_dialog_current_type) { - case MENU_POPUP_WELCOME: + case MENU_DIALOG_WELCOME: { static int64_t timeout_end; int64_t timeout; @@ -82,7 +82,7 @@ int menu_popup_iterate(char *s, size_t len, const char *label) } } break; - case MENU_POPUP_HELP_CONTROLS: + case MENU_DIALOG_HELP_CONTROLS: { unsigned i; char s2[PATH_MAX_LENGTH] = {0}; @@ -180,37 +180,37 @@ int menu_popup_iterate(char *s, size_t len, const char *label) break; #ifdef HAVE_CHEEVOS - case MENU_POPUP_HELP_CHEEVOS_DESCRIPTION: - desc_info.idx = menu_popup_current_id; + case MENU_DIALOG_HELP_CHEEVOS_DESCRIPTION: + desc_info.idx = menu_dialog_current_id; desc_info.s = s; desc_info.len = len; cheevos_get_description(&desc_info); break; #endif - case MENU_POPUP_HELP_WHAT_IS_A_CORE: + case MENU_DIALOG_HELP_WHAT_IS_A_CORE: menu_hash_get_help_enum(MENU_ENUM_LABEL_VALUE_WHAT_IS_A_CORE_DESC, s, len); break; - case MENU_POPUP_HELP_LOADING_CONTENT: + case MENU_DIALOG_HELP_LOADING_CONTENT: menu_hash_get_help_enum(MENU_ENUM_LABEL_LOAD_CONTENT_LIST, s, len); break; - case MENU_POPUP_HELP_CHANGE_VIRTUAL_GAMEPAD: + case MENU_DIALOG_HELP_CHANGE_VIRTUAL_GAMEPAD: menu_hash_get_help_enum( MENU_ENUM_LABEL_VALUE_HELP_CHANGE_VIRTUAL_GAMEPAD_DESC, s, len); break; - case MENU_POPUP_HELP_AUDIO_VIDEO_TROUBLESHOOTING: + case MENU_DIALOG_HELP_AUDIO_VIDEO_TROUBLESHOOTING: menu_hash_get_help_enum( MENU_ENUM_LABEL_VALUE_HELP_AUDIO_VIDEO_TROUBLESHOOTING_DESC, s, len); break; - case MENU_POPUP_HELP_SCANNING_CONTENT: + case MENU_DIALOG_HELP_SCANNING_CONTENT: menu_hash_get_help_enum(MENU_ENUM_LABEL_VALUE_HELP_SCANNING_CONTENT_DESC, s, len); break; - case MENU_POPUP_HELP_EXTRACT: + case MENU_DIALOG_HELP_EXTRACT: menu_hash_get_help_enum(MENU_ENUM_LABEL_VALUE_EXTRACTING_PLEASE_WAIT, s, len); @@ -220,50 +220,50 @@ int menu_popup_iterate(char *s, size_t len, const char *label) do_exit = true; } break; - case MENU_POPUP_QUIT_CONFIRM: - case MENU_POPUP_INFORMATION: - case MENU_POPUP_QUESTION: - case MENU_POPUP_WARNING: - case MENU_POPUP_ERROR: - menu_hash_get_help_enum(menu_popup_current_msg, + case MENU_DIALOG_QUIT_CONFIRM: + case MENU_DIALOG_INFORMATION: + case MENU_DIALOG_QUESTION: + case MENU_DIALOG_WARNING: + case MENU_DIALOG_ERROR: + menu_hash_get_help_enum(menu_dialog_current_msg, s, len); break; - case MENU_POPUP_NONE: + case MENU_DIALOG_NONE: default: break; } if (do_exit) { - menu_popup_current_type = MENU_POPUP_NONE; + menu_dialog_current_type = MENU_DIALOG_NONE; return 1; } return 0; } -bool menu_popup_is_push_pending(void) +bool menu_dialog_is_push_pending(void) { - return menu_popup_pending_push; + return menu_dialog_pending_push; } -void menu_popup_unset_pending_push(void) +void menu_dialog_unset_pending_push(void) { - menu_popup_pending_push = false; + menu_dialog_pending_push = false; } -void menu_popup_push_pending(bool push, enum menu_popup_type type) +void menu_dialog_push_pending(bool push, enum menu_dialog_type type) { - menu_popup_pending_push = push; - menu_popup_current_type = type; - menu_popup_active = true; + menu_dialog_pending_push = push; + menu_dialog_current_type = type; + menu_dialog_active = true; } -void menu_popup_push(void) +void menu_dialog_push(void) { menu_displaylist_info_t info = {0}; - if (!menu_popup_is_push_pending()) + if (!menu_dialog_is_push_pending()) return; info.list = menu_entries_get_menu_stack_ptr(0); @@ -275,39 +275,39 @@ void menu_popup_push(void) menu_displaylist_ctl(DISPLAYLIST_HELP, &info); } -void menu_popup_reset(void) +void menu_dialog_reset(void) { - menu_popup_pending_push = false; - menu_popup_current_id = 0; - menu_popup_current_type = MENU_POPUP_NONE; - menu_popup_current_msg = MSG_UNKNOWN; + menu_dialog_pending_push = false; + menu_dialog_current_id = 0; + menu_dialog_current_type = MENU_DIALOG_NONE; + menu_dialog_current_msg = MSG_UNKNOWN; menu_display_toggle_set_reason(MENU_TOGGLE_REASON_NONE); } -void menu_popup_show_message( - enum menu_popup_type type, enum msg_hash_enums msg) +void menu_dialog_show_message( + enum menu_dialog_type type, enum msg_hash_enums msg) { - menu_popup_current_msg = msg; + menu_dialog_current_msg = msg; if (!menu_driver_ctl(RARCH_MENU_CTL_IS_TOGGLE, NULL)) menu_display_toggle_set_reason(MENU_TOGGLE_REASON_MESSAGE); - menu_popup_push_pending(true, type); - menu_popup_push(); + menu_dialog_push_pending(true, type); + menu_dialog_push(); } -bool menu_popup_is_active(void) +bool menu_dialog_is_active(void) { - return menu_popup_active; + return menu_dialog_active; } -void menu_popup_set_active(bool on) +void menu_dialog_set_active(bool on) { - menu_popup_active = on; + menu_dialog_active = on; } -enum menu_popup_type menu_popup_get_current_type(void) +enum menu_dialog_type menu_dialog_get_current_type(void) { - return menu_popup_current_type; + return menu_dialog_current_type; } diff --git a/menu/widgets/menu_dialog.h b/menu/widgets/menu_dialog.h new file mode 100644 index 0000000000..90c9eb80ba --- /dev/null +++ b/menu/widgets/menu_dialog.h @@ -0,0 +1,76 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef _MENU_DIALOG_H +#define _MENU_DIALOG_H + +#include +#include + +#include + +#include + +#include "../../msg_hash.h" + +enum menu_dialog_type +{ + MENU_DIALOG_NONE = 0, + MENU_DIALOG_WELCOME, + MENU_DIALOG_HELP_EXTRACT, + MENU_DIALOG_HELP_CONTROLS, + MENU_DIALOG_HELP_CHEEVOS_DESCRIPTION, + MENU_DIALOG_HELP_LOADING_CONTENT, + MENU_DIALOG_HELP_WHAT_IS_A_CORE, + MENU_DIALOG_HELP_CHANGE_VIRTUAL_GAMEPAD, + MENU_DIALOG_HELP_AUDIO_VIDEO_TROUBLESHOOTING, + MENU_DIALOG_HELP_SCANNING_CONTENT, + MENU_DIALOG_QUIT_CONFIRM, + MENU_DIALOG_INFORMATION, + MENU_DIALOG_QUESTION, + MENU_DIALOG_WARNING, + MENU_DIALOG_ERROR, + MENU_DIALOG_LAST +}; + +RETRO_BEGIN_DECLS + +void menu_dialog_push_pending( + bool push, enum menu_dialog_type type); + +int menu_dialog_iterate( + char *s, size_t len, const char *label); + +void menu_dialog_unset_pending_push(void); + +bool menu_dialog_is_push_pending(void); + +void menu_dialog_push(void); + +void menu_dialog_reset(void); + +void menu_dialog_show_message( + enum menu_dialog_type type, enum msg_hash_enums msg); + +bool menu_dialog_is_active(void); + +void menu_dialog_set_active(bool on); + +enum menu_dialog_type menu_dialog_get_current_type(void); + +RETRO_END_DECLS + +#endif diff --git a/menu/menu_entry.c b/menu/widgets/menu_entry.c similarity index 97% rename from menu/menu_entry.c rename to menu/widgets/menu_entry.c index 10909da6be..77012117f3 100644 --- a/menu/menu_entry.c +++ b/menu/widgets/menu_entry.c @@ -18,11 +18,10 @@ #include #include -#include "menu_driver.h" -#include "menu_display.h" -#include "menu_navigation.h" +#include "menu_input_dialog.h" -#include "../setting_list.h" +#include "../menu_driver.h" +#include "../menu_navigation.h" /* This file provides an abstraction of the currently displayed * menu. @@ -234,10 +233,12 @@ void menu_entry_reset(uint32_t i) menu_entry_action(&entry, i, MENU_ACTION_START); } -void menu_entry_get_value(uint32_t i, char *s, size_t len) +void menu_entry_get_value(uint32_t i, void *data, char *s, size_t len) { + file_list_t *list = (file_list_t*)data; menu_entry_t entry = {{0}}; - menu_entry_get(&entry, 0, i, NULL, true); + + menu_entry_get(&entry, 0, i, list, true); strlcpy(s, entry.value, len); } @@ -405,7 +406,7 @@ int menu_entry_action(menu_entry_t *entry, unsigned i, enum menu_action action) entry->label, entry->type, i); break; case MENU_ACTION_SEARCH: - menu_input_ctl(MENU_INPUT_CTL_SEARCH_START, NULL); + menu_input_dialog_start_search(); break; case MENU_ACTION_SCAN: diff --git a/menu/menu_entry.h b/menu/widgets/menu_entry.h similarity index 95% rename from menu/menu_entry.h rename to menu/widgets/menu_entry.h index 7722b4eba0..7fe41cbe8c 100644 --- a/menu/menu_entry.h +++ b/menu/widgets/menu_entry.h @@ -22,7 +22,9 @@ #include #include -#include "menu_input.h" +#include "../../msg_hash.h" + +#include "../menu_input.h" RETRO_BEGIN_DECLS @@ -92,7 +94,7 @@ void menu_entry_reset(uint32_t i); void menu_entry_get_rich_label(uint32_t i, char *s, size_t len); -void menu_entry_get_value(uint32_t i, char *s, size_t len); +void menu_entry_get_value(uint32_t i, void *data, char *s, size_t len); void menu_entry_set_value(uint32_t i, const char *s); diff --git a/menu/widgets/menu_input_bind_dialog.c b/menu/widgets/menu_input_bind_dialog.c new file mode 100644 index 0000000000..109c2cccb9 --- /dev/null +++ b/menu/widgets/menu_input_bind_dialog.c @@ -0,0 +1,445 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +#include + +#include "menu_input_bind_dialog.h" + +#include "../menu_driver.h" +#include "../menu_navigation.h" + +#include "../../input/input_config.h" + +#include "../../configuration.h" + +#define MENU_MAX_BUTTONS 219 +#define MENU_MAX_AXES 32 +#define MENU_MAX_HATS 4 + +struct menu_bind_state_port +{ + bool buttons[MENU_MAX_BUTTONS]; + int16_t axes[MENU_MAX_AXES]; + uint16_t hats[MENU_MAX_HATS]; +}; + +struct menu_bind_axis_state +{ + /* Default axis state. */ + int16_t rested_axes[MENU_MAX_AXES]; + /* Locked axis state. If we configured an axis, + * avoid having the same axis state trigger something again right away. */ + int16_t locked_axes[MENU_MAX_AXES]; +}; + +struct menu_bind_state +{ + struct retro_keybind *target; + /* For keyboard binding. */ + int64_t timeout_end; + unsigned begin; + unsigned last; + unsigned user; + struct menu_bind_state_port state[MAX_USERS]; + struct menu_bind_axis_state axis_state[MAX_USERS]; + bool skip; +}; + +static unsigned menu_bind_port = 0; +static struct menu_bind_state menu_input_binds; + +static bool menu_input_key_bind_custom_bind_keyboard_cb( + void *data, unsigned code) +{ + settings_t *settings = config_get_ptr(); + + menu_input_binds.target->key = (enum retro_key)code; + menu_input_binds.begin++; + menu_input_binds.target++; + menu_input_binds.timeout_end = cpu_features_get_time_usec() + + settings->input.bind_timeout * 1000000; + + return (menu_input_binds.begin <= menu_input_binds.last); +} + +static int menu_input_key_bind_set_mode_common( + enum menu_input_binds_ctl_state state, + rarch_setting_t *setting) +{ + size_t selection; + unsigned bind_type = 0; + menu_displaylist_info_t info = {0}; + struct retro_keybind *keybind = NULL; + settings_t *settings = config_get_ptr(); + unsigned index_offset = setting_get_index_offset(setting); + file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + + menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection); + + switch (state) + { + case MENU_INPUT_BINDS_CTL_BIND_SINGLE: + keybind = (struct retro_keybind*)setting_get_ptr(setting); + + if (!keybind) + return -1; + + bind_type = setting_get_bind_type(setting); + + menu_input_binds.begin = bind_type; + menu_input_binds.last = bind_type; + menu_input_binds.target = keybind; + menu_input_binds.user = index_offset; + + info.list = menu_stack; + info.type = MENU_SETTINGS_CUSTOM_BIND_KEYBOARD; + info.directory_ptr = selection; + info.enum_idx = MENU_ENUM_LABEL_CUSTOM_BIND; + strlcpy(info.label, + msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND), sizeof(info.label)); + + if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info)) + menu_displaylist_ctl(DISPLAYLIST_PROCESS, &info); + break; + case MENU_INPUT_BINDS_CTL_BIND_ALL: + menu_input_binds.target = &settings->input.binds + [index_offset][0]; + menu_input_binds.begin = MENU_SETTINGS_BIND_BEGIN; + menu_input_binds.last = MENU_SETTINGS_BIND_LAST; + + info.list = menu_stack; + info.type = MENU_SETTINGS_CUSTOM_BIND_KEYBOARD; + info.directory_ptr = selection; + info.enum_idx = MENU_ENUM_LABEL_CUSTOM_BIND_ALL; + strlcpy(info.label, + msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND_ALL), + sizeof(info.label)); + + if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info)) + menu_displaylist_ctl(DISPLAYLIST_PROCESS, &info); + break; + default: + case MENU_INPUT_BINDS_CTL_BIND_NONE: + break; + } + + return 0; +} + +static void menu_input_key_bind_poll_bind_get_rested_axes( + struct menu_bind_state *state, unsigned port) +{ + unsigned a; + const input_device_driver_t *joypad = + input_driver_get_joypad_driver(); + const input_device_driver_t *sec_joypad = + input_driver_get_sec_joypad_driver(); + + if (!state || !joypad) + return; + + /* poll only the relevant port */ + for (a = 0; a < MENU_MAX_AXES; a++) + state->axis_state[port].rested_axes[a] = + input_joypad_axis_raw(joypad, port, a); + + if (sec_joypad) + { + /* poll only the relevant port */ + for (a = 0; a < MENU_MAX_AXES; a++) + state->axis_state[port].rested_axes[a] = + input_joypad_axis_raw(sec_joypad, port, a); + } +} + +static void menu_input_key_bind_poll_bind_state_internal( + const input_device_driver_t *joypad, + struct menu_bind_state *state, + unsigned port, + bool timed_out) +{ + unsigned b, a, h; + if (!joypad) + return; + + if (joypad->poll) + joypad->poll(); + + /* poll only the relevant port */ + for (b = 0; b < MENU_MAX_BUTTONS; b++) + state->state[port].buttons[b] = + input_joypad_button_raw(joypad, port, b); + + for (a = 0; a < MENU_MAX_AXES; a++) + state->state[port].axes[a] = + input_joypad_axis_raw(joypad, port, a); + + for (h = 0; h < MENU_MAX_HATS; h++) + { + if (input_joypad_hat_raw(joypad, port, HAT_UP_MASK, h)) + state->state[port].hats[h] |= HAT_UP_MASK; + if (input_joypad_hat_raw(joypad, port, HAT_DOWN_MASK, h)) + state->state[port].hats[h] |= HAT_DOWN_MASK; + if (input_joypad_hat_raw(joypad, port, HAT_LEFT_MASK, h)) + state->state[port].hats[h] |= HAT_LEFT_MASK; + if (input_joypad_hat_raw(joypad, port, HAT_RIGHT_MASK, h)) + state->state[port].hats[h] |= HAT_RIGHT_MASK; + } +} + +static void menu_input_key_bind_poll_bind_state( + struct menu_bind_state *state, + unsigned port, + bool timed_out) +{ + const input_device_driver_t *joypad = + input_driver_get_joypad_driver(); + const input_device_driver_t *sec_joypad = + input_driver_get_sec_joypad_driver(); + + if (!state) + return; + + memset(state->state, 0, sizeof(state->state)); + state->skip = timed_out || input_driver_state(NULL, 0, + RETRO_DEVICE_KEYBOARD, 0, RETROK_RETURN); + + menu_input_key_bind_poll_bind_state_internal( + joypad, state, port, timed_out); + + if (sec_joypad) + menu_input_key_bind_poll_bind_state_internal( + sec_joypad, state, port, timed_out); +} + +bool menu_input_key_bind_set_mode( + enum menu_input_binds_ctl_state state, void *data) +{ + unsigned index_offset; + input_keyboard_ctx_wait_t keys; + menu_handle_t *menu = NULL; + rarch_setting_t *setting = (rarch_setting_t*)data; + settings_t *settings = config_get_ptr(); + + if (!setting) + return false; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return false; + if (menu_input_key_bind_set_mode_common(state, setting) == -1) + return false; + + index_offset = setting_get_index_offset(setting); + menu_bind_port = settings->input.joypad_map[index_offset]; + + menu_input_key_bind_poll_bind_get_rested_axes( + &menu_input_binds, menu_bind_port); + menu_input_key_bind_poll_bind_state( + &menu_input_binds, menu_bind_port, false); + + menu_input_binds.timeout_end = cpu_features_get_time_usec() + + settings->input.bind_timeout * 1000000; + + keys.userdata = menu; + keys.cb = menu_input_key_bind_custom_bind_keyboard_cb; + + input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_START_WAIT_KEYS, &keys); + return true; +} + +static bool menu_input_key_bind_poll_find_trigger_pad( + struct menu_bind_state *state, + struct menu_bind_state *new_state, + unsigned p) +{ + unsigned a, b, h; + const struct menu_bind_state_port *n = (const struct menu_bind_state_port*) + &new_state->state[p]; + const struct menu_bind_state_port *o = (const struct menu_bind_state_port*) + &state->state[p]; + + for (b = 0; b < MENU_MAX_BUTTONS; b++) + { + bool iterate = n->buttons[b] && !o->buttons[b]; + + if (!iterate) + continue; + + state->target->joykey = b; + state->target->joyaxis = AXIS_NONE; + return true; + } + + /* Axes are a bit tricky ... */ + for (a = 0; a < MENU_MAX_AXES; a++) + { + int locked_distance = abs(n->axes[a] - + new_state->axis_state[p].locked_axes[a]); + int rested_distance = abs(n->axes[a] - + new_state->axis_state[p].rested_axes[a]); + + if (abs(n->axes[a]) >= 20000 && + locked_distance >= 20000 && + rested_distance >= 20000) + { + /* Take care of case where axis rests on +/- 0x7fff + * (e.g. 360 controller on Linux) */ + state->target->joyaxis = n->axes[a] > 0 + ? AXIS_POS(a) : AXIS_NEG(a); + state->target->joykey = NO_BTN; + + /* Lock the current axis */ + new_state->axis_state[p].locked_axes[a] = + n->axes[a] > 0 ? + 0x7fff : -0x7fff; + return true; + } + + if (locked_distance >= 20000) /* Unlock the axis. */ + new_state->axis_state[p].locked_axes[a] = 0; + } + + for (h = 0; h < MENU_MAX_HATS; h++) + { + uint16_t trigged = n->hats[h] & (~o->hats[h]); + uint16_t sane_trigger = 0; + + if (trigged & HAT_UP_MASK) + sane_trigger = HAT_UP_MASK; + else if (trigged & HAT_DOWN_MASK) + sane_trigger = HAT_DOWN_MASK; + else if (trigged & HAT_LEFT_MASK) + sane_trigger = HAT_LEFT_MASK; + else if (trigged & HAT_RIGHT_MASK) + sane_trigger = HAT_RIGHT_MASK; + + if (sane_trigger) + { + state->target->joykey = HAT_MAP(h, sane_trigger); + state->target->joyaxis = AXIS_NONE; + return true; + } + } + + return false; +} + +static bool menu_input_key_bind_poll_find_trigger( + struct menu_bind_state *state, + struct menu_bind_state *new_state) +{ + unsigned i; + settings_t *settings = config_get_ptr(); + + if (!state || !new_state) + return false; + + for (i = 0; i < settings->input.max_users; i++) + { + if (!menu_input_key_bind_poll_find_trigger_pad( + state, new_state, i)) + continue; + + return true; + } + + return false; +} + +bool menu_input_key_bind_set_min_max(menu_input_ctx_bind_limits_t *lim) +{ + if (!lim) + return false; + + menu_input_binds.begin = lim->min; + menu_input_binds.last = lim->max; + + return true; +} + +bool menu_input_key_bind_iterate(menu_input_ctx_bind_t *bind) +{ + struct menu_bind_state binds; + bool timed_out = false; + settings_t *settings = config_get_ptr(); + int64_t current = cpu_features_get_time_usec(); + int timeout = + (menu_input_binds.timeout_end - current) / 1000000; + + if (!bind) + return false; + + if (timeout <= 0) + { + input_driver_keyboard_mapping_set_block(false); + + menu_input_binds.begin++; + menu_input_binds.target++; + menu_input_binds.timeout_end = cpu_features_get_time_usec() + + settings->input.bind_timeout * 1000000; + timed_out = true; + } + + snprintf(bind->s, bind->len, + "[%s]\npress keyboard or joypad\n(timeout %d %s)", + input_config_bind_map_get_desc( + menu_input_binds.begin - MENU_SETTINGS_BIND_BEGIN), + timeout, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SECONDS)); + + /* binds.begin is updated in keyboard_press callback. */ + if (menu_input_binds.begin > menu_input_binds.last) + { + /* Avoid new binds triggering things right away. */ + input_driver_set_flushing_input(); + + /* We won't be getting any key events, so just cancel early. */ + if (timed_out) + input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_CANCEL_WAIT_KEYS, NULL); + + return true; + } + + binds = menu_input_binds; + + input_driver_keyboard_mapping_set_block(true); + menu_input_key_bind_poll_bind_state(&binds, menu_bind_port, timed_out); + + if ((binds.skip && !menu_input_binds.skip) || + menu_input_key_bind_poll_find_trigger(&menu_input_binds, &binds)) + { + input_driver_keyboard_mapping_set_block(false); + + /* Avoid new binds triggering things right away. */ + input_driver_set_flushing_input(); + + binds.begin++; + + if (binds.begin > binds.last) + { + input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_CANCEL_WAIT_KEYS, NULL); + return true; + } + + binds.target++; + binds.timeout_end = cpu_features_get_time_usec() + + settings->input.bind_timeout * 1000000; + } + menu_input_binds = binds; + + return false; +} diff --git a/menu/widgets/menu_input_bind_dialog.h b/menu/widgets/menu_input_bind_dialog.h new file mode 100644 index 0000000000..58c08fcb16 --- /dev/null +++ b/menu/widgets/menu_input_bind_dialog.h @@ -0,0 +1,59 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef _MENU_INPUT_BIND_DIALOG_H +#define _MENU_INPUT_BIND_DIALOG_H + +#include +#include + +#include + +#include + +#include "../menu_input.h" + +RETRO_BEGIN_DECLS + +enum menu_input_binds_ctl_state +{ + MENU_INPUT_BINDS_CTL_BIND_NONE = 0, + MENU_INPUT_BINDS_CTL_BIND_SINGLE, + MENU_INPUT_BINDS_CTL_BIND_ALL +}; + +typedef struct menu_input_ctx_bind +{ + char *s; + size_t len; +} menu_input_ctx_bind_t; + +typedef struct menu_input_ctx_bind_limits +{ + unsigned min; + unsigned max; +} menu_input_ctx_bind_limits_t; + +bool menu_input_key_bind_set_mode( + enum menu_input_binds_ctl_state state, void *data); + +bool menu_input_key_bind_set_min_max(menu_input_ctx_bind_limits_t *lim); + +bool menu_input_key_bind_iterate(menu_input_ctx_bind_t *bind); + +RETRO_END_DECLS + +#endif diff --git a/menu/widgets/menu_input_dialog.c b/menu/widgets/menu_input_dialog.c new file mode 100644 index 0000000000..f0356d58f7 --- /dev/null +++ b/menu/widgets/menu_input_dialog.c @@ -0,0 +1,136 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +#include "menu_input_dialog.h" + +#include "../menu_driver.h" +#include "../menu_navigation.h" +#include "../../input/input_driver.h" + +static const char **menu_input_dialog_keyboard_buffer = {NULL}; +static bool menu_input_dialog_keyboard_display = false; +static unsigned menu_input_dialog_keyboard_type = 0; +static unsigned menu_input_dialog_keyboard_idx = 0; +static char menu_input_dialog_keyboard_label_setting[256] = {0}; +static char menu_input_dialog_keyboard_label[256] = {0}; + +static void menu_input_search_cb(void *userdata, const char *str) +{ + size_t idx = 0; + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + + if (!selection_buf) + return; + + if (str && *str && file_list_search(selection_buf, str, &idx)) + { + bool scroll = true; + menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &idx); + menu_navigation_ctl(MENU_NAVIGATION_CTL_SET, &scroll); + } + + menu_input_dialog_end(); +} + +const char *menu_input_dialog_get_label_buffer(void) +{ + return menu_input_dialog_keyboard_label; +} + +const char *menu_input_dialog_get_label_setting_buffer(void) +{ + return menu_input_dialog_keyboard_label_setting; +} + +void menu_input_dialog_end(void) +{ + menu_input_dialog_keyboard_type = 0; + menu_input_dialog_keyboard_idx = 0; + menu_input_dialog_keyboard_display = false; + menu_input_dialog_keyboard_label[0] = '\0'; + menu_input_dialog_keyboard_label_setting[0] = '\0'; + + /* Avoid triggering states on pressing return. */ + input_driver_set_flushing_input(); +} + +const char *menu_input_dialog_get_buffer(void) +{ + if (!(*menu_input_dialog_keyboard_buffer)) + return ""; + return *menu_input_dialog_keyboard_buffer; +} + +unsigned menu_input_dialog_get_kb_type(void) +{ + return menu_input_dialog_keyboard_type; +} + +bool menu_input_dialog_get_display_kb(void) +{ + return menu_input_dialog_keyboard_display; +} + +void menu_input_dialog_display_kb(void) +{ + menu_input_dialog_keyboard_display = true; +} + +void menu_input_dialog_hide_kb(void) +{ + menu_input_dialog_keyboard_display = false; +} + +bool menu_input_dialog_start_search(void) +{ + menu_handle_t *menu = NULL; + + if (!menu_driver_ctl( + RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return false; + + menu_input_dialog_display_kb(); + strlcpy(menu_input_dialog_keyboard_label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH), + sizeof(menu_input_dialog_keyboard_label)); + menu_input_dialog_keyboard_buffer = + input_keyboard_start_line(menu, menu_input_search_cb); + + return true; +} + +bool menu_input_dialog_start(menu_input_ctx_line_t *line) +{ + menu_handle_t *menu = NULL; + if (!line) + return false; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return false; + + menu_input_dialog_display_kb(); + strlcpy(menu_input_dialog_keyboard_label, line->label, + sizeof(menu_input_dialog_keyboard_label)); + strlcpy(menu_input_dialog_keyboard_label_setting, + line->label_setting, sizeof(menu_input_dialog_keyboard_label_setting)); + + menu_input_dialog_keyboard_type = line->type; + menu_input_dialog_keyboard_idx = line->idx; + menu_input_dialog_keyboard_buffer = + input_keyboard_start_line(menu, line->cb); + + return true; +} diff --git a/menu/widgets/menu_input_dialog.h b/menu/widgets/menu_input_dialog.h new file mode 100644 index 0000000000..bfdc1bdc04 --- /dev/null +++ b/menu/widgets/menu_input_dialog.h @@ -0,0 +1,62 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef _MENU_INPUT_DIALOG_H +#define _MENU_INPUT_DIALOG_H + +#include +#include + +#include + +#include + +#include "../../input/input_keyboard.h" + +RETRO_BEGIN_DECLS + +typedef struct menu_input_ctx_line +{ + const char *label; + const char *label_setting; + unsigned type; + unsigned idx; + input_keyboard_line_complete_t cb; +} menu_input_ctx_line_t; + +const char *menu_input_dialog_get_label_setting_buffer(void); + +const char *menu_input_dialog_get_label_buffer(void); + +const char *menu_input_dialog_get_buffer(void); + +unsigned menu_input_dialog_get_kb_type(void); + +bool menu_input_dialog_start_search(void); + +void menu_input_dialog_hide_kb(void); + +void menu_input_dialog_display_kb(void); + +bool menu_input_dialog_get_display_kb(void); + +bool menu_input_dialog_start(menu_input_ctx_line_t *line); + +void menu_input_dialog_end(void); + +RETRO_END_DECLS + +#endif diff --git a/menu/widgets/menu_list.c b/menu/widgets/menu_list.c new file mode 100644 index 0000000000..dc583aa324 --- /dev/null +++ b/menu/widgets/menu_list.c @@ -0,0 +1,213 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +#include + +#include "menu_list.h" + +#include "../menu_driver.h" +#include "../menu_navigation.h" + +struct menu_list +{ + file_list_t **menu_stack; + size_t menu_stack_size; + file_list_t **selection_buf; + size_t selection_buf_size; +}; + +void menu_list_free_list(file_list_t *list) +{ + unsigned i; + + for (i = 0; i < list->size; i++) + { + menu_ctx_list_t list_info; + + list_info.list = list; + list_info.idx = i; + list_info.list_size = list->size; + + menu_driver_ctl(RARCH_MENU_CTL_LIST_FREE, &list_info); + } + + file_list_free(list); +} + +void menu_list_free(menu_list_t *menu_list) +{ + unsigned i; + if (!menu_list) + return; + + for (i = 0; i < menu_list->menu_stack_size; i++) + { + if (!menu_list->menu_stack[i]) + continue; + + menu_list_free_list(menu_list->menu_stack[i]); + menu_list->menu_stack[i] = NULL; + } + for (i = 0; i < menu_list->selection_buf_size; i++) + { + if (!menu_list->selection_buf[i]) + continue; + + menu_list_free_list(menu_list->selection_buf[i]); + menu_list->selection_buf[i] = NULL; + } + + free(menu_list->menu_stack); + free(menu_list->selection_buf); + + free(menu_list); +} + +menu_list_t *menu_list_new(void) +{ + unsigned i; + menu_list_t *list = (menu_list_t*)calloc(1, sizeof(*list)); + + if (!list) + return NULL; + + list->menu_stack = (file_list_t**)calloc(1, sizeof(*list->menu_stack)); + + if (!list->menu_stack) + goto error; + + list->selection_buf = (file_list_t**)calloc(1, sizeof(*list->selection_buf)); + + if (!list->selection_buf) + goto error; + + list->menu_stack_size = 1; + list->selection_buf_size = 1; + + for (i = 0; i < list->menu_stack_size; i++) + list->menu_stack[i] = (file_list_t*)calloc(1, sizeof(*list->menu_stack[i])); + + for (i = 0; i < list->selection_buf_size; i++) + list->selection_buf[i] = (file_list_t*)calloc(1, sizeof(*list->selection_buf[i])); + + return list; + +error: + menu_list_free(list); + return NULL; +} + +file_list_t *menu_list_get(menu_list_t *list, unsigned idx) +{ + if (!list) + return NULL; + return list->menu_stack[idx]; +} + +file_list_t *menu_list_get_selection(menu_list_t *list, unsigned idx) +{ + if (!list) + return NULL; + return list->selection_buf[idx]; +} + +size_t menu_list_get_stack_size(menu_list_t *list, size_t idx) +{ + if (!list) + return 0; + return file_list_get_size(list->menu_stack[idx]); +} + +static int menu_list_flush_stack_type(const char *needle, const char *label, + unsigned type, unsigned final_type) +{ + return needle ? !string_is_equal(needle, label) : (type != final_type); +} + +void menu_list_flush_stack(menu_list_t *list, + size_t idx, const char *needle, unsigned final_type) +{ + bool refresh = false; + const char *path = NULL; + const char *label = NULL; + unsigned type = 0; + size_t entry_idx = 0; + file_list_t *menu_list = menu_list_get(list, idx); + if (!list) + return; + + menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); + menu_entries_get_last(menu_list, + &path, &label, &type, &entry_idx); + + while (menu_list_flush_stack_type( + needle, label, type, final_type) != 0) + { + size_t new_selection_ptr; + + menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, + &new_selection_ptr); + + if (!menu_list_pop_stack(list, idx, &new_selection_ptr, 1)) + break; + + menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, + &new_selection_ptr); + + menu_list = menu_list_get(list, idx); + + menu_entries_get_last(menu_list, + &path, &label, &type, &entry_idx); + } +} + +bool menu_list_pop_stack(menu_list_t *list, + size_t idx, size_t *directory_ptr, bool animate) +{ + menu_ctx_list_t list_info; + bool refresh = false; + file_list_t *menu_list = menu_list_get(list, idx); + if (!list) + return false; + + if (menu_list_get_stack_size(list, idx) <= 1) + return false; + + list_info.type = MENU_LIST_PLAIN; + list_info.action = 0; + + if (animate) + menu_driver_ctl(RARCH_MENU_CTL_LIST_CACHE, &list_info); + + if (menu_list->size != 0) + { + menu_ctx_list_t list_info; + + list_info.list = menu_list; + list_info.idx = menu_list->size - 1; + list_info.list_size = menu_list->size - 1; + + menu_driver_ctl(RARCH_MENU_CTL_LIST_FREE, &list_info); + } + + file_list_pop(menu_list, directory_ptr); + menu_driver_ctl(RARCH_MENU_CTL_LIST_SET_SELECTION, menu_list); + if (animate) + menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); + + return true; +} diff --git a/menu/widgets/menu_list.h b/menu/widgets/menu_list.h new file mode 100644 index 0000000000..ebea133238 --- /dev/null +++ b/menu/widgets/menu_list.h @@ -0,0 +1,73 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef _MENU_WIDGETS_LIST_H +#define _MENU_WIDGETS_LIST_H + +#include +#include + +#include + +#include +#include + +RETRO_BEGIN_DECLS + +enum menu_list_type +{ + MENU_LIST_PLAIN = 0, + MENU_LIST_HORIZONTAL, + MENU_LIST_TABS +}; + +typedef struct menu_ctx_list +{ + file_list_t *list; + size_t list_size; + const char *path; + const char *label; + size_t idx; + enum menu_list_type type; + unsigned action; + size_t selection; + size_t size; + void *entry; +} menu_ctx_list_t; + +typedef struct menu_list menu_list_t; + +void menu_list_free_list(file_list_t *list); + +void menu_list_free(menu_list_t *menu_list); + +menu_list_t *menu_list_new(void); + +file_list_t *menu_list_get(menu_list_t *list, unsigned idx); + +file_list_t *menu_list_get_selection(menu_list_t *list, unsigned idx); + +size_t menu_list_get_stack_size(menu_list_t *list, size_t idx); + +void menu_list_flush_stack(menu_list_t *list, + size_t idx, const char *needle, unsigned final_type); + +bool menu_list_pop_stack(menu_list_t *list, + size_t idx, size_t *directory_ptr, bool animate); + +RETRO_END_DECLS + +#endif diff --git a/menu/widgets/menu_popup.h b/menu/widgets/menu_popup.h deleted file mode 100644 index 06f254783e..0000000000 --- a/menu/widgets/menu_popup.h +++ /dev/null @@ -1,76 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * Copyright (C) 2011-2016 - Daniel De Matteis - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#ifndef _MENU_POPUP_H -#define _MENU_POPUP_H - -#include -#include - -#include - -#include - -#include "../../msg_hash.h" - -enum menu_popup_type -{ - MENU_POPUP_NONE = 0, - MENU_POPUP_WELCOME, - MENU_POPUP_HELP_EXTRACT, - MENU_POPUP_HELP_CONTROLS, - MENU_POPUP_HELP_CHEEVOS_DESCRIPTION, - MENU_POPUP_HELP_LOADING_CONTENT, - MENU_POPUP_HELP_WHAT_IS_A_CORE, - MENU_POPUP_HELP_CHANGE_VIRTUAL_GAMEPAD, - MENU_POPUP_HELP_AUDIO_VIDEO_TROUBLESHOOTING, - MENU_POPUP_HELP_SCANNING_CONTENT, - MENU_POPUP_QUIT_CONFIRM, - MENU_POPUP_INFORMATION, - MENU_POPUP_QUESTION, - MENU_POPUP_WARNING, - MENU_POPUP_ERROR, - MENU_POPUP_LAST -}; - -RETRO_BEGIN_DECLS - -void menu_popup_push_pending( - bool push, enum menu_popup_type type); - -int menu_popup_iterate( - char *s, size_t len, const char *label); - -void menu_popup_unset_pending_push(void); - -bool menu_popup_is_push_pending(void); - -void menu_popup_push(void); - -void menu_popup_reset(void); - -void menu_popup_show_message( - enum menu_popup_type type, enum msg_hash_enums msg); - -bool menu_popup_is_active(void); - -void menu_popup_set_active(bool on); - -enum menu_popup_type menu_popup_get_current_type(void); - -RETRO_END_DECLS - -#endif diff --git a/movie.c b/movie.c index d7f0366660..7a5d2b97db 100644 --- a/movie.c +++ b/movie.c @@ -31,6 +31,9 @@ #include "msg_hash.h" #include "verbosity.h" +#include "command.h" +#include "file_path_special.h" + struct bsv_movie { FILE *file; @@ -458,3 +461,94 @@ bool bsv_movie_init_handle(const char *path, return false; return true; } + +/* Checks if movie is being played back. */ +static bool runloop_check_movie_playback(void) +{ + if (!bsv_movie_ctl(BSV_MOVIE_CTL_END, NULL)) + return false; + + runloop_msg_queue_push( + msg_hash_to_str(MSG_MOVIE_PLAYBACK_ENDED), 2, 180, false); + RARCH_LOG("%s\n", msg_hash_to_str(MSG_MOVIE_PLAYBACK_ENDED)); + + command_event(CMD_EVENT_BSV_MOVIE_DEINIT, NULL); + + bsv_movie_ctl(BSV_MOVIE_CTL_UNSET_END, NULL); + bsv_movie_ctl(BSV_MOVIE_CTL_UNSET_PLAYBACK, NULL); + + return true; +} + +/* Checks if movie is being recorded. */ +static bool runloop_check_movie_record(void) +{ + if (!bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) + return false; + + runloop_msg_queue_push( + msg_hash_to_str(MSG_MOVIE_RECORD_STOPPED), 2, 180, true); + RARCH_LOG("%s\n", msg_hash_to_str(MSG_MOVIE_RECORD_STOPPED)); + + command_event(CMD_EVENT_BSV_MOVIE_DEINIT, NULL); + + return true; +} + +static bool runloop_check_movie_init(void) +{ + char msg[128] = {0}; + char path[PATH_MAX_LENGTH] = {0}; + settings_t *settings = config_get_ptr(); + + if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) + return false; + + settings->rewind_granularity = 1; + + if (settings->state_slot > 0) + snprintf(path, sizeof(path), "%s%d", + bsv_movie_get_path(), settings->state_slot); + else + strlcpy(path, bsv_movie_get_path(), sizeof(path)); + + strlcat(path, + file_path_str(FILE_PATH_BSV_EXTENSION), + sizeof(path)); + + snprintf(msg, sizeof(msg), "%s \"%s\".", + msg_hash_to_str(MSG_STARTING_MOVIE_RECORD_TO), + path); + + bsv_movie_init_handle(path, RARCH_MOVIE_RECORD); + + if (!bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) + return false; + + if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) + { + runloop_msg_queue_push(msg, 2, 180, true); + RARCH_LOG("%s \"%s\".\n", + msg_hash_to_str(MSG_STARTING_MOVIE_RECORD_TO), + path); + } + else + { + runloop_msg_queue_push( + msg_hash_to_str(MSG_FAILED_TO_START_MOVIE_RECORD), + 2, 180, true); + RARCH_ERR("%s\n", + msg_hash_to_str(MSG_FAILED_TO_START_MOVIE_RECORD)); + } + + return true; +} + +bool bsv_movie_check(void) +{ + if (bsv_movie_ctl(BSV_MOVIE_CTL_PLAYBACK_ON, NULL)) + return runloop_check_movie_playback(); + if (!bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) + return runloop_check_movie_init(); + return runloop_check_movie_record(); +} diff --git a/movie.h b/movie.h index 91a40c218c..533b0889ae 100644 --- a/movie.h +++ b/movie.h @@ -77,6 +77,8 @@ void bsv_movie_set_start_path(const char *path); bool bsv_movie_ctl(enum bsv_ctl_state state, void *data); +bool bsv_movie_check(void); + bool bsv_movie_init_handle(const char *path, enum rarch_movie_type type); RETRO_END_DECLS diff --git a/msg_hash.c b/msg_hash.c index b9de0ef781..d2f1f3a0e8 100644 --- a/msg_hash.c +++ b/msg_hash.c @@ -198,6 +198,8 @@ uint32_t msg_hash_calculate(const char *s) #define FILE_HASH_APK 0x0b885e61U +#define HASH_EXTENSION_7Z 0x005971d6U +#define HASH_EXTENSION_7Z_UPP 0x005971b6U #define HASH_EXTENSION_ZIP 0x0b88c7d8U #define HASH_EXTENSION_ZIP_UPP 0x0b883b78U #define HASH_EXTENSION_CUE 0x0b886782U @@ -211,6 +213,8 @@ enum msg_file_type msg_hash_to_file_type(uint32_t hash) switch (hash) { case MENU_VALUE_COMP: + case HASH_EXTENSION_7Z: + case HASH_EXTENSION_7Z_UPP: case HASH_EXTENSION_ZIP: case HASH_EXTENSION_ZIP_UPP: case FILE_HASH_APK: diff --git a/msg_hash.h b/msg_hash.h index 2fcdbc3425..83b8eea021 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1111,6 +1111,8 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, MENU_ENUM_LABEL_NETPLAY_DELAY_FRAMES, MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, + MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES, + MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES, MENU_ENUM_LABEL_NETPLAY_SPECTATOR_MODE_ENABLE, MENU_ENUM_LABEL_VALUE_NETPLAY_SPECTATOR_MODE_ENABLE, MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT, diff --git a/httpserver/handle_form.inl b/network/httpserver/handle_form.inl similarity index 100% rename from httpserver/handle_form.inl rename to network/httpserver/handle_form.inl diff --git a/httpserver/httpserver.c b/network/httpserver/httpserver.c similarity index 94% rename from httpserver/httpserver.c rename to network/httpserver/httpserver.c index 5e0c44e55a..05efe15552 100644 --- a/httpserver/httpserver.c +++ b/network/httpserver/httpserver.c @@ -1,3 +1,18 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2015-2016 - Andre Leiradella + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + #define __STDC_FORMAT_MACROS #include #include @@ -9,19 +24,19 @@ #include #include -#include "../core.h" -#include "../runloop.h" -#include "../core.h" -#include "../gfx/video_driver.h" -#include "../managers/core_option_manager.h" -#include "../cheevos.h" -#include "../content.h" +#include "../../core.h" +#include "../../runloop.h" +#include "../../core.h" +#include "../../gfx/video_driver.h" +#include "../../managers/core_option_manager.h" +#include "../../cheevos.h" +#include "../../content.h" #define BASIC_INFO "info" #define MEMORY_MAP "memoryMap" static struct mg_callbacks s_httpserver_callbacks; -static struct mg_context* s_httpserver_ctx; +static struct mg_context *s_httpserver_ctx = NULL; /* Based on https://github.com/zeromq/rfc/blob/master/src/spec_32.c */ static void httpserver_z85_encode_inplace(Bytef* data, size_t size) diff --git a/network/httpserver/httpserver.h b/network/httpserver/httpserver.h new file mode 100644 index 0000000000..301d0d67eb --- /dev/null +++ b/network/httpserver/httpserver.h @@ -0,0 +1,29 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2015-2016 - Andre Leiradella + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __RARCH_HTTPSERVR_H +#define __RARCH_HTTPSERVR_H + +#include + +RETRO_BEGIN_DECLS + +int httpserver_init(unsigned port); + +void httpserver_destroy(void); + +RETRO_END_DECLS + +#endif /* __RARCH_HTTPSERVR_H */ diff --git a/network/netplay/README b/network/netplay/README new file mode 100644 index 0000000000..8f0ca71057 --- /dev/null +++ b/network/netplay/README @@ -0,0 +1,153 @@ +This is RetroArch's Netplay code. RetroArch Netplay allows a second player to +be connected via the Internet, rather than local at the same computer. Netplay +in RetroArch is guaranteed* to work with perfect synchronization given a few +minor constraints: + +(1) The core is deterministic, +(2) The only input devices the core interacts with are the joypad and analog sticks, and +(3) Both the core and the loaded content are identical on host and client. + +Furthermore, if the core supports serialization (save states), Netplay allows +for latency and clock drift, providing both host and client with a smooth +experience. + +Note that this documentation is all for (the poorly-named) "net" mode, which is +the normal mode, and not "spectator" mode, which has its own whole host of +problems. + +Netplay in RetroArch works by expecting input to come delayed from the network, +then rewinding and re-playing with the delayed input to get a consistent state. +So long as both sides agree on which frame is which, it should be impossible +for them to become de-synced, since each input event always happens at the +correct frame. + +In terms of the implementation, Netplay is in effect a state buffer +(implemented as a ring of buffers) and some pre- and post-frame behaviors. + +Within the state buffers, there are three locations: self, other and read. Each +refers to a frame, and a state buffer corresponding to that frame. The state +buffer contains the savestate for the frame, and the input from both the local +and remote players. + +Self is where the emulator believes itself to be, which may be ahead or behind +of what it's read from the peer. Generally speaking, self progresses at 1 frame +per frame, except when the network stalls, described later. + +Other is where it was most recently in perfect sync: i.e., other-1 is the last +frame from which both local and remote input have been actioned. As such, other +is always less than or equal to both self and read. Since the state buffer is a +ring, other is the first frame that it's unsafe to overwrite. + +Read is where it's read up to, which can be slightly ahead of other since it +can't always immediately act upon new data. + +In general, other ≤ read and other ≤ self. In all likelihood, read ≤ self, but +it is both possible and supported for the remote host to get ahead of the local +host. + +Pre-frame, Netplay serializes the core's state, polls for local input, and +polls for input from the other side. If the input from the other side is too +far behind, it stalls to allow the other side to catch up. To assure that this +stalling does not block the UI thread, it is implemented similarly to pausing, +rather than by blocking on the socket. + +If input has not been received for the other side up to the current frame (the +usual case), the remote input is simulated in a simplistic manner. Each +frame's local serialized state and simulated or real input goes into the frame +buffers. + +During the frame of execution, when the core requests input, it receives the +input from the state buffer, both local and real or simulated remote. + +Post-frame, it checks whether it's read more than it's actioned, i.e. if read > +other self > other. If so, it first checks whether its simulated remote data +was correct. If it was, it simply moves other up. If not, it rewinds to other +(by loading the serialized state there) and runs the core in replay mode with +the real data up to the least of self and read, then sets other to that. + +When in Netplay mode, the callback for receiving input is replaced by +input_state_net. It is the role of input_state_net to combine the true local +input (whether live or replay) with the remote input (whether true or +simulated). + +Some thoughts about "frame counts": The frame counters act like indexes into a +0-indexed array; i.e., they refer to the first unactioned frame. So, when +read_frame_count is 23, we've read 23 frames, but the last frame we read is +frame 22. With self_frame_count it's slightly more complicated, since there are +two relevant actions: Reading the data and emulating with the data. The frame +count is only incremented after the latter, so there is a period of time during +which we've actually read self_frame_count+1 frames of local input. + + +* Guarantee not actually a guarantee. + + +Netplay's command format + +Netplay commands consist of a 32-bit command identifier, followed by a 32-bit +payload size, both in network byte order, followed by a payload. The command +identifiers are listed in netplay.h. The commands are described below. Unless +specified otherwise, all payload values are in network byte order. + +Command: ACK +Payload: None +Description: + Acknowledgement. Not used. + +Command: NAK +Payload: None +Description: + Negative Acknowledgement. If received, the connection is terminated. Sent + whenever a command is malformed or otherwise not understood. + +Command: INPUT +Payload: + { + frame number: uint32 + joypad input: uint32 + analog 1 input: uint32 + analog 2 input: uint32 + OPTIONAL state CRC: uint32 + } +Description: + Input state for each frame. Netplay must send an INPUT command for every + frame in order to function at all. + +Command: FLIP_PLAYERS +Payload: + { + frame number: uint32 + } +Description: + Flip players at the requested frame. + +Command: DISCONNECT +Payload: None +Description: + Gracefully disconnect. Not used. + +Command: CRC +Payload: + { + frame number: uint32 + hash: uint32 + } +Description: + Informs the peer of the correct CRC hash for the specified frame. If the + receiver's hash doesn't match, they should send a REQUEST_SAVESTATE + command. + +Command: REQUEST_SAVESTATE +Payload: None +Description: + Requests that the peer send a savestate. + +Command: LOAD_SAVESTATE +Payload: + { + frame number: uint32 + serialized save state: blob (variable size) + } +Description: + Cause the other side to load a savestate, notionally one which the sending + side has also loaded. diff --git a/network/netplay/netplay.c b/network/netplay/netplay.c index a2a574ac52..2955c660a8 100644 --- a/network/netplay/netplay.c +++ b/network/netplay/netplay.c @@ -1,6 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2016 - Daniel De Matteis + * Copyright (C) 2016 - Gregor Richards * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- @@ -25,6 +26,7 @@ #include #include #include +#include #include #include "netplay_private.h" @@ -43,7 +45,7 @@ enum CMD_OPT_REQUIRE_SYNC = 0x10 }; -void *netplay_data; +static void *netplay_data = NULL; /** * warn_hangup: @@ -56,19 +58,8 @@ static void warn_hangup(void) runloop_msg_queue_push("Netplay has disconnected. Will continue without connection.", 0, 480, false); } -/** - * check_netplay_synched: - * @netplay: pointer to the netplay object. - * Checks to see if the host and client have synchronized states. Returns true - * on success and false on failure. - */ -bool check_netplay_synched(netplay_t* netplay) +static bool netplay_info_cb(netplay_t* netplay, unsigned frames) { - retro_assert(netplay); - return netplay->frame_count < (netplay->flip_frame + 2 * UDP_FRAME_PACKETS); -} - -static bool netplay_info_cb(netplay_t* netplay, unsigned frames) { return netplay->net_cbs->info_cb(netplay, frames); } @@ -96,39 +87,6 @@ static bool netplay_can_poll(netplay_t *netplay) return netplay->can_poll; } -static bool send_chunk(netplay_t *netplay) -{ - const struct sockaddr *addr = NULL; - - if (netplay->addr) - addr = netplay->addr->ai_addr; - else if (netplay->has_client_addr) - addr = (const struct sockaddr*)&netplay->their_addr; - - if (addr) - { - ssize_t bytes_sent; - -#ifdef HAVE_IPV6 - bytes_sent = (sendto(netplay->udp_fd, (const char*)netplay->packet_buffer, - sizeof(netplay->packet_buffer), 0, addr, - sizeof(struct sockaddr_in6))); -#else - bytes_sent = (sendto(netplay->udp_fd, (const char*)netplay->packet_buffer, - sizeof(netplay->packet_buffer), 0, addr, - sizeof(struct sockaddr_in))); -#endif - - if (bytes_sent != sizeof(netplay->packet_buffer)) - { - warn_hangup(); - netplay->has_connection = false; - return false; - } - } - return true; -} - /** * get_self_input_state: * @netplay : pointer to netplay object @@ -139,10 +97,19 @@ static bool send_chunk(netplay_t *netplay) **/ static bool get_self_input_state(netplay_t *netplay) { - uint32_t state[UDP_WORDS_PER_FRAME - 1] = {0}; - struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr]; + uint32_t state[WORDS_PER_FRAME - 1] = {0, 0, 0}; + struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr]; - if (!input_driver_is_libretro_input_blocked() && netplay->frame_count > 0) + if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count)) + return false; + + if (ptr->have_local) + { + /* We've already read this frame! */ + return true; + } + + if (!input_driver_is_libretro_input_blocked() && netplay->self_frame_count > 0) { unsigned i; settings_t *settings = config_get_ptr(); @@ -179,66 +146,143 @@ static bool get_self_input_state(netplay_t *netplay) * } * * payload { - * ; To compat packet losses, send input in a sliding window - * frame redundancy_frames[UDP_FRAME_PACKETS]; + * cmd (CMD_INPUT) + * cmd_size (4 words) + * frame * } */ - memmove(netplay->packet_buffer, netplay->packet_buffer + UDP_WORDS_PER_FRAME, - sizeof (netplay->packet_buffer) - UDP_WORDS_PER_FRAME * sizeof(uint32_t)); - netplay->packet_buffer[(UDP_FRAME_PACKETS - 1) * UDP_WORDS_PER_FRAME] = htonl(netplay->frame_count); - netplay->packet_buffer[(UDP_FRAME_PACKETS - 1) * UDP_WORDS_PER_FRAME + 1] = htonl(state[0]); - netplay->packet_buffer[(UDP_FRAME_PACKETS - 1) * UDP_WORDS_PER_FRAME + 2] = htonl(state[1]); - netplay->packet_buffer[(UDP_FRAME_PACKETS - 1) * UDP_WORDS_PER_FRAME + 3] = htonl(state[2]); + netplay->packet_buffer[0] = htonl(NETPLAY_CMD_INPUT); + netplay->packet_buffer[1] = htonl(WORDS_PER_FRAME * sizeof(uint32_t)); + netplay->packet_buffer[2] = htonl(netplay->self_frame_count); + netplay->packet_buffer[3] = htonl(state[0]); + netplay->packet_buffer[4] = htonl(state[1]); + netplay->packet_buffer[5] = htonl(state[2]); - if (!send_chunk(netplay)) + if (!netplay->spectate.enabled) /* Spectate sends in its own way */ { - warn_hangup(); - netplay->has_connection = false; - return false; + if (!socket_send_all_blocking(netplay->fd, + netplay->packet_buffer, sizeof(netplay->packet_buffer), false)) + { + warn_hangup(); + netplay->has_connection = false; + return false; + } } memcpy(ptr->self_state, state, sizeof(state)); - netplay->self_ptr = NEXT_PTR(netplay->self_ptr); + ptr->have_local = true; return true; } -static bool netplay_cmd_ack(netplay_t *netplay) +static bool netplay_send_raw_cmd(netplay_t *netplay, uint32_t cmd, + const void *data, size_t size) { - uint32_t cmd = htonl(NETPLAY_CMD_ACK); - return socket_send_all_blocking(netplay->fd, &cmd, sizeof(cmd), false); + uint32_t cmdbuf[2]; + + cmdbuf[0] = htonl(cmd); + cmdbuf[1] = htonl(size); + + if (!socket_send_all_blocking(netplay->fd, cmdbuf, sizeof(cmdbuf), false)) + return false; + + if (size > 0) + if (!socket_send_all_blocking(netplay->fd, data, size, false)) + return false; + + return true; } static bool netplay_cmd_nak(netplay_t *netplay) { - uint32_t cmd = htonl(NETPLAY_CMD_NAK); - return socket_send_all_blocking(netplay->fd, &cmd, sizeof(cmd), false); + return netplay_send_raw_cmd(netplay, NETPLAY_CMD_NAK, NULL, 0); } -static bool netplay_get_response(netplay_t *netplay) +bool netplay_cmd_crc(netplay_t *netplay, struct delta_frame *delta) { - uint32_t response; - if (!socket_receive_all_blocking(netplay->fd, &response, sizeof(response))) - return false; + uint32_t payload[2]; + payload[0] = htonl(delta->frame); + payload[1] = htonl(delta->crc); + return netplay_send_raw_cmd(netplay, NETPLAY_CMD_CRC, payload, sizeof(payload)); +} - return ntohl(response) == NETPLAY_CMD_ACK; +bool netplay_cmd_request_savestate(netplay_t *netplay) +{ + if (netplay->savestate_request_outstanding) + return true; + netplay->savestate_request_outstanding = true; + return netplay_send_raw_cmd(netplay, NETPLAY_CMD_REQUEST_SAVESTATE, NULL, 0); } static bool netplay_get_cmd(netplay_t *netplay) { uint32_t cmd; uint32_t flip_frame; - size_t cmd_size; + uint32_t cmd_size; + + /* FIXME: This depends on delta_frame_ready */ + + netplay->timeout_cnt = 0; if (!socket_receive_all_blocking(netplay->fd, &cmd, sizeof(cmd))) return false; cmd = ntohl(cmd); - cmd_size = cmd & 0xffff; - cmd = cmd >> 16; + if (!socket_receive_all_blocking(netplay->fd, &cmd_size, sizeof(cmd))) + return false; + + cmd_size = ntohl(cmd_size); switch (cmd) { + case NETPLAY_CMD_ACK: + /* Why are we even bothering? */ + return true; + + case NETPLAY_CMD_NAK: + /* Disconnect now! */ + return false; + + case NETPLAY_CMD_INPUT: + { + uint32_t buffer[WORDS_PER_FRAME]; + unsigned i; + + if (cmd_size != WORDS_PER_FRAME * sizeof(uint32_t)) + { + RARCH_ERR("NETPLAY_CMD_INPUT received an unexpected payload size.\n"); + return netplay_cmd_nak(netplay); + } + + if (!socket_receive_all_blocking(netplay->fd, buffer, sizeof(buffer))) + { + RARCH_ERR("Failed to receive NETPLAY_CMD_INPUT input.\n"); + return netplay_cmd_nak(netplay); + } + + for (i = 0; i < WORDS_PER_FRAME; i++) + buffer[i] = ntohl(buffer[i]); + + if (buffer[0] < netplay->read_frame_count) + { + /* We already had this, so ignore the new transmission */ + return true; + } + else if (buffer[0] > netplay->read_frame_count) + { + /* Out of order = out of luck */ + return netplay_cmd_nak(netplay); + } + + /* The data's good! */ + netplay->buffer[netplay->read_ptr].have_remote = true; + memcpy(netplay->buffer[netplay->read_ptr].real_input_state, + buffer + 1, sizeof(buffer) - sizeof(uint32_t)); + netplay->read_ptr = NEXT_PTR(netplay->read_ptr); + netplay->read_frame_count++; + return true; + } + case NETPLAY_CMD_FLIP_PLAYERS: if (cmd_size != sizeof(uint32_t)) { @@ -255,7 +299,7 @@ static bool netplay_get_cmd(netplay_t *netplay) flip_frame = ntohl(flip_frame); - if (flip_frame < netplay->flip_frame) + if (flip_frame < netplay->read_frame_count) { RARCH_ERR("Host asked us to flip users in the past. Not possible ...\n"); return netplay_cmd_nak(netplay); @@ -264,10 +308,16 @@ static bool netplay_get_cmd(netplay_t *netplay) netplay->flip ^= true; netplay->flip_frame = flip_frame; + /* Force a rewind to assure the flip happens: This just prevents us + * from skipping other past the flip because our prediction was + * correct */ + if (flip_frame < netplay->self_frame_count) + netplay->force_rewind = true; + RARCH_LOG("Netplay users are flipped.\n"); runloop_msg_queue_push("Netplay users are flipped.", 1, 180, false); - return netplay_cmd_ack(netplay); + return true; case NETPLAY_CMD_SPECTATE: RARCH_ERR("NETPLAY_CMD_SPECTATE unimplemented.\n"); @@ -275,19 +325,142 @@ static bool netplay_get_cmd(netplay_t *netplay) case NETPLAY_CMD_DISCONNECT: warn_hangup(); - return netplay_cmd_ack(netplay); + return true; + + case NETPLAY_CMD_CRC: + { + uint32_t buffer[2]; + size_t tmp_ptr = netplay->self_ptr; + bool found = false; + + if (cmd_size != sizeof(buffer)) + { + RARCH_ERR("NETPLAY_CMD_CRC received unexpected payload size.\n"); + return netplay_cmd_nak(netplay); + } + + if (!socket_receive_all_blocking(netplay->fd, buffer, sizeof(buffer))) + { + RARCH_ERR("NETPLAY_CMD_CRC failed to receive payload.\n"); + return netplay_cmd_nak(netplay); + } + + buffer[0] = ntohl(buffer[0]); + buffer[1] = ntohl(buffer[1]); + + /* Received a CRC for some frame. If we still have it, check if it + * matched. This approach could be improved with some quick modular + * arithmetic. */ + do + { + if (netplay->buffer[tmp_ptr].frame == buffer[0]) + { + found = true; + break; + } + + tmp_ptr = PREV_PTR(tmp_ptr); + } while (tmp_ptr != netplay->self_ptr); + + if (!found) + { + /* Oh well, we got rid of it! */ + return true; + } + + if (buffer[0] <= netplay->other_frame_count) + { + /* We've already replayed up to this frame, so we can check it + * directly */ + uint32_t local_crc = netplay_delta_frame_crc(netplay, &netplay->buffer[tmp_ptr]); + if (buffer[1] != local_crc) + { + /* Problem! */ + netplay_cmd_request_savestate(netplay); + } + } + else + { + /* We'll have to check it when we catch up */ + netplay->buffer[tmp_ptr].crc = buffer[1]; + } + + return true; + } + + case NETPLAY_CMD_REQUEST_SAVESTATE: + /* Delay until next frame so we don't send the savestate after the + * input */ + netplay->force_send_savestate = true; + return true; case NETPLAY_CMD_LOAD_SAVESTATE: - RARCH_ERR("NETPLAY_CMD_LOAD_SAVESTATE unimplemented.\n"); - return netplay_cmd_nak(netplay); + { + uint32_t frame; + + /* There is a subtlty in whether the load comes before or after the + * current frame: + * + * If it comes before the current frame, then we need to force a + * rewind to that point. + * + * If it comes after the current frame, we need to jump ahead, then + * (strangely) force a rewind to the frame we're already on, so it + * gets loaded. This is just to avoid having reloading implemented in + * too many places. */ + if (cmd_size > netplay->state_size + sizeof(uint32_t)) + { + RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected save state size.\n"); + return netplay_cmd_nak(netplay); + } + + if (!socket_receive_all_blocking(netplay->fd, &frame, sizeof(frame))) + { + RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate frame.\n"); + return netplay_cmd_nak(netplay); + } + frame = ntohl(frame); + + if (frame != netplay->read_frame_count) + { + RARCH_ERR("CMD_LOAD_SAVESTATE loading a state out of order!\n"); + return netplay_cmd_nak(netplay); + } + + if (!socket_receive_all_blocking(netplay->fd, + netplay->buffer[netplay->read_ptr].state, cmd_size - sizeof(uint32_t))) + { + RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate.\n"); + return netplay_cmd_nak(netplay); + } + + /* Skip ahead if it's past where we are */ + if (frame > netplay->self_frame_count) + { + /* This is squirrely: We need to assure that when we advance the + * frame in post_frame, THEN we're referring to the frame to + * load into. If we refer directly to read_ptr, then we'll end + * up never reading the input for read_frame_count itself, which + * will make the other side unhappy. */ + netplay->self_ptr = PREV_PTR(netplay->read_ptr); + netplay->self_frame_count = frame - 1; + } + + /* And force rewind to it */ + netplay->force_rewind = true; + netplay->savestate_request_outstanding = false; + netplay->other_ptr = netplay->read_ptr; + netplay->other_frame_count = frame; + return true; + } case NETPLAY_CMD_PAUSE: - command_event(CMD_EVENT_PAUSE, NULL); - return netplay_cmd_ack(netplay); + netplay->remote_paused = true; + return true; case NETPLAY_CMD_RESUME: - command_event(CMD_EVENT_UNPAUSE, NULL); - return netplay_cmd_ack(netplay); + netplay->remote_paused = false; + return true; default: break; } @@ -301,8 +474,8 @@ static bool netplay_get_cmd(netplay_t *netplay) static int poll_input(netplay_t *netplay, bool block) { - int max_fd = (netplay->fd > netplay->udp_fd ? - netplay->fd : netplay->udp_fd) + 1; + bool had_input = false; + int max_fd = netplay->fd + 1; struct timeval tv = {0}; tv.tv_sec = 0; tv.tv_usec = block ? (RETRY_MS * 1000) : 0; @@ -314,11 +487,11 @@ static int poll_input(netplay_t *netplay, bool block) * Technically possible for select() to modify tmp_tv, so * we go paranoia mode. */ struct timeval tmp_tv = tv; + had_input = false; netplay->timeout_cnt++; FD_ZERO(&fds); - FD_SET(netplay->udp_fd, &fds); FD_SET(netplay->fd, &fds); if (socket_select(max_fd, &fds, NULL, NULL, &tmp_tv) < 0) @@ -326,83 +499,43 @@ static int poll_input(netplay_t *netplay, bool block) /* Somewhat hacky, * but we aren't using the TCP connection for anything useful atm. */ - if (FD_ISSET(netplay->fd, &fds) && !netplay_get_cmd(netplay)) - return -1; - - if (FD_ISSET(netplay->udp_fd, &fds)) - return 1; + if (FD_ISSET(netplay->fd, &fds)) + { + /* If we're not ready for input, wait until we are. Could fill the TCP buffer, stalling the other side. */ + if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->read_ptr], netplay->read_frame_count)) + { + had_input = true; + if (!netplay_get_cmd(netplay)) + return -1; + } + } if (!block) continue; - if (!send_chunk(netplay)) - { - warn_hangup(); - netplay->has_connection = false; + RARCH_LOG("Network is stalling at frame %u, count %u of %d ...\n", + netplay->self_frame_count, netplay->timeout_cnt, MAX_RETRIES); + + if (netplay->timeout_cnt >= MAX_RETRIES && !netplay->remote_paused) return -1; - } + } while (had_input || (block && (netplay->read_frame_count <= netplay->self_frame_count))); - RARCH_LOG("Network is stalling, resending packet... Count %u of %d ...\n", - netplay->timeout_cnt, MAX_RETRIES); - } while ((netplay->timeout_cnt < MAX_RETRIES) && block); - - if (block) - return -1; return 0; } -static bool receive_data(netplay_t *netplay, uint32_t *buffer, size_t size) -{ - socklen_t addrlen = sizeof(netplay->their_addr); - - if (recvfrom(netplay->udp_fd, (char*)buffer, size, 0, - (struct sockaddr*)&netplay->their_addr, &addrlen) != (ssize_t)size) - return false; - - netplay->has_client_addr = true; - - return true; -} - -static void parse_packet(netplay_t *netplay, uint32_t *buffer, unsigned size) -{ - unsigned i; - - for (i = 0; i < size * UDP_WORDS_PER_FRAME; i++) - buffer[i] = ntohl(buffer[i]); - - for (i = 0; i < size && netplay->read_frame_count <= netplay->frame_count; i++) - { - uint32_t frame = buffer[UDP_WORDS_PER_FRAME * i + 0]; - const uint32_t *state = &buffer[UDP_WORDS_PER_FRAME * i + 1]; - - if (frame != netplay->read_frame_count) - continue; - - netplay->buffer[netplay->read_ptr].is_simulated = false; - memcpy(netplay->buffer[netplay->read_ptr].real_input_state, state, - sizeof(netplay->buffer[netplay->read_ptr].real_input_state)); - - netplay->read_ptr = NEXT_PTR(netplay->read_ptr); - netplay->read_frame_count++; - netplay->timeout_cnt = 0; - } -} - /* TODO: Somewhat better prediction. :P */ static void simulate_input(netplay_t *netplay) { - size_t ptr = PREV_PTR(netplay->self_ptr); + size_t ptr = netplay->self_ptr; size_t prev = PREV_PTR(netplay->read_ptr); memcpy(netplay->buffer[ptr].simulated_input_state, netplay->buffer[prev].real_input_state, sizeof(netplay->buffer[prev].real_input_state)); - - netplay->buffer[ptr].is_simulated = true; - netplay->buffer[ptr].used_real = false; } +#define MAX_STALL_TIME_USEC (10*1000*1000) + /** * netplay_poll: * @netplay : pointer to netplay object @@ -422,27 +555,15 @@ static bool netplay_poll(netplay_t *netplay) netplay->can_poll = false; - if (!get_self_input_state(netplay)) - return false; + get_self_input_state(netplay); - /* We skip reading the first frame so the host has a chance to grab - * our host info so we don't block forever :') */ - if (netplay->frame_count == 0) - { - netplay->buffer[0].used_real = true; - netplay->buffer[0].is_simulated = false; - - memset(netplay->buffer[0].real_input_state, - 0, sizeof(netplay->buffer[0].real_input_state)); - - netplay->read_ptr = NEXT_PTR(netplay->read_ptr); - netplay->read_frame_count++; + /* No network side in spectate mode */ + if (netplay_is_server(netplay) && netplay->spectate.enabled) return true; - } - /* We might have reached the end of the buffer, where we - * simply have to block. */ - res = poll_input(netplay, netplay->other_ptr == netplay->self_ptr); + /* Read Netplay input, block if we're configured to stall for input every + * frame */ + res = poll_input(netplay, (netplay->stall_frames == 0) && (netplay->read_frame_count <= netplay->self_frame_count)); if (res == -1) { netplay->has_connection = false; @@ -450,39 +571,43 @@ static bool netplay_poll(netplay_t *netplay) return false; } - if (res == 1) - { - uint32_t first_read = netplay->read_frame_count; - do - { - uint32_t buffer[UDP_FRAME_PACKETS * UDP_WORDS_PER_FRAME]; - if (!receive_data(netplay, buffer, sizeof(buffer))) - { - warn_hangup(); - netplay->has_connection = false; - return false; - } - parse_packet(netplay, buffer, UDP_FRAME_PACKETS); + /* Simulate the input if we don't have real input */ + if (!netplay->buffer[netplay->self_ptr].have_remote) + simulate_input(netplay); - } while ((netplay->read_frame_count <= netplay->frame_count) && - poll_input(netplay, (netplay->other_ptr == netplay->self_ptr) && - (first_read == netplay->read_frame_count)) == 1); + /* Consider stalling */ + switch (netplay->stall) { + case RARCH_NETPLAY_STALL_RUNNING_FAST: + if (netplay->read_frame_count >= netplay->self_frame_count) + netplay->stall = RARCH_NETPLAY_STALL_NONE; + break; + + default: /* not stalling */ + if (netplay->read_frame_count + netplay->stall_frames <= netplay->self_frame_count) + { + netplay->stall = RARCH_NETPLAY_STALL_RUNNING_FAST; + netplay->stall_time = cpu_features_get_time_usec(); + } } - else + + /* If we're stalling, consider disconnection */ + if (netplay->stall) { - /* Cannot allow this. Should not happen though. */ - if (netplay->self_ptr == netplay->other_ptr) + retro_time_t now = cpu_features_get_time_usec(); + if (netplay->remote_paused) { + /* Don't stall out while they're paused */ + netplay->stall_time = now; + } + else if (now - netplay->stall_time >= MAX_STALL_TIME_USEC) + { + /* Stalled out! */ + netplay->has_connection = false; warn_hangup(); return false; } } - if (netplay->read_ptr != netplay->self_ptr) - simulate_input(netplay); - else - netplay->buffer[PREV_PTR(netplay->self_ptr)].used_real = true; - return true; } @@ -504,14 +629,14 @@ void video_frame_net(const void *data, unsigned width, void audio_sample_net(int16_t left, int16_t right) { netplay_t *netplay = (netplay_t*)netplay_data; - if (!netplay_should_skip(netplay)) + if (!netplay_should_skip(netplay) && !netplay->stall) netplay->cbs.sample_cb(left, right); } size_t audio_sample_batch_net(const int16_t *data, size_t frames) { netplay_t *netplay = (netplay_t*)netplay_data; - if (!netplay_should_skip(netplay)) + if (!netplay_should_skip(netplay) && !netplay->stall) return netplay->cbs.sample_batch_cb(data, frames); return frames; } @@ -533,13 +658,13 @@ static bool netplay_is_alive(netplay_t *netplay) static bool netplay_flip_port(netplay_t *netplay, bool port) { - size_t frame = netplay->frame_count; + size_t frame = netplay->self_frame_count; if (netplay->flip_frame == 0) return port; if (netplay->is_replay) - frame = netplay->tmp_frame_count; + frame = netplay->replay_frame_count; return port ^ netplay->flip ^ (frame < netplay->flip_frame); } @@ -549,16 +674,21 @@ static int16_t netplay_input_state(netplay_t *netplay, unsigned idx, unsigned id) { size_t ptr = netplay->is_replay ? - netplay->tmp_ptr : PREV_PTR(netplay->self_ptr); + netplay->replay_ptr : netplay->self_ptr; const uint32_t *curr_input_state = netplay->buffer[ptr].self_state; if (netplay->port == (netplay_flip_port(netplay, port) ? 1 : 0)) { - if (netplay->buffer[ptr].is_simulated) - curr_input_state = netplay->buffer[ptr].simulated_input_state; - else + if (netplay->buffer[ptr].have_remote) + { + netplay->buffer[ptr].used_real = true; curr_input_state = netplay->buffer[ptr].real_input_state; + } + else + { + curr_input_state = netplay->buffer[ptr].simulated_input_state; + } } switch (device) @@ -663,6 +793,13 @@ static int init_tcp_connection(const struct addrinfo *res, bool ret = true; int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); +#if defined(IPPROTO_TCP) && defined(TCP_NODELAY) + { + int flag = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&flag, sizeof(int)); + } +#endif + if (fd < 0) { ret = false; @@ -762,37 +899,6 @@ static bool init_tcp_socket(netplay_t *netplay, const char *server, return ret; } -static bool init_udp_socket(netplay_t *netplay, const char *server, - uint16_t port) -{ - int fd = socket_init((void**)&netplay->addr, port, server, SOCKET_TYPE_DATAGRAM); - - if (fd < 0) - goto error; - - netplay->udp_fd = fd; - - if (!server) - { - /* Not sure if we have to do this for UDP, but hey :) */ - if (!socket_bind(netplay->udp_fd, (void*)netplay->addr)) - { - RARCH_ERR("Failed to bind socket.\n"); - socket_close(netplay->udp_fd); - netplay->udp_fd = -1; - } - - freeaddrinfo_retro(netplay->addr); - netplay->addr = NULL; - } - - return true; - -error: - RARCH_ERR("Failed to initialize socket.\n"); - return false; -} - static bool init_socket(netplay_t *netplay, const char *server, uint16_t port) { if (!network_init()) @@ -800,9 +906,42 @@ static bool init_socket(netplay_t *netplay, const char *server, uint16_t port) if (!init_tcp_socket(netplay, server, port, netplay->spectate.enabled)) return false; - if (!netplay->spectate.enabled && !init_udp_socket(netplay, server, port)) + + return true; +} + +static bool netplay_init_buffers(netplay_t *netplay, unsigned frames) +{ + unsigned i; + retro_ctx_size_info_t info; + + if (!netplay) return false; + /* * 2 + 1 because: + * Self sits in the middle, + * Other is allowed to drift as much as 'frames' frames behind + * Read is allowed to drift as much as 'frames' frames ahead */ + netplay->buffer_size = frames * 2 + 1; + + netplay->buffer = (struct delta_frame*)calloc(netplay->buffer_size, + sizeof(*netplay->buffer)); + + if (!netplay->buffer) + return false; + + core_serialize_size(&info); + + netplay->state_size = info.size; + + for (i = 0; i < netplay->buffer_size; i++) + { + netplay->buffer[i].state = calloc(netplay->state_size, 1); + + if (!netplay->buffer[i].state) + return false; + } + return true; } @@ -811,6 +950,7 @@ static bool init_socket(netplay_t *netplay, const char *server, uint16_t port) * @server : IP address of server. * @port : Port of server. * @frames : Amount of lag frames. + * @check_frames : Frequency with which to check CRCs. * @cb : Libretro callbacks. * @spectate : If true, enable spectator mode. * @nick : Nickname of user. @@ -821,26 +961,27 @@ static bool init_socket(netplay_t *netplay, const char *server, uint16_t port) * Returns: new netplay handle. **/ netplay_t *netplay_new(const char *server, uint16_t port, - unsigned frames, const struct retro_callbacks *cb, - bool spectate, - const char *nick) + unsigned frames, unsigned check_frames, const struct retro_callbacks *cb, + bool spectate, const char *nick) { - netplay_t *netplay = NULL; - - if (frames > UDP_FRAME_PACKETS) - frames = UDP_FRAME_PACKETS; - - netplay = (netplay_t*)calloc(1, sizeof(*netplay)); + netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay)); if (!netplay) return NULL; netplay->fd = -1; - netplay->udp_fd = -1; netplay->cbs = *cb; netplay->port = server ? 0 : 1; netplay->spectate.enabled = spectate; netplay->is_server = server == NULL; strlcpy(netplay->nick, nick, sizeof(netplay->nick)); + netplay->stall_frames = frames; + netplay->check_frames = check_frames; + + if (!netplay_init_buffers(netplay, frames)) + { + free(netplay); + return NULL; + } if(spectate) netplay->net_cbs = netplay_get_cbs_spectate(); @@ -861,28 +1002,11 @@ netplay_t *netplay_new(const char *server, uint16_t port, error: if (netplay->fd >= 0) socket_close(netplay->fd); - if (netplay->udp_fd >= 0) - socket_close(netplay->udp_fd); free(netplay); return NULL; } -static bool netplay_send_raw_cmd(netplay_t *netplay, uint32_t cmd, - const void *data, size_t size) -{ - cmd = (cmd << 16) | (size & 0xffff); - cmd = htonl(cmd); - - if (!socket_send_all_blocking(netplay->fd, &cmd, sizeof(cmd), false)) - return false; - - if (!socket_send_all_blocking(netplay->fd, data, size, false)) - return false; - - return true; -} - /** * netplay_command: * @netplay : pointer to netplay object @@ -905,7 +1029,6 @@ bool netplay_command(netplay_t* netplay, enum netplay_cmd cmd, const char* msg = NULL; bool allowed_spectate = !!(flags & CMD_OPT_ALLOWED_IN_SPECTATE_MODE); bool host_only = !!(flags & CMD_OPT_HOST_ONLY); - bool require_sync = !!(flags & CMD_OPT_REQUIRE_SYNC); retro_assert(netplay); @@ -921,21 +1044,11 @@ bool netplay_command(netplay_t* netplay, enum netplay_cmd cmd, goto error; } - if(require_sync && check_netplay_synched(netplay)) - { - msg = "Cannot %s while host and client are not in sync."; + if (!netplay_send_raw_cmd(netplay, cmd, data, sz)) goto error; - } - if(netplay_send_raw_cmd(netplay, cmd, data, sz)) { - if(netplay_get_response(netplay)) - runloop_msg_queue_push(success_msg, 1, 180, false); - else - { - msg = "Failed to send command \"%s\""; - goto error; - } - } + runloop_msg_queue_push(success_msg, 1, 180, false); + return true; error: @@ -953,14 +1066,15 @@ error: **/ static void netplay_flip_users(netplay_t *netplay) { - uint32_t flip_frame = netplay->frame_count + 2 * UDP_FRAME_PACKETS; + /* Must be in the future because we may have already sent this frame's data */ + uint32_t flip_frame = netplay->self_frame_count + 1; uint32_t flip_frame_net = htonl(flip_frame); - bool command = netplay_command( + bool command = netplay_command( netplay, NETPLAY_CMD_FLIP_PLAYERS, &flip_frame_net, sizeof flip_frame_net, CMD_OPT_HOST_ONLY | CMD_OPT_REQUIRE_SYNC, "flip users", "Successfully flipped users.\n"); - + if(command) { netplay->flip ^= true; @@ -990,8 +1104,6 @@ void netplay_free(netplay_t *netplay) } else { - socket_close(netplay->udp_fd); - for (i = 0; i < netplay->buffer_size; i++) free(netplay->buffer[i].state); @@ -1004,67 +1116,28 @@ void netplay_free(netplay_t *netplay) free(netplay); } - -static void netplay_set_spectate_input(netplay_t *netplay, int16_t input) -{ - if (netplay->spectate.input_ptr >= netplay->spectate.input_sz) - { - netplay->spectate.input_sz++; - netplay->spectate.input_sz *= 2; - netplay->spectate.input = (uint16_t*)realloc(netplay->spectate.input, - netplay->spectate.input_sz * sizeof(uint16_t)); - } - - netplay->spectate.input[netplay->spectate.input_ptr++] = swap_if_big16(input); -} - -int16_t input_state_spectate(unsigned port, unsigned device, - unsigned idx, unsigned id) -{ - netplay_t *netplay = (netplay_t*)netplay_data; - int16_t res = netplay->cbs.state_cb(port, device, idx, id); - - netplay_set_spectate_input(netplay, res); - return res; -} - -static int16_t netplay_get_spectate_input(netplay_t *netplay, bool port, - unsigned device, unsigned idx, unsigned id) -{ - int16_t inp; - retro_ctx_input_state_info_t input_info; - - if (socket_receive_all_blocking(netplay->fd, (char*)&inp, sizeof(inp))) - return swap_if_big16(inp); - - RARCH_ERR("Connection with host was cut.\n"); - runloop_msg_queue_push("Connection with host was cut.", 1, 180, true); - - input_info.cb = netplay->cbs.state_cb; - - core_set_input_state(&input_info); - - return netplay->cbs.state_cb(port, device, idx, id); -} - -int16_t input_state_spectate_client(unsigned port, unsigned device, - unsigned idx, unsigned id) -{ - return netplay_get_spectate_input((netplay_t*)netplay_data, port, - device, idx, id); -} - /** * netplay_pre_frame: * @netplay : pointer to netplay object * * Pre-frame for Netplay. * Call this before running retro_run(). + * + * Returns: true (1) if the frontend is cleared to emulate the frame, false (0) + * if we're stalled or paused **/ -void netplay_pre_frame(netplay_t *netplay) +bool netplay_pre_frame(netplay_t *netplay) { retro_assert(netplay && netplay->net_cbs->pre_frame); - netplay->net_cbs->pre_frame(netplay); + + /* FIXME: This is an ugly way to learn we're not paused anymore */ + if (netplay->local_paused) + netplay_frontend_paused(netplay, false); + + if (!netplay->net_cbs->pre_frame(netplay)) + return false; + + return (!netplay->has_connection || (!netplay->stall && !netplay->remote_paused)); } /** @@ -1081,6 +1154,99 @@ void netplay_post_frame(netplay_t *netplay) netplay->net_cbs->post_frame(netplay); } +/** + * netplay_frontend_paused + * @netplay : pointer to netplay object + * @paused : true if frontend is paused + * + * Inform Netplay of the frontend's pause state (paused or otherwise) + **/ +void netplay_frontend_paused(netplay_t *netplay, bool paused) +{ + /* Nothing to do if we already knew this */ + if (netplay->local_paused == paused) + return; + + netplay->local_paused = paused; + if (netplay->has_connection && !netplay->spectate.enabled) + netplay_send_raw_cmd(netplay, paused + ? NETPLAY_CMD_PAUSE : NETPLAY_CMD_RESUME, NULL, 0); +} + +/** + * netplay_load_savestate + * @netplay : pointer to netplay object + * @serial_info : the savestate being loaded, NULL means "load it yourself" + * @save : whether to save the provided serial_info into the frame buffer + * + * Inform Netplay of a savestate load and send it to the other side + **/ +void netplay_load_savestate(netplay_t *netplay, retro_ctx_serialize_info_t *serial_info, bool save) +{ + uint32_t header[3]; + retro_ctx_serialize_info_t tmp_serial_info; + + if (!netplay->has_connection) + return; + + /* Record it in our own buffer */ + if (save || !serial_info) + { + if (netplay_delta_frame_ready(netplay, + &netplay->buffer[netplay->self_ptr], netplay->self_frame_count)) + { + if (!serial_info) + { + tmp_serial_info.size = netplay->state_size; + tmp_serial_info.data = netplay->buffer[netplay->self_ptr].state; + if (!core_serialize(&tmp_serial_info)) + return; + tmp_serial_info.data_const = tmp_serial_info.data; + serial_info = &tmp_serial_info; + } + else + { + if (serial_info->size <= netplay->state_size) + { + memcpy(netplay->buffer[netplay->self_ptr].state, + serial_info->data_const, serial_info->size); + } + } + } + } + + /* We need to ignore any intervening data from the other side, and never rewind past this */ + if (netplay->read_frame_count < netplay->self_frame_count) + { + netplay->read_ptr = netplay->self_ptr; + netplay->read_frame_count = netplay->self_frame_count; + } + if (netplay->other_frame_count < netplay->self_frame_count) + { + netplay->other_ptr = netplay->self_ptr; + netplay->other_frame_count = netplay->self_frame_count; + } + + /* And send it to the peer (FIXME: this is an ugly way to do this) */ + header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE); + header[1] = htonl(serial_info->size + sizeof(uint32_t)); + header[2] = htonl(netplay->self_frame_count); + + if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false)) + { + warn_hangup(); + netplay->has_connection = false; + return; + } + + if (!socket_send_all_blocking(netplay->fd, serial_info->data_const, serial_info->size, false)) + { + warn_hangup(); + netplay->has_connection = false; + return; + } +} + void deinit_netplay(void) { netplay_t *netplay = (netplay_t*)netplay_data; @@ -1128,8 +1294,8 @@ bool init_netplay(void) netplay_data = (netplay_t*)netplay_new( global->netplay.is_client ? global->netplay.server : NULL, global->netplay.port ? global->netplay.port : RARCH_DEFAULT_PORT, - global->netplay.sync_frames, &cbs, global->netplay.is_spectate, - settings->username); + global->netplay.sync_frames, global->netplay.check_frames, &cbs, + global->netplay.is_spectate, settings->username); if (netplay_data) return true; @@ -1146,7 +1312,12 @@ bool init_netplay(void) bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data) { if (!netplay_data) - return false; + { + if (state == RARCH_NETPLAY_CTL_IS_DATA_INITED) + return false; + else + return true; + } switch (state) { @@ -1156,8 +1327,7 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data) netplay_post_frame((netplay_t*)netplay_data); break; case RARCH_NETPLAY_CTL_PRE_FRAME: - netplay_pre_frame((netplay_t*)netplay_data); - break; + return netplay_pre_frame((netplay_t*)netplay_data); case RARCH_NETPLAY_CTL_FLIP_PLAYERS: { bool *state = (bool*)data; @@ -1172,6 +1342,15 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data) command_event(CMD_EVENT_FULLSCREEN_TOGGLE, NULL); } break; + case RARCH_NETPLAY_CTL_PAUSE: + netplay_frontend_paused((netplay_t*)netplay_data, true); + break; + case RARCH_NETPLAY_CTL_UNPAUSE: + netplay_frontend_paused((netplay_t*)netplay_data, false); + break; + case RARCH_NETPLAY_CTL_LOAD_SAVESTATE: + netplay_load_savestate((netplay_t*)netplay_data, (retro_ctx_serialize_info_t*)data, true); + break; default: case RARCH_NETPLAY_CTL_NONE: break; diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h index b0888a2ba0..a2bb44efba 100644 --- a/network/netplay/netplay.h +++ b/network/netplay/netplay.h @@ -35,12 +35,15 @@ enum rarch_netplay_ctl_state RARCH_NETPLAY_CTL_FULLSCREEN_TOGGLE, RARCH_NETPLAY_CTL_POST_FRAME, RARCH_NETPLAY_CTL_PRE_FRAME, - RARCH_NETPLAY_CTL_IS_DATA_INITED + RARCH_NETPLAY_CTL_IS_DATA_INITED, + RARCH_NETPLAY_CTL_PAUSE, + RARCH_NETPLAY_CTL_UNPAUSE, + RARCH_NETPLAY_CTL_LOAD_SAVESTATE }; enum netplay_cmd { - /* Miscellaneous commands */ + /* Basic commands */ /* Acknowlegement response */ NETPLAY_CMD_ACK = 0x0000, @@ -48,28 +51,40 @@ enum netplay_cmd /* Failed acknowlegement response */ NETPLAY_CMD_NAK = 0x0001, + /* Input data */ + NETPLAY_CMD_INPUT = 0x0002, + + /* Misc. commands */ + /* Swap inputs between player 1 and player 2 */ - NETPLAY_CMD_FLIP_PLAYERS = 0x0002, + NETPLAY_CMD_FLIP_PLAYERS = 0x0003, /* Toggle spectate/join mode */ - NETPLAY_CMD_SPECTATE = 0x0003, + NETPLAY_CMD_SPECTATE = 0x0004, /* Gracefully disconnects from host */ - NETPLAY_CMD_DISCONNECT = 0x0004, + NETPLAY_CMD_DISCONNECT = 0x0005, /* Sends multiple config requests over, * See enum netplay_cmd_cfg */ - NETPLAY_CMD_CFG = 0x0005, + NETPLAY_CMD_CFG = 0x0006, /* CMD_CFG streamlines sending multiple configurations. This acknowledges each one individually */ - NETPLAY_CMD_CFG_ACK = 0x0006, + NETPLAY_CMD_CFG_ACK = 0x0007, /* Loading and synchronization */ + /* Send the CRC hash of a frame's state */ + NETPLAY_CMD_CRC = 0x0010, + + /* Request a savestate */ + NETPLAY_CMD_REQUEST_SAVESTATE = 0x0011, + /* Send a savestate for the client to load */ NETPLAY_CMD_LOAD_SAVESTATE = 0x0012, + /* Sends over cheats enabled on client */ NETPLAY_CMD_CHEATS = 0x0013, @@ -110,17 +125,12 @@ void audio_sample_net(int16_t left, int16_t right); size_t audio_sample_batch_net(const int16_t *data, size_t frames); -int16_t input_state_spectate(unsigned port, unsigned device, - unsigned idx, unsigned id); - -int16_t input_state_spectate_client(unsigned port, unsigned device, - unsigned idx, unsigned id); - /** * netplay_new: * @server : IP address of server. * @port : Port of server. * @frames : Amount of lag frames. + * @check_frames : Frequency with which to check CRCs. * @cb : Libretro callbacks. * @spectate : If true, enable spectator mode. * @nick : Nickname of user. @@ -131,7 +141,7 @@ int16_t input_state_spectate_client(unsigned port, unsigned device, * Returns: new netplay handle. **/ netplay_t *netplay_new(const char *server, - uint16_t port, unsigned frames, + uint16_t port, unsigned frames, unsigned check_frames, const struct retro_callbacks *cb, bool spectate, const char *nick); @@ -149,8 +159,11 @@ void netplay_free(netplay_t *handle); * * Pre-frame for Netplay. * Call this before running retro_run(). + * + * Returns: true (1) if the frontend is clear to emulate the frame, false (0) + * if we're stalled or paused **/ -void netplay_pre_frame(netplay_t *handle); +bool netplay_pre_frame(netplay_t *handle); /** * netplay_post_frame: @@ -162,6 +175,25 @@ void netplay_pre_frame(netplay_t *handle); **/ void netplay_post_frame(netplay_t *handle); +/** + * netplay_frontend_paused + * @netplay : pointer to netplay object + * @paused : true if frontend is paused + * + * Inform Netplay of the frontend's pause state (paused or otherwise) + **/ +void netplay_frontend_paused(netplay_t *netplay, bool paused); + +/** + * netplay_load_savestate + * @netplay : pointer to netplay object + * @serial_info : the savestate being loaded, NULL means "load it yourself" + * @save : whether to save the provided serial_info into the frame buffer + * + * Inform Netplay of a savestate load and send it to the other side + **/ +void netplay_load_savestate(netplay_t *netplay, retro_ctx_serialize_info_t *serial_info, bool save); + /** * init_netplay: * diff --git a/network/netplay/netplay_common.c b/network/netplay/netplay_common.c index dba2eef948..52bd5b4c65 100644 --- a/network/netplay/netplay_common.c +++ b/network/netplay/netplay_common.c @@ -1,6 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2016 - Daniel De Matteis + * Copyright (C) 2016 - Gregor Richards * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- @@ -19,6 +20,8 @@ #include "netplay_private.h" #include +#include "compat/zlib.h" + #include "../../movie.h" #include "../../msg_hash.h" #include "../../content.h" @@ -79,7 +82,7 @@ uint32_t *netplay_bsv_header_generate(size_t *size, uint32_t magic) uint32_t *header, bsv_header[4] = {0}; core_serialize_size(&info); - + serialize_size = info.size; header_size = sizeof(bsv_header) + serialize_size; *size = header_size; @@ -177,9 +180,9 @@ uint32_t netplay_impl_magic(void) core_api_version(&api_info); api = api_info.version; - + runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &info); - + if (info) lib = info->info.library_name; @@ -199,6 +202,8 @@ uint32_t netplay_impl_magic(void) for (i = 0; i < len; i++) res ^= ver[i] << ((i & 0xf) + 16); + res ^= NETPLAY_PROTOCOL_VERSION << 24; + return res; } @@ -215,7 +220,7 @@ bool netplay_send_info(netplay_t *netplay) core_get_memory(&mem_info); content_get_crc(&content_crc_ptr); - + header[0] = htonl(*content_crc_ptr); header[1] = htonl(netplay_impl_magic()); header[2] = htonl(mem_info.size); @@ -342,3 +347,30 @@ bool netplay_is_spectate(netplay_t* netplay) return false; return netplay->spectate.enabled; } + +bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta, uint32_t frame) +{ + void *remember_state; + if (delta->used) + { + if (delta->frame == frame) return true; + if (netplay->other_frame_count <= delta->frame) + { + /* We haven't even replayed this frame yet, so we can't overwrite it! */ + return false; + } + } + remember_state = delta->state; + memset(delta, 0, sizeof(struct delta_frame)); + delta->used = true; + delta->frame = frame; + delta->state = remember_state; + return true; +} + +uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta) +{ + if (!netplay->state_size) + return 0; + return crc32(0L, (const unsigned char*)delta->state, netplay->state_size); +} diff --git a/network/netplay/netplay_net.c b/network/netplay/netplay_net.c index 91dfc88615..56ede8892e 100644 --- a/network/netplay/netplay_net.c +++ b/network/netplay/netplay_net.c @@ -1,6 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2016 - Daniel De Matteis + * Copyright (C) 2016 - Gregor Richards * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- @@ -15,33 +16,79 @@ */ #include +#include #include "netplay_private.h" +#include "retro_assert.h" + #include "../../autosave.h" +static void netplay_handle_frame_hash(netplay_t *netplay, struct delta_frame *delta) +{ + if (netplay_is_server(netplay)) + { + if (netplay->check_frames && delta->frame % netplay->check_frames == 0) + { + delta->crc = netplay_delta_frame_crc(netplay, delta); + netplay_cmd_crc(netplay, delta); + } + } + else if (delta->crc) + { + /* We have a remote CRC, so check it */ + uint32_t local_crc = netplay_delta_frame_crc(netplay, delta); + if (local_crc != delta->crc) + { + /* Fix this! */ + netplay_cmd_request_savestate(netplay); + } + } +} + /** - * pre_frame: + * netplay_net_pre_frame: * @netplay : pointer to netplay object * * Pre-frame for Netplay (normal version). **/ -static void netplay_net_pre_frame(netplay_t *netplay) +static bool netplay_net_pre_frame(netplay_t *netplay) { retro_ctx_serialize_info_t serial_info; - serial_info.data = netplay->buffer[netplay->self_ptr].state; - serial_info.size = netplay->state_size; + if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->self_ptr], netplay->self_frame_count)) + { + serial_info.data_const = NULL; + serial_info.data = netplay->buffer[netplay->self_ptr].state; + serial_info.size = netplay->state_size; - core_serialize(&serial_info); + if (core_serialize(&serial_info)) + { + if (netplay->force_send_savestate) + { + /* Send this along to the other side */ + serial_info.data_const = netplay->buffer[netplay->self_ptr].state; + netplay_load_savestate(netplay, &serial_info, false); + netplay->force_send_savestate = false; + } + } + else + { + /* If the core can't serialize properly, we must stall for the + * remote input on EVERY frame, because we can't recover */ + netplay->stall_frames = 0; + } + } netplay->can_poll = true; input_poll_net(); + + return true; } /** - * post_frame: + * netplay_net_post_frame: * @netplay : pointer to netplay object * * Post-frame for Netplay (normal version). @@ -49,49 +96,61 @@ static void netplay_net_pre_frame(netplay_t *netplay) **/ static void netplay_net_post_frame(netplay_t *netplay) { - netplay->frame_count++; + netplay->self_ptr = NEXT_PTR(netplay->self_ptr); + netplay->self_frame_count++; - /* Nothing to do... */ - if (netplay->other_frame_count == netplay->read_frame_count) + /* Only relevant if we're connected */ + if (!netplay->has_connection) return; - /* Skip ahead if we predicted correctly. - * Skip until our simulation failed. */ - while (netplay->other_frame_count < netplay->read_frame_count) + if (!netplay->force_rewind) { - const struct delta_frame *ptr = &netplay->buffer[netplay->other_ptr]; + /* Skip ahead if we predicted correctly. + * Skip until our simulation failed. */ + while (netplay->other_frame_count < netplay->read_frame_count && + netplay->other_frame_count < netplay->self_frame_count) + { + struct delta_frame *ptr = &netplay->buffer[netplay->other_ptr]; - if (memcmp(ptr->simulated_input_state, ptr->real_input_state, - sizeof(ptr->real_input_state)) != 0 - && !ptr->used_real) - break; - netplay->other_ptr = NEXT_PTR(netplay->other_ptr); - netplay->other_frame_count++; + if (memcmp(ptr->simulated_input_state, ptr->real_input_state, + sizeof(ptr->real_input_state)) != 0 + && !ptr->used_real) + break; + netplay_handle_frame_hash(netplay, ptr); + netplay->other_ptr = NEXT_PTR(netplay->other_ptr); + netplay->other_frame_count++; + } } - if (netplay->other_frame_count < netplay->read_frame_count) + /* Now replay the real input if we've gotten ahead of it */ + if (netplay->force_rewind || + (netplay->other_frame_count < netplay->read_frame_count && + netplay->other_frame_count < netplay->self_frame_count)) { retro_ctx_serialize_info_t serial_info; - bool first = true; /* Replay frames. */ netplay->is_replay = true; - netplay->tmp_ptr = netplay->other_ptr; - netplay->tmp_frame_count = netplay->other_frame_count; + netplay->replay_ptr = netplay->other_ptr; + netplay->replay_frame_count = netplay->other_frame_count; - serial_info.data_const = netplay->buffer[netplay->other_ptr].state; + serial_info.data = NULL; + serial_info.data_const = netplay->buffer[netplay->replay_ptr].state; serial_info.size = netplay->state_size; core_unserialize(&serial_info); - while (first || (netplay->tmp_ptr != netplay->self_ptr)) + while (netplay->replay_frame_count < netplay->self_frame_count) { - serial_info.data = netplay->buffer[netplay->tmp_ptr].state; + struct delta_frame *ptr = &netplay->buffer[netplay->replay_ptr]; + serial_info.data = ptr->state; serial_info.size = netplay->state_size; serial_info.data_const = NULL; core_serialize(&serial_info); + netplay_handle_frame_hash(netplay, ptr); + #if defined(HAVE_THREADS) autosave_lock(); #endif @@ -99,45 +158,39 @@ static void netplay_net_post_frame(netplay_t *netplay) #if defined(HAVE_THREADS) autosave_unlock(); #endif - netplay->tmp_ptr = NEXT_PTR(netplay->tmp_ptr); - netplay->tmp_frame_count++; - first = false; + netplay->replay_ptr = NEXT_PTR(netplay->replay_ptr); + netplay->replay_frame_count++; } - netplay->other_ptr = netplay->read_ptr; - netplay->other_frame_count = netplay->read_frame_count; + if (netplay->read_frame_count < netplay->self_frame_count) + { + netplay->other_ptr = netplay->read_ptr; + netplay->other_frame_count = netplay->read_frame_count; + } + else + { + netplay->other_ptr = netplay->self_ptr; + netplay->other_frame_count = netplay->self_frame_count; + } netplay->is_replay = false; + netplay->force_rewind = false; } -} -static bool netplay_net_init_buffers(netplay_t *netplay) -{ - unsigned i; - retro_ctx_size_info_t info; - if (!netplay) - return false; - - netplay->buffer = (struct delta_frame*)calloc(netplay->buffer_size, - sizeof(*netplay->buffer)); - - if (!netplay->buffer) - return false; - - core_serialize_size(&info); - - netplay->state_size = info.size; - - for (i = 0; i < netplay->buffer_size; i++) + /* If we're supposed to stall, rewind (we shouldn't get this far if we're + * stalled, so this is a last resort) */ + if (netplay->stall) { - netplay->buffer[i].state = malloc(netplay->state_size); + retro_ctx_serialize_info_t serial_info; - if (!netplay->buffer[i].state) - return false; + netplay->self_ptr = PREV_PTR(netplay->self_ptr); + netplay->self_frame_count--; - netplay->buffer[i].is_simulated = true; + serial_info.data = NULL; + serial_info.data_const = netplay->buffer[netplay->self_ptr].state; + serial_info.size = netplay->state_size; + + core_unserialize(&serial_info); } - - return true; } static bool netplay_net_info_cb(netplay_t* netplay, unsigned frames) @@ -153,11 +206,6 @@ static bool netplay_net_info_cb(netplay_t* netplay, unsigned frames) return false; } - netplay->buffer_size = frames + 1; - - if (!netplay_net_init_buffers(netplay)) - return false; - netplay->has_connection = true; return true; diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index f0e30aac17..852cf158c4 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -20,6 +20,7 @@ #include "netplay.h" #include +#include #include #include "../../core.h" @@ -30,32 +31,52 @@ #define HAVE_IPV6 #endif -#define UDP_FRAME_PACKETS 16 -#define UDP_WORDS_PER_FRAME 4 /* Allows us to send 128 bits worth of state per frame. */ -#define MAX_SPECTATORS 16 -#define RARCH_DEFAULT_PORT 55435 +#define WORDS_PER_FRAME 4 /* Allows us to send 128 bits worth of state per frame. */ +#define MAX_SPECTATORS 16 +#define RARCH_DEFAULT_PORT 55435 + +#define NETPLAY_PROTOCOL_VERSION 1 #define PREV_PTR(x) ((x) == 0 ? netplay->buffer_size - 1 : (x) - 1) #define NEXT_PTR(x) ((x + 1) % netplay->buffer_size) struct delta_frame { + bool used; /* a bit derpy, but this is how we know if the delta's been used at all */ + uint32_t frame; + + /* The serialized state of the core at this frame, before input */ void *state; - uint32_t real_input_state[UDP_WORDS_PER_FRAME - 1]; - uint32_t simulated_input_state[UDP_WORDS_PER_FRAME - 1]; - uint32_t self_state[UDP_WORDS_PER_FRAME - 1]; + /* The CRC-32 of the serialized state if we've calculated it, else 0 */ + uint32_t crc; - bool is_simulated; + uint32_t real_input_state[WORDS_PER_FRAME - 1]; + uint32_t simulated_input_state[WORDS_PER_FRAME - 1]; + uint32_t self_state[WORDS_PER_FRAME - 1]; + + /* Have we read local input? */ + bool have_local; + + /* Have we read the real remote input? */ + bool have_remote; + + /* Is the current state as of self_frame_count using the real remote data? */ bool used_real; }; struct netplay_callbacks { - void (*pre_frame) (netplay_t *netplay); + bool (*pre_frame) (netplay_t *netplay); void (*post_frame)(netplay_t *netplay); bool (*info_cb) (netplay_t *netplay, unsigned frames); }; +enum rarch_netplay_stall_reasons +{ + RARCH_NETPLAY_STALL_NONE = 0, + RARCH_NETPLAY_STALL_RUNNING_FAST +}; + struct netplay { char nick[32]; @@ -65,8 +86,6 @@ struct netplay struct retro_callbacks cbs; /* TCP connection for state sending, etc. Also used for commands */ int fd; - /* UDP connection for game state updates. */ - int udp_fd; /* Which port is governed by netplay (other user)? */ unsigned port; bool has_connection; @@ -81,23 +100,34 @@ struct netplay /* Pointer to where we are reading. * Generally, other_ptr <= read_ptr <= self_ptr. */ size_t read_ptr; - /* A temporary pointer used on replay. */ - size_t tmp_ptr; + /* A pointer used temporarily for replay. */ + size_t replay_ptr; size_t state_size; /* Are we replaying old frames? */ bool is_replay; + /* We don't want to poll several times on a frame. */ bool can_poll; - /* To compat UDP packet loss we also send - * old data along with the packets. */ - uint32_t packet_buffer[UDP_FRAME_PACKETS * UDP_WORDS_PER_FRAME]; - uint32_t frame_count; + /* Force a rewind to other_frame_count/other_ptr. This is for synchronized + * events, such as player flipping or savestate loading. */ + bool force_rewind; + + /* Force our state to be sent to the other side. Used when they request a + * savestate, to send at the next pre-frame. */ + bool force_send_savestate; + + /* Have we requested a savestate as a sync point? */ + bool savestate_request_outstanding; + + /* A buffer for outgoing input packets. */ + uint32_t packet_buffer[2 + WORDS_PER_FRAME]; + uint32_t self_frame_count; uint32_t read_frame_count; uint32_t other_frame_count; - uint32_t tmp_frame_count; + uint32_t replay_frame_count; struct addrinfo *addr; struct sockaddr_storage their_addr; bool has_client_addr; @@ -108,29 +138,35 @@ struct netplay struct { bool enabled; int fds[MAX_SPECTATORS]; + uint32_t frames[MAX_SPECTATORS]; uint16_t *input; size_t input_ptr; size_t input_sz; } spectate; bool is_server; + /* User flipping - * Flipping state. If ptr >= flip_frame, we apply the flip. - * If not, we apply the opposite, effectively creating a trigger point. - * To avoid collition we need to make sure our client/host is synced up - * well after flip_frame before allowing another flip. */ + * Flipping state. If frame >= flip_frame, we apply the flip. + * If not, we apply the opposite, effectively creating a trigger point. */ bool flip; uint32_t flip_frame; /* Netplay pausing */ - bool pause; - uint32_t pause_frame; + bool local_paused; + bool remote_paused; + + /* And stalling */ + uint32_t stall_frames; + int stall; + retro_time_t stall_time; + + /* Frequency with which to check CRCs */ + uint32_t check_frames; struct netplay_callbacks* net_cbs; }; -extern void *netplay_data; - struct netplay_callbacks* netplay_get_cbs_net(void); struct netplay_callbacks* netplay_get_cbs_spectate(void); @@ -158,4 +194,12 @@ bool netplay_is_server(netplay_t* netplay); bool netplay_is_spectate(netplay_t* netplay); +bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta, uint32_t frame); + +uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta); + +bool netplay_cmd_crc(netplay_t *netplay, struct delta_frame *delta); + +bool netplay_cmd_request_savestate(netplay_t *netplay); + #endif diff --git a/network/netplay/netplay_spectate.c b/network/netplay/netplay_spectate.c index c122b20b88..e6dbbd8aac 100644 --- a/network/netplay/netplay_spectate.c +++ b/network/netplay/netplay_spectate.c @@ -1,6 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2016 - Daniel De Matteis + * Copyright (C) 2016 - Gregor Richards * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- @@ -14,10 +15,8 @@ * If not, see . */ +#include #include -#include -#include -#include #include #include @@ -25,157 +24,266 @@ #include "netplay_private.h" -#include "../../runloop.h" +#include "retro_assert.h" + +#include "../../autosave.h" /** - * netplay_pre_frame_spectate: + * netplay_spectate_pre_frame: * @netplay : pointer to netplay object * - * Pre-frame for Netplay (spectate mode version). + * Pre-frame for Netplay (spectator version). **/ -static void netplay_spectate_pre_frame(netplay_t *netplay) +static bool netplay_spectate_pre_frame(netplay_t *netplay) { - unsigned i; - uint32_t *header; - int new_fd, idx, bufsize; - size_t header_size; - struct sockaddr_storage their_addr; - socklen_t addr_size; - fd_set fds; - struct timeval tmp_tv = {0}; - - if (!netplay_is_server(netplay)) - return; - - FD_ZERO(&fds); - FD_SET(netplay->fd, &fds); - - if (socket_select(netplay->fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0) - return; - - if (!FD_ISSET(netplay->fd, &fds)) - return; - - addr_size = sizeof(their_addr); - new_fd = accept(netplay->fd, (struct sockaddr*)&their_addr, &addr_size); - if (new_fd < 0) + if (netplay_is_server(netplay)) { - RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_ACCEPT_INCOMING_SPECTATOR)); - return; - } + fd_set fds; + struct timeval tmp_tv = {0}; + int new_fd, idx, i; + struct sockaddr_storage their_addr; + socklen_t addr_size; + retro_ctx_serialize_info_t serial_info; + uint32_t header[3]; - idx = -1; - for (i = 0; i < MAX_SPECTATORS; i++) - { - if (netplay->spectate.fds[i] == -1) + netplay->can_poll = true; + input_poll_net(); + + /* Send our input to any connected spectators */ + for (i = 0; i < MAX_SPECTATORS; i++) { - idx = i; - break; + if (netplay->spectate.fds[i] >= 0) + { + netplay->packet_buffer[2] = htonl(netplay->self_frame_count - netplay->spectate.frames[i]); + if (!socket_send_all_blocking(netplay->spectate.fds[i], netplay->packet_buffer, sizeof(netplay->packet_buffer), false)) + { + socket_close(netplay->spectate.fds[i]); + netplay->spectate.fds[i] = -1; + } + } } - } - /* No vacant client streams :( */ - if (idx == -1) + /* Check for connections */ + FD_ZERO(&fds); + FD_SET(netplay->fd, &fds); + if (socket_select(netplay->fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0) + return true; + + if (!FD_ISSET(netplay->fd, &fds)) + return true; + + addr_size = sizeof(their_addr); + new_fd = accept(netplay->fd, (struct sockaddr*)&their_addr, &addr_size); + if (new_fd < 0) + { + RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_ACCEPT_INCOMING_SPECTATOR)); + return true; + } + + idx = -1; + for (i = 0; i < MAX_SPECTATORS; i++) + { + if (netplay->spectate.fds[i] == -1) + { + idx = i; + break; + } + } + + /* No vacant client streams :( */ + if (idx == -1) + { + socket_close(new_fd); + return true; + } + + if (!netplay_get_nickname(netplay, new_fd)) + { + RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT)); + socket_close(new_fd); + return true; + } + + if (!netplay_send_nickname(netplay, new_fd)) + { + RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_CLIENT)); + socket_close(new_fd); + return true; + } + + /* Start them at the current frame */ + netplay->spectate.frames[idx] = netplay->self_frame_count; + serial_info.data_const = NULL; + serial_info.data = netplay->buffer[netplay->self_ptr].state; + serial_info.size = netplay->state_size; + if (core_serialize(&serial_info)) + { + /* Send them the savestate */ + header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE); + header[1] = htonl(serial_info.size + sizeof(uint32_t)); + header[2] = htonl(0); + if (!socket_send_all_blocking(new_fd, header, sizeof(header), false)) + { + socket_close(new_fd); + return true; + } + + if (!socket_send_all_blocking(new_fd, serial_info.data, serial_info.size, false)) + { + socket_close(new_fd); + return true; + } + } + + /* And send them this frame's input */ + netplay->packet_buffer[2] = htonl(0); + if (!socket_send_all_blocking(new_fd, netplay->packet_buffer, sizeof(netplay->packet_buffer), false)) + { + socket_close(new_fd); + return true; + } + + netplay->spectate.fds[idx] = new_fd; + + } + else { - socket_close(new_fd); - return; + if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->self_ptr], netplay->self_frame_count)) + { + /* Mark our own data as already read, so we ignore local input */ + netplay->buffer[netplay->self_ptr].have_local = true; + } + + netplay->can_poll = true; + input_poll_net(); + + /* Only proceed if we have data */ + if (netplay->read_frame_count <= netplay->self_frame_count) + return false; + } - if (!netplay_get_nickname(netplay, new_fd)) - { - RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT)); - socket_close(new_fd); - return; - } - - if (!netplay_send_nickname(netplay, new_fd)) - { - RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_CLIENT)); - socket_close(new_fd); - return; - } - - header = netplay_bsv_header_generate(&header_size, - netplay_impl_magic()); - - if (!header) - { - RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_GENERATE_BSV_HEADER)); - socket_close(new_fd); - return; - } - - bufsize = header_size; - setsockopt(new_fd, SOL_SOCKET, SO_SNDBUF, (const char*)&bufsize, - sizeof(int)); - - if (!socket_send_all_blocking(new_fd, header, header_size, false)) - { - RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_HEADER_TO_CLIENT)); - socket_close(new_fd); - free(header); - return; - } - - free(header); - netplay->spectate.fds[idx] = new_fd; - -#ifndef HAVE_SOCKET_LEGACY - netplay_log_connection(&their_addr, idx, netplay->other_nick); -#endif + return true; } /** - * netplay_post_frame_spectate: + * netplay_spectate_post_frame: * @netplay : pointer to netplay object * - * Post-frame for Netplay (spectate mode version). - * We check if we have new input and replay from recorded input. + * Post-frame for Netplay (spectator version). + * Not much here, just fast forward if we're behind the server. **/ static void netplay_spectate_post_frame(netplay_t *netplay) { - unsigned i; + netplay->self_ptr = NEXT_PTR(netplay->self_ptr); + netplay->self_frame_count++; - if (!netplay_is_server(netplay)) - return; - - for (i = 0; i < MAX_SPECTATORS; i++) + if (netplay_is_server(netplay)) { - char msg[128]; + /* Not expecting any client data */ + netplay->read_ptr = netplay->other_ptr = netplay->self_ptr; + netplay->read_frame_count = netplay->other_frame_count = netplay->self_frame_count; - if (netplay->spectate.fds[i] == -1) - continue; - - if (socket_send_all_blocking(netplay->spectate.fds[i], - netplay->spectate.input, - netplay->spectate.input_ptr * sizeof(int16_t), - false)) - continue; - - RARCH_LOG("Client (#%u) disconnected ...\n", i); - - snprintf(msg, sizeof(msg), "Client (#%u) disconnected.", i); - runloop_msg_queue_push(msg, 1, 180, false); - - socket_close(netplay->spectate.fds[i]); - netplay->spectate.fds[i] = -1; - break; } + else + { + /* If we must rewind, it's because we got a save state */ + if (netplay->force_rewind) + { + retro_ctx_serialize_info_t serial_info; - netplay->spectate.input_ptr = 0; + /* Replay frames. */ + netplay->is_replay = true; + netplay->replay_ptr = netplay->other_ptr; + netplay->replay_frame_count = netplay->other_frame_count; + + serial_info.data = NULL; + serial_info.data_const = netplay->buffer[netplay->replay_ptr].state; + serial_info.size = netplay->state_size; + + core_unserialize(&serial_info); + + while (netplay->replay_frame_count < netplay->self_frame_count) + { +#if defined(HAVE_THREADS) + autosave_lock(); +#endif + core_run(); +#if defined(HAVE_THREADS) + autosave_unlock(); +#endif + netplay->replay_ptr = NEXT_PTR(netplay->replay_ptr); + netplay->replay_frame_count++; + } + + netplay->is_replay = false; + netplay->force_rewind = false; + } + + /* We're in sync by definition */ + if (netplay->read_frame_count < netplay->self_frame_count) + { + netplay->other_ptr = netplay->read_ptr; + netplay->other_frame_count = netplay->read_frame_count; + } + else + { + netplay->other_ptr = netplay->self_ptr; + netplay->other_frame_count = netplay->self_frame_count; + } + + /* If the server gets significantly ahead, skip to catch up */ + if (netplay->self_frame_count + netplay->stall_frames <= netplay->read_frame_count) + { + /* "Replay" into the future */ + netplay->is_replay = true; + netplay->replay_ptr = netplay->self_ptr; + netplay->replay_frame_count = netplay->self_frame_count; + + while (netplay->replay_frame_count < netplay->read_frame_count - 1) + { +#if defined(HAVE_THREADS) + autosave_lock(); +#endif + core_run(); +#if defined(HAVE_THREADS) + autosave_unlock(); +#endif + + netplay->replay_ptr = NEXT_PTR(netplay->replay_ptr); + netplay->replay_frame_count++; + netplay->self_ptr = netplay->replay_ptr; + netplay->self_frame_count = netplay->replay_frame_count; + } + + netplay->is_replay = false; + } + + } } -static bool netplay_spectate_info_cb(netplay_t *netplay, unsigned frames) +static bool netplay_spectate_info_cb(netplay_t* netplay, unsigned frames) { - unsigned i; - if(netplay_is_server(netplay)) + if (netplay_is_server(netplay)) { - if(!netplay_get_info(netplay)) + int i; + for (i = 0; i < MAX_SPECTATORS; i++) + { + netplay->spectate.fds[i] = -1; + } + + } + else + { + if (!netplay_send_nickname(netplay, netplay->fd)) + return false; + + if (!netplay_get_nickname(netplay, netplay->fd)) return false; } - for (i = 0; i < MAX_SPECTATORS; i++) - netplay->spectate.fds[i] = -1; + netplay->has_connection = true; + return true; } @@ -186,6 +294,5 @@ struct netplay_callbacks* netplay_get_cbs_spectate(void) &netplay_spectate_post_frame, &netplay_spectate_info_cb }; - return &cbs; } diff --git a/patch.c b/patch.c index 39794765a3..c302295e41 100644 --- a/patch.c +++ b/patch.c @@ -24,14 +24,11 @@ #include #include -#include #include #include #include -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include #include "msg_hash.h" #include "patch.h" @@ -88,12 +85,8 @@ typedef enum patch_error (*patch_func_t)(const uint8_t*, size_t, static uint8_t bps_read(struct bps_data *bps) { uint8_t data = bps->modify_data[bps->modify_offset++]; -#ifdef HAVE_ZLIB - const struct file_archive_file_backend *stream_backend = - file_archive_get_default_file_backend(); - bps->modify_checksum = ~stream_backend->stream_crc_calculate( - ~bps->modify_checksum, &data, 1); -#endif + bps->modify_checksum = ~(encoding_crc32( + ~bps->modify_checksum, &data, 1)); return data; } @@ -116,17 +109,11 @@ static uint64_t bps_decode(struct bps_data *bps) static void bps_write(struct bps_data *bps, uint8_t data) { -#ifdef HAVE_ZLIB - const struct file_archive_file_backend *stream_backend = - file_archive_get_default_file_backend(); -#endif if (!bps) return; bps->target_data[bps->output_offset++] = data; -#ifdef HAVE_ZLIB - bps->target_checksum = ~stream_backend->stream_crc_calculate(~bps->target_checksum, &data, 1); -#endif + bps->target_checksum = ~(encoding_crc32(~bps->target_checksum, &data, 1)); } static enum patch_error bps_apply_patch( @@ -135,25 +122,24 @@ static enum patch_error bps_apply_patch( uint8_t *target_data, size_t *target_length) { size_t i; - size_t modify_source_size, modify_target_size, - modify_markup_size; + uint32_t checksum; + size_t modify_source_size; + size_t modify_target_size; + size_t modify_markup_size; struct bps_data bps = {0}; - uint32_t modify_source_checksum = 0, modify_target_checksum = 0, - modify_modify_checksum = 0, checksum; -#ifdef HAVE_ZLIB - const struct file_archive_file_backend *stream_backend = - file_archive_get_default_file_backend(); -#endif + uint32_t modify_source_checksum = 0; + uint32_t modify_target_checksum = 0; + uint32_t modify_modify_checksum = 0; if (modify_length < 19) return PATCH_PATCH_TOO_SMALL; - bps.modify_data = modify_data; - bps.modify_length = modify_length; - bps.target_data = target_data; - bps.target_length = *target_length; - bps.source_data = source_data; - bps.source_length = source_length; + bps.modify_data = modify_data; + bps.modify_length = modify_length; + bps.target_data = target_data; + bps.target_length = *target_length; + bps.source_data = source_data; + bps.source_length = source_length; bps.modify_checksum = ~0; bps.target_checksum = ~0; @@ -161,9 +147,9 @@ static enum patch_error bps_apply_patch( (bps_read(&bps) != 'S') || (bps_read(&bps) != '1')) return PATCH_PATCH_INVALID_HEADER; - modify_source_size = bps_decode(&bps); - modify_target_size = bps_decode(&bps); - modify_markup_size = bps_decode(&bps); + modify_source_size = bps_decode(&bps); + modify_target_size = bps_decode(&bps); + modify_markup_size = bps_decode(&bps); for (i = 0; i < modify_markup_size; i++) bps_read(&bps); @@ -229,19 +215,16 @@ static enum patch_error bps_apply_patch( for (i = 0; i < 32; i += 8) modify_modify_checksum |= bps_read(&bps) << i; -#ifdef HAVE_ZLIB - bps.source_checksum = stream_backend->stream_crc_calculate(0, + bps.source_checksum = encoding_crc32(0, bps.source_data, bps.source_length); -#else - return PATCH_PATCH_CHECKSUM_INVALID; -#endif - bps.target_checksum = ~bps.target_checksum; if (bps.source_checksum != modify_source_checksum) return PATCH_SOURCE_CHECKSUM_INVALID; + if (bps.target_checksum != modify_target_checksum) return PATCH_TARGET_CHECKSUM_INVALID; + if (checksum != modify_modify_checksum) return PATCH_PATCH_CHECKSUM_INVALID; @@ -252,18 +235,11 @@ static enum patch_error bps_apply_patch( static uint8_t ups_patch_read(struct ups_data *data) { -#ifdef HAVE_ZLIB - const struct file_archive_file_backend *stream_backend = - file_archive_get_default_file_backend(); -#endif - if (data && data->patch_offset < data->patch_length) { uint8_t n = data->patch_data[data->patch_offset++]; -#ifdef HAVE_ZLIB data->patch_checksum = - ~stream_backend->stream_crc_calculate(~data->patch_checksum, &n, 1); -#endif + ~(encoding_crc32(~data->patch_checksum, &n, 1)); return n; } return 0x00; @@ -271,18 +247,11 @@ static uint8_t ups_patch_read(struct ups_data *data) static uint8_t ups_source_read(struct ups_data *data) { -#ifdef HAVE_ZLIB - const struct file_archive_file_backend *stream_backend = - file_archive_get_default_file_backend(); -#endif - if (data && data->source_offset < data->source_length) { uint8_t n = data->source_data[data->source_offset++]; -#ifdef HAVE_ZLIB - data->source_checksum = - ~stream_backend->stream_crc_calculate(~data->source_checksum, &n, 1); -#endif + data->source_checksum = + ~(encoding_crc32(~data->source_checksum, &n, 1)); return n; } return 0x00; @@ -290,18 +259,12 @@ static uint8_t ups_source_read(struct ups_data *data) static void ups_target_write(struct ups_data *data, uint8_t n) { -#ifdef HAVE_ZLIB - const struct file_archive_file_backend *stream_backend = - file_archive_get_default_file_backend(); -#endif if (data && data->target_offset < data->target_length) { data->target_data[data->target_offset] = n; -#ifdef HAVE_ZLIB data->target_checksum = - ~stream_backend->stream_crc_calculate(~data->target_checksum, &n, 1); -#endif + ~(encoding_crc32(~data->target_checksum, &n, 1)); } if (data) @@ -330,10 +293,13 @@ static enum patch_error ups_apply_patch( uint8_t *targetdata, size_t *targetlength) { size_t i; - unsigned source_read_length, target_read_length; - uint32_t patch_read_checksum = 0, source_read_checksum = 0, - target_read_checksum = 0, patch_result_checksum; - struct ups_data data = {0}; + unsigned source_read_length; + unsigned target_read_length; + uint32_t patch_result_checksum; + uint32_t patch_read_checksum = 0; + uint32_t source_read_checksum = 0; + uint32_t target_read_checksum = 0; + struct ups_data data = {0}; data.patch_data = patchdata; data.source_data = sourcedata; @@ -521,8 +487,10 @@ static bool apply_patch_content(uint8_t **buf, if (!path_is_valid(patch_path)) return false; + if (!filestream_read_file(patch_path, &patch_data, &patch_size)) return false; + if (patch_size < 0) { free(patch_data); diff --git a/paths.c b/paths.c new file mode 100644 index 0000000000..b0d28242a4 --- /dev/null +++ b/paths.c @@ -0,0 +1,640 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_NETPLAY +#include "network/netplay/netplay.h" +#endif + +#include "dirs.h" +#include "paths.h" + +#include "configuration.h" +#include "command.h" +#include "content.h" +#include "dynamic.h" +#include "movie.h" +#include "file_path_special.h" + +#include "core.h" +#include "msg_hash.h" +#include "retroarch.h" +#include "runloop.h" +#include "verbosity.h" +#include "tasks/tasks_internal.h" + +#define MENU_VALUE_NO_CORE 0x7d5472cbU + +static char path_main_basename[PATH_MAX_LENGTH] = {0} +; +static char current_savefile_dir[PATH_MAX_LENGTH] = {0}; +static char path_libretro[PATH_MAX_LENGTH] = {0}; +static char path_config_file[PATH_MAX_LENGTH] = {0}; +static char path_config_append_file[PATH_MAX_LENGTH] = {0}; +/* Config file associated with per-core configs. */ +static char path_core_options_file[PATH_MAX_LENGTH] = {0}; + +void path_set_redirect(void) +{ + char current_savestate_dir[PATH_MAX_LENGTH] = {0}; + uint32_t global_library_name_hash = 0; + bool check_global_library_name_hash = false; + global_t *global = global_get_ptr(); + rarch_system_info_t *info = NULL; + const char *old_savefile_dir = NULL; + const char *old_savestate_dir = NULL; + + runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &info); + + if (!global) + return; + + old_savefile_dir = dir_get_savefile(); + old_savestate_dir = dir_get_savestate(); + + if (info->info.library_name && + !string_is_empty(info->info.library_name)) + global_library_name_hash = + msg_hash_calculate(info->info.library_name); + + /* Initialize current save directories + * with the values from the config. */ + strlcpy(current_savefile_dir, + old_savefile_dir, + sizeof(current_savefile_dir)); + strlcpy(current_savestate_dir, + old_savestate_dir, + sizeof(current_savestate_dir)); + + check_global_library_name_hash = (global_library_name_hash != 0); +#ifdef HAVE_MENU + check_global_library_name_hash = check_global_library_name_hash && + (global_library_name_hash != MENU_VALUE_NO_CORE); +#endif + + if (check_global_library_name_hash) + { + settings_t *settings = config_get_ptr(); + + /* per-core saves: append the library_name to the save location */ + if (settings->sort_savefiles_enable + && !string_is_empty(old_savefile_dir)) + { + fill_pathname_join( + current_savefile_dir, + old_savefile_dir, + info->info.library_name, + sizeof(current_savefile_dir)); + + /* If path doesn't exist, try to create it, + * if everything fails revert to the original path. */ + if(!path_is_directory(current_savefile_dir) + && !string_is_empty(current_savefile_dir)) + { + path_mkdir(current_savefile_dir); + if(!path_is_directory(current_savefile_dir)) + { + RARCH_LOG("%s %s\n", + msg_hash_to_str(MSG_REVERTING_SAVEFILE_DIRECTORY_TO), + old_savefile_dir); + + strlcpy(current_savefile_dir, + old_savefile_dir, + sizeof(current_savefile_dir)); + } + } + } + + /* per-core states: append the library_name to the save location */ + if (settings->sort_savestates_enable + && !string_is_empty(old_savestate_dir)) + { + fill_pathname_join( + current_savestate_dir, + old_savestate_dir, + info->info.library_name, + sizeof(current_savestate_dir)); + + /* If path doesn't exist, try to create it. + * If everything fails, revert to the original path. */ + if(!path_is_directory(current_savestate_dir) && + !string_is_empty(current_savestate_dir)) + { + path_mkdir(current_savestate_dir); + if(!path_is_directory(current_savestate_dir)) + { + RARCH_LOG("%s %s\n", + msg_hash_to_str(MSG_REVERTING_SAVESTATE_DIRECTORY_TO), + old_savestate_dir); + strlcpy(current_savestate_dir, + old_savestate_dir, + sizeof(current_savestate_dir)); + } + } + } + } + + /* Set savefile directory if empty based on content directory */ + if (string_is_empty(current_savefile_dir)) + { + strlcpy(current_savefile_dir, path_main_basename, + sizeof(current_savefile_dir)); + path_basedir(current_savefile_dir); + } + + if(path_is_directory(current_savefile_dir)) + strlcpy(global->name.savefile, current_savefile_dir, + sizeof(global->name.savefile)); + + if(path_is_directory(current_savestate_dir)) + strlcpy(global->name.savestate, current_savestate_dir, + sizeof(global->name.savestate)); + + if (path_is_directory(global->name.savefile)) + { + fill_pathname_dir(global->name.savefile, path_main_basename, + file_path_str(FILE_PATH_SRM_EXTENSION), + sizeof(global->name.savefile)); + RARCH_LOG("%s \"%s\".\n", + msg_hash_to_str(MSG_REDIRECTING_SAVEFILE_TO), + global->name.savefile); + } + + if (path_is_directory(global->name.savestate)) + { + fill_pathname_dir(global->name.savestate, path_main_basename, + file_path_str(FILE_PATH_STATE_EXTENSION), + sizeof(global->name.savestate)); + RARCH_LOG("%s \"%s\".\n", + msg_hash_to_str(MSG_REDIRECTING_SAVESTATE_TO), + global->name.savestate); + } + + if (path_is_directory(global->name.cheatfile)) + { + fill_pathname_dir(global->name.cheatfile, path_main_basename, + file_path_str(FILE_PATH_STATE_EXTENSION), + sizeof(global->name.cheatfile)); + RARCH_LOG("%s \"%s\".\n", + msg_hash_to_str(MSG_REDIRECTING_CHEATFILE_TO), + global->name.cheatfile); + } +} + +void path_set_basename(const char *path) +{ + char *dst = NULL; + + runloop_ctl(RUNLOOP_CTL_SET_CONTENT_PATH, (void*)path); + strlcpy(path_main_basename, path, sizeof(path_main_basename)); + +#ifdef HAVE_COMPRESSION + /* Removing extension is a bit tricky for compressed files. + * Basename means: + * /file/to/path/game.extension should be: + * /file/to/path/game + * + * Two things to consider here are: /file/to/path/ is expected + * to be a directory and "game" is a single file. This is used for + * states and srm default paths. + * + * For compressed files we have: + * + * /file/to/path/comp.7z#game.extension and + * /file/to/path/comp.7z#folder/game.extension + * + * The choice I take here is: + * /file/to/path/game as basename. We might end up in a writable + * directory then and the name of srm and states are meaningful. + * + */ + path_basedir(path_main_basename); + fill_pathname_dir(path_main_basename, path, "", sizeof(path_main_basename)); +#endif + + if ((dst = strrchr(path_main_basename, '.'))) + *dst = '\0'; +} + +const char *path_get_current_savefile_dir(void) +{ + char *ret = current_savefile_dir; + + /* try to infer the path in case it's still empty by calling + path_set_redirect */ + if (string_is_empty(ret) && !content_does_not_need_content()) + path_set_redirect(); + + return ret; +} + +void path_set_special(char **argv, unsigned num_content) +{ + unsigned i; + union string_list_elem_attr attr; + global_t *global = global_get_ptr(); + + /* First content file is the significant one. */ + path_set_basename(argv[0]); + + global->subsystem_fullpaths = string_list_new(); + retro_assert(global->subsystem_fullpaths); + + attr.i = 0; + + for (i = 0; i < num_content; i++) + string_list_append(global->subsystem_fullpaths, argv[i], attr); + + /* We defer SRAM path updates until we can resolve it. + * It is more complicated for special content types. */ + + if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_STATE_PATH)) + fill_pathname_noext(global->name.savestate, path_main_basename, + file_path_str(FILE_PATH_STATE_EXTENSION), + sizeof(global->name.savestate)); + + if (path_is_directory(global->name.savestate)) + { + fill_pathname_dir(global->name.savestate, path_main_basename, + file_path_str(FILE_PATH_STATE_EXTENSION), + sizeof(global->name.savestate)); + RARCH_LOG("%s \"%s\".\n", + msg_hash_to_str(MSG_REDIRECTING_SAVESTATE_TO), + global->name.savestate); + } +} + +static bool path_init_subsystem(void) +{ + unsigned i, j, num_content; + const struct retro_subsystem_info *info = NULL; + rarch_system_info_t *system = NULL; + global_t *global = global_get_ptr(); + + runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system); + + if (!system) + return false; + + if (string_is_empty(global->subsystem)) + return false; + + /* For subsystems, we know exactly which RAM types are supported. */ + + info = libretro_find_subsystem_info( + system->subsystem.data, + system->subsystem.size, + global->subsystem); + + /* We'll handle this error gracefully later. */ + num_content = MIN(info ? info->num_roms : 0, + global->subsystem_fullpaths ? + global->subsystem_fullpaths->size : 0); + + + for (i = 0; i < num_content; i++) + { + for (j = 0; j < info->roms[i].num_memory; j++) + { + union string_list_elem_attr attr; + char path[PATH_MAX_LENGTH] = {0}; + char ext[32] = {0}; + const struct retro_subsystem_memory_info *mem = + (const struct retro_subsystem_memory_info*) + &info->roms[i].memory[j]; + + snprintf(ext, sizeof(ext), ".%s", mem->extension); + + if (path_is_directory(dir_get_savefile())) + { + /* Use SRAM dir */ + /* Redirect content fullpath to save directory. */ + strlcpy(path, dir_get_savefile(), sizeof(path)); + fill_pathname_dir(path, + global->subsystem_fullpaths->elems[i].data, ext, + sizeof(path)); + } + else + { + fill_pathname(path, global->subsystem_fullpaths->elems[i].data, + ext, sizeof(path)); + } + + attr.i = mem->type; + string_list_append((struct string_list*)savefile_ptr_get(), path, attr); + } + } + + /* Let other relevant paths be inferred from the main SRAM location. */ + if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_SAVE_PATH)) + fill_pathname_noext(global->name.savefile, + path_main_basename, + file_path_str(FILE_PATH_SRM_EXTENSION), + sizeof(global->name.savefile)); + + if (path_is_directory(global->name.savefile)) + { + fill_pathname_dir(global->name.savefile, + path_main_basename, + file_path_str(FILE_PATH_SRM_EXTENSION), + sizeof(global->name.savefile)); + RARCH_LOG("%s \"%s\".\n", + msg_hash_to_str(MSG_REDIRECTING_SAVEFILE_TO), + global->name.savefile); + } + + return true; +} + +void path_init_savefile(void) +{ + global_t *global = global_get_ptr(); + + if (!global) + return; + + global->sram.use = global->sram.use && !global->sram.save_disable; +#ifdef HAVE_NETPLAY + global->sram.use = global->sram.use && + (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL) + || !global->netplay.is_client); +#endif + + if (!global->sram.use) + RARCH_LOG("%s\n", + msg_hash_to_str(MSG_SRAM_WILL_NOT_BE_SAVED)); + + if (global->sram.use) + command_event(CMD_EVENT_AUTOSAVE_INIT, NULL); +} + +static void path_init_savefile_internal(void) +{ + path_deinit_savefile(); + + path_init_savefile_new(); + + if (!path_init_subsystem()) + path_init_savefile_rtc(); +} + +void path_set_names(const char *path) +{ + global_t *global = global_get_ptr(); + + path_set_basename(path); + + if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_SAVE_PATH)) + fill_pathname_noext(global->name.savefile, path_main_basename, + file_path_str(FILE_PATH_SRM_EXTENSION), sizeof(global->name.savefile)); + + if (!retroarch_override_setting_is_set(RARCH_OVERRIDE_SETTING_STATE_PATH)) + fill_pathname_noext(global->name.savestate, path_main_basename, + file_path_str(FILE_PATH_STATE_EXTENSION), sizeof(global->name.savestate)); + + fill_pathname_noext(global->name.cheatfile, path_main_basename, + file_path_str(FILE_PATH_CHT_EXTENSION), sizeof(global->name.cheatfile)); + + path_set_redirect(); +} + +void path_fill_names(void) +{ + global_t *global = global_get_ptr(); + + path_init_savefile_internal(); + bsv_movie_set_path(global->name.savefile); + + if (string_is_empty(path_main_basename)) + return; + + if (string_is_empty(global->name.ups)) + fill_pathname_noext(global->name.ups, path_main_basename, + file_path_str(FILE_PATH_UPS_EXTENSION), + sizeof(global->name.ups)); + + if (string_is_empty(global->name.bps)) + fill_pathname_noext(global->name.bps, path_main_basename, + file_path_str(FILE_PATH_BPS_EXTENSION), + sizeof(global->name.bps)); + + if (string_is_empty(global->name.ips)) + fill_pathname_noext(global->name.ips, path_main_basename, + file_path_str(FILE_PATH_IPS_EXTENSION), + sizeof(global->name.ips)); +} + +/* Core file path */ + +const char *path_get_basename(void) +{ + return path_main_basename; +} + +char *path_get_core_ptr(void) +{ + return path_libretro; +} + +const char *path_get_core(void) +{ + return path_libretro; +} + +bool path_is_core_empty(void) +{ + return !path_libretro[0]; +} + +size_t path_get_core_size(void) +{ + return sizeof(path_libretro); +} + +void path_set_core(const char *path) +{ + strlcpy(path_libretro, path, sizeof(path_libretro)); +} + +void path_clear_core(void) +{ + *path_libretro = '\0'; +} + +/* Config file path */ + +bool path_is_config_empty(void) +{ + if (string_is_empty(path_config_file)) + return true; + + return false; +} + +void path_set_config(const char *path) +{ + strlcpy(path_config_file, path, sizeof(path_config_file)); +} + +const char *path_get_config(void) +{ + if (!path_is_config_empty()) + return path_config_file; + + return NULL; +} + +void path_clear_config(void) +{ + *path_config_file = '\0'; +} + +void path_clear_basename(void) +{ + *path_main_basename = '\0'; +} + +/* Core options file path */ + +bool path_is_core_options_empty(void) +{ + if (string_is_empty(path_core_options_file)) + return true; + + return false; +} + +void path_clear_core_options(void) +{ + *path_core_options_file = '\0'; +} + +void path_set_core_options(const char *path) +{ + strlcpy(path_core_options_file, path, sizeof(path_core_options_file)); +} + +const char *path_get_core_options(void) +{ + if (!path_is_core_options_empty()) + return path_core_options_file; + + return NULL; +} + +/* Append config file path */ + +bool path_is_config_append_empty(void) +{ + if (string_is_empty(path_config_append_file)) + return true; + + return false; +} + +void path_clear_config_append(void) +{ + *path_config_append_file = '\0'; +} + +void path_set_config_append(const char *path) +{ + strlcpy(path_config_append_file, path, sizeof(path_config_append_file)); +} + +const char *path_get_config_append(void) +{ + if (!path_is_config_append_empty()) + return path_config_append_file; + + return NULL; +} + +void path_clear_all(void) +{ + path_clear_config(); + path_clear_config_append(); + path_clear_core_options(); + + path_clear_basename(); +} + +enum rarch_content_type path_is_media_type(const char *path) +{ + char ext_lower[PATH_MAX_LENGTH] = {0}; + + strlcpy(ext_lower, path_get_extension(path), sizeof(ext_lower)); + + string_to_lower(ext_lower); + + switch (msg_hash_to_file_type(msg_hash_calculate(ext_lower))) + { +#ifdef HAVE_FFMPEG + case FILE_TYPE_OGM: + case FILE_TYPE_MKV: + case FILE_TYPE_AVI: + case FILE_TYPE_MP4: + case FILE_TYPE_FLV: + case FILE_TYPE_WEBM: + case FILE_TYPE_3GP: + case FILE_TYPE_3G2: + case FILE_TYPE_F4F: + case FILE_TYPE_F4V: + case FILE_TYPE_MOV: + case FILE_TYPE_WMV: + case FILE_TYPE_MPG: + case FILE_TYPE_MPEG: + case FILE_TYPE_VOB: + case FILE_TYPE_ASF: + case FILE_TYPE_DIVX: + case FILE_TYPE_M2P: + case FILE_TYPE_M2TS: + case FILE_TYPE_PS: + case FILE_TYPE_TS: + case FILE_TYPE_MXF: + return RARCH_CONTENT_MOVIE; + case FILE_TYPE_WMA: + case FILE_TYPE_OGG: + case FILE_TYPE_MP3: + case FILE_TYPE_M4A: + case FILE_TYPE_FLAC: + case FILE_TYPE_WAV: + return RARCH_CONTENT_MUSIC; +#endif +#ifdef HAVE_IMAGEVIEWER + case FILE_TYPE_JPEG: + case FILE_TYPE_PNG: + case FILE_TYPE_TGA: + case FILE_TYPE_BMP: + return RARCH_CONTENT_IMAGE; +#endif + case FILE_TYPE_NONE: + default: + break; + } + + return RARCH_CONTENT_NONE; +} diff --git a/paths.h b/paths.h new file mode 100644 index 0000000000..d5079d63ea --- /dev/null +++ b/paths.h @@ -0,0 +1,108 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __PATHS_H +#define __PATHS_H + +#include +#include + +RETRO_BEGIN_DECLS + +enum rarch_content_type +{ + RARCH_CONTENT_NONE = 0, + RARCH_CONTENT_MOVIE, + RARCH_CONTENT_MUSIC, + RARCH_CONTENT_IMAGE +}; + +void path_deinit_savefile(void); + +void path_init_savefile(void); + +/* fill functions */ + +void path_fill_names(void); + +/* set functions */ + +void path_set_redirect(void); + +void path_set_names(const char *path); + +void path_set_special(char **argv, unsigned num_content); + +void path_set_basename(const char *path); + +void path_set_core(const char *path); + +void path_set_core_options(const char *path); + +void path_set_config(const char *path); + +void path_set_config_append(const char *path); + +/* get size functions */ + +size_t path_get_core_size(void); + +/* get ptr functions */ + +char *path_get_core_ptr(void); + +/* get functions */ + +const char *path_get_current_savefile_dir(void); + +const char *path_get_basename(void); + +const char *path_get_core(void); + +const char *path_get_core_options(void); + +const char *path_get_config(void); + +const char *path_get_config_append(void); + +/* clear functions */ + +void path_clear_basename(void); + +void path_clear_core(void); + +void path_clear_config(void); + +void path_clear_core_options(void); + +void path_clear_config_append(void); + +void path_clear_all(void); + +/* is functions */ + +bool path_is_core_empty(void); + +bool path_is_config_empty(void); + +bool path_is_core_options_empty(void); + +bool path_is_config_append_empty(void); + +enum rarch_content_type path_is_media_type(const char *path); + +RETRO_END_DECLS + +#endif diff --git a/pkg/emscripten/README.md b/pkg/emscripten/README.md new file mode 100644 index 0000000000..7fd125a155 --- /dev/null +++ b/pkg/emscripten/README.md @@ -0,0 +1,51 @@ +# RetroArch Web Player + +# Requirements +Most of the magic happens on the browser so nothing really on that regard + +I you want a self hosted version you need +- A web server, nginx/apache will do, unzip a nightly here: + https://buildbot.libretro.com/nightly/emscripten/ +- Move the template files from the embed folder to the top level dir, + the other templates are for specific sites +- A SSL certificate if you want to integrate with dropbox +- A dropbox application, you can't use ours on a self hosted version + +If you want assets for XMB, shaders, overlays you need to do the following: +- Grab the asset bundle: + https://bot.libretro.com/assets/frontend/bundle.zip +- Unzip it somewhere in your web server +- Generate an index of the folder with this script, save it inside the folder you want to serve + https://github.com/jvilk/BrowserFS/blob/master/tools/XHRIndexer.coffee +- Add the mount in the setupFileSystem function, look for the xfs blocks and + unconmment them and tweak them accordingly + +If you want to add your game directory so it serves content to your users you +need to do the following: +- Add the dir to your web server +- Generate an index with this script, save it inside the folder you want to serve + https://github.com/jvilk/BrowserFS/blob/master/tools/XHRIndexer.coffee +- Add a new mount in the setupFileSystem function, look for the xfs blocks and + unconmment them and tweak them accordingly + +```javascript + /* create an XmlHttpRequest filesystem for the bundled data */ + /* --> uncomment this if you want builtin assets for XMB, overlays, etc.*/ + var xfs1 = new BrowserFS.FileSystem.XmlHttpRequest + ("--your-assets-index-file-name--", "--your-index-url--"); + + /* create an XmlHttpRequest filesystem for content */ + /* --> uncomment this if you want to serve content.*/ + var xfs2 = new BrowserFS.FileSystem.XmlHttpRequest + ("--your-content-index-file-name--", "--your-index-url--"); + + + // lots and lots of code + // and then + + /* + mfs.mount('/home/web_user/retroarch/bundle', xfs1); + mfs.mount('/home/web_user/retroarch/userdata/content/', xfs2); + */ +} +``` \ No newline at end of file diff --git a/pkg/emscripten/proto.css b/pkg/emscripten/embed/embed.css similarity index 100% rename from pkg/emscripten/proto.css rename to pkg/emscripten/embed/embed.css diff --git a/pkg/emscripten/embed/embed.js b/pkg/emscripten/embed/embed.js new file mode 100644 index 0000000000..8b0e908a00 --- /dev/null +++ b/pkg/emscripten/embed/embed.js @@ -0,0 +1,385 @@ +/** + * RetroArch Web Player + * + * This provides the basic JavaScript for the RetroArch web player. + */ + +/* setup your key for dropbox support */ +var client = new Dropbox.Client({ key: "--your-api-key--" }); +var BrowserFS = browserfs; +var afs; + +var showError = function(error) { + switch (error.status) { + case Dropbox.ApiError.INVALID_TOKEN: + // If you're using dropbox.js, the only cause behind this error is that + // the user token expired. + // Get the user through the authentication flow again. + break; + + case Dropbox.ApiError.NOT_FOUND: + // The file or folder you tried to access is not in the user's Dropbox. + // Handling this error is specific to your application. + break; + + case Dropbox.ApiError.OVER_QUOTA: + // The user is over their Dropbox quota. + // Tell them their Dropbox is full. Refreshing the page won't help. + break; + + case Dropbox.ApiError.RATE_LIMITED: + // Too many API requests. Tell the user to try again later. + // Long-term, optimize your code to use fewer API calls. + break; + + case Dropbox.ApiError.NETWORK_ERROR: + // An error occurred at the XMLHttpRequest layer. + // Most likely, the user's network connection is down. + // API calls will not succeed until the user gets back online. + break; + + case Dropbox.ApiError.INVALID_PARAM: + case Dropbox.ApiError.OAUTH_ERROR: + case Dropbox.ApiError.INVALID_METHOD: + default: + // Caused by a bug in dropbox.js, in your application, or in Dropbox. + // Tell the user an error occurred, ask them to refresh the page. + } +}; + +function cleanupStorage() +{ + localStorage.clear(); + document.getElementById('btnClean').disabled = true; +} + +function dropboxInit() +{ + document.getElementById('btnRun').disabled = true; + //document.getElementById('btnDrop').disabled = true; + //$('#icnDrop').removeClass('fa-dropbox'); + //$('#icnDrop').addClass('fa-spinner fa-spin'); + + + client.authDriver(new Dropbox.AuthDriver.Redirect()); + client.authenticate({ rememberUser: true }, function(error, client) + { + if (error) + { + return showError(error); + } + dropboxSync(client, dropboxSyncComplete); + }); +} + +function dropboxSyncComplete() +{ + document.getElementById('btnRun').disabled = false; + //$('#icnDrop').removeClass('fa-spinner').removeClass('fa-spin'); + //$('#icnDrop').addClass('fa-check'); + console.log("WEBPLAYER: Sync successful"); + + setupFileSystem("dropbox"); + preLoadingComplete(); +} + +function dropboxSync(dropboxClient, cb) +{ + var dbfs = new BrowserFS.FileSystem.Dropbox(dropboxClient); + // Wrap in afsFS. + afs = new BrowserFS.FileSystem.AsyncMirror( + new BrowserFS.FileSystem.InMemory(), dbfs); + + afs.initialize(function(err) + { + // Initialize it as the root file system. + //BrowserFS.initialize(afs); + cb(); + }); +} + +function preLoadingComplete() +{ + /* Make the Preview image clickable to start RetroArch. */ + $('.webplayer-preview').addClass('loaded').click(function () { + startRetroArch(); + return false; + }); +} + + +function setupFileSystem(backend) +{ + /* create a mountable filesystem that will server as a root + mountpoint for browserfs */ + var mfs = new BrowserFS.FileSystem.MountableFileSystem(); + + /* setup this if you setup your server to serve assets or core assets, + you can find more information in the included README */ + + /* create an XmlHttpRequest filesystem for the bundled data + uncomment this section if you want XMB assets, Overlays, Shaders, etc. + var xfs1 = new BrowserFS.FileSystem.XmlHttpRequest + ("--your-assets-index-file-name--", "--your-index-url--");*/ + /* create an XmlHttpRequest filesystem for content + uncomment this section if you want to serve content + var xfs2 = new BrowserFS.FileSystem.XmlHttpRequest + ("--your-content-index-file-name--", "--your-index-url--");*/ + + console.log("WEBPLAYER: Initializing Filesystem"); + if(backend == "browser") + { + console.log("WEBPLAYER: Initializing LocalStorage"); + + /* create a local filesystem */ + var lsfs = new BrowserFS.FileSystem.LocalStorage(); + + /* mount the filesystems onto mfs */ + mfs.mount('/home/web_user/retroarch/userdata', lsfs); + + } + else + { + /* mount the filesystems onto mfs */ + mfs.mount('/home/web_user/retroarch/userdata', afs); + } + + /* setup this if you setup your server to serve assets or core assets, + you can find more information in the included README */ + /* + mfs.mount('/home/web_user/retroarch/bundle', xfs1); + mfs.mount('/home/web_user/retroarch/userdata/content/', xfs2); + */ + BrowserFS.initialize(mfs); + var BFS = new BrowserFS.EmscriptenFS(); + FS.mount(BFS, {root: '/home'}, '/home'); + console.log("WEBPLAYER: " + backend + " filesystem initialized"); +} + +/** + * Retrieve the value of the given GET parameter. + */ +function getParam(name) { + var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href); + if (results) { + return results[1] || null; + } +} + +function startRetroArch() +{ + $('.webplayer').show(); + $('.webplayer-preview').hide(); + //document.getElementById('btnDrop').disabled = true; + document.getElementById('btnRun').disabled = true; + + $('#btnFullscreen').removeClass('disabled'); + $('#btnMenu').removeClass('disabled'); + $('#btnAdd').removeClass('disabled'); + $('#btnRom').removeClass('disabled'); + + document.getElementById("btnAdd").disabled = false; + document.getElementById("btnRom").disabled = false; + document.getElementById("btnMenu").disabled = false; + document.getElementById("btnFullscreen").disabled = false; + + Module['callMain'](Module['arguments']); + document.getElementById('canvas').focus(); +} + +function selectFiles(files) +{ + $('#btnAdd').addClass('disabled'); + $('#icnAdd').removeClass('fa-plus'); + $('#icnAdd').addClass('fa-spinner spinning'); + var count = files.length; + + for (var i = 0; i < files.length; i++) + { + filereader = new FileReader(); + filereader.file_name = files[i].name; + filereader.readAsArrayBuffer(files[i]); + filereader.onload = function(){uploadData(this.result, this.file_name)}; + filereader.onloadend = function(evt) + { + console.log("WEBPLAYER: File: " + this.file_name + " Upload Complete"); + if (evt.target.readyState == FileReader.DONE) + { + $('#btnAdd').removeClass('disabled'); + $('#icnAdd').removeClass('fa-spinner spinning'); + $('#icnAdd').addClass('fa-plus'); + } + } + } +} + +function uploadData(data,name) +{ + var dataView = new Uint8Array(data); + FS.createDataFile('/', name, dataView, true, false); + + var data = FS.readFile(name,{ encoding: 'binary' }); + FS.writeFile('/home/web_user/retroarch/userdata/content/' + name, data ,{ encoding: 'binary' }); + FS.unlink(name); +} + +var Module = +{ + noInitialRun: true, + arguments: ["-v", "--menu"], + preRun: [], + postRun: [], + print: (function() + { + var element = document.getElementById('output'); + element.value = ''; // clear browser cache + return function(text) + { + text = Array.prototype.slice.call(arguments).join(' '); + element.value += text + "\n"; + element.scrollTop = 99999; // focus on bottom + }; + })(), + + printErr: function(text) + { + var text = Array.prototype.slice.call(arguments).join(' '); + var element = document.getElementById('output'); + element.value += text + "\n"; + element.scrollTop = 99999; // focus on bottom + }, + canvas: document.getElementById('canvas'), + totalDependencies: 0, + monitorRunDependencies: function(left) + { + this.totalDependencies = Math.max(this.totalDependencies, left); + } +}; + +function switchCore(corename) { + localStorage.setItem("core", corename); +} + +function switchStorage(backend) { + if (backend != localStorage.getItem("backend")) + { + localStorage.setItem("backend", backend); + location.reload(); + } +} + +// When the browser has loaded everything. +$(function() { + /** + * Attempt to disable some default browser keys. + */ + var keys = { + 9: "tab", + 13: "enter", + 16: "shift", + 18: "alt", + 27: "esc", + 33: "rePag", + 34: "avPag", + 35: "end", + 36: "home", + 37: "left", + 38: "up", + 39: "right", + 40: "down", + 112: "F1", + 113: "F2", + 114: "F3", + 115: "F4", + 116: "F5", + 117: "F6", + 118: "F7", + 119: "F8", + 120: "F9", + 121: "F10", + 122: "F11", + 123: "F12" + }; + window.addEventListener('keydown', function (e) { + if (keys[e.which]) { + e.preventDefault(); + } + }); + + // Switch the core when selecting one. + $('#core-selector a').click(function () { + var coreChoice = $(this).data('core'); + switchCore(coreChoice); + }); + + // Find which core to load. + var core = localStorage.getItem("core", core); + if (!core) { + core = 'gambatte'; + } + // Make the core the selected core in the UI. + var coreTitle = $('#core-selector a[data-core="' + core + '"]').addClass('active').text(); + $('#dropdownMenu1').text(coreTitle); + + // Load the Core's related JavaScript. + $.getScript(core + '_libretro.js', function () + { + // Activate the Start RetroArch button. + $('#btnRun').removeClass('disabled'); + $('#icnRun').removeClass('fa-spinner').removeClass('fa-spin'); + $('#icnRun').addClass('fa-play'); + + document.getElementById("btnRun").disabled = false; + + if (localStorage.getItem("backend") == "dropbox") + { + $('#lblDrop').addClass('active'); + $('#lblLocal').removeClass('active'); + dropboxInit(); + } + else + { + $('#lblDrop').removeClass('active'); + $('#lblLocal').addClass('active'); + preLoadingComplete(); + setupFileSystem("browser"); + } + }); + }); + +function keyPress(k) +{ + kp(k, "keydown"); + setInterval(function(){kp(k, "keyup")}, 1000); +} + +kp = function(k, event) { + var oEvent = document.createEvent('KeyboardEvent'); + + // Chromium Hack + Object.defineProperty(oEvent, 'keyCode', { + get : function() { + return this.keyCodeVal; + } + }); + Object.defineProperty(oEvent, 'which', { + get : function() { + return this.keyCodeVal; + } + }); + + if (oEvent.initKeyboardEvent) { + oEvent.initKeyboardEvent(event, true, true, document.defaultView, false, false, false, false, k, k); + } else { + oEvent.initKeyEvent(event, true, true, document.defaultView, false, false, false, false, k, 0); + } + + oEvent.keyCodeVal = k; + + if (oEvent.keyCode !== k) { + alert("keyCode mismatch " + oEvent.keyCode + "(" + oEvent.which + ")"); + } + + document.dispatchEvent(oEvent); + document.getElementById('canvas').focus(); +} diff --git a/pkg/emscripten/proto.html b/pkg/emscripten/embed/index.html similarity index 84% rename from pkg/emscripten/proto.html rename to pkg/emscripten/embed/index.html index c759e8174f..6cc4198c7a 100644 --- a/pkg/emscripten/proto.html +++ b/pkg/emscripten/embed/index.html @@ -2,6 +2,7 @@ + RetroArch Web Player @@ -9,33 +10,22 @@ - - - + + - -