Files
ci-libretro-fceumm/src/drivers/win/netplay.c

435 lines
10 KiB
C

/* 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
};