static bool netplay_send_raw_cmd(netplay_t *netplay, uint32_t cmd, const void *data, size_t size) { cmd = (cmd << 16) | (size & 0xffff); cmd = htonl(cmd); if (!socket_send_all_blocking(netplay->fd, &cmd, sizeof(cmd))) return false; if (!socket_send_all_blocking(netplay->fd, data, size)) return false; return true; }
/** * netplay_post_frame_spectate: * @netplay : pointer to netplay object * * Post-frame for Netplay (spectate mode version). * We check if we have new input and replay from recorded input. **/ static void netplay_post_frame_spectate(netplay_t *netplay) { unsigned i; if (netplay->spectate_client) return; for (i = 0; i < MAX_SPECTATORS; i++) { char msg[PATH_MAX_LENGTH] = {0}; if (netplay->spectate_fds[i] == -1) continue; if (socket_send_all_blocking(netplay->spectate_fds[i], netplay->spectate_input, netplay->spectate_input_ptr * sizeof(int16_t))) continue; RARCH_LOG("Client (#%u) disconnected ...\n", i); snprintf(msg, sizeof(msg), "Client (#%u) disconnected.", i); rarch_main_msg_queue_push(msg, 1, 180, false); socket_close(netplay->spectate_fds[i]); netplay->spectate_fds[i] = -1; break; } netplay->spectate_input_ptr = 0; }
/** * netplay_post_frame_spectate: * @netplay : pointer to netplay object * * Post-frame for Netplay (spectate mode version). * We check if we have new input and replay from recorded input. **/ static void netplay_spectate_post_frame(netplay_t *netplay) { unsigned i; if (!np_is_server(netplay)) return; for (i = 0; i < MAX_SPECTATORS; i++) { char msg[128]; if (netplay->spectate.fds[i] == -1) continue; if (socket_send_all_blocking(netplay->spectate.fds[i], netplay->spectate.input, netplay->spectate.input_ptr * sizeof(int16_t))) continue; RARCH_LOG("Client (#%u) disconnected ...\n", i); snprintf(msg, sizeof(msg), "Client (#%u) disconnected.", i); runloop_msg_queue_push(msg, 1, 180, false); socket_close(netplay->spectate.fds[i]); netplay->spectate.fds[i] = -1; break; } netplay->spectate.input_ptr = 0; }
bool np_get_info(netplay_t *netplay) { unsigned sram_size; uint32_t header[3]; const void *sram = NULL; global_t *global = global_get_ptr(); if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header))) { RARCH_ERR("Failed to receive header from client.\n"); return false; } if (global->content_crc != ntohl(header[0])) { RARCH_ERR("Content CRC32s differ. Cannot use different games.\n"); return false; } if (np_impl_magic() != ntohl(header[1])) { RARCH_ERR("Implementations differ, make sure you're using exact same libretro implementations and RetroArch version.\n"); return false; } if (core.retro_get_memory_size(RETRO_MEMORY_SAVE_RAM) != ntohl(header[2])) { RARCH_ERR("Content SRAM sizes do not correspond.\n"); return false; } if (!np_get_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to get nickname from client.\n"); return false; } /* Send SRAM data to our User 2. */ sram = core.retro_get_memory_data(RETRO_MEMORY_SAVE_RAM); sram_size = core.retro_get_memory_size(RETRO_MEMORY_SAVE_RAM); if (!socket_send_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("Failed to send SRAM data to client.\n"); return false; } if (!np_send_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to send nickname to client.\n"); return false; } #ifndef HAVE_SOCKET_LEGACY np_log_connection(&netplay->other_addr, 0, netplay->other_nick); #endif return true; }
static bool netplay_send_raw_cmd(netplay_t *netplay, uint32_t cmd, const void *data, size_t size) { uint32_t cmdbuf[2]; cmdbuf[0] = htonl(cmd); cmdbuf[1] = htonl(size); if (!socket_send_all_blocking(netplay->fd, cmdbuf, sizeof(cmdbuf), false)) return false; if (size > 0) if (!socket_send_all_blocking(netplay->fd, data, size, false)) return false; return true; }
static void net_http_send_str(int fd, bool *error, const char *text) { if (*error) return; if (!socket_send_all_blocking(fd, text, strlen(text), true)) *error = true; }
bool np_send_nickname(netplay_t *netplay, int fd) { uint8_t nick_size = strlen(netplay->nick); if (!socket_send_all_blocking(fd, &nick_size, sizeof(nick_size))) { RARCH_ERR("Failed to send nick size.\n"); return false; } if (!socket_send_all_blocking(fd, netplay->nick, nick_size)) { RARCH_ERR("Failed to send nick.\n"); return false; } return true; }
bool netplay_send_nickname(netplay_t *netplay, int fd) { uint8_t nick_size = strlen(netplay->nick); if (!socket_send_all_blocking(fd, &nick_size, sizeof(nick_size), false)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_SIZE)); return false; } if (!socket_send_all_blocking(fd, netplay->nick, nick_size, false)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME)); return false; } return true; }
bool netplay_send_info(netplay_t *netplay) { unsigned sram_size; retro_ctx_memory_info_t mem_info; char msg[512] = {0}; uint32_t *content_crc_ptr = NULL; void *sram = NULL; uint32_t header[3] = {0}; mem_info.id = RETRO_MEMORY_SAVE_RAM; core_get_memory(&mem_info); content_get_crc(&content_crc_ptr); header[0] = htonl(*content_crc_ptr); header[1] = htonl(netplay_impl_magic()); header[2] = htonl(mem_info.size); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false)) return false; if (!netplay_send_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_HOST)); return false; } /* Get SRAM data from User 1. */ sram = mem_info.data; sram_size = mem_info.size; if (!socket_receive_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST)); return false; } if (!netplay_get_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST)); return false; } snprintf(msg, sizeof(msg), "%s: \"%s\"", msg_hash_to_str(MSG_CONNECTED_TO), netplay->other_nick); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); return true; }
bool np_send_info(netplay_t *netplay) { unsigned sram_size; retro_ctx_memory_info_t mem_info; char msg[512] = {0}; uint32_t *content_crc_ptr = NULL; void *sram = NULL; uint32_t header[3] = {0}; mem_info.id = RETRO_MEMORY_SAVE_RAM; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); content_ctl(CONTENT_CTL_GET_CRC, &content_crc_ptr); header[0] = htonl(*content_crc_ptr); header[1] = htonl(np_impl_magic()); header[2] = htonl(mem_info.size); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header))) return false; if (!np_send_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to send nick to host.\n"); return false; } /* Get SRAM data from User 1. */ sram = mem_info.data; sram_size = mem_info.size; if (!socket_receive_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("Failed to receive SRAM data from host.\n"); return false; } if (!np_get_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to receive nick from host.\n"); return false; } snprintf(msg, sizeof(msg), "Connected to: \"%s\"", netplay->other_nick); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); return true; }
static void net_http_send_str(struct http_socket_state_t *sock_state, bool *error, const char *text) { if (*error) return; #ifdef HAVE_SSL if (sock_state->ssl) { if (!ssl_socket_send_all_blocking(sock_state->ssl_ctx, text, strlen(text), true)) *error = true; } else #endif { if (!socket_send_all_blocking(sock_state->fd, text, strlen(text), true)) *error = true; } }
bool np_send_info(netplay_t *netplay) { unsigned sram_size; char msg[512] = {0}; void *sram = NULL; uint32_t header[3] = {0}; global_t *global = global_get_ptr(); header[0] = htonl(global->content_crc); header[1] = htonl(np_impl_magic()); header[2] = htonl(core.retro_get_memory_size(RETRO_MEMORY_SAVE_RAM)); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header))) return false; if (!np_send_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to send nick to host.\n"); return false; } /* Get SRAM data from User 1. */ sram = core.retro_get_memory_data(RETRO_MEMORY_SAVE_RAM); sram_size = core.retro_get_memory_size(RETRO_MEMORY_SAVE_RAM); if (!socket_receive_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("Failed to receive SRAM data from host.\n"); return false; } if (!np_get_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to receive nick from host.\n"); return false; } snprintf(msg, sizeof(msg), "Connected to: \"%s\"", netplay->other_nick); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); return true; }
/** * netplay_send * * Queue the given data for sending. */ bool netplay_send(struct socket_buffer *sbuf, int sockfd, const void *buf, size_t len) { if (buf_remaining(sbuf) < len) { /* Need to force a blocking send */ if (!netplay_send_flush(sbuf, sockfd, true)) return false; } if (buf_remaining(sbuf) < len) { /* Can only be that this is simply too big for our buffer, in which case * we just need to do a blocking send */ if (!socket_send_all_blocking(sockfd, buf, len, false)) return false; return true; } /* Copy it into our buffer */ if (sbuf->bufsz - sbuf->end < len) { /* Half at a time */ size_t chunka = sbuf->bufsz - sbuf->end, chunkb = len - chunka; memcpy(sbuf->data + sbuf->end, buf, chunka); memcpy(sbuf->data, (const unsigned char *) buf + chunka, chunkb); sbuf->end = chunkb; } else { /* Straight in */ memcpy(sbuf->data + sbuf->end, buf, len); sbuf->end += len; } /* Flush what we can immediately */ return netplay_send_flush(sbuf, sockfd, false); }
/** * get_self_input_state: * @netplay : pointer to netplay object * * Grab our own input state and send this over the network. * * Returns: true (1) if successful, otherwise false (0). **/ static bool get_self_input_state(netplay_t *netplay) { uint32_t state[WORDS_PER_FRAME - 1] = {0, 0, 0}; struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr]; if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count)) return false; if (ptr->have_local) { /* We've already read this frame! */ return true; } if (!input_driver_is_libretro_input_blocked() && netplay->self_frame_count > 0) { unsigned i; settings_t *settings = config_get_ptr(); /* First frame we always give zero input since relying on * input from first frame screws up when we use -F 0. */ retro_input_state_t cb = netplay->cbs.state_cb; for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) { int16_t tmp = cb(settings->netplay.swap_input ? 0 : !netplay->port, RETRO_DEVICE_JOYPAD, 0, i); state[0] |= tmp ? 1 << i : 0; } for (i = 0; i < 2; i++) { int16_t tmp_x = cb(settings->netplay.swap_input ? 0 : !netplay->port, RETRO_DEVICE_ANALOG, i, 0); int16_t tmp_y = cb(settings->netplay.swap_input ? 0 : !netplay->port, RETRO_DEVICE_ANALOG, i, 1); state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); } } /* Here we construct the payload format: * frame { * uint32_t frame_number * uint32_t RETRO_DEVICE_JOYPAD state (top 16 bits zero) * uint32_t ANALOG state[0] * uint32_t ANALOG state[1] * } * * payload { * cmd (CMD_INPUT) * cmd_size (4 words) * frame * } */ netplay->packet_buffer[0] = htonl(NETPLAY_CMD_INPUT); netplay->packet_buffer[1] = htonl(WORDS_PER_FRAME * sizeof(uint32_t)); netplay->packet_buffer[2] = htonl(netplay->self_frame_count); netplay->packet_buffer[3] = htonl(state[0]); netplay->packet_buffer[4] = htonl(state[1]); netplay->packet_buffer[5] = htonl(state[2]); if (!netplay->spectate.enabled) /* Spectate sends in its own way */ { if (!socket_send_all_blocking(netplay->fd, netplay->packet_buffer, sizeof(netplay->packet_buffer), false)) { hangup(netplay); return false; } } memcpy(ptr->self_state, state, sizeof(state)); ptr->have_local = true; return true; }
/** * netplay_load_savestate * @netplay : pointer to netplay object * @serial_info : the savestate being loaded, NULL means "load it yourself" * @save : whether to save the provided serial_info into the frame buffer * * Inform Netplay of a savestate load and send it to the other side **/ void netplay_load_savestate(netplay_t *netplay, retro_ctx_serialize_info_t *serial_info, bool save) { uint32_t header[3]; retro_ctx_serialize_info_t tmp_serial_info; if (!netplay->has_connection) return; /* Record it in our own buffer */ if (save || !serial_info) { if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->self_ptr], netplay->self_frame_count)) { if (!serial_info) { tmp_serial_info.size = netplay->state_size; tmp_serial_info.data = netplay->buffer[netplay->self_ptr].state; if (!core_serialize(&tmp_serial_info)) return; tmp_serial_info.data_const = tmp_serial_info.data; serial_info = &tmp_serial_info; } else { if (serial_info->size <= netplay->state_size) { memcpy(netplay->buffer[netplay->self_ptr].state, serial_info->data_const, serial_info->size); } } } } /* We need to ignore any intervening data from the other side, and never rewind past this */ if (netplay->read_frame_count < netplay->self_frame_count) { netplay->read_ptr = netplay->self_ptr; netplay->read_frame_count = netplay->self_frame_count; } if (netplay->other_frame_count < netplay->self_frame_count) { netplay->other_ptr = netplay->self_ptr; netplay->other_frame_count = netplay->self_frame_count; } /* 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; /* And send it to the peer (FIXME: this is an ugly way to do this) */ header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE); header[1] = htonl(serial_info->size + sizeof(uint32_t)); header[2] = htonl(netplay->self_frame_count); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false)) { hangup(netplay); return; } if (!socket_send_all_blocking(netplay->fd, serial_info->data_const, serial_info->size, false)) { hangup(netplay); return; } }
static bool netplay_cmd_nak(netplay_t *netplay) { uint32_t cmd = htonl(NETPLAY_CMD_NAK); return socket_send_all_blocking(netplay->fd, &cmd, sizeof(cmd)); }
/** * netplay_send_flush * * Flush unsent data in the given socket buffer, blocking to do so if * requested. * * Returns false only on socket failures, true otherwise. */ bool netplay_send_flush(struct socket_buffer *sbuf, int sockfd, bool block) { ssize_t sent; if (buf_used(sbuf) == 0) return true; if (sbuf->end > sbuf->start) { /* Usual case: Everything's in order */ if (block) { if (!socket_send_all_blocking(sockfd, sbuf->data + sbuf->start, buf_used(sbuf), false)) return false; sbuf->start = sbuf->end = 0; } else { sent = socket_send_all_nonblocking(sockfd, sbuf->data + sbuf->start, buf_used(sbuf), false); if (sent < 0) return false; sbuf->start += sent; if (sbuf->start == sbuf->end) sbuf->start = sbuf->end = 0; } } else { /* Unusual case: Buffer overlaps break */ if (block) { if (!socket_send_all_blocking(sockfd, sbuf->data + sbuf->start, sbuf->bufsz - sbuf->start, false)) return false; sbuf->start = 0; return netplay_send_flush(sbuf, sockfd, true); } else { sent = socket_send_all_nonblocking(sockfd, sbuf->data + sbuf->start, sbuf->bufsz - sbuf->start, false); if (sent < 0) return false; sbuf->start += sent; if (sbuf->start >= sbuf->bufsz) { sbuf->start = 0; return netplay_send_flush(sbuf, sockfd, false); } } } return true; }
/** * netplay_pre_frame_spectate: * @netplay : pointer to netplay object * * Pre-frame for Netplay (spectate mode version). **/ static void netplay_spectate_pre_frame(netplay_t *netplay) { unsigned i; uint32_t *header; int new_fd, idx, bufsize; size_t header_size; struct sockaddr_storage their_addr; socklen_t addr_size; fd_set fds; struct timeval tmp_tv = {0}; if (!np_is_server(netplay)) return; FD_ZERO(&fds); FD_SET(netplay->fd, &fds); if (socket_select(netplay->fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0) return; if (!FD_ISSET(netplay->fd, &fds)) return; addr_size = sizeof(their_addr); new_fd = accept(netplay->fd, (struct sockaddr*)&their_addr, &addr_size); if (new_fd < 0) { RARCH_ERR("Failed to accept incoming spectator.\n"); return; } idx = -1; for (i = 0; i < MAX_SPECTATORS; i++) { if (netplay->spectate.fds[i] == -1) { idx = i; break; } } /* No vacant client streams :( */ if (idx == -1) { socket_close(new_fd); return; } if (!np_get_nickname(netplay, new_fd)) { RARCH_ERR("Failed to get nickname from client.\n"); socket_close(new_fd); return; } if (!np_send_nickname(netplay, new_fd)) { RARCH_ERR("Failed to send nickname to client.\n"); socket_close(new_fd); return; } header = np_bsv_header_generate(&header_size, np_impl_magic()); if (!header) { RARCH_ERR("Failed to generate BSV header.\n"); socket_close(new_fd); return; } bufsize = header_size; setsockopt(new_fd, SOL_SOCKET, SO_SNDBUF, (const char*)&bufsize, sizeof(int)); if (!socket_send_all_blocking(new_fd, header, header_size)) { RARCH_ERR("Failed to send header to client.\n"); socket_close(new_fd); free(header); return; } free(header); netplay->spectate.fds[idx] = new_fd; #ifndef HAVE_SOCKET_LEGACY np_log_connection(&their_addr, idx, netplay->other_nick); #endif }
bool netplay_get_info(netplay_t *netplay) { unsigned sram_size; uint32_t header[3]; retro_ctx_memory_info_t mem_info; uint32_t *content_crc_ptr = NULL; const void *sram = NULL; if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header))) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT)); return false; } content_get_crc(&content_crc_ptr); if (*content_crc_ptr != ntohl(header[0])) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_CONTENT_CRC32S_DIFFER)); return false; } if (netplay_impl_magic() != ntohl(header[1])) { RARCH_ERR("Implementations differ, make sure you're using exact same " "libretro implementations and RetroArch version.\n"); return false; } mem_info.id = RETRO_MEMORY_SAVE_RAM; core_get_memory(&mem_info); if (mem_info.size != ntohl(header[2])) { RARCH_ERR("Content SRAM sizes do not correspond.\n"); return false; } if (!netplay_get_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT)); return false; } /* Send SRAM data to our User 2. */ sram = mem_info.data; sram_size = mem_info.size; if (!socket_send_all_blocking(netplay->fd, sram, sram_size, false)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_SRAM_DATA_TO_CLIENT)); return false; } if (!netplay_send_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_CLIENT)); return false; } #ifndef HAVE_SOCKET_LEGACY netplay_log_connection(&netplay->other_addr, 0, netplay->other_nick); #endif return true; }
bool np_get_info(netplay_t *netplay) { unsigned sram_size; uint32_t header[3]; retro_ctx_memory_info_t mem_info; uint32_t *content_crc_ptr = NULL; const void *sram = NULL; if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header))) { RARCH_ERR("Failed to receive header from client.\n"); return false; } content_ctl(CONTENT_CTL_GET_CRC, &content_crc_ptr); if (*content_crc_ptr != ntohl(header[0])) { RARCH_ERR("Content CRC32s differ. Cannot use different games.\n"); return false; } if (np_impl_magic() != ntohl(header[1])) { RARCH_ERR("Implementations differ, make sure you're using exact same " "libretro implementations and RetroArch version.\n"); return false; } mem_info.id = RETRO_MEMORY_SAVE_RAM; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); if (mem_info.size != ntohl(header[2])) { RARCH_ERR("Content SRAM sizes do not correspond.\n"); return false; } if (!np_get_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to get nickname from client.\n"); return false; } /* Send SRAM data to our User 2. */ sram = mem_info.data; sram_size = mem_info.size; if (!socket_send_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("Failed to send SRAM data to client.\n"); return false; } if (!np_send_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to send nickname to client.\n"); return false; } #ifndef HAVE_SOCKET_LEGACY np_log_connection(&netplay->other_addr, 0, netplay->other_nick); #endif return true; }
bool netplay_handshake(netplay_t *netplay) { size_t i; uint32_t local_pmagic, remote_pmagic; unsigned sram_size, remote_sram_size; retro_ctx_memory_info_t mem_info; char msg[512]; uint32_t *content_crc_ptr = NULL; void *sram = NULL; uint32_t header[5] = {0}; bool is_server = netplay->is_server; int compression = 0; msg[0] = '\0'; mem_info.id = RETRO_MEMORY_SAVE_RAM; core_get_memory(&mem_info); content_get_crc(&content_crc_ptr); local_pmagic = netplay_platform_magic(); header[0] = htonl(*content_crc_ptr); 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; if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header))) { strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT), sizeof(msg)); goto error; } if (*content_crc_ptr != ntohl(header[0])) { strlcpy(msg, msg_hash_to_str(MSG_CONTENT_CRC32S_DIFFER), sizeof(msg)); goto error; } if (netplay_impl_magic() != ntohl(header[1])) { strlcpy(msg, "Implementations differ. Make sure you're using exact same " "libretro implementations and RetroArch version.", sizeof(msg)); goto error; } /* Some cores only report the correct sram size late, so we can't actually * error out if the sram size seems wrong. */ sram_size = mem_info.size; remote_sram_size = ntohl(header[2]); if (sram_size != 0 && remote_sram_size != 0 && sram_size != remote_sram_size) { RARCH_WARN("Content SRAM sizes do not correspond.\n"); } /* We only care about platform magic if our core is quirky */ remote_pmagic = ntohl(header[3]); if ((netplay->quirks & NETPLAY_QUIRK_ENDIAN_DEPENDENT) && netplay_endian_mismatch(local_pmagic, remote_pmagic)) { RARCH_ERR("Endianness mismatch with an endian-sensitive core.\n"); strlcpy(msg, "This core does not support inter-architecture netplay " "between these systems.", sizeof(msg)); goto error; } if ((netplay->quirks & NETPLAY_QUIRK_PLATFORM_DEPENDENT) && (local_pmagic != remote_pmagic)) { RARCH_ERR("Platform mismatch with a platform-sensitive core.\n"); strlcpy(msg, "This core does not support inter-architecture netplay.", sizeof(msg)); goto error; } /* Clear any existing compression */ if (netplay->compression_stream) netplay->compression_backend->stream_free(netplay->compression_stream); if (netplay->decompression_stream) netplay->decompression_backend->stream_free(netplay->decompression_stream); /* Check what compression is supported */ compression = ntohl(header[4]); compression &= NETPLAY_COMPRESSION_SUPPORTED; if (compression & NETPLAY_COMPRESSION_ZLIB) { netplay->compression_backend = trans_stream_get_zlib_deflate_backend(); if (!netplay->compression_backend) netplay->compression_backend = trans_stream_get_pipe_backend(); } else { netplay->compression_backend = trans_stream_get_pipe_backend(); } netplay->decompression_backend = netplay->compression_backend->reverse; /* Allocate our compression stream */ netplay->compression_stream = netplay->compression_backend->stream_new(); netplay->decompression_stream = netplay->decompression_backend->stream_new(); if (!netplay->compression_stream || !netplay->decompression_stream) { RARCH_ERR("Failed to allocate compression transcoder!\n"); return false; } /* Client sends nickname first, server replies with nickname */ if (!is_server) { if (!netplay_send_nickname(netplay, netplay->fd)) { strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_HOST), sizeof(msg)); goto error; } } if (!netplay_get_nickname(netplay, netplay->fd)) { if (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)); goto error; } if (is_server) { if (!netplay_send_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_CLIENT)); return false; } } /* Server sends SRAM, client receives */ if (is_server) { /* Send SRAM data to the client */ sram = mem_info.data; if (!socket_send_all_blocking(netplay->fd, sram, sram_size, false)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_SRAM_DATA_TO_CLIENT)); return false; } } else { /* Get SRAM data from User 1. */ if (sram_size != 0 && sram_size == remote_sram_size) { sram = mem_info.data; if (!socket_receive_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST)); 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) { if (!socket_receive_all_blocking(netplay->fd, &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)); return false; } if (remote_sram_size > sizeof(uint32_t)) remote_sram_size -= sizeof(uint32_t); else remote_sram_size = 0; } } } /* Reset our frame count so it's consistent with the server */ netplay->self_frame_count = netplay->other_frame_count = 0; netplay->read_frame_count = 1; for (i = 0; i < netplay->buffer_size; i++) { netplay->buffer[i].used = false; if (i == netplay->self_ptr) { netplay_delta_frame_ready(netplay, &netplay->buffer[i], 0); netplay->buffer[i].have_remote = true; netplay->other_ptr = i; netplay->read_ptr = NEXT_PTR(i); } else { netplay->buffer[i].used = false; } } if (is_server) { netplay_log_connection(&netplay->other_addr, 0, netplay->other_nick); } else { snprintf(msg, sizeof(msg), "%s: \"%s\"", msg_hash_to_str(MSG_CONNECTED_TO), netplay->other_nick); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); } return true; error: if (msg[0]) { RARCH_ERR("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); } return false; }