Exemple #1
0
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;
}
Exemple #2
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_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;
}
Exemple #5
0
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;
}
Exemple #9
0
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;
}
Exemple #11
0
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;
}
Exemple #13
0
/**
 * 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);
}
Exemple #14
0
/**
 * 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;
}
Exemple #15
0
/**
 * 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;
   }
}
Exemple #16
0
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));
}
Exemple #17
0
/**
 * 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;
}
Exemple #18
0
/**
 * 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
}
Exemple #19
0
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;
}
Exemple #21
0
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;
}