fceux/drivers/win/netplay.cpp

494 lines
12 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 uint32 de32(uint8 *morp)
{
return(morp[0]|(morp[1]<<8)|(morp[2]<<16)|(morp[3]<<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] = (char*)malloc(strlen(buf) + 1); //mbg merge 7/17/06 added cast
strcpy(*strs[x], buf);
}
}
remotetport = GetDlgItemInt(hwndDlg,201,0,0);
netlocalplayers=1 + SendDlgItemMessage(hwndDlg,204,CB_GETCURSEL,0,(LPARAM)(LPSTR)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]=(char*)malloc(strlen(text)+1))) //mbg merge 7/17/06 added cast
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 = (char *)malloc(totallen); //mbg merge 7/17/06 added cast
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 = (char*)malloc(totallen); //mbg merge 7/17/06 added cast
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((char*)text); //mbg merge 7/17/06 added cast
}
int FCEUD_NetworkConnect(void)
{
WSADATA WSAData;
SOCKADDR_IN sockin; /* I want to play with fighting robots. */ /* clack clack clack razzzzzzzzzz */
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 = (uint8*)malloc(sblen); //mbg merge 7/17/06 added cast
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, (uint8*)netgamekey, strlen(netgamekey)); //mbg merge 7/17/06 added cast
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, (uint8*)netpassword, strlen(netpassword)); //mbg merge 7/17/06 added cast
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, (char*)sendbuf, sblen, 0); //mbg merge 7/17/06 added cast
free(sendbuf);
recv_tcpwrap(buf, 1);
netdivisor = buf[0];
}
}
FCEUI_NetplayStart(netlocalplayers,netdivisor);
NetStatAdd("*** Connection established.");
FCEUDnetplay = 1;
char tcpopt = 1; //mbg merge 7/17/06 changed to char
if(setsockopt(TSocket, IPPROTO_TCP, TCP_NODELAY, &tcpopt, sizeof(int)))
puts("Nodelay fail");
return(1);
}
int FCEUD_SendData(void *data, uint32 len)
{
send(Socket, (char*)data, len ,0); //mbg merge 7/17/06 added cast
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,(char*)buf,len,0); //mbg merge 7/17/06 added csat
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((uint8*)data,len)>0) //mbg merge 7/17/06 added cast
{
unsigned long beefie;
if(!ioctlsocket(Socket,FIONREAD,&beefie))
if(beefie)
NoWaiting|=2;
return(1);
}
else
return(0);
}
else
return(0);
}
return 0;
}
CFGSTRUCT NetplayConfig[]={
AC(remotetport),
AC(netlocalplayers),
ACS(netgamekey),
ACS(netplayhost),
ACS(netplaynick),
ACS(netpassword),
ENDCFGSTRUCT
};
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((uint8*)buf); //mbg merge 7/17/06 added cast
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;
}
void ShowNetplayConsole(void)
{
if(!netwin)
netwin=CreateDialog(fceu_hInstance,"NETMOO",0,NetCon);
}