using new sdl_haa library
[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 #include "display.h"
195
196 void S9xNPClientLoop (void *);
197 bool8 S9xNPLoadROM (uint32 len);
198 bool8 S9xNPLoadROMDialog (const char *);
199 bool8 S9xNPGetROMImage (uint32 len);
200 void S9xNPGetSRAMData (uint32 len);
201 void S9xNPGetFreezeFile (uint32 len);
202
203 unsigned long START = 0;
204
205 bool8 S9xNPConnectToServer (const char *hostname, int port,
206                             const char *rom_name)
207 {
208     if (!S9xNPInitialise ())
209         return (FALSE);
210
211     S9xNPDisconnect ();
212
213     NetPlay.MySequenceNum = 0;
214     NetPlay.ServerSequenceNum = 0;
215     NetPlay.Connected = FALSE;
216     NetPlay.Abort = FALSE;
217     NetPlay.Player = 0;
218     NetPlay.Paused = FALSE;
219     NetPlay.PercentageComplete = 0;
220     NetPlay.Socket = 0;
221     if (NetPlay.ServerHostName)
222         free ((char *) NetPlay.ServerHostName);
223     NetPlay.ServerHostName = strdup (hostname);
224     if (NetPlay.ROMName)
225         free ((char *) NetPlay.ROMName);
226     NetPlay.ROMName = strdup (rom_name);
227     NetPlay.Port = port;
228     NetPlay.PendingWait4Sync = FALSE;
229
230 #ifdef __WIN32__
231     if (GUI.ClientSemaphore == NULL)
232         GUI.ClientSemaphore = CreateSemaphore (NULL, 0, NP_JOYPAD_HIST_SIZE, NULL);
233
234     if (NetPlay.ReplyEvent == NULL)
235         NetPlay.ReplyEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
236
237     _beginthread (S9xNPClientLoop, 0, NULL);
238 #endif
239
240     return (TRUE);
241 }
242
243 bool8 S9xNPConnect ()
244 {
245     struct sockaddr_in address;
246     struct hostent *hostinfo;
247     unsigned int addr;
248     
249     address.sin_family = AF_INET;
250     address.sin_port = htons (NetPlay.Port);
251 #ifdef NP_DEBUG
252     printf ("CLIENT: Looking up server's hostname (%s) @%ld\n", NetPlay.ServerHostName, S9xGetMilliTime () - START);
253 #endif
254     S9xNPSetAction ("Looking up server's hostname...");
255     if ((int) (addr = inet_addr (NetPlay.ServerHostName)) == -1)
256     {
257         if ((hostinfo = gethostbyname (NetPlay.ServerHostName)))
258         {
259             memcpy ((char *)&address.sin_addr, hostinfo->h_addr,
260                     hostinfo->h_length);
261         }
262         else
263         {
264             S9xNPSetError ("\
265 Unable to look up server's IP address from hostname.\n\n\
266 Unknown hostname or may be your nameserver isn't set\n\
267 up correctly?");
268             return (FALSE);
269         }
270     }
271     else
272     {
273         memcpy ((char *)&address.sin_addr, &addr, sizeof (addr));
274     }
275
276 #ifdef NP_DEBUG
277     printf ("CLIENT: Creating socket @%ld\n", S9xGetMilliTime () - START);
278 #endif
279     S9xNPSetAction ("Creating network socket...");
280     if ((NetPlay.Socket = socket (AF_INET, SOCK_STREAM, 0)) < 0)
281     {
282         S9xNPSetError ("Creating network socket failed.");
283         return (FALSE);
284     }
285
286 #ifdef NP_DEBUG
287     printf ("CLIENT: Trying to connect to server @%ld...\n", S9xGetMilliTime () - START);
288 #endif
289     S9xNPSetAction ("Trying to connect to Snes9X server...");
290
291     if (connect (NetPlay.Socket, (struct sockaddr *) &address, sizeof (address)) < 0)
292     {
293         char buf [100];
294 #ifdef __WIN32__
295         if (WSAGetLastError () == WSAECONNREFUSED)
296 #else
297         if (errno == ECONNREFUSED)
298 #endif
299         {
300             S9xNPSetError ("\
301 Connection to remote server socket refused:\n\n\
302 Is there actually a Snes9X NetPlay server running\n\
303 on the remote machine on this port?");
304         }
305         else
306         {
307             sprintf (buf, "Connection to server failed with error number %d",
308 #ifdef __WIN32__
309                      WSAGetLastError ()
310 #else
311                      errno
312 #endif
313                      );
314             S9xNPDisconnect ();
315         }
316         return (FALSE);
317     }
318     NetPlay.Connected = TRUE;
319
320 #ifdef NP_DEBUG
321     printf ("CLIENT: Sending 'HELLO' message @%ld...\n", S9xGetMilliTime () - START);
322 #endif
323     S9xNPSetAction ("Sending 'HELLO' message...");
324     /* Send the server a HELLO packet*/
325     int len = 7 + 4 + strlen (NetPlay.ROMName) + 1;
326     uint8 *tmp = new uint8 [len];
327     uint8 *ptr = tmp;
328
329     *ptr++ = NP_CLNT_MAGIC;
330     *ptr++ = NetPlay.MySequenceNum++;
331     *ptr++ = NP_CLNT_HELLO;
332     WRITE_LONG (ptr, len);
333     ptr += 4;
334 #ifdef __WIN32__
335     uint32 ft = Settings.FrameTime * 1000;
336
337     WRITE_LONG (ptr, ft);
338 #else
339     WRITE_LONG (ptr, Settings.FrameTime);
340 #endif
341     ptr += 4;
342     strcpy ((char *) ptr, NetPlay.ROMName);
343
344     if (!S9xNPSendData (NetPlay.Socket, tmp, len))
345     {
346         S9xNPSetError ("Sending 'HELLO' message failed.");
347         S9xNPDisconnect ();
348         delete tmp;
349         return (FALSE);
350     }
351     delete tmp;
352
353 #ifdef NP_DEBUG
354     printf ("CLIENT: Waiting for 'WELCOME' reply from server @%ld...\n", S9xGetMilliTime () - START);
355 #endif
356     S9xNPSetAction ("Waiting for 'HELLO' reply from server...");
357
358     uint8 header [7];
359
360     if (!S9xNPGetData (NetPlay.Socket, header, 7) || 
361         header [0] != NP_SERV_MAGIC || header [1] != 0 || 
362         (header [2] & 0x1f) != NP_SERV_HELLO)
363     {
364         S9xNPSetError ("Error in 'HELLO' reply packet received from server.");
365         S9xNPDisconnect ();
366         return (FALSE);
367     }
368 #ifdef NP_DEBUG
369     printf ("CLIENT: Got 'WELCOME' reply @%ld\n", S9xGetMilliTime () - START);
370 #endif
371     len = READ_LONG (&header [3]);
372     if (len > 256)
373     {
374         S9xNPSetError ("Error in 'HELLO' reply packet received from server.");
375         S9xNPDisconnect ();
376         return (FALSE);
377     }
378     uint8 *data = new uint8 [len];
379     if (!S9xNPGetData (NetPlay.Socket, data, len - 7))
380     {
381         S9xNPSetError ("Error in 'HELLO' reply packet received from server.");
382         delete data;
383         S9xNPDisconnect ();
384         return (FALSE);
385     }
386
387     if (data [0] != NP_VERSION)
388     {
389         S9xNPSetError ("\
390 The Snes9X NetPlay server implements a different\n\
391 version of the protocol. Disconnecting.");
392         delete data;
393         S9xNPDisconnect ();
394         return (FALSE);
395     }
396
397     NetPlay.FrameCount = READ_LONG (&data [2]);
398
399     if (!(header [2] & 0x80) &&
400         strcmp ((char *) data + 4 + 2, NetPlay.ROMName) != 0)
401     {
402         if (!S9xNPLoadROMDialog ((char *) data + 4 + 2))
403         {
404             delete data;
405             S9xNPDisconnect ();
406             return (FALSE);
407         }
408     }
409     NetPlay.Player = data [1];
410     delete data;
411
412     NetPlay.PendingWait4Sync = TRUE;
413     Settings.NetPlay = TRUE;
414     S9xNPResetJoypadReadPos ();
415     NetPlay.ServerSequenceNum = 1;
416     
417 #ifdef NP_DEBUG
418     printf ("CLIENT: Sending 'READY' to server @%ld...\n", S9xGetMilliTime () - START);
419 #endif
420     S9xNPSetAction ("Sending 'READY' to the server...");
421
422     return (S9xNPSendReady ((header [2] & 0x80) ? 
423                             NP_CLNT_WAITING_FOR_ROM_IMAGE : 
424                             NP_CLNT_READY));
425 }
426
427 bool8 S9xNPSendReady (uint8 op)
428 {
429     uint8 ready [7];
430     uint8 *ptr = ready;
431     *ptr++ = NP_CLNT_MAGIC;
432     *ptr++ = NetPlay.MySequenceNum++;
433     *ptr++ = op;
434     WRITE_LONG (ptr, 7);
435     ptr += 4;
436
437     if (!S9xNPSendData (NetPlay.Socket, ready, 7))
438     {
439         S9xNPDisconnect ();
440         S9xNPSetError ("Sending 'READY' message failed.");
441         return (FALSE);
442     }
443
444     return (TRUE);
445 }
446
447 bool8 S9xNPSendPause (bool8 paused)
448 {
449 #ifdef NP_DEBUG
450     printf ("CLIENT: Pause - %s @%ld\n", paused ? "YES" : "NO", S9xGetMilliTime () - START);
451 #endif
452     uint8 pause [7];
453     uint8 *ptr = pause;
454     *ptr++ = NP_CLNT_MAGIC;
455     *ptr++ = NetPlay.MySequenceNum++;
456     *ptr++ = NP_CLNT_PAUSE | (paused ? 0x80 : 0);
457     WRITE_LONG (ptr, 7);
458     ptr += 4;
459
460     if (!S9xNPSendData (NetPlay.Socket, pause, 7))
461     {
462         S9xNPSetError ("Sending 'PAUSE' message failed.");
463         S9xNPDisconnect ();
464         return (FALSE);
465     }
466
467     return (TRUE);
468 }
469
470 #ifdef __WIN32__
471 void S9xNPClientLoop (void *)
472 {
473     NetPlay.Waiting4EmulationThread = FALSE;
474
475     if (S9xNPConnect ())
476     {
477         S9xClearPause (PAUSE_NETPLAY_CONNECT);
478         while (NetPlay.Connected)
479         {
480             if (S9xNPWaitForHeartBeat ())
481             {
482                 LONG prev;
483                 if (!ReleaseSemaphore (GUI.ClientSemaphore, 1, &prev))
484                 {
485 #ifdef NP_DEBUG
486                     printf ("CLIENT: ReleaseSemaphore failed - already hit max count (%d) %ld\n", NP_JOYPAD_HIST_SIZE, S9xGetMilliTime () - START);
487 #endif
488                     S9xNPSetWarning ("NetPlay: Client may be out of sync with server.");
489                 }
490                 else
491                 {
492                     if (!NetPlay.Waiting4EmulationThread && 
493                         prev == (int) NetPlay.MaxBehindFrameCount)
494                     {
495                         NetPlay.Waiting4EmulationThread = TRUE;
496                         S9xNPSendPause (TRUE);
497                     }
498                 }
499             }
500             else
501                 S9xNPDisconnect ();
502         }
503     }
504     else
505     {
506         S9xClearPause (PAUSE_NETPLAY_CONNECT);
507     }
508 #ifdef NP_DEBUG
509     printf ("CLIENT: Client thread exiting @%ld\n", S9xGetMilliTime () - START);
510 #endif
511 }
512 #endif
513
514 bool8 S9xNPWaitForHeartBeat ()
515 {
516     uint8 header [3 + 4 + 4 * 5];
517
518     while (S9xNPGetData (NetPlay.Socket, header, 3 + 4))
519     {
520         if (header [0] != NP_SERV_MAGIC)
521         {
522             S9xNPSetError ("Bad magic value from server while waiting for heart-beat message\n");
523             S9xNPDisconnect ();
524             return (FALSE);
525         }
526         if (header [1] != NetPlay.ServerSequenceNum)
527         {
528             char buf [200];
529             sprintf (buf, "Unexpected message sequence number from server, expected %d, got %d\n", NetPlay.ServerSequenceNum, header [1]);
530             S9xNPSetWarning (buf);
531             NetPlay.ServerSequenceNum = header [1] + 1;
532         }
533         else
534             NetPlay.ServerSequenceNum++;
535         
536         if ((header [2] & 0x1f) == NP_SERV_JOYPAD)
537         {
538             // Top 2 bits + 1 of opcode is joypad data count.
539             int num = (header [2] >> 6) + 1;
540
541             if (num)
542             {
543                 if (!S9xNPGetData (NetPlay.Socket, header + 3 + 4, num * 4))
544                 {
545                     S9xNPSetError ("Error while receiving 'JOYPAD' message.");
546                     S9xNPDisconnect ();
547                     return (FALSE);
548                 }
549             }
550             NetPlay.Frame [NetPlay.JoypadWriteInd] = READ_LONG (&header [3]);
551
552             for (int i = 0; i < num; i++)
553             {
554                 NetPlay.Joypads [NetPlay.JoypadWriteInd][i] = 
555                     READ_LONG (&header [3 + 4 + i * sizeof (uint32)]);
556             }
557             NetPlay.Paused = (header [2] & 0x20) != 0;
558
559             NetPlay.JoypadWriteInd = (NetPlay.JoypadWriteInd + 1) % NP_JOYPAD_HIST_SIZE;
560             
561             if (NetPlay.JoypadWriteInd != (NetPlay.JoypadReadInd + 1) % NP_JOYPAD_HIST_SIZE)
562             {
563                 //printf ("(%d)", (NetPlay.JoypadWriteInd - NetPlay.JoypadReadInd) % NP_JOYPAD_HIST_SIZE); fflush (stdout);
564             }
565 //printf ("CLIENT: HB: @%d\n", S9xGetMilliTime () - START);
566             return (TRUE);
567         }
568         else
569         {
570             uint32 len = READ_LONG (&header [3]);
571             switch (header [2] & 0x1f)
572             {
573             case NP_SERV_RESET:
574 #ifdef NP_DEBUG
575                 printf ("CLIENT: RESET received @%ld\n", S9xGetMilliTime () - START);
576 #endif
577                 S9xNPDiscardHeartbeats ();
578                 S9xReset ();
579                 NetPlay.FrameCount = READ_LONG (&header [3]);
580                 S9xNPResetJoypadReadPos ();
581                 S9xNPSendReady ();
582                 break;
583             case NP_SERV_PAUSE:
584                 NetPlay.Paused = (header [2] & 0x20) != 0;
585                 break;
586             case NP_SERV_LOAD_ROM:
587 #ifdef NP_DEBUG
588                 printf ("CLIENT: LOAD_ROM received @%ld\n", S9xGetMilliTime () - START);
589 #endif
590                 S9xNPDiscardHeartbeats ();
591                 if (S9xNPLoadROM (len - 7))
592                     S9xNPSendReady (NP_CLNT_LOADED_ROM);
593                 break;
594             case NP_SERV_ROM_IMAGE:
595 #ifdef NP_DEBUG
596                 printf ("CLIENT: ROM_IMAGE received @%ld\n", S9xGetMilliTime () - START);
597 #endif
598                 S9xNPDiscardHeartbeats ();
599                 if (S9xNPGetROMImage (len - 7))
600                     S9xNPSendReady (NP_CLNT_RECEIVED_ROM_IMAGE);
601                 break;
602             case NP_SERV_SRAM_DATA:
603 #ifdef NP_DEBUG
604                 printf ("CLIENT: SRAM_DATA received @%ld\n", S9xGetMilliTime () - START);
605 #endif
606                 S9xNPDiscardHeartbeats ();
607                 S9xNPGetSRAMData (len - 7);
608                 break;
609             case NP_SERV_FREEZE_FILE:
610 #ifdef NP_DEBUG
611                 printf ("CLIENT: FREEZE_FILE received @%ld\n", S9xGetMilliTime () - START);
612 #endif
613                 S9xNPDiscardHeartbeats ();
614                 S9xNPGetFreezeFile (len - 7);
615                 S9xNPResetJoypadReadPos ();
616                 S9xNPSendReady ();
617                 break;
618             default:
619 #ifdef NP_DEBUG
620                 printf ("CLIENT: UNKNOWN received @%ld\n", S9xGetMilliTime () - START);
621 #endif
622                 S9xNPDisconnect ();
623                 return (FALSE);
624             }
625         }
626     }
627
628     S9xNPDisconnect ();
629     return (FALSE);
630 }
631
632 bool8 S9xNPLoadROMDialog (const char *rom_name)
633 {
634     NetPlay.Answer = FALSE;
635
636 #ifdef __WIN32__
637     ResetEvent (NetPlay.ReplyEvent); 
638
639 #ifdef NP_DEBUG
640     printf ("CLIENT: Asking GUI thread to open ROM load dialog...\n");
641 #endif
642
643     PostMessage (GUI.hWnd, WM_USER + 3, (uint32) rom_name, (uint32) rom_name);
644
645 #ifdef NP_DEBUG
646     printf ("CLIENT: Waiting for reply from GUI thread...\n");
647 #endif
648
649     WaitForSingleObject (NetPlay.ReplyEvent, INFINITE);
650
651 #ifdef NP_DEBUG
652     printf ("CLIENT: Got reply from GUI thread (%d)\n", NetPlay.Answer);
653 #endif
654 #endif
655
656     return (NetPlay.Answer);
657 }
658
659 bool8 S9xNPLoadROM (uint32 len)
660 {
661     uint8 *data = new uint8 [len];
662     
663     S9xNPSetAction ("Receiving ROM name...");
664     if (!S9xNPGetData (NetPlay.Socket, data, len))
665     {
666         S9xNPSetError ("Error while receiving ROM name.");
667         delete data;
668         S9xNPDisconnect ();
669         return (FALSE);
670     }
671     
672     S9xNPSetAction ("Opening LoadROM dialog...");
673     if (!S9xNPLoadROMDialog ((char *) data))
674     {
675         S9xNPSetError ("Disconnected from NetPlay server because you are playing a different game!");
676         delete data;
677         S9xNPDisconnect ();
678         return (FALSE);
679     }
680     delete data;
681     return (TRUE);
682 }
683
684 bool8 S9xNPGetROMImage (uint32 len)
685 {
686     uint8 rom_info [5];
687
688     S9xNPSetAction ("Receiving ROM information...");
689     if (!S9xNPGetData (NetPlay.Socket, rom_info, 5))
690     {
691         S9xNPSetError ("Error while receiving ROM information.");
692         S9xNPDisconnect ();
693         return (FALSE);
694     }
695     uint32 CalculatedSize = READ_LONG (&rom_info [1]);
696 #ifdef NP_DEBUG
697     printf ("CLIENT: Hi-ROM: %s, Size: %04x\n", rom_info [0] ? "Y" : "N", CalculatedSize);
698 #endif
699     if (CalculatedSize + 5 >= len || 
700         CalculatedSize >= CMemory::MAX_ROM_SIZE)
701     {
702         S9xNPSetError ("Size error in ROM image data received from server.");
703         S9xNPDisconnect ();
704         return (FALSE);
705     }
706     
707     Memory.HiROM = rom_info [0];
708     Memory.LoROM = !Memory.HiROM;
709     Memory.HeaderCount = 0;
710     Memory.CalculatedSize = CalculatedSize;
711
712     // Load up ROM image
713 #ifdef NP_DEBUG
714     printf ("CLIENT: Receiving ROM image @%ld...\n", S9xGetMilliTime () - START);
715 #endif
716     S9xNPSetAction ("Receiving ROM image...");
717     if (!S9xNPGetData (NetPlay.Socket, Memory.ROM, Memory.CalculatedSize))
718     {
719         S9xNPSetError ("Error while receiving ROM image from server.");
720         Settings.StopEmulation = TRUE; 
721         S9xNPDisconnect ();
722         return (FALSE);
723     }
724 #ifdef NP_DEBUG
725     printf ("CLIENT: Receiving ROM filename @%ld...\n", S9xGetMilliTime () - START);
726 #endif
727     S9xNPSetAction ("Receiving ROM filename...");
728     uint32 filename_len = len - Memory.CalculatedSize - 5;
729     if (filename_len > _MAX_PATH ||
730         !S9xNPGetData (NetPlay.Socket, (uint8 *) Memory.ROMFilename, filename_len))
731     {
732         S9xNPSetError ("Error while receiving ROM filename from server.");
733         S9xNPDisconnect ();
734         Settings.StopEmulation = TRUE;
735         return (FALSE);
736     }
737     Memory.InitROM (FALSE);
738     S9xReset ();
739     S9xNPResetJoypadReadPos ();
740     Settings.StopEmulation = FALSE;
741
742 #ifdef __WIN32__
743     PostMessage (GUI.hWnd, WM_NULL, 0, 0);
744 #endif
745     
746     return (TRUE);
747 }
748
749 void S9xNPGetSRAMData (uint32 len)
750 {
751     if (len > 0x10000)
752     {
753         S9xNPSetError ("Length error in S-RAM data received from server.");
754         S9xNPDisconnect ();
755         return;
756     }
757     S9xNPSetAction ("Receiving S-RAM data...");
758     if (len > 0 && !S9xNPGetData (NetPlay.Socket, ::SRAM, len))
759     {
760         S9xNPSetError ("Error while receiving S-RAM data from server.");
761         S9xNPDisconnect ();
762     }
763 }
764
765 void S9xNPGetFreezeFile (uint32 len)
766 {
767     uint8 frame_count [4];
768
769 #ifdef NP_DEBUG
770     printf ("CLIENT: Receiving freeze file information @%ld...\n", S9xGetMilliTime () - START);
771 #endif
772     S9xNPSetAction ("Receiving freeze file information...");
773     if (!S9xNPGetData (NetPlay.Socket, frame_count, 4))
774     {
775         S9xNPSetError ("Error while receiving freeze file information from server.");
776         S9xNPDisconnect ();
777         return;
778     }
779     NetPlay.FrameCount = READ_LONG (frame_count);
780
781 #ifdef NP_DEBUG
782     printf ("CLIENT: Receiving freeze file @%ld...\n", S9xGetMilliTime () - START);
783 #endif
784     S9xNPSetAction ("Receiving freeze file...");
785     uint8 *data = new uint8 [len];
786     if (!S9xNPGetData (NetPlay.Socket, data, len - 4))
787     {
788         S9xNPSetError ("Error while receiving freeze file from server.");
789         S9xNPDisconnect ();
790         delete data;
791         return;
792     }
793
794     //FIXME: Setting umask here wouldn't hurt.
795     FILE *file;
796 #ifdef HAVE_MKSTEMP
797     int fd;
798     char fname[] = "/tmp/snes9x_fztmpXXXXXX";
799     if ((fd = mkstemp(fname)) < 0)
800     {
801         if ((file = fdopen (fd, "wb")))
802 #else
803     char fname [L_tmpnam];
804     if (tmpnam (fname))
805     {
806         if ((file = fopen (fname, "wb")))
807 #endif
808         {
809             if (fwrite (data, 1, len, file) == len)
810             {
811                 fclose(file);
812                 if (!S9xUnfreezeGame (fname))
813                     S9xNPSetError ("Unable to load freeze file just received.");
814             } else {
815                 S9xNPSetError ("Failed to write to temporary freeze file.");
816                 fclose (file);
817             }
818         } else
819             S9xNPSetError ("Failed to create temporary freeze file.");
820         remove (fname);
821     } else
822         S9xNPSetError ("Unable to get name for temporary freeze file.");
823     delete data;
824 }
825
826 uint32 S9xNPGetJoypad (int which1)
827 {
828     if (Settings.NetPlay && which1 < 5)
829         return (NetPlay.Joypads [NetPlay.JoypadReadInd][which1]);
830
831     return (0);
832 }
833
834 void S9xNPStepJoypadHistory ()
835 {
836     if ((NetPlay.JoypadReadInd + 1) % NP_JOYPAD_HIST_SIZE != NetPlay.JoypadWriteInd)
837     {
838         NetPlay.JoypadReadInd = (NetPlay.JoypadReadInd + 1) % NP_JOYPAD_HIST_SIZE;
839         if (NetPlay.FrameCount != NetPlay.Frame [NetPlay.JoypadReadInd])
840         {
841             S9xNPSetWarning ("This Snes9X session may be out of sync with the server.");
842 #ifdef NP_DEBUG
843             printf ("*** CLIENT: client out of sync with server (%d, %d) @%ld\n", NetPlay.FrameCount, NetPlay.Frame [NetPlay.JoypadReadInd], S9xGetMilliTime () - START);
844 #endif
845         }
846     }
847     else
848     {
849 #ifdef NP_DEBUG
850         printf ("*** CLIENT: S9xNPStepJoypadHistory NOT OK@%ld\n", S9xGetMilliTime () - START);
851 #endif
852     }
853 }
854
855
856 void S9xNPResetJoypadReadPos ()
857 {
858 #ifdef NP_DEBUG
859     printf ("CLIENT: ResetJoyReadPos @%ld\n", S9xGetMilliTime () - START); fflush (stdout);
860 #endif
861     NetPlay.JoypadWriteInd = 0;
862     NetPlay.JoypadReadInd = NP_JOYPAD_HIST_SIZE - 1;
863     for (int h = 0; h < NP_JOYPAD_HIST_SIZE; h++)
864         memset ((void *) &NetPlay.Joypads [h], 0, sizeof (NetPlay.Joypads [0]));
865 }
866
867 bool8 S9xNPSendJoypadUpdate (uint32 joypad)
868 {
869     uint8 data [7];
870     uint8 *ptr = data;
871
872     *ptr++ = NP_CLNT_MAGIC;
873     *ptr++ = NetPlay.MySequenceNum++;
874     *ptr++ = NP_CLNT_JOYPAD;
875
876     joypad |= 0x80000000;
877
878     WRITE_LONG (ptr, joypad);
879     if (!S9xNPSendData (NetPlay.Socket, data, 7))
880     {
881         S9xNPSetError ("Error while sending joypad data server.");
882         S9xNPDisconnect ();
883         return (FALSE);
884     }
885     return (TRUE);
886 }
887
888 void S9xNPDisconnect ()
889 {
890     close (NetPlay.Socket);
891     NetPlay.Socket = -1;
892     NetPlay.Connected = FALSE;
893     Settings.NetPlay = FALSE;
894 }
895
896 bool8 S9xNPSendData (int socket, const uint8 *data, int length)
897 {
898     int len = length;
899     const uint8 *ptr = data;
900
901     NetPlay.PercentageComplete = 0;
902
903     do
904     {
905         if (NetPlay.Abort)
906             return (FALSE);
907
908         int num_bytes = len;
909
910         // Write the data in small chunks, allowing this thread to spot an
911         // abort request from another thread.
912         if (num_bytes > 512)
913             num_bytes = 512;
914
915         int sent = write (socket, (char *) ptr, num_bytes);
916         if (sent < 0)
917         {
918             if (errno == EINTR 
919 #ifdef EAGAIN
920                 || errno == EAGAIN
921 #endif
922 #ifdef EWOULDBLOCK
923                 || errno == EWOULDBLOCK
924 #endif
925                 )
926             {
927 #ifdef NP_DEBUG
928                 printf ("CLIENT: EINTR, EAGAIN or EWOULDBLOCK while sending data @%ld\n", S9xGetMilliTime () - START);
929 #endif
930                 continue;
931             }
932             return (FALSE);
933         }
934         else
935         if (sent == 0)
936             return (FALSE);
937         len -= sent;
938         ptr += sent;
939
940         NetPlay.PercentageComplete = (uint8) (((length - len) * 100) / length);
941     } while (len > 0);
942
943     return (TRUE);
944 }
945
946 bool8 S9xNPGetData (int socket, uint8 *data, int length)
947 {
948     int len = length;
949     uint8 *ptr = data;
950     int chunk = length / 50;
951
952     if (chunk < 1024)
953         chunk = 1024;
954
955     NetPlay.PercentageComplete = 0;
956     do
957     {
958         if (NetPlay.Abort)
959             return (FALSE);
960
961         int num_bytes = len;
962
963         // Read the data in small chunks, allowing this thread to spot an
964         // abort request from another thread.
965         if (num_bytes > chunk)
966             num_bytes = chunk;
967
968         int got = read (socket, (char *) ptr, num_bytes);
969         if (got < 0)
970         {
971             if (errno == EINTR
972 #ifdef EAGAIN
973                 || errno == EAGAIN
974 #endif
975 #ifdef EWOULDBLOCK
976                 || errno == EWOULDBLOCK
977 #endif
978 #ifdef WSAEWOULDBLOCK
979                 || errno == WSAEWOULDBLOCK
980 #endif
981                 )
982             {
983 #ifdef NP_DEBUG
984                 printf ("CLIENT: EINTR, EAGAIN or EWOULDBLOCK while receiving data @%ld\n", S9xGetMilliTime () - START);
985 #endif
986                 continue;
987             }
988 #ifdef WSAEMSGSIZE
989             if (errno != WSAEMSGSIZE)
990                 return (FALSE);
991             else
992             {
993                 got = num_bytes;
994 #ifdef NP_DEBUG
995                 printf ("CLIENT: WSAEMSGSIZE, actual bytes %d while receiving data @%ld\n", got, S9xGetMilliTime () - START);
996 #endif
997             }
998 #else
999             return (FALSE);
1000 #endif
1001         }
1002         else
1003         if (got == 0)
1004             return (FALSE);
1005
1006         len -= got;
1007         ptr += got;
1008
1009         if (!Settings.NetPlayServer && length > 1024)
1010         {
1011             NetPlay.PercentageComplete = (uint8) (((length - len) * 100) / length);
1012 #ifdef __WIN32__
1013             PostMessage (GUI.hWnd, WM_USER, NetPlay.PercentageComplete, 
1014                          NetPlay.PercentageComplete);
1015             Sleep (0);
1016 #endif
1017         }
1018             
1019     } while (len > 0);
1020
1021     return (TRUE);
1022 }
1023
1024 bool8 S9xNPInitialise ()
1025 {
1026 #ifdef __WIN32__
1027     static bool8 initialised = FALSE;
1028
1029     if (!initialised)
1030     {
1031         initialised = TRUE;
1032         WSADATA data;
1033
1034 #ifdef NP_DEBUG
1035         START = S9xGetMilliTime ();
1036
1037         printf ("CLIENT/SERVER: Initialising WinSock @%ld\n", S9xGetMilliTime () - START);
1038 #endif
1039         S9xNPSetAction ("Initialising Windows sockets interface...");
1040         if (WSAStartup (MAKEWORD (1, 0), &data) != 0)
1041         {
1042             S9xNPSetError ("Call to init Windows sockets failed. Do you have WinSock2 installed?");
1043             return (FALSE); 
1044         }
1045     }
1046 #endif
1047     return (TRUE); 
1048 }
1049
1050 void S9xNPDiscardHeartbeats ()
1051 {
1052     // Discard any pending heartbeats and wait for any frame that is currently
1053     // being emulated to complete.
1054 #ifdef NP_DEBUG
1055     printf ("CLIENT: DiscardHeartbeats @%ld, finished @", S9xGetMilliTime () - START);
1056     fflush (stdout);
1057 #endif
1058
1059 #ifdef __WIN32__
1060     while (WaitForSingleObject (GUI.ClientSemaphore, 200) == WAIT_OBJECT_0)
1061         ;
1062 #endif
1063
1064 #ifdef NP_DEBUG
1065     printf ("%ld\n", S9xGetMilliTime () - START);
1066 #endif
1067     NetPlay.Waiting4EmulationThread = FALSE;
1068 }
1069
1070 void S9xNPSetAction (const char *action, bool8 force)
1071 {
1072     if (force || !Settings.NetPlayServer)
1073     {
1074         strncpy (NetPlay.ActionMsg, action, NP_MAX_ACTION_LEN - 1);
1075         NetPlay.ActionMsg [NP_MAX_ACTION_LEN - 1] = 0;
1076 #ifdef __WIN32__
1077         PostMessage (GUI.hWnd, WM_USER, 0, 0);
1078         Sleep (0);
1079 #endif
1080     }
1081 }
1082
1083 void S9xNPSetError (const char *error)
1084 {
1085     strncpy (NetPlay.ErrorMsg, error, NP_MAX_ACTION_LEN - 1);
1086     NetPlay.ErrorMsg [NP_MAX_ACTION_LEN - 1] = 0;
1087 #ifdef __WIN32
1088     PostMessage (GUI.hWnd, WM_USER + 1, 0, 0);
1089     Sleep (0);
1090 #endif
1091 }
1092
1093 void S9xNPSetWarning (const char *warning)
1094 {
1095     strncpy (NetPlay.WarningMsg, warning, NP_MAX_ACTION_LEN - 1);
1096     NetPlay.WarningMsg [NP_MAX_ACTION_LEN - 1] = 0;
1097 #ifdef __WIN32__
1098     PostMessage (GUI.hWnd, WM_USER + 2, 0, 0);
1099     Sleep (0);
1100 #endif
1101 }
1102 #endif
1103
1104