diff --git a/Makefile b/Makefile index 60f3f787d3..ef6b41255d 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ ifeq ($(HAVE_BSV_MOVIE), 1) endif ifeq ($(HAVE_NETPLAY), 1) - OBJ += netplay.o + OBJ += netplay.o network_cmd.o endif ifeq ($(HAVE_RSOUND), 1) diff --git a/Makefile.win b/Makefile.win index 3273443b52..4ce79a2b52 100644 --- a/Makefile.win +++ b/Makefile.win @@ -95,7 +95,7 @@ endif ifeq ($(HAVE_NETPLAY), 1) DEFINES += -DHAVE_NETPLAY - OBJ += netplay.o + OBJ += netplay.o network_cmd.o LIBS += -lws2_32 endif diff --git a/config.def.h b/config.def.h index b2178a6172..f1f22da4df 100644 --- a/config.def.h +++ b/config.def.h @@ -280,6 +280,10 @@ static const bool savestate_auto_index = false; // Slowmotion ratio. static const float slowmotion_ratio = 3.0; +// Enable network command interface +static const bool network_cmd_enable = false; +static const uint16_t network_cmd_port = 55355; + //////////////////// // Keybinds, Joypad diff --git a/driver.h b/driver.h index 0666597ba0..f52a6f7231 100644 --- a/driver.h +++ b/driver.h @@ -24,6 +24,14 @@ #include "msvc/msvc_compat.h" #include "input/keysym.h" +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_NETPLAY +#include "network_cmd.h" +#endif + #define AUDIO_CHUNK_SIZE_BLOCKING 64 #define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio. #define AUDIO_MAX_RATIO 16 @@ -167,6 +175,10 @@ typedef struct driver void *audio_data; void *video_data; void *input_data; + +#ifdef HAVE_NETPLAY + network_cmd_t *network_cmd; +#endif } driver_t; void init_drivers(void); @@ -243,8 +255,17 @@ extern const input_driver_t input_xdk360; #define input_poll_func() driver.input->poll(driver.input_data) #define input_input_state_func(snes_keybinds, port, device, index, id) \ driver.input->input_state(driver.input_data, snes_keybinds, port, device, index, id) -#define input_key_pressed_func(key) driver.input->key_pressed(driver.input_data, key) #define input_free_func() driver.input->free(driver.input_data) + +static inline bool input_key_pressed_func(int key) +{ + bool ret = driver.input->key_pressed(driver.input_data, key); +#ifdef HAVE_NETPLAY + if (!ret && driver.network_cmd) + ret = network_cmd_get(driver.network_cmd, key); +#endif + return ret; +} #endif #endif diff --git a/general.h b/general.h index 154e69efd7..a0a20e80b4 100644 --- a/general.h +++ b/general.h @@ -53,6 +53,7 @@ #ifdef HAVE_NETPLAY #include "netplay.h" +#include "network_cmd.h" #endif #include "audio/resampler.h" @@ -177,6 +178,9 @@ struct settings bool block_sram_overwrite; bool savestate_auto_index; + + bool network_cmd_enable; + uint16_t network_cmd_port; }; // Settings and/or global state that is specific to a console-style implementation. diff --git a/netplay.c b/netplay.c index f94bf0beb1..9c977b9097 100644 --- a/netplay.c +++ b/netplay.c @@ -13,35 +13,7 @@ * If not, see . */ -#if defined(_WIN32) && !defined(_XBOX) -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 -#endif -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#elif defined(_XBOX) -#define NOD3D -#include -#else -#include -#include -#include -#include -#include -#include -#include - -#ifdef __CELLOS_LV2__ -#include -#include -#else -#include -#endif - -#endif - +#include "netplay_compat.h" #include "netplay.h" #include "general.h" #include "autosave.h" @@ -50,8 +22,6 @@ #include #include -#include "netplay_compat.h" - // Checks if input port/index is controlled by netplay or not. static bool netplay_is_alive(netplay_t *handle); diff --git a/netplay.h b/netplay.h index 9b2c110fd9..5da2df58aa 100644 --- a/netplay.h +++ b/netplay.h @@ -57,3 +57,4 @@ void netplay_pre_frame(netplay_t *handle); void netplay_post_frame(netplay_t *handle); #endif + diff --git a/netplay_compat.h b/netplay_compat.h index ad0cc901e4..2b114c88e1 100644 --- a/netplay_compat.h +++ b/netplay_compat.h @@ -20,6 +20,34 @@ #include "config.h" #endif +#if defined(_WIN32) && !defined(_XBOX) +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#elif defined(_XBOX) +#define NOD3D +#include +#else +#include +#include +#include +#include +#include +#include +#include + +#ifdef __CELLOS_LV2__ +#include +#include +#else +#include +#endif +#endif + #ifdef _XBOX #define socklen_t int #endif @@ -29,9 +57,7 @@ #define close(x) closesocket(x) #define CONST_CAST (const char*) #define NONCONST_CAST (char*) - #else - #define CONST_CAST #define NONCONST_CAST #include @@ -41,7 +67,6 @@ #define close(x) socketclose(x) #define select(nfds, readfds, writefds, errorfds, timeout) socketselect(nfds, readfds, writefds, errorfds, timeout) #endif - #endif // Compatibility layer for legacy or incomplete BSD socket implementations. @@ -80,6 +105,5 @@ void freeaddrinfo(struct addrinfo *res); // gai_strerror() not used, so we skip that. #endif - #endif diff --git a/network_cmd.c b/network_cmd.c new file mode 100644 index 0000000000..51b67c1d78 --- /dev/null +++ b/network_cmd.c @@ -0,0 +1,179 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2012 - 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. + * + * 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 "netplay_compat.h" +#include "network_cmd.h" +#include "driver.h" +#include "general.h" +#include +#include + +struct network_cmd +{ + int fd; + bool state[RARCH_BIND_LIST_END]; +}; + +network_cmd_t *network_cmd_new(uint16_t port) +{ + network_cmd_t *handle = (network_cmd_t*)calloc(1, sizeof(*handle)); + if (!handle) + return NULL; + + handle->fd = -1; + + struct addrinfo hints, *res = NULL; + memset(&hints, 0, sizeof(hints)); +#if defined(_WIN32) || defined(HAVE_SOCKET_LEGACY) + hints.ai_family = AF_INET; +#else + hints.ai_family = AF_UNSPEC; +#endif + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; + + char port_buf[16]; + int yes = 1; + + snprintf(port_buf, sizeof(port_buf), "%hu", (unsigned short)port); + if (getaddrinfo(NULL, port_buf, &hints, &res) < 0) + goto error; + + handle->fd = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK, res->ai_protocol); + if (handle->fd < 0) + goto error; + + setsockopt(handle->fd, SOL_SOCKET, SO_REUSEADDR, CONST_CAST &yes, sizeof(int)); + if (bind(handle->fd, res->ai_addr, res->ai_addrlen) < 0) + { + RARCH_ERR("Failed to bind socket.\n"); + goto error; + } + + freeaddrinfo(res); + + return handle; + +error: + if (res) + freeaddrinfo(res); + network_cmd_free(handle); + return NULL; +} + +void network_cmd_free(network_cmd_t *handle) +{ + if (handle->fd >= 0) + close(handle->fd); + + free(handle); +} + +struct cmd_map +{ + const char *str; + unsigned id; +}; + +static const struct cmd_map map[] = { + { "FAST_FORWARD", RARCH_FAST_FORWARD_KEY }, + { "FAST_FORWARD_HOLD", RARCH_FAST_FORWARD_HOLD_KEY }, + { "LOAD_STATE", RARCH_LOAD_STATE_KEY }, + { "SAVE_STATE", RARCH_SAVE_STATE_KEY }, + { "FULLSCREEN_TOGGLE", RARCH_FULLSCREEN_TOGGLE_KEY }, + { "QUIT", RARCH_QUIT_KEY }, + { "STATE_SLOT_PLUS", RARCH_STATE_SLOT_PLUS }, + { "STATE_SLOT_MINUS", RARCH_STATE_SLOT_MINUS }, + { "AUDIO_INPUT_RATE_PLUS", RARCH_AUDIO_INPUT_RATE_PLUS }, + { "AUDIO_INPUT_RATE_MINUS", RARCH_AUDIO_INPUT_RATE_MINUS }, + { "REWIND", RARCH_REWIND }, + { "MOVIE_RECORD_TOGGLE", RARCH_MOVIE_RECORD_TOGGLE }, + { "PAUSE_TOGGLE", RARCH_PAUSE_TOGGLE }, + { "FRAMEADVANCE", RARCH_FRAMEADVANCE }, + { "RESET", RARCH_RESET }, + { "SHADER_NEXT", RARCH_SHADER_NEXT }, + { "SHADER_PREV", RARCH_SHADER_PREV }, + { "CHEAT_INDEX_PLUS", RARCH_CHEAT_INDEX_PLUS }, + { "CHEAT_INDEX_MINUS", RARCH_CHEAT_INDEX_MINUS }, + { "CHEAT_TOGGLE", RARCH_CHEAT_TOGGLE }, + { "SCREENSHOT", RARCH_SCREENSHOT }, + { "DSP_CONFIG", RARCH_DSP_CONFIG }, + { "MUTE", RARCH_MUTE }, + { "NETPLAY_FLIP", RARCH_NETPLAY_FLIP }, + { "SLOWMOTION", RARCH_SLOWMOTION }, +}; + +static void parse_sub_msg(network_cmd_t *handle, const char *tok) +{ + for (unsigned i = 0; i < sizeof(map) / sizeof(map[0]); i++) + { + if (strcmp(tok, map[i].str) == 0) + { + handle->state[map[i].id] = true; + return; + } + } + + RARCH_WARN("Unrecognized command \"%s\" received.\n", tok); +} + +static void parse_msg(network_cmd_t *handle, char *buf) +{ + const char *tok = strtok(buf, "\n"); + while (tok) + { + parse_sub_msg(handle, tok); + tok = strtok(NULL, "\n"); + } +} + +void network_cmd_set(network_cmd_t *handle, unsigned id) +{ + if (id < RARCH_BIND_LIST_END) + handle->state[id] = true; +} + +bool network_cmd_get(network_cmd_t *handle, unsigned id) +{ + return id < RARCH_BIND_LIST_END && handle->state[id]; +} + +void network_cmd_pre_frame(network_cmd_t *handle) +{ + memset(handle->state, 0, sizeof(handle->state)); + + fd_set fds; + FD_ZERO(&fds); + FD_SET(handle->fd, &fds); + + struct timeval tmp_tv = {0}; + if (select(handle->fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0) + return; + + if (!FD_ISSET(handle->fd, &fds)) + return; + + for (;;) + { + char buf[1024]; + ssize_t ret = recvfrom(handle->fd, buf, sizeof(buf) - 1, 0, NULL, NULL); + if (ret <= 0) + break; + + buf[ret] = '\0'; + parse_msg(handle, buf); + } +} + diff --git a/network_cmd.h b/network_cmd.h new file mode 100644 index 0000000000..fd19f6a305 --- /dev/null +++ b/network_cmd.h @@ -0,0 +1,32 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2012 - 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. + * + * 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 NETWORK_CMD_H__ +#define NETWORK_CMD_H__ + +#include +#include + +typedef struct network_cmd network_cmd_t; + +network_cmd_t *network_cmd_new(uint16_t port); +void network_cmd_free(network_cmd_t *handle); + +void network_cmd_pre_frame(network_cmd_t *handle); +void network_cmd_set(network_cmd_t *handle, unsigned id); +bool network_cmd_get(network_cmd_t *handle, unsigned id); + +#endif + diff --git a/retroarch.c b/retroarch.c index 17a04db351..176b33da0a 100644 --- a/retroarch.c +++ b/retroarch.c @@ -1337,6 +1337,25 @@ static void deinit_netplay(void) if (g_extern.netplay) netplay_free(g_extern.netplay); } + +static void init_network_cmd(void) +{ + if (!g_settings.network_cmd_enable) + return; + + driver.network_cmd = network_cmd_new(g_settings.network_cmd_port); + if (!driver.network_cmd) + RARCH_ERR("Failed to initialize network command interface.\n"); +} + +static void deinit_network_cmd(void) +{ + if (driver.network_cmd) + { + network_cmd_free(driver.network_cmd); + driver.network_cmd = NULL; + } +} #endif static void init_libretro_cbs_plain(void) @@ -2353,6 +2372,7 @@ int rarch_main_init(int argc, char *argv[]) #ifdef HAVE_NETPLAY init_netplay(); + init_network_cmd(); #endif init_drivers(); @@ -2427,6 +2447,11 @@ bool rarch_main_iterate(void) !video_alive_func()) return false; +#ifdef HAVE_NETPLAY + if (driver.network_cmd) + network_cmd_pre_frame(driver.network_cmd); +#endif + // Checks for stuff like fullscreen, save states, etc. do_state_checks(); @@ -2480,6 +2505,7 @@ void rarch_main_deinit(void) { #ifdef HAVE_NETPLAY deinit_netplay(); + deinit_network_cmd(); #endif #ifdef HAVE_THREADS diff --git a/retroarch.cfg b/retroarch.cfg index 80e6950e0f..9246d93788 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -358,3 +358,7 @@ # Slowmotion ratio. When slowmotion, game will slow down by factor. # slowmotion_ratio = 3.0 +# Enable network command interface. +# network_cmd_enable = false +# network_cmd_port = 55355 + diff --git a/settings.c b/settings.c index 84d352e028..a5109d2b1c 100644 --- a/settings.c +++ b/settings.c @@ -189,6 +189,8 @@ void config_set_defaults(void) g_settings.block_sram_overwrite = block_sram_overwrite; g_settings.savestate_auto_index = savestate_auto_index; + g_settings.network_cmd_enable = network_cmd_enable; + g_settings.network_cmd_port = network_cmd_port; rarch_assert(sizeof(g_settings.input.binds[0]) >= sizeof(snes_keybinds_1)); rarch_assert(sizeof(g_settings.input.binds[1]) >= sizeof(snes_keybinds_rest)); @@ -455,6 +457,9 @@ bool config_load_file(const char *path) CONFIG_GET_BOOL(block_sram_overwrite, "block_sram_overwrite"); CONFIG_GET_BOOL(savestate_auto_index, "savestate_auto_index"); + CONFIG_GET_BOOL(network_cmd_enable, "network_cmd_enable"); + CONFIG_GET_INT(network_cmd_port, "network_cmd_port"); + if (config_get_string(conf, "environment_variables", &g_extern.system.environment)) {