gpfce patch
[fceu.git] / drivers / win / netplay.c
CommitLineData
c62d2810 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
21static char netplayhost[256]={0};\r
22static int netplayport=0xFCE;\r
23\r
24static int netplayon=0;\r
25static int netplaytype=0;\r
26\r
27static HWND hwndns=0;\r
28\r
29static SOCKET Socket=INVALID_SOCKET;\r
30static int wsainit=0;\r
31\r
32static volatile int abortnetplay=0;\r
33static volatile int concommand=0;\r
34\r
35static 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
42int 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
54BOOL 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
93static 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
102void CreateStatusDialog(void)\r
103{\r
104 hwndns=CreateDialog(fceu_hInstance,"NETSTAT",hAppWnd,BoogaDooga);\r
105}\r
106\r
107void 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
124int 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
280int 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
308int 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
362BOOL 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
406static 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