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 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; }
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; }