/*****************************************************************************\ Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. This file is licensed under the Snes9x License. For further information, consult the LICENSE file in the root directory. \*****************************************************************************/ #ifdef NETPLAY_SUPPORT #include #include #include #include #include #include "snes9x.h" #include "controls.h" #ifdef __WIN32__ #include #include #include "win32/wsnes9x.h" #define ioctl ioctlsocket #define close(h) if(h){closesocket(h);} #define read(a,b,c) recv(a, b, c, 0) #define write(a,b,c) send(a, b, c, 0) #else #include #include #include #include #include #include #include #include #include #include #ifdef __SVR4 #include #endif #endif #ifdef USE_THREADS #include #include #include #endif #include "memmap.h" #include "netplay.h" #include "snapshot.h" #include "display.h" void S9xNPClientLoop (void *); bool8 S9xNPLoadROM (uint32 len); bool8 S9xNPLoadROMDialog (const char *); bool8 S9xNPGetROMImage (uint32 len); void S9xNPGetSRAMData (uint32 len); void S9xNPGetFreezeFile (uint32 len); unsigned long START = 0; bool8 S9xNPConnect (); bool8 S9xNPConnectToServer (const char *hostname, int port, const char *rom_name) { if (!S9xNPInitialise ()) return (FALSE); S9xNPDisconnect (); NetPlay.MySequenceNum = 0; NetPlay.ServerSequenceNum = 0; NetPlay.Connected = FALSE; NetPlay.Abort = FALSE; NetPlay.Player = 0; NetPlay.Paused = FALSE; NetPlay.PercentageComplete = 0; NetPlay.Socket = 0; if (NetPlay.ServerHostName) free ((char *) NetPlay.ServerHostName); NetPlay.ServerHostName = strdup (hostname); if (NetPlay.ROMName) free ((char *) NetPlay.ROMName); NetPlay.ROMName = strdup (rom_name); NetPlay.Port = port; NetPlay.PendingWait4Sync = FALSE; #ifdef __WIN32__ if (GUI.ClientSemaphore == NULL) GUI.ClientSemaphore = CreateSemaphore (NULL, 0, NP_JOYPAD_HIST_SIZE, NULL); if (NetPlay.ReplyEvent == NULL) NetPlay.ReplyEvent = CreateEvent (NULL, FALSE, FALSE, NULL); _beginthread (S9xNPClientLoop, 0, NULL); return (TRUE); #endif return S9xNPConnect(); } bool8 S9xNPConnect () { struct sockaddr_in address; struct hostent *hostinfo; unsigned int addr; address.sin_family = AF_INET; address.sin_port = htons (NetPlay.Port); #ifdef NP_DEBUG printf ("CLIENT: Looking up server's hostname (%s) @%ld\n", NetPlay.ServerHostName, S9xGetMilliTime () - START); #endif S9xNPSetAction ("Looking up server's hostname..."); if ((int) (addr = inet_addr (NetPlay.ServerHostName)) == -1) { if ((hostinfo = gethostbyname (NetPlay.ServerHostName))) { memcpy ((char *)&address.sin_addr, hostinfo->h_addr, hostinfo->h_length); } else { S9xNPSetError ("\ Unable to look up server's IP address from hostname.\n\n\ Unknown hostname or may be your nameserver isn't set\n\ up correctly?"); return (FALSE); } } else { memcpy ((char *)&address.sin_addr, &addr, sizeof (addr)); } #ifdef NP_DEBUG printf ("CLIENT: Creating socket @%ld\n", S9xGetMilliTime () - START); #endif S9xNPSetAction ("Creating network socket..."); if ((NetPlay.Socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) { S9xNPSetError ("Creating network socket failed."); return (FALSE); } #ifdef NP_DEBUG printf ("CLIENT: Trying to connect to server @%ld...\n", S9xGetMilliTime () - START); #endif S9xNPSetAction ("Trying to connect to Snes9x server..."); if (connect (NetPlay.Socket, (struct sockaddr *) &address, sizeof (address)) < 0) { char buf [100]; #ifdef __WIN32__ if (WSAGetLastError () == WSAECONNREFUSED) #else if (errno == ECONNREFUSED) #endif { S9xNPSetError ("\ Connection to remote server socket refused:\n\n\ Is there actually a Snes9x NetPlay server running\n\ on the remote machine on this port?"); } else { sprintf (buf, "Connection to server failed with error number %d", #ifdef __WIN32__ WSAGetLastError () #else errno #endif ); S9xNPSetError(buf); S9xNPDisconnect (); } return (FALSE); } NetPlay.Connected = TRUE; #ifdef NP_DEBUG printf ("CLIENT: Sending 'HELLO' message @%ld...\n", S9xGetMilliTime () - START); #endif S9xNPSetAction ("Sending 'HELLO' message..."); /* Send the server a HELLO packet*/ int len = 7 + 4 + strlen (NetPlay.ROMName) + 1; uint8 *tmp = new uint8 [len]; uint8 *ptr = tmp; *ptr++ = NP_CLNT_MAGIC; *ptr++ = NetPlay.MySequenceNum++; *ptr++ = NP_CLNT_HELLO; WRITE_LONG (ptr, len); ptr += 4; #ifdef __WIN32__ uint32 ft = Settings.FrameTime; WRITE_LONG (ptr, ft); #else WRITE_LONG (ptr, Settings.FrameTime); #endif ptr += 4; strcpy ((char *) ptr, NetPlay.ROMName); if (!S9xNPSendData (NetPlay.Socket, tmp, len)) { S9xNPSetError ("Sending 'HELLO' message failed."); S9xNPDisconnect (); delete[] tmp; return (FALSE); } delete[] tmp; #ifdef NP_DEBUG printf ("CLIENT: Waiting for 'WELCOME' reply from server @%ld...\n", S9xGetMilliTime () - START); #endif S9xNPSetAction ("Waiting for 'HELLO' reply from server..."); uint8 header [7]; if (!S9xNPGetData (NetPlay.Socket, header, 7) || header [0] != NP_SERV_MAGIC || header [1] != 0 || (header [2] & 0x1f) != NP_SERV_HELLO) { S9xNPSetError ("Error in 'HELLO' reply packet received from server."); S9xNPDisconnect (); return (FALSE); } #ifdef NP_DEBUG printf ("CLIENT: Got 'WELCOME' reply @%ld\n", S9xGetMilliTime () - START); #endif len = READ_LONG (&header [3]); if (len > 256) { S9xNPSetError ("Error in 'HELLO' reply packet received from server."); S9xNPDisconnect (); return (FALSE); } uint8 *data = new uint8 [len]; if (!S9xNPGetData (NetPlay.Socket, data, len - 7)) { S9xNPSetError ("Error in 'HELLO' reply packet received from server."); delete[] data; S9xNPDisconnect (); return (FALSE); } if (data [0] != NP_VERSION) { S9xNPSetError ("\ The Snes9x NetPlay server implements a different\n\ version of the protocol. Disconnecting."); delete[] data; S9xNPDisconnect (); return (FALSE); } NetPlay.FrameCount = READ_LONG (&data [2]); if (!(header [2] & 0x80) && strcmp ((char *) data + 4 + 2, NetPlay.ROMName) != 0) { if (!S9xNPLoadROMDialog ((char *) data + 4 + 2)) { delete[] data; S9xNPDisconnect (); return (FALSE); } } NetPlay.Player = data [1]; delete[] data; NetPlay.PendingWait4Sync = TRUE; Settings.NetPlay = TRUE; S9xNPResetJoypadReadPos (); NetPlay.ServerSequenceNum = 1; #ifdef NP_DEBUG printf ("CLIENT: Sending 'READY' to server @%ld...\n", S9xGetMilliTime () - START); #endif S9xNPSetAction ("Sending 'READY' to the server..."); return (S9xNPSendReady ((header [2] & 0x80) ? NP_CLNT_WAITING_FOR_ROM_IMAGE : NP_CLNT_READY)); } bool8 S9xNPSendReady (uint8 op) { uint8 ready [7]; uint8 *ptr = ready; *ptr++ = NP_CLNT_MAGIC; *ptr++ = NetPlay.MySequenceNum++; *ptr++ = op; WRITE_LONG (ptr, 7); ptr += 4; if (!S9xNPSendData (NetPlay.Socket, ready, 7)) { S9xNPDisconnect (); S9xNPSetError ("Sending 'READY' message failed."); return (FALSE); } return (TRUE); } bool8 S9xNPSendPause (bool8 paused) { #ifdef NP_DEBUG printf ("CLIENT: Pause - %s @%ld\n", paused ? "YES" : "NO", S9xGetMilliTime () - START); #endif uint8 pause [7]; uint8 *ptr = pause; *ptr++ = NP_CLNT_MAGIC; *ptr++ = NetPlay.MySequenceNum++; *ptr++ = NP_CLNT_PAUSE | (paused ? 0x80 : 0); WRITE_LONG (ptr, 7); ptr += 4; if (!S9xNPSendData (NetPlay.Socket, pause, 7)) { S9xNPSetError ("Sending 'PAUSE' message failed."); S9xNPDisconnect (); return (FALSE); } return (TRUE); } #ifdef __WIN32__ void S9xNPClientLoop (void *) { NetPlay.Waiting4EmulationThread = FALSE; if (S9xNPConnect ()) { S9xClearPause (PAUSE_NETPLAY_CONNECT); while (NetPlay.Connected) { if (S9xNPWaitForHeartBeat ()) { LONG prev; if (!ReleaseSemaphore (GUI.ClientSemaphore, 1, &prev)) { #ifdef NP_DEBUG printf ("CLIENT: ReleaseSemaphore failed - already hit max count (%d) %ld\n", NP_JOYPAD_HIST_SIZE, S9xGetMilliTime () - START); #endif S9xNPSetWarning ("NetPlay: Client may be out of sync with server."); } else { if (!NetPlay.Waiting4EmulationThread && prev == (int) NetPlay.MaxBehindFrameCount) { NetPlay.Waiting4EmulationThread = TRUE; S9xNPSendPause (TRUE); } } } else S9xNPDisconnect (); } } else { S9xClearPause (PAUSE_NETPLAY_CONNECT); } #ifdef NP_DEBUG printf ("CLIENT: Client thread exiting @%ld\n", S9xGetMilliTime () - START); #endif } #endif bool8 S9xNPCheckForHeartBeat (uint32 time_msec) { fd_set read_fds; struct timeval timeout; int res; int i; int max_fd = NetPlay.Socket; FD_ZERO (&read_fds); FD_SET (NetPlay.Socket, &read_fds); timeout.tv_sec = 0; timeout.tv_usec = time_msec * 1000; res = select (max_fd + 1, &read_fds, NULL, NULL, &timeout); i = (res > 0 && FD_ISSET(NetPlay.Socket, &read_fds)); #if defined(NP_DEBUG) && NP_DEBUG >= 4 printf ("CLIENT: S9xCheckForHeartBeat %s @%ld\n", (i?"successful":"still waiting"), S9xGetMilliTime () - START); #endif return i; } bool8 S9xNPWaitForHeartBeatDelay (uint32 time_msec) { if (!S9xNPCheckForHeartBeat(time_msec)) return FALSE; if (!S9xNPWaitForHeartBeat()) { S9xNPDisconnect(); return FALSE; } return TRUE; } bool8 S9xNPWaitForHeartBeat () { uint8 header [3 + 4 + 4 * 5]; while (S9xNPGetData (NetPlay.Socket, header, 3 + 4)) { if (header [0] != NP_SERV_MAGIC) { S9xNPSetError ("Bad magic value from server while waiting for heart-beat message\n"); S9xNPDisconnect (); return (FALSE); } if (header [1] != NetPlay.ServerSequenceNum) { char buf [200]; sprintf (buf, "Unexpected message sequence number from server, expected %d, got %d\n", NetPlay.ServerSequenceNum, header [1]); S9xNPSetWarning (buf); NetPlay.ServerSequenceNum = header [1] + 1; } else NetPlay.ServerSequenceNum++; if ((header [2] & 0x1f) == NP_SERV_JOYPAD) { // Top 2 bits + 1 of opcode is joypad data count. int num = (header [2] >> 6) + 1; if (num) { if (!S9xNPGetData (NetPlay.Socket, header + 3 + 4, num * 4)) { S9xNPSetError ("Error while receiving 'JOYPAD' message."); S9xNPDisconnect (); return (FALSE); } } NetPlay.Frame [NetPlay.JoypadWriteInd] = READ_LONG (&header [3]); int i; for (i = 0; i < num; i++) NetPlay.Joypads [NetPlay.JoypadWriteInd][i] = READ_LONG (&header [3 + 4 + i * sizeof (uint32)]); for (i = 0; i < NP_MAX_CLIENTS; i++) NetPlay.JoypadsReady [NetPlay.JoypadWriteInd][i] = TRUE; NetPlay.Paused = (header [2] & 0x20) != 0; NetPlay.JoypadWriteInd = (NetPlay.JoypadWriteInd + 1) % NP_JOYPAD_HIST_SIZE; if (NetPlay.JoypadWriteInd != (NetPlay.JoypadReadInd + 1) % NP_JOYPAD_HIST_SIZE) { //printf ("(%d)", (NetPlay.JoypadWriteInd - NetPlay.JoypadReadInd) % NP_JOYPAD_HIST_SIZE); fflush (stdout); } //printf ("CLIENT: HB: @%d\n", S9xGetMilliTime () - START); return (TRUE); } else { uint32 len = READ_LONG (&header [3]); switch (header [2] & 0x1f) { case NP_SERV_RESET: #ifdef NP_DEBUG printf ("CLIENT: RESET received @%ld\n", S9xGetMilliTime () - START); #endif S9xNPDiscardHeartbeats (); S9xReset (); NetPlay.FrameCount = READ_LONG (&header [3]); S9xNPResetJoypadReadPos (); S9xNPSendReady (); break; case NP_SERV_PAUSE: NetPlay.Paused = (header [2] & 0x20) != 0; if (NetPlay.Paused) S9xNPSetWarning("CLIENT: Server has paused."); else S9xNPSetWarning("CLIENT: Server has resumed."); break; case NP_SERV_JOYPAD_SWAP: #ifdef NP_DEBUG printf("CLIENT: Joypad Swap received @%ld\n", S9xGetMilliTime() - START); #endif S9xApplyCommand(S9xGetCommandT("SwapJoypads"), 1, 1); break; case NP_SERV_LOAD_ROM: #ifdef NP_DEBUG printf ("CLIENT: LOAD_ROM received @%ld\n", S9xGetMilliTime () - START); #endif S9xNPDiscardHeartbeats (); if (S9xNPLoadROM (len - 7)) S9xNPSendReady (NP_CLNT_LOADED_ROM); break; case NP_SERV_ROM_IMAGE: #ifdef NP_DEBUG printf ("CLIENT: ROM_IMAGE received @%ld\n", S9xGetMilliTime () - START); #endif S9xNPDiscardHeartbeats (); if (S9xNPGetROMImage (len - 7)) S9xNPSendReady (NP_CLNT_RECEIVED_ROM_IMAGE); break; case NP_SERV_SRAM_DATA: #ifdef NP_DEBUG printf ("CLIENT: SRAM_DATA received @%ld\n", S9xGetMilliTime () - START); #endif S9xNPDiscardHeartbeats (); S9xNPGetSRAMData (len - 7); break; case NP_SERV_FREEZE_FILE: #ifdef NP_DEBUG printf ("CLIENT: FREEZE_FILE received @%ld\n", S9xGetMilliTime () - START); #endif S9xNPDiscardHeartbeats (); S9xNPGetFreezeFile (len - 7); S9xNPResetJoypadReadPos (); S9xNPSendReady (); break; default: #ifdef NP_DEBUG printf ("CLIENT: UNKNOWN received @%ld\n", S9xGetMilliTime () - START); #endif S9xNPDisconnect (); return (FALSE); } } } S9xNPDisconnect (); return (FALSE); } bool8 S9xNPLoadROMDialog (const char *rom_name) { NetPlay.Answer = FALSE; #ifdef __WIN32__ ResetEvent (NetPlay.ReplyEvent); #ifdef NP_DEBUG printf ("CLIENT: Asking GUI thread to open ROM load dialog...\n"); #endif PostMessage (GUI.hWnd, WM_USER + 3, (WPARAM) rom_name, (LPARAM) rom_name); #ifdef NP_DEBUG printf ("CLIENT: Waiting for reply from GUI thread...\n"); #endif WaitForSingleObject (NetPlay.ReplyEvent, INFINITE); #ifdef NP_DEBUG printf ("CLIENT: Got reply from GUI thread (%d)\n", NetPlay.Answer); #endif #else NetPlay.Answer = TRUE; #endif return (NetPlay.Answer); } bool8 S9xNPLoadROM (uint32 len) { uint8 *data = new uint8 [len]; S9xNPSetAction ("Receiving ROM name..."); if (!S9xNPGetData (NetPlay.Socket, data, len)) { S9xNPSetError ("Error while receiving ROM name."); delete[] data; S9xNPDisconnect (); return (FALSE); } S9xNPSetAction ("Opening LoadROM dialog..."); if (!S9xNPLoadROMDialog ((char *) data)) { S9xNPSetError ("Disconnected from NetPlay server because you are playing a different game!"); delete[] data; S9xNPDisconnect (); return (FALSE); } delete[] data; return (TRUE); } bool8 S9xNPGetROMImage (uint32 len) { uint8 rom_info [5]; S9xNPSetAction ("Receiving ROM information..."); if (!S9xNPGetData (NetPlay.Socket, rom_info, 5)) { S9xNPSetError ("Error while receiving ROM information."); S9xNPDisconnect (); return (FALSE); } uint32 CalculatedSize = READ_LONG (&rom_info [1]); #ifdef NP_DEBUG printf ("CLIENT: Hi-ROM: %s, Size: %04x\n", rom_info [0] ? "Y" : "N", CalculatedSize); #endif if (CalculatedSize + 5 >= len || CalculatedSize >= CMemory::MAX_ROM_SIZE) { S9xNPSetError ("Size error in ROM image data received from server."); S9xNPDisconnect (); return (FALSE); } Memory.HiROM = rom_info [0]; Memory.LoROM = !Memory.HiROM; Memory.HeaderCount = 0; Memory.CalculatedSize = CalculatedSize; // Load up ROM image #ifdef NP_DEBUG printf ("CLIENT: Receiving ROM image @%ld...\n", S9xGetMilliTime () - START); #endif S9xNPSetAction ("Receiving ROM image..."); if (!S9xNPGetData (NetPlay.Socket, Memory.ROM, Memory.CalculatedSize)) { S9xNPSetError ("Error while receiving ROM image from server."); Settings.StopEmulation = TRUE; S9xNPDisconnect (); return (FALSE); } #ifdef NP_DEBUG printf ("CLIENT: Receiving ROM filename @%ld...\n", S9xGetMilliTime () - START); #endif S9xNPSetAction ("Receiving ROM filename..."); uint32 filename_len = len - Memory.CalculatedSize - 5; if (filename_len > PATH_MAX || !S9xNPGetData (NetPlay.Socket, (uint8 *) Memory.ROMFilename, filename_len)) { S9xNPSetError ("Error while receiving ROM filename from server."); S9xNPDisconnect (); Settings.StopEmulation = TRUE; return (FALSE); } Memory.InitROM (); S9xReset (); S9xNPResetJoypadReadPos (); Settings.StopEmulation = FALSE; #ifdef __WIN32__ PostMessage (GUI.hWnd, WM_NULL, 0, 0); #endif return (TRUE); } void S9xNPGetSRAMData (uint32 len) { if (len > 0x70000) { S9xNPSetError ("Length error in S-RAM data received from server."); S9xNPDisconnect (); return; } S9xNPSetAction ("Receiving S-RAM data..."); if (len > 0 && !S9xNPGetData (NetPlay.Socket, Memory.SRAM, len)) { S9xNPSetError ("Error while receiving S-RAM data from server."); S9xNPDisconnect (); } S9xNPSetAction ("", TRUE); } void S9xNPGetFreezeFile (uint32 len) { uint8 frame_count [4]; #ifdef NP_DEBUG printf ("CLIENT: Receiving freeze file information @%ld...\n", S9xGetMilliTime () - START); #endif S9xNPSetAction ("Receiving freeze file information..."); if (!S9xNPGetData (NetPlay.Socket, frame_count, 4)) { S9xNPSetError ("Error while receiving freeze file information from server."); S9xNPDisconnect (); return; } NetPlay.FrameCount = READ_LONG (frame_count); #ifdef NP_DEBUG printf ("CLIENT: Receiving freeze file @%ld...\n", S9xGetMilliTime () - START); #endif S9xNPSetAction ("Receiving freeze file..."); uint8 *data = new uint8 [len]; if (!S9xNPGetData (NetPlay.Socket, data, len - 4)) { S9xNPSetError ("Error while receiving freeze file from server."); S9xNPDisconnect (); delete[] data; return; } S9xNPSetAction ("", TRUE); //FIXME: Setting umask here wouldn't hurt. FILE *file; #ifdef HAVE_MKSTEMP int fd; char fname[] = "/tmp/snes9x_fztmpXXXXXX"; if ((fd = mkstemp(fname)) >= 0) { if ((file = fdopen (fd, "wb"))) #else char fname [L_tmpnam]; if (tmpnam (fname)) { if ((file = fopen (fname, "wb"))) #endif { if (fwrite (data, 1, len, file) == len) { fclose(file); #ifndef __WIN32__ /* We need .s96 extension, else .s96 is addded by unix code */ char buf[PATH_MAX +1 ]; strncpy(buf, fname, PATH_MAX); strcat(buf, ".s96"); rename(fname, buf); if (!S9xUnfreezeGame (buf)) #else if (!S9xUnfreezeGame (fname)) #endif S9xNPSetError ("Unable to load freeze file just received."); } else { S9xNPSetError ("Failed to write to temporary freeze file."); fclose(file); } } else S9xNPSetError ("Failed to create temporary freeze file."); remove (fname); } else S9xNPSetError ("Unable to get name for temporary freeze file."); delete[] data; } uint32 S9xNPGetJoypad (int which1) { if (Settings.NetPlay && which1 < 8) { #ifdef NP_DEBUG if(!NetPlay.JoypadsReady [NetPlay.JoypadReadInd][which1]) { S9xNPSetWarning ("Missing input from server!"); } #endif NetPlay.JoypadsReady [NetPlay.JoypadReadInd][which1] = FALSE; return (NetPlay.Joypads [NetPlay.JoypadReadInd][which1]); } return (0); } void S9xNPStepJoypadHistory () { if ((NetPlay.JoypadReadInd + 1) % NP_JOYPAD_HIST_SIZE != NetPlay.JoypadWriteInd) { NetPlay.JoypadReadInd = (NetPlay.JoypadReadInd + 1) % NP_JOYPAD_HIST_SIZE; if (NetPlay.FrameCount != NetPlay.Frame [NetPlay.JoypadReadInd]) { S9xNPSetWarning ("This Snes9x session may be out of sync with the server."); #ifdef NP_DEBUG printf ("*** CLIENT: client out of sync with server (%d, %d) @%ld\n", NetPlay.FrameCount, NetPlay.Frame [NetPlay.JoypadReadInd], S9xGetMilliTime () - START); #endif } } else { #ifdef NP_DEBUG printf ("*** CLIENT: S9xNPStepJoypadHistory NOT OK@%ld\n", S9xGetMilliTime () - START); #endif } } void S9xNPResetJoypadReadPos () { #ifdef NP_DEBUG printf ("CLIENT: ResetJoyReadPos @%ld\n", S9xGetMilliTime () - START); fflush (stdout); #endif NetPlay.JoypadWriteInd = 0; NetPlay.JoypadReadInd = NP_JOYPAD_HIST_SIZE - 1; for (int h = 0; h < NP_JOYPAD_HIST_SIZE; h++) memset ((void *) &NetPlay.Joypads [h], 0, sizeof (NetPlay.Joypads [0])); for (int h = 0; h < NP_JOYPAD_HIST_SIZE; h++) memset ((void *) &NetPlay.JoypadsReady [h], 0, sizeof (NetPlay.JoypadsReady [0])); } bool8 S9xNPSendJoypadUpdate (uint32 joypad) { uint8 data [7]; uint8 *ptr = data; *ptr++ = NP_CLNT_MAGIC; *ptr++ = NetPlay.MySequenceNum++; *ptr++ = NP_CLNT_JOYPAD; joypad |= 0x80000000; WRITE_LONG (ptr, joypad); if (!S9xNPSendData (NetPlay.Socket, data, 7)) { S9xNPSetError ("Error while sending joypad data server."); S9xNPDisconnect (); return (FALSE); } return (TRUE); } void S9xNPDisconnect () { close (NetPlay.Socket); NetPlay.Socket = -1; NetPlay.Connected = FALSE; Settings.NetPlay = FALSE; } bool8 S9xNPSendData (int socket, const uint8 *data, int length) { int len = length; const uint8 *ptr = data; NetPlay.PercentageComplete = 0; do { if (NetPlay.Abort) return (FALSE); int num_bytes = len; // Write the data in small chunks, allowing this thread to spot an // abort request from another thread. if (num_bytes > 512) num_bytes = 512; int sent = write (socket, (char *) ptr, num_bytes); if (sent < 0) { if (errno == EINTR #ifdef EAGAIN || errno == EAGAIN #endif #ifdef EWOULDBLOCK || errno == EWOULDBLOCK #endif ) { #ifdef NP_DEBUG printf ("CLIENT: EINTR, EAGAIN or EWOULDBLOCK while sending data @%ld\n", S9xGetMilliTime () - START); #endif continue; } return (FALSE); } else if (sent == 0) return (FALSE); len -= sent; ptr += sent; NetPlay.PercentageComplete = (uint8) (((length - len) * 100) / length); } while (len > 0); return (TRUE); } bool8 S9xNPGetData (int socket, uint8 *data, int length) { int len = length; uint8 *ptr = data; int chunk = length / 50; if (chunk < 1024) chunk = 1024; NetPlay.PercentageComplete = 0; do { if (NetPlay.Abort) return (FALSE); int num_bytes = len; // Read the data in small chunks, allowing this thread to spot an // abort request from another thread. if (num_bytes > chunk) num_bytes = chunk; int got = read (socket, (char *) ptr, num_bytes); if (got < 0) { if (errno == EINTR #ifdef EAGAIN || errno == EAGAIN #endif #ifdef EWOULDBLOCK || errno == EWOULDBLOCK #endif #ifdef WSAEWOULDBLOCK || errno == WSAEWOULDBLOCK #endif ) { #ifdef NP_DEBUG printf ("CLIENT: EINTR, EAGAIN or EWOULDBLOCK while receiving data @%ld\n", S9xGetMilliTime () - START); #endif continue; } #ifdef WSAEMSGSIZE if (errno != WSAEMSGSIZE) return (FALSE); else { got = num_bytes; #ifdef NP_DEBUG printf ("CLIENT: WSAEMSGSIZE, actual bytes %d while receiving data @%ld\n", got, S9xGetMilliTime () - START); #endif } #else return (FALSE); #endif } else if (got == 0) return (FALSE); len -= got; ptr += got; if (!Settings.NetPlayServer && length > 1024) { NetPlay.PercentageComplete = (uint8) (((length - len) * 100) / length); #ifdef __WIN32__ PostMessage (GUI.hWnd, WM_USER, NetPlay.PercentageComplete, NetPlay.PercentageComplete); Sleep (0); #endif } } while (len > 0); return (TRUE); } bool8 S9xNPInitialise () { #ifdef __WIN32__ static bool8 initialised = FALSE; if (!initialised) { initialised = TRUE; WSADATA data; #ifdef NP_DEBUG START = S9xGetMilliTime (); printf ("CLIENT/SERVER: Initialising WinSock @%ld\n", S9xGetMilliTime () - START); #endif S9xNPSetAction ("Initialising Windows sockets interface..."); if (WSAStartup (MAKEWORD (1, 0), &data) != 0) { S9xNPSetError ("Call to init Windows sockets failed. Do you have WinSock2 installed?"); return (FALSE); } } #endif return (TRUE); } void S9xNPDiscardHeartbeats () { // Discard any pending heartbeats and wait for any frame that is currently // being emulated to complete. #ifdef NP_DEBUG printf ("CLIENT: DiscardHeartbeats @%ld, finished @", S9xGetMilliTime () - START); fflush (stdout); #endif #ifdef __WIN32__ while (WaitForSingleObject (GUI.ClientSemaphore, 200) == WAIT_OBJECT_0) ; #endif #ifdef NP_DEBUG printf ("%ld\n", S9xGetMilliTime () - START); #endif NetPlay.Waiting4EmulationThread = FALSE; } void S9xNPSetAction (const char *action, bool8 force) { #ifdef NP_DEBUG printf ("NPSetAction: %s, forced = %d %ld\n", action, force, S9xGetMilliTime () - START); #endif if (force || !Settings.NetPlayServer) { strncpy (NetPlay.ActionMsg, action, NP_MAX_ACTION_LEN - 1); NetPlay.ActionMsg [NP_MAX_ACTION_LEN - 1] = 0; #ifdef __WIN32__ PostMessage (GUI.hWnd, WM_USER, 0, 0); Sleep (0); #endif } } void S9xNPSetError (const char *error) { #if defined(NP_DEBUG) && NP_DEBUG == 2 printf("ERROR: %s\n", error); fflush (stdout); #endif strncpy (NetPlay.ErrorMsg, error, NP_MAX_ACTION_LEN - 1); NetPlay.ErrorMsg [NP_MAX_ACTION_LEN - 1] = 0; #ifdef __WIN32__ PostMessage (GUI.hWnd, WM_USER + 1, 0, 0); Sleep (0); #endif } void S9xNPSetWarning (const char *warning) { #if defined(NP_DEBUG) && NP_DEBUG == 3 printf("Warning: %s\n", warning); fflush (stdout); #endif strncpy (NetPlay.WarningMsg, warning, NP_MAX_ACTION_LEN - 1); NetPlay.WarningMsg [NP_MAX_ACTION_LEN - 1] = 0; #ifdef __WIN32__ PostMessage (GUI.hWnd, WM_USER + 2, 0, 0); Sleep (0); #endif } #endif