diff --git a/Makefile.common b/Makefile.common
index 9071b8a29c..97c1113b76 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -2099,10 +2099,9 @@ ifeq ($(HAVE_NETWORKING), 1)
# Netplay
DEFINES += -DHAVE_NETWORK_CMD
- OBJ += network/netplay/netplay_handshake.o \
- network/netplay/netplay_io.o \
- network/netplay/netplay_discovery.o \
- network/netplay/netplay_room_parse.o
+ OBJ += \
+ network/netplay/netplay_frontend.o \
+ network/netplay/netplay_room_parse.o
# RetroAchievements
ifeq ($(HAVE_CHEEVOS), 1)
diff --git a/griffin/griffin.c b/griffin/griffin.c
index c39e328d63..6b4c86588a 100644
--- a/griffin/griffin.c
+++ b/griffin/griffin.c
@@ -1290,9 +1290,7 @@ THREAD
NETPLAY
============================================================ */
#ifdef HAVE_NETWORKING
-#include "../network/netplay/netplay_handshake.c"
-#include "../network/netplay/netplay_io.c"
-#include "../network/netplay/netplay_discovery.c"
+#include "../network/netplay/netplay_frontend.c"
#include "../network/netplay/netplay_room_parse.c"
#include "../libretro-common/net/net_compat.c"
#include "../libretro-common/net/net_socket.c"
diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h
index 547ed16a5d..d88693b10b 100644
--- a/network/netplay/netplay.h
+++ b/network/netplay/netplay.h
@@ -96,4 +96,82 @@ int netplay_rooms_get_count(void);
void netplay_rooms_free(void);
+/**
+* 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_toggle_play_spectate
+ *
+ * Toggle between play mode and spectate mode
+ */
+void netplay_toggle_play_spectate(netplay_t *netplay);
+
+/**
+ * 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);
+
+/**
+ * netplay_core_reset
+ * @netplay : pointer to netplay object
+ *
+ * Indicate that the core has been reset to netplay peers
+ **/
+void netplay_core_reset(netplay_t *netplay);
+
+int16_t netplay_input_state(netplay_t *netplay,
+ unsigned port, unsigned device,
+ unsigned idx, unsigned id);
+
+/**
+ * netplay_poll:
+ * @netplay : pointer to netplay object
+ *
+ * Polls network to see if we have anything new. If our
+ * network buffer is full, we simply have to block
+ * for new input data.
+ *
+ * Returns: true (1) if successful, otherwise false (0).
+ **/
+bool netplay_poll(
+ bool block_libretro_input,
+ void *settings_data,
+ netplay_t *netplay);
+
+/**
+ * netplay_is_alive:
+ * @netplay : pointer to netplay object
+ *
+ * Checks if input port/index is controlled by netplay or not.
+ *
+ * Returns: true (1) if alive, otherwise false (0).
+ **/
+bool netplay_is_alive(netplay_t *netplay);
+
+/**
+ * netplay_should_skip:
+ * @netplay : pointer to netplay object
+ *
+ * If we're fast-forward replaying to resync, check if we
+ * should actually show frame.
+ *
+ * Returns: bool (1) if we should skip this frame, otherwise
+ * false (0).
+ **/
+bool netplay_should_skip(netplay_t *netplay);
+
#endif
diff --git a/network/netplay/netplay_discovery.c b/network/netplay/netplay_discovery.c
deleted file mode 100644
index 091518604f..0000000000
--- a/network/netplay/netplay_discovery.c
+++ /dev/null
@@ -1,556 +0,0 @@
-/* RetroArch - A frontend for libretro.
- * Copyright (C) 2016-2017 - 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-
- * 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 .
- */
-
-/*
- * AD PACKET FORMAT:
- *
- * Request:
- * 1 word: RANQ (RetroArch Netplay Query)
- * 1 word: Netplay protocol version
- *
- * Reply:
- * 1 word : RANS (RetroArch Netplay Server)
- * 1 word : Netplay protocol version
- * 1 word : Port
- * 8 words: RetroArch version
- * 8 words: Nick
- * 8 words: Core name
- * 8 words: Core version
- * 8 words: Content name (currently always blank)
- */
-
-#include
-#include
-#include
-#include
-#include
-#include "../../file_path_special.h"
-#include "../../paths.h"
-#include "../../content.h"
-
-#include
-#include
-
-#include "../../retroarch.h"
-#include "../../version.h"
-#include "../../verbosity.h"
-
-#include "netplay.h"
-#include "netplay_discovery.h"
-#include "netplay_private.h"
-
-#if defined(AF_INET6) && !defined(HAVE_SOCKET_LEGACY) && !defined(_3DS)
-#define HAVE_INET6 1
-#endif
-
-/* TODO/FIXME - globals referenced outside */
-struct netplay_room *netplay_room_list = NULL;
-int netplay_room_count = 0;
-
-#ifdef HAVE_NETPLAYDISCOVERY
-struct ad_packet
-{
- uint32_t header;
- uint32_t protocol_version;
- uint32_t port;
- char address[NETPLAY_HOST_STR_LEN];
- char retroarch_version[NETPLAY_HOST_STR_LEN];
- char nick[NETPLAY_HOST_STR_LEN];
- char frontend[NETPLAY_HOST_STR_LEN];
- char core[NETPLAY_HOST_STR_LEN];
- char core_version[NETPLAY_HOST_STR_LEN];
- char content[NETPLAY_HOST_LONGSTR_LEN];
- char content_crc[NETPLAY_HOST_STR_LEN];
- char subsystem_name[NETPLAY_HOST_STR_LEN];
-};
-
-/* TODO/FIXME - static globals */
-
-/* LAN discovery sockets */
-static int lan_ad_server_fd = -1;
-static int lan_ad_client_fd = -1;
-
-/* Packet buffer for advertisement and responses */
-static struct ad_packet ad_packet_buffer;
-
-/* List of discovered hosts */
-static struct netplay_host_list discovered_hosts;
-
-static size_t discovered_hosts_allocated;
-
-#ifdef HAVE_SOCKET_LEGACY
-
-#ifndef htons
-/* The fact that I need to write this is deeply depressing */
-static int16_t htons_for_morons(int16_t value)
-{
- union {
- int32_t l;
- int16_t s[2];
- } val;
- val.l = htonl(value);
- return val.s[1];
-}
-#define htons htons_for_morons
-#endif
-
-#endif
-
-static bool netplay_lan_ad_client(void)
-{
- unsigned i;
- fd_set fds;
- socklen_t addr_size;
- struct sockaddr their_addr;
- struct timeval tmp_tv = {0};
-
- if (lan_ad_client_fd < 0)
- return false;
-
- their_addr.sa_family = 0;
- for (i = 0; i < 14; i++)
- their_addr.sa_data[i] = 0;
-
- /* Check for any ad queries */
- for (;;)
- {
- FD_ZERO(&fds);
- FD_SET(lan_ad_client_fd, &fds);
- if (socket_select(lan_ad_client_fd + 1,
- &fds, NULL, NULL, &tmp_tv) <= 0)
- break;
-
- if (!FD_ISSET(lan_ad_client_fd, &fds))
- break;
-
- /* Somebody queried, so check that it's valid */
- addr_size = sizeof(their_addr);
-
- if (recvfrom(lan_ad_client_fd, (char*)&ad_packet_buffer,
- sizeof(struct ad_packet), 0, &their_addr, &addr_size) >=
- (ssize_t) sizeof(struct ad_packet))
- {
- struct netplay_host *host = NULL;
-
- /* Make sure it's a valid response */
- if (memcmp((void *) &ad_packet_buffer, "RANS", 4))
- continue;
-
- /* For this version */
- if (ntohl(ad_packet_buffer.protocol_version)
- != NETPLAY_PROTOCOL_VERSION)
- continue;
-
- /* And that we know how to handle it */
- if (their_addr.sa_family == AF_INET)
- {
- struct sockaddr_in *sin = NULL;
-
- RARCH_WARN ("[Discovery] Using IPv4 for discovery\n");
- sin = (struct sockaddr_in *) &their_addr;
- sin->sin_port = htons(ntohl(ad_packet_buffer.port));
-
- }
-#ifdef HAVE_INET6
- else if (their_addr.sa_family == AF_INET6)
- {
- struct sockaddr_in6 *sin6 = NULL;
- RARCH_WARN ("[Discovery] Using IPv6 for discovery\n");
- sin6 = (struct sockaddr_in6 *) &their_addr;
- sin6->sin6_port = htons(ad_packet_buffer.port);
-
- }
-#endif
- else
- continue;
-
- /* Allocate space for it */
- if (discovered_hosts.size >= discovered_hosts_allocated)
- {
- size_t allocated = discovered_hosts_allocated;
- struct netplay_host *new_hosts = NULL;
-
- if (allocated == 0)
- allocated = 2;
- else
- allocated *= 2;
-
- if (discovered_hosts.hosts)
- new_hosts = (struct netplay_host *)
- realloc(discovered_hosts.hosts, allocated * sizeof(struct
- netplay_host));
- else
- /* Should be equivalent to realloc,
- * but I don't trust screwy libcs */
- new_hosts = (struct netplay_host *)
- malloc(allocated * sizeof(struct netplay_host));
-
- if (!new_hosts)
- return false;
-
- discovered_hosts.hosts = new_hosts;
- discovered_hosts_allocated = allocated;
- }
-
- /* Get our host structure */
- host = &discovered_hosts.hosts[discovered_hosts.size++];
-
- /* Copy in the response */
- memset(host, 0, sizeof(struct netplay_host));
- host->addr = their_addr;
- host->addrlen = addr_size;
-
- host->port = ntohl(ad_packet_buffer.port);
-
- strlcpy(host->address, ad_packet_buffer.address, NETPLAY_HOST_STR_LEN);
- strlcpy(host->nick, ad_packet_buffer.nick, NETPLAY_HOST_STR_LEN);
- strlcpy(host->core, ad_packet_buffer.core, NETPLAY_HOST_STR_LEN);
- strlcpy(host->retroarch_version, ad_packet_buffer.retroarch_version,
- NETPLAY_HOST_STR_LEN);
- strlcpy(host->core_version, ad_packet_buffer.core_version,
- NETPLAY_HOST_STR_LEN);
- strlcpy(host->content, ad_packet_buffer.content,
- NETPLAY_HOST_LONGSTR_LEN);
- strlcpy(host->subsystem_name, ad_packet_buffer.subsystem_name,
- NETPLAY_HOST_LONGSTR_LEN);
- strlcpy(host->frontend, ad_packet_buffer.frontend,
- NETPLAY_HOST_STR_LEN);
-
- host->content_crc =
- atoi(ad_packet_buffer.content_crc);
- host->nick[NETPLAY_HOST_STR_LEN-1] =
- host->core[NETPLAY_HOST_STR_LEN-1] =
- host->core_version[NETPLAY_HOST_STR_LEN-1] =
- host->content[NETPLAY_HOST_LONGSTR_LEN-1] = '\0';
- }
- }
-
- return true;
-}
-
-/** Initialize Netplay discovery (client) */
-bool init_netplay_discovery(void)
-{
- struct addrinfo *addr = NULL;
- int fd = socket_init((void **)&addr, 0, NULL, SOCKET_TYPE_DATAGRAM);
-
- if (fd < 0)
- goto error;
-
- if (!socket_bind(fd, (void*)addr))
- {
- socket_close(fd);
- goto error;
- }
-
- lan_ad_client_fd = fd;
- freeaddrinfo_retro(addr);
- return true;
-
-error:
- if (addr)
- freeaddrinfo_retro(addr);
- RARCH_ERR("[Discovery] Failed to initialize netplay advertisement client socket.\n");
- return false;
-}
-
-/** Deinitialize and free Netplay discovery */
-/* TODO/FIXME - this is apparently never called? */
-void deinit_netplay_discovery(void)
-{
- if (lan_ad_client_fd >= 0)
- {
- socket_close(lan_ad_client_fd);
- lan_ad_client_fd = -1;
- }
-}
-
-/** Discovery control */
-/* TODO/FIXME: implement net_ifinfo and ntohs for consoles */
-bool netplay_discovery_driver_ctl(
- enum rarch_netplay_discovery_ctl_state state, void *data)
-{
- int ret;
- char port_str[6];
- unsigned k = 0;
-
- if (lan_ad_client_fd < 0)
- return false;
-
- switch (state)
- {
- case RARCH_NETPLAY_DISCOVERY_CTL_LAN_SEND_QUERY:
- {
- net_ifinfo_t interfaces;
- struct addrinfo hints = {0}, *addr;
-
- if (!net_ifinfo_new(&interfaces))
- return false;
-
- /* Get the broadcast address (IPv4 only for now) */
- snprintf(port_str, 6, "%hu", (unsigned short) RARCH_DEFAULT_PORT);
- if (getaddrinfo_retro("255.255.255.255", port_str, &hints, &addr) < 0)
- return false;
-
- /* Make it broadcastable */
-#if defined(SOL_SOCKET) && defined(SO_BROADCAST)
- {
- int can_broadcast = 1;
- if (setsockopt(lan_ad_client_fd, SOL_SOCKET, SO_BROADCAST,
- (const char *)&can_broadcast, sizeof(can_broadcast)) < 0)
- RARCH_WARN("[Discovery] Failed to set netplay discovery port to broadcast\n");
- }
-#endif
-
- /* Put together the request */
- memcpy((void *) &ad_packet_buffer, "RANQ", 4);
- ad_packet_buffer.protocol_version = htonl(NETPLAY_PROTOCOL_VERSION);
-
- for (k = 0; k < (unsigned)interfaces.size; k++)
- {
- strlcpy(ad_packet_buffer.address, interfaces.entries[k].host,
- NETPLAY_HOST_STR_LEN);
-
- /* And send it off */
- ret = (int)sendto(lan_ad_client_fd, (const char *) &ad_packet_buffer,
- sizeof(struct ad_packet), 0, addr->ai_addr, addr->ai_addrlen);
- if (ret < (ssize_t) (2*sizeof(uint32_t)))
- RARCH_WARN("[Discovery] Failed to send netplay discovery query (error: %d)\n", errno);
- }
-
- freeaddrinfo_retro(addr);
- net_ifinfo_free(&interfaces);
-
- break;
- }
-
- case RARCH_NETPLAY_DISCOVERY_CTL_LAN_GET_RESPONSES:
- if (!netplay_lan_ad_client())
- return false;
- *((struct netplay_host_list **) data) = &discovered_hosts;
- break;
-
- case RARCH_NETPLAY_DISCOVERY_CTL_LAN_CLEAR_RESPONSES:
- discovered_hosts.size = 0;
- break;
-
- default:
- return false;
- }
- return true;
-}
-
-static bool init_lan_ad_server_socket(netplay_t *netplay, uint16_t port)
-{
- struct addrinfo *addr = NULL;
- int fd = socket_init((void **) &addr, port, NULL, SOCKET_TYPE_DATAGRAM);
-
- if (fd < 0)
- goto error;
-
- if (!socket_bind(fd, (void*)addr))
- {
- socket_close(fd);
- goto error;
- }
-
- lan_ad_server_fd = fd;
- freeaddrinfo_retro(addr);
-
- return true;
-
-error:
- if (addr)
- freeaddrinfo_retro(addr);
- return false;
-}
-
-/**
- * netplay_lan_ad_server
- *
- * Respond to any LAN ad queries that the netplay server has received.
- */
-bool netplay_lan_ad_server(netplay_t *netplay)
-{
- /* TODO/FIXME: implement net_ifinfo and ntohs for consoles */
- fd_set fds;
- int ret;
- unsigned i;
- char buf[4096];
- net_ifinfo_t interfaces;
- socklen_t addr_size;
- char reply_addr[NETPLAY_HOST_STR_LEN], port_str[6];
- struct sockaddr their_addr;
- struct timeval tmp_tv = {0};
- unsigned k = 0;
- struct addrinfo *our_addr, hints = {0};
- struct string_list *subsystem = path_get_subsystem_list();
-
- interfaces.entries = NULL;
- interfaces.size = 0;
-
- their_addr.sa_family = 0;
- for (i = 0; i < 14; i++)
- their_addr.sa_data[i] = 0;
-
- if (!net_ifinfo_new(&interfaces))
- return false;
-
- if ( (lan_ad_server_fd < 0)
- && !init_lan_ad_server_socket(netplay, RARCH_DEFAULT_PORT))
- {
- RARCH_ERR("[Discovery] Failed to initialize netplay advertisement socket\n");
- return false;
- }
-
- /* Check for any ad queries */
- for (;;)
- {
- FD_ZERO(&fds);
- FD_SET(lan_ad_server_fd, &fds);
- if (socket_select(lan_ad_server_fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0)
- break;
- if (!FD_ISSET(lan_ad_server_fd, &fds))
- break;
-
- /* Somebody queried, so check that it's valid */
- addr_size = sizeof(their_addr);
- ret = (int)recvfrom(lan_ad_server_fd, (char*)&ad_packet_buffer,
- sizeof(struct ad_packet), 0, &their_addr, &addr_size);
- if (ret >= (ssize_t) (2 * sizeof(uint32_t)))
- {
- char s[NETPLAY_HOST_STR_LEN];
- uint32_t content_crc = 0;
-
- /* Make sure it's a valid query */
- if (memcmp((void *) &ad_packet_buffer, "RANQ", 4))
- {
- RARCH_LOG("[Discovery] Invalid query\n");
- continue;
- }
-
- /* For this version */
- if (ntohl(ad_packet_buffer.protocol_version) !=
- NETPLAY_PROTOCOL_VERSION)
- {
- RARCH_LOG("[Discovery] Invalid protocol version\n");
- continue;
- }
-
- if (!string_is_empty(ad_packet_buffer.address))
- strlcpy(reply_addr, ad_packet_buffer.address, NETPLAY_HOST_STR_LEN);
-
- for (k = 0; k < interfaces.size; k++)
- {
- char *p;
- char sub[NETPLAY_HOST_STR_LEN];
- char frontend_architecture_tmp[32];
- char frontend[256];
- const frontend_ctx_driver_t *frontend_drv =
- (const frontend_ctx_driver_t*)
- frontend_driver_get_cpu_architecture_str(
- frontend_architecture_tmp, sizeof(frontend_architecture_tmp));
- snprintf(frontend, sizeof(frontend), "%s %s",
- frontend_drv->ident, frontend_architecture_tmp);
-
- p=strrchr(reply_addr,'.');
- if (p)
- {
- strlcpy(sub, reply_addr, p - reply_addr + 1);
- if (strstr(interfaces.entries[k].host, sub) &&
- !strstr(interfaces.entries[k].host, "127.0.0.1"))
- {
- struct retro_system_info *info = runloop_get_libretro_system_info();
-
- RARCH_LOG ("[Discovery] Query received on common interface: %s/%s (theirs / ours) \n",
- reply_addr, interfaces.entries[k].host);
-
- /* Now build our response */
- buf[0] = '\0';
- content_crc = content_get_crc();
-
- memset(&ad_packet_buffer, 0, sizeof(struct ad_packet));
- memcpy(&ad_packet_buffer, "RANS", 4);
-
- if (subsystem)
- {
- unsigned i;
-
- for (i = 0; i < subsystem->size; i++)
- {
- strlcat(buf, path_basename(subsystem->elems[i].data), NETPLAY_HOST_LONGSTR_LEN);
- if (i < subsystem->size - 1)
- strlcat(buf, "|", NETPLAY_HOST_LONGSTR_LEN);
- }
- strlcpy(ad_packet_buffer.content, buf,
- NETPLAY_HOST_LONGSTR_LEN);
- strlcpy(ad_packet_buffer.subsystem_name, path_get(RARCH_PATH_SUBSYSTEM),
- NETPLAY_HOST_STR_LEN);
- }
- else
- {
- strlcpy(ad_packet_buffer.content, !string_is_empty(
- path_basename(path_get(RARCH_PATH_BASENAME)))
- ? path_basename(path_get(RARCH_PATH_BASENAME)) : "N/A",
- NETPLAY_HOST_LONGSTR_LEN);
- strlcpy(ad_packet_buffer.subsystem_name, "N/A", NETPLAY_HOST_STR_LEN);
- }
-
- strlcpy(ad_packet_buffer.address, interfaces.entries[k].host,
- NETPLAY_HOST_STR_LEN);
- ad_packet_buffer.protocol_version =
- htonl(NETPLAY_PROTOCOL_VERSION);
- ad_packet_buffer.port = htonl(netplay->tcp_port);
- strlcpy(ad_packet_buffer.retroarch_version, PACKAGE_VERSION,
- NETPLAY_HOST_STR_LEN);
- strlcpy(ad_packet_buffer.nick, netplay->nick, NETPLAY_HOST_STR_LEN);
- strlcpy(ad_packet_buffer.frontend, frontend, NETPLAY_HOST_STR_LEN);
-
- if (info)
- {
- strlcpy(ad_packet_buffer.core, info->library_name,
- NETPLAY_HOST_STR_LEN);
- strlcpy(ad_packet_buffer.core_version, info->library_version,
- NETPLAY_HOST_STR_LEN);
- }
-
- snprintf(s, sizeof(s), "%d", content_crc);
- strlcpy(ad_packet_buffer.content_crc, s,
- NETPLAY_HOST_STR_LEN);
-
- /* Build up the destination address*/
- snprintf(port_str, 6, "%hu", ntohs(((struct sockaddr_in*)(&their_addr))->sin_port));
- if (getaddrinfo_retro(reply_addr, port_str, &hints, &our_addr) < 0)
- continue;
-
- RARCH_LOG ("[Discovery] Sending reply to %s \n", reply_addr);
-
- /* And send it */
- sendto(lan_ad_server_fd, (const char*)&ad_packet_buffer,
- sizeof(struct ad_packet), 0, our_addr->ai_addr, our_addr->ai_addrlen);
- freeaddrinfo_retro(our_addr);
- }
- else
- continue;
- }
- else
- continue;
- }
- }
- }
- net_ifinfo_free(&interfaces);
- return true;
-}
-#endif
diff --git a/network/netplay/netplay_io.c b/network/netplay/netplay_frontend.c
similarity index 64%
rename from network/netplay/netplay_io.c
rename to network/netplay/netplay_frontend.c
index a819fe37ed..99b43afdc3 100644
--- a/network/netplay/netplay_io.c
+++ b/network/netplay/netplay_frontend.c
@@ -22,42 +22,156 @@
#include
#include
#include
+#include
#include
-#include
-#include
+#include
#include
#include
+#include
+#include
+#include
-#include "netplay_private.h"
-#include "netplay_discovery.h"
+#include
+#include
+
+#include "../../file_path_special.h"
+#include "../../paths.h"
+#include "../../content.h"
+
+#ifdef HAVE_CONFIG_H
+#include "../../config.h"
+#endif
#include "../../autosave.h"
#include "../../configuration.h"
+#include "../../command.h"
+#include "../../content.h"
#include "../../driver.h"
#include "../../retroarch.h"
-#include "../../command.h"
+#include "../../version.h"
+#include "../../verbosity.h"
+
#include "../../tasks/tasks_internal.h"
#include "../../input/input_driver.h"
+#ifdef HAVE_MENU
+#include "../../menu/menu_input.h"
+#endif
+
+#ifdef HAVE_DISCORD
+#include "../discord.h"
+#endif
+
+#include "netplay.h"
+#include "netplay_discovery.h"
+#include "netplay_private.h"
+
#if defined(AF_INET6) && !defined(HAVE_SOCKET_LEGACY) && !defined(_3DS)
#define HAVE_INET6 1
#endif
-#ifdef HAVE_DISCORD
-#include "../discord.h"
+#define RECV(buf, sz) \
+ recvd = netplay_recv(&connection->recv_packet_buffer, connection->fd, (buf), (sz), false); \
+ if (recvd >= 0 && recvd < (ssize_t) (sz)) \
+ { \
+ netplay_recv_reset(&connection->recv_packet_buffer); \
+ return true; \
+ } \
+ else if (recvd < 0)
-/* TODO/FIXME - global public variable */
-extern bool discord_is_inited;
-#endif
+#define NETPLAY_MAGIC 0x52414E50 /* RANP */
+
+
+/*
+ * AD PACKET FORMAT:
+ *
+ * Request:
+ * 1 word: RANQ (RetroArch Netplay Query)
+ * 1 word: Netplay protocol version
+ *
+ * Reply:
+ * 1 word : RANS (RetroArch Netplay Server)
+ * 1 word : Netplay protocol version
+ * 1 word : Port
+ * 8 words: RetroArch version
+ * 8 words: Nick
+ * 8 words: Core name
+ * 8 words: Core version
+ * 8 words: Content name (currently always blank)
+ */
struct vote_count
{
uint16_t votes[32];
};
+struct nick_buf_s
+{
+ uint32_t cmd[2];
+ char nick[NETPLAY_NICK_LEN];
+};
+
+struct password_buf_s
+{
+ uint32_t cmd[2];
+ char password[NETPLAY_PASS_HASH_LEN];
+};
+
+struct info_buf_s
+{
+ uint32_t cmd[2];
+ uint32_t content_crc;
+ char core_name[NETPLAY_NICK_LEN];
+ char core_version[NETPLAY_NICK_LEN];
+};
+
+#ifdef HAVE_NETPLAYDISCOVERY
+struct ad_packet
+{
+ uint32_t header;
+ uint32_t protocol_version;
+ uint32_t port;
+ char address[NETPLAY_HOST_STR_LEN];
+ char retroarch_version[NETPLAY_HOST_STR_LEN];
+ char nick[NETPLAY_HOST_STR_LEN];
+ char frontend[NETPLAY_HOST_STR_LEN];
+ char core[NETPLAY_HOST_STR_LEN];
+ char core_version[NETPLAY_HOST_STR_LEN];
+ char content[NETPLAY_HOST_LONGSTR_LEN];
+ char content_crc[NETPLAY_HOST_STR_LEN];
+ char subsystem_name[NETPLAY_HOST_STR_LEN];
+};
+#endif
+
+/* Forward declarations */
+#ifdef HAVE_DISCORD
+extern bool discord_is_inited;
+#endif
+
+/* TODO/FIXME - globals */
+struct netplay_room *netplay_room_list = NULL;
+int netplay_room_count = 0;
+static netplay_t *handshake_password_netplay = NULL;
+static unsigned long simple_rand_next = 1;
+
+
+#ifdef HAVE_NETPLAYDISCOVERY
+/* LAN discovery sockets */
+static int lan_ad_server_fd = -1;
+static int lan_ad_client_fd = -1;
+
+/* Packet buffer for advertisement and responses */
+static struct ad_packet ad_packet_buffer;
+
+/* List of discovered hosts */
+static struct netplay_host_list discovered_hosts;
+
+static size_t discovered_hosts_allocated;
+#endif
+
/* The mapping of keys from netplay (network) to libretro (host) */
const uint16_t netplay_key_ntoh_mapping[] = {
(uint16_t) RETROK_UNKNOWN,
@@ -72,6 +186,1574 @@ const uint16_t netplay_key_ntoh_mapping[] = {
/* TODO/FIXME - static global variables */
static uint16_t netplay_mapping[RETROK_LAST];
+#ifdef HAVE_NETPLAYDISCOVERY
+#ifdef HAVE_SOCKET_LEGACY
+
+#ifndef htons
+/* The fact that I need to write this is deeply depressing */
+static int16_t htons_for_morons(int16_t value)
+{
+ union {
+ int32_t l;
+ int16_t s[2];
+ } val;
+ val.l = htonl(value);
+ return val.s[1];
+}
+#define htons htons_for_morons
+#endif
+
+#endif
+
+static bool netplay_lan_ad_client(void)
+{
+ unsigned i;
+ fd_set fds;
+ socklen_t addr_size;
+ struct sockaddr their_addr;
+ struct timeval tmp_tv = {0};
+
+ if (lan_ad_client_fd < 0)
+ return false;
+
+ their_addr.sa_family = 0;
+ for (i = 0; i < 14; i++)
+ their_addr.sa_data[i] = 0;
+
+ /* Check for any ad queries */
+ for (;;)
+ {
+ FD_ZERO(&fds);
+ FD_SET(lan_ad_client_fd, &fds);
+ if (socket_select(lan_ad_client_fd + 1,
+ &fds, NULL, NULL, &tmp_tv) <= 0)
+ break;
+
+ if (!FD_ISSET(lan_ad_client_fd, &fds))
+ break;
+
+ /* Somebody queried, so check that it's valid */
+ addr_size = sizeof(their_addr);
+
+ if (recvfrom(lan_ad_client_fd, (char*)&ad_packet_buffer,
+ sizeof(struct ad_packet), 0, &their_addr, &addr_size) >=
+ (ssize_t) sizeof(struct ad_packet))
+ {
+ struct netplay_host *host = NULL;
+
+ /* Make sure it's a valid response */
+ if (memcmp((void *) &ad_packet_buffer, "RANS", 4))
+ continue;
+
+ /* For this version */
+ if (ntohl(ad_packet_buffer.protocol_version)
+ != NETPLAY_PROTOCOL_VERSION)
+ continue;
+
+ /* And that we know how to handle it */
+ if (their_addr.sa_family == AF_INET)
+ {
+ struct sockaddr_in *sin = NULL;
+
+ RARCH_WARN ("[Discovery] Using IPv4 for discovery\n");
+ sin = (struct sockaddr_in *) &their_addr;
+ sin->sin_port = htons(ntohl(ad_packet_buffer.port));
+
+ }
+#ifdef HAVE_INET6
+ else if (their_addr.sa_family == AF_INET6)
+ {
+ struct sockaddr_in6 *sin6 = NULL;
+ RARCH_WARN ("[Discovery] Using IPv6 for discovery\n");
+ sin6 = (struct sockaddr_in6 *) &their_addr;
+ sin6->sin6_port = htons(ad_packet_buffer.port);
+
+ }
+#endif
+ else
+ continue;
+
+ /* Allocate space for it */
+ if (discovered_hosts.size >= discovered_hosts_allocated)
+ {
+ size_t allocated = discovered_hosts_allocated;
+ struct netplay_host *new_hosts = NULL;
+
+ if (allocated == 0)
+ allocated = 2;
+ else
+ allocated *= 2;
+
+ if (discovered_hosts.hosts)
+ new_hosts = (struct netplay_host *)
+ realloc(discovered_hosts.hosts, allocated * sizeof(struct
+ netplay_host));
+ else
+ /* Should be equivalent to realloc,
+ * but I don't trust screwy libcs */
+ new_hosts = (struct netplay_host *)
+ malloc(allocated * sizeof(struct netplay_host));
+
+ if (!new_hosts)
+ return false;
+
+ discovered_hosts.hosts = new_hosts;
+ discovered_hosts_allocated = allocated;
+ }
+
+ /* Get our host structure */
+ host = &discovered_hosts.hosts[discovered_hosts.size++];
+
+ /* Copy in the response */
+ memset(host, 0, sizeof(struct netplay_host));
+ host->addr = their_addr;
+ host->addrlen = addr_size;
+
+ host->port = ntohl(ad_packet_buffer.port);
+
+ strlcpy(host->address, ad_packet_buffer.address, NETPLAY_HOST_STR_LEN);
+ strlcpy(host->nick, ad_packet_buffer.nick, NETPLAY_HOST_STR_LEN);
+ strlcpy(host->core, ad_packet_buffer.core, NETPLAY_HOST_STR_LEN);
+ strlcpy(host->retroarch_version, ad_packet_buffer.retroarch_version,
+ NETPLAY_HOST_STR_LEN);
+ strlcpy(host->core_version, ad_packet_buffer.core_version,
+ NETPLAY_HOST_STR_LEN);
+ strlcpy(host->content, ad_packet_buffer.content,
+ NETPLAY_HOST_LONGSTR_LEN);
+ strlcpy(host->subsystem_name, ad_packet_buffer.subsystem_name,
+ NETPLAY_HOST_LONGSTR_LEN);
+ strlcpy(host->frontend, ad_packet_buffer.frontend,
+ NETPLAY_HOST_STR_LEN);
+
+ host->content_crc =
+ atoi(ad_packet_buffer.content_crc);
+ host->nick[NETPLAY_HOST_STR_LEN-1] =
+ host->core[NETPLAY_HOST_STR_LEN-1] =
+ host->core_version[NETPLAY_HOST_STR_LEN-1] =
+ host->content[NETPLAY_HOST_LONGSTR_LEN-1] = '\0';
+ }
+ }
+
+ return true;
+}
+
+/** Initialize Netplay discovery (client) */
+bool init_netplay_discovery(void)
+{
+ struct addrinfo *addr = NULL;
+ int fd = socket_init((void **)&addr, 0, NULL, SOCKET_TYPE_DATAGRAM);
+
+ if (fd < 0)
+ goto error;
+
+ if (!socket_bind(fd, (void*)addr))
+ {
+ socket_close(fd);
+ goto error;
+ }
+
+ lan_ad_client_fd = fd;
+ freeaddrinfo_retro(addr);
+ return true;
+
+error:
+ if (addr)
+ freeaddrinfo_retro(addr);
+ RARCH_ERR("[Discovery] Failed to initialize netplay advertisement client socket.\n");
+ return false;
+}
+
+/** Deinitialize and free Netplay discovery */
+/* TODO/FIXME - this is apparently never called? */
+void deinit_netplay_discovery(void)
+{
+ if (lan_ad_client_fd >= 0)
+ {
+ socket_close(lan_ad_client_fd);
+ lan_ad_client_fd = -1;
+ }
+}
+
+/** Discovery control */
+/* TODO/FIXME: implement net_ifinfo and ntohs for consoles */
+bool netplay_discovery_driver_ctl(
+ enum rarch_netplay_discovery_ctl_state state, void *data)
+{
+ int ret;
+ char port_str[6];
+ unsigned k = 0;
+
+ if (lan_ad_client_fd < 0)
+ return false;
+
+ switch (state)
+ {
+ case RARCH_NETPLAY_DISCOVERY_CTL_LAN_SEND_QUERY:
+ {
+ net_ifinfo_t interfaces;
+ struct addrinfo hints = {0}, *addr;
+
+ if (!net_ifinfo_new(&interfaces))
+ return false;
+
+ /* Get the broadcast address (IPv4 only for now) */
+ snprintf(port_str, 6, "%hu", (unsigned short) RARCH_DEFAULT_PORT);
+ if (getaddrinfo_retro("255.255.255.255", port_str, &hints, &addr) < 0)
+ return false;
+
+ /* Make it broadcastable */
+#if defined(SOL_SOCKET) && defined(SO_BROADCAST)
+ {
+ int can_broadcast = 1;
+ if (setsockopt(lan_ad_client_fd, SOL_SOCKET, SO_BROADCAST,
+ (const char *)&can_broadcast, sizeof(can_broadcast)) < 0)
+ RARCH_WARN("[Discovery] Failed to set netplay discovery port to broadcast\n");
+ }
+#endif
+
+ /* Put together the request */
+ memcpy((void *) &ad_packet_buffer, "RANQ", 4);
+ ad_packet_buffer.protocol_version = htonl(NETPLAY_PROTOCOL_VERSION);
+
+ for (k = 0; k < (unsigned)interfaces.size; k++)
+ {
+ strlcpy(ad_packet_buffer.address, interfaces.entries[k].host,
+ NETPLAY_HOST_STR_LEN);
+
+ /* And send it off */
+ ret = (int)sendto(lan_ad_client_fd, (const char *) &ad_packet_buffer,
+ sizeof(struct ad_packet), 0, addr->ai_addr, addr->ai_addrlen);
+ if (ret < (ssize_t) (2*sizeof(uint32_t)))
+ RARCH_WARN("[Discovery] Failed to send netplay discovery query (error: %d)\n", errno);
+ }
+
+ freeaddrinfo_retro(addr);
+ net_ifinfo_free(&interfaces);
+
+ break;
+ }
+
+ case RARCH_NETPLAY_DISCOVERY_CTL_LAN_GET_RESPONSES:
+ if (!netplay_lan_ad_client())
+ return false;
+ *((struct netplay_host_list **) data) = &discovered_hosts;
+ break;
+
+ case RARCH_NETPLAY_DISCOVERY_CTL_LAN_CLEAR_RESPONSES:
+ discovered_hosts.size = 0;
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool init_lan_ad_server_socket(netplay_t *netplay, uint16_t port)
+{
+ struct addrinfo *addr = NULL;
+ int fd = socket_init((void **) &addr, port, NULL, SOCKET_TYPE_DATAGRAM);
+
+ if (fd < 0)
+ goto error;
+
+ if (!socket_bind(fd, (void*)addr))
+ {
+ socket_close(fd);
+ goto error;
+ }
+
+ lan_ad_server_fd = fd;
+ freeaddrinfo_retro(addr);
+
+ return true;
+
+error:
+ if (addr)
+ freeaddrinfo_retro(addr);
+ return false;
+}
+
+/**
+ * netplay_lan_ad_server
+ *
+ * Respond to any LAN ad queries that the netplay server has received.
+ */
+bool netplay_lan_ad_server(netplay_t *netplay)
+{
+ /* TODO/FIXME: implement net_ifinfo and ntohs for consoles */
+ fd_set fds;
+ int ret;
+ unsigned i;
+ char buf[4096];
+ net_ifinfo_t interfaces;
+ socklen_t addr_size;
+ char reply_addr[NETPLAY_HOST_STR_LEN], port_str[6];
+ struct sockaddr their_addr;
+ struct timeval tmp_tv = {0};
+ unsigned k = 0;
+ struct addrinfo *our_addr, hints = {0};
+ struct string_list *subsystem = path_get_subsystem_list();
+
+ interfaces.entries = NULL;
+ interfaces.size = 0;
+
+ their_addr.sa_family = 0;
+ for (i = 0; i < 14; i++)
+ their_addr.sa_data[i] = 0;
+
+ if (!net_ifinfo_new(&interfaces))
+ return false;
+
+ if ( (lan_ad_server_fd < 0)
+ && !init_lan_ad_server_socket(netplay, RARCH_DEFAULT_PORT))
+ {
+ RARCH_ERR("[Discovery] Failed to initialize netplay advertisement socket\n");
+ return false;
+ }
+
+ /* Check for any ad queries */
+ for (;;)
+ {
+ FD_ZERO(&fds);
+ FD_SET(lan_ad_server_fd, &fds);
+ if (socket_select(lan_ad_server_fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0)
+ break;
+ if (!FD_ISSET(lan_ad_server_fd, &fds))
+ break;
+
+ /* Somebody queried, so check that it's valid */
+ addr_size = sizeof(their_addr);
+ ret = (int)recvfrom(lan_ad_server_fd, (char*)&ad_packet_buffer,
+ sizeof(struct ad_packet), 0, &their_addr, &addr_size);
+ if (ret >= (ssize_t) (2 * sizeof(uint32_t)))
+ {
+ char s[NETPLAY_HOST_STR_LEN];
+ uint32_t content_crc = 0;
+
+ /* Make sure it's a valid query */
+ if (memcmp((void *) &ad_packet_buffer, "RANQ", 4))
+ {
+ RARCH_LOG("[Discovery] Invalid query\n");
+ continue;
+ }
+
+ /* For this version */
+ if (ntohl(ad_packet_buffer.protocol_version) !=
+ NETPLAY_PROTOCOL_VERSION)
+ {
+ RARCH_LOG("[Discovery] Invalid protocol version\n");
+ continue;
+ }
+
+ if (!string_is_empty(ad_packet_buffer.address))
+ strlcpy(reply_addr, ad_packet_buffer.address, NETPLAY_HOST_STR_LEN);
+
+ for (k = 0; k < interfaces.size; k++)
+ {
+ char *p;
+ char sub[NETPLAY_HOST_STR_LEN];
+ char frontend_architecture_tmp[32];
+ char frontend[256];
+ const frontend_ctx_driver_t *frontend_drv =
+ (const frontend_ctx_driver_t*)
+ frontend_driver_get_cpu_architecture_str(
+ frontend_architecture_tmp, sizeof(frontend_architecture_tmp));
+ snprintf(frontend, sizeof(frontend), "%s %s",
+ frontend_drv->ident, frontend_architecture_tmp);
+
+ p=strrchr(reply_addr,'.');
+ if (p)
+ {
+ strlcpy(sub, reply_addr, p - reply_addr + 1);
+ if (strstr(interfaces.entries[k].host, sub) &&
+ !strstr(interfaces.entries[k].host, "127.0.0.1"))
+ {
+ struct retro_system_info *info = runloop_get_libretro_system_info();
+
+ RARCH_LOG ("[Discovery] Query received on common interface: %s/%s (theirs / ours) \n",
+ reply_addr, interfaces.entries[k].host);
+
+ /* Now build our response */
+ buf[0] = '\0';
+ content_crc = content_get_crc();
+
+ memset(&ad_packet_buffer, 0, sizeof(struct ad_packet));
+ memcpy(&ad_packet_buffer, "RANS", 4);
+
+ if (subsystem)
+ {
+ unsigned i;
+
+ for (i = 0; i < subsystem->size; i++)
+ {
+ strlcat(buf, path_basename(subsystem->elems[i].data), NETPLAY_HOST_LONGSTR_LEN);
+ if (i < subsystem->size - 1)
+ strlcat(buf, "|", NETPLAY_HOST_LONGSTR_LEN);
+ }
+ strlcpy(ad_packet_buffer.content, buf,
+ NETPLAY_HOST_LONGSTR_LEN);
+ strlcpy(ad_packet_buffer.subsystem_name, path_get(RARCH_PATH_SUBSYSTEM),
+ NETPLAY_HOST_STR_LEN);
+ }
+ else
+ {
+ strlcpy(ad_packet_buffer.content, !string_is_empty(
+ path_basename(path_get(RARCH_PATH_BASENAME)))
+ ? path_basename(path_get(RARCH_PATH_BASENAME)) : "N/A",
+ NETPLAY_HOST_LONGSTR_LEN);
+ strlcpy(ad_packet_buffer.subsystem_name, "N/A", NETPLAY_HOST_STR_LEN);
+ }
+
+ strlcpy(ad_packet_buffer.address, interfaces.entries[k].host,
+ NETPLAY_HOST_STR_LEN);
+ ad_packet_buffer.protocol_version =
+ htonl(NETPLAY_PROTOCOL_VERSION);
+ ad_packet_buffer.port = htonl(netplay->tcp_port);
+ strlcpy(ad_packet_buffer.retroarch_version, PACKAGE_VERSION,
+ NETPLAY_HOST_STR_LEN);
+ strlcpy(ad_packet_buffer.nick, netplay->nick, NETPLAY_HOST_STR_LEN);
+ strlcpy(ad_packet_buffer.frontend, frontend, NETPLAY_HOST_STR_LEN);
+
+ if (info)
+ {
+ strlcpy(ad_packet_buffer.core, info->library_name,
+ NETPLAY_HOST_STR_LEN);
+ strlcpy(ad_packet_buffer.core_version, info->library_version,
+ NETPLAY_HOST_STR_LEN);
+ }
+
+ snprintf(s, sizeof(s), "%d", content_crc);
+ strlcpy(ad_packet_buffer.content_crc, s,
+ NETPLAY_HOST_STR_LEN);
+
+ /* Build up the destination address*/
+ snprintf(port_str, 6, "%hu", ntohs(((struct sockaddr_in*)(&their_addr))->sin_port));
+ if (getaddrinfo_retro(reply_addr, port_str, &hints, &our_addr) < 0)
+ continue;
+
+ RARCH_LOG ("[Discovery] Sending reply to %s \n", reply_addr);
+
+ /* And send it */
+ sendto(lan_ad_server_fd, (const char*)&ad_packet_buffer,
+ sizeof(struct ad_packet), 0, our_addr->ai_addr, our_addr->ai_addrlen);
+ freeaddrinfo_retro(our_addr);
+ }
+ else
+ continue;
+ }
+ else
+ continue;
+ }
+ }
+ }
+ net_ifinfo_free(&interfaces);
+ return true;
+}
+#endif
+
+/* TODO/FIXME - replace netplay_log_connection with calls
+ * to inet_ntop_compat and move runloop message queue pushing
+ * outside */
+#if !defined(HAVE_SOCKET_LEGACY) && !defined(WIIU) && !defined(_3DS)
+/* Custom inet_ntop. Win32 doesn't seem to support this ... */
+static void netplay_log_connection(
+ const struct sockaddr_storage *their_addr,
+ unsigned slot, const char *nick, char *s, size_t len)
+{
+ union
+ {
+ const struct sockaddr_storage *storage;
+ const struct sockaddr_in *v4;
+ const struct sockaddr_in6 *v6;
+ } u;
+ const char *str = NULL;
+ char buf_v4[INET_ADDRSTRLEN] = {0};
+ char buf_v6[INET6_ADDRSTRLEN] = {0};
+
+ u.storage = their_addr;
+
+ switch (their_addr->ss_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in in;
+
+ memset(&in, 0, sizeof(in));
+
+ str = buf_v4;
+ in.sin_family = AF_INET;
+ memcpy(&in.sin_addr, &u.v4->sin_addr, sizeof(struct in_addr));
+
+ getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in),
+ buf_v4, sizeof(buf_v4),
+ NULL, 0, NI_NUMERICHOST);
+ }
+ break;
+ case AF_INET6:
+ {
+ struct sockaddr_in6 in;
+ memset(&in, 0, sizeof(in));
+
+ str = buf_v6;
+ in.sin6_family = AF_INET6;
+ memcpy(&in.sin6_addr, &u.v6->sin6_addr, sizeof(struct in6_addr));
+
+ getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in6),
+ buf_v6, sizeof(buf_v6), NULL, 0, NI_NUMERICHOST);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (str)
+ snprintf(s, len, msg_hash_to_str(MSG_GOT_CONNECTION_FROM_NAME),
+ nick, str);
+ else
+ snprintf(s, len, msg_hash_to_str(MSG_GOT_CONNECTION_FROM),
+ nick);
+}
+#else
+static void netplay_log_connection(
+ const struct sockaddr_storage *their_addr,
+ unsigned slot, const char *nick, char *s, size_t len)
+{
+ /* Stub code - will need to be implemented */
+ snprintf(s, len, msg_hash_to_str(MSG_GOT_CONNECTION_FROM),
+ nick);
+}
+#endif
+
+/**
+ * netplay_impl_magic:
+ *
+ * A pseudo-hash of the RetroArch and Netplay version, so only compatible
+ * versions play together.
+ */
+static uint32_t netplay_impl_magic(void)
+{
+ size_t i;
+ uint32_t res = 0;
+ const char *ver = PACKAGE_VERSION;
+ size_t len = strlen(ver);
+
+ for (i = 0; i < len; i++)
+ res ^= ver[i] << (i & 0xf);
+
+ res ^= NETPLAY_PROTOCOL_VERSION << (i & 0xf);
+
+ return res;
+}
+
+/**
+ * netplay_platform_magic
+ *
+ * Just enough info to tell us if our platforms mismatch: Endianness and a
+ * couple of type sizes.
+ *
+ * Format:
+ * bit 31: Reserved
+ * bit 30: 1 for big endian
+ * bits 29-15: sizeof(size_t)
+ * bits 14-0: sizeof(long)
+ */
+static uint32_t netplay_platform_magic(void)
+{
+ uint32_t ret =
+ ((1 == htonl(1)) << 30)
+ |(sizeof(size_t) << 15)
+ |(sizeof(long));
+ return ret;
+}
+
+/**
+ * netplay_endian_mismatch
+ *
+ * Do the platform magics mismatch on endianness?
+ */
+static bool netplay_endian_mismatch(uint32_t pma, uint32_t pmb)
+{
+ uint32_t ebit = (1<<30);
+ return (pma & ebit) != (pmb & ebit);
+}
+
+static int simple_rand(void)
+{
+ simple_rand_next = simple_rand_next * 1103515245 + 12345;
+ return((unsigned)(simple_rand_next / 65536) % 32768);
+}
+
+static void simple_srand(unsigned int seed)
+{
+ simple_rand_next = seed;
+}
+
+static uint32_t simple_rand_uint32(void)
+{
+ uint32_t parts[3];
+ parts[0] = simple_rand();
+ parts[1] = simple_rand();
+ parts[2] = simple_rand();
+ return ((parts[0] << 30) +
+ (parts[1] << 15) +
+ parts[2]);
+}
+
+/**
+ * netplay_handshake_init_send
+ *
+ * Initialize our handshake and send the first part of the handshake protocol.
+ */
+bool netplay_handshake_init_send(netplay_t *netplay,
+ struct netplay_connection *connection)
+{
+ uint32_t header[6];
+ unsigned conn_salt = 0;
+ settings_t *settings = config_get_ptr();
+
+ header[0] = htonl(NETPLAY_MAGIC);
+ header[1] = htonl(netplay_platform_magic());
+ header[2] = htonl(NETPLAY_COMPRESSION_SUPPORTED);
+ header[3] = 0;
+ header[4] = htonl(NETPLAY_PROTOCOL_VERSION);
+ header[5] = htonl(netplay_impl_magic());
+
+ if (netplay->is_server &&
+ (settings->paths.netplay_password[0] ||
+ settings->paths.netplay_spectate_password[0]))
+ {
+ /* Demand a password */
+ if (simple_rand_next == 1)
+ simple_srand((unsigned int) time(NULL));
+ connection->salt = simple_rand_uint32();
+ if (connection->salt == 0)
+ connection->salt = 1;
+ conn_salt = connection->salt;
+ }
+
+ header[3] = htonl(conn_salt);
+
+ if (!netplay_send(&connection->send_packet_buffer, connection->fd, header,
+ sizeof(header)) ||
+ !netplay_send_flush(&connection->send_packet_buffer, connection->fd, false))
+ return false;
+
+ return true;
+}
+
+#ifdef HAVE_MENU
+static void handshake_password(void *ignore, const char *line)
+{
+ struct password_buf_s password_buf;
+ char password[8+NETPLAY_PASS_LEN]; /* 8 for salt, 128 for password */
+ char hash[NETPLAY_PASS_HASH_LEN+1]; /* + NULL terminator */
+ netplay_t *netplay = handshake_password_netplay;
+ struct netplay_connection *connection = &netplay->connections[0];
+
+ snprintf(password, sizeof(password), "%08X", connection->salt);
+ if (!string_is_empty(line))
+ strlcpy(password + 8, line, sizeof(password)-8);
+
+ password_buf.cmd[0] = htonl(NETPLAY_CMD_PASSWORD);
+ password_buf.cmd[1] = htonl(sizeof(password_buf.password));
+ sha256_hash(hash, (uint8_t *) password, strlen(password));
+ memcpy(password_buf.password, hash, NETPLAY_PASS_HASH_LEN);
+
+ /* We have no way to handle an error here, so we'll let the next function error out */
+ if (netplay_send(&connection->send_packet_buffer, connection->fd, &password_buf, sizeof(password_buf)))
+ netplay_send_flush(&connection->send_packet_buffer, connection->fd, false);
+
+#ifdef HAVE_MENU
+ menu_input_dialog_end();
+ retroarch_menu_running_finished(false);
+#endif
+}
+#endif
+
+/**
+ * netplay_handshake_init
+ *
+ * Data receiver for the initial part of the handshake, i.e., waiting for the
+ * netplay header.
+ */
+bool netplay_handshake_init(netplay_t *netplay,
+ struct netplay_connection *connection, bool *had_input)
+{
+ ssize_t recvd;
+ struct nick_buf_s nick_buf;
+ uint32_t header[6];
+ uint32_t local_pmagic = 0;
+ uint32_t remote_pmagic = 0;
+ uint32_t remote_version = 0;
+ uint32_t compression = 0;
+ struct compression_transcoder *ctrans = NULL;
+ const char *dmsg = NULL;
+
+ memset(header, 0, sizeof(header));
+
+ RECV(header, sizeof(uint32_t))
+ {
+ dmsg = msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT);
+ goto error;
+ }
+
+ if (ntohl(header[0]) != NETPLAY_MAGIC)
+ {
+ dmsg = msg_hash_to_str(MSG_NETPLAY_NOT_RETROARCH);
+ goto error;
+ }
+
+ RECV(header + 1, sizeof(header) - sizeof(uint32_t))
+ {
+ dmsg = msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT);
+ goto error;
+ }
+
+ remote_version = ntohl(header[4]);
+ if (remote_version < NETPLAY_PROTOCOL_VERSION)
+ {
+ dmsg = msg_hash_to_str(MSG_NETPLAY_OUT_OF_DATE);
+ goto error;
+ }
+
+ if (ntohl(header[5]) != netplay_impl_magic())
+ {
+ /* We allow the connection but warn that this could cause issues. */
+ dmsg = msg_hash_to_str(MSG_NETPLAY_DIFFERENT_VERSIONS);
+ RARCH_WARN("%s\n", dmsg);
+ runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
+ }
+
+ /* We only care about platform magic if our core is quirky */
+ local_pmagic = netplay_platform_magic();
+ remote_pmagic = ntohl(header[1]);
+
+ if ((netplay->quirks & NETPLAY_QUIRK_ENDIAN_DEPENDENT) &&
+ netplay_endian_mismatch(local_pmagic, remote_pmagic))
+ {
+ RARCH_ERR("Endianness mismatch with an endian-sensitive core.\n");
+ dmsg = msg_hash_to_str(MSG_NETPLAY_ENDIAN_DEPENDENT);
+ goto error;
+ }
+ if ((netplay->quirks & NETPLAY_QUIRK_PLATFORM_DEPENDENT) &&
+ (local_pmagic != remote_pmagic))
+ {
+ RARCH_ERR("Platform mismatch with a platform-sensitive core.\n");
+ dmsg = msg_hash_to_str(MSG_NETPLAY_PLATFORM_DEPENDENT);
+ goto error;
+ }
+
+ /* Check what compression is supported */
+ compression = ntohl(header[2]);
+ compression &= NETPLAY_COMPRESSION_SUPPORTED;
+
+ if (compression & NETPLAY_COMPRESSION_ZLIB)
+ {
+ ctrans = &netplay->compress_zlib;
+ if (!ctrans->compression_backend)
+ {
+ ctrans->compression_backend =
+ trans_stream_get_zlib_deflate_backend();
+ if (!ctrans->compression_backend)
+ ctrans->compression_backend = trans_stream_get_pipe_backend();
+ }
+ connection->compression_supported = NETPLAY_COMPRESSION_ZLIB;
+ }
+ else
+ {
+ ctrans = &netplay->compress_nil;
+ if (!ctrans->compression_backend)
+ {
+ ctrans->compression_backend =
+ trans_stream_get_pipe_backend();
+ }
+ connection->compression_supported = 0;
+ }
+
+ if (!ctrans->decompression_backend)
+ ctrans->decompression_backend = ctrans->compression_backend->reverse;
+
+ /* Allocate our compression stream */
+ if (!ctrans->compression_stream)
+ {
+ ctrans->compression_stream = ctrans->compression_backend->stream_new();
+ ctrans->decompression_stream = ctrans->decompression_backend->stream_new();
+ }
+ if (!ctrans->compression_stream || !ctrans->decompression_stream)
+ {
+ RARCH_ERR("Failed to allocate compression transcoder!\n");
+ return false;
+ }
+
+ /* If a password is demanded, ask for it */
+ if (!netplay->is_server && (connection->salt = ntohl(header[3])))
+ {
+#ifdef HAVE_MENU
+ menu_input_ctx_line_t line;
+ retroarch_menu_running();
+#endif
+
+ handshake_password_netplay = netplay;
+
+#ifdef HAVE_MENU
+ memset(&line, 0, sizeof(line));
+ line.label = msg_hash_to_str(MSG_NETPLAY_ENTER_PASSWORD);
+ line.label_setting = "no_setting";
+ line.cb = handshake_password;
+ if (!menu_input_dialog_start(&line))
+ return false;
+#endif
+ }
+
+ /* Send our nick */
+ nick_buf.cmd[0] = htonl(NETPLAY_CMD_NICK);
+ nick_buf.cmd[1] = htonl(sizeof(nick_buf.nick));
+ memset(nick_buf.nick, 0, sizeof(nick_buf.nick));
+ strlcpy(nick_buf.nick, netplay->nick, sizeof(nick_buf.nick));
+ if (!netplay_send(&connection->send_packet_buffer, connection->fd, &nick_buf,
+ sizeof(nick_buf)) ||
+ !netplay_send_flush(&connection->send_packet_buffer, connection->fd, false))
+ return false;
+
+ /* Move on to the next mode */
+ connection->mode = NETPLAY_CONNECTION_PRE_NICK;
+ *had_input = true;
+ netplay_recv_flush(&connection->recv_packet_buffer);
+ return true;
+
+error:
+ if (dmsg)
+ {
+ RARCH_ERR("%s\n", dmsg);
+ runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
+ }
+ return false;
+}
+
+static void netplay_handshake_ready(netplay_t *netplay,
+ struct netplay_connection *connection)
+{
+ char msg[512];
+ msg[0] = '\0';
+
+ if (netplay->is_server)
+ {
+ unsigned slot = (unsigned)(connection - netplay->connections);
+
+ netplay_log_connection(&connection->addr,
+ slot, connection->nick, msg, sizeof(msg));
+
+ RARCH_LOG("%s %u\n", msg_hash_to_str(MSG_CONNECTION_SLOT), slot);
+
+ /* Send them the savestate */
+ if (!(netplay->quirks &
+ (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)))
+ netplay->force_send_savestate = true;
+ }
+ else
+ {
+ netplay->is_connected = true;
+ snprintf(msg, sizeof(msg), "%s: \"%s\"",
+ msg_hash_to_str(MSG_CONNECTED_TO),
+ connection->nick);
+ }
+
+ RARCH_LOG("%s\n", msg);
+ runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
+
+ /* Unstall if we were waiting for this */
+ if (netplay->stall == NETPLAY_STALL_NO_CONNECTION)
+ netplay->stall = NETPLAY_STALL_NONE;
+}
+
+/**
+ * netplay_handshake_info
+ *
+ * Send an INFO command.
+ */
+static bool netplay_handshake_info(netplay_t *netplay,
+ struct netplay_connection *connection)
+{
+ struct info_buf_s info_buf;
+ uint32_t content_crc = 0;
+ struct retro_system_info *system = runloop_get_libretro_system_info();
+
+ memset(&info_buf, 0, sizeof(info_buf));
+ info_buf.cmd[0] = htonl(NETPLAY_CMD_INFO);
+ info_buf.cmd[1] = htonl(sizeof(info_buf) - 2*sizeof(uint32_t));
+
+ /* Get our core info */
+ if (system)
+ {
+ strlcpy(info_buf.core_name,
+ system->library_name, sizeof(info_buf.core_name));
+ strlcpy(info_buf.core_version,
+ system->library_version, sizeof(info_buf.core_version));
+ }
+ else
+ {
+ strlcpy(info_buf.core_name,
+ "UNKNOWN", sizeof(info_buf.core_name));
+ strlcpy(info_buf.core_version,
+ "UNKNOWN", sizeof(info_buf.core_version));
+ }
+
+ /* Get our content CRC */
+ content_crc = content_get_crc();
+
+ if (content_crc != 0)
+ info_buf.content_crc = htonl(content_crc);
+
+ /* Send it off and wait for info back */
+ if (!netplay_send(&connection->send_packet_buffer, connection->fd,
+ &info_buf, sizeof(info_buf)) ||
+ !netplay_send_flush(&connection->send_packet_buffer, connection->fd,
+ false))
+ return false;
+
+ connection->mode = NETPLAY_CONNECTION_PRE_INFO;
+ return true;
+}
+
+/**
+ * netplay_handshake_sync
+ *
+ * Send a SYNC command.
+ */
+static bool netplay_handshake_sync(netplay_t *netplay,
+ struct netplay_connection *connection)
+{
+ /* If we're the server, now we send sync info */
+ size_t i;
+ int matchct;
+ uint32_t cmd[4];
+ retro_ctx_memory_info_t mem_info;
+ uint32_t client_num = 0;
+ uint32_t device = 0;
+ size_t nicklen, nickmangle = 0;
+ bool nick_matched = false;
+
+#ifdef HAVE_THREADS
+ autosave_lock();
+#endif
+ mem_info.id = RETRO_MEMORY_SAVE_RAM;
+ core_get_memory(&mem_info);
+#ifdef HAVE_THREADS
+ autosave_unlock();
+#endif
+
+ /* Send basic sync info */
+ cmd[0] = htonl(NETPLAY_CMD_SYNC);
+ cmd[1] = htonl(2*sizeof(uint32_t)
+ /* Controller devices */
+ + MAX_INPUT_DEVICES*sizeof(uint32_t)
+
+ /* Share modes */
+ + MAX_INPUT_DEVICES*sizeof(uint8_t)
+
+ /* Device-client mapping */
+ + MAX_INPUT_DEVICES*sizeof(uint32_t)
+
+ /* Client nick */
+ + NETPLAY_NICK_LEN
+
+ /* And finally, sram */
+ + mem_info.size);
+ cmd[2] = htonl(netplay->self_frame_count);
+ client_num = (uint32_t)(connection - netplay->connections + 1);
+
+ if (netplay->local_paused || netplay->remote_paused)
+ client_num |= NETPLAY_CMD_SYNC_BIT_PAUSED;
+
+ cmd[3] = htonl(client_num);
+
+ if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd,
+ sizeof(cmd)))
+ return false;
+
+ /* Now send the device info */
+ for (i = 0; i < MAX_INPUT_DEVICES; i++)
+ {
+ device = htonl(netplay->config_devices[i]);
+ if (!netplay_send(&connection->send_packet_buffer, connection->fd,
+ &device, sizeof(device)))
+ return false;
+ }
+
+ /* Then the share mode */
+ if (!netplay_send(&connection->send_packet_buffer, connection->fd,
+ netplay->device_share_modes, sizeof(netplay->device_share_modes)))
+ return false;
+
+ /* Then the device-client mapping */
+ for (i = 0; i < MAX_INPUT_DEVICES; i++)
+ {
+ device = htonl(netplay->device_clients[i]);
+ if (!netplay_send(&connection->send_packet_buffer, connection->fd,
+ &device, sizeof(device)))
+ return false;
+ }
+
+ /* Now see if we need to mangle their nick */
+ nicklen = strlen(connection->nick);
+ if (nicklen > NETPLAY_NICK_LEN - 5)
+ nickmangle = NETPLAY_NICK_LEN - 5;
+ else
+ nickmangle = nicklen;
+ matchct = 1;
+ do
+ {
+ nick_matched = false;
+ for (i = 0; i < netplay->connections_size; i++)
+ {
+ struct netplay_connection *sc = &netplay->connections[i];
+ if (sc == connection)
+ continue;
+ if (sc->active &&
+ sc->mode >= NETPLAY_CONNECTION_CONNECTED &&
+ !strncmp(connection->nick, sc->nick, NETPLAY_NICK_LEN))
+ {
+ nick_matched = true;
+ break;
+ }
+ }
+ if (!strncmp(connection->nick, netplay->nick, NETPLAY_NICK_LEN))
+ nick_matched = true;
+
+ if (nick_matched)
+ {
+ /* Somebody has this nick, make a new one! */
+ snprintf(connection->nick + nickmangle,
+ NETPLAY_NICK_LEN - nickmangle, " (%d)", ++matchct);
+ connection->nick[NETPLAY_NICK_LEN - 1] = '\0';
+ }
+ } while (nick_matched);
+
+ /* Send the nick */
+ if (!netplay_send(&connection->send_packet_buffer, connection->fd,
+ connection->nick, NETPLAY_NICK_LEN))
+ return false;
+
+ /* And finally, the SRAM */
+#ifdef HAVE_THREADS
+ autosave_lock();
+#endif
+ if (!netplay_send(&connection->send_packet_buffer, connection->fd,
+ mem_info.data, mem_info.size) ||
+ !netplay_send_flush(&connection->send_packet_buffer, connection->fd,
+ false))
+ {
+#ifdef HAVE_THREADS
+ autosave_unlock();
+#endif
+ return false;
+ }
+#ifdef HAVE_THREADS
+ autosave_unlock();
+#endif
+
+ /* Now we're ready! */
+ connection->mode = NETPLAY_CONNECTION_SPECTATING;
+ netplay_handshake_ready(netplay, connection);
+
+ return true;
+}
+
+/**
+ * netplay_handshake_pre_nick
+ *
+ * Data receiver for the second stage of handshake, receiving the other side's
+ * nickname.
+ */
+static bool netplay_handshake_pre_nick(netplay_t *netplay,
+ struct netplay_connection *connection, bool *had_input)
+{
+ struct nick_buf_s nick_buf;
+ ssize_t recvd;
+ char msg[512];
+
+ msg[0] = '\0';
+
+ RECV(&nick_buf, sizeof(nick_buf)) {}
+
+ /* Expecting only a nick command */
+ if (recvd < 0 ||
+ ntohl(nick_buf.cmd[0]) != NETPLAY_CMD_NICK ||
+ ntohl(nick_buf.cmd[1]) != sizeof(nick_buf.nick))
+ {
+ if (netplay->is_server)
+ strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT),
+ sizeof(msg));
+ else
+ strlcpy(msg,
+ msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST),
+ sizeof(msg));
+ RARCH_ERR("%s\n", msg);
+ runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
+ return false;
+ }
+
+ strlcpy(connection->nick, nick_buf.nick,
+ (sizeof(connection->nick) < sizeof(nick_buf.nick)) ?
+ sizeof(connection->nick) : sizeof(nick_buf.nick));
+
+ if (netplay->is_server)
+ {
+ settings_t *settings = config_get_ptr();
+
+ /* There's a password, so just put them in PRE_PASSWORD mode */
+ if ( settings->paths.netplay_password[0] ||
+ settings->paths.netplay_spectate_password[0])
+ connection->mode = NETPLAY_CONNECTION_PRE_PASSWORD;
+ else
+ {
+ connection->can_play = true;
+ if (!netplay_handshake_info(netplay, connection))
+ return false;
+ connection->mode = NETPLAY_CONNECTION_PRE_INFO;
+ }
+ }
+ /* Client needs to wait for INFO */
+ else
+ connection->mode = NETPLAY_CONNECTION_PRE_INFO;
+
+ *had_input = true;
+ netplay_recv_flush(&connection->recv_packet_buffer);
+ return true;
+}
+
+/**
+ * netplay_handshake_pre_password
+ *
+ * Data receiver for the third, optional stage of server handshake, receiving
+ * the password and sending core/content info.
+ */
+static bool netplay_handshake_pre_password(netplay_t *netplay,
+ struct netplay_connection *connection, bool *had_input)
+{
+ struct password_buf_s password_buf;
+ char password[8+NETPLAY_PASS_LEN]; /* 8 for salt */
+ char hash[NETPLAY_PASS_HASH_LEN+1]; /* + NULL terminator */
+ ssize_t recvd;
+ char msg[512];
+ bool correct = false;
+ settings_t *settings = config_get_ptr();
+
+ msg[0] = '\0';
+
+ RECV(&password_buf, sizeof(password_buf)) {}
+
+ /* Expecting only a password command */
+ if (recvd < 0 ||
+ ntohl(password_buf.cmd[0]) != NETPLAY_CMD_PASSWORD ||
+ ntohl(password_buf.cmd[1]) != sizeof(password_buf.password))
+ {
+ if (netplay->is_server)
+ strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT),
+ sizeof(msg));
+ else
+ strlcpy(msg,
+ msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST),
+ sizeof(msg));
+ RARCH_ERR("%s\n", msg);
+ runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
+ return false;
+ }
+
+ /* Calculate the correct password hash(es) and compare */
+ correct = false;
+ snprintf(password, sizeof(password), "%08X", connection->salt);
+
+ if (settings->paths.netplay_password[0])
+ {
+ strlcpy(password + 8,
+ settings->paths.netplay_password, sizeof(password)-8);
+
+ sha256_hash(hash, (uint8_t *) password, strlen(password));
+
+ if (!memcmp(password_buf.password, hash, NETPLAY_PASS_HASH_LEN))
+ {
+ correct = true;
+ connection->can_play = true;
+ }
+ }
+ if (settings->paths.netplay_spectate_password[0])
+ {
+ strlcpy(password + 8,
+ settings->paths.netplay_spectate_password, sizeof(password)-8);
+
+ sha256_hash(hash, (uint8_t *) password, strlen(password));
+
+ if (!memcmp(password_buf.password, hash, NETPLAY_PASS_HASH_LEN))
+ correct = true;
+ }
+
+ /* Just disconnect if it was wrong */
+ if (!correct)
+ return false;
+
+ /* Otherwise, exchange info */
+ if (!netplay_handshake_info(netplay, connection))
+ return false;
+
+ *had_input = true;
+ connection->mode = NETPLAY_CONNECTION_PRE_INFO;
+ netplay_recv_flush(&connection->recv_packet_buffer);
+ return true;
+}
+
+/**
+ * netplay_handshake_pre_info
+ *
+ * Data receiver for the third stage of server handshake, receiving
+ * the password.
+ */
+static bool netplay_handshake_pre_info(netplay_t *netplay,
+ struct netplay_connection *connection, bool *had_input)
+{
+ struct info_buf_s info_buf;
+ uint32_t cmd_size;
+ ssize_t recvd;
+ uint32_t content_crc = 0;
+ const char *dmsg = NULL;
+ struct retro_system_info *system = runloop_get_libretro_system_info();
+
+ RECV(&info_buf, sizeof(info_buf.cmd)) {}
+
+ if (recvd < 0 ||
+ ntohl(info_buf.cmd[0]) != NETPLAY_CMD_INFO)
+ {
+ RARCH_ERR("Failed to receive netplay info.\n");
+ return false;
+ }
+
+ cmd_size = ntohl(info_buf.cmd[1]);
+ if (cmd_size != sizeof(info_buf) - 2*sizeof(uint32_t))
+ {
+ /* Either the host doesn't have anything loaded, or this is just screwy */
+ if (cmd_size != 0)
+ {
+ /* Huh? */
+ RARCH_ERR("Invalid NETPLAY_CMD_INFO payload size.\n");
+ return false;
+ }
+
+ /* Send our info and hope they load it! */
+ if (!netplay_handshake_info(netplay, connection))
+ return false;
+
+ *had_input = true;
+ netplay_recv_flush(&connection->recv_packet_buffer);
+ return true;
+ }
+
+ RECV(&info_buf.content_crc, cmd_size)
+ {
+ RARCH_ERR("Failed to receive netplay info payload.\n");
+ return false;
+ }
+
+ /* Check the core info */
+ if (system)
+ {
+ if (strncmp(info_buf.core_name,
+ system->library_name, sizeof(info_buf.core_name)))
+ {
+ /* Wrong core! */
+ dmsg = msg_hash_to_str(MSG_NETPLAY_DIFFERENT_CORES);
+ RARCH_ERR("%s\n", dmsg);
+ runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
+ /* FIXME: Should still send INFO, so the other side knows what's what */
+ return false;
+ }
+ if (strncmp(info_buf.core_version,
+ system->library_version, sizeof(info_buf.core_version)))
+ {
+ dmsg = msg_hash_to_str(MSG_NETPLAY_DIFFERENT_CORE_VERSIONS);
+ RARCH_WARN("%s\n", dmsg);
+ runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
+ }
+ }
+
+ /* Check the content CRC */
+ content_crc = content_get_crc();
+
+ if (content_crc != 0)
+ {
+ if (ntohl(info_buf.content_crc) != content_crc)
+ {
+ dmsg = msg_hash_to_str(MSG_CONTENT_CRC32S_DIFFER);
+ RARCH_WARN("%s\n", dmsg);
+ runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
+ }
+ }
+
+ /* Now switch to the right mode */
+ if (netplay->is_server)
+ {
+ if (!netplay_handshake_sync(netplay, connection))
+ return false;
+ }
+ else
+ {
+ if (!netplay_handshake_info(netplay, connection))
+ return false;
+ connection->mode = NETPLAY_CONNECTION_PRE_SYNC;
+ }
+
+ *had_input = true;
+ netplay_recv_flush(&connection->recv_packet_buffer);
+ return true;
+}
+
+/**
+ * netplay_handshake_pre_sync
+ *
+ * Data receiver for the client's third handshake stage, receiving the
+ * synchronization information.
+ */
+static bool netplay_handshake_pre_sync(netplay_t *netplay,
+ struct netplay_connection *connection, bool *had_input)
+{
+ uint32_t cmd[2];
+ uint32_t new_frame_count, client_num;
+ uint32_t device;
+ uint32_t local_sram_size, remote_sram_size;
+ size_t i, j;
+ ssize_t recvd;
+ retro_ctx_controller_info_t pad;
+ char new_nick[NETPLAY_NICK_LEN];
+ retro_ctx_memory_info_t mem_info;
+
+ RECV(cmd, sizeof(cmd))
+ {
+ const char *msg = msg_hash_to_str(MSG_NETPLAY_INCORRECT_PASSWORD);
+ RARCH_ERR("%s\n", msg);
+ runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
+ return false;
+ }
+
+ /* Only expecting a sync command */
+ if (ntohl(cmd[0]) != NETPLAY_CMD_SYNC ||
+ ntohl(cmd[1]) < (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) + (MAX_INPUT_DEVICES)*sizeof(uint8_t) +
+ NETPLAY_NICK_LEN)
+ {
+ RARCH_ERR("%s\n",
+ msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST));
+ return false;
+ }
+
+ /* Get the frame count */
+ RECV(&new_frame_count, sizeof(new_frame_count))
+ return false;
+ new_frame_count = ntohl(new_frame_count);
+
+ /* Get our client number and pause mode */
+ RECV(&client_num, sizeof(client_num))
+ return false;
+ client_num = ntohl(client_num);
+ if (client_num & NETPLAY_CMD_SYNC_BIT_PAUSED)
+ {
+ netplay->remote_paused = true;
+ client_num ^= NETPLAY_CMD_SYNC_BIT_PAUSED;
+ }
+ netplay->self_client_num = client_num;
+
+ /* Set our frame counters as requested */
+ netplay->self_frame_count = netplay->run_frame_count =
+ netplay->other_frame_count = netplay->unread_frame_count =
+ netplay->server_frame_count = new_frame_count;
+
+ /* And clear out the framebuffer */
+ for (i = 0; i < netplay->buffer_size; i++)
+ {
+ struct delta_frame *ptr = &netplay->buffer[i];
+
+ ptr->used = false;
+
+ if (i == netplay->self_ptr)
+ {
+ /* Clear out any current data but still use this frame */
+ if (!netplay_delta_frame_ready(netplay, ptr, 0))
+ return false;
+
+ ptr->frame = new_frame_count;
+ ptr->have_local = true;
+ netplay->run_ptr = netplay->other_ptr = netplay->unread_ptr =
+ netplay->server_ptr = i;
+
+ }
+ }
+ for (i = 0; i < MAX_CLIENTS; i++)
+ {
+ netplay->read_ptr[i] = netplay->self_ptr;
+ netplay->read_frame_count[i] = netplay->self_frame_count;
+ }
+
+ /* Get and set each input device */
+ for (i = 0; i < MAX_INPUT_DEVICES; i++)
+ {
+ RECV(&device, sizeof(device))
+ return false;
+
+ pad.port = (unsigned)i;
+ pad.device = ntohl(device);
+ netplay->config_devices[i] = pad.device;
+ if ((pad.device&RETRO_DEVICE_MASK) == RETRO_DEVICE_KEYBOARD)
+ {
+ netplay->have_updown_device = true;
+ netplay_key_hton_init();
+ }
+
+ core_set_controller_port_device(&pad);
+ }
+
+ /* Get the share modes */
+ RECV(netplay->device_share_modes, sizeof(netplay->device_share_modes))
+ return false;
+
+ /* Get the client-controller mapping */
+ netplay->connected_players =
+ netplay->connected_slaves =
+ netplay->self_devices = 0;
+ for (i = 0; i < MAX_CLIENTS; i++)
+ netplay->client_devices[i] = 0;
+ for (i = 0; i < MAX_INPUT_DEVICES; i++)
+ {
+ RECV(&device, sizeof(device))
+ return false;
+ device = ntohl(device);
+
+ netplay->device_clients[i] = device;
+ netplay->connected_players |= device;
+ for (j = 0; j < MAX_CLIENTS; j++)
+ {
+ if (device & (1<client_devices[j] |= 1<nick, new_nick, NETPLAY_NICK_LEN))
+ {
+ char msg[512];
+ strlcpy(netplay->nick, new_nick, NETPLAY_NICK_LEN);
+ snprintf(msg, sizeof(msg),
+ msg_hash_to_str(MSG_NETPLAY_CHANGED_NICK), netplay->nick);
+ RARCH_LOG("%s\n", msg);
+ runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
+ }
+
+ /* Now check the SRAM */
+#ifdef HAVE_THREADS
+ autosave_lock();
+#endif
+ mem_info.id = RETRO_MEMORY_SAVE_RAM;
+ core_get_memory(&mem_info);
+
+ local_sram_size = (unsigned)mem_info.size;
+ remote_sram_size = ntohl(cmd[1]) -
+ (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) - (MAX_INPUT_DEVICES)*sizeof(uint8_t) - NETPLAY_NICK_LEN;
+
+ if (local_sram_size != 0 && local_sram_size == remote_sram_size)
+ {
+ RECV(mem_info.data, local_sram_size)
+ {
+ RARCH_ERR("%s\n",
+ msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST));
+#ifdef HAVE_THREADS
+ autosave_unlock();
+#endif
+ return false;
+ }
+
+ }
+ else if (remote_sram_size != 0)
+ {
+ /* We can't load this, but we still need to get rid of the data */
+ uint32_t quickbuf;
+ while (remote_sram_size > 0)
+ {
+ RECV(&quickbuf, (remote_sram_size > sizeof(uint32_t)) ? sizeof(uint32_t) : remote_sram_size)
+ {
+ RARCH_ERR("%s\n",
+ msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST));
+#ifdef HAVE_THREADS
+ autosave_unlock();
+#endif
+ return false;
+ }
+ if (remote_sram_size > sizeof(uint32_t))
+ remote_sram_size -= sizeof(uint32_t);
+ else
+ remote_sram_size = 0;
+ }
+
+ }
+#ifdef HAVE_THREADS
+ autosave_unlock();
+#endif
+
+ /* We're ready! */
+ *had_input = true;
+ netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
+ connection->mode = NETPLAY_CONNECTION_PLAYING;
+ netplay_handshake_ready(netplay, connection);
+ netplay_recv_flush(&connection->recv_packet_buffer);
+
+ /* Ask to switch to playing mode if we should */
+ {
+ settings_t *settings = config_get_ptr();
+ if (!settings->bools.netplay_start_as_spectator)
+ return netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING);
+ }
+
+ return true;
+}
+
+/**
+ * netplay_handshake
+ *
+ * Data receiver for all handshake states.
+ */
+bool netplay_handshake(netplay_t *netplay,
+ struct netplay_connection *connection, bool *had_input)
+{
+ bool ret = false;
+
+ switch (connection->mode)
+ {
+ case NETPLAY_CONNECTION_INIT:
+ ret = netplay_handshake_init(netplay, connection, had_input);
+ break;
+ case NETPLAY_CONNECTION_PRE_NICK:
+ ret = netplay_handshake_pre_nick(netplay, connection, had_input);
+ break;
+ case NETPLAY_CONNECTION_PRE_PASSWORD:
+ ret = netplay_handshake_pre_password(netplay, connection, had_input);
+ break;
+ case NETPLAY_CONNECTION_PRE_INFO:
+ ret = netplay_handshake_pre_info(netplay, connection, had_input);
+ break;
+ case NETPLAY_CONNECTION_PRE_SYNC:
+ ret = netplay_handshake_pre_sync(netplay, connection, had_input);
+ break;
+ case NETPLAY_CONNECTION_NONE:
+ default:
+ return false;
+ }
+
+ if (connection->mode >= NETPLAY_CONNECTION_CONNECTED &&
+ !netplay_send_cur_input(netplay, connection))
+ return false;
+
+ return ret;
+}
+
/* The mapping of keys from libretro (host) to netplay (network) */
uint32_t netplay_key_hton(unsigned key)
{
@@ -4282,3 +5964,725 @@ void netplay_free(netplay_t *netplay)
free(netplay);
}
+
+/**
+ * netplay_send_savestate
+ * @netplay : pointer to netplay object
+ * @serial_info : the savestate being loaded
+ * @cx : compression type
+ * @z : compression backend to use
+ *
+ * Send a loaded savestate to those connected peers using the given compression
+ * scheme.
+ */
+static void netplay_send_savestate(netplay_t *netplay,
+ retro_ctx_serialize_info_t *serial_info, uint32_t cx,
+ struct compression_transcoder *z)
+{
+ uint32_t header[4];
+ uint32_t rd, wn;
+ size_t i;
+
+ /* Compress it */
+ z->compression_backend->set_in(z->compression_stream,
+ (const uint8_t*)serial_info->data_const, (uint32_t)serial_info->size);
+ z->compression_backend->set_out(z->compression_stream,
+ netplay->zbuffer, (uint32_t)netplay->zbuffer_size);
+ if (!z->compression_backend->trans(z->compression_stream, true, &rd,
+ &wn, NULL))
+ {
+ /* Catastrophe! */
+ for (i = 0; i < netplay->connections_size; i++)
+ netplay_hangup(netplay, &netplay->connections[i]);
+ return;
+ }
+
+ /* Send it to relevant peers */
+ header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE);
+ header[1] = htonl(wn + 2*sizeof(uint32_t));
+ header[2] = htonl(netplay->run_frame_count);
+ header[3] = htonl(serial_info->size);
+
+ for (i = 0; i < netplay->connections_size; i++)
+ {
+ struct netplay_connection *connection = &netplay->connections[i];
+ if (!connection->active ||
+ connection->mode < NETPLAY_CONNECTION_CONNECTED ||
+ connection->compression_supported != cx) continue;
+
+ if (!netplay_send(&connection->send_packet_buffer, connection->fd, header,
+ sizeof(header)) ||
+ !netplay_send(&connection->send_packet_buffer, connection->fd,
+ netplay->zbuffer, wn))
+ netplay_hangup(netplay, connection);
+ }
+}
+
+void netplay_frontend_paused(netplay_t *netplay, bool paused)
+{
+ size_t i;
+ uint32_t paused_ct = 0;
+
+ netplay->local_paused = paused;
+
+ /* Communicating this is a bit odd: If exactly one other connection is
+ * paused, then we must tell them that we're unpaused, as from their
+ * perspective we are. If more than one other connection is paused, then our
+ * status as proxy means we are NOT unpaused to either of them. */
+ for (i = 0; i < netplay->connections_size; i++)
+ {
+ struct netplay_connection *connection = &netplay->connections[i];
+ if (connection->active && connection->paused)
+ paused_ct++;
+ }
+
+ if (paused_ct > 1)
+ return;
+
+ /* Send our unpaused status. Must send manually because we must immediately
+ * flush the buffer: If we're paused, we won't be polled. */
+ for (i = 0; i < netplay->connections_size; i++)
+ {
+ struct netplay_connection *connection = &netplay->connections[i];
+ if ( connection->active
+ && connection->mode >= NETPLAY_CONNECTION_CONNECTED)
+ {
+ if (paused)
+ netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_PAUSE,
+ netplay->nick, NETPLAY_NICK_LEN);
+ else
+ netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_RESUME,
+ NULL, 0);
+
+ /* We're not going to be polled, so we need to
+ * flush this command now */
+ netplay_send_flush(&connection->send_packet_buffer,
+ connection->fd, true);
+ }
+ }
+}
+
+/**
+ * netplay_force_future
+ * @netplay : pointer to netplay object
+ *
+ * Force netplay to ignore all past input, typically because we've just loaded
+ * a state or reset.
+ */
+static void netplay_force_future(netplay_t *netplay)
+{
+ /* Wherever we're inputting, that's where we consider our state to be loaded */
+ netplay->run_ptr = netplay->self_ptr;
+ netplay->run_frame_count = netplay->self_frame_count;
+
+ /* We need to ignore any intervening data from the other side,
+ * and never rewind past this */
+ netplay_update_unread_ptr(netplay);
+
+ if (netplay->unread_frame_count < netplay->run_frame_count)
+ {
+ uint32_t client;
+ for (client = 0; client < MAX_CLIENTS; client++)
+ {
+ if (!(netplay->connected_players & (1 << client)))
+ continue;
+
+ if (netplay->read_frame_count[client] < netplay->run_frame_count)
+ {
+ netplay->read_ptr[client] = netplay->run_ptr;
+ netplay->read_frame_count[client] = netplay->run_frame_count;
+ }
+ }
+ if (netplay->server_frame_count < netplay->run_frame_count)
+ {
+ netplay->server_ptr = netplay->run_ptr;
+ netplay->server_frame_count = netplay->run_frame_count;
+ }
+ netplay_update_unread_ptr(netplay);
+ }
+ if (netplay->other_frame_count < netplay->run_frame_count)
+ {
+ netplay->other_ptr = netplay->run_ptr;
+ netplay->other_frame_count = netplay->run_frame_count;
+ }
+}
+
+
+void netplay_core_reset(netplay_t *netplay)
+{
+ size_t i;
+ uint32_t cmd[3];
+
+ /* Ignore past input */
+ netplay_force_future(netplay);
+
+ /* Request that our peers reset */
+ cmd[0] = htonl(NETPLAY_CMD_RESET);
+ cmd[1] = htonl(sizeof(uint32_t));
+ cmd[2] = htonl(netplay->self_frame_count);
+
+ for (i = 0; i < netplay->connections_size; i++)
+ {
+ struct netplay_connection *connection = &netplay->connections[i];
+ if (!connection->active ||
+ connection->mode < NETPLAY_CONNECTION_CONNECTED) continue;
+
+ if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd,
+ sizeof(cmd)))
+ netplay_hangup(netplay, connection);
+ }
+}
+
+void netplay_load_savestate(netplay_t *netplay,
+ retro_ctx_serialize_info_t *serial_info, bool save)
+{
+ retro_ctx_serialize_info_t tmp_serial_info;
+
+ netplay_force_future(netplay);
+
+ /* Record it in our own buffer */
+ if (save || !serial_info)
+ {
+ /* TODO/FIXME: This is a critical failure! */
+ if (!netplay_delta_frame_ready(netplay,
+ &netplay->buffer[netplay->run_ptr], netplay->run_frame_count))
+ return;
+
+ if (!serial_info)
+ {
+ tmp_serial_info.size = netplay->state_size;
+ tmp_serial_info.data = netplay->buffer[netplay->run_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->run_ptr].state,
+ serial_info->data_const, serial_info->size);
+ }
+ }
+
+ /* Don't send it if we're expected to be desynced */
+ if (netplay->desync)
+ return;
+
+ /* If we can't send it to the peer, loading a state was a bad idea */
+ if (netplay->quirks & (
+ NETPLAY_QUIRK_NO_SAVESTATES
+ | NETPLAY_QUIRK_NO_TRANSMISSION))
+ return;
+
+ /* Send this to every peer */
+ if (netplay->compress_nil.compression_backend)
+ netplay_send_savestate(netplay, serial_info, 0, &netplay->compress_nil);
+ if (netplay->compress_zlib.compression_backend)
+ netplay_send_savestate(netplay, serial_info, NETPLAY_COMPRESSION_ZLIB,
+ &netplay->compress_zlib);
+}
+
+void netplay_toggle_play_spectate(netplay_t *netplay)
+{
+ switch (netplay->self_mode)
+ {
+ case NETPLAY_CONNECTION_PLAYING:
+ case NETPLAY_CONNECTION_SLAVE:
+ /* Switch to spectator mode immediately */
+ netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
+ netplay_cmd_mode(netplay, NETPLAY_CONNECTION_SPECTATING);
+ break;
+ case NETPLAY_CONNECTION_SPECTATING:
+ /* Switch only after getting permission */
+ netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING);
+ break;
+ default:
+ break;
+ }
+}
+
+int16_t netplay_input_state(netplay_t *netplay,
+ unsigned port, unsigned device,
+ unsigned idx, unsigned id)
+{
+ struct delta_frame *delta;
+ netplay_input_state_t istate;
+ const uint32_t *curr_input_state = NULL;
+ size_t ptr =
+ netplay->is_replay
+ ? netplay->replay_ptr
+ : netplay->run_ptr;
+
+ if (port >= MAX_INPUT_DEVICES)
+ return 0;
+
+ /* If the port doesn't seem to correspond to the device, "correct" it. This
+ * is common with devices that typically only have one instance, such as
+ * keyboards, mice and lightguns. */
+ if (device != RETRO_DEVICE_JOYPAD &&
+ (netplay->config_devices[port]&RETRO_DEVICE_MASK) != device)
+ {
+ for (port = 0; port < MAX_INPUT_DEVICES; port++)
+ {
+ if ((netplay->config_devices[port]&RETRO_DEVICE_MASK) == device)
+ break;
+ }
+ if (port == MAX_INPUT_DEVICES)
+ return 0;
+ }
+
+ delta = &netplay->buffer[ptr];
+ istate = delta->resolved_input[port];
+ if (!istate || !istate->used || istate->size == 0)
+ return 0;
+
+ curr_input_state = istate->data;
+
+ switch (device)
+ {
+ case RETRO_DEVICE_JOYPAD:
+ if (id == RETRO_DEVICE_ID_JOYPAD_MASK)
+ return curr_input_state[0];
+ return ((1 << id) & curr_input_state[0]) ? 1 : 0;
+
+ case RETRO_DEVICE_ANALOG:
+ if (istate->size == 3)
+ {
+ uint32_t state = curr_input_state[1 + idx];
+ return (int16_t)(uint16_t)(state >> (id * 16));
+ }
+ break;
+ case RETRO_DEVICE_MOUSE:
+ case RETRO_DEVICE_LIGHTGUN:
+ if (istate->size == 2)
+ {
+ if (id <= RETRO_DEVICE_ID_MOUSE_Y)
+ return (int16_t)(uint16_t)(curr_input_state[1] >> (id * 16));
+ return ((1 << id) & curr_input_state[0]) ? 1 : 0;
+ }
+ break;
+ case RETRO_DEVICE_KEYBOARD:
+ {
+ unsigned key = netplay_key_hton(id);
+ if (key != NETPLAY_KEY_UNKNOWN)
+ {
+ unsigned word = key / 32;
+ unsigned bit = key % 32;
+ if (word <= istate->size)
+ return ((UINT32_C(1) << bit) & curr_input_state[word]) ? 1 : 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * get_self_input_state:
+ * @netplay : pointer to netplay object
+ *
+ * Grab our own input state and send this frame's input state (self and remote)
+ * over the network
+ *
+ * Returns: true (1) if successful, otherwise false (0).
+ */
+static bool get_self_input_state(
+ bool block_libretro_input,
+ netplay_t *netplay)
+{
+ unsigned i;
+ struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr];
+ netplay_input_state_t istate = NULL;
+ uint32_t devices, used_devices = 0, devi, dev_type, local_device;
+
+ if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count))
+ return false;
+
+ /* We've already read this frame! */
+ if (ptr->have_local)
+ return true;
+
+ devices = netplay->self_devices;
+ used_devices = 0;
+
+ for (devi = 0; devi < MAX_INPUT_DEVICES; devi++)
+ {
+ if (!(devices & (1 << devi)))
+ continue;
+
+ /* Find an appropriate local device */
+ dev_type = netplay->config_devices[devi]&RETRO_DEVICE_MASK;
+
+ for (local_device = 0; local_device < MAX_INPUT_DEVICES; local_device++)
+ {
+ if (used_devices & (1 << local_device))
+ continue;
+ if ((netplay->config_devices[local_device]&RETRO_DEVICE_MASK) == dev_type)
+ break;
+ }
+
+ if (local_device == MAX_INPUT_DEVICES)
+ local_device = 0;
+ used_devices |= (1 << local_device);
+
+ istate = netplay_input_state_for(&ptr->real_input[devi],
+ /* If we're a slave, we write our own input to MAX_CLIENTS to keep it separate */
+ (netplay->self_mode==NETPLAY_CONNECTION_SLAVE)?MAX_CLIENTS:netplay->self_client_num,
+ netplay_expected_input_size(netplay, 1 << devi),
+ true, false);
+ if (!istate)
+ continue; /* FIXME: More severe? */
+
+ /* First frame we always give zero input since relying on
+ * input from first frame screws up when we use -F 0. */
+ if ( !block_libretro_input
+ && netplay->self_frame_count > 0)
+ {
+ uint32_t *state = istate->data;
+ retro_input_state_t cb = netplay->cbs.state_cb;
+ unsigned dtype = netplay->config_devices[devi]&RETRO_DEVICE_MASK;
+
+ switch (dtype)
+ {
+ case RETRO_DEVICE_ANALOG:
+ for (i = 0; i < 2; i++)
+ {
+ int16_t tmp_x = cb(local_device,
+ RETRO_DEVICE_ANALOG, (unsigned)i, 0);
+ int16_t tmp_y = cb(local_device,
+ RETRO_DEVICE_ANALOG, (unsigned)i, 1);
+ state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16);
+ }
+ /* no break */
+
+ case RETRO_DEVICE_JOYPAD:
+ for (i = 0; i <= RETRO_DEVICE_ID_JOYPAD_R3; i++)
+ {
+ int16_t tmp = cb(local_device,
+ RETRO_DEVICE_JOYPAD, 0, (unsigned)i);
+ state[0] |= tmp ? 1 << i : 0;
+ }
+ break;
+
+ case RETRO_DEVICE_MOUSE:
+ case RETRO_DEVICE_LIGHTGUN:
+ {
+ int16_t tmp_x = cb(local_device, dtype, 0, 0);
+ int16_t tmp_y = cb(local_device, dtype, 0, 1);
+ state[1] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16);
+ for (i = 2;
+ i <= (unsigned)((dtype == RETRO_DEVICE_MOUSE) ?
+ RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN :
+ RETRO_DEVICE_ID_LIGHTGUN_START);
+ i++)
+ {
+ int16_t tmp = cb(local_device, dtype, 0,
+ (unsigned) i);
+ state[0] |= tmp ? 1 << i : 0;
+ }
+ break;
+ }
+
+ case RETRO_DEVICE_KEYBOARD:
+ {
+ unsigned key, word = 0, bit = 1;
+ for (key = 1; key < NETPLAY_KEY_LAST; key++)
+ {
+ state[word] |=
+ cb(local_device, RETRO_DEVICE_KEYBOARD, 0,
+ NETPLAY_KEY_NTOH(key)) ?
+ (UINT32_C(1) << bit) : 0;
+ bit++;
+ if (bit >= 32)
+ {
+ bit = 0;
+ word++;
+ if (word >= istate->size)
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ ptr->have_local = true;
+ if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
+ {
+ ptr->have_real[netplay->self_client_num] = true;
+ netplay->read_ptr[netplay->self_client_num] = NEXT_PTR(netplay->self_ptr);
+ netplay->read_frame_count[netplay->self_client_num] = netplay->self_frame_count + 1;
+ }
+
+ /* And send this input to our peers */
+ for (i = 0; i < netplay->connections_size; i++)
+ {
+ struct netplay_connection *connection = &netplay->connections[i];
+ if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED)
+ netplay_send_cur_input(netplay, &netplay->connections[i]);
+ }
+
+ /* Handle any delayed state changes */
+ if (netplay->is_server)
+ netplay_delayed_state_change(netplay);
+
+ return true;
+}
+
+
+bool netplay_poll(
+ bool block_libretro_input,
+ void *settings_data,
+ netplay_t *netplay)
+{
+ size_t i;
+ int res;
+ uint32_t client;
+ settings_t *settings = (settings_t*)settings_data;
+
+ if (!get_self_input_state(block_libretro_input, netplay))
+ goto catastrophe;
+
+ /* If we're not connected, we're done */
+ if (netplay->self_mode == NETPLAY_CONNECTION_NONE)
+ return true;
+
+ /* Read Netplay input, block if we're configured to stall for input every
+ * frame */
+ netplay_update_unread_ptr(netplay);
+ if (netplay->stateless_mode &&
+ (netplay->connected_players>1) &&
+ netplay->unread_frame_count <= netplay->run_frame_count)
+ res = netplay_poll_net_input(netplay, true);
+ else
+ res = netplay_poll_net_input(netplay, false);
+ if (res == -1)
+ goto catastrophe;
+
+ /* Resolve and/or simulate the input if we don't have real input */
+ netplay_resolve_input(netplay, netplay->run_ptr, false);
+
+ /* Handle any slaves */
+ if (netplay->is_server && netplay->connected_slaves)
+ netplay_handle_slaves(netplay);
+
+ netplay_update_unread_ptr(netplay);
+
+ /* Figure out how many frames of input latency we should be using to hide
+ * network latency */
+ if (netplay->frame_run_time_avg || netplay->stateless_mode)
+ {
+ /* FIXME: Using fixed 60fps for this calculation */
+ unsigned frames_per_frame = netplay->frame_run_time_avg ?
+ (16666 / netplay->frame_run_time_avg) :
+ 0;
+ unsigned frames_ahead = (netplay->run_frame_count > netplay->unread_frame_count) ?
+ (netplay->run_frame_count - netplay->unread_frame_count) :
+ 0;
+ int input_latency_frames_min = settings->uints.netplay_input_latency_frames_min -
+ (settings->bools.run_ahead_enabled ? settings->uints.run_ahead_frames : 0);
+ int input_latency_frames_max = input_latency_frames_min + settings->uints.netplay_input_latency_frames_range;
+
+ /* Assume we need a couple frames worth of time to actually run the
+ * current frame */
+ if (frames_per_frame > 2)
+ frames_per_frame -= 2;
+ else
+ frames_per_frame = 0;
+
+ /* Shall we adjust our latency? */
+ if (netplay->stateless_mode)
+ {
+ /* In stateless mode, we adjust up if we're "close" and down if we
+ * have a lot of slack */
+ if (netplay->input_latency_frames < input_latency_frames_min ||
+ (netplay->unread_frame_count == netplay->run_frame_count + 1 &&
+ netplay->input_latency_frames < input_latency_frames_max))
+ netplay->input_latency_frames++;
+ else if (netplay->input_latency_frames > input_latency_frames_max ||
+ (netplay->unread_frame_count > netplay->run_frame_count + 2 &&
+ netplay->input_latency_frames > input_latency_frames_min))
+ netplay->input_latency_frames--;
+ }
+ else if (netplay->input_latency_frames < input_latency_frames_min ||
+ (frames_per_frame < frames_ahead &&
+ netplay->input_latency_frames < input_latency_frames_max))
+ {
+ /* We can't hide this much network latency with replay, so hide some
+ * with input latency */
+ netplay->input_latency_frames++;
+ }
+ else if (netplay->input_latency_frames > input_latency_frames_max ||
+ (frames_per_frame > frames_ahead + 2 &&
+ netplay->input_latency_frames > input_latency_frames_min))
+ {
+ /* We don't need this much latency (any more) */
+ netplay->input_latency_frames--;
+ }
+ }
+
+ /* If we're stalled, consider unstalling */
+ switch (netplay->stall)
+ {
+ case NETPLAY_STALL_RUNNING_FAST:
+ if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2
+ > netplay->self_frame_count)
+ {
+ netplay->stall = NETPLAY_STALL_NONE;
+ for (i = 0; i < netplay->connections_size; i++)
+ {
+ struct netplay_connection *connection = &netplay->connections[i];
+ if (connection->active && connection->stall)
+ connection->stall = NETPLAY_STALL_NONE;
+ }
+ }
+ break;
+
+ case NETPLAY_STALL_SPECTATOR_WAIT:
+ if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || netplay->unread_frame_count > netplay->self_frame_count)
+ netplay->stall = NETPLAY_STALL_NONE;
+ break;
+
+ case NETPLAY_STALL_INPUT_LATENCY:
+ /* Just let it recalculate momentarily */
+ netplay->stall = NETPLAY_STALL_NONE;
+ break;
+
+ case NETPLAY_STALL_SERVER_REQUESTED:
+ /* See if the stall is done */
+ if (netplay->connections[0].stall_frame == 0)
+ {
+ /* Stop stalling! */
+ netplay->connections[0].stall = NETPLAY_STALL_NONE;
+ netplay->stall = NETPLAY_STALL_NONE;
+ }
+ else
+ netplay->connections[0].stall_frame--;
+ break;
+ case NETPLAY_STALL_NO_CONNECTION:
+ /* We certainly haven't fixed this */
+ break;
+ default: /* not stalling */
+ break;
+ }
+
+ /* If we're not stalled, consider stalling */
+ if (!netplay->stall)
+ {
+ /* Have we not read enough latency frames? */
+ if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING &&
+ netplay->connected_players &&
+ netplay->run_frame_count + netplay->input_latency_frames > netplay->self_frame_count)
+ {
+ netplay->stall = NETPLAY_STALL_INPUT_LATENCY;
+ netplay->stall_time = 0;
+ }
+
+ /* Are we too far ahead? */
+ if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES
+ <= netplay->self_frame_count)
+ {
+ netplay->stall = NETPLAY_STALL_RUNNING_FAST;
+ netplay->stall_time = cpu_features_get_time_usec();
+
+ /* Figure out who to blame */
+ if (netplay->is_server)
+ {
+ for (client = 1; client < MAX_CLIENTS; client++)
+ {
+ struct netplay_connection *connection;
+ if (!(netplay->connected_players & (1 << client)))
+ continue;
+ if (netplay->read_frame_count[client] > netplay->unread_frame_count)
+ continue;
+ connection = &netplay->connections[client-1];
+ if (connection->active &&
+ connection->mode == NETPLAY_CONNECTION_PLAYING)
+ {
+ connection->stall = NETPLAY_STALL_RUNNING_FAST;
+ connection->stall_time = netplay->stall_time;
+ }
+ }
+ }
+
+ }
+
+ /* If we're a spectator, are we ahead at all? */
+ if (!netplay->is_server &&
+ (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING ||
+ netplay->self_mode == NETPLAY_CONNECTION_SLAVE) &&
+ netplay->unread_frame_count <= netplay->self_frame_count)
+ {
+ netplay->stall = NETPLAY_STALL_SPECTATOR_WAIT;
+ netplay->stall_time = cpu_features_get_time_usec();
+ }
+ }
+
+ /* If we're stalling, consider disconnection */
+ if (netplay->stall && netplay->stall_time)
+ {
+ retro_time_t now = cpu_features_get_time_usec();
+
+ /* Don't stall out while they're paused */
+ if (netplay->remote_paused)
+ netplay->stall_time = now;
+ else if (now - netplay->stall_time >=
+ (netplay->is_server ? MAX_SERVER_STALL_TIME_USEC :
+ MAX_CLIENT_STALL_TIME_USEC))
+ {
+ /* Stalled out! */
+ if (netplay->is_server)
+ {
+ bool fixed = false;
+ for (i = 0; i < netplay->connections_size; i++)
+ {
+ struct netplay_connection *connection = &netplay->connections[i];
+ if (connection->active &&
+ connection->mode == NETPLAY_CONNECTION_PLAYING &&
+ connection->stall)
+ {
+ netplay_hangup(netplay, connection);
+ fixed = true;
+ }
+ }
+
+ if (fixed)
+ {
+ /* Not stalled now :) */
+ netplay->stall = NETPLAY_STALL_NONE;
+ return true;
+ }
+ }
+ else
+ goto catastrophe;
+ return false;
+ }
+ }
+
+ return true;
+
+catastrophe:
+ for (i = 0; i < netplay->connections_size; i++)
+ netplay_hangup(netplay, &netplay->connections[i]);
+ return false;
+}
+
+bool netplay_is_alive(netplay_t *netplay)
+{
+ return (netplay->is_server) ||
+ (!netplay->is_server &&
+ netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED);
+}
+
+bool netplay_should_skip(netplay_t *netplay)
+{
+ if (!netplay)
+ return false;
+ return netplay->is_replay
+ && (netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED);
+}
diff --git a/network/netplay/netplay_handshake.c b/network/netplay/netplay_handshake.c
deleted file mode 100644
index 312040c648..0000000000
--- a/network/netplay/netplay_handshake.c
+++ /dev/null
@@ -1,1176 +0,0 @@
-/* RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2017 - Daniel De Matteis
- * Copyright (C) 2016-2017 - 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-
- * 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
-
-#include "netplay_private.h"
-
-#ifdef HAVE_CONFIG_H
-#include "../../config.h"
-#endif
-
-#include "../../autosave.h"
-#include "../../configuration.h"
-#include "../../content.h"
-#include "../../retroarch.h"
-#include "../../version.h"
-#include "../../menu/menu_input.h"
-
-struct nick_buf_s
-{
- uint32_t cmd[2];
- char nick[NETPLAY_NICK_LEN];
-};
-
-struct password_buf_s
-{
- uint32_t cmd[2];
- char password[NETPLAY_PASS_HASH_LEN];
-};
-
-struct info_buf_s
-{
- uint32_t cmd[2];
- uint32_t content_crc;
- char core_name[NETPLAY_NICK_LEN];
- char core_version[NETPLAY_NICK_LEN];
-};
-
-#define RECV(buf, sz) \
- recvd = netplay_recv(&connection->recv_packet_buffer, connection->fd, (buf), (sz), false); \
- if (recvd >= 0 && recvd < (ssize_t) (sz)) \
- { \
- netplay_recv_reset(&connection->recv_packet_buffer); \
- return true; \
- } \
- else if (recvd < 0)
-
-#define NETPLAY_MAGIC 0x52414E50 /* RANP */
-
-/* TODO/FIXME - static global variables */
-static netplay_t *handshake_password_netplay = NULL;
-static unsigned long simple_rand_next = 1;
-
-/* TODO/FIXME - replace netplay_log_connection with calls
- * to inet_ntop_compat and move runloop message queue pushing
- * outside */
-#if !defined(HAVE_SOCKET_LEGACY) && !defined(WIIU) && !defined(_3DS)
-/* Custom inet_ntop. Win32 doesn't seem to support this ... */
-static void netplay_log_connection(
- const struct sockaddr_storage *their_addr,
- unsigned slot, const char *nick, char *s, size_t len)
-{
- union
- {
- const struct sockaddr_storage *storage;
- const struct sockaddr_in *v4;
- const struct sockaddr_in6 *v6;
- } u;
- const char *str = NULL;
- char buf_v4[INET_ADDRSTRLEN] = {0};
- char buf_v6[INET6_ADDRSTRLEN] = {0};
-
- u.storage = their_addr;
-
- switch (their_addr->ss_family)
- {
- case AF_INET:
- {
- struct sockaddr_in in;
-
- memset(&in, 0, sizeof(in));
-
- str = buf_v4;
- in.sin_family = AF_INET;
- memcpy(&in.sin_addr, &u.v4->sin_addr, sizeof(struct in_addr));
-
- getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in),
- buf_v4, sizeof(buf_v4),
- NULL, 0, NI_NUMERICHOST);
- }
- break;
- case AF_INET6:
- {
- struct sockaddr_in6 in;
- memset(&in, 0, sizeof(in));
-
- str = buf_v6;
- in.sin6_family = AF_INET6;
- memcpy(&in.sin6_addr, &u.v6->sin6_addr, sizeof(struct in6_addr));
-
- getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in6),
- buf_v6, sizeof(buf_v6), NULL, 0, NI_NUMERICHOST);
- }
- break;
- default:
- break;
- }
-
- if (str)
- snprintf(s, len, msg_hash_to_str(MSG_GOT_CONNECTION_FROM_NAME),
- nick, str);
- else
- snprintf(s, len, msg_hash_to_str(MSG_GOT_CONNECTION_FROM),
- nick);
-}
-#else
-static void netplay_log_connection(
- const struct sockaddr_storage *their_addr,
- unsigned slot, const char *nick, char *s, size_t len)
-{
- /* Stub code - will need to be implemented */
- snprintf(s, len, msg_hash_to_str(MSG_GOT_CONNECTION_FROM),
- nick);
-}
-#endif
-
-/**
- * netplay_impl_magic:
- *
- * A pseudo-hash of the RetroArch and Netplay version, so only compatible
- * versions play together.
- */
-static uint32_t netplay_impl_magic(void)
-{
- size_t i;
- uint32_t res = 0;
- const char *ver = PACKAGE_VERSION;
- size_t len = strlen(ver);
-
- for (i = 0; i < len; i++)
- res ^= ver[i] << (i & 0xf);
-
- res ^= NETPLAY_PROTOCOL_VERSION << (i & 0xf);
-
- return res;
-}
-
-/**
- * netplay_platform_magic
- *
- * Just enough info to tell us if our platforms mismatch: Endianness and a
- * couple of type sizes.
- *
- * Format:
- * bit 31: Reserved
- * bit 30: 1 for big endian
- * bits 29-15: sizeof(size_t)
- * bits 14-0: sizeof(long)
- */
-static uint32_t netplay_platform_magic(void)
-{
- uint32_t ret =
- ((1 == htonl(1)) << 30)
- |(sizeof(size_t) << 15)
- |(sizeof(long));
- return ret;
-}
-
-/**
- * netplay_endian_mismatch
- *
- * Do the platform magics mismatch on endianness?
- */
-static bool netplay_endian_mismatch(uint32_t pma, uint32_t pmb)
-{
- uint32_t ebit = (1<<30);
- return (pma & ebit) != (pmb & ebit);
-}
-
-static int simple_rand(void)
-{
- simple_rand_next = simple_rand_next * 1103515245 + 12345;
- return((unsigned)(simple_rand_next / 65536) % 32768);
-}
-
-static void simple_srand(unsigned int seed)
-{
- simple_rand_next = seed;
-}
-
-static uint32_t simple_rand_uint32(void)
-{
- uint32_t parts[3];
- parts[0] = simple_rand();
- parts[1] = simple_rand();
- parts[2] = simple_rand();
- return ((parts[0] << 30) +
- (parts[1] << 15) +
- parts[2]);
-}
-
-/**
- * netplay_handshake_init_send
- *
- * Initialize our handshake and send the first part of the handshake protocol.
- */
-bool netplay_handshake_init_send(netplay_t *netplay,
- struct netplay_connection *connection)
-{
- uint32_t header[6];
- unsigned conn_salt = 0;
- settings_t *settings = config_get_ptr();
-
- header[0] = htonl(NETPLAY_MAGIC);
- header[1] = htonl(netplay_platform_magic());
- header[2] = htonl(NETPLAY_COMPRESSION_SUPPORTED);
- header[3] = 0;
- header[4] = htonl(NETPLAY_PROTOCOL_VERSION);
- header[5] = htonl(netplay_impl_magic());
-
- if (netplay->is_server &&
- (settings->paths.netplay_password[0] ||
- settings->paths.netplay_spectate_password[0]))
- {
- /* Demand a password */
- if (simple_rand_next == 1)
- simple_srand((unsigned int) time(NULL));
- connection->salt = simple_rand_uint32();
- if (connection->salt == 0)
- connection->salt = 1;
- conn_salt = connection->salt;
- }
-
- header[3] = htonl(conn_salt);
-
- if (!netplay_send(&connection->send_packet_buffer, connection->fd, header,
- sizeof(header)) ||
- !netplay_send_flush(&connection->send_packet_buffer, connection->fd, false))
- return false;
-
- return true;
-}
-
-#ifdef HAVE_MENU
-static void handshake_password(void *ignore, const char *line)
-{
- struct password_buf_s password_buf;
- char password[8+NETPLAY_PASS_LEN]; /* 8 for salt, 128 for password */
- char hash[NETPLAY_PASS_HASH_LEN+1]; /* + NULL terminator */
- netplay_t *netplay = handshake_password_netplay;
- struct netplay_connection *connection = &netplay->connections[0];
-
- snprintf(password, sizeof(password), "%08X", connection->salt);
- if (!string_is_empty(line))
- strlcpy(password + 8, line, sizeof(password)-8);
-
- password_buf.cmd[0] = htonl(NETPLAY_CMD_PASSWORD);
- password_buf.cmd[1] = htonl(sizeof(password_buf.password));
- sha256_hash(hash, (uint8_t *) password, strlen(password));
- memcpy(password_buf.password, hash, NETPLAY_PASS_HASH_LEN);
-
- /* We have no way to handle an error here, so we'll let the next function error out */
- if (netplay_send(&connection->send_packet_buffer, connection->fd, &password_buf, sizeof(password_buf)))
- netplay_send_flush(&connection->send_packet_buffer, connection->fd, false);
-
-#ifdef HAVE_MENU
- menu_input_dialog_end();
- retroarch_menu_running_finished(false);
-#endif
-}
-#endif
-
-/**
- * netplay_handshake_init
- *
- * Data receiver for the initial part of the handshake, i.e., waiting for the
- * netplay header.
- */
-bool netplay_handshake_init(netplay_t *netplay,
- struct netplay_connection *connection, bool *had_input)
-{
- ssize_t recvd;
- struct nick_buf_s nick_buf;
- uint32_t header[6];
- uint32_t local_pmagic = 0;
- uint32_t remote_pmagic = 0;
- uint32_t remote_version = 0;
- uint32_t compression = 0;
- struct compression_transcoder *ctrans = NULL;
- const char *dmsg = NULL;
-
- memset(header, 0, sizeof(header));
-
- RECV(header, sizeof(uint32_t))
- {
- dmsg = msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT);
- goto error;
- }
-
- if (ntohl(header[0]) != NETPLAY_MAGIC)
- {
- dmsg = msg_hash_to_str(MSG_NETPLAY_NOT_RETROARCH);
- goto error;
- }
-
- RECV(header + 1, sizeof(header) - sizeof(uint32_t))
- {
- dmsg = msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT);
- goto error;
- }
-
- remote_version = ntohl(header[4]);
- if (remote_version < NETPLAY_PROTOCOL_VERSION)
- {
- dmsg = msg_hash_to_str(MSG_NETPLAY_OUT_OF_DATE);
- goto error;
- }
-
- if (ntohl(header[5]) != netplay_impl_magic())
- {
- /* We allow the connection but warn that this could cause issues. */
- dmsg = msg_hash_to_str(MSG_NETPLAY_DIFFERENT_VERSIONS);
- RARCH_WARN("%s\n", dmsg);
- runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
-
- /* We only care about platform magic if our core is quirky */
- local_pmagic = netplay_platform_magic();
- remote_pmagic = ntohl(header[1]);
-
- if ((netplay->quirks & NETPLAY_QUIRK_ENDIAN_DEPENDENT) &&
- netplay_endian_mismatch(local_pmagic, remote_pmagic))
- {
- RARCH_ERR("Endianness mismatch with an endian-sensitive core.\n");
- dmsg = msg_hash_to_str(MSG_NETPLAY_ENDIAN_DEPENDENT);
- goto error;
- }
- if ((netplay->quirks & NETPLAY_QUIRK_PLATFORM_DEPENDENT) &&
- (local_pmagic != remote_pmagic))
- {
- RARCH_ERR("Platform mismatch with a platform-sensitive core.\n");
- dmsg = msg_hash_to_str(MSG_NETPLAY_PLATFORM_DEPENDENT);
- goto error;
- }
-
- /* Check what compression is supported */
- compression = ntohl(header[2]);
- compression &= NETPLAY_COMPRESSION_SUPPORTED;
-
- if (compression & NETPLAY_COMPRESSION_ZLIB)
- {
- ctrans = &netplay->compress_zlib;
- if (!ctrans->compression_backend)
- {
- ctrans->compression_backend =
- trans_stream_get_zlib_deflate_backend();
- if (!ctrans->compression_backend)
- ctrans->compression_backend = trans_stream_get_pipe_backend();
- }
- connection->compression_supported = NETPLAY_COMPRESSION_ZLIB;
- }
- else
- {
- ctrans = &netplay->compress_nil;
- if (!ctrans->compression_backend)
- {
- ctrans->compression_backend =
- trans_stream_get_pipe_backend();
- }
- connection->compression_supported = 0;
- }
-
- if (!ctrans->decompression_backend)
- ctrans->decompression_backend = ctrans->compression_backend->reverse;
-
- /* Allocate our compression stream */
- if (!ctrans->compression_stream)
- {
- ctrans->compression_stream = ctrans->compression_backend->stream_new();
- ctrans->decompression_stream = ctrans->decompression_backend->stream_new();
- }
- if (!ctrans->compression_stream || !ctrans->decompression_stream)
- {
- RARCH_ERR("Failed to allocate compression transcoder!\n");
- return false;
- }
-
- /* If a password is demanded, ask for it */
- if (!netplay->is_server && (connection->salt = ntohl(header[3])))
- {
-#ifdef HAVE_MENU
- menu_input_ctx_line_t line;
- retroarch_menu_running();
-#endif
-
- handshake_password_netplay = netplay;
-
-#ifdef HAVE_MENU
- memset(&line, 0, sizeof(line));
- line.label = msg_hash_to_str(MSG_NETPLAY_ENTER_PASSWORD);
- line.label_setting = "no_setting";
- line.cb = handshake_password;
- if (!menu_input_dialog_start(&line))
- return false;
-#endif
- }
-
- /* Send our nick */
- nick_buf.cmd[0] = htonl(NETPLAY_CMD_NICK);
- nick_buf.cmd[1] = htonl(sizeof(nick_buf.nick));
- memset(nick_buf.nick, 0, sizeof(nick_buf.nick));
- strlcpy(nick_buf.nick, netplay->nick, sizeof(nick_buf.nick));
- if (!netplay_send(&connection->send_packet_buffer, connection->fd, &nick_buf,
- sizeof(nick_buf)) ||
- !netplay_send_flush(&connection->send_packet_buffer, connection->fd, false))
- return false;
-
- /* Move on to the next mode */
- connection->mode = NETPLAY_CONNECTION_PRE_NICK;
- *had_input = true;
- netplay_recv_flush(&connection->recv_packet_buffer);
- return true;
-
-error:
- if (dmsg)
- {
- RARCH_ERR("%s\n", dmsg);
- runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- return false;
-}
-
-static void netplay_handshake_ready(netplay_t *netplay,
- struct netplay_connection *connection)
-{
- char msg[512];
- msg[0] = '\0';
-
- if (netplay->is_server)
- {
- unsigned slot = (unsigned)(connection - netplay->connections);
-
- netplay_log_connection(&connection->addr,
- slot, connection->nick, msg, sizeof(msg));
-
- RARCH_LOG("%s %u\n", msg_hash_to_str(MSG_CONNECTION_SLOT), slot);
-
- /* Send them the savestate */
- if (!(netplay->quirks &
- (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)))
- netplay->force_send_savestate = true;
- }
- else
- {
- netplay->is_connected = true;
- snprintf(msg, sizeof(msg), "%s: \"%s\"",
- msg_hash_to_str(MSG_CONNECTED_TO),
- connection->nick);
- }
-
- RARCH_LOG("%s\n", msg);
- runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
-
- /* Unstall if we were waiting for this */
- if (netplay->stall == NETPLAY_STALL_NO_CONNECTION)
- netplay->stall = NETPLAY_STALL_NONE;
-}
-
-/**
- * netplay_handshake_info
- *
- * Send an INFO command.
- */
-static bool netplay_handshake_info(netplay_t *netplay,
- struct netplay_connection *connection)
-{
- struct info_buf_s info_buf;
- uint32_t content_crc = 0;
- struct retro_system_info *system = runloop_get_libretro_system_info();
-
- memset(&info_buf, 0, sizeof(info_buf));
- info_buf.cmd[0] = htonl(NETPLAY_CMD_INFO);
- info_buf.cmd[1] = htonl(sizeof(info_buf) - 2*sizeof(uint32_t));
-
- /* Get our core info */
- if (system)
- {
- strlcpy(info_buf.core_name,
- system->library_name, sizeof(info_buf.core_name));
- strlcpy(info_buf.core_version,
- system->library_version, sizeof(info_buf.core_version));
- }
- else
- {
- strlcpy(info_buf.core_name,
- "UNKNOWN", sizeof(info_buf.core_name));
- strlcpy(info_buf.core_version,
- "UNKNOWN", sizeof(info_buf.core_version));
- }
-
- /* Get our content CRC */
- content_crc = content_get_crc();
-
- if (content_crc != 0)
- info_buf.content_crc = htonl(content_crc);
-
- /* Send it off and wait for info back */
- if (!netplay_send(&connection->send_packet_buffer, connection->fd,
- &info_buf, sizeof(info_buf)) ||
- !netplay_send_flush(&connection->send_packet_buffer, connection->fd,
- false))
- return false;
-
- connection->mode = NETPLAY_CONNECTION_PRE_INFO;
- return true;
-}
-
-/**
- * netplay_handshake_sync
- *
- * Send a SYNC command.
- */
-static bool netplay_handshake_sync(netplay_t *netplay,
- struct netplay_connection *connection)
-{
- /* If we're the server, now we send sync info */
- size_t i;
- int matchct;
- uint32_t cmd[4];
- retro_ctx_memory_info_t mem_info;
- uint32_t client_num = 0;
- uint32_t device = 0;
- size_t nicklen, nickmangle = 0;
- bool nick_matched = false;
-
-#ifdef HAVE_THREADS
- autosave_lock();
-#endif
- mem_info.id = RETRO_MEMORY_SAVE_RAM;
- core_get_memory(&mem_info);
-#ifdef HAVE_THREADS
- autosave_unlock();
-#endif
-
- /* Send basic sync info */
- cmd[0] = htonl(NETPLAY_CMD_SYNC);
- cmd[1] = htonl(2*sizeof(uint32_t)
- /* Controller devices */
- + MAX_INPUT_DEVICES*sizeof(uint32_t)
-
- /* Share modes */
- + MAX_INPUT_DEVICES*sizeof(uint8_t)
-
- /* Device-client mapping */
- + MAX_INPUT_DEVICES*sizeof(uint32_t)
-
- /* Client nick */
- + NETPLAY_NICK_LEN
-
- /* And finally, sram */
- + mem_info.size);
- cmd[2] = htonl(netplay->self_frame_count);
- client_num = (uint32_t)(connection - netplay->connections + 1);
-
- if (netplay->local_paused || netplay->remote_paused)
- client_num |= NETPLAY_CMD_SYNC_BIT_PAUSED;
-
- cmd[3] = htonl(client_num);
-
- if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd,
- sizeof(cmd)))
- return false;
-
- /* Now send the device info */
- for (i = 0; i < MAX_INPUT_DEVICES; i++)
- {
- device = htonl(netplay->config_devices[i]);
- if (!netplay_send(&connection->send_packet_buffer, connection->fd,
- &device, sizeof(device)))
- return false;
- }
-
- /* Then the share mode */
- if (!netplay_send(&connection->send_packet_buffer, connection->fd,
- netplay->device_share_modes, sizeof(netplay->device_share_modes)))
- return false;
-
- /* Then the device-client mapping */
- for (i = 0; i < MAX_INPUT_DEVICES; i++)
- {
- device = htonl(netplay->device_clients[i]);
- if (!netplay_send(&connection->send_packet_buffer, connection->fd,
- &device, sizeof(device)))
- return false;
- }
-
- /* Now see if we need to mangle their nick */
- nicklen = strlen(connection->nick);
- if (nicklen > NETPLAY_NICK_LEN - 5)
- nickmangle = NETPLAY_NICK_LEN - 5;
- else
- nickmangle = nicklen;
- matchct = 1;
- do
- {
- nick_matched = false;
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *sc = &netplay->connections[i];
- if (sc == connection)
- continue;
- if (sc->active &&
- sc->mode >= NETPLAY_CONNECTION_CONNECTED &&
- !strncmp(connection->nick, sc->nick, NETPLAY_NICK_LEN))
- {
- nick_matched = true;
- break;
- }
- }
- if (!strncmp(connection->nick, netplay->nick, NETPLAY_NICK_LEN))
- nick_matched = true;
-
- if (nick_matched)
- {
- /* Somebody has this nick, make a new one! */
- snprintf(connection->nick + nickmangle,
- NETPLAY_NICK_LEN - nickmangle, " (%d)", ++matchct);
- connection->nick[NETPLAY_NICK_LEN - 1] = '\0';
- }
- } while (nick_matched);
-
- /* Send the nick */
- if (!netplay_send(&connection->send_packet_buffer, connection->fd,
- connection->nick, NETPLAY_NICK_LEN))
- return false;
-
- /* And finally, the SRAM */
-#ifdef HAVE_THREADS
- autosave_lock();
-#endif
- if (!netplay_send(&connection->send_packet_buffer, connection->fd,
- mem_info.data, mem_info.size) ||
- !netplay_send_flush(&connection->send_packet_buffer, connection->fd,
- false))
- {
-#ifdef HAVE_THREADS
- autosave_unlock();
-#endif
- return false;
- }
-#ifdef HAVE_THREADS
- autosave_unlock();
-#endif
-
- /* Now we're ready! */
- connection->mode = NETPLAY_CONNECTION_SPECTATING;
- netplay_handshake_ready(netplay, connection);
-
- return true;
-}
-
-/**
- * netplay_handshake_pre_nick
- *
- * Data receiver for the second stage of handshake, receiving the other side's
- * nickname.
- */
-static bool netplay_handshake_pre_nick(netplay_t *netplay,
- struct netplay_connection *connection, bool *had_input)
-{
- struct nick_buf_s nick_buf;
- ssize_t recvd;
- char msg[512];
-
- msg[0] = '\0';
-
- RECV(&nick_buf, sizeof(nick_buf)) {}
-
- /* Expecting only a nick command */
- if (recvd < 0 ||
- ntohl(nick_buf.cmd[0]) != NETPLAY_CMD_NICK ||
- ntohl(nick_buf.cmd[1]) != sizeof(nick_buf.nick))
- {
- if (netplay->is_server)
- strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT),
- sizeof(msg));
- else
- strlcpy(msg,
- msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST),
- sizeof(msg));
- RARCH_ERR("%s\n", msg);
- runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return false;
- }
-
- strlcpy(connection->nick, nick_buf.nick,
- (sizeof(connection->nick) < sizeof(nick_buf.nick)) ?
- sizeof(connection->nick) : sizeof(nick_buf.nick));
-
- if (netplay->is_server)
- {
- settings_t *settings = config_get_ptr();
-
- /* There's a password, so just put them in PRE_PASSWORD mode */
- if ( settings->paths.netplay_password[0] ||
- settings->paths.netplay_spectate_password[0])
- connection->mode = NETPLAY_CONNECTION_PRE_PASSWORD;
- else
- {
- connection->can_play = true;
- if (!netplay_handshake_info(netplay, connection))
- return false;
- connection->mode = NETPLAY_CONNECTION_PRE_INFO;
- }
- }
- /* Client needs to wait for INFO */
- else
- connection->mode = NETPLAY_CONNECTION_PRE_INFO;
-
- *had_input = true;
- netplay_recv_flush(&connection->recv_packet_buffer);
- return true;
-}
-
-/**
- * netplay_handshake_pre_password
- *
- * Data receiver for the third, optional stage of server handshake, receiving
- * the password and sending core/content info.
- */
-static bool netplay_handshake_pre_password(netplay_t *netplay,
- struct netplay_connection *connection, bool *had_input)
-{
- struct password_buf_s password_buf;
- char password[8+NETPLAY_PASS_LEN]; /* 8 for salt */
- char hash[NETPLAY_PASS_HASH_LEN+1]; /* + NULL terminator */
- ssize_t recvd;
- char msg[512];
- bool correct = false;
- settings_t *settings = config_get_ptr();
-
- msg[0] = '\0';
-
- RECV(&password_buf, sizeof(password_buf)) {}
-
- /* Expecting only a password command */
- if (recvd < 0 ||
- ntohl(password_buf.cmd[0]) != NETPLAY_CMD_PASSWORD ||
- ntohl(password_buf.cmd[1]) != sizeof(password_buf.password))
- {
- if (netplay->is_server)
- strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT),
- sizeof(msg));
- else
- strlcpy(msg,
- msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST),
- sizeof(msg));
- RARCH_ERR("%s\n", msg);
- runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return false;
- }
-
- /* Calculate the correct password hash(es) and compare */
- correct = false;
- snprintf(password, sizeof(password), "%08X", connection->salt);
-
- if (settings->paths.netplay_password[0])
- {
- strlcpy(password + 8,
- settings->paths.netplay_password, sizeof(password)-8);
-
- sha256_hash(hash, (uint8_t *) password, strlen(password));
-
- if (!memcmp(password_buf.password, hash, NETPLAY_PASS_HASH_LEN))
- {
- correct = true;
- connection->can_play = true;
- }
- }
- if (settings->paths.netplay_spectate_password[0])
- {
- strlcpy(password + 8,
- settings->paths.netplay_spectate_password, sizeof(password)-8);
-
- sha256_hash(hash, (uint8_t *) password, strlen(password));
-
- if (!memcmp(password_buf.password, hash, NETPLAY_PASS_HASH_LEN))
- correct = true;
- }
-
- /* Just disconnect if it was wrong */
- if (!correct)
- return false;
-
- /* Otherwise, exchange info */
- if (!netplay_handshake_info(netplay, connection))
- return false;
-
- *had_input = true;
- connection->mode = NETPLAY_CONNECTION_PRE_INFO;
- netplay_recv_flush(&connection->recv_packet_buffer);
- return true;
-}
-
-/**
- * netplay_handshake_pre_info
- *
- * Data receiver for the third stage of server handshake, receiving
- * the password.
- */
-static bool netplay_handshake_pre_info(netplay_t *netplay,
- struct netplay_connection *connection, bool *had_input)
-{
- struct info_buf_s info_buf;
- uint32_t cmd_size;
- ssize_t recvd;
- uint32_t content_crc = 0;
- const char *dmsg = NULL;
- struct retro_system_info *system = runloop_get_libretro_system_info();
-
- RECV(&info_buf, sizeof(info_buf.cmd)) {}
-
- if (recvd < 0 ||
- ntohl(info_buf.cmd[0]) != NETPLAY_CMD_INFO)
- {
- RARCH_ERR("Failed to receive netplay info.\n");
- return false;
- }
-
- cmd_size = ntohl(info_buf.cmd[1]);
- if (cmd_size != sizeof(info_buf) - 2*sizeof(uint32_t))
- {
- /* Either the host doesn't have anything loaded, or this is just screwy */
- if (cmd_size != 0)
- {
- /* Huh? */
- RARCH_ERR("Invalid NETPLAY_CMD_INFO payload size.\n");
- return false;
- }
-
- /* Send our info and hope they load it! */
- if (!netplay_handshake_info(netplay, connection))
- return false;
-
- *had_input = true;
- netplay_recv_flush(&connection->recv_packet_buffer);
- return true;
- }
-
- RECV(&info_buf.content_crc, cmd_size)
- {
- RARCH_ERR("Failed to receive netplay info payload.\n");
- return false;
- }
-
- /* Check the core info */
- if (system)
- {
- if (strncmp(info_buf.core_name,
- system->library_name, sizeof(info_buf.core_name)))
- {
- /* Wrong core! */
- dmsg = msg_hash_to_str(MSG_NETPLAY_DIFFERENT_CORES);
- RARCH_ERR("%s\n", dmsg);
- runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- /* FIXME: Should still send INFO, so the other side knows what's what */
- return false;
- }
- if (strncmp(info_buf.core_version,
- system->library_version, sizeof(info_buf.core_version)))
- {
- dmsg = msg_hash_to_str(MSG_NETPLAY_DIFFERENT_CORE_VERSIONS);
- RARCH_WARN("%s\n", dmsg);
- runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- }
-
- /* Check the content CRC */
- content_crc = content_get_crc();
-
- if (content_crc != 0)
- {
- if (ntohl(info_buf.content_crc) != content_crc)
- {
- dmsg = msg_hash_to_str(MSG_CONTENT_CRC32S_DIFFER);
- RARCH_WARN("%s\n", dmsg);
- runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- }
-
- /* Now switch to the right mode */
- if (netplay->is_server)
- {
- if (!netplay_handshake_sync(netplay, connection))
- return false;
- }
- else
- {
- if (!netplay_handshake_info(netplay, connection))
- return false;
- connection->mode = NETPLAY_CONNECTION_PRE_SYNC;
- }
-
- *had_input = true;
- netplay_recv_flush(&connection->recv_packet_buffer);
- return true;
-}
-
-/**
- * netplay_handshake_pre_sync
- *
- * Data receiver for the client's third handshake stage, receiving the
- * synchronization information.
- */
-static bool netplay_handshake_pre_sync(netplay_t *netplay,
- struct netplay_connection *connection, bool *had_input)
-{
- uint32_t cmd[2];
- uint32_t new_frame_count, client_num;
- uint32_t device;
- uint32_t local_sram_size, remote_sram_size;
- size_t i, j;
- ssize_t recvd;
- retro_ctx_controller_info_t pad;
- char new_nick[NETPLAY_NICK_LEN];
- retro_ctx_memory_info_t mem_info;
-
- RECV(cmd, sizeof(cmd))
- {
- const char *msg = msg_hash_to_str(MSG_NETPLAY_INCORRECT_PASSWORD);
- RARCH_ERR("%s\n", msg);
- runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return false;
- }
-
- /* Only expecting a sync command */
- if (ntohl(cmd[0]) != NETPLAY_CMD_SYNC ||
- ntohl(cmd[1]) < (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) + (MAX_INPUT_DEVICES)*sizeof(uint8_t) +
- NETPLAY_NICK_LEN)
- {
- RARCH_ERR("%s\n",
- msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST));
- return false;
- }
-
- /* Get the frame count */
- RECV(&new_frame_count, sizeof(new_frame_count))
- return false;
- new_frame_count = ntohl(new_frame_count);
-
- /* Get our client number and pause mode */
- RECV(&client_num, sizeof(client_num))
- return false;
- client_num = ntohl(client_num);
- if (client_num & NETPLAY_CMD_SYNC_BIT_PAUSED)
- {
- netplay->remote_paused = true;
- client_num ^= NETPLAY_CMD_SYNC_BIT_PAUSED;
- }
- netplay->self_client_num = client_num;
-
- /* Set our frame counters as requested */
- netplay->self_frame_count = netplay->run_frame_count =
- netplay->other_frame_count = netplay->unread_frame_count =
- netplay->server_frame_count = new_frame_count;
-
- /* And clear out the framebuffer */
- for (i = 0; i < netplay->buffer_size; i++)
- {
- struct delta_frame *ptr = &netplay->buffer[i];
-
- ptr->used = false;
-
- if (i == netplay->self_ptr)
- {
- /* Clear out any current data but still use this frame */
- if (!netplay_delta_frame_ready(netplay, ptr, 0))
- return false;
-
- ptr->frame = new_frame_count;
- ptr->have_local = true;
- netplay->run_ptr = netplay->other_ptr = netplay->unread_ptr =
- netplay->server_ptr = i;
-
- }
- }
- for (i = 0; i < MAX_CLIENTS; i++)
- {
- netplay->read_ptr[i] = netplay->self_ptr;
- netplay->read_frame_count[i] = netplay->self_frame_count;
- }
-
- /* Get and set each input device */
- for (i = 0; i < MAX_INPUT_DEVICES; i++)
- {
- RECV(&device, sizeof(device))
- return false;
-
- pad.port = (unsigned)i;
- pad.device = ntohl(device);
- netplay->config_devices[i] = pad.device;
- if ((pad.device&RETRO_DEVICE_MASK) == RETRO_DEVICE_KEYBOARD)
- {
- netplay->have_updown_device = true;
- netplay_key_hton_init();
- }
-
- core_set_controller_port_device(&pad);
- }
-
- /* Get the share modes */
- RECV(netplay->device_share_modes, sizeof(netplay->device_share_modes))
- return false;
-
- /* Get the client-controller mapping */
- netplay->connected_players =
- netplay->connected_slaves =
- netplay->self_devices = 0;
- for (i = 0; i < MAX_CLIENTS; i++)
- netplay->client_devices[i] = 0;
- for (i = 0; i < MAX_INPUT_DEVICES; i++)
- {
- RECV(&device, sizeof(device))
- return false;
- device = ntohl(device);
-
- netplay->device_clients[i] = device;
- netplay->connected_players |= device;
- for (j = 0; j < MAX_CLIENTS; j++)
- {
- if (device & (1<client_devices[j] |= 1<nick, new_nick, NETPLAY_NICK_LEN))
- {
- char msg[512];
- strlcpy(netplay->nick, new_nick, NETPLAY_NICK_LEN);
- snprintf(msg, sizeof(msg),
- msg_hash_to_str(MSG_NETPLAY_CHANGED_NICK), netplay->nick);
- RARCH_LOG("%s\n", msg);
- runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
-
- /* Now check the SRAM */
-#ifdef HAVE_THREADS
- autosave_lock();
-#endif
- mem_info.id = RETRO_MEMORY_SAVE_RAM;
- core_get_memory(&mem_info);
-
- local_sram_size = (unsigned)mem_info.size;
- remote_sram_size = ntohl(cmd[1]) -
- (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) - (MAX_INPUT_DEVICES)*sizeof(uint8_t) - NETPLAY_NICK_LEN;
-
- if (local_sram_size != 0 && local_sram_size == remote_sram_size)
- {
- RECV(mem_info.data, local_sram_size)
- {
- RARCH_ERR("%s\n",
- msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST));
-#ifdef HAVE_THREADS
- autosave_unlock();
-#endif
- return false;
- }
-
- }
- else if (remote_sram_size != 0)
- {
- /* We can't load this, but we still need to get rid of the data */
- uint32_t quickbuf;
- while (remote_sram_size > 0)
- {
- RECV(&quickbuf, (remote_sram_size > sizeof(uint32_t)) ? sizeof(uint32_t) : remote_sram_size)
- {
- RARCH_ERR("%s\n",
- msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST));
-#ifdef HAVE_THREADS
- autosave_unlock();
-#endif
- return false;
- }
- if (remote_sram_size > sizeof(uint32_t))
- remote_sram_size -= sizeof(uint32_t);
- else
- remote_sram_size = 0;
- }
-
- }
-#ifdef HAVE_THREADS
- autosave_unlock();
-#endif
-
- /* We're ready! */
- *had_input = true;
- netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
- connection->mode = NETPLAY_CONNECTION_PLAYING;
- netplay_handshake_ready(netplay, connection);
- netplay_recv_flush(&connection->recv_packet_buffer);
-
- /* Ask to switch to playing mode if we should */
- {
- settings_t *settings = config_get_ptr();
- if (!settings->bools.netplay_start_as_spectator)
- return netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING);
- }
-
- return true;
-}
-
-/**
- * netplay_handshake
- *
- * Data receiver for all handshake states.
- */
-bool netplay_handshake(netplay_t *netplay,
- struct netplay_connection *connection, bool *had_input)
-{
- bool ret = false;
-
- switch (connection->mode)
- {
- case NETPLAY_CONNECTION_INIT:
- ret = netplay_handshake_init(netplay, connection, had_input);
- break;
- case NETPLAY_CONNECTION_PRE_NICK:
- ret = netplay_handshake_pre_nick(netplay, connection, had_input);
- break;
- case NETPLAY_CONNECTION_PRE_PASSWORD:
- ret = netplay_handshake_pre_password(netplay, connection, had_input);
- break;
- case NETPLAY_CONNECTION_PRE_INFO:
- ret = netplay_handshake_pre_info(netplay, connection, had_input);
- break;
- case NETPLAY_CONNECTION_PRE_SYNC:
- ret = netplay_handshake_pre_sync(netplay, connection, had_input);
- break;
- case NETPLAY_CONNECTION_NONE:
- default:
- return false;
- }
-
- if (connection->mode >= NETPLAY_CONNECTION_CONNECTED &&
- !netplay_send_cur_input(netplay, connection))
- return false;
-
- return ret;
-}
diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h
index 518994edf4..7cf59ebf53 100644
--- a/network/netplay/netplay_private.h
+++ b/network/netplay/netplay_private.h
@@ -674,19 +674,6 @@ bool netplay_lan_ad_server(netplay_t *netplay);
* NETPLAY-FRONTEND.C
**************************************************************/
-/**
- * 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);
-
/**
* input_poll_net
*
diff --git a/retroarch.c b/retroarch.c
index 6573ab9fdd..040d3556d3 100644
--- a/retroarch.c
+++ b/retroarch.c
@@ -4641,192 +4641,6 @@ static void discord_init(
#endif
#ifdef HAVE_NETWORKING
-/**
- * netplay_is_alive:
- * @netplay : pointer to netplay object
- *
- * Checks if input port/index is controlled by netplay or not.
- *
- * Returns: true (1) if alive, otherwise false (0).
- **/
-static bool netplay_is_alive(netplay_t *netplay)
-{
- return (netplay->is_server) ||
- (!netplay->is_server &&
- netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED);
-}
-
-/**
- * netplay_should_skip:
- * @netplay : pointer to netplay object
- *
- * If we're fast-forward replaying to resync, check if we
- * should actually show frame.
- *
- * Returns: bool (1) if we should skip this frame, otherwise
- * false (0).
- **/
-static bool netplay_should_skip(netplay_t *netplay)
-{
- if (!netplay)
- return false;
- return netplay->is_replay
- && (netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED);
-}
-
-/**
- * get_self_input_state:
- * @netplay : pointer to netplay object
- *
- * Grab our own input state and send this frame's input state (self and remote)
- * over the network
- *
- * Returns: true (1) if successful, otherwise false (0).
- */
-static bool get_self_input_state(
- bool block_libretro_input,
- netplay_t *netplay)
-{
- unsigned i;
- struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr];
- netplay_input_state_t istate = NULL;
- uint32_t devices, used_devices = 0, devi, dev_type, local_device;
-
- if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count))
- return false;
-
- /* We've already read this frame! */
- if (ptr->have_local)
- return true;
-
- devices = netplay->self_devices;
- used_devices = 0;
-
- for (devi = 0; devi < MAX_INPUT_DEVICES; devi++)
- {
- if (!(devices & (1 << devi)))
- continue;
-
- /* Find an appropriate local device */
- dev_type = netplay->config_devices[devi]&RETRO_DEVICE_MASK;
-
- for (local_device = 0; local_device < MAX_INPUT_DEVICES; local_device++)
- {
- if (used_devices & (1 << local_device))
- continue;
- if ((netplay->config_devices[local_device]&RETRO_DEVICE_MASK) == dev_type)
- break;
- }
-
- if (local_device == MAX_INPUT_DEVICES)
- local_device = 0;
- used_devices |= (1 << local_device);
-
- istate = netplay_input_state_for(&ptr->real_input[devi],
- /* If we're a slave, we write our own input to MAX_CLIENTS to keep it separate */
- (netplay->self_mode==NETPLAY_CONNECTION_SLAVE)?MAX_CLIENTS:netplay->self_client_num,
- netplay_expected_input_size(netplay, 1 << devi),
- true, false);
- if (!istate)
- continue; /* FIXME: More severe? */
-
- /* First frame we always give zero input since relying on
- * input from first frame screws up when we use -F 0. */
- if ( !block_libretro_input
- && netplay->self_frame_count > 0)
- {
- uint32_t *state = istate->data;
- retro_input_state_t cb = netplay->cbs.state_cb;
- unsigned dtype = netplay->config_devices[devi]&RETRO_DEVICE_MASK;
-
- switch (dtype)
- {
- case RETRO_DEVICE_ANALOG:
- for (i = 0; i < 2; i++)
- {
- int16_t tmp_x = cb(local_device,
- RETRO_DEVICE_ANALOG, (unsigned)i, 0);
- int16_t tmp_y = cb(local_device,
- RETRO_DEVICE_ANALOG, (unsigned)i, 1);
- state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16);
- }
- /* no break */
-
- case RETRO_DEVICE_JOYPAD:
- for (i = 0; i <= RETRO_DEVICE_ID_JOYPAD_R3; i++)
- {
- int16_t tmp = cb(local_device,
- RETRO_DEVICE_JOYPAD, 0, (unsigned)i);
- state[0] |= tmp ? 1 << i : 0;
- }
- break;
-
- case RETRO_DEVICE_MOUSE:
- case RETRO_DEVICE_LIGHTGUN:
- {
- int16_t tmp_x = cb(local_device, dtype, 0, 0);
- int16_t tmp_y = cb(local_device, dtype, 0, 1);
- state[1] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16);
- for (i = 2;
- i <= (unsigned)((dtype == RETRO_DEVICE_MOUSE) ?
- RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN :
- RETRO_DEVICE_ID_LIGHTGUN_START);
- i++)
- {
- int16_t tmp = cb(local_device, dtype, 0,
- (unsigned) i);
- state[0] |= tmp ? 1 << i : 0;
- }
- break;
- }
-
- case RETRO_DEVICE_KEYBOARD:
- {
- unsigned key, word = 0, bit = 1;
- for (key = 1; key < NETPLAY_KEY_LAST; key++)
- {
- state[word] |=
- cb(local_device, RETRO_DEVICE_KEYBOARD, 0,
- NETPLAY_KEY_NTOH(key)) ?
- (UINT32_C(1) << bit) : 0;
- bit++;
- if (bit >= 32)
- {
- bit = 0;
- word++;
- if (word >= istate->size)
- break;
- }
- }
- break;
- }
- }
- }
- }
-
- ptr->have_local = true;
- if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
- {
- ptr->have_real[netplay->self_client_num] = true;
- netplay->read_ptr[netplay->self_client_num] = NEXT_PTR(netplay->self_ptr);
- netplay->read_frame_count[netplay->self_client_num] = netplay->self_frame_count + 1;
- }
-
- /* And send this input to our peers */
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED)
- netplay_send_cur_input(netplay, &netplay->connections[i]);
- }
-
- /* Handle any delayed state changes */
- if (netplay->is_server)
- netplay_delayed_state_change(netplay);
-
- return true;
-}
-
static bool init_netplay_deferred(
struct rarch_state *p_rarch,
const char* server, unsigned port)
@@ -4844,252 +4658,6 @@ static bool init_netplay_deferred(
return p_rarch->netplay_client_deferred;
}
-/**
- * netplay_poll:
- * @netplay : pointer to netplay object
- *
- * Polls network to see if we have anything new. If our
- * network buffer is full, we simply have to block
- * for new input data.
- *
- * Returns: true (1) if successful, otherwise false (0).
- **/
-static bool netplay_poll(
- bool block_libretro_input,
- settings_t *settings,
- netplay_t *netplay)
-{
- int res;
- uint32_t client;
- size_t i;
-
- if (!get_self_input_state(block_libretro_input, netplay))
- goto catastrophe;
-
- /* If we're not connected, we're done */
- if (netplay->self_mode == NETPLAY_CONNECTION_NONE)
- return true;
-
- /* Read Netplay input, block if we're configured to stall for input every
- * frame */
- netplay_update_unread_ptr(netplay);
- if (netplay->stateless_mode &&
- (netplay->connected_players>1) &&
- netplay->unread_frame_count <= netplay->run_frame_count)
- res = netplay_poll_net_input(netplay, true);
- else
- res = netplay_poll_net_input(netplay, false);
- if (res == -1)
- goto catastrophe;
-
- /* Resolve and/or simulate the input if we don't have real input */
- netplay_resolve_input(netplay, netplay->run_ptr, false);
-
- /* Handle any slaves */
- if (netplay->is_server && netplay->connected_slaves)
- netplay_handle_slaves(netplay);
-
- netplay_update_unread_ptr(netplay);
-
- /* Figure out how many frames of input latency we should be using to hide
- * network latency */
- if (netplay->frame_run_time_avg || netplay->stateless_mode)
- {
- /* FIXME: Using fixed 60fps for this calculation */
- unsigned frames_per_frame = netplay->frame_run_time_avg ?
- (16666 / netplay->frame_run_time_avg) :
- 0;
- unsigned frames_ahead = (netplay->run_frame_count > netplay->unread_frame_count) ?
- (netplay->run_frame_count - netplay->unread_frame_count) :
- 0;
- int input_latency_frames_min = settings->uints.netplay_input_latency_frames_min -
- (settings->bools.run_ahead_enabled ? settings->uints.run_ahead_frames : 0);
- int input_latency_frames_max = input_latency_frames_min + settings->uints.netplay_input_latency_frames_range;
-
- /* Assume we need a couple frames worth of time to actually run the
- * current frame */
- if (frames_per_frame > 2)
- frames_per_frame -= 2;
- else
- frames_per_frame = 0;
-
- /* Shall we adjust our latency? */
- if (netplay->stateless_mode)
- {
- /* In stateless mode, we adjust up if we're "close" and down if we
- * have a lot of slack */
- if (netplay->input_latency_frames < input_latency_frames_min ||
- (netplay->unread_frame_count == netplay->run_frame_count + 1 &&
- netplay->input_latency_frames < input_latency_frames_max))
- netplay->input_latency_frames++;
- else if (netplay->input_latency_frames > input_latency_frames_max ||
- (netplay->unread_frame_count > netplay->run_frame_count + 2 &&
- netplay->input_latency_frames > input_latency_frames_min))
- netplay->input_latency_frames--;
- }
- else if (netplay->input_latency_frames < input_latency_frames_min ||
- (frames_per_frame < frames_ahead &&
- netplay->input_latency_frames < input_latency_frames_max))
- {
- /* We can't hide this much network latency with replay, so hide some
- * with input latency */
- netplay->input_latency_frames++;
- }
- else if (netplay->input_latency_frames > input_latency_frames_max ||
- (frames_per_frame > frames_ahead + 2 &&
- netplay->input_latency_frames > input_latency_frames_min))
- {
- /* We don't need this much latency (any more) */
- netplay->input_latency_frames--;
- }
- }
-
- /* If we're stalled, consider unstalling */
- switch (netplay->stall)
- {
- case NETPLAY_STALL_RUNNING_FAST:
- if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2
- > netplay->self_frame_count)
- {
- netplay->stall = NETPLAY_STALL_NONE;
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (connection->active && connection->stall)
- connection->stall = NETPLAY_STALL_NONE;
- }
- }
- break;
-
- case NETPLAY_STALL_SPECTATOR_WAIT:
- if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || netplay->unread_frame_count > netplay->self_frame_count)
- netplay->stall = NETPLAY_STALL_NONE;
- break;
-
- case NETPLAY_STALL_INPUT_LATENCY:
- /* Just let it recalculate momentarily */
- netplay->stall = NETPLAY_STALL_NONE;
- break;
-
- case NETPLAY_STALL_SERVER_REQUESTED:
- /* See if the stall is done */
- if (netplay->connections[0].stall_frame == 0)
- {
- /* Stop stalling! */
- netplay->connections[0].stall = NETPLAY_STALL_NONE;
- netplay->stall = NETPLAY_STALL_NONE;
- }
- else
- netplay->connections[0].stall_frame--;
- break;
- case NETPLAY_STALL_NO_CONNECTION:
- /* We certainly haven't fixed this */
- break;
- default: /* not stalling */
- break;
- }
-
- /* If we're not stalled, consider stalling */
- if (!netplay->stall)
- {
- /* Have we not read enough latency frames? */
- if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING &&
- netplay->connected_players &&
- netplay->run_frame_count + netplay->input_latency_frames > netplay->self_frame_count)
- {
- netplay->stall = NETPLAY_STALL_INPUT_LATENCY;
- netplay->stall_time = 0;
- }
-
- /* Are we too far ahead? */
- if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES
- <= netplay->self_frame_count)
- {
- netplay->stall = NETPLAY_STALL_RUNNING_FAST;
- netplay->stall_time = cpu_features_get_time_usec();
-
- /* Figure out who to blame */
- if (netplay->is_server)
- {
- for (client = 1; client < MAX_CLIENTS; client++)
- {
- struct netplay_connection *connection;
- if (!(netplay->connected_players & (1 << client)))
- continue;
- if (netplay->read_frame_count[client] > netplay->unread_frame_count)
- continue;
- connection = &netplay->connections[client-1];
- if (connection->active &&
- connection->mode == NETPLAY_CONNECTION_PLAYING)
- {
- connection->stall = NETPLAY_STALL_RUNNING_FAST;
- connection->stall_time = netplay->stall_time;
- }
- }
- }
-
- }
-
- /* If we're a spectator, are we ahead at all? */
- if (!netplay->is_server &&
- (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING ||
- netplay->self_mode == NETPLAY_CONNECTION_SLAVE) &&
- netplay->unread_frame_count <= netplay->self_frame_count)
- {
- netplay->stall = NETPLAY_STALL_SPECTATOR_WAIT;
- netplay->stall_time = cpu_features_get_time_usec();
- }
- }
-
- /* If we're stalling, consider disconnection */
- if (netplay->stall && netplay->stall_time)
- {
- retro_time_t now = cpu_features_get_time_usec();
-
- /* Don't stall out while they're paused */
- if (netplay->remote_paused)
- netplay->stall_time = now;
- else if (now - netplay->stall_time >=
- (netplay->is_server ? MAX_SERVER_STALL_TIME_USEC :
- MAX_CLIENT_STALL_TIME_USEC))
- {
- /* Stalled out! */
- if (netplay->is_server)
- {
- bool fixed = false;
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (connection->active &&
- connection->mode == NETPLAY_CONNECTION_PLAYING &&
- connection->stall)
- {
- netplay_hangup(netplay, connection);
- fixed = true;
- }
- }
-
- if (fixed)
- {
- /* Not stalled now :) */
- netplay->stall = NETPLAY_STALL_NONE;
- return true;
- }
- }
- else
- goto catastrophe;
- return false;
- }
- }
-
- return true;
-
-catastrophe:
- for (i = 0; i < netplay->connections_size; i++)
- netplay_hangup(netplay, &netplay->connections[i]);
- return false;
-}
-
/**
* input_poll_net
*
@@ -5136,85 +4704,6 @@ static size_t audio_sample_batch_net(const int16_t *data, size_t frames)
return frames;
}
-static int16_t netplay_input_state(netplay_t *netplay,
- unsigned port, unsigned device,
- unsigned idx, unsigned id)
-{
- struct delta_frame *delta;
- netplay_input_state_t istate;
- const uint32_t *curr_input_state = NULL;
- size_t ptr =
- netplay->is_replay
- ? netplay->replay_ptr
- : netplay->run_ptr;
-
- if (port >= MAX_INPUT_DEVICES)
- return 0;
-
- /* If the port doesn't seem to correspond to the device, "correct" it. This
- * is common with devices that typically only have one instance, such as
- * keyboards, mice and lightguns. */
- if (device != RETRO_DEVICE_JOYPAD &&
- (netplay->config_devices[port]&RETRO_DEVICE_MASK) != device)
- {
- for (port = 0; port < MAX_INPUT_DEVICES; port++)
- {
- if ((netplay->config_devices[port]&RETRO_DEVICE_MASK) == device)
- break;
- }
- if (port == MAX_INPUT_DEVICES)
- return 0;
- }
-
- delta = &netplay->buffer[ptr];
- istate = delta->resolved_input[port];
- if (!istate || !istate->used || istate->size == 0)
- return 0;
-
- curr_input_state = istate->data;
-
- switch (device)
- {
- case RETRO_DEVICE_JOYPAD:
- if (id == RETRO_DEVICE_ID_JOYPAD_MASK)
- return curr_input_state[0];
- return ((1 << id) & curr_input_state[0]) ? 1 : 0;
-
- case RETRO_DEVICE_ANALOG:
- if (istate->size == 3)
- {
- uint32_t state = curr_input_state[1 + idx];
- return (int16_t)(uint16_t)(state >> (id * 16));
- }
- break;
- case RETRO_DEVICE_MOUSE:
- case RETRO_DEVICE_LIGHTGUN:
- if (istate->size == 2)
- {
- if (id <= RETRO_DEVICE_ID_MOUSE_Y)
- return (int16_t)(uint16_t)(curr_input_state[1] >> (id * 16));
- return ((1 << id) & curr_input_state[0]) ? 1 : 0;
- }
- break;
- case RETRO_DEVICE_KEYBOARD:
- {
- unsigned key = netplay_key_hton(id);
- if (key != NETPLAY_KEY_UNKNOWN)
- {
- unsigned word = key / 32;
- unsigned bit = key % 32;
- if (word <= istate->size)
- return ((UINT32_C(1) << bit) & curr_input_state[word]) ? 1 : 0;
- }
- }
- break;
- default:
- break;
- }
-
- return 0;
-}
-
static void netplay_announce_cb(retro_task_t *task,
void *task_data, void *user_data, const char *error)
{
@@ -5486,57 +4975,6 @@ static int16_t input_state_net(unsigned port, unsigned device,
/* ^^^ Netplay polling callbacks */
-/**
-* 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)
- */
-static void netplay_frontend_paused(netplay_t *netplay, bool paused)
-{
- size_t i;
- uint32_t paused_ct = 0;
-
- netplay->local_paused = paused;
-
- /* Communicating this is a bit odd: If exactly one other connection is
- * paused, then we must tell them that we're unpaused, as from their
- * perspective we are. If more than one other connection is paused, then our
- * status as proxy means we are NOT unpaused to either of them. */
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (connection->active && connection->paused)
- paused_ct++;
- }
-
- if (paused_ct > 1)
- return;
-
- /* Send our unpaused status. Must send manually because we must immediately
- * flush the buffer: If we're paused, we won't be polled. */
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if ( connection->active
- && connection->mode >= NETPLAY_CONNECTION_CONNECTED)
- {
- if (paused)
- netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_PAUSE,
- netplay->nick, NETPLAY_NICK_LEN);
- else
- netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_RESUME,
- NULL, 0);
-
- /* We're not going to be polled, so we need to
- * flush this command now */
- netplay_send_flush(&connection->send_packet_buffer,
- connection->fd, true);
- }
- }
-}
-
/**
* netplay_disconnect
* @netplay : pointer to netplay object
@@ -5685,219 +5123,6 @@ static void netplay_post_frame(
netplay_disconnect(p_rarch, netplay);
}
-/**
- * netplay_force_future
- * @netplay : pointer to netplay object
- *
- * Force netplay to ignore all past input, typically because we've just loaded
- * a state or reset.
- */
-static void netplay_force_future(netplay_t *netplay)
-{
- /* Wherever we're inputting, that's where we consider our state to be loaded */
- netplay->run_ptr = netplay->self_ptr;
- netplay->run_frame_count = netplay->self_frame_count;
-
- /* We need to ignore any intervening data from the other side,
- * and never rewind past this */
- netplay_update_unread_ptr(netplay);
-
- if (netplay->unread_frame_count < netplay->run_frame_count)
- {
- uint32_t client;
- for (client = 0; client < MAX_CLIENTS; client++)
- {
- if (!(netplay->connected_players & (1 << client)))
- continue;
-
- if (netplay->read_frame_count[client] < netplay->run_frame_count)
- {
- netplay->read_ptr[client] = netplay->run_ptr;
- netplay->read_frame_count[client] = netplay->run_frame_count;
- }
- }
- if (netplay->server_frame_count < netplay->run_frame_count)
- {
- netplay->server_ptr = netplay->run_ptr;
- netplay->server_frame_count = netplay->run_frame_count;
- }
- netplay_update_unread_ptr(netplay);
- }
- if (netplay->other_frame_count < netplay->run_frame_count)
- {
- netplay->other_ptr = netplay->run_ptr;
- netplay->other_frame_count = netplay->run_frame_count;
- }
-}
-
-/**
- * netplay_send_savestate
- * @netplay : pointer to netplay object
- * @serial_info : the savestate being loaded
- * @cx : compression type
- * @z : compression backend to use
- *
- * Send a loaded savestate to those connected peers using the given compression
- * scheme.
- */
-static void netplay_send_savestate(netplay_t *netplay,
- retro_ctx_serialize_info_t *serial_info, uint32_t cx,
- struct compression_transcoder *z)
-{
- uint32_t header[4];
- uint32_t rd, wn;
- size_t i;
-
- /* Compress it */
- z->compression_backend->set_in(z->compression_stream,
- (const uint8_t*)serial_info->data_const, (uint32_t)serial_info->size);
- z->compression_backend->set_out(z->compression_stream,
- netplay->zbuffer, (uint32_t)netplay->zbuffer_size);
- if (!z->compression_backend->trans(z->compression_stream, true, &rd,
- &wn, NULL))
- {
- /* Catastrophe! */
- for (i = 0; i < netplay->connections_size; i++)
- netplay_hangup(netplay, &netplay->connections[i]);
- return;
- }
-
- /* Send it to relevant peers */
- header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE);
- header[1] = htonl(wn + 2*sizeof(uint32_t));
- header[2] = htonl(netplay->run_frame_count);
- header[3] = htonl(serial_info->size);
-
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (!connection->active ||
- connection->mode < NETPLAY_CONNECTION_CONNECTED ||
- connection->compression_supported != cx) continue;
-
- if (!netplay_send(&connection->send_packet_buffer, connection->fd, header,
- sizeof(header)) ||
- !netplay_send(&connection->send_packet_buffer, connection->fd,
- netplay->zbuffer, wn))
- netplay_hangup(netplay, connection);
- }
-}
-
-/**
- * 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)
-{
- retro_ctx_serialize_info_t tmp_serial_info;
-
- netplay_force_future(netplay);
-
- /* Record it in our own buffer */
- if (save || !serial_info)
- {
- /* TODO/FIXME: This is a critical failure! */
- if (!netplay_delta_frame_ready(netplay,
- &netplay->buffer[netplay->run_ptr], netplay->run_frame_count))
- return;
-
- if (!serial_info)
- {
- tmp_serial_info.size = netplay->state_size;
- tmp_serial_info.data = netplay->buffer[netplay->run_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->run_ptr].state,
- serial_info->data_const, serial_info->size);
- }
- }
-
- /* Don't send it if we're expected to be desynced */
- if (netplay->desync)
- return;
-
- /* If we can't send it to the peer, loading a state was a bad idea */
- if (netplay->quirks & (
- NETPLAY_QUIRK_NO_SAVESTATES
- | NETPLAY_QUIRK_NO_TRANSMISSION))
- return;
-
- /* Send this to every peer */
- if (netplay->compress_nil.compression_backend)
- netplay_send_savestate(netplay, serial_info, 0, &netplay->compress_nil);
- if (netplay->compress_zlib.compression_backend)
- netplay_send_savestate(netplay, serial_info, NETPLAY_COMPRESSION_ZLIB,
- &netplay->compress_zlib);
-}
-
-/**
- * netplay_core_reset
- * @netplay : pointer to netplay object
- *
- * Indicate that the core has been reset to netplay peers
- **/
-static void netplay_core_reset(netplay_t *netplay)
-{
- size_t i;
- uint32_t cmd[3];
-
- /* Ignore past input */
- netplay_force_future(netplay);
-
- /* Request that our peers reset */
- cmd[0] = htonl(NETPLAY_CMD_RESET);
- cmd[1] = htonl(sizeof(uint32_t));
- cmd[2] = htonl(netplay->self_frame_count);
-
- for (i = 0; i < netplay->connections_size; i++)
- {
- struct netplay_connection *connection = &netplay->connections[i];
- if (!connection->active ||
- connection->mode < NETPLAY_CONNECTION_CONNECTED) continue;
-
- if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd,
- sizeof(cmd)))
- netplay_hangup(netplay, connection);
- }
-}
-
-/**
- * netplay_toggle_play_spectate
- *
- * Toggle between play mode and spectate mode
- */
-static void netplay_toggle_play_spectate(netplay_t *netplay)
-{
- switch (netplay->self_mode)
- {
- case NETPLAY_CONNECTION_PLAYING:
- case NETPLAY_CONNECTION_SLAVE:
- /* Switch to spectator mode immediately */
- netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
- netplay_cmd_mode(netplay, NETPLAY_CONNECTION_SPECTATING);
- break;
- case NETPLAY_CONNECTION_SPECTATING:
- /* Switch only after getting permission */
- netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING);
- break;
- default:
- break;
- }
-}
-
static void deinit_netplay(struct rarch_state *p_rarch)
{
if (p_rarch->netplay_data)