workaround a problem with the harmattan gcc
[drnoksnes] / netplay.cpp
1 /**********************************************************************************
2   Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3
4   (c) Copyright 1996 - 2002  Gary Henderson (gary.henderson@ntlworld.com) and
5                              Jerremy Koot (jkoot@snes9x.com)
6
7   (c) Copyright 2002 - 2004  Matthew Kendora
8
9   (c) Copyright 2002 - 2005  Peter Bortas (peter@bortas.org)
10
11   (c) Copyright 2004 - 2005  Joel Yliluoma (http://iki.fi/bisqwit/)
12
13   (c) Copyright 2001 - 2006  John Weidman (jweidman@slip.net)
14
15   (c) Copyright 2002 - 2006  Brad Jorsch (anomie@users.sourceforge.net),
16                              funkyass (funkyass@spam.shaw.ca),
17                              Kris Bleakley (codeviolation@hotmail.com),
18                              Nach (n-a-c-h@users.sourceforge.net), and
19                              zones (kasumitokoduck@yahoo.com)
20
21   BS-X C emulator code
22   (c) Copyright 2005 - 2006  Dreamer Nom,
23                              zones
24
25   C4 x86 assembler and some C emulation code
26   (c) Copyright 2000 - 2003  _Demo_ (_demo_@zsnes.com),
27                              Nach,
28                              zsKnight (zsknight@zsnes.com)
29
30   C4 C++ code
31   (c) Copyright 2003 - 2006  Brad Jorsch,
32                              Nach
33
34   DSP-1 emulator code
35   (c) Copyright 1998 - 2006  _Demo_,
36                              Andreas Naive (andreasnaive@gmail.com)
37                              Gary Henderson,
38                              Ivar (ivar@snes9x.com),
39                              John Weidman,
40                              Kris Bleakley,
41                              Matthew Kendora,
42                              Nach,
43                              neviksti (neviksti@hotmail.com)
44
45   DSP-2 emulator code
46   (c) Copyright 2003         John Weidman,
47                              Kris Bleakley,
48                              Lord Nightmare (lord_nightmare@users.sourceforge.net),
49                              Matthew Kendora,
50                              neviksti
51
52
53   DSP-3 emulator code
54   (c) Copyright 2003 - 2006  John Weidman,
55                              Kris Bleakley,
56                              Lancer,
57                              z80 gaiden
58
59   DSP-4 emulator code
60   (c) Copyright 2004 - 2006  Dreamer Nom,
61                              John Weidman,
62                              Kris Bleakley,
63                              Nach,
64                              z80 gaiden
65
66   OBC1 emulator code
67   (c) Copyright 2001 - 2004  zsKnight,
68                              pagefault (pagefault@zsnes.com),
69                              Kris Bleakley,
70                              Ported from x86 assembler to C by sanmaiwashi
71
72   SPC7110 and RTC C++ emulator code
73   (c) Copyright 2002         Matthew Kendora with research by
74                              zsKnight,
75                              John Weidman,
76                              Dark Force
77
78   S-DD1 C emulator code
79   (c) Copyright 2003         Brad Jorsch with research by
80                              Andreas Naive,
81                              John Weidman
82
83   S-RTC C emulator code
84   (c) Copyright 2001-2006    byuu,
85                              John Weidman
86
87   ST010 C++ emulator code
88   (c) Copyright 2003         Feather,
89                              John Weidman,
90                              Kris Bleakley,
91                              Matthew Kendora
92
93   Super FX x86 assembler emulator code
94   (c) Copyright 1998 - 2003  _Demo_,
95                              pagefault,
96                              zsKnight,
97
98   Super FX C emulator code
99   (c) Copyright 1997 - 1999  Ivar,
100                              Gary Henderson,
101                              John Weidman
102
103   Sound DSP emulator code is derived from SNEeSe and OpenSPC:
104   (c) Copyright 1998 - 2003  Brad Martin
105   (c) Copyright 1998 - 2006  Charles Bilyue'
106
107   SH assembler code partly based on x86 assembler code
108   (c) Copyright 2002 - 2004  Marcus Comstedt (marcus@mc.pp.se)
109
110   2xSaI filter
111   (c) Copyright 1999 - 2001  Derek Liauw Kie Fa
112
113   HQ2x filter
114   (c) Copyright 2003         Maxim Stepin (maxim@hiend3d.com)
115
116   Specific ports contains the works of other authors. See headers in
117   individual files.
118
119   Snes9x homepage: http://www.snes9x.com
120
121   Permission to use, copy, modify and/or distribute Snes9x in both binary
122   and source form, for non-commercial purposes, is hereby granted without 
123   fee, providing that this license information and copyright notice appear 
124   with all copies and any derived work.
125
126   This software is provided 'as-is', without any express or implied
127   warranty. In no event shall the authors be held liable for any damages
128   arising from the use of this software or it's derivatives.
129
130   Snes9x is freeware for PERSONAL USE only. Commercial users should
131   seek permission of the copyright holders first. Commercial use includes,
132   but is not limited to, charging money for Snes9x or software derived from
133   Snes9x, including Snes9x or derivatives in commercial game bundles, and/or
134   using Snes9x as a promotion for your commercial product.
135
136   The copyright holders request that bug fixes and improvements to the code
137   should be forwarded to them so everyone can benefit from the modifications
138   in future versions.
139
140   Super NES and Super Nintendo Entertainment System are trademarks of
141   Nintendo Co., Limited and its subsidiary companies.
142 **********************************************************************************/
143
144 #include "port.h"
145
146 #ifdef NETPLAY_SUPPORT
147
148 #include <stdio.h>
149 #include <stdlib.h>
150 #include <errno.h>
151 #include <memory.h>
152 #include <sys/types.h>
153
154 #ifndef __WIN32__
155 #include <unistd.h>
156 #include <sys/time.h>
157 #include <sys/types.h>
158 #include <sys/stat.h>
159 #endif
160
161 #if defined (__WIN32__)
162 #include <winsock.h>
163 #include <process.h>
164
165 #define ioctl ioctlsocket
166 #define close closesocket
167 #define read(a,b,c) recv(a, b, c, 0)
168 #define write(a,b,c) send(a, b, c, 0)
169 #else
170
171 #include <netdb.h>
172 #include <sys/ioctl.h>
173 #include <sys/socket.h>
174 #include <sys/param.h>
175 #include <netinet/in.h>
176 #include <arpa/inet.h>
177
178 #ifdef __SVR4
179 #include <sys/stropts.h>
180 #endif
181 #endif
182
183 #ifdef USE_THREADS
184 #include <pthread.h>
185 #include <sched.h>
186 #include <semaphore.h>
187 #endif
188
189 #include "snes9x.h"
190 #include "cpuexec.h"
191 #include "netplay.h"
192 #include "memmap.h"
193 #include "snapshot.h"
194
195 void S9xNPClientLoop (void *);
196 bool8 S9xNPLoadROM (uint32 len);
197 bool8 S9xNPLoadROMDialog (const char *);
198 bool8 S9xNPGetROMImage (uint32 len);
199 void S9xNPGetSRAMData (uint32 len);
200 void S9xNPGetFreezeFile (uint32 len);
201
202 unsigned long START = 0;
203
204 bool8 S9xNPConnectToServer (const char *hostname, int port,
205                             const char *rom_name)
206 {
207     if (!S9xNPInitialise ())
208         return (FALSE);
209
210     S9xNPDisconnect ();
211
212     NetPlay.MySequenceNum = 0;
213     NetPlay.ServerSequenceNum = 0;
214     NetPlay.Connected = FALSE;
215     NetPlay.Abort = FALSE;
216     NetPlay.Player = 0;
217     NetPlay.Paused = FALSE;
218     NetPlay.PercentageComplete = 0;
219     NetPlay.Socket = 0;
220     if (NetPlay.ServerHostName)
221         free ((char *) NetPlay.ServerHostName);
222     NetPlay.ServerHostName = strdup (hostname);
223     if (NetPlay.ROMName)
224         free ((char *) NetPlay.ROMName);
225     NetPlay.ROMName = strdup (rom_name);
226     NetPlay.Port = port;
227     NetPlay.PendingWait4Sync = FALSE;
228
229 #ifdef __WIN32__
230     if (GUI.ClientSemaphore == NULL)
231         GUI.ClientSemaphore = CreateSemaphore (NULL, 0, NP_JOYPAD_HIST_SIZE, NULL);
232
233     if (NetPlay.ReplyEvent == NULL)
234         NetPlay.ReplyEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
235
236     _beginthread (S9xNPClientLoop, 0, NULL);
237 #endif
238
239     return (TRUE);
240 }
241
242 bool8 S9xNPConnect ()
243 {
244     struct sockaddr_in address;
245     struct hostent *hostinfo;
246     unsigned int addr;
247     
248     address.sin_family = AF_INET;
249     address.sin_port = htons (NetPlay.Port);
250 #ifdef NP_DEBUG
251     printf ("CLIENT: Looking up server's hostname (%s) @%ld\n", NetPlay.ServerHostName, S9xGetMilliTime () - START);
252 #endif
253     S9xNPSetAction ("Looking up server's hostname...");
254     if ((int) (addr = inet_addr (NetPlay.ServerHostName)) == -1)
255     {
256         if ((hostinfo = gethostbyname (NetPlay.ServerHostName)))
257         {
258             memcpy ((char *)&address.sin_addr, hostinfo->h_addr,
259                     hostinfo->h_length);
260         }
261         else
262         {
263             S9xNPSetError ("\
264 Unable to look up server's IP address from hostname.\n\n\
265 Unknown hostname or may be your nameserver isn't set\n\
266 up correctly?");
267             return (FALSE);
268         }
269     }
270     else
271     {
272         memcpy ((char *)&address.sin_addr, &addr, sizeof (addr));
273     }
274
275 #ifdef NP_DEBUG
276     printf ("CLIENT: Creating socket @%ld\n", S9xGetMilliTime () - START);
277 #endif
278     S9xNPSetAction ("Creating network socket...");
279     if ((NetPlay.Socket = socket (AF_INET, SOCK_STREAM, 0)) < 0)
280     {
281         S9xNPSetError ("Creating network socket failed.");
282         return (FALSE);
283     }
284
285 #ifdef NP_DEBUG
286     printf ("CLIENT: Trying to connect to server @%ld...\n", S9xGetMilliTime () - START);
287 #endif
288     S9xNPSetAction ("Trying to connect to Snes9X server...");
289
290     if (connect (NetPlay.Socket, (struct sockaddr *) &address, sizeof (address)) < 0)
291     {
292         char buf [100];
293 #ifdef __WIN32__
294         if (WSAGetLastError () == WSAECONNREFUSED)
295 #else
296         if (errno == ECONNREFUSED)
297 #endif
298         {
299             S9xNPSetError ("\
300 Connection to remote server socket refused:\n\n\
301 Is there actually a Snes9X NetPlay server running\n\
302 on the remote machine on this port?");
303         }
304         else
305         {
306             sprintf (buf, "Connection to server failed with error number %d",
307 #ifdef __WIN32__
308                      WSAGetLastError ()
309 #else
310                      errno
311 #endif
312                      );
313             S9xNPDisconnect ();
314         }
315         return (FALSE);
316     }
317     NetPlay.Connected = TRUE;
318
319 #ifdef NP_DEBUG
320     printf ("CLIENT: Sending 'HELLO' message @%ld...\n", S9xGetMilliTime () - START);
321 #endif
322     S9xNPSetAction ("Sending 'HELLO' message...");
323     /* Send the server a HELLO packet*/
324     int len = 7 + 4 + strlen (NetPlay.ROMName) + 1;
325     uint8 *tmp = new uint8 [len];
326     uint8 *ptr = tmp;
327
328     *ptr++ = NP_CLNT_MAGIC;
329     *ptr++ = NetPlay.MySequenceNum++;
330     *ptr++ = NP_CLNT_HELLO;
331     WRITE_LONG (ptr, len);
332     ptr += 4;
333 #ifdef __WIN32__
334     uint32 ft = Settings.FrameTime * 1000;
335
336     WRITE_LONG (ptr, ft);
337 #else
338     WRITE_LONG (ptr, Settings.FrameTime);
339 #endif
340     ptr += 4;
341     strcpy ((char *) ptr, NetPlay.ROMName);
342
343     if (!S9xNPSendData (NetPlay.Socket, tmp, len))
344     {
345         S9xNPSetError ("Sending 'HELLO' message failed.");
346         S9xNPDisconnect ();
347         delete tmp;
348         return (FALSE);
349     }
350     delete tmp;
351
352 #ifdef NP_DEBUG
353     printf ("CLIENT: Waiting for 'WELCOME' reply from server @%ld...\n", S9xGetMilliTime () - START);
354 #endif
355     S9xNPSetAction ("Waiting for 'HELLO' reply from server...");
356
357     uint8 header [7];
358
359     if (!S9xNPGetData (NetPlay.Socket, header, 7) || 
360         header [0] != NP_SERV_MAGIC || header [1] != 0 || 
361         (header [2] & 0x1f) != NP_SERV_HELLO)
362     {
363         S9xNPSetError ("Error in 'HELLO' reply packet received from server.");
364         S9xNPDisconnect ();
365         return (FALSE);
366     }
367 #ifdef NP_DEBUG
368     printf ("CLIENT: Got 'WELCOME' reply @%ld\n", S9xGetMilliTime () - START);
369 #endif
370     len = READ_LONG (&header [3]);
371     if (len > 256)
372     {
373         S9xNPSetError ("Error in 'HELLO' reply packet received from server.");
374         S9xNPDisconnect ();
375         return (FALSE);
376     }
377     uint8 *data = new uint8 [len];
378     if (!S9xNPGetData (NetPlay.Socket, data, len - 7))
379     {
380         S9xNPSetError ("Error in 'HELLO' reply packet received from server.");
381         delete data;
382         S9xNPDisconnect ();
383         return (FALSE);
384     }
385
386     if (data [0] != NP_VERSION)
387     {
388         S9xNPSetError ("\
389 The Snes9X NetPlay server implements a different\n\
390 version of the protocol. Disconnecting.");
391         delete data;
392         S9xNPDisconnect ();
393         return (FALSE);
394     }
395
396     NetPlay.FrameCount = READ_LONG (&data [2]);
397
398     if (!(header [2] & 0x80) &&
399         strcmp ((char *) data + 4 + 2, NetPlay.ROMName) != 0)
400     {
401         if (!S9xNPLoadROMDialog ((char *) data + 4 + 2))
402         {
403             delete data;
404             S9xNPDisconnect ();
405             return (FALSE);
406         }
407     }
408     NetPlay.Player = data [1];
409     delete data;
410
411     NetPlay.PendingWait4Sync = TRUE;
412     Settings.NetPlay = TRUE;
413     S9xNPResetJoypadReadPos ();
414     NetPlay.ServerSequenceNum = 1;
415     
416 #ifdef NP_DEBUG
417     printf ("CLIENT: Sending 'READY' to server @%ld...\n", S9xGetMilliTime () - START);
418 #endif
419     S9xNPSetAction ("Sending 'READY' to the server...");
420
421     return (S9xNPSendReady ((header [2] & 0x80) ? 
422                             NP_CLNT_WAITING_FOR_ROM_IMAGE : 
423                             NP_CLNT_READY));
424 }
425
426 bool8 S9xNPSendReady (uint8 op)
427 {
428     uint8 ready [7];
429     uint8 *ptr = ready;
430     *ptr++ = NP_CLNT_MAGIC;
431     *ptr++ = NetPlay.MySequenceNum++;
432     *ptr++ = op;
433     WRITE_LONG (ptr, 7);
434     ptr += 4;
435
436     if (!S9xNPSendData (NetPlay.Socket, ready, 7))
437     {
438         S9xNPDisconnect ();
439         S9xNPSetError ("Sending 'READY' message failed.");
440         return (FALSE);
441     }
442
443     return (TRUE);
444 }
445
446 bool8 S9xNPSendPause (bool8 paused)
447 {
448 #ifdef NP_DEBUG
449     printf ("CLIENT: Pause - %s @%ld\n", paused ? "YES" : "NO", S9xGetMilliTime () - START);
450 #endif
451     uint8 pause [7];
452     uint8 *ptr = pause;
453     *ptr++ = NP_CLNT_MAGIC;
454     *ptr++ = NetPlay.MySequenceNum++;
455     *ptr++ = NP_CLNT_PAUSE | (paused ? 0x80 : 0);
456     WRITE_LONG (ptr, 7);
457     ptr += 4;
458
459     if (!S9xNPSendData (NetPlay.Socket, pause, 7))
460     {
461         S9xNPSetError ("Sending 'PAUSE' message failed.");
462         S9xNPDisconnect ();
463         return (FALSE);
464     }
465
466     return (TRUE);
467 }
468
469 #ifdef __WIN32__
470 void S9xNPClientLoop (void *)
471 {
472     NetPlay.Waiting4EmulationThread = FALSE;
473
474     if (S9xNPConnect ())
475     {
476         S9xClearPause (PAUSE_NETPLAY_CONNECT);
477         while (NetPlay.Connected)
478         {
479             if (S9xNPWaitForHeartBeat ())
480             {
481                 LONG prev;
482                 if (!ReleaseSemaphore (GUI.ClientSemaphore, 1, &prev))
483                 {
484 #ifdef NP_DEBUG
485                     printf ("CLIENT: ReleaseSemaphore failed - already hit max count (%d) %ld\n", NP_JOYPAD_HIST_SIZE, S9xGetMilliTime () - START);
486 #endif
487                     S9xNPSetWarning ("NetPlay: Client may be out of sync with server.");
488                 }
489                 else
490                 {
491                     if (!NetPlay.Waiting4EmulationThread && 
492                         prev == (int) NetPlay.MaxBehindFrameCount)
493                     {
494                         NetPlay.Waiting4EmulationThread = TRUE;
495                         S9xNPSendPause (TRUE);
496                     }
497                 }
498             }
499             else
500                 S9xNPDisconnect ();
501         }
502     }
503     else
504     {
505         S9xClearPause (PAUSE_NETPLAY_CONNECT);
506     }
507 #ifdef NP_DEBUG
508     printf ("CLIENT: Client thread exiting @%ld\n", S9xGetMilliTime () - START);
509 #endif
510 }
511 #endif
512
513 bool8 S9xNPWaitForHeartBeat ()
514 {
515     uint8 header [3 + 4 + 4 * 5];
516
517     while (S9xNPGetData (NetPlay.Socket, header, 3 + 4))
518     {
519         if (header [0] != NP_SERV_MAGIC)
520         {
521             S9xNPSetError ("Bad magic value from server while waiting for heart-beat message\n");
522             S9xNPDisconnect ();
523             return (FALSE);
524         }
525         if (header [1] != NetPlay.ServerSequenceNum)
526         {
527             char buf [200];
528             sprintf (buf, "Unexpected message sequence number from server, expected %d, got %d\n", NetPlay.ServerSequenceNum, header [1]);
529             S9xNPSetWarning (buf);
530             NetPlay.ServerSequenceNum = header [1] + 1;
531         }
532         else
533             NetPlay.ServerSequenceNum++;
534         
535         if ((header [2] & 0x1f) == NP_SERV_JOYPAD)
536         {
537             // Top 2 bits + 1 of opcode is joypad data count.
538             int num = (header [2] >> 6) + 1;
539
540             if (num)
541             {
542                 if (!S9xNPGetData (NetPlay.Socket, header + 3 + 4, num * 4))
543                 {
544                     S9xNPSetError ("Error while receiving 'JOYPAD' message.");
545                     S9xNPDisconnect ();
546                     return (FALSE);
547                 }
548             }
549             NetPlay.Frame [NetPlay.JoypadWriteInd] = READ_LONG (&header [3]);
550
551             for (int i = 0; i < num; i++)
552             {
553                 NetPlay.Joypads [NetPlay.JoypadWriteInd][i] = 
554                     READ_LONG (&header [3 + 4 + i * sizeof (uint32)]);
555             }
556             NetPlay.Paused = (header [2] & 0x20) != 0;
557
558             NetPlay.JoypadWriteInd = (NetPlay.JoypadWriteInd + 1) % NP_JOYPAD_HIST_SIZE;
559             
560             if (NetPlay.JoypadWriteInd != (NetPlay.JoypadReadInd + 1) % NP_JOYPAD_HIST_SIZE)
561             {
562                 //printf ("(%d)", (NetPlay.JoypadWriteInd - NetPlay.JoypadReadInd) % NP_JOYPAD_HIST_SIZE); fflush (stdout);
563             }
564 //printf ("CLIENT: HB: @%d\n", S9xGetMilliTime () - START);
565             return (TRUE);
566         }
567         else
568         {
569             uint32 len = READ_LONG (&header [3]);
570             switch (header [2] & 0x1f)
571             {
572             case NP_SERV_RESET:
573 #ifdef NP_DEBUG
574                 printf ("CLIENT: RESET received @%ld\n", S9xGetMilliTime () - START);
575 #endif
576                 S9xNPDiscardHeartbeats ();
577                 S9xReset ();
578                 NetPlay.FrameCount = READ_LONG (&header [3]);
579                 S9xNPResetJoypadReadPos ();
580                 S9xNPSendReady ();
581                 break;
582             case NP_SERV_PAUSE:
583                 NetPlay.Paused = (header [2] & 0x20) != 0;
584                 break;
585             case NP_SERV_LOAD_ROM:
586 #ifdef NP_DEBUG
587                 printf ("CLIENT: LOAD_ROM received @%ld\n", S9xGetMilliTime () - START);
588 #endif
589                 S9xNPDiscardHeartbeats ();
590                 if (S9xNPLoadROM (len - 7))
591                     S9xNPSendReady (NP_CLNT_LOADED_ROM);
592                 break;
593             case NP_SERV_ROM_IMAGE:
594 #ifdef NP_DEBUG
595                 printf ("CLIENT: ROM_IMAGE received @%ld\n", S9xGetMilliTime () - START);
596 #endif
597                 S9xNPDiscardHeartbeats ();
598                 if (S9xNPGetROMImage (len - 7))
599                     S9xNPSendReady (NP_CLNT_RECEIVED_ROM_IMAGE);
600                 break;
601             case NP_SERV_SRAM_DATA:
602 #ifdef NP_DEBUG
603                 printf ("CLIENT: SRAM_DATA received @%ld\n", S9xGetMilliTime () - START);
604 #endif
605                 S9xNPDiscardHeartbeats ();
606                 S9xNPGetSRAMData (len - 7);
607                 break;
608             case NP_SERV_FREEZE_FILE:
609 #ifdef NP_DEBUG
610                 printf ("CLIENT: FREEZE_FILE received @%ld\n", S9xGetMilliTime () - START);
611 #endif
612                 S9xNPDiscardHeartbeats ();
613                 S9xNPGetFreezeFile (len - 7);
614                 S9xNPResetJoypadReadPos ();
615                 S9xNPSendReady ();
616                 break;
617             default:
618 #ifdef NP_DEBUG
619                 printf ("CLIENT: UNKNOWN received @%ld\n", S9xGetMilliTime () - START);
620 #endif
621                 S9xNPDisconnect ();
622                 return (FALSE);
623             }
624         }
625     }
626
627     S9xNPDisconnect ();
628     return (FALSE);
629 }
630
631 bool8 S9xNPLoadROMDialog (const char *rom_name)
632 {
633     NetPlay.Answer = FALSE;
634
635 #ifdef __WIN32__
636     ResetEvent (NetPlay.ReplyEvent); 
637
638 #ifdef NP_DEBUG
639     printf ("CLIENT: Asking GUI thread to open ROM load dialog...\n");
640 #endif
641
642     PostMessage (GUI.hWnd, WM_USER + 3, (uint32) rom_name, (uint32) rom_name);
643
644 #ifdef NP_DEBUG
645     printf ("CLIENT: Waiting for reply from GUI thread...\n");
646 #endif
647
648     WaitForSingleObject (NetPlay.ReplyEvent, INFINITE);
649
650 #ifdef NP_DEBUG
651     printf ("CLIENT: Got reply from GUI thread (%d)\n", NetPlay.Answer);
652 #endif
653 #endif
654
655     return (NetPlay.Answer);
656 }
657
658 bool8 S9xNPLoadROM (uint32 len)
659 {
660     uint8 *data = new uint8 [len];
661     
662     S9xNPSetAction ("Receiving ROM name...");
663     if (!S9xNPGetData (NetPlay.Socket, data, len))
664     {
665         S9xNPSetError ("Error while receiving ROM name.");
666         delete data;
667         S9xNPDisconnect ();
668         return (FALSE);
669     }
670     
671     S9xNPSetAction ("Opening LoadROM dialog...");
672     if (!S9xNPLoadROMDialog ((char *) data))
673     {
674         S9xNPSetError ("Disconnected from NetPlay server because you are playing a different game!");
675         delete data;
676         S9xNPDisconnect ();
677         return (FALSE);
678     }
679     delete data;
680     return (TRUE);
681 }
682
683 bool8 S9xNPGetROMImage (uint32 len)
684 {
685     uint8 rom_info [5];
686
687     S9xNPSetAction ("Receiving ROM information...");
688     if (!S9xNPGetData (NetPlay.Socket, rom_info, 5))
689     {
690         S9xNPSetError ("Error while receiving ROM information.");
691         S9xNPDisconnect ();
692         return (FALSE);
693     }
694     uint32 CalculatedSize = READ_LONG (&rom_info [1]);
695 #ifdef NP_DEBUG
696     printf ("CLIENT: Hi-ROM: %s, Size: %04x\n", rom_info [0] ? "Y" : "N", CalculatedSize);
697 #endif
698     if (CalculatedSize + 5 >= len || 
699         CalculatedSize >= CMemory::MAX_ROM_SIZE)
700     {
701         S9xNPSetError ("Size error in ROM image data received from server.");
702         S9xNPDisconnect ();
703         return (FALSE);
704     }
705     
706     Memory.HiROM = rom_info [0];
707     Memory.LoROM = !Memory.HiROM;
708     Memory.HeaderCount = 0;
709     Memory.CalculatedSize = CalculatedSize;
710
711     // Load up ROM image
712 #ifdef NP_DEBUG
713     printf ("CLIENT: Receiving ROM image @%ld...\n", S9xGetMilliTime () - START);
714 #endif
715     S9xNPSetAction ("Receiving ROM image...");
716     if (!S9xNPGetData (NetPlay.Socket, Memory.ROM, Memory.CalculatedSize))
717     {
718         S9xNPSetError ("Error while receiving ROM image from server.");
719         Settings.StopEmulation = TRUE; 
720         S9xNPDisconnect ();
721         return (FALSE);
722     }
723 #ifdef NP_DEBUG
724     printf ("CLIENT: Receiving ROM filename @%ld...\n", S9xGetMilliTime () - START);
725 #endif
726     S9xNPSetAction ("Receiving ROM filename...");
727     uint32 filename_len = len - Memory.CalculatedSize - 5;
728     if (filename_len > _MAX_PATH ||
729         !S9xNPGetData (NetPlay.Socket, (uint8 *) Memory.ROMFilename, filename_len))
730     {
731         S9xNPSetError ("Error while receiving ROM filename from server.");
732         S9xNPDisconnect ();
733         Settings.StopEmulation = TRUE;
734         return (FALSE);
735     }
736     Memory.InitROM (FALSE);
737     S9xReset ();
738     S9xNPResetJoypadReadPos ();
739     Settings.StopEmulation = FALSE;
740
741 #ifdef __WIN32__
742     PostMessage (GUI.hWnd, WM_NULL, 0, 0);
743 #endif
744     
745     return (TRUE);
746 }
747
748 void S9xNPGetSRAMData (uint32 len)
749 {
750     if (len > 0x10000)
751     {
752         S9xNPSetError ("Length error in S-RAM data received from server.");
753         S9xNPDisconnect ();
754         return;
755     }
756     S9xNPSetAction ("Receiving S-RAM data...");
757     if (len > 0 && !S9xNPGetData (NetPlay.Socket, ::SRAM, len))
758     {
759         S9xNPSetError ("Error while receiving S-RAM data from server.");
760         S9xNPDisconnect ();
761     }
762 }
763
764 void S9xNPGetFreezeFile (uint32 len)
765 {
766     uint8 frame_count [4];
767
768 #ifdef NP_DEBUG
769     printf ("CLIENT: Receiving freeze file information @%ld...\n", S9xGetMilliTime () - START);
770 #endif
771     S9xNPSetAction ("Receiving freeze file information...");
772     if (!S9xNPGetData (NetPlay.Socket, frame_count, 4))
773     {
774         S9xNPSetError ("Error while receiving freeze file information from server.");
775         S9xNPDisconnect ();
776         return;
777     }
778     NetPlay.FrameCount = READ_LONG (frame_count);
779
780 #ifdef NP_DEBUG
781     printf ("CLIENT: Receiving freeze file @%ld...\n", S9xGetMilliTime () - START);
782 #endif
783     S9xNPSetAction ("Receiving freeze file...");
784     uint8 *data = new uint8 [len];
785     if (!S9xNPGetData (NetPlay.Socket, data, len - 4))
786     {
787         S9xNPSetError ("Error while receiving freeze file from server.");
788         S9xNPDisconnect ();
789         delete data;
790         return;
791     }
792
793     //FIXME: Setting umask here wouldn't hurt.
794     FILE *file;
795 #ifdef HAVE_MKSTEMP
796     int fd;
797     char fname[] = "/tmp/snes9x_fztmpXXXXXX";
798     if ((fd = mkstemp(fname)) < 0)
799     {
800         if ((file = fdopen (fd, "wb")))
801 #else
802     char fname [L_tmpnam];
803     if (tmpnam (fname))
804     {
805         if ((file = fopen (fname, "wb")))
806 #endif
807         {
808             if (fwrite (data, 1, len, file) == len)
809             {
810                 fclose(file);
811                 if (!S9xUnfreezeGame (fname))
812                     S9xNPSetError ("Unable to load freeze file just received.");
813             } else {
814                 S9xNPSetError ("Failed to write to temporary freeze file.");
815                 fclose (file);
816             }
817         } else
818             S9xNPSetError ("Failed to create temporary freeze file.");
819         remove (fname);
820     } else
821         S9xNPSetError ("Unable to get name for temporary freeze file.");
822     delete data;
823 }
824
825 uint32 S9xNPGetJoypad (int which1)
826 {
827     if (Settings.NetPlay && which1 < 5)
828         return (NetPlay.Joypads [NetPlay.JoypadReadInd][which1]);
829
830     return (0);
831 }
832
833 void S9xNPStepJoypadHistory ()
834 {
835     if ((NetPlay.JoypadReadInd + 1) % NP_JOYPAD_HIST_SIZE != NetPlay.JoypadWriteInd)
836     {
837         NetPlay.JoypadReadInd = (NetPlay.JoypadReadInd + 1) % NP_JOYPAD_HIST_SIZE;
838         if (NetPlay.FrameCount != NetPlay.Frame [NetPlay.JoypadReadInd])
839         {
840             S9xNPSetWarning ("This Snes9X session may be out of sync with the server.");
841 #ifdef NP_DEBUG
842             printf ("*** CLIENT: client out of sync with server (%d, %d) @%ld\n", NetPlay.FrameCount, NetPlay.Frame [NetPlay.JoypadReadInd], S9xGetMilliTime () - START);
843 #endif
844         }
845     }
846     else
847     {
848 #ifdef NP_DEBUG
849         printf ("*** CLIENT: S9xNPStepJoypadHistory NOT OK@%ld\n", S9xGetMilliTime () - START);
850 #endif
851     }
852 }
853
854
855 void S9xNPResetJoypadReadPos ()
856 {
857 #ifdef NP_DEBUG
858     printf ("CLIENT: ResetJoyReadPos @%ld\n", S9xGetMilliTime () - START); fflush (stdout);
859 #endif
860     NetPlay.JoypadWriteInd = 0;
861     NetPlay.JoypadReadInd = NP_JOYPAD_HIST_SIZE - 1;
862     for (int h = 0; h < NP_JOYPAD_HIST_SIZE; h++)
863         memset ((void *) &NetPlay.Joypads [h], 0, sizeof (NetPlay.Joypads [0]));
864 }
865
866 bool8 S9xNPSendJoypadUpdate (uint32 joypad)
867 {
868     uint8 data [7];
869     uint8 *ptr = data;
870
871     *ptr++ = NP_CLNT_MAGIC;
872     *ptr++ = NetPlay.MySequenceNum++;
873     *ptr++ = NP_CLNT_JOYPAD;
874
875     joypad |= 0x80000000;
876
877     WRITE_LONG (ptr, joypad);
878     if (!S9xNPSendData (NetPlay.Socket, data, 7))
879     {
880         S9xNPSetError ("Error while sending joypad data server.");
881         S9xNPDisconnect ();
882         return (FALSE);
883     }
884     return (TRUE);
885 }
886
887 void S9xNPDisconnect ()
888 {
889     close (NetPlay.Socket);
890     NetPlay.Socket = -1;
891     NetPlay.Connected = FALSE;
892     Settings.NetPlay = FALSE;
893 }
894
895 bool8 S9xNPSendData (int socket, const uint8 *data, int length)
896 {
897     int len = length;
898     const uint8 *ptr = data;
899
900     NetPlay.PercentageComplete = 0;
901
902     do
903     {
904         if (NetPlay.Abort)
905             return (FALSE);
906
907         int num_bytes = len;
908
909         // Write the data in small chunks, allowing this thread to spot an
910         // abort request from another thread.
911         if (num_bytes > 512)
912             num_bytes = 512;
913
914         int sent = write (socket, (char *) ptr, num_bytes);
915         if (sent < 0)
916         {
917             if (errno == EINTR 
918 #ifdef EAGAIN
919                 || errno == EAGAIN
920 #endif
921 #ifdef EWOULDBLOCK
922                 || errno == EWOULDBLOCK
923 #endif
924                 )
925             {
926 #ifdef NP_DEBUG
927                 printf ("CLIENT: EINTR, EAGAIN or EWOULDBLOCK while sending data @%ld\n", S9xGetMilliTime () - START);
928 #endif
929                 continue;
930             }
931             return (FALSE);
932         }
933         else
934         if (sent == 0)
935             return (FALSE);
936         len -= sent;
937         ptr += sent;
938
939         NetPlay.PercentageComplete = (uint8) (((length - len) * 100) / length);
940     } while (len > 0);
941
942     return (TRUE);
943 }
944
945 bool8 S9xNPGetData (int socket, uint8 *data, int length)
946 {
947     int len = length;
948     uint8 *ptr = data;
949     int chunk = length / 50;
950
951     if (chunk < 1024)
952         chunk = 1024;
953
954     NetPlay.PercentageComplete = 0;
955     do
956     {
957         if (NetPlay.Abort)
958             return (FALSE);
959
960         int num_bytes = len;
961
962         // Read the data in small chunks, allowing this thread to spot an
963         // abort request from another thread.
964         if (num_bytes > chunk)
965             num_bytes = chunk;
966
967         int got = read (socket, (char *) ptr, num_bytes);
968         if (got < 0)
969         {
970             if (errno == EINTR
971 #ifdef EAGAIN
972                 || errno == EAGAIN
973 #endif
974 #ifdef EWOULDBLOCK
975                 || errno == EWOULDBLOCK
976 #endif
977 #ifdef WSAEWOULDBLOCK
978                 || errno == WSAEWOULDBLOCK
979 #endif
980                 )
981             {
982 #ifdef NP_DEBUG
983                 printf ("CLIENT: EINTR, EAGAIN or EWOULDBLOCK while receiving data @%ld\n", S9xGetMilliTime () - START);
984 #endif
985                 continue;
986             }
987 #ifdef WSAEMSGSIZE
988             if (errno != WSAEMSGSIZE)
989                 return (FALSE);
990             else
991             {
992                 got = num_bytes;
993 #ifdef NP_DEBUG
994                 printf ("CLIENT: WSAEMSGSIZE, actual bytes %d while receiving data @%ld\n", got, S9xGetMilliTime () - START);
995 #endif
996             }
997 #else
998             return (FALSE);
999 #endif
1000         }
1001         else
1002         if (got == 0)
1003             return (FALSE);
1004
1005         len -= got;
1006         ptr += got;
1007
1008         if (!Settings.NetPlayServer && length > 1024)
1009         {
1010             NetPlay.PercentageComplete = (uint8) (((length - len) * 100) / length);
1011 #ifdef __WIN32__
1012             PostMessage (GUI.hWnd, WM_USER, NetPlay.PercentageComplete, 
1013                          NetPlay.PercentageComplete);
1014             Sleep (0);
1015 #endif
1016         }
1017             
1018     } while (len > 0);
1019
1020     return (TRUE);
1021 }
1022
1023 bool8 S9xNPInitialise ()
1024 {
1025 #ifdef __WIN32__
1026     static bool8 initialised = FALSE;
1027
1028     if (!initialised)
1029     {
1030         initialised = TRUE;
1031         WSADATA data;
1032
1033 #ifdef NP_DEBUG
1034         START = S9xGetMilliTime ();
1035
1036         printf ("CLIENT/SERVER: Initialising WinSock @%ld\n", S9xGetMilliTime () - START);
1037 #endif
1038         S9xNPSetAction ("Initialising Windows sockets interface...");
1039         if (WSAStartup (MAKEWORD (1, 0), &data) != 0)
1040         {
1041             S9xNPSetError ("Call to init Windows sockets failed. Do you have WinSock2 installed?");
1042             return (FALSE); 
1043         }
1044     }
1045 #endif
1046     return (TRUE); 
1047 }
1048
1049 void S9xNPDiscardHeartbeats ()
1050 {
1051     // Discard any pending heartbeats and wait for any frame that is currently
1052     // being emulated to complete.
1053 #ifdef NP_DEBUG
1054     printf ("CLIENT: DiscardHeartbeats @%ld, finished @", S9xGetMilliTime () - START);
1055     fflush (stdout);
1056 #endif
1057
1058 #ifdef __WIN32__
1059     while (WaitForSingleObject (GUI.ClientSemaphore, 200) == WAIT_OBJECT_0)
1060         ;
1061 #endif
1062
1063 #ifdef NP_DEBUG
1064     printf ("%ld\n", S9xGetMilliTime () - START);
1065 #endif
1066     NetPlay.Waiting4EmulationThread = FALSE;
1067 }
1068
1069 void S9xNPSetAction (const char *action, bool8 force)
1070 {
1071     if (force || !Settings.NetPlayServer)
1072     {
1073         strncpy (NetPlay.ActionMsg, action, NP_MAX_ACTION_LEN - 1);
1074         NetPlay.ActionMsg [NP_MAX_ACTION_LEN - 1] = 0;
1075 #ifdef __WIN32__
1076         PostMessage (GUI.hWnd, WM_USER, 0, 0);
1077         Sleep (0);
1078 #endif
1079     }
1080 }
1081
1082 void S9xNPSetError (const char *error)
1083 {
1084     strncpy (NetPlay.ErrorMsg, error, NP_MAX_ACTION_LEN - 1);
1085     NetPlay.ErrorMsg [NP_MAX_ACTION_LEN - 1] = 0;
1086 #ifdef __WIN32
1087     PostMessage (GUI.hWnd, WM_USER + 1, 0, 0);
1088     Sleep (0);
1089 #endif
1090 }
1091
1092 void S9xNPSetWarning (const char *warning)
1093 {
1094     strncpy (NetPlay.WarningMsg, warning, NP_MAX_ACTION_LEN - 1);
1095     NetPlay.WarningMsg [NP_MAX_ACTION_LEN - 1] = 0;
1096 #ifdef __WIN32__
1097     PostMessage (GUI.hWnd, WM_USER + 2, 0, 0);
1098     Sleep (0);
1099 #endif
1100 }
1101 #endif
1102
1103