/* FCE Ultra - NES/Famicom Emulator * * Copyright notice for this file: * Copyright (C) 2002 Xodnizel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include "../../md5.h" static int recv_tcpwrap(uint8 *buf, int len); static void NetStatAdd(char *text); static HWND netwin = 0; static char *netstatt[64]; static int netstattcount = 0; static int netlocalplayers = 1; static char *netplayhost = 0; static char *netplaynick = 0; static char *netgamekey = 0; static char *netpassword = 0; static int remotetport = 0xFCE; static SOCKET Socket = INVALID_SOCKET; static int wsainit = 0; int FCEUDnetplay = 0; static void WSE(char *ahh) { char tmp[256]; sprintf(tmp, "*** Winsock: %s", ahh); NetStatAdd(tmp); } static void en32(uint8 *buf, uint32 morp) { buf[0] = morp; buf[1] = morp >> 8; buf[2] = morp >> 16; buf[3] = morp >> 24; } static void FixCDis(HWND hParent, int how); void FCEUD_NetworkClose(void) { NetStatAdd("*** Connection lost."); if (netwin) { SetDlgItemText(netwin, 250, "Connect"); FixCDis(netwin, 1); } if (Socket != INVALID_SOCKET) { closesocket(Socket); Socket = INVALID_SOCKET; } if (wsainit) { WSACleanup(); wsainit = 0; } /* Make sure blocking is returned to normal once network play is stopped. */ NoWaiting &= ~2; FCEUDnetplay = 0; FCEUI_NetplayStop(); } static void FixCDis(HWND hParent, int how) { int x; for (x = 200; x <= 206; x++) EnableWindow(GetDlgItem(hParent, x), how); } static void GetSettings(HWND hwndDlg) { char buf[256]; char **strs[4] = { &netplayhost, &netplaynick, &netgamekey, &netpassword }; int ids[4] = { 200, 203, 205, 206 }; int x; for (x = 0; x < 4; x++) { GetDlgItemText(hwndDlg, ids[x], buf, 256); if (*strs[x]) { free(*strs[x]); *strs[x] = 0; } if (buf[0]) { *strs[x] = malloc(strlen(buf) + 1); strcpy(*strs[x], buf); } } remotetport = GetDlgItemInt(hwndDlg, 201, 0, 0); netlocalplayers = 1 + SendDlgItemMessage(hwndDlg, 204, CB_GETCURSEL, 0, (LPARAM)(LPSTR)0); } int FCEUD_NetworkConnect(); static BOOL CALLBACK NetCon(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CLOSE: GetSettings(hwndDlg); DestroyWindow(hwndDlg); netwin = 0; FCEUD_NetworkClose(); break; case WM_COMMAND: if (HIWORD(wParam) == BN_CLICKED) { switch (LOWORD(wParam)) { case 250: if (FCEUDnetplay) { FCEUD_NetworkClose(); SetDlgItemText(hwndDlg, 250, "Connect"); FixCDis(hwndDlg, 1); } else if (GI) { GetSettings(hwndDlg); if (FCEUD_NetworkConnect()) { SetDlgItemText(hwndDlg, 250, "Disconnect"); FixCDis(hwndDlg, 0); } } break; } } else if (HIWORD(wParam) == EN_CHANGE && Socket != INVALID_SOCKET) { char buf[1024]; int t; t = GetDlgItemText(hwndDlg, 102, buf, 1024); buf[1023] = 0; if (strchr(buf, '\r')) { char *src, *dest; src = dest = buf; while (*src) { if (*src != '\n' && *src != '\r') { *dest = *src; dest++; } src++; } *dest = 0; FCEUI_NetplayText(buf); SetDlgItemText(hwndDlg, 102, ""); } } break; case WM_INITDIALOG: if (netplayhost) SetDlgItemText(hwndDlg, 200, netplayhost); SetDlgItemInt(hwndDlg, 201, remotetport, 0); if (netplaynick) SetDlgItemText(hwndDlg, 203, netplaynick); if (netgamekey) SetDlgItemText(hwndDlg, 205, netgamekey); if (netpassword) SetDlgItemText(hwndDlg, 206, netpassword); { int x; char buf[8]; for (x = 0; x < 4; x++) { sprintf(buf, "%d", x + 1); SendDlgItemMessage(hwndDlg, 204, CB_ADDSTRING, 0, (LPARAM)(LPSTR)buf); } SendDlgItemMessage(hwndDlg, 204, CB_SETCURSEL, netlocalplayers - 1, (LPARAM)(LPSTR)0); } break; } return 0; } static void NetStatAdd(char *text) { int x; uint32 totallen = 0; char *textbuf; if (!netwin) return; if (netstattcount >= 64) free(netstatt[netstattcount & 63]); if (!(netstatt[netstattcount & 63] = malloc(strlen(text) + 1))) return; strcpy(netstatt[netstattcount & 63], text); netstattcount++; if (netstattcount >= 64) { for (x = netstattcount & 63;; ) { totallen += strlen(netstatt[x]); x = (x + 1) & 63; if (x == (netstattcount & 63)) break; totallen += 2; } totallen++; // NULL textbuf = malloc(totallen); textbuf[0] = 0; for (x = netstattcount & 63;; ) { strcat(textbuf, netstatt[x]); x = (x + 1) & 63; if (x == (netstattcount & 63)) break; strcat(textbuf, "\r\n"); } } else { for (x = 0; x < netstattcount; x++) { totallen += strlen(netstatt[x]); if (x < (netstattcount - 1)) totallen += 2; } totallen++; textbuf = malloc(totallen); textbuf[0] = 0; for (x = 0; x < netstattcount; x++) { strcat(textbuf, netstatt[x]); if (x < (netstattcount - 1)) strcat(textbuf, "\r\n"); } } SetDlgItemText(netwin, 101, textbuf); free(textbuf); SendDlgItemMessage(netwin, 101, EM_LINESCROLL, 0, 32767); } void FCEUD_NetplayText(uint8 *text) { NetStatAdd(text); } int FCEUD_NetworkConnect(void) { WSADATA WSAData; SOCKADDR_IN sockin; /* I want to play with fighting robots. */ SOCKET TSocket; int netdivisor; if (WSAStartup(MAKEWORD(1, 1), &WSAData)) { NetStatAdd("*** Error initializing WIndows Sockets."); return(0); } wsainit = 1; if ((TSocket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { WSE("Error creating stream socket."); FCEUD_NetworkClose(); return(0); } memset(&sockin, 0, sizeof(sockin)); sockin.sin_family = AF_INET; { struct hostent *phostentb; unsigned long hadr; int sockin_len; sockin.sin_port = 0; sockin.sin_addr.s_addr = INADDR_ANY; sockin_len = sizeof(sockin); hadr = inet_addr(netplayhost); if (hadr != INADDR_NONE) sockin.sin_addr.s_addr = hadr; else { NetStatAdd("*** Looking up host name..."); if (!(phostentb = gethostbyname((const char*)netplayhost))) { WSE("Error getting host network information."); closesocket(TSocket); FCEUD_NetworkClose(); return(0); } memcpy((char*)&sockin.sin_addr, ((PHOSTENT)phostentb)->h_addr, ((PHOSTENT)phostentb)->h_length); } sockin.sin_port = htons(remotetport); NetStatAdd("*** Connecting to remote host..."); if (connect(TSocket, (PSOCKADDR)&sockin, sizeof(sockin)) == SOCKET_ERROR) { WSE("Error connecting to remote host."); closesocket(TSocket); FCEUD_NetworkClose(); return(0); } Socket = TSocket; NetStatAdd("*** Sending initialization data to server..."); { uint8 *sendbuf; uint8 buf[1]; uint32 sblen; sblen = 4 + 16 + 16 + 64 + 1 + (netplaynick ? strlen(netplaynick) : 0); sendbuf = malloc(sblen); memset(sendbuf, 0, sblen); en32(sendbuf, sblen - 4); if (netgamekey) { struct md5_context md5; uint8 md5out[16]; md5_starts(&md5); md5_update(&md5, GI->MD5, 16); md5_update(&md5, netgamekey, strlen(netgamekey)); md5_finish(&md5, md5out); memcpy(sendbuf + 4, md5out, 16); } else memcpy(sendbuf + 4, GI->MD5, 16); if (netpassword) { struct md5_context md5; uint8 md5out[16]; md5_starts(&md5); md5_update(&md5, netpassword, strlen(netpassword)); md5_finish(&md5, md5out); memcpy(sendbuf + 4 + 16, md5out, 16); } memset(sendbuf + 4 + 16 + 16, 0, 64); sendbuf[4 + 16 + 16 + 64] = netlocalplayers; if (netplaynick) memcpy(sendbuf + 4 + 16 + 16 + 64 + 1, netplaynick, strlen(netplaynick)); send(Socket, sendbuf, sblen, 0); free(sendbuf); recv_tcpwrap(buf, 1); netdivisor = buf[0]; } } FCEUI_NetplayStart(netlocalplayers, netdivisor); NetStatAdd("*** Connection established."); FCEUDnetplay = 1; int tcpopt = 1; if (setsockopt(TSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&tcpopt, sizeof(int))) puts("Nodelay fail"); return(1); } int FCEUD_SendData(void *data, uint32 len) { send(Socket, data, len, 0); return(1); } static int recv_tcpwrap(uint8 *buf, int len) { fd_set fdoo; int t; struct timeval popeye; popeye.tv_sec = 0; popeye.tv_usec = 100000; while (len) { FD_ZERO(&fdoo); FD_SET(Socket, &fdoo); switch (select(0, &fdoo, 0, 0, &popeye)) { case 0: //BlockingCheck(); continue; case SOCKET_ERROR: return(0); } t = recv(Socket, buf, len, 0); if (t <= 0) return(0); len -= t; buf += t; } return(1); } int FCEUD_RecvData(void *data, uint32 len) { NoWaiting &= ~2; for (;; ) { fd_set funfun; struct timeval popeye; popeye.tv_sec = 0; popeye.tv_usec = 100000; FD_ZERO(&funfun); FD_SET(Socket, &funfun); switch (select(0, &funfun, 0, 0, &popeye)) { case 0: continue; case SOCKET_ERROR: return(0); } if (FD_ISSET(Socket, &funfun)) { if (recv_tcpwrap(data, len) > 0) { unsigned long beefie; if (!ioctlsocket(Socket, FIONREAD, &beefie)) if (beefie) NoWaiting |= 2; return(1); } else return(0); } else return(0); } return 0; } void ShowNetplayConsole(void) { if (!netwin) netwin = CreateDialog(fceu_hInstance, "NETMOO", 0, NetCon); } CFGSTRUCT NetplayConfig[] = { AC(remotetport), AC(netlocalplayers), ACS(netgamekey), ACS(netplayhost), ACS(netplaynick), ACS(netpassword), ENDCFGSTRUCT };