// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. // Copyright (C) 1999-2003 Forgotten // Copyright (C) 2004 Forgotten and the VBA development team // This file was written by denopqrihg // 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, 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. // Link.cpp : Emulation of GBA link accessories // #include "GBA.h" #include #include "win32/stdafx.h" #include "port.h" #include "Link.h" #include "win32/vba.h" #include "win32/MainWnd.h" #include "win32/LinkOptions.h" #include "win32/Reg.h" #define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value) #define GBLINK_READY 8 int linktime = 0; u8 tspeed=3; u8 transfer=0; LINKDATA *linkmem=NULL; int linkid = 0, vbaid = 0; HANDLE linksync[4]; int savedlinktime=0; HANDLE mmf=NULL; char linkevent[] = "VBA link event "; static int i, j; int linktimeout = 1000; int linklog = 0; FILE *linklogfile = NULL; LANLINKDATA lanlink; u16 linkdata[4]; int lspeed = 0; lserver ls; lclient lc; bool oncewait = false, after = false; bool adapter = false; bool linkenable = false; u8 rfu_cmd, rfu_qsend, rfu_qrecv; int rfu_state, rfu_polarity, linktime2, counter, rfu_masterq; int transferend, numtransfers = 0; u32 rfu_masterdata[32]; extern unsigned char *gbMemory; extern int gbInterrupt; int trtimedata[4][4] = {{34080, 8520, 5680, 2840}, {65536, 16384, 10923, 5461}, {99609, 24903, 16602, 8301}, {133692, 33423, 22282, 11141}}; int trtimeend[3][4] = {{72527, 18132, 12088, 6044}, {106608, 26652, 17768, 8884}, {133692, 33423, 22282, 11141}}; int gbtime = 1024; DWORD WINAPI LinkClientThread(void *); DWORD WINAPI LinkServerThread(void *); int StartServer(void); int GetSioMode(u16, u16); u16 StartRFU(u16); char *MakeInstanceFilename(const char *Input) { if (vbaid == 0) return (char *)Input; static char *result=NULL; if (result!=NULL) free(result); result = (char *)malloc(strlen(Input)+3); char *p = strrchr((char *)Input, '.'); sprintf(result, "%.*s-%d.%s", (int)(p-Input), Input, vbaid+1, p+1); return result; } void StartLink(WORD value){ if(ioMem==NULL) return; if(adapter){ UPDATE_REG(0x128, StartRFU(value)); return; } switch(GetSioMode(value, READ16LE(&ioMem[0x134]))){ case MULTIPLAYER: if(value & 0x80){ if(!linkid){ if(!transfer){ if(lanlink.active){ if(lanlink.connected){ linkdata[0] = READ16LE(&ioMem[0x12a]); savedlinktime = linktime; tspeed = value & 3; ls.Send(); transfer = 1; linktime = 0; UPDATE_REG(0x120, linkdata[0]); UPDATE_REG(0x122, 0xffff); WRITE32LE(&ioMem[0x124], 0xffffffff); if(lanlink.speed&&oncewait==false) ls.howmanytimes++; after = false; } } else if(linkmem->numgbas>1){ ResetEvent(linksync[0]); linkmem->linkcmd[0] = ('M'<<8)+(value&3); linkmem->linkdata[0] = READ16LE(&ioMem[0x12a]); if(linkmem->numtransfers!=0) linkmem->lastlinktime = linktime; else linkmem->lastlinktime = 0; if((++linkmem->numtransfers)==0) linkmem->numtransfers=2; transfer = 1; linktime = 0; tspeed = value & 3; WRITE32LE(&ioMem[0x120], 0xffffffff); WRITE32LE(&ioMem[0x124], 0xffffffff); } } } value &= 0xff7f; value |= (transfer!=0)<<7; } value &= 0xff8b; value |= (linkid ? 0xc : 8); value |= linkid<<4; UPDATE_REG(0x128, value); if(linkid) UPDATE_REG(0x134, 7); else UPDATE_REG(0x134, 3); break; case NORMAL8: if(linklog) fprintf(linklogfile, "Attempt to use 8-bit Normal mode %04x\n", value); UPDATE_REG(0x128, value); break; case NORMAL32: if(linklog) fprintf(linklogfile, "Attempt to use 32-bit Normal mode %04x %x%x\n", value, READ16LE(&ioMem[0x122]), READ16LE(&ioMem[0x120])); UPDATE_REG(0x128, value); break; case UART: if(linklog) fprintf(linklogfile, "Attempt to use UART mode %04x\n", value); UPDATE_REG(0x128, value); break; default: UPDATE_REG(0x128, value); break; } } void StartGPLink(u16 value){ if(!value){ UPDATE_REG(0x134, 0); return; } switch(GetSioMode(READ16LE(&ioMem[0x128]), value)){ case MULTIPLAYER: value &= 0xc0f0; value |= 3; if(linkid) value |= 4; UPDATE_REG(0x134, value); UPDATE_REG(0x128, ((READ16LE(&ioMem[0x128])&0xff8b)|(linkid ? 0xc : 8)|(linkid<<4))); return; break; case GP: if(linklog){ if(value==0x8000) fprintf(linklogfile, "Circuit reset\n"); else if(!adapter) fprintf(linklogfile, "Attempt to use General-purpose mode %04x\n", value); } if(adapter) rfu_state = RFU_INIT; // This was not there, but sonic games won't start if it's not here. UPDATE_REG(0x134, value); break; case JOYBUS: UPDATE_REG(0x134, value); break; default: UPDATE_REG(0x134, value); break; } return; } void StartJOYLink(u16 value){ if(!value){ UPDATE_REG(0x140, 0); return; } if(GetSioMode(READ16LE(&ioMem[0x128]), READ16LE(&ioMem[0x134]))==JOYBUS&&linklog) fprintf(linklogfile, "Attempt to use JOY-BUS mode %04x\n", value); return; } void LinkUpdate(int ticks){ linktime += ticks; if(adapter){ linktime2 += ticks; transferend -= ticks; if(transfer&&transferend<=0){ transfer = 0; if(READ16LE(&ioMem[0x128])&0x4000){ IF |= 0x80; UPDATE_REG(0x202, IF); } UPDATE_REG(0x128, READ16LE(&ioMem[0x128]) & 0xff7f); } return; } if(lanlink.active){ if(lanlink.connected){ if(after){ if(linkid&&linktime>6044){ lc.Recv(); oncewait = true; } else return; } if(linkid&&!transfer&&lc.numtransfers>0&&linktime>=savedlinktime){ linkdata[linkid] = READ16LE(&ioMem[0x12a]); if(!lc.oncesend) lc.Send(); lc.oncesend = false; UPDATE_REG(0x120, linkdata[0]); UPDATE_REG(0x128, READ16LE(&ioMem[0x128]) | 0x80); transfer = 1; if(lc.numtransfers==1) linktime = 0; else linktime -= savedlinktime; } if(transfer&&linktime>=trtimeend[lanlink.numgbas-1][tspeed]){ if(READ16LE(&ioMem[0x128]) & 0x4000){ IF |= 0x80; UPDATE_REG(0x202, IF); } UPDATE_REG(0x128, (READ16LE(&ioMem[0x128]) & 0xff0f) | (linkid << 4)); transfer = 0; linktime -= trtimeend[lanlink.numgbas-1][tspeed]; oncewait = false; if(!lanlink.speed){ if(linkid) lc.Recv(); else ls.Recv(); UPDATE_REG(0x122, linkdata[1]); UPDATE_REG(0x124, linkdata[2]); UPDATE_REG(0x126, linkdata[3]); if(linklog) fprintf(linklogfile, "%04x %04x %04x %04x %10u\n", linkdata[0], linkdata[1], linkdata[2], linkdata[3], savedlinktime); oncewait = true; } else { after = true; if(lanlink.numgbas==1){ UPDATE_REG(0x122, linkdata[1]); UPDATE_REG(0x124, linkdata[2]); UPDATE_REG(0x126, linkdata[3]); if(linklog) fprintf(linklogfile, "%04x %04x %04x %04x %10u\n", linkdata[0], linkdata[1], linkdata[2], linkdata[3], savedlinktime); } } } } return; } // ** CRASH ** linkmem is NULL, todo investigate why, added null check if(linkid&&!transfer&&linkmem&&linktime>=linkmem->lastlinktime&&linkmem->numtransfers){ linkmem->linkdata[linkid] = READ16LE(&ioMem[0x12a]); if(linkmem->numtransfers==1){ linktime = 0; if(WaitForSingleObject(linksync[linkid], linktimeout)==WAIT_TIMEOUT) linkmem->numtransfers=0; } else linktime -= linkmem->lastlinktime; switch((linkmem->linkcmd[0])>>8){ case 'M': tspeed = (linkmem->linkcmd[0]) & 3; transfer = 1; WRITE32LE(&ioMem[0x120], 0xffffffff); WRITE32LE(&ioMem[0x124], 0xffffffff); UPDATE_REG(0x128, READ16LE(&ioMem[0x128]) | 0x80); break; } } if(!transfer) return; if(transfer&&linktime>=trtimedata[transfer-1][tspeed]&&transfer<=linkmem->numgbas){ if(transfer-linkid==2){ SetEvent(linksync[linkid+1]); if(WaitForSingleObject(linksync[linkid], linktimeout)==WAIT_TIMEOUT) linkmem->numtransfers=0; ResetEvent(linksync[linkid]); if(linklog) fprintf(linklogfile, "%04x %04x %04x %04x %10u\n", linkmem->linkdata[0], linkmem->linkdata[1], linkmem->linkdata[2], linkmem->linkdata[3], linkmem->lastlinktime); } UPDATE_REG(0x11e + (transfer<<1), linkmem->linkdata[transfer-1]); transfer++; } if(transfer&&linktime>=trtimeend[linkmem->numgbas-2][tspeed]){ if(linkid==linkmem->numgbas-1){ SetEvent(linksync[0]); if(WaitForSingleObject(linksync[linkid], linktimeout)==WAIT_TIMEOUT) linkmem->numtransfers=0; ResetEvent(linksync[linkid]); if(linklog) fprintf(linklogfile, "%04x %04x %04x %04x %10u\n", linkmem->linkdata[0], linkmem->linkdata[1], linkmem->linkdata[2], linkmem->linkdata[3], linkmem->lastlinktime); } transfer = 0; linktime -= trtimeend[0][tspeed]; if(READ16LE(&ioMem[0x128]) & 0x4000){ IF |= 0x80; UPDATE_REG(0x202, IF); } UPDATE_REG(0x128, (READ16LE(&ioMem[0x128]) & 0xff0f) | (linkid << 4)); linkmem->linkdata[linkid] = 0xffff; } return; } inline int GetSioMode(u16 reg1, u16 reg2){ if(!(reg2&0x8000)){ switch(reg1&0x3000){ case 0x0000: return NORMAL8; case 0x1000: return NORMAL32; case 0x2000: return MULTIPLAYER; case 0x3000: return UART; } } if(reg2&0x4000) return JOYBUS; return GP; } u16 StartRFU(u16 value){ switch(GetSioMode(value, READ16LE(&ioMem[0x134]))){ case NORMAL8: rfu_polarity = 0; return value; break; case NORMAL32: if(value&8) value &= 0xfffb; // A kind of acknowledge procedure else value |= 4; if(value&0x80){ if((value&3)==1) transferend = 2048; else transferend = 256; u16 a = READ16LE(&ioMem[0x122]); switch(rfu_state){ case RFU_INIT: if(READ32LE(&ioMem[0x120])==0xb0bb8001){ rfu_state = RFU_COMM; // end of startup } UPDATE_REG(0x122, READ16LE(&ioMem[0x120])); UPDATE_REG(0x120, a); break; case RFU_COMM: if(a==0x9966){ rfu_cmd = ioMem[0x120]; if((rfu_qsend=ioMem[0x121])!=0){ rfu_state = RFU_SEND; counter = 0; } if(rfu_cmd==0x25||rfu_cmd==0x24){ linkmem->rfu_q[vbaid] = rfu_qsend; } UPDATE_REG(0x120, 0); UPDATE_REG(0x122, 0x8000); } else if(a==0x8000){ switch(rfu_cmd){ case 0x1a: // check if someone joined if(linkmem->rfu_request[vbaid]!=0){ rfu_state = RFU_RECV; rfu_qrecv = 1; } linkid = -1; rfu_cmd |= 0x80; break; case 0x1e: // receive broadcast data case 0x1d: // no visible difference rfu_polarity = 0; rfu_state = RFU_RECV; rfu_qrecv = 7; counter = 0; rfu_cmd |= 0x80; break; case 0x30: linkmem->rfu_request[vbaid] = 0; linkmem->rfu_q[vbaid] = 0; linkid = 0; numtransfers = 0; rfu_cmd |= 0x80; if(linkmem->numgbas==2) SetEvent(linksync[1-vbaid]); break; case 0x11: // ? always receives 0xff - I suspect it's something for 3+ players case 0x13: // unknown case 0x20: // this has something to do with 0x1f case 0x21: // this too rfu_cmd |= 0x80; rfu_polarity = 0; rfu_state = 3; rfu_qrecv = 1; break; case 0x26: if(linkid>0){ rfu_qrecv = rfu_masterq; } if((rfu_qrecv=linkmem->rfu_q[1-vbaid])!=0){ rfu_state = RFU_RECV; counter = 0; } rfu_cmd |= 0x80; break; case 0x24: // send data if((numtransfers++)==0) linktime = 1; linkmem->rfu_linktime[vbaid] = linktime; if(linkmem->numgbas==2){ SetEvent(linksync[1-vbaid]); WaitForSingleObject(linksync[vbaid], linktimeout); ResetEvent(linksync[vbaid]); } rfu_cmd |= 0x80; linktime = 0; linkid = -1; break; case 0x25: // send & wait for data case 0x1f: // pick a server case 0x10: // init case 0x16: // send broadcast data case 0x17: // setup or something ? case 0x27: // wait for data ? case 0x3d: // init default: rfu_cmd |= 0x80; break; case 0xa5: // 2nd part of send&wait function 0x25 case 0xa7: // 2nd part of wait function 0x27 if(linkid==-1){ linkid++; linkmem->rfu_linktime[vbaid] = 0; } if(linkid&&linkmem->rfu_request[1-vbaid]==0){ linkmem->rfu_q[1-vbaid] = 0; transferend = 256; rfu_polarity = 1; rfu_cmd = 0x29; linktime = 0; break; } if((numtransfers++)==0) linktime = 0; linkmem->rfu_linktime[vbaid] = linktime; if(linkmem->numgbas==2){ if(!linkid||(linkid&&numtransfers)) SetEvent(linksync[1-vbaid]); WaitForSingleObject(linksync[vbaid], linktimeout); ResetEvent(linksync[vbaid]); } if(linkid>0){ memcpy(rfu_masterdata, linkmem->rfu_data[1-vbaid], 128); rfu_masterq = linkmem->rfu_q[1-vbaid]; } transferend = linkmem->rfu_linktime[1-vbaid] - linktime + 256; if(transferend<256) transferend = 256; linktime = -transferend; rfu_polarity = 1; rfu_cmd = 0x28; break; } UPDATE_REG(0x122, 0x9966); UPDATE_REG(0x120, (rfu_qrecv<<8) | rfu_cmd); } else { UPDATE_REG(0x120, 0); UPDATE_REG(0x122, 0x8000); } break; case RFU_SEND: if(--rfu_qsend==0) rfu_state = RFU_COMM; switch(rfu_cmd){ case 0x16: linkmem->rfu_bdata[vbaid][counter++] = READ32LE(&ioMem[0x120]); break; case 0x17: linkid = 1; break; case 0x1f: linkmem->rfu_request[1-vbaid] = 1; break; case 0x24: case 0x25: linkmem->rfu_data[vbaid][counter++] = READ32LE(&ioMem[0x120]); break; } UPDATE_REG(0x120, 0); UPDATE_REG(0x122, 0x8000); break; case RFU_RECV: if(--rfu_qrecv==0) rfu_state = RFU_COMM; switch(rfu_cmd){ case 0x9d: case 0x9e: if(counter==0){ UPDATE_REG(0x120, 0x61f1); UPDATE_REG(0x122, 0); counter++; break; } UPDATE_REG(0x120, linkmem->rfu_bdata[1-vbaid][counter-1]&0xffff); UPDATE_REG(0x122, linkmem->rfu_bdata[1-vbaid][counter-1]>>16); counter++; break; case 0xa6: if(linkid>0){ UPDATE_REG(0x120, rfu_masterdata[counter]&0xffff); UPDATE_REG(0x122, rfu_masterdata[counter++]>>16); } else { UPDATE_REG(0x120, linkmem->rfu_data[1-vbaid][counter]&0xffff); UPDATE_REG(0x122, linkmem->rfu_data[1-vbaid][counter++]>>16); } break; case 0x93: // it seems like the game doesn't care about this value UPDATE_REG(0x120, 0x1234); // put anything in here UPDATE_REG(0x122, 0x0200); // also here, but it should be 0200 break; case 0xa0: case 0xa1: UPDATE_REG(0x120, 0x641b); UPDATE_REG(0x122, 0x0000); break; case 0x9a: UPDATE_REG(0x120, 0x61f9); UPDATE_REG(0x122, 0); break; case 0x91: UPDATE_REG(0x120, 0x00ff); UPDATE_REG(0x122, 0x0000); break; default: UPDATE_REG(0x120, 0x0173); UPDATE_REG(0x122, 0x0000); break; } break; } transfer = 1; } if(rfu_polarity) value ^= 4; // sometimes it's the other way around default: return value; } } void gbLinkStart(u8 value){ // Not in this version :-) } void gbLinkUpdate(void){ } int InitLink(void){ WSADATA wsadata; BOOL disable = true; linkid = 0; if(WSAStartup(MAKEWORD(1,1), &wsadata)!=0){ WSACleanup(); return 0; } if((lanlink.tcpsocket=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET){ MessageBox(NULL, "Couldn't create socket.", "Error!", MB_OK); WSACleanup(); return 0; } setsockopt(lanlink.tcpsocket, IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); if((mmf=CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(LINKDATA), "VBA link memory"))==NULL){ closesocket(lanlink.tcpsocket); WSACleanup(); MessageBox(NULL, "Error creating file mapping", "Error", MB_OK|MB_ICONEXCLAMATION); return 0; } if(GetLastError() == ERROR_ALREADY_EXISTS) vbaid = 1; else vbaid = 0; if((linkmem=(LINKDATA *)MapViewOfFile(mmf, FILE_MAP_WRITE, 0, 0, sizeof(LINKDATA)))==NULL){ closesocket(lanlink.tcpsocket); WSACleanup(); CloseHandle(mmf); MessageBox(NULL, "Error mapping file", "Error", MB_OK|MB_ICONEXCLAMATION); return 0; } if(linkmem->linkflags&LINK_PARENTLOST) vbaid = 0; if(vbaid==0){ linkid = 0; if(linkmem->linkflags&LINK_PARENTLOST){ linkmem->numgbas++; linkmem->linkflags &= ~LINK_PARENTLOST; } else linkmem->numgbas=1; for(i=0;i<4;i++){ linkevent[15]=(char)i+'1'; if((linksync[i]=CreateEvent(NULL, true, false, linkevent))==NULL){ closesocket(lanlink.tcpsocket); WSACleanup(); UnmapViewOfFile(linkmem); CloseHandle(mmf); for(j=0;jnumgbas; linkid = vbaid; linkmem->numgbas++; linklog = 0; if(linkmem->numgbas>4){ linkmem->numgbas=4; closesocket(lanlink.tcpsocket); WSACleanup(); MessageBox(NULL, "5 or more GBAs not supported.", "Error!", MB_OK|MB_ICONEXCLAMATION); UnmapViewOfFile(linkmem); CloseHandle(mmf); return 0; } for(i=0;i<4;i++){ linkevent[15]=(char)i+'1'; if((linksync[i]=OpenEvent(EVENT_ALL_ACCESS, false, linkevent))==NULL){ closesocket(lanlink.tcpsocket); WSACleanup(); CloseHandle(mmf); UnmapViewOfFile(linkmem); for(j=0;jlastlinktime=0xffffffff; linkmem->numtransfers=0; linkmem->linkflags=0; lanlink.connected = false; lanlink.thread = NULL; lanlink.speed = false; for(i=0;i<4;i++){ linkmem->linkdata[i] = 0xffff; linkdata[i] = 0xffff; } return 1; } int openLinkLog(void){ char filename[20]; if(linklog){ sprintf(filename, "vbalog%1d.txt", vbaid+1); if((linklogfile=fopen(filename, "at"))==NULL){ linklog=false; return 0; } fprintf(linklogfile, "----- Log opened -----\n"); } return 1; } void closeLinkLog() { if(linklogfile) { fclose(linklogfile); linklogfile=NULL; } } void CloseLink(void){ if(lanlink.connected){ if(linkid){ char outbuffer[4]; outbuffer[0] = 4; outbuffer[1] = -32; if(lanlink.type==0) send(lanlink.tcpsocket, outbuffer, 4, 0); } else { char outbuffer[12]; int i; outbuffer[0] = 12; outbuffer[1] = -32; for(i=1;i<=lanlink.numgbas;i++){ if(lanlink.type==0){ send(ls.tcpsocket[i], outbuffer, 12, 0); } closesocket(ls.tcpsocket[i]); } } } linkmem->numgbas--; if(!linkid&&linkmem->numgbas!=0) linkmem->linkflags|=LINK_PARENTLOST; CloseHandle(mmf); UnmapViewOfFile(linkmem); for(i=0;i<4;i++){ if(linksync[i]!=NULL){ PulseEvent(linksync[i]); CloseHandle(linksync[i]); } } regSetDwordValue("LAN", lanlink.active); if(linklog) closeLinkLog(); closesocket(lanlink.tcpsocket); WSACleanup(); return; } lserver::lserver(void){ intinbuffer = (int*)inbuffer; u16inbuffer = (u16*)inbuffer; intoutbuffer = (int*)outbuffer; u16outbuffer = (u16*)outbuffer; oncewait = false; } int lserver::Init(void *serverdlg){ SOCKADDR_IN info; DWORD nothing; char str[100]; info.sin_family = AF_INET; info.sin_addr.S_un.S_addr = INADDR_ANY; info.sin_port = htons(5738); if(bind(lanlink.tcpsocket, (LPSOCKADDR)&info, sizeof(SOCKADDR_IN))==SOCKET_ERROR){ closesocket(lanlink.tcpsocket); if((lanlink.tcpsocket=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET) return WSAGetLastError(); if(bind(lanlink.tcpsocket, (LPSOCKADDR)&info, sizeof(SOCKADDR_IN))==SOCKET_ERROR) return WSAGetLastError(); } if(listen(lanlink.tcpsocket, lanlink.numgbas)==SOCKET_ERROR) return WSAGetLastError(); if(lanlink.thread!=NULL){ lanlink.terminate = true; WaitForSingleObject(linksync[vbaid], 500); lanlink.thread = NULL; } lanlink.terminate = false; linkid = 0; gethostname(str, 100); ((ServerWait*)serverdlg)->m_serveraddress.Format("Server IP address is: %s", inet_ntoa(*(LPIN_ADDR)(gethostbyname(str)->h_addr_list[0]))); lanlink.thread = CreateThread(NULL, 0, LinkServerThread, serverdlg, 0, ¬hing); return 0; } DWORD WINAPI LinkServerThread(void *serverdlg){ fd_set fdset; timeval wsocktimeout; char inbuffer[256], outbuffer[256]; int *intinbuffer = (int*)inbuffer; u16 *u16inbuffer = (u16*)inbuffer; int *intoutbuffer = (int*)outbuffer; u16 *u16outbuffer = (u16*)outbuffer; BOOL disable = true; wsocktimeout.tv_sec = 1; wsocktimeout.tv_usec = 0; i = 0; while(im_plconn[i].Format("Player %d connected", i+1); ((ServerWait*)serverdlg)->UpdateData(false); i++; } } ((ServerWait*)serverdlg)->m_prgctrl.StepIt(); } MessageBox(NULL, "All players connected", "Link", MB_OK); ((ServerWait*)serverdlg)->SendMessage(WM_CLOSE, 0, 0); for(i=1;i<=lanlink.numgbas;i++){ outbuffer[0] = 4; send(ls.tcpsocket[i], outbuffer, 4, 0); } lanlink.connected = true; return 0; } void lserver::Send(void){ if(lanlink.type==0){ // TCP if(savedlinktime==-1){ outbuffer[0] = 4; outbuffer[1] = -32; //0xe0 for(i=1;i<=lanlink.numgbas;i++){ send(tcpsocket[i], outbuffer, 4, 0); recv(tcpsocket[i], inbuffer, 4, 0); } } outbuffer[1] = tspeed; u16outbuffer[1] = linkdata[0]; intoutbuffer[1] = savedlinktime; if(lanlink.numgbas==1){ if(lanlink.type==0){ outbuffer[0] = 8; send(tcpsocket[1], outbuffer, 8, 0); } } else if(lanlink.numgbas==2){ u16outbuffer[4] = linkdata[2]; if(lanlink.type==0){ outbuffer[0] = 10; send(tcpsocket[1], outbuffer, 10, 0); u16outbuffer[4] = linkdata[1]; send(tcpsocket[2], outbuffer, 10, 0); } } else { if(lanlink.type==0){ outbuffer[0] = 12; u16outbuffer[4] = linkdata[2]; u16outbuffer[5] = linkdata[3]; send(tcpsocket[1], outbuffer, 12, 0); u16outbuffer[4] = linkdata[1]; send(tcpsocket[2], outbuffer, 12, 0); u16outbuffer[5] = linkdata[2]; send(tcpsocket[3], outbuffer, 12, 0); } } } return; } void lserver::Recv(void){ int numbytes; if(lanlink.type==0){ // TCP wsocktimeout.tv_usec = 0; wsocktimeout.tv_sec = linktimeout / 1000; fdset.fd_count = lanlink.numgbas; for(i=0;i1) memcpy(inbuffer, inbuffer+inbuffer[0]*(howmanytimes-1), inbuffer[0]); if(inbuffer[1]==-32){ char message[30]; lanlink.connected = false; sprintf(message, "Player %d disconnected.", i+2); MessageBox(NULL, message, "Link", MB_OK); outbuffer[0] = 4; outbuffer[1] = -32; for(i=1;ih_addr_list); if(ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block)==SOCKET_ERROR) return WSAGetLastError(); if(lanlink.thread!=NULL){ lanlink.terminate = true; WaitForSingleObject(linksync[vbaid], 500); lanlink.thread = NULL; } ((ServerWait*)waitdlg)->SetWindowText("Connecting..."); lanlink.terminate = false; lanlink.thread = CreateThread(NULL, 0, LinkClientThread, waitdlg, 0, ¬hing); return 0; } DWORD WINAPI LinkClientThread(void *waitdlg){ fd_set fdset; timeval wsocktimeout; int numbytes; char inbuffer[16]; u16 *u16inbuffer = (u16*)inbuffer; unsigned long block = 0; if(connect(lanlink.tcpsocket, (LPSOCKADDR)&lc.serverinfo, sizeof(SOCKADDR_IN))==SOCKET_ERROR){ if(WSAGetLastError()!=WSAEWOULDBLOCK){ MessageBox(NULL, "Couldn't connect to server.", "Link", MB_OK); return 1; } wsocktimeout.tv_sec = 1; wsocktimeout.tv_usec = 0; do{ if(lanlink.terminate) return 0; fdset.fd_count = 1; fdset.fd_array[0] = lanlink.tcpsocket; ((ServerWait*)waitdlg)->m_prgctrl.StepIt(); } while(select(0, NULL, &fdset, NULL, &wsocktimeout)!=1&&connect(lanlink.tcpsocket, (LPSOCKADDR)&lc.serverinfo, sizeof(SOCKADDR_IN))!=0); } ioctlsocket(lanlink.tcpsocket, FIONBIO, &block); numbytes = 0; while(numbytes<4) numbytes += recv(lanlink.tcpsocket, inbuffer+numbytes, 16, 0); linkid = (int)u16inbuffer[0]; lanlink.numgbas = (int)u16inbuffer[1]; ((ServerWait*)waitdlg)->m_serveraddress.Format("Connected as #%d", linkid+1); if(lanlink.numgbas!=linkid) ((ServerWait*)waitdlg)->m_plconn[0].Format("Waiting for %d players to join", lanlink.numgbas-linkid); else ((ServerWait*)waitdlg)->m_plconn[0].Format("All players joined."); numbytes = 0; inbuffer[0] = 1; while(numbytesSendMessage(WM_CLOSE, 0, 0); block = 1; ioctlsocket(lanlink.tcpsocket, FIONBIO, &block); lanlink.connected = true; return 0; } void lclient::CheckConn(void){ if((numbytes=recv(lanlink.tcpsocket, inbuffer, 256, 0))>0){ while(numbytes