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 |
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 |