diff --git a/network/netplay/README b/network/netplay/README index 34b71609a0..e5a5384163 100644 --- a/network/netplay/README +++ b/network/netplay/README @@ -151,7 +151,8 @@ Payload: } Description: Cause the other side to load a savestate, notionally one which the sending - side has also loaded. The serialized savestate is zlib compressed. + side has also loaded. If both sides support zlib compression, the + serialized state is zlib compressed. Otherwise it is uncompressed. Command: PAUSE Payload: None diff --git a/network/netplay/netplay.c b/network/netplay/netplay.c index a5cd1ff0e4..f776024d9a 100644 --- a/network/netplay/netplay.c +++ b/network/netplay/netplay.c @@ -23,13 +23,16 @@ #include #include -#include #include #include #include #include #include +#if HAVE_ZLIB +#include +#endif + #include "netplay_private.h" #include "../../autosave.h" @@ -586,7 +589,9 @@ static bool netplay_get_cmd(netplay_t *netplay) { uint32_t frame; uint32_t isize; +#if HAVE_ZLIB z_stream stream; +#endif /* Make sure we're ready for it */ if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) @@ -655,24 +660,33 @@ static bool netplay_get_cmd(netplay_t *netplay) } /* And uncompress it */ - memset(&stream, 0, sizeof(stream)); - inflateInit(&stream); - stream.next_in = netplay->zbuffer; - stream.avail_in = cmd_size - 2*sizeof(uint32_t); - stream.next_out = (Bytef *) netplay->buffer[netplay->read_ptr].state; - stream.avail_out = netplay->state_size; - if (inflate(&stream, 1) == Z_STREAM_ERROR) +#if HAVE_ZLIB + if (netplay->compression == NETPLAY_COMPRESSION_ZLIB) { - RARCH_ERR("CMD_LOAD_SAVESTATE failed to uncompress savestate.\n"); - return netplay_cmd_nak(netplay); - } + memset(&stream, 0, sizeof(stream)); + inflateInit(&stream); + stream.next_in = netplay->zbuffer; + stream.avail_in = cmd_size - 2*sizeof(uint32_t); + stream.next_out = (Bytef *) netplay->buffer[netplay->read_ptr].state; + stream.avail_out = netplay->state_size; + if (inflate(&stream, 1) == Z_STREAM_ERROR) + { + RARCH_ERR("CMD_LOAD_SAVESTATE failed to uncompress savestate.\n"); + return netplay_cmd_nak(netplay); + } - if (stream.total_out != netplay->state_size) - { - RARCH_ERR("CMD_LOAD_SAVESTATE received too-short savestate.\n"); - return netplay_cmd_nak(netplay); + if (stream.total_out != netplay->state_size) + { + RARCH_ERR("CMD_LOAD_SAVESTATE received too-short savestate.\n"); + return netplay_cmd_nak(netplay); + } + inflateEnd(&stream); + } + else +#endif + { + memcpy(netplay->buffer[netplay->read_ptr].state, netplay->zbuffer, netplay->state_size); } - inflateEnd(&stream); /* Skip ahead if it's past where we are */ if (frame > netplay->self_frame_count) @@ -1441,7 +1455,10 @@ void netplay_load_savestate(netplay_t *netplay, { uint32_t header[4]; retro_ctx_serialize_info_t tmp_serial_info; + size_t zbuffer_used; +#if HAVE_ZLIB z_stream stream; +#endif if (!netplay->has_connection) return; @@ -1492,23 +1509,42 @@ void netplay_load_savestate(netplay_t *netplay, return; /* Compress it */ - memset(&stream, 0, sizeof(stream)); - deflateInit(&stream, Z_DEFAULT_COMPRESSION); - stream.next_in = (Bytef *) serial_info->data_const; - stream.avail_in = serial_info->size; - stream.next_out = netplay->zbuffer; - stream.avail_out = netplay->zbuffer_size; - if (deflate(&stream, 1) == Z_STREAM_ERROR) +#if HAVE_ZLIB + if (netplay->compression == NETPLAY_COMPRESSION_ZLIB) { - hangup(netplay); - return; + memset(&stream, 0, sizeof(stream)); + deflateInit(&stream, Z_DEFAULT_COMPRESSION); + stream.next_in = (Bytef *) serial_info->data_const; + stream.avail_in = serial_info->size; + stream.next_out = netplay->zbuffer; + stream.avail_out = netplay->zbuffer_size; + if (deflate(&stream, 1) == Z_STREAM_ERROR) + { + hangup(netplay); + return; + } + if (stream.total_in != serial_info->size) + { + hangup(netplay); + return; + } + zbuffer_used = stream.total_out; + deflateEnd(&stream); } - if (stream.total_in != serial_info->size) + else +#endif { - hangup(netplay); - return; + if (serial_info->size < netplay->zbuffer_size) + { + memcpy(netplay->zbuffer, serial_info->data_const, serial_info->size); + zbuffer_used = serial_info->size; + } + else + { + memcpy(netplay->zbuffer, serial_info->data_const, netplay->zbuffer_size); + zbuffer_used = netplay->zbuffer_size; + } } - deflateEnd(&stream); /* And send it to the peer */ header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE); @@ -1523,7 +1559,7 @@ void netplay_load_savestate(netplay_t *netplay, } if (!socket_send_all_blocking(netplay->fd, - netplay->zbuffer, stream.total_out, false)) + netplay->zbuffer, zbuffer_used, false)) { hangup(netplay); return; diff --git a/network/netplay/netplay_common.c b/network/netplay/netplay_common.c index da7a9a9926..5348297e2a 100644 --- a/network/netplay/netplay_common.c +++ b/network/netplay/netplay_common.c @@ -166,8 +166,9 @@ bool netplay_handshake(netplay_t *netplay) char msg[512]; uint32_t *content_crc_ptr = NULL; void *sram = NULL; - uint32_t header[4] = {0}; + uint32_t header[5] = {0}; bool is_server = netplay->is_server; + int compression = 0; msg[0] = '\0'; @@ -182,6 +183,7 @@ bool netplay_handshake(netplay_t *netplay) header[1] = htonl(netplay_impl_magic()); header[2] = htonl(mem_info.size); header[3] = htonl(local_pmagic); + header[4] = htonl(NETPLAY_COMPRESSION_SUPPORTED); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false)) return false; @@ -230,6 +232,13 @@ bool netplay_handshake(netplay_t *netplay) return false; } + /* Check what compression is supported */ + compression = ntohl(header[4]); + compression &= NETPLAY_COMPRESSION_SUPPORTED; + /* If multiple compression algorithms were supported, we would need to + * explicitly choose the best here */ + netplay->compression = compression; + /* Client sends nickname first, server replies with nickname */ if (!is_server) { diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index bb65034e8e..14ef2795b1 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -66,6 +66,14 @@ #define NETPLAY_QUIRK_MAP_PLATFORM_DEPENDENT \ (RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT) +/* Compression protocols supported */ +#define NETPLAY_COMPRESSION_ZLIB (1<<0) +#if HAVE_ZLIB +#define NETPLAY_COMPRESSION_SUPPORTED NETPLAY_COMPRESSION_ZLIB +#else +#define NETPLAY_COMPRESSION_SUPPORTED 0 +#endif + struct delta_frame { bool used; /* a bit derpy, but this is how we know if the delta's been used at all */ @@ -122,6 +130,9 @@ struct netplay struct delta_frame *buffer; size_t buffer_size; + /* Which compression algorithm are we using for this connection? */ + int compression; + /* A buffer into which to compress frames for transfer */ uint8_t *zbuffer; size_t zbuffer_size;