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))
{
| |