gpfce patch
[fceu.git] / drivers / win / netplay.c
1 /* FCE Ultra - NES/Famicom Emulator\r
2  *\r
3  * Copyright notice for this file:\r
4  *  Copyright (C) 2002 Ben Parnell\r
5  *\r
6  * This program is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * This program is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with this program; if not, write to the Free Software\r
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19  */\r
20 \r
21 static char netplayhost[256]={0};\r
22 static int netplayport=0xFCE;\r
23 \r
24 static int netplayon=0;\r
25 static int netplaytype=0;\r
26 \r
27 static HWND hwndns=0;\r
28 \r
29 static SOCKET Socket=INVALID_SOCKET;\r
30 static int wsainit=0;\r
31 \r
32 static volatile int abortnetplay=0;\r
33 static volatile int concommand=0;\r
34 \r
35 static void WSE(char *ahh)\r
36 {\r
37  char tmp[256];\r
38  sprintf(tmp,"Winsock: %s",ahh);\r
39  FCEUD_PrintError(tmp);\r
40 }\r
41 \r
42 int SetBlockingSock(SOCKET Socko)\r
43 {\r
44    unsigned long t;\r
45    t=1;\r
46    if(ioctlsocket(Socko,FIONBIO,&t))\r
47    {\r
48     WSE("Error setting socket to non-blocking mode!\n");\r
49     return 0;\r
50    }\r
51   return 1;\r
52 }\r
53 \r
54 BOOL CALLBACK BoogaDooga(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
55 {\r
56   switch(uMsg) {\r
57    case WM_USER+1:\r
58                 if(WSAGETASYNCERROR(lParam))\r
59                  concommand=1;\r
60                 else\r
61                  concommand=2;\r
62                 break;\r
63    case WM_USER:\r
64                 if(WSAGETSELECTEVENT(lParam)==FD_CONNECT)\r
65                  {\r
66                   if(WSAGETSELECTERROR(lParam))\r
67                    concommand=1;\r
68                   else\r
69                    concommand=2;\r
70                  }                \r
71                 break;\r
72    case WM_INITDIALOG:\r
73                 if(!netplaytype) SetDlgItemText(hwndDlg,100,(LPTSTR)"Waiting for a connection...");\r
74                 else SetDlgItemText(hwndDlg,100,(LPTSTR)"Attempting to establish a connection...");                                 \r
75                 break;\r
76    case WM_CLOSE:\r
77    case WM_QUIT: goto gornk;\r
78    case WM_COMMAND:\r
79                 if(!(wParam>>16))\r
80                 switch(wParam&0xFFFF)\r
81                 {\r
82                   case 1:\r
83                         gornk:\r
84                         abortnetplay=1;\r
85                         EndDialog(hwndDlg,0);\r
86                         break;\r
87                }\r
88               }\r
89   return 0;\r
90 \r
91 }\r
92 \r
93 static void CloseNSDialog(void)\r
94 {\r
95  if(hwndns)\r
96   {\r
97    SendMessage(hwndns,WM_COMMAND,1,0);\r
98    hwndns=0;\r
99   }\r
100 }\r
101 \r
102 void CreateStatusDialog(void)\r
103 {\r
104  hwndns=CreateDialog(fceu_hInstance,"NETSTAT",hAppWnd,BoogaDooga);\r
105 }\r
106 \r
107 void FCEUD_NetworkClose(void)\r
108 {\r
109  CloseNSDialog();\r
110  if(Socket!=INVALID_SOCKET)\r
111  {\r
112   closesocket(Socket);\r
113   Socket=INVALID_SOCKET;\r
114  }\r
115  if(wsainit)\r
116  {\r
117   WSACleanup();\r
118   wsainit=0;\r
119  }\r
120  /* Make sure blocking is returned to normal once network play is stopped. */\r
121  NoWaiting&=~2;\r
122 }\r
123 \r
124 int FCEUD_NetworkConnect(void)\r
125 {\r
126  WSADATA WSAData;\r
127  SOCKADDR_IN sockin;    /* I want to play with fighting robots. */\r
128  SOCKET TSocket;\r
129 \r
130  if(WSAStartup(MAKEWORD(1,1),&WSAData))\r
131  {\r
132   FCEUD_PrintError("Error initializing Windows Sockets.");\r
133   return(0);\r
134  }\r
135  wsainit=1;\r
136  concommand=abortnetplay=0;\r
137 \r
138  if( (TSocket=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)\r
139  {\r
140   WSE("Error creating socket.");\r
141   FCEUD_NetworkClose();\r
142   return(0);\r
143  }\r
144 \r
145  memset(&sockin,0,sizeof(sockin));\r
146  sockin.sin_family=AF_INET;\r
147  sockin.sin_port=htons(netplayport);\r
148 \r
149  if(!netplaytype)       /* Act as a server. */\r
150  {\r
151 \r
152   int sockin_len;\r
153   sockin.sin_addr.s_addr=INADDR_ANY;\r
154 \r
155   sockin_len=sizeof(sockin);\r
156 \r
157   if(bind(TSocket,(struct sockaddr *)&sockin,sizeof(sockin))==SOCKET_ERROR)\r
158   {\r
159    WSE("Error binding to socket.");\r
160    closesocket(TSocket);\r
161    FCEUD_NetworkClose();\r
162    return(0);\r
163   }\r
164 \r
165   if(listen(TSocket,1)==SOCKET_ERROR)\r
166   {\r
167    WSE("Error listening on socket.");\r
168    closesocket(TSocket);\r
169    FCEUD_NetworkClose();\r
170    return(0);\r
171   }\r
172 \r
173   CreateStatusDialog();\r
174   if(!SetBlockingSock(TSocket))\r
175   {\r
176    closesocket(TSocket);\r
177    FCEUD_NetworkClose();\r
178    return(0);\r
179   }\r
180 \r
181   while( (Socket=accept(TSocket,(struct sockaddr *) &sockin,(int *)&sockin_len)) ==\r
182                 INVALID_SOCKET)\r
183   {\r
184    if(abortnetplay || WSAGetLastError()!=WSAEWOULDBLOCK)\r
185    {\r
186     if(!abortnetplay)\r
187      WSE("Error accepting connection.");\r
188     closesocket(TSocket);\r
189     FCEUD_NetworkClose();\r
190     return(0);\r
191    }\r
192    else\r
193     BlockingCheck();\r
194   }\r
195 \r
196   if(!SetBlockingSock(Socket))\r
197   {\r
198    FCEUD_NetworkClose();\r
199    return(0);\r
200   }\r
201 \r
202  }    \r
203  else   /* We're a client... */\r
204  {\r
205   char phostentb[MAXGETHOSTSTRUCT];\r
206   unsigned long hadr;\r
207 \r
208   hadr=inet_addr(netplayhost);\r
209 \r
210   CreateStatusDialog();\r
211 \r
212   if(hadr!=INADDR_NONE)\r
213    sockin.sin_addr.s_addr=hadr;\r
214   else\r
215   {\r
216    if(!WSAAsyncGetHostByName(hwndns,WM_USER+1,(const char *)netplayhost,phostentb,MAXGETHOSTSTRUCT))\r
217    {\r
218     ghosterr:\r
219     WSE("Error getting host network information.");\r
220     closesocket(TSocket);\r
221     FCEUD_NetworkClose();\r
222     return(0);\r
223    }\r
224    while(concommand!=2)\r
225    {\r
226     BlockingCheck();\r
227     if(concommand==1 || abortnetplay)\r
228      goto ghosterr;\r
229    }\r
230    memcpy((char *)&sockin.sin_addr,((PHOSTENT)phostentb)->h_addr,((PHOSTENT)phostentb)->h_length);\r
231   }\r
232   concommand=0;\r
233 \r
234   if(WSAAsyncSelect(TSocket,hwndns,WM_USER,FD_CONNECT|FD_CLOSE)==SOCKET_ERROR)\r
235   {\r
236    eventnoterr:\r
237    WSE("Error setting event notification on socket.");\r
238    closesocket(TSocket);\r
239    FCEUD_NetworkClose();\r
240    return(0);\r
241   }\r
242 \r
243   if(!SetBlockingSock(TSocket))\r
244   {\r
245    closesocket(TSocket);\r
246    FCEUD_NetworkClose();\r
247    return(0);\r
248   }\r
249 \r
250   if(connect(TSocket,(PSOCKADDR)&sockin,sizeof(sockin))==SOCKET_ERROR)\r
251   {\r
252    if(WSAGetLastError()!=WSAEWOULDBLOCK)\r
253    {\r
254     cerrav:\r
255     WSE("Error connecting to remote host.");\r
256 \r
257     cerra:\r
258     closesocket(TSocket);\r
259     FCEUD_NetworkClose();\r
260     return(0);\r
261    }\r
262 \r
263    while(concommand!=2)\r
264    {\r
265     BlockingCheck();        \r
266     if(abortnetplay) goto cerra;\r
267     if(concommand==1) goto cerrav;\r
268    }\r
269   }\r
270   if(WSAAsyncSelect(TSocket,hAppWnd,WM_USER,0)==SOCKET_ERROR)\r
271    goto eventnoterr;\r
272   Socket=TSocket;\r
273 \r
274  }\r
275  CloseNSDialog();\r
276  return(1);\r
277 }\r
278 \r
279 \r
280 int FCEUD_NetworkSendData(uint8 *data, uint32 len)\r
281 {\r
282         int erc;\r
283 \r
284         while((erc=send(Socket,data,len,0)))\r
285         {\r
286          if(erc!=SOCKET_ERROR)\r
287          {\r
288           len-=erc;\r
289           data+=erc;\r
290           if(!len)\r
291            return(1);   /* All data sent. */\r
292 \r
293           if(!BlockingCheck()) return(0);\r
294          }\r
295          else\r
296          {\r
297           if(WSAGetLastError()==WSAEWOULDBLOCK)\r
298           {\r
299            if(!BlockingCheck()) return(0);\r
300            continue;\r
301           }\r
302           return(0);\r
303          }\r
304         }\r
305         return(0);\r
306 }\r
307 \r
308 int FCEUD_NetworkRecvData(uint8 *data, uint32 len, int block)\r
309 {\r
310  int erc;\r
311 \r
312  if(block)      // TODO: Add code elsewhere to handle sound buffer underruns.\r
313  {\r
314   while((erc=recv(Socket,data,len,0))!=len)\r
315   {         \r
316          if(!erc)\r
317           return(0);\r
318          if(WSAGetLastError()==WSAEWOULDBLOCK)\r
319          {\r
320           if(!BlockingCheck()) return(0);\r
321           continue;\r
322          }\r
323          return(0);\r
324   }\r
325 \r
326   {\r
327    char buf[24];\r
328    if(recv(Socket,buf,24,MSG_PEEK)==SOCKET_ERROR)\r
329    {\r
330     if(WSAGetLastError()==WSAEWOULDBLOCK)\r
331      NoWaiting&=~2;\r
332     else\r
333      return(0);\r
334    }\r
335    else\r
336     NoWaiting|=2;       /* We're the client and we're lagging behind.\r
337                            disable blocking(particularly sound...) to *try*\r
338                            to catch up.\r
339                         */                          \r
340   }\r
341 \r
342   return 1;\r
343  }\r
344 \r
345  else   /* We're the server.  See if there's any new data\r
346            from player 2.  If not, then return(-1).\r
347         */\r
348  {\r
349   erc=recv(Socket,data,len,0);\r
350   if(!erc)\r
351    return(0);\r
352   if(erc==SOCKET_ERROR)\r
353   {\r
354    if(WSAGetLastError()==WSAEWOULDBLOCK)\r
355     return(-1);\r
356    return(0);   // Some other(bad) error occurred.\r
357   }\r
358   return(1);\r
359  } // end else to if(block)\r
360 }\r
361 \r
362 BOOL CALLBACK NetConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
363 {\r
364   switch(uMsg) {\r
365    case WM_INITDIALOG:                \r
366 \r
367                 CheckDlgButton(hwndDlg,100,netplayon?BST_CHECKED:BST_UNCHECKED);\r
368                 CheckRadioButton(hwndDlg,101,102,101+netplaytype);\r
369                 SetDlgItemInt(hwndDlg,107,netplayport,0);\r
370 \r
371                 if(netplayhost[0])\r
372                  SetDlgItemText(hwndDlg,104,netplayhost);\r
373                 break;\r
374    case WM_CLOSE:\r
375    case WM_QUIT: goto gornk;\r
376    case WM_COMMAND:\r
377                 if(!(wParam>>16))\r
378                 switch(wParam&0xFFFF)\r
379                 {\r
380                  case 1:\r
381                         gornk:\r
382 \r
383                         netplayport=GetDlgItemInt(hwndDlg,107,0,0);\r
384 \r
385                         if(IsDlgButtonChecked(hwndDlg,100)==BST_CHECKED)\r
386                          netplayon=1;\r
387                         else\r
388                          netplayon=0;\r
389 \r
390                         if(IsDlgButtonChecked(hwndDlg,101)==BST_CHECKED)\r
391                          netplaytype=0;\r
392                         else\r
393                          netplaytype=1;\r
394 \r
395                         GetDlgItemText(hwndDlg,104,netplayhost,255);\r
396                         netplayhost[255]=0;\r
397 \r
398                         EndDialog(hwndDlg,0);\r
399                         break;\r
400                }\r
401               }\r
402   return 0;\r
403 }\r
404 \r
405 \r
406 static void ConfigNetplay(void)\r
407 {\r
408  DialogBox(fceu_hInstance,"NETPLAYCONFIG",hAppWnd,NetConCallB);\r
409 \r
410  if(netplayon)\r
411   FCEUI_SetNetworkPlay(netplaytype+1);\r
412  else\r
413   FCEUI_SetNetworkPlay(0);\r
414 }\r
415 \r